Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

IPv6 Address Autoconfiguration — SLAAC and DHCPv6

“In IPv4, you either typed in your address or you asked a server. In IPv6, you can be told a prefix, ask for one, or compute your own — and the same host may do all three simultaneously.”

1. The Two Models

IPv6 gives a host three independent ways to acquire an address:

  1. Manual — operator-configured, identical in spirit to a static IPv4 address.
  2. SLAAC (StateLess Address AutoConfiguration, RFC 4862) — the host listens for Router Advertisements on the link, learns the prefix, and combines it with an interface identifier (IID) it generates locally. No server tracks the allocation; uniqueness is verified by DAD.
  3. DHCPv6 (RFC 8415, obsoletes RFC 3315) — a server explicitly leases an address or other configuration back to the requesting client. DHCPv6 has both stateful (server tracks lease) and stateless (server only hands out non-address options like DNS) modes.

The RA flags M and O (RFC 4861 §4.2) tell a host which combination to use:

RA M-flagRA O-flagHost behaviour
00SLAAC for address; assume DNS comes via RA’s RDNSS option (RFC 8106)
01SLAAC for address; query DHCPv6 for DNS and other config only (stateless DHCPv6)
10Use DHCPv6 for address and config (stateful) — operator disables SLAAC by setting A=0 on the prefix option
11Use DHCPv6 for everything

2. SLAAC — the stateless path

2.1 The mechanism

SLAAC is defined entirely in RFC 4862 and builds on RFC 4861 NDP. The complete sequence on a host that just came up on an Ethernet link:

   Host boot
        |
        v
   (1) Generate link-local: fe80:: + 64-bit interface identifier
        |
        v
   (2) DAD on link-local (RFC 4861 §7.3)
        |
        v
   (3) Send 3× RS to ff02::2 (All-Routers) at 4s intervals
        |
        v
   (4) Router responds with RA containing one or more
        Prefix Information options (PI):
           prefix=2001:db8:0:1::/64
           valid-lifetime=86400 s (24h, default)
           preferred-lifetime=14400 s (4h, default)
           A=1 (autonomous, SLAAC-eligible)
           L=1 (on-link, prefix is reachable on this L2)
        |
        v
   (5) For each PI with A=1, host forms a global address:
           global = prefix(64) | interface_id(64)
        |
        v
   (6) DAD on the new global
        |
        v
   (7) Address becomes PREFERRED for preferred-lifetime seconds,
        then DEPRECATED for the remainder of valid-lifetime,
        then DELETED. RA refreshes lifetimes periodically.

2.2 Generating the Interface Identifier — three RFCs

The 64-bit IID can be generated by any of:

EUI-64 from a 48-bit MAC (RFC 4291 Appendix A, deprecated practice):

   MAC: 00:1a:2b:3c:4d:5e
   (1) Insert ff:fe in the middle:
        00:1a:2b:ff:fe:3c:4d:5e
   (2) Toggle the U/L bit (bit 1 of first octet):
        02:1a:2b:ff:fe:3c:4d:5e
   (3) Resulting IID: 021a:2bff:fe3c:4d5e
   (4) Global address: 2001:db8:0:1:21a:2bff:fe3c:4d5e

The U/L bit (Universal/Local) is set so the IID is globally unique when the MAC was assigned by the IEEE (universal); the I/G (Individual/Group) bit must be 0 because the address is unicast.

EUI-64 has a known privacy problem: the IID never changes when the host moves networks, so the same MAC yields the same low 64 bits on every prefix, making the host trivially trackable across networks. RFC 7217 and RFC 4941 fix this.

RFC 7217 — stable, semantically opaque addresses:

A stable-per-network address that does not reveal the MAC. The host computes the IID as a hash (SHA-256 truncated to 64 bits) over the concatenation of:

   Hash inputs:
      - the prefix (so the IID differs per network)
      - the network interface's stable secret (e.g., a per-host random key)
      - the interface's hardware address (or other stable identifier)
      - the DAD counter (so if a collision is detected, the host can recompute)
   Output: 64-bit IID with U/L bit forced to "local"

The host keeps the same IID every time it rejoins the same prefix (useful for inbound connections to a stable server) but is a different IID on every other prefix. This is the recommended default in modern OSes (Linux addr_gen_mode 1, macOS, Windows 10+).

RFC 4941 — privacy extensions, temporary addresses:

For clients that want outbound-only anonymity, the host generates additional temporary IIDs with a fresh random value, rotating them frequently:

   Stable  addr: 2001:db8:0:1:021a:2bff:fe3c:4d5e   (for inbound: SSH, servers)
   Temp #1 addr: 2001:db8:0:1:7f4a:9b3c:8e1d:2a5f   (for outbound: web browsing)
                       ↑ regenerated every preferred-lifetime seconds
   Temp #2 addr: 2001:db8:0:1:ab12:cd34:ef56:0123   (new one ready to take over)

