feat(gateway): add egress masquerade datapath to edgenat.c - #381
Closed
privateip wants to merge 3 commits into
Closed
feat(gateway): add egress masquerade datapath to edgenat.c#381privateip wants to merge 3 commits into
privateip wants to merge 3 commits into
Conversation
scotwells
approved these changes
Aug 13, 2026
This was referenced Aug 13, 2026
Contributor
Author
|
The test failures are expected. Need to merge datum-cloud/network#15 first. |
This was referenced Aug 13, 2026
Phase B of #865, per docs/plans/865-edge-gateway-nat66-egress.md §3. Adds egress_config_table, egress_conn_table, handle_egress_forward, handle_egress_return, and the updated 4-way top-level dispatch in edge_nat(). tenant_arg (the egress_sid locator's uFMT Argument bits) keys the forward row so two tenants with colliding ULA backend addresses never collide in egress_conn_table. backend_usid (needed to route replies back to the originating worker node) is captured directly from the egress-forward packet's own outer SRv6 source address, since Phase B proposes no per-tenant policy table to look it up from. This assumes RouteEgressAdd's SEG6 encap route always stamps the originating node's own uSID as that outer source, consistent with every other cross-node SRv6 packet in this codebase, but that has not been independently verified here -- flagged in the code and worth confirming before Phase D's e2e proof relies on it. handle_egress_return drops any non-TCP/UDP protocol addressed to masq_addr (e.g. ICMPv6) rather than passing it to the kernel stack, the same fail-closed convention gw_addr's own return branch already uses for claimed addresses -- a real cost for an internet-facing address that gw_addr's internal-only precedent didn't have to weigh. Tests cover both new branches end-to-end (SNAT/DNAT, checksum, real FIB-resolved L2), non-SYN/no-conn drops, PAT exhaustion, and the tenant-isolation scenario the plan's §6 calls out explicitly: two tenants presenting the same colliding backend_addr:port->dest_addr:port tuple via distinct tenant_arg values resolve to two independent egress_conn_table rows and masq_port allocations.
privateip
force-pushed
the
feat/865-egress-phase-b
branch
from
August 14, 2026 01:15
0886b06 to
9c0a1c6
Compare
This was referenced Aug 14, 2026
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>
4 tasks
…dling fix(gateway): translate ICMPv6 egress replies instead of dropping them
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
Phase B of #865, per docs/plans/865-edge-gateway-nat66-egress.md §3.
Stacked on #380 (Phase A).
egress_config_table,egress_conn_table,handle_egress_forward,handle_egress_return, updated 4-way dispatch inedge_nat().tenant_arg(egress_sid's uFMT Argument bits) keys the forward row,so two tenants with colliding ULA backend addresses never collide in
egress_conn_table.dropreason.go.Flagged for review
backend_usid(needed to route replies back to the originatingworker node) has no per-tenant policy table in Phase B. Resolved by
reading it off the wire -- the egress-forward packet's own outer
SRv6 source address, captured before the outer header is stripped.
Assumes
RouteEgressAdd's SEG6 encap route always stamps theoriginating node's own uSID as that outer source, consistent with
every other cross-node SRv6 packet in this codebase -- not
independently verified here. Marked in the code; worth confirming
before Phase D's e2e proof depends on it.
handle_egress_returndrops any non-TCP/UDP protocol addressed tomasq_addr(e.g. ICMPv6) rather than passing it to the kernelstack -- same fail-closed convention
gw_addr's own return branchuses for claimed addresses, but a real cost for an internet-facing
address that
gw_addr's internal-only precedent didn't have toweigh.
Testing
edgenat_egress_test.go: both branchesend-to-end (SNAT/DNAT, checksum, real FIB-resolved L2), non-SYN/no-conn
drops, PAT exhaustion, and the tenant-isolation scenario §6 calls out
explicitly (colliding backend/dest tuple, distinct
tenant_arg->two independent rows and
masq_portallocations).go build ./...,go vet ./...,task lint(0 issues) -- clean.edgenatsuite passes under root +-race.task test:unit: one pre-existing, unrelated failure ininternal/cni/tap(environment-specific -- confirmed on theunmodified branch too).
🤖 Generated with Claude Code