Skip to content

net: forward-port FreeBSD IPv6 stack with SLAAC and DHCPv6 autoconfiguration - #1442

Open
gburd wants to merge 38 commits into
cloudius-systems:masterfrom
gburd:pr/ipv6-forward-port
Open

net: forward-port FreeBSD IPv6 stack with SLAAC and DHCPv6 autoconfiguration#1442
gburd wants to merge 38 commits into
cloudius-systems:masterfrom
gburd:pr/ipv6-forward-port

Conversation

@gburd

@gburd gburd commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

This forward-ports the FreeBSD IPv6 stack to current OSv and adds the boot-time
autoconfiguration a unikernel needs (SLAAC and a DHCPv6 client), so an
IPv6-only or dual-stack application works out of the box. All IPv6 code is
compile-time gated so IPv6-less builds stay small.

What this does

  • netinet6 stack — imports the FreeBSD 9 netinet6 sources (ip6, icmp6,
    nd6, in6_*, udp6, raw_ip6, mld6, frag6, dest6, route6, scope6) adapted for
    OSv. 45 new files under bsd/sys/netinet6/, plus the shared eth/tcp/udp code
    taught about IPv6.
  • OSv fast paths for IPv6 — the net-channel classifier, LRO, and RCU
    connection lookup are extended to IPv6/TCP so v6 gets the same in-kernel fast
    path as v4 (mirrors the existing IPv4 optimizations).
  • Linux socket compatibility — 43 IPV6_* socket options translated in the
    Linux compat layer (V6ONLY, PKTINFO, HOPLIMIT, TCLASS, MULTICAST_*,
    JOIN/LEAVE_GROUP, etc.), plus getifaddrs()/if_nameindex() over a NETLINK
    socket and AAAA-aware getaddrinfo.
  • SLAAC — OSv has no rtsold, so the kernel now sends its own Router
    Solicitation (nd6_rs_output) at interface bring-up and processes the Router
    Advertisement to form a global address. Also fixes an ordering bug where
    ND6_IFF_ACCEPT_RTADV was never set on interfaces that attached before the
    accept-RA flag was enabled at boot.
  • DHCPv6 client — a minimal RFC 8415 stateful client
    (bsd/porting/dhcp6.cc): SOLICIT/ADVERTISE/REQUEST/REPLY with DUID-LLT and
    IA_NA, plus Rapid Commit (two-message SOLICIT/REPLY). The loader chooses SLAAC
    vs DHCPv6 from the RA flags: an Autonomous-prefix RA (radvd, QEMU SLIRP) uses
    SLAAC, a Managed-flag RA (many enterprise/cloud networks) uses DHCPv6. Works
    with standard servers (isc-dhcp, kea, dnsmasq).
  • Boot options--ipv6 selects the IPv6-only boot path (skips the
    blocking DHCPv4 wait); static --ip/--defaultgw/--nameserver already
    accept v6 addresses.

Compile-time gating

Everything is behind conf-INET6 (the FreeBSD INET6 macro) and, for the
DHCPv6 client, conf_networking_dhcp6. A conf-INET6=0 build compiles and
links with no IPv6 code and no dangling references (verified), saving ~2.7 MB.
The default remains conf-INET6=1.

Testing

  • tst-tcp-v6-smoke (new, no boost): IPv6 + IPv4 loopback TCP echo, concurrent
    dual-stack, and the socket-option/multicast/getaddrinfo API surface. Passes.
  • tst-ifaddr-dump (new): dumps interface addresses to observe SLAAC/DHCPv6.
  • tst-ipv6-internet (new, manual): DNS AAAA + IPv6 TCP + HTTP to a real host.
  • Existing tst-tcp-v6, tst-pktinfo, tst-socket-timestamp build and run.