The address-generation pseudocode (RFC 4941 §3.2.2) takes 5–7 random bytes and a token computed from the stable IID, and uses them to derive a new random IID each time the previous one expires its preferred lifetime. The deprecated temporary address is kept for the valid lifetime so existing TCP connections don’t break, but new connections use the new address.

Privacy extensions are the default in modern mobile OSes (Android 6+, iOS) and in Windows; they are enabled in Linux via sysctl net.ipv6.conf.*.use_tempaddr=2. The flip-side: when an operator expects the same address to come back (e.g., DNS reverse mapping, ACLs), they should deploy RFC 7217 stable privacy addresses instead, so inbound services work.

2.3 Lifetimes and address deprecation

Every address has two clocks running in parallel:

   [assignment]
        |
        |<--- preferred-lifetime (default 4h)  --->|
        |                                        |
        |        PREFERRED (active for new + old)|
        |                          DEPRECATED (active for old connections only)
        |<------------- valid-lifetime (default 24h) --------->|
                                                            DELETED
  • A PREFERRED address is fair game for new outgoing connections; getaddrinfo() will prefer it.
  • A DEPRECATED address may still be used for existing connections (so a TCP socket with that source continues to work) but not for new connections.
  • After valid-lifetime elapses, the address is removed from the interface.

This dual-clock design means address rotation is graceful: by the time a temporary address is removed, every existing connection has had plenty of time to either close or migrate to a newer source. A long-lived TCP connection that outlives both the valid-lifetime and any successor address will still break — RFC 4941 recommends keeping at least one or two successors in the DEPRECATED state to make this rare.

3. DHCPv6 — the stateful path

DHCPv6 is specified by RFC 8415 (which obsoleted RFC 3315 and 3633). It uses UDP on ports 546 (client) and 547 (server) and shares many state-machine concepts with IPv4 DHCP (RFC 2131), but the wire format is different — DHCPv6 messages are typed, not option-tag-laden in the IPv4 way.

3.1 The classic 4-message exchange (stateful, address assignment)

   Client                                   Server
   -----                                   ------
   (1) SOLICIT  (multicast ff02::1:2)  -->          "Any DHCPv6 server out there?"
                                              (server decides whether to reply)
   (2)             ADVERTISE         <--            "I can give you this address"
                                              (multiple servers can each ADVERTISE)
   (3) REQUEST (multicast ff02::1:2)  -->          "I want the address you offered"
                                              (server reserves the lease)
   (4)             REPLY             <--            "Here's the address + DNS + lifetimes"

Both SOLICIT and REQUEST go to the All-DHCPv6-Relay-Agents-and-Servers multicast group ff02::1:2 — link-scoped, so a server on the same link sees it without a relay. When the server is off-link, a relay agent (typically a router) wraps the message in a Relay-Message envelope and forwards it to the server unicast; the reply is relayed back.

3.2 The stateless 2-message exchange

When the host already has an address (via SLAAC) and only wants other config (DNS, NTP, SIP), the host skips ADVERTISE and REQUEST, and only needs two messages:

   (1) INFORMATION-REQUEST  -->           "I'm a SLAAC client, just give me DNS/NTP"
   (2) REPLY               <--           "Here: DNS=2001:db8::53, NTP=2001:db8::123"

This is what O=1 in the RA triggers. Many operators prefer stateless DHCPv6 to RDNSS because their DHCPv6 servers already integrate with IPAM (IP Address Management) and dynamic DNS updates.

3.3 The RAPID-COMMIT optimisation (2-message stateful)

RFC 8415 §5.1 defines a RAPID-COMMIT option that lets a SOLICIT/REPLY pair skip ADVERTISE/REQUEST. The client includes the option in SOLICIT; a server that supports RAPID-COMMIT and is willing to commit immediately sends REPLY directly. Operators typically enable RAPID-COMMIT on small subnets where contention is impossible; it is disabled on shared DHCPv6 servers because they need REQUEST/REPLY to coordinate allocation.

3.4 Prefix Delegation (PD)

DHCPv6 isn’t just for addresses; it can delegate prefixes to a downstream router. The CPE sends SOLICIT with an IA_PD option (Identity Association for Prefix Delegation, RFC 8415 §31); the operator’s DHCPv6-PD server delegates a /56 or /60 to the CPE, which then carves it into /64s on the customer LANs.

   Operator DHCPv6-PD server
        |
        | REPLY: delegate 2001:db8:ab:cd00::/56 to CPE
        v
   Customer CPE
        |
        |  +-- LAN1: 2001:db8:ab:cd00::/64  (SLAAC advertised on eth0)
        |  +-- LAN2: 2001:db8:ab:cd01::/64  (SLAAC advertised on eth1)
        |  +-- LAN3: 2001:db8:ab:cd02::/64
        |  ... 256 LANs maximum from /56

