[client] Add non-root ICMP support to userspace firewall forwarder#4792
[client] Add non-root ICMP support to userspace firewall forwarder#4792
Conversation
📝 WalkthroughWalkthroughAdds atomic MTU and lifecycle stubs to endpoint; probes raw ICMP capability and implements async ICMP Echo handling with raw-socket and system-ping fallbacks; changes UDP handler to return bool; generalizes ICE receiver to accept batch readers; adds Warn4 logger and updates module dependencies. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant Client
participant Forwarder
participant ICMP as ICMPHandler
participant Raw as RawSocket
participant Ping as PingCmd
participant Stack as Netstack
Client->>Forwarder: inbound IP packet (ICMP Echo request)
Forwarder->>ICMP: handleICMP(id, pkt)
ICMP->>ICMP: handleICMPEcho(flowID, id, pkt) (async, semaphore)
alt Forwarder.hasRawICMPAccess == true
ICMP->>Raw: forwardICMPPacket / handleICMPViaSocket
Raw->>Raw: open raw socket, send packet, receive reply
Raw->>ICMP: handleEchoResponse(conn)
ICMP->>Stack: injectICMPReply
else
ICMP->>Ping: handleICMPViaPing (build & run ping)
Ping->>Ping: execute system ping, capture RTT
Ping->>ICMP: synthesizeEchoReply
ICMP->>Stack: injectICMPReply
end
Stack-->>Client: ICMP reply delivered
sequenceDiagram
autonumber
participant Caller
participant UDP as UDPHandler
participant Endpoint
participant Proxy
Caller->>UDP: handleUDP(request)
alt ctx cancelled
UDP-->>Caller: return false
else
UDP->>Endpoint: SetMTU (atomic.Store)
UDP->>UDP: create gonet.NewUDPConn(...)
alt conn created
UDP-->>Caller: return true
UDP->>Proxy: spawn proxy goroutine
else
UDP-->>Caller: return false
end
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
📜 Recent review detailsConfiguration used: defaults Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
🧰 Additional context used🧠 Learnings (5)📓 Common learnings📚 Learning: 2026-01-08T16:23:05.027ZApplied to files:
📚 Learning: 2025-11-15T14:46:06.743ZApplied to files:
📚 Learning: 2025-11-13T15:19:32.799ZApplied to files:
📚 Learning: 2025-11-16T19:27:08.018ZApplied to files:
🧬 Code graph analysis (1)client/iface/bind/ice_bind.go (3)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (24)
🔇 Additional comments (1)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
81ce68d to
a7a5407
Compare
a7a5407 to
e7d5cdc
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
client/firewall/uspfilter/forwarder/icmp.go (1)
57-65: Consider adding panic recovery to the goroutine.If
handleICMPViaSocketorhandleICMPViaPingpanic, the goroutine will crash silently. Consider adding a deferred panic recovery handler to log any unexpected panics.Apply this diff to add panic recovery:
go func() { defer func() { <-f.pingSemaphore }() + defer func() { + if r := recover(); r != nil { + f.logger.Error1("forwarder: Panic in ICMP echo handler: %v", r) + } + }() if f.hasRawICMPAccess { f.handleICMPViaSocket(flowID, id, icmpHdr, icmpData, rxBytes)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
client/firewall/uspfilter/forwarder/icmp.go(4 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: lixmal
Repo: netbirdio/netbird PR: 4777
File: client/firewall/iptables/acl_linux.go:439-450
Timestamp: 2025-11-13T15:19:32.799Z
Learning: In the netbird client firewall iptables implementation (client/firewall/iptables/), IPv6 is not currently handled or supported.
🧬 Code graph analysis (1)
client/firewall/uspfilter/forwarder/icmp.go (1)
client/firewall/uspfilter/forwarder/forwarder.go (2)
Forwarder(35-49)New(51-140)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (32)
- GitHub Check: Management / Benchmark (amd64, postgres)
- GitHub Check: Management / Benchmark (amd64, sqlite)
- GitHub Check: Signal / Unit (amd64)
- GitHub Check: Relay / Unit (386)
- GitHub Check: Signal / Unit (386)
- GitHub Check: Management / Benchmark (API) (amd64, postgres)
- GitHub Check: Relay / Unit (amd64, -race)
- GitHub Check: Management / Unit (amd64, sqlite)
- GitHub Check: Management / Integration (amd64, sqlite)
- GitHub Check: Management / Benchmark (API) (amd64, sqlite)
- GitHub Check: Management / Integration (amd64, postgres)
- GitHub Check: Management / Unit (amd64, postgres)
- GitHub Check: Management / Unit (amd64, mysql)
- GitHub Check: Client / Unit (386)
- GitHub Check: Client / Unit (amd64)
- GitHub Check: Client (Docker) / Unit
- GitHub Check: test-docker-compose (mysql)
- GitHub Check: test-docker-compose (postgres)
- GitHub Check: test-docker-compose (sqlite)
- GitHub Check: test-getting-started-script
- GitHub Check: Client / Unit
- GitHub Check: Windows
- GitHub Check: Linux
- GitHub Check: Darwin
- GitHub Check: release_ui
- GitHub Check: release_ui_darwin
- GitHub Check: release
- GitHub Check: iOS / Build
- GitHub Check: Client / Unit
- GitHub Check: JS / Lint
- GitHub Check: Android / Build
- GitHub Check: Client / Unit
🔇 Additional comments (8)
client/firewall/uspfilter/forwarder/icmp.go (8)
5-9: LGTM! Necessary imports for ping fallback.The new imports support the ping binary fallback mechanism and platform-specific command construction.
20-48: LGTM! Clean separation of echo and non-echo ICMP handling.The refactored logic properly delegates echo requests to asynchronous handling and forwards non-echo ICMP types when raw socket access is available.
73-99: LGTM! Proper resource management.The function correctly creates a raw ICMP socket, sends the packet, and handles errors appropriately. The caller is responsible for closing the returned connection as documented.
125-141: LGTM! Proper timeout and buffer sizing.The function correctly sets a read deadline and sizes the response buffer using the atomic MTU value.
233-245: LGTM! Correct ICMP echo reply synthesis.The function properly creates an echo reply by copying the data, changing the type, and recalculating the checksum following the standard ICMP checksum algorithm.
247-272: LGTM! Proper IP header construction and injection.The function correctly constructs an IPv4 header, wraps the ICMP payload, and bypasses the netstack to avoid re-processing by the ICMP handler. The TTL value of 64 is appropriate for local responses.
195-196: TheWarn4logger method exists and is properly implemented.Verification confirms that
Warn4has been added to the Logger type atclient/firewall/uspfilter/log/log.go:171with the correct signature supporting four arguments. The method call at line 195-196 is valid.
212-231: The original review comment's reasoning is fundamentally incorrect.The review claims
-tis TTL on macOS/BSD, but macOS uses-t <timeout>to specify timeout in seconds and FreeBSD uses-t <timeout>for timeout in seconds. The current code is correct for both platforms.However, there IS a real issue: OpenBSD uses
-w <maxwait>, not-t(which is TTL on OpenBSD), and NetBSD uses-w <deadline>for timeout. The current code incorrectly groups all three BSD variants together on line 225, which fails for OpenBSD and NetBSD.The correct fix requires splitting BSD platforms:
- FreeBSD: keep
-t- OpenBSD: change to
-w- NetBSD: change to
-wThe original review's suggested fix is partially correct by accident but based on false premises.
Likely an incorrect or invalid review comment.
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (2)
client/firewall/uspfilter/forwarder/icmp.go (2)
101-123: Send TypeEnd event on error to complete flow tracking.When
forwardICMPPacketfails at line 107, the function returns early without sending aTypeEndevent. This leaves the flow incomplete with only aTypeStartevent.Apply this diff to send an end event on error:
conn, err := f.forwardICMPPacket(id, payload, icmpHdr, 5*time.Second) if err != nil { f.logger.Error2("forwarder: Failed to send ICMP packet for %v: %v", epID(id), err) + f.sendICMPEvent(nftypes.TypeEnd, flowID, id, uint8(icmpHdr.Type()), uint8(icmpHdr.Code()), uint64(rxBytes), 0) return }
184-210: Send TypeEnd event on error to complete flow tracking.When the ping binary fails at line 194, the function returns without sending a
TypeEndevent, leaving the flow incomplete.Apply this diff to send an end event on error:
pingStart := time.Now() if err := cmd.Run(); err != nil { f.logger.Warn4("forwarder: Ping binary failed for %v type %v code %v: %v", epID(id), icmpHdr.Type(), icmpHdr.Code(), err) + f.sendICMPEvent(nftypes.TypeEnd, flowID, id, uint8(icmpHdr.Type()), uint8(icmpHdr.Code()), uint64(rxBytes), 0) return }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
client/firewall/uspfilter/forwarder/icmp.go(4 hunks)client/firewall/uspfilter/forwarder/udp.go(8 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: lixmal
Repo: netbirdio/netbird PR: 4777
File: client/firewall/iptables/acl_linux.go:439-450
Timestamp: 2025-11-13T15:19:32.799Z
Learning: In the netbird client firewall iptables implementation (client/firewall/iptables/), IPv6 is not currently handled or supported.
🧬 Code graph analysis (2)
client/firewall/uspfilter/forwarder/udp.go (1)
client/firewall/uspfilter/forwarder/forwarder.go (1)
Forwarder(35-49)
client/firewall/uspfilter/forwarder/icmp.go (1)
client/firewall/uspfilter/forwarder/forwarder.go (2)
Forwarder(35-49)New(51-140)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (30)
- GitHub Check: Client / Unit (amd64)
- GitHub Check: Relay / Unit (386)
- GitHub Check: Management / Benchmark (API) (amd64, postgres)
- GitHub Check: Management / Unit (amd64, mysql)
- GitHub Check: Management / Benchmark (API) (amd64, sqlite)
- GitHub Check: Relay / Unit (amd64, -race)
- GitHub Check: Management / Integration (amd64, postgres)
- GitHub Check: Management / Integration (amd64, sqlite)
- GitHub Check: Management / Unit (amd64, sqlite)
- GitHub Check: Signal / Unit (amd64)
- GitHub Check: Management / Benchmark (amd64, sqlite)
- GitHub Check: Management / Unit (amd64, postgres)
- GitHub Check: Management / Benchmark (amd64, postgres)
- GitHub Check: Signal / Unit (386)
- GitHub Check: Client / Unit (386)
- GitHub Check: Client (Docker) / Unit
- GitHub Check: release_ui_darwin
- GitHub Check: release_ui
- GitHub Check: release
- GitHub Check: Client / Unit
- GitHub Check: Android / Build
- GitHub Check: test-docker-compose (sqlite)
- GitHub Check: test-docker-compose (mysql)
- GitHub Check: test-getting-started-script
- GitHub Check: test-docker-compose (postgres)
- GitHub Check: JS / Lint
- GitHub Check: Client / Unit
- GitHub Check: Darwin
- GitHub Check: Linux
- GitHub Check: Windows
🔇 Additional comments (4)
client/firewall/uspfilter/forwarder/udp.go (2)
352-354: Good addition of io.EOF to closed-error detection.Treating
io.EOFas a terminal condition inisClosedErroris appropriate, as EOF signals a closed connection and should not be logged as an unexpected error.
180-180: Signature change verified and correct.The web search confirms that
gonet.NewUDPConnin the upgraded gvisor/netstack has signaturefunc NewUDPConn(wq *waiter.Queue, ep tcpip.Endpoint) *UDPConn. The code at line 180 (gonet.NewUDPConn(&wq, ep)) correctly matches this signature, with the stack parameter properly removed as part of the netstack upgrade.client/firewall/uspfilter/forwarder/icmp.go (2)
247-272: Good implementation of ICMP reply injection.The
injectICMPReplymethod correctly:
- Constructs a proper IPv4 header with appropriate fields
- Calculates and sets the IP checksum
- Bypasses netstack to avoid looping through the ICMP handler
- Returns the injected size for flow tracking
212-231: I need to verify the flags across all platforms mentioned. The web search already revealed important information about macOS that contradicts the review comment. Let me search for the other platforms:Based on my verification, the review comment contains fundamental inaccuracies regarding the macOS ping flags.
The review comment is incorrect and should be disregarded.
Evidence from official documentation:
Linux/Android:
-Wspecifies time to wait for a response in seconds, with-wsetting the timeout for the entire program session. The code correctly uses-Wfor timeout.FreeBSD/OpenBSD/NetBSD:
-tspecifies a timeout in seconds before ping exits. The code correctly uses-tfor timeout on these platforms.Windows:
-wsets timeout in milliseconds. The code correctly multipliestimeoutSecby 1000 to convert to milliseconds.macOS/iOS: The review's claim is incorrect. From the earlier web search,
-t timeouton macOS is "overall timeout in seconds before ping exits regardless of packets received." The code correctly uses-twith timeout in seconds. The review mistakenly claims-tis TTL and suggests using-W, but on macOS,-Wuses milliseconds, not seconds, which would be inconsistent with the code's handling of seconds.The platform-specific ping flags in the code are correct and consistent with official documentation across all platforms.
Likely an incorrect or invalid review comment.
|
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In @client/firewall/uspfilter/forwarder/icmp.go:
- Around line 212-231: In buildPingCommand fix the incorrect use of "-t" (TTL)
on darwin/ios and BSDs: replace the "-t" flag with "-W" and pass the timeout in
milliseconds (e.g. timeoutSec * 1000) for the "darwin","ios" and the
"freebsd","openbsd","netbsd" switch cases; also ensure the darwin case keeps the
"-q" flag and add "-q" to the BSD case if supported so behavior matches other
platforms while still relying on the context timeout as a fallback.
🧹 Nitpick comments (4)
client/iface/bind/ice_bind.go (1)
34-44: Non-IPv4 receiver path lacks batch optimization.The non-IPv4 fallback path only reads a single packet per call (
bufs[0]) and doesn't leverage batch reading capabilities that the IPv4 path uses. While this is functionally correct, it may impact performance for non-IPv4 or fallback scenarios.This is likely an intentional simplification for a less common code path, but consider documenting this limitation if performance becomes a concern.
client/firewall/uspfilter/forwarder/icmp.go (3)
50-71: Consider TypeEnd event for rate-limited requests.When the rate limit is exceeded (lines 66-69), a
TypeStartevent was already sent (line 24 inhandleICMP), but no correspondingTypeEndevent is sent. While the learned pattern indicates TypeEnd is primarily for completed echo flows, having a TypeStart without TypeEnd for rate-limited requests may cause incomplete flow tracking.Consider sending a TypeEnd event in the rate-limit case to maintain flow consistency:
Optional: Send TypeEnd for rate-limited requests
default: f.logger.Debug3("forwarder: ICMP rate limit exceeded for %v type %v code %v", epID(id), icmpHdr.Type(), icmpHdr.Code()) + f.sendICMPEvent(nftypes.TypeEnd, flowID, id, uint8(icmpHdr.Type()), uint8(icmpHdr.Code()), 0, 0) }
101-123: Consider sending TypeEnd event on socket forward failure.When
forwardICMPPacketfails (lines 107-108), aTypeStartevent was already emitted (line 24 inhandleICMP), but noTypeEndevent is sent. While the learned pattern focuses TypeEnd on complete bidirectional flows, leaving a TypeStart without a matching TypeEnd may impact flow tracking consistency.For robust flow lifecycle tracking, consider sending TypeEnd with zero metrics on failure:
Send TypeEnd on error
conn, err := f.forwardICMPPacket(id, payload, icmpHdr, 5*time.Second) if err != nil { f.logger.Error2("forwarder: Failed to send ICMP packet for %v: %v", epID(id), err) + f.sendICMPEvent(nftypes.TypeEnd, flowID, id, uint8(icmpHdr.Type()), uint8(icmpHdr.Code()), uint64(rxBytes), 0) return }
184-210: Consider sending TypeEnd event on ping failure.When the ping command fails (lines 194-197), a
TypeStartevent was already emitted but noTypeEndevent is sent. For consistent flow lifecycle tracking, consider sending TypeEnd with zero txBytes on failure:Send TypeEnd on ping error
if err := cmd.Run(); err != nil { f.logger.Warn4("forwarder: Ping binary failed for %v type %v code %v: %v", epID(id), icmpHdr.Type(), icmpHdr.Code(), err) + f.sendICMPEvent(nftypes.TypeEnd, flowID, id, uint8(icmpHdr.Type()), uint8(icmpHdr.Code()), uint64(rxBytes), 0) return }
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
go.sumis excluded by!**/*.sum
📒 Files selected for processing (7)
client/firewall/uspfilter/forwarder/endpoint.goclient/firewall/uspfilter/forwarder/forwarder.goclient/firewall/uspfilter/forwarder/icmp.goclient/firewall/uspfilter/forwarder/udp.goclient/firewall/uspfilter/log/log.goclient/iface/bind/ice_bind.gogo.mod
🚧 Files skipped from review as they are similar to previous changes (3)
- client/firewall/uspfilter/log/log.go
- client/firewall/uspfilter/forwarder/forwarder.go
- client/firewall/uspfilter/forwarder/endpoint.go
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: lixmal
Repo: netbirdio/netbird PR: 4792
File: client/firewall/uspfilter/forwarder/icmp.go:32-48
Timestamp: 2025-11-15T14:46:06.743Z
Learning: In the netbird userspace firewall forwarder (client/firewall/uspfilter/forwarder/), the boolean return value from ICMP/UDP handlers indicates packet ownership to netstack: `true` means "I handled this packet, don't process it further" and `false` means "I didn't handle it, netstack should handle it". This is used to prevent netstack from generating its own replies even when forwarding fails.
Learnt from: lixmal
Repo: netbirdio/netbird PR: 4792
File: client/firewall/uspfilter/forwarder/icmp.go:32-48
Timestamp: 2025-11-16T19:27:08.018Z
Learning: In the netbird userspace firewall forwarder (client/firewall/uspfilter/forwarder/), flow events (TypeStart/TypeEnd) are only sent for ICMP echo requests/replies to track the complete bidirectional flow. Other ICMP types (Time Exceeded, Destination Unreachable, etc.) only get a TypeStart event without a corresponding TypeEnd event, as they are typically one-way notifications that don't require complete lifecycle tracking.
📚 Learning: 2025-11-15T14:46:06.743Z
Learnt from: lixmal
Repo: netbirdio/netbird PR: 4792
File: client/firewall/uspfilter/forwarder/icmp.go:32-48
Timestamp: 2025-11-15T14:46:06.743Z
Learning: In the netbird userspace firewall forwarder (client/firewall/uspfilter/forwarder/), the boolean return value from ICMP/UDP handlers indicates packet ownership to netstack: `true` means "I handled this packet, don't process it further" and `false` means "I didn't handle it, netstack should handle it". This is used to prevent netstack from generating its own replies even when forwarding fails.
Applied to files:
client/firewall/uspfilter/forwarder/udp.goclient/firewall/uspfilter/forwarder/icmp.go
📚 Learning: 2025-11-16T19:27:08.018Z
Learnt from: lixmal
Repo: netbirdio/netbird PR: 4792
File: client/firewall/uspfilter/forwarder/icmp.go:32-48
Timestamp: 2025-11-16T19:27:08.018Z
Learning: In the netbird userspace firewall forwarder (client/firewall/uspfilter/forwarder/), flow events (TypeStart/TypeEnd) are only sent for ICMP echo requests/replies to track the complete bidirectional flow. Other ICMP types (Time Exceeded, Destination Unreachable, etc.) only get a TypeStart event without a corresponding TypeEnd event, as they are typically one-way notifications that don't require complete lifecycle tracking.
Applied to files:
client/firewall/uspfilter/forwarder/icmp.go
🧬 Code graph analysis (2)
client/firewall/uspfilter/forwarder/udp.go (1)
client/firewall/uspfilter/forwarder/forwarder.go (1)
Forwarder(35-49)
client/iface/bind/ice_bind.go (3)
client/net/listener_listen.go (2)
UDPConn(58-62)PacketConn(37-41)client/iface/udpmux/universal.go (1)
UDPConn(119-127)client/iface/bind/endpoint.go (1)
Endpoint(9-9)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (14)
- GitHub Check: Linux
- GitHub Check: Darwin
- GitHub Check: Windows
- GitHub Check: Android / Build
- GitHub Check: iOS / Build
- GitHub Check: Build Cache
- GitHub Check: Client / Unit
- GitHub Check: JS / Lint
- GitHub Check: FreeBSD Port / Build & Test
- GitHub Check: release_ui
- GitHub Check: release
- GitHub Check: release_ui_darwin
- GitHub Check: Client / Unit
- GitHub Check: Client / Unit
🔇 Additional comments (11)
client/firewall/uspfilter/forwarder/udp.go (3)
352-354: LGTM!Treating
io.EOFas a closed error condition is appropriate and aligns with the semantic meaning of EOF (end-of-stream). This prevents unnecessary error logging when connections naturally close.
135-213: LGTM!The boolean return value semantics are consistently applied throughout
handleUDP:
- Returns
falsewhen the packet cannot be handled (context done, dial errors, endpoint errors)- Returns
truewhen the packet is owned/handled (existing connection, successful setup)This aligns with the learned pattern where the return value indicates packet ownership to netstack.
Based on learnings, the boolean return value prevents netstack from generating its own replies.
180-180: The gonet.NewUDPConn signature has been correctly updated for the gvisor bump.The code at line 180 uses the new 2-parameter signature
gonet.NewUDPConn(&wq, ep), which is correct for the current gvisor version (v0.0.0-20251031020517-ecfcdd2f171c). The stack parameter has been properly removed as required by the updated gvisor API.client/firewall/uspfilter/forwarder/icmp.go (4)
20-48: LGTM!The refactored ICMP handling properly separates echo and non-echo paths:
- Echo requests are handled asynchronously via
handleICMPEcho- Non-echo types are forwarded directly when raw access is available
- Returning
trueon line 41 even when forwarding fails is correct per the learned pattern—it signals packet ownership to netstack, preventing it from generating its own repliesBased on learnings, only echo requests/replies track complete flows with TypeEnd events, while other ICMP types only emit TypeStart.
73-99: LGTM!The
forwardICMPPacketfunction is well-designed:
- Properly uses context with timeout for socket creation
- Handles write errors with cleanup before returning
- Clearly documents caller responsibility for closing the connection
- Provides good trace logging for observability
125-141: LGTM!The
handleEchoResponsefunction properly:
- Uses atomic MTU access for buffer sizing
- Sets appropriate read deadlines
- Distinguishes timeout errors from other failures
- Returns meaningful size information for metrics
233-272: LGTM!The helper functions for synthesizing and injecting ICMP replies are well-implemented:
synthesizeEchoReplyproperly copies data, updates type, and recalculates checksumsinjectICMPReplyconstructs valid IPv4 headers with correct field swapping and checksum calculation- Properly bypasses netstack via
endpoint.device.CreateOutboundPacketto avoid infinite loops (line 266)go.mod (3)
25-25: Verify security advisories for significant dependency version bumps.The dependency versions cited are present in go.mod (google.golang.org/api v0.249.0, OpenTelemetry instrumentation v0.61.0, and others). To complete verification, run
go list -u -m allandgovulncheck ./...locally to check for security advisories and breaking changes in these updates.
122-122: The gvisor versionv0.0.0-20251031020517-ecfcdd2f171cexists and is compatible with the wireguard-go fork.The gvisor bump is verified: the version exists and is properly configured in go.mod with the corresponding wireguard-go fork replacement (
github.com/netbirdio/wireguard-go v0.0.0-20251115110821-938e8b40e2f3).
266-266: Verify the netbirdio/wireguard-go dependency before merging.The go.mod update references a dependency on netbirdio/wireguard-go (v0.0.0-20251115110821-938e8b40e2f3), but the associated PR status could not be verified through standard GitHub channels. Since netbirdio/wireguard-go appears to be a mirror repository, manually confirm the dependency is stable and the referenced work is complete before merging this PR.
client/iface/bind/ice_bind.go (1)
30-33: CreateReceiverFn signature correctly generalizes receiver creation for multiple packet connection types.The method signature change from
CreateIPv4ReceiverFntoCreateReceiverFnwithwgConn.BatchReaderparameter properly aligns with wireguard-go's interface contract. The implementation correctly handles IPv4 connections via type assertion while providing a fallback for non-IPv4 paths using standardnet.UDPConnsemantics.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In @client/firewall/uspfilter/forwarder/icmp.go:
- Line 21: The goroutine is capturing icmpHdr which points into the original
packet buffer (created via
header.ICMPv4(pkt.TransportHeader().View().AsSlice())), causing a data race when
the packet is recycled; fix by constructing the ICMP header from the copied
icmpData before spawning the goroutine (e.g., create a new
header.ICMPv4(icmpData) and use that in the goroutine instead of the original
icmpHdr), ensuring the goroutine only references the safe, copied slice.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
go.sumis excluded by!**/*.sum
📒 Files selected for processing (3)
client/firewall/uspfilter/forwarder/icmp.goclient/iface/bind/ice_bind.gogo.mod
🚧 Files skipped from review as they are similar to previous changes (1)
- go.mod
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: lixmal
Repo: netbirdio/netbird PR: 4792
File: client/firewall/uspfilter/forwarder/icmp.go:32-48
Timestamp: 2025-11-15T14:46:06.743Z
Learning: In the netbird userspace firewall forwarder (client/firewall/uspfilter/forwarder/), the boolean return value from ICMP/UDP handlers indicates packet ownership to netstack: `true` means "I handled this packet, don't process it further" and `false` means "I didn't handle it, netstack should handle it". This is used to prevent netstack from generating its own replies even when forwarding fails.
Learnt from: lixmal
Repo: netbirdio/netbird PR: 4792
File: client/firewall/uspfilter/forwarder/icmp.go:32-48
Timestamp: 2025-11-16T19:27:08.018Z
Learning: In the netbird userspace firewall forwarder (client/firewall/uspfilter/forwarder/), flow events (TypeStart/TypeEnd) are only sent for ICMP echo requests/replies to track the complete bidirectional flow. Other ICMP types (Time Exceeded, Destination Unreachable, etc.) only get a TypeStart event without a corresponding TypeEnd event, as they are typically one-way notifications that don't require complete lifecycle tracking.
📚 Learning: 2025-11-15T14:46:06.743Z
Learnt from: lixmal
Repo: netbirdio/netbird PR: 4792
File: client/firewall/uspfilter/forwarder/icmp.go:32-48
Timestamp: 2025-11-15T14:46:06.743Z
Learning: In the netbird userspace firewall forwarder (client/firewall/uspfilter/forwarder/), the boolean return value from ICMP/UDP handlers indicates packet ownership to netstack: `true` means "I handled this packet, don't process it further" and `false` means "I didn't handle it, netstack should handle it". This is used to prevent netstack from generating its own replies even when forwarding fails.
Applied to files:
client/firewall/uspfilter/forwarder/icmp.go
📚 Learning: 2025-11-16T19:27:08.018Z
Learnt from: lixmal
Repo: netbirdio/netbird PR: 4792
File: client/firewall/uspfilter/forwarder/icmp.go:32-48
Timestamp: 2025-11-16T19:27:08.018Z
Learning: In the netbird userspace firewall forwarder (client/firewall/uspfilter/forwarder/), flow events (TypeStart/TypeEnd) are only sent for ICMP echo requests/replies to track the complete bidirectional flow. Other ICMP types (Time Exceeded, Destination Unreachable, etc.) only get a TypeStart event without a corresponding TypeEnd event, as they are typically one-way notifications that don't require complete lifecycle tracking.
Applied to files:
client/firewall/uspfilter/forwarder/icmp.go
📚 Learning: 2026-01-07T10:08:00.555Z
Learnt from: lixmal
Repo: netbirdio/netbird PR: 4792
File: client/firewall/uspfilter/forwarder/icmp.go:212-231
Timestamp: 2026-01-07T10:08:00.555Z
Learning: OS-specific ping flag handling: macOS/iOS and FreeBSD use -t timeout; OpenBSD/NetBSD use -w timeout. In code that shells out to ping (e.g., icmp.go and related files), implement OS-aware flag selection. Use build tags or GOOS checks to choose the correct flag, and encapsulate this in a helper (e.g., getPingTimeoutFlag()) to avoid hard-coding across platforms. Document the OS assumption and ensure tests cover the differing flags.
Applied to files:
client/firewall/uspfilter/forwarder/icmp.go
🧬 Code graph analysis (1)
client/firewall/uspfilter/forwarder/icmp.go (1)
client/firewall/uspfilter/forwarder/forwarder.go (2)
Forwarder(35-49)New(51-140)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (12)
- GitHub Check: release
- GitHub Check: release_ui
- GitHub Check: FreeBSD Port / Build & Test
- GitHub Check: Client / Unit
- GitHub Check: Client / Unit
- GitHub Check: Build Cache
- GitHub Check: iOS / Build
- GitHub Check: Linux
- GitHub Check: Windows
- GitHub Check: Android / Build
- GitHub Check: JS / Lint
- GitHub Check: Check External GPL/AGPL Licenses
🔇 Additional comments (8)
client/iface/bind/ice_bind.go (2)
29-31: LGTM: Interface generalization improves flexibility.The change from concrete
*ipv4.PacketConntowgConn.BatchReaderinterface aligns with the wireguard-go update and improves flexibility. The method name change fromCreateIPv4ReceiverFntoCreateReceiverFnis also more accurate.
162-233: BatchReader interface is properly available from the configured wireguard-go fork.The
BatchReaderinterface is imported fromgolang.zx2c4.com/wireguard/conn(via the netbirdio/wireguard-go fork v0.0.0-20260107100953-33b7c9d03db0 already configured in go.mod), and theReadBatchmethod is called correctly at lines 187 and 196. The implementation is consistent across both function signatures and integrates properly with the updated dependency.client/firewall/uspfilter/forwarder/icmp.go (6)
73-99: LGTM: Clean error handling and resource management.The function properly creates the ICMP socket, writes the packet, and returns the connection with clear ownership semantics. Error paths correctly close the connection when needed and wrap errors with context.
125-141: LGTM: Proper timeout handling and atomic MTU access.The function correctly sets a read deadline, handles timeout errors gracefully, and uses atomic load for the MTU value. The return value properly indicates the injected packet size.
212-233: LGTM: Correct platform-specific timeout flags.The function correctly uses platform-specific ping timeout flags:
-tfor macOS/iOS/FreeBSD,-wfor OpenBSD/NetBSD/Windows, and-Wfor Linux/Android, consistent with their respective man pages.Based on learnings, OS-specific ping flag handling is implemented correctly.
235-247: LGTM: Proper ICMP echo reply synthesis.The function correctly creates an echo reply by copying the request data, changing the type to
EchoReply, and recalculating the checksum. The implementation follows standard ICMP reply construction.
249-274: LGTM: Correct IP packet construction and injection.The function properly constructs an IPv4 packet with the ICMP payload, calculates checksums, and bypasses netstack to inject directly via the endpoint device. This correctly prevents the packet from looping back through the ICMP handler.
32-47: LGTM: Correct non-echo ICMP handling.The non-echo ICMP path correctly returns
falsewhen raw socket access is unavailable (letting netstack handle it), and returnstrueafter forwarding when raw access is available. Per the design,TypeEndevents are intentionally only sent for echo requests/replies, so omittingTypeEndhere is correct.Based on learnings, the return value semantics and flow tracking behavior are correct.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In @client/iface/bind/ice_bind.go:
- Around line 34-44: The fallback packet receiver returned in ice_bind.go
currently reads a single packet via conn.ReadFromUDPAddrPort but does not filter
STUN messages, record transport activity, or interact with the UDP mux like the
IPv4 path; modify that returned func to call filterOutStunMessages on the
received buf (and skip returning the packet if it’s a STUN message), update
activity tracking by invoking the same recordTransportActivity logic used in the
IPv4 branch (using the StdNetEndpoint/wgConn.StdNetEndpoint), and ensure any use
of s.udpMux/GetICEMux() mirrors the IPv4 path’s handling (propagate or handle
the error as callers expect) so STUNs are consistently filtered and activity is
recorded even in the fallback path.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
go.sumis excluded by!**/*.sum
📒 Files selected for processing (2)
client/iface/bind/ice_bind.gogo.mod
🚧 Files skipped from review as they are similar to previous changes (1)
- go.mod
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: lixmal
Repo: netbirdio/netbird PR: 4792
File: client/firewall/uspfilter/forwarder/icmp.go:32-48
Timestamp: 2025-11-15T14:46:06.743Z
Learning: In the netbird userspace firewall forwarder (client/firewall/uspfilter/forwarder/), the boolean return value from ICMP/UDP handlers indicates packet ownership to netstack: `true` means "I handled this packet, don't process it further" and `false` means "I didn't handle it, netstack should handle it". This is used to prevent netstack from generating its own replies even when forwarding fails.
Learnt from: lixmal
Repo: netbirdio/netbird PR: 4792
File: client/firewall/uspfilter/forwarder/icmp.go:32-48
Timestamp: 2025-11-16T19:27:08.018Z
Learning: In the netbird userspace firewall forwarder (client/firewall/uspfilter/forwarder/), flow events (TypeStart/TypeEnd) are only sent for ICMP echo requests/replies to track the complete bidirectional flow. Other ICMP types (Time Exceeded, Destination Unreachable, etc.) only get a TypeStart event without a corresponding TypeEnd event, as they are typically one-way notifications that don't require complete lifecycle tracking.
Learnt from: lixmal
Repo: netbirdio/netbird PR: 4777
File: client/firewall/iptables/acl_linux.go:439-450
Timestamp: 2025-11-13T15:19:32.799Z
Learning: In the netbird client firewall iptables implementation (client/firewall/iptables/), IPv6 is not currently handled or supported.
🧬 Code graph analysis (1)
client/iface/bind/ice_bind.go (3)
client/net/listener_listen.go (2)
UDPConn(58-62)PacketConn(37-41)client/iface/udpmux/universal.go (1)
UDPConn(119-127)client/iface/bind/endpoint.go (1)
Endpoint(9-9)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (15)
- GitHub Check: Darwin
- GitHub Check: Linux
- GitHub Check: Windows
- GitHub Check: Client / Unit
- GitHub Check: Build Cache
- GitHub Check: Android / Build
- GitHub Check: JS / Lint
- GitHub Check: iOS / Build
- GitHub Check: FreeBSD Port / Build & Test
- GitHub Check: Client / Unit
- GitHub Check: release_ui_darwin
- GitHub Check: release_ui
- GitHub Check: release
- GitHub Check: Client / Unit
- GitHub Check: Check External GPL/AGPL Licenses
🔇 Additional comments (1)
client/iface/bind/ice_bind.go (1)
30-33: LGTM: Clean generalization with safe fallback.The function signature change from
CreateIPv4ReceiverFntoCreateReceiverFnacceptingwgConn.BatchReaderproperly accommodates the updated wireguard-go API while preserving the existing IPv4-specific path through safe type assertion.
|



Describe your changes
Depends on netbirdio/wireguard-go#12
Issue ticket number and link
Stack
Checklist
Documentation
Select exactly one:
Docs PR URL (required if "docs added" is checked)
Paste the PR link from https://github.com/netbirdio/docs here:
https://github.com/netbirdio/docs/pull/__
Summary by CodeRabbit
New Features
Bug Fixes / Stability
Chores
✏️ Tip: You can customize this high-level summary in your review settings.