Validated on KVM/QEMU (SLIRP ipv6=on → SLAAC) and on a bare-metal host:

  • SLAAC forms a global address from a Router Advertisement.
  • The DHCPv6 client obtains a global address from a standard dnsmasq DHCPv6
    server (SOLICIT with Rapid Commit → REPLY → address bound), confirming the
    client is standards-compliant and not tied to any specific network.
  • IPv6-only boot (no IPv4 on the link) reaches the IPv6 internet.

Notes for review

  • The history preserves the original ipv6-branch commits (FreeBSD import →
    OSv adaptation → net-channel → Linux compat → getifaddrs/netlink → tests) so
    authorship is intact, with the SLAAC + DHCPv6 work as the final commit. Happy
    to squash if preferred.
  • The DHCPv6 client lives in bsd/porting/ (not core/) because it needs the
    BSD-internal netinet6 headers, which cannot coexist with the musl/glibc
    in6_addr that boost::asio pulls in; the DNS setter is reached through a
    small C-linkage shim in libc/network/__dns.cc.

cmyers-spirent and others added 30 commits December 11, 2019 12:58
Signed-off-by: Charles Myers <charles.myers@spirent.com>
Message-Id: <1533610192-4019-2-git-send-email-charles.myers@spirent.com>
Signed-off-by: Charles Myers <charles.myers@spirent.com>
Message-Id: <1533610192-4019-3-git-send-email-charles.myers@spirent.com>
Signed-off-by: Charles Myers <charles.myers@spirent.com>
Message-Id: <1533610192-4019-4-git-send-email-charles.myers@spirent.com>
Signed-off-by: Charles Myers <charles.myers@spirent.com>
Message-Id: <1533610192-4019-5-git-send-email-charles.myers@spirent.com>
Signed-off-by: Charles Myers <charles.myers@spirent.com>
Message-Id: <1533610192-4019-6-git-send-email-charles.myers@spirent.com>
classifier changed to object pointer instead of embedded in struct ifnet
to reduce compile dependencies.
There was also issue with including ip6.h in this file, but that could probably
be fixed with struct ip6_hdr forward delcaration and including in6.h instead.

Signed-off-by: Charles Myers <charles.myers@spirent.com>
Signed-off-by: Waldemar Kozaczuk <jwkozaczuk@gmail.com>
Message-Id: <1533610192-4019-7-git-send-email-charles.myers@spirent.com>
lltable_foreach(), lltable_foreach_lle() added to support NETLINK sockets

Signed-off-by: Charles Myers <charles.myers@spirent.com>
Message-Id: <1533610192-4019-8-git-send-email-charles.myers@spirent.com>
Signed-off-by: Waldemar Kozaczuk <jwkozaczuk@gmail.com>
Signed-off-by: Charles Myers <charles.myers@spirent.com>
Message-Id: <1533610192-4019-9-git-send-email-charles.myers@spirent.com>
Signed-off-by: Charles Myers <charles.myers@spirent.com>
Message-Id: <1533610192-4019-10-git-send-email-charles.myers@spirent.com>
Use AF_INET instead of AF_UNIX

Signed-off-by: Charles Myers <charles.myers@spirent.com>
Message-Id: <1533610192-4019-11-git-send-email-charles.myers@spirent.com>
…socket

libc code is from musl library with minor change to prevent partial reads if faster than sender.

Signed-off-by: Charles Myers <charles.myers@spirent.com>
Message-Id: <1533610192-4019-12-git-send-email-charles.myers@spirent.com>
Signed-off-by: Charles Myers <charles.myers@spirent.com>
Message-Id: <1533610192-4019-13-git-send-email-charles.myers@spirent.com>
…ESTAMP

l_size_t in linux.h was not correct size for 64-bit long

Signed-off-by: Charles Myers <charles.myers@spirent.com>
Message-Id: <1533610192-4019-14-git-send-email-charles.myers@spirent.com>
…ESTAMP

