fix(gateway): translate ICMPv6 egress replies instead of dropping them - #419
Merged
privateip merged 1 commit intoAug 16, 2026
Merged
Conversation
edgenat.c's handle_egress_return claimed masq_addr and dropped anything that wasn't TCP or UDP, so ICMPv6 Destination Unreachable, Packet Too Big (path MTU discovery), Time Exceeded, and Echo Reply never reached the tenant that originated the flow. PMTUD failures presented as stalled large transfers instead of an adapting connection, and the simplest reachability check a tenant can run (ping) never worked. handle_egress_return now dispatches ICMPv6 into its own branch: the four RFC 4443 error types translate back to the tenant by parsing the embedded original datagram (a new parse_embedded_ports helper, since RFC 4443 only guarantees 8 bytes -- not enough for parse_l4's full TCP header bounds check) and reusing egress_conn_table's existing reverse key one layer deeper. Echo Request/Reply gets a full round trip, masquerading the Echo Identifier as a pseudo-port in both handle_egress_forward_icmp6 and handle_egress_return_icmp6_echo, the same technique Linux's own nf_conntrack ICMP tracker uses. Any other ICMPv6 type now XDP_PASSes instead of being claimed and dropped. Two new drop reasons (DROP_REASON_MALFORMED_EGRESS_ICMP, DROP_REASON_NO_EGRESS_ICMP_CONN) replace the previous mislabeled use of DROP_REASON_MALFORMED_EGRESS_RETURN, which counted a well-formed ICMPv6 packet as a parse failure rather than the protocol-policy decision it actually was -- flagged in the #381 review comment this closes. dropreason.go mirrors both constants; edgemetrics's collector picks them up automatically since it already iterates DropReasonCount generically. Adds edgenat_egress_icmp_test.go: seven root-required kernel tests covering all three translated error types, an unknown-flow drop, an unhandled-type passthrough, a forward-side non-echo-request rejection, and a full ping round trip proving the tenant's original identifier is restored, not just the address. Verified against the real eBPF verifier and passing, alongside the full existing suite (task test:unit, task test:unit-root, task lint, and task build all green). Includes docs/plans/404-egress-return-icmpv6-handling.md, the implementation plan this change follows. Fixes #404 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
privateip
requested review from
0xmc,
JoseSzycho,
ecv,
scotwells and
slindseysr
and removed request for
a team
August 16, 2026 16:22
ecv
approved these changes
Aug 16, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The gateway's egress masquerade path claimed its public address and dropped anything that wasn't TCP or UDP, so ICMPv6 errors and echo replies from the internet never reached the tenant that sent the original packet. Path MTU discovery rides on one of those dropped message types, so large transfers silently stalled instead of adapting, and a tenant's own ping never worked. Both cases now translate back to the originating tenant instead of being dropped, and any other ICMPv6 type passes through to the normal network stack instead of being silently discarded.
Note
Base branch is
feat/865-egress-phase-b(#381), not the default branch — the code this fixes doesn't exist onmainyet, since the whole egress feature is still an open, unmerged stack. Merging here won't auto-close #404 until that stack lands onmain.Test plan
Fixes #404