ateomnet: give the actor an IPv6 address when the pod has one - #1057
Draft
Yuan Gao (ygao-g) wants to merge 2 commits into
Draft
ateomnet: give the actor an IPv6 address when the pod has one#1057Yuan Gao (ygao-g) wants to merge 2 commits into
Yuan Gao (ygao-g) wants to merge 2 commits into
Conversation
2 tasks
Yuan Gao (ygao-g)
force-pushed
the
ateomnet-ipv6-dualstack
branch
from
August 19, 2026 22:34
8311751 to
42e7c46
Compare
This was referenced Aug 20, 2026
Draft
Yuan Gao (ygao-g)
force-pushed
the
ateomnet-ipv6-dualstack
branch
from
August 20, 2026 16:11
298b6d3 to
25b16cd
Compare
This was referenced Aug 20, 2026
Yuan Gao (ygao-g)
marked this pull request as ready for review
August 20, 2026 21:10
Yuan Gao (ygao-g)
marked this pull request as draft
August 20, 2026 21:49
Yuan Gao (ygao-g)
force-pushed
the
ateomnet-ipv6-dualstack
branch
from
August 21, 2026 00:52
25b16cd to
c7d1156
Compare
Comment on lines
+1089
to
+1091
| // TODO(#246): the guest is configured IPv4-only, so a micro-VM actor sees no | ||
| // IPv6 even on a dual-stack pod where the host veth has one. gVisor reads the | ||
| // interior netns and picks the address up; this path has to be told. |
Collaborator
There was a problem hiding this comment.
Please do not merge new gVisor only features. This should be resolved prior to merge.
e2e tests should exercise both in lockstep.
Yuan Gao (ygao-g)
force-pushed
the
ateomnet-ipv6-dualstack
branch
from
August 21, 2026 04:46
c7d1156 to
cf4f38e
Compare
Benjamin Elder (BenTheElder)
added a commit
to BenTheElder/substrate
that referenced
this pull request
Aug 21, 2026
Both ateoms now build actor networking through internal/actornet, so ateomnet's version has had no caller outside its own package since the micro-VM ateom moved. This deletes it: SetupActorNetwork, NetworkConfig, CleanupActorNetwork, ConfigureActorVeth, the nftables install and remove, their expression helpers, and PodIPv4 and DumpNetInfo, which only those used. What stays is what actornet actually consumes -- the veth constants and addresses, MustParse*, EnableIPv4Forwarding, TCPProtocol, and the namespace helpers. Leaving it would not have been merely untidy. It creates a table named ateom_actor in the ip family, and actornet creates one under the same name for the actors that are really running; a table name is unique per family, so the two are invisible to each other's cleanup and either could delete or shadow the other's. That is latent while nothing calls the dead path, and stops being latent the moment anyone does. The table name moves to actornet with it, unexported, since actornet is now its only creator. The value is unchanged so an ateom rolling onto this code still recognises and clears the table its predecessor left behind. Two smaller leftovers of the same replacement: prepareOCIBundles no longer takes the ateom pod UID, because the namespace it writes into the bundle is keyed by actor now; and ateomnet's tests go with the code they covered, after porting the three that pin behaviour actornet still has -- repeatable setup, the fixed host-veth MAC, and the interior-link sweep. The last two are micro-VM requirements, so losing their coverage would have been a real gap rather than a bookkeeping one. NOTE: this collides with agent-substrate#1116, which moves the same table to the inet family, and with agent-substrate#1057 behind it. Both are dual-stack work on code this deletes. Whichever lands first, the other side is a delete-vs-modify conflict -- loud, and resolved by keeping the delete -- but the dual-stack change will need to be made in actornet instead. Worth settling before any of this becomes a real PR.
The actor's NAT and filter rules lived in an ip table, which can only ever carry IPv4. They are now in an inet table, so one table can hold both address families when the actor veth becomes dual-stack. Every IPv4 match opens with an NFPROTO comparison, because a bare payload match in an inet table would read an IPv4 offset out of an IPv6 header. IPv4 behaviour is unchanged, but the move is not a no-op on a dual-stack pod: inet nat chains register the nat hooks for both families, so IPv6 traffic in the worker pod netns is now conntracked, and the forward accept now covers IPv6. NAT in the inet family needs Linux 5.2 or later. Teardown sweeps ip as well as inet. A table name is unique per family, so the ip table an earlier ateom left behind is invisible to an inet-only cleanup: the dump comes back empty, the "already clean" path reports success, and the stale table keeps redirecting alongside the new one. Part of agent-substrate#246
Actor networking was IPv4-only, so an actor on a dual-stack worker pod could not reach an IPv6-only destination at all. SetupActorNetwork now assigns the fd00:169:254::/126 counterparts of the existing point-to-point pair to both ends of the actor veth, installs an IPv6 default route in the interior netns, and adds the matching rules to the inet-family actor table. Whether the actor gets IPv6 is decided once in the worker pod netns and carried into the interior one, which is created fresh and so always reports IPv6 available whatever the cluster's families are. Both halves have to hold: the pod needs a global IPv6 address of its own, and the veth has to accept an IPv6 address -- IPv4-only GKE sets disable_ipv6 and netlink then rejects the assignment with EPERM. Addresses carry IFA_F_NODAD rather than the accept_dad sysctl, which the unprivileged ateom container cannot write. Part of agent-substrate#246
Yuan Gao (ygao-g)
force-pushed
the
ateomnet-ipv6-dualstack
branch
from
August 21, 2026 20:03
cf4f38e to
3b5ae1e
Compare
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.
Part of #246. Do not merge before #979 and #753.
Actor networking is IPv4-only, so an actor on a dual-stack worker pod cannot
reach an IPv6-only destination at all.
SetupActorNetworknow assigns thefd00:169:254::/126counterparts of the existing point-to-point pair to bothends of the actor veth, installs an IPv6 default route in the interior netns,
and adds the matching rules to the inet-family actor table.
Whether the actor gets IPv6 is decided once in the worker pod netns and carried
into the interior one, which is created fresh and so always reports IPv6
available whatever the cluster's families are. Both halves have to hold: the pod
needs a global IPv6 address of its own, and the veth has to accept an IPv6
address — IPv4-only GKE sets
disable_ipv6and netlink then rejects theassignment with EPERM.
Stacked on #1116
The first commit here is #1116, which moves the actor nftables table from the
ipfamily toinetwith no behaviour change on IPv4. Review it there; GitHubcannot target a base branch that lives on a fork, so it shows up in this diff
too. Only the second commit is new in this PR.
Dependencies
net.ipv6.conf.all.forwarding=1in the pod netns, the newdefault route is a black hole. Measured: 100% packet loss in a netns replica
at
all.forwarding=0, 0% at=1.atunnelrecovers the destination withgetsockopt(SOL_IP, SO_ORIGINAL_DST)into a 16-byteRawSockaddrInet4, so anIPv6 connection REDIRECTed into the tunnel is accepted and then dies with no
usable CONNECT authority. That function's own TODO asks for the IPv6 variant
"when actor veth setup gains dual-stack support" — which is this PR.
Even with both, dual-stack egress is red: #1089, caught by the test in #1104.
This lands the address, not a working dual-stack egress path.
Testing
Green CI here means the family gate stays shut on an IPv4 cluster, not that
the IPv6 datapath works. There is no dual-stack CI lane, so none of the IPv6
behaviour below is exercised there.
TestSetupActorNetworkIPv6Gateis the truth table, root-gated, five cases.Reverting either half of the gate fails exactly one case and nothing else, and
reverting
IFA_F_NODADfails exactly the two that expect IPv6.TestActorNftablesRuleExprspins the IPv6 rule expressions alongside the IPv4ones, and needs no root.
TestSetupActorNetworkInstallsEgressRedirectchecksthe kernel takes both redirects in one
inetnat chain; the IPv6 rule isinstalled whatever the gate decided, and simply matches nothing when the actor
has no IPv6 source address.
Not covered
eth0is not observable under gVisor, so only the netns-levelsetup is asserted.
IPv6 even where the host veth has one. There is a
TODO(#246)at that callsite.
🤖 Generated with Claude Code