Skip to content

Host mode without -lan, and a CLAT for this host's IPv4 - #7

Merged
lqs merged 3 commits into
mainfrom
clat
Oct 13, 2026
Merged

lqs merged 3 commits into
mainfrom
clat

Conversation

@lqs

@lqs lqs commented Oct 11, 2026 •

Copy link
Copy Markdown
Owner

Summary

Three commits.

Configure this host alone when no -lan is given

  • Without -lan, sixup takes the WAN addresses and routes and builds the IPv4 tunnel with its source NAT for the machine it runs on, and forwards nothing: no forwarding sysctls, no RA, DHCPv6 server, NDP proxy, PCP or border firewall. -nat64 is refused.
  • The WAN's accept_ra is turned off, since a host that forwards nothing would take the RA itself beside sixup.
  • On a line that delegates a prefix but puts none on the link, the host takes its address in the first /64 of the delegation the ISP does not keep for the link (RFC 6603). A change of that /64 alone is now published.

Run a CLAT for this host's IPv4 when the line announces a PREF64

  • Router or not, when the upstream RA carries a PREF64 (RFC 8781), sixup builds sixup-clat with 192.0.0.4 (RFC 7335) and an IPv4 default route of last resort (metric 4294967295) through it; any other IPv4 route comes first.
  • bpf/clat.c translates on that device's egress to the host's CLAT address and sends the IPv6 out of the WAN with bpf_redirect_neigh; on the WAN's ingress it takes what the NAT64 sends that address, and the errors routers on the IPv6 path send it, as from 192.0.0.8 (RFC 7600). Everything else on the WAN passes untouched.
  • The CLAT address is stable in the WAN subnet or the SLAAC prefix, kept on the WAN with a preferred lifetime of 0 like a tunnel endpoint. The WAN has to be an Ethernet link; Linux 6.6 or later.
  • The translation of bpf/nat64.c moves to bpf/xlat.h, shared with its own address mapping and delivery per program. It now keeps skb->csum right for packets received with CHECKSUM_COMPLETE, as the CLAT's are. The slow path takes the mapping of the program it serves.
  • CI regenerates the BPF objects with a pinned toolchain (golang:1.27-alpine3.24) and fails when they differ from those committed; AGENTS.md gives the same command.
  • The amd64 binary is 5,955,744 bytes, within the 6 MiB limit.

Test a PLAT and a CLAT of sixup's end to end, and wait for their teardown

  • Two sixup processes in namespaces of their own: one routes a LAN with -nat64 bpf, and the other, behind it without -lan, learns the PREF64 from its RA alone and reaches an IPv4-only server through its CLAT.
  • main now waits for the BPF translators to take down their devices and routes before it returns, rather than leaving it to the half second it sleeps.

Test plan

  • go build ./... && go vet ./... && go test ./..., and go build ./... for linux, darwin, freebsd and mips
  • GOOS=linux golangci-lint run, with and without -tags integration
  • The first commit on its own: build, vet, lint, unit tests and the host-mode integration test
  • fail2pass: the host-mode tests fail on main and pass here
  • Docker integration suite: sixup without -lan against the kernel; the CLAT program through BPF_PROG_TEST_RUN; a host with no IPv4 reaching an IPv4 server through its CLAT and sixup's own NAT64 in another namespace (ping, TCP, UDP with fragments, a fragmented ping through both slow paths, PMTU from the NAT64 and from a router on the IPv6 path, drop reports, teardown); all NAT64 tests after the refactor; new code at 100% coverage
  • Two full sixups end to end, the PREF64 learnt from the upstream's RA: ping, TCP, a fragmented ping, the CLAT reported and gone on exit; three runs in a row
  • go generate in the pinned image reproduces the committed files
  • CHECKSUM_COMPLETE on a real NIC, which veth does not produce
  • On a real line that announces a PREF64

🤖 Generated with Claude Code

lqs and others added 2 commits October 12, 2026 00:59
Without -lan, sixup takes the WAN addresses and routes and builds the IPv4
tunnel with its source NAT for the machine it runs on, and forwards
nothing: no forwarding sysctls, no RA, DHCPv6 server, NDP proxy, PCP or
border firewall, and -nat64 is refused. The WAN's accept_ra is turned
off, since a host that forwards nothing would take the RA itself beside
sixup. On a line that delegates a prefix but puts none on the link, the
host takes its address in the first /64 of the delegation the ISP does
not keep for the link (RFC 6603), and a change of that /64 alone is now
published.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When the upstream RA carries a PREF64 (RFC 8781), router or not, sixup
builds sixup-clat with 192.0.0.4 (RFC 7335) and an IPv4 default route of
last resort through it. bpf/clat.c translates on its egress to the host's
CLAT address and sends the IPv6 out of the WAN with bpf_redirect_neigh,
and on the WAN's ingress takes what the NAT64 sends that address, and
the errors routers on the IPv6 path send it, from 192.0.0.8 (RFC 7600).
The address is a stable one in the WAN subnet or the SLAAC prefix, kept
on the WAN by its address manager with a preferred lifetime of 0, like a
tunnel endpoint. The WAN has to be an Ethernet link.

The translation of bpf/nat64.c moves to bpf/xlat.h, which both programs
include with their own address mapping and delivery. It now keeps
skb->csum right for packets received with CHECKSUM_COMPLETE, as the
CLAT's are. The slow path takes the mapping of the program it serves.

CI regenerates the BPF objects with a pinned toolchain and fails when
they differ from those committed; AGENTS.md gives the same command.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@codecov-commenter

codecov-commenter commented Oct 11, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.18%. Comparing base (61a6b26) to head (f516ca0).

Additional details and impacted files
@@            Coverage Diff             @@
##             main       #7      +/-   ##
==========================================
+ Coverage   97.06%   97.18%   +0.11%     
==========================================
  Files          33       34       +1     
  Lines        7291     7455     +164     
==========================================
+ Hits         7077     7245     +168     
+ Misses        214      210       -4     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

…down

Two sixups in namespaces of their own: one routes a LAN with -nat64 bpf,
and the other, behind it without -lan, learns the PREF64 from its RA alone
and reaches an IPv4-only server through its CLAT, with TCP and a
fragmented ping through both slow paths.

main now waits for the BPF translators to take down their devices and
routes before it returns, rather than leaving it to the half second it
sleeps.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@lqs
lqs merged commit 820f682 into main Oct 13, 2026
14 checks passed
@lqs
lqs deleted the clat branch October 13, 2026 13:12
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.

2 participants