Signed-off-by: Charles Myers <charles.myers@spirent.com>
Signed-off-by: Waldemar Kozaczuk <jwkozaczuk@gmail.com>
Message-Id: <1533610192-4019-16-git-send-email-charles.myers@spirent.com>
Signed-off-by: Charles Myers <charles.myers@spirent.com>
Signed-off-by: Waldemar Kozaczuk <jwkozaczuk@gmail.com>
Message-Id: <1533610192-4019-17-git-send-email-charles.myers@spirent.com>
Signed-off-by: Waldemar Kozaczuk <jwkozaczuk@gmail.com>
Signed-off-by: Waldemar Kozaczuk <jwkozaczuk@gmail.com>
Signed-off-by: Waldemar Kozaczuk <jwkozaczuk@gmail.com>
# Conflicts:
#	modules/tests/Makefile
Signed-off-by: Waldemar Kozaczuk <jwkozaczuk@gmail.com>
Signed-off-by: Waldemar Kozaczuk <jwkozaczuk@gmail.com>
Signed-off-by: Waldemar Kozaczuk <jwkozaczuk@gmail.com>
Signed-off-by: Waldemar Kozaczuk <jwkozaczuk@gmail.com>
# Conflicts:
#	modules/tests/Makefile
Signed-off-by: Waldemar Kozaczuk <jwkozaczuk@gmail.com>
# Conflicts:
#	Makefile
#	modules/tests/Makefile
Signed-off-by: Waldemar Kozaczuk <jwkozaczuk@gmail.com>
Signed-off-by: Waldemar Kozaczuk <jwkozaczuk@gmail.com>
Signed-off-by: Waldemar Kozaczuk <jwkozaczuk@gmail.com>
Signed-off-by: Waldemar Kozaczuk <jwkozaczuk@gmail.com>
Signed-off-by: Waldemar Kozaczuk <jwkozaczuk@gmail.com>
Signed-off-by: Waldemar Kozaczuk <jwkozaczuk@gmail.com>
@gburd

gburd commented Jul 13, 2026

Copy link
Copy Markdown
Contributor Author

Follow-up (deliberately not in this PR): IPv6 inherits OSv's high-value fast paths — net channels (classify_ipv6_tcp, tcp_net_channel_ipv6_packet), LRO (ETHERTYPE_IPV6 in tcp_lro.cc), the RCU connection classifier, and the AF-agnostic TCP header-prediction path. What it does not yet inherit is virtio-net hardware checksum + TSO offload for IPv6: drivers/virtio-net.cc hard-codes the offload switch to ETHERTYPE_IP (TX builds only GSO_TCPV4, RX has a literal // How come - no support for IPv6?!), and if_hwassist/if_capabilities advertise IFCAP_TSO4 but not IFCAP_TSO6 or the CSUM_*_IPV6 bits.

Net effect: IPv6 is correct but computes checksums in software and doesn't get large-segment TSO, so high-throughput v6 sends/receives use more CPU than v4. This is a self-contained, driver-local performance follow-up (extend the TX/RX offload switch to ETHERTYPE_IPV6CSUM_TCP_IPV6/GSO_TCPV6, add IFCAP_TSO6), best done and benchmarked as its own PR rather than expanding this correctness-focused one.

gburd added 5 commits July 31, 2026 08:58
Merge the upstream ipv6 branch (FreeBSD 9 netinet6 import + OSv glue) and add
boot-time IPv6 autoconfiguration:

- netinet6 stack (ip6, icmp6, nd6, in6_*, udp6, raw_ip6, mld6) behind conf-INET6
- OSv net-channel fast path, LRO, RCU classifier extended for IPv6/TCP
- Linux socket-option compat for 43 IPV6_* options; getifaddrs/netlink v6
- SLAAC: kernel Router Solicitation (nd6_rs_output) + accept-RA per-interface
  flag fix so a unikernel (no rtsold) autoconfigures from Router Advertisements
- DHCPv6 client (RFC 8415 stateful: SOLICIT/ADVERTISE/REQUEST/REPLY, DUID-LLT,
  IA_NA) triggered when the RA sets the Managed (M) flag, e.g. AWS VPC
- loader picks SLAAC vs DHCPv6 from RA flags; --ipv6 for IPv6-only boot
- all IPv6 code compile-gated (conf-INET6, conf_networking_dhcp6)
- tests: tst-tcp-v6-smoke, tst-ipv6-internet, tst-ifaddr-dump
…r and DHCPv6 client

Security hardening for the two network-reachable memory-safety issues in the
new IPv6/DHCPv6 code (found in audit):

1. classify_ipv6_tcp() OOB read (core/net_channel.cc). ip6_lasthdr_nofrag only
   guarantees the base IPv6 header + extension headers fit; it did NOT
   guarantee a full TCP header is present and contiguous, yet the code then
   dereferenced tcp_hdr (th_flags at +13, ports at +0..3) on the lock-free RX
   fast path. A crafted IPv6/TCP frame (short, or with extension headers
   pushing the L4 offset toward the end, or split across a mergeable-buffer
   mbuf chain via GRO/jumbo) could read out of bounds in the driver softirq.
   Require nxt_off + sizeof(tcphdr) <= mh_len before touching the TCP header;
   otherwise fall through to the slow path (tcp_input), which pulls up.

2. DHCPv6 parse() OOB read across an mbuf chain (bsd/porting/dhcp6.cc). The
   option walk bounded off against pkthdr.len (whole chain) while reading via
   mtod (first mbuf only); udp6_input makes only the UDP header contiguous. A
   malicious on-link DHCPv6 server (DHCPv6 is unauthenticated; the client is
   auto-started on a Managed-flag RA) sending a >4 KiB reply on a jumbo/GRO
   link becomes an mbuf chain and the walk reads past the first mbuf. Clamp
   blen to the contiguous first-mbuf length (a well-formed reply is far smaller
   than one RX buffer).

3. DHCPv6 unbounded state growth (DoS). process_packet() parsed every packet
   even when BOUND, and parse() appended to _dns/_server_duid, so an on-link
   attacker who observed our SOLICIT xid could spray REPLYs and grow those
   without bound. Ignore packets outside SOLICITING/REQUESTING, and reset the
   per-reply learned state only after the xid matches (so a spoofed packet
   cannot wipe good state and repeated replies do not accumulate).

Verified: IPv6 loopback + dual-stack TCP smoke still passes.
This branch predated the merges of the tgkill/rt_sigtimedwait (osv_sigtimedwait)
and pthread_mutex_timedlock work; its diff vs master had begun to revert them
(and delete tst-signal-fills.cc).  Restore libc/signal.cc, libc/pthread.cc,
linux.cc and tst-signal-fills.cc to master and re-add tst-signal-fills.so to the
test list, so this branch touches only the IPv6 forward-port.
@gburd
gburd force-pushed the pr/ipv6-forward-port branch from ad0615e to b06d3df Compare July 31, 2026 13:00
@gburd

gburd commented Aug 7, 2026

Copy link
Copy Markdown
Contributor Author

Heads-up before merging: this branch was last synced against an older master (via a merge commit), so although GitHub reports it mergeable, its tree predates several tests that have since landed (tst-iovcnt-guard, tst-splice, tst-fs-syscalls, tst-membarrier, tst-mremap) and it still edits the tests := literal in modules/tests/Makefile directly. A clean rebase onto current master hits genuine net-stack conflicts (net_channel.cc, tcp_input.cc, routecache.hh) from the merged IPv6 net-channel/classifier changes, so this needs a real forward-port rather than a mechanical Makefile fixup. I will rebase it onto current master and convert the test entries to standalone tests += lines (per #1469) before it is ready; please hold this one until then. The smaller syscall/mm/ext4 PRs are already converted and ready.

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.

3 participants