PD is how most residential operators (Comcast, BT, Free, KDDI) give customers enough IPv6 space for many subnets without per-host coordination.

3.5 Lifetimes and lease renewal

DHCPv6 leases use the same preferred/valid dual-clock model as SLAAC — the client must renew (T1) and rebind (T2) before the valid lifetime expires:

   [lease acquired]
        |
        |<-- T1 (default = .5 * valid) -->|  RENEW (unicast to original server)
        |                                 |
        |<-- T2 (default = .8 * valid) -->|  REBIND (multicast, in case original server is dead)
        |                                 |
        |<-- valid lifetime -------------->|  RELEASE or expiry

The server’s REPLY for RENEW/REPLY gives new T1, T2, and preferred/valid lifetimes — operators can shorten or extend leases without restarting clients.

4. SLAAC vs DHCPv6 — when to use which

PropertySLAACDHCPv6
Server requiredNo (only routers, which you have anyway)Yes
Address tracking / auditNo (host picks IID)Yes (operator logs every lease)
Static reverse DNSHard (host picks IID, may rotate)Easy (DHCPv6 → dynamic DNS)
Stability across reattachDepends on IID policy (RFC 7217 stable, RFC 4941 rotates)Server-controlled, can be sticky
Android compatibilityYes (Android ships SLAAC only)Android does NOT do DHCPv6 address assignment
Prefix-Per-Host (PPH) routingTrivial (host picks IID in delegated prefix)Awkward (server must know per-host prefix)
ScalabilityUnlimited — statelessServer must track leases
Configuration besides addressRDNSS (RFC 8106), RA options (RFC 4191, 6106)All of DHCPv4 + stateless options
Operator control of IIDNoneServer picks address

The Android DHCPv6 gap is operationally critical: Android does not implement RFC 8415 IA_NA (Identity Association for Non-temporary Address), only INFORMATION-REQUEST. So if you set M=1, O=1, A=0 on your RA to “force” DHCPv6-only addresses, every Android phone on the link will get only a link-local address and silently fail. The only portable configuration today is SLAAC + stateless DHCPv6 (M=0, O=1, A=1).

When to use SLAAC alone

  • Public-access networks (cafés, hotels) where you don’t need to log IPs.
  • Mobile carriers — every handset gets a /64 from the cellular prefix via SLAAC.
  • IoT — most constrained stacks have an SLAAC implementation but not DHCPv6.

When to use DHCPv6 (stateful)

  • Enterprises that need per-host audit logging (PCI DSS, regulated industries).
  • Datacentres where the host address must match a DNS reverse-zone entry that operations controls.
  • Hosts that don’t move networks (server farms).

When to use both

  • A campus where SLAAC is for visitors (their IIDs are random, don’t pollute DNS) and DHCPv6 stateful is for managed workstations (with dynamic DNS).

5. Source Address Selection — RFC 6724

A host with multiple IPv6 addresses (link-local + several GUAs, some deprecated, some temporary, some stable) must pick one source per outgoing connection. RFC 6724 (which obsoleted RFC 3484) defines the algorithm:

   Candidate source set, for destination D:
     - all addresses on the outgoing interface
     - EXCLUDE tentative, duplicate, deprecated unless no alternative
   Sort by (in order):
     1. Same-scope as destination (rule 1)
     2. Prefer appropriate source scope (rule 2)
     3. Avoid deprecated addresses (rule 3)
     4. Prefer home addresses (rule 4, Mobile IPv6)
     5. Prefer outgoing interface's address (rule 5)
     6. Prefer matching label (rule 6) — labels from RFC 6724 §2.1
     7. Prefer public over temporary (rule 7) — historically reversed by
        privacy-conscious OSes to "prefer temporary"
     8. Use longest matching prefix (rule 8)

Most modern OSes flip rule 7 to “prefer temporary” by policy (so RFC 4941 actually changes the address that gets used for outbound traffic). Operators should not rely on rule 8 — it’s the last tiebreaker, not the first.

t=0      Host up. Interface eth0 has MAC 02:42:ac:11:00:02.

t=0.001  Host generates link-local IID via RFC 7217:
           Hash(Prefix=fe80::/64, Secret=0x4a9b..., Iface=eth0) = 0x3c2a:1f5e:...
           Link-local = fe80::3c2a:1f5e:abcd:1234

t=0.001  DAD on link-local → no reply in 1 s → link-local PREFERRED

t=1.000  Host sends 3× RS to ff02::2

t=1.100  Router RA:
           Prefix=2001:db8:42:1::/64, valid=24h, pref=4h, A=1, L=1
           M=0, O=1
           RDNSS=2001:db8:42::53

t=1.101  Host assigns stable GUA (RFC 7217):
           Hash(Prefix=2001:db8:42:1::/64, Secret, Iface) = 0x...
           GUA-stable = 2001:db8:42:1:7f3a:9b1c:5d2e:1f4a
           DAD → PREFERRED in 1 s

t=1.102  Host generates temporary GUA (RFC 4941):
           random IID = 0xb1c2:d3e4:f5a6:0718
           GUA-temp1 = 2001:db8:42:1:b1c2:d3e4:f5a6:0718
           DAD → PREFERRED in 1 s

t=2.102  Host has 3 GUAs and a link-local. Opens a browser to https://example.com/
         getaddrinfo() returns GUA-temp1 as source (per RFC 6724 rule 7,
           inverted to prefer temporary)

t=2.200  Host also sends DHCPv6 INFORMATION-REQUEST to ff02::1:2
         (RA said O=1)

t=2.300  DHCPv6 server REPLY:
           DNS=2001:db8:42::53 (matches RDNSS, good)
           NTP=2001:db8:42::123
           Domain-search=corp.example.com

t=4h     preferred-lifetime on GUA-temp1 expires → DEPRECATED
         Host generates GUA-temp2 (new random IID), DADs, uses it for new connections
         Existing TCP connections on GUA-temp1 keep working until 24h valid-lifetime

7. Common Pitfalls

  1. Forgetting O-flag when DNS lives in DHCPv6 — clients that don’t understand RDNSS will end up without DNS. Either set O=1 or deploy RDNSS (RFC 8106).
  2. Setting A=1 and M=1 — every host gets both a SLAAC address and a DHCPv6 address, doubling their address count for no benefit. Use A=0 if you want DHCPv6-only on a prefix.
  3. Hard-coding EUI-64 IIDs in ACLs — works until the NIC is replaced and breaks under RFC 4941.
  4. Telling Android to use DHCPv6-only — Android ignores IA_NA, you’ve just made every Android phone on the link unaddressable.
  5. Disabling RA’s periodic unsolicted broadcasts — RFC 7559 explicitly recommends RAs every ~7 minutes; longer intervals leave new hosts stranded for minutes if they miss the initial burst.

8. Summary

  • SLAAC and DHCPv6 are complementary: SLAAC handles addresses (stateless, scale-invariant), DHCPv6 handles everything else (stateful or stateless).
  • The IID policy is a separate decision from SLAAC-vs-DHCPv6: SLAAC with RFC 7217 stable privacy is the modern default for hosts that need a stable address; SLAAC with RFC 4941 temporaries is the default for clients that need outbound-only anonymity.
  • Android’s lack of DHCPv6 IA_NA support means a portable configuration today is SLAAC + stateless DHCPv6 (RA: M=0, O=1, A=1).
  • Operators get address auditing via IPAM/DNS for stateful DHCPv6; get scale via SLAAC; get both via the dual mode.

9. References

  • RFC 4862 — IPv6 Stateless Address Autoconfiguration. https://www.rfc-editor.org/rfc/rfc4862
  • RFC 8415 — Dynamic Host Configuration Protocol for IPv6 (DHCPv6). https://www.rfc-editor.org/rfc/rfc8415
  • RFC 4941 — Privacy Extensions for Stateless Address Autoconfiguration. https://www.rfc-editor.org/rfc/rfc4941
  • RFC 7217 — A Method for Generating Semantically Opaque Interface Identifiers with IPv6. https://www.rfc-editor.org/rfc/rfc7217
  • RFC 6724 — Default Address Selection for IPv6. https://www.rfc-editor.org/rfc/rfc6724
  • RFC 4291 — IPv6 Addressing Architecture (EUI-64). https://www.rfc-editor.org/rfc/rfc4291
  • RFC 8106 — IPv6 Router Advertisement Options for DNS Configuration. https://www.rfc-editor.org/rfc/rfc8106
  • RFC 7559 — Making IPv6-Only Networks Reliable with DHCPv6. https://www.rfc-editor.org/rfc/rfc7559
  • RFC 6106 — IPv6 Router Advertisement Options for DNS Configuration (older, superseded by 8106). https://www.rfc-editor.org/rfc/rfc6106
  • RFC 7078 — DHCPv6 Option for SRV RRs. https://www.rfc-editor.org/rfc/rfc7078
  • RFC 8068 — DHCPv6 PD Per-Host. https://www.rfc-editor.org/rfc/rfc8068

Cross-References