IPv6 networking: SLAAC, DHCPv6, Router Advertisements#

Why#

This guide targets a dual-stack (IPv4 + IPv6) accessible server, most of the configuration examples shown (nftables, chrony, nginx) already handle both address families. But IPv6 has a fundamentally different address assignment mechanism from IPv4, with a classic trap that can leave a server apparently configured but unreachable over IPv6, without any error message clearly pointing to it.

How IPv6 address assignment works#

Unlike IPv4, where DHCP systematically provides address, gateway, and DNS through a single mechanism, IPv6 splits these responsibilities across several mechanisms that can be combined:

MechanismProvidesAddress control
SLAAC (Stateless Address Autoconfiguration, RFC 4862)Address computed by the client itself from the announced /64 prefixNo, derived from the interface (or random with privacy extensions, RFC 4941)
Stateful DHCPv6 (RFC 8415)Address assigned by a server, from a pool or a reservationYes, a fixed address is possible via a DUID reservation
Router Advertisement (RA)Announces the network prefix and the gatewayn/a (the RA assigns no address)

A commonly missed point, and the source of a classic trap: stateful DHCPv6 never provides the default route. The IPv6 gateway always comes from Router Advertisements, never from DHCPv6. A DHCPv6 server active without RAs on the same network segment hands out addresses… that nobody can route traffic to.

The RA carries two flags that determine client behavior:

M (Managed)O (Other)A (Autonomous, in the prefix)Mode
001Pure SLAAC
011SLAAC + stateless DHCPv6 (DNS only)
110Pure stateful DHCPv6, fixed address + gateway via RA
111Stateful DHCPv6 + SLAAC in parallel

For a server where a fixed, predictable address is wanted (a necessary condition for a stable AAAA DNS record), Managed mode (M=1, O=1, A=0) is the right choice: DHCPv6 provides an address reserved by DUID, and RAs (enabled in parallel on the router) provide the gateway.

Pitfalls to avoid#

A stateful DHCPv6 server active without Router Advertisements on the same segment is an inconsistent state that only reveals itself in use. The symptom is confusing: the router has a DHCPv6 reservation for the server’s address, public DNS has an AAAA record pointing to it, and yet the server has no global IPv6 address, only its fe80:: link-local one. Legitimate incoming IPv6 traffic arrives (visible in tcpdump as repeated Neighbor Solicitations, “who has this address?”), but nobody answers. Cause: without an active RA, the client doesn’t even trigger a DHCPv6 request: it’s the RA’s M=1 flag that triggers this client-side behavior (RFC 4861 §4.6.2). A DHCPv6 pool configured on the router but never solicited is therefore useless.

  • Confusing “the server doesn’t respond to outbound ping6” with “IPv6 doesn’t work”. An outbound ICMPv6 echo-request timing out can simply mean it’s filtered on egress by the upstream firewall (a common default configuration), which doesn’t prevent external IPv6 clients from reaching the server over TCP (HTTPS) or UDP (NTP) at all. Always check actual inbound traffic (nginx logs, chronyc serverstats) before concluding IPv6 is fully down.
  • Enabling SLAAC alongside stateful DHCPv6 without an identified need (Assisted mode, A=1). The server ends up with two simultaneous global IPv6 addresses, one derived from EUI-64/random via SLAAC, one fixed via DHCPv6. Functionally correct, but it complicates DNS record consistency and diagnosis. Absent a specific need, pure Managed mode (A=0) remains preferable.

Verify#

# Currently assigned IPv6 addresses
ip -6 addr show

# Actual presence of Router Advertisements on the segment
# 134 = the ICMPv6 "Router Advertisement" type (RFC 4861), byte 40 being
# the first byte of the ICMPv6 header right after the fixed 40-byte IPv6
# header
timeout 30 tcpdump -i end0 -n -v 'icmp6 and ip6[40] = 134'
# Should capture at least one message within 30 seconds (routers send
# periodic RAs, typically every few minutes, in addition to responding
# to solicitations)

# Default IPv6 route actually learned
ip -6 route show default

# End-to-end test from a third-party machine
curl -6 https://time.example.org/

If ip -6 addr show only shows an fe80::/10 (link-local) address despite a DHCPv6 configuration supposedly providing a global address, the diagnosis should immediately focus on missing RAs on the router side, that’s the most common cause of this specific symptom.


📚 Going further

  • RFC 8200 defines IPv6 itself (128-bit addressing).
  • RFC 4862 specifies SLAAC (Stateless Address Autoconfiguration).
  • RFC 8415 specifies DHCPv6, including the stateful mechanism and the DUID (DHCP Unique Identifier) types used for reservations.
  • RFC 4861 (Neighbor Discovery Protocol) defines the format and semantics of Router Advertisements, including the M/O flags and the Prefix Information options (A flag).
  • RFC 4941 introduces privacy extensions for SLAAC (random temporary addresses rather than MAC-derived ones), relevant for clients but not desirable for a server whose address needs to stay stable for DNS. Obsolete since 2021, superseded by RFC 8981 (Temporary Address Extensions…), which covers the same mechanism.