Skip to content

atunnel: support IPv6 original destination lookup - #18

Open
ygao-g wants to merge 2 commits into
mainfrom
fork-753-clean
Open

atunnel: support IPv6 original destination lookup#18
ygao-g wants to merge 2 commits into
mainfrom
fork-753-clean

Conversation

@ygao-g

@ygao-g ygao-g commented Sep 1, 2026

Copy link
Copy Markdown
Owner

A commit-history proposal for agent-substrate#753. Not for merge.

The tree here is byte-identical to agent-substrate#753's head b97ab7c9, rebased onto upstream/main
fe9013a4; git diff between the two is empty. Only the history differs: agent-substrate#753's eight
commits are re-cut as two.

commit files size
atunnel: look up the original destination on IPv6 original_dst_linux.go, original_dst_format_linux_test.go +136 −20
atunnel: test original destination against REDIRECT rules original_dst_linux_test.go +472

agent-substrate#753's history records the review conversation rather than the change: seven of its eight
commits have no body, and select original destination by family replaces the ENOENT-retry
dispatch introduced two commits earlier, so a bisector can land on an approach that was
abandoned. The split above puts the ~40-line behavior change and the test that runs on any
machine in one commit, and the privileged netns/nftables harness in another, so the part
worth scrutinising is not buried under scaffolding.

Both commits were checked independently on Linux 6.17 / arm64 — go vet clean, gofmt
clean, and go test ./internal/atunnel/ passing at each one (the second under sudo, which
is where the four root-gated tests actually run).

🤖 Generated with Claude Code

TCPOriginalDestination only ever asked for SO_ORIGINAL_DST at SOL_IP. On a
pure IPv6 socket that builds a PF_INET conntrack tuple from a zeroed address
pair, so the lookup misses and an IPv6 actor connection has no destination
to tunnel to. The socket option is now chosen from the connection's local
address, reading IP6T_SO_ORIGINAL_DST at SOL_IPV6 when that address is not
v4-mapped, and the resulting errno reaches the caller labelled with the
family whose lookup ran.

Dispatching on the local address rather than the socket domain keeps a
v4-mapped connection on a dual-stack listener using the IPv4 option, where
the IPv6 one would return EOPNOTSUPP and mask the real error.
Nothing exercised the socket-option path itself: the formatting test cannot
tell whether the kernel was asked the right question. These tests build an
actor-like network namespace joined by a veth, install an nftables
PREROUTING REDIRECT, and assert the pre-redirect destination on both
families, along with the ENOENT an unredirected connection must still
report.

The worker side runs in a private namespace so that a host default-deny
INPUT policy cannot drop the redirected SYN, and so that conntrack in the
ambient namespace cannot answer the lookup that is supposed to miss.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant