Skip to main content

IPv6

IPv6 is a first-class peer of IPv4 across the dataplane, IPAM, Kubernetes reconciliation, L2 announcement and BGP. A VIP's backends must all share its address family; mixing v4 and v6 behind one VIP is rejected.

Dataplane

XDP and TCX parse, forward and checksum-rewrite IPv6 TCP and UDP in DSR, full-NAT and L3 DSR, including Maglev, session affinity, draining, port ranges, per-VIP rate limiting and ICMPv6 error steering. Behaviour mirrors IPv4 except where the protocols differ:

  • No IP-header checksum. Only the L4 checksum is updated, and it covers the 128-bit addresses.
  • UDP checksum is never "unset". IPv4 uses zero to mean "no checksum"; over IPv6 that is forbidden, so the checksum is always maintained.
  • Extension headers. Hop-by-Hop and Destination Options headers are stepped over to reach the TCP or UDP header (up to four, 248 bytes in total) and stay in the packet. A Routing header, AH, ESP or a longer chain is passed through untouched.
  • Fragments. The first fragment carries the ports and is balanced; its backend is remembered (keyed by source, destination, the Fragment header's identification and protocol) and later fragments follow it. In NAT the backend's fragmented replies are un-NATed on the way out. A fragment that arrives before its first is not steered.
  • ICMPv6 errors ("packet too big", "time exceeded", "parameter problem") quoting a flow are sent to the backend that owns it, so path-MTU discovery keeps working behind a VIP. Only when ICMPv6 directly follows the IPv6 header.
  • L3 DSR wraps an IPv6 VIP's packets in IPv6-in-IPv6 or GRE over IPv6 (tunnelSource6), and answers an oversize packet with "packet too big".

Details and limits: the runbook.

sudo ./bin/rivorad -config config/examples/single-vip-ipv6-nat.yaml -bpf-dir bpf
sudo ./bin/rivorad -config config/examples/single-vip-ipv6-dsr.yaml -bpf-dir bpf
interface: eth0
vips:
- address: fd00:77::100
port: 80
protocol: tcp
mode: nat # or dsr (backends need the VIP on lo and a MAC)
backends:
- {address: fd00:77::11, port: 8080}

For DSR labs, bind the VIP on the backend's lo with preferred_lft 0, and give the balancer its VIP with preferred_lft 0 too, so its own health probes are never sourced from an address the backend also owns.

IPAM and AddressPool

AddressPool.spec.addresses takes IPv4 or IPv6 CIDRs, ranges (a-b) or single addresses.

  • Small prefixes (host bits of 16 or fewer, such as a /112 or tighter) are expanded into concrete addresses, like an IPv4 /24.
  • Large prefixes (more than 16 host bits, typically a /64) are kept sparse: the controller allocates by randomising host bits inside the prefix instead of enumerating 2^64 addresses. Capacity accounting uses a capped virtual size so the status stays useful.
apiVersion: rivora.zyvor.dev/v1alpha1
kind: AddressPool
metadata: {name: v6}
spec:
addresses: ["2001:db8:1::/64"] # sparse
autoAssign: true

Kubernetes dual-stack

  • A Service or Gateway may carry IPv6 (or both) addresses allocated from IPv6 (or dual) pools.
  • EndpointSlice backends are filtered to the VIP's address family: a v6 VIP programs only v6 endpoints.
  • A dual-stack Service yields one programmed VIP per family, each Maglev-partitioned independently.

NDP speaker

The L2 speaker answers ARP and NDP behind one cluster-wide Lease: gratuitous ARP and replies for IPv4 VIPs; unsolicited Neighbor Advertisements, solicited-node multicast membership and replies to Neighbor Solicitations for IPv6 VIPs. It falls back to ARP only if the interface has no IPv6 link-local address (a v4-only lab NIC). Only NAT-mode VIPs are announced: a DSR backend owns its VIP on the wire.

BGP /128

Each healthy IPv6 VIP is advertised as a /128 (RF_IPv6_UC), with bgp.ipv6NextHop as the next hop: set it, or IPv6 VIPs are skipped with an error. Sessions negotiate both IPv4 and IPv6 unicast, so one peer carries both. See BGP.

Maps

Only maps keyed or valued by a raw address have IPv6 siblings (vip_map, backend_map, connection_affinity_map, nat_reverse_map, rl_buckets_map, the fragment tables). The rest are shared. See Architecture.

Verification

LayerHow
Dataplane DSR and NAT, Maglev, failoverscripts/selftest-ipv6.sh
Session affinity and drop counters over IPv6scripts/selftest-ipv6-policy.sh
Extension headers and fragmented UDP, NAT and DSRscripts/selftest-ipv6-ext.sh
L3 DSR and its oversize-packet answerscripts/selftest-l3dsr.sh
ICMPv6 path-MTU steering, VLANscripts/selftest-edgecases.sh
Checksums (offload off, so every packet is verified)scripts/selftest-checksum.sh
NDP solicit to advertisescripts/selftest-ndp.sh
Sparse IPAMgo test ./internal/ipam
BGP /128go test ./internal/bgp
Helm IPv6 pool and next hopCI helm job

Not yet verified live: end-to-end dual-stack Service traffic on a cluster, and BGP IPv6 peering against a real router.