Networking for beginners. Part 15: SLAAC

All parts of the series “Networking for beginners”

In part fourteen the router sent out Router Advertisements, and they contained the segment prefix with the auto flag: "you may build yourself an address from this prefix". Today is about how a host does that. The mechanism is called SLAAC, Stateless Address Autoconfiguration, RFC 4862, and the key word in it is "stateless": no one keeps track of which address was given to whom. No server, no leases, no table. The router says "the prefix is this", the host makes up the second half of the address itself, checks that it is free, and starts working. For home networks and phones this is the main, and often the only, way to get an IPv6 address.

We already know half of the mechanism: RA and DAD from the previous part. What remains is to understand where the second half of the address comes from and how addresses live and die.


Address = prefix + identifier

In part thirteen a global address was split into 64 bits of prefix and 64 bits of interface identifier (IID). SLAAC is simply concatenation:

 from RA, prefix info with flag A:  2001:db8:1234:5678::/64
 made up by the host itself:                            3e52:82ff:fe1a:2b3c
                                    ─────────────────────────────────────────
 address:                           2001:db8:1234:5678:3e52:82ff:fe1a:2b3c

That is why the rule "every network is a /64" exists: SLAAC only works with a prefix of exactly 64 bits, so the host is left with exactly 64 for the identifier. Hosts will ignore a /80 prefix in an RA.

The host makes up an identifier once for the link-local address (fe80:: + IID) and then the same one or others for each prefix in the RA. There are three ways to do it, and choosing between them is a choice between convenience and privacy.

EUI-64: from the MAC address

The original method from 1998. Take the 48-bit MAC 3c:52:82:1a:2b:3c, insert ff:fe in the middle to get 64 bits, and flip the U/L bit in the first byte (the second least significant bit, the same one from part two):

MAC:              3c:52:82        :1a:2b:3c
insert ff:fe:     3c:52:82:ff:fe:1a:2b:3c
3c = 0011 1100 → flip the U/L bit → 0011 1110 = 3e
IID:              3e52:82ff:fe1a:2b3c

The bit is flipped so that manually assigned addresses like ::1 end up with the "local" value of the bit, and addresses from globally unique MACs with the "global" one; it has almost no practical significance, but it explains why 3c became 3e. EUI-64 is easy to recognize by the ff:fe in the middle.

The upside: the address is predictable — know the MAC, and you know the address in any network. The downside is the same thing: the MAC address is visible to every website, and the same laptop is recognizable at home, in the office and in a café by the lower half of its address. That is why today EUI-64 is used mostly by servers and network equipment, while client systems have moved away from it.

Stable random

RFC 7217: the identifier is computed as a hash of the network prefix, the interface name and a secret key of the machine. The result is stable within one network (every time you connect to the office the address is the same, so it can go into DNS and the firewall) and different across networks (home and office give different identifiers that cannot be linked). The MAC plays no part in it. This is the default on Linux (addr_gen_mode = 2, stable-privacy) and on Windows (randomizeidentifiers), and such addresses without ff:fe in the middle are the norm on modern machines.

Temporary

RFC 8981, formerly 4941, "privacy extensions". On top of the stable address, the host creates one more with a fully random identifier, changes it once a day and uses it as the source address for new outgoing connections. The stable address remains for incoming ones. This way a website cannot track a machine for longer than a day, even within one network.

$ ip -6 addr show eth0 | grep global
    inet6 2001:db8:1234:5678:a1b2:c3d4:e5f6:789a/64 scope global temporary dynamic
       valid_lft 86153sec preferred_lft 14153sec
    inet6 2001:db8:1234:5678:4f3e:2d1c:0b9a:8877/64 scope global dynamic mngtmpaddr stable-privacy
       valid_lft 86153sec preferred_lft 86153sec

Linux markings: temporary — temporary; mngtmpaddr — "temporary addresses are spawned from this one"; stable-privacy — the generation method. There can be several temporary addresses: the old one is still winding down while the new one is already in use, and on a long-running machine ip -6 addr may show three or four of them.


The life of an address

Every address from an RA has two lifetimes, both from the prefix info:

 received   preferred_lft                      valid_lft
 ├──────────────┼─────────────────────────────────┤
 │ preferred    │ deprecated                      │ removed
 │ for anything │ existing connections live on,   │
 │              │ new ones use another address    │
  • Preferred — the address may be used for new connections.
  • Deprecated — the preferred lifetime has expired but the valid one has not: the address accepts packets and established connections continue, but the stack will open new connections from another address if there is one.
  • Valid expired — the address is removed from the interface.

Every new RA with the same prefix refreshes both lifetimes, so in a live network an address never gets to deprecated: with valid 86400 and an RA every ten minutes, the countdown restarts every ten minutes. Hence valid_lft 86153sec in the output above — an RA arrived 247 seconds ago.

For temporary addresses, preferred is deliberately shorter (a day or less), and address rotation works precisely through deprecation: a few minutes before preferred expires, a new temporary address is created, the old one becomes deprecated, finishes its connections and is removed after a week (valid) without getting in anyone's way.

The router also withdraws a prefix through lifetimes when switching providers: it sends an RA in which the old prefix has preferred = 0 (deprecated at once) and a small valid, alongside the new prefix. Hosts move over by themselves within minutes. The only restriction: a single RA cannot cut valid below two hours if more than that remains — a safeguard against one forged RA killing every address on the segment.


What happens on connect

The whole sequence, from plugging in the cable to a working address, in three seconds:

$ sudo tcpdump -i eth0 -n icmp6
0.000 IP6 :: > ff02::1:ff1a:2b3c: ICMP6, neighbor solicitation, who has fe80::3e52:82ff:fe1a:2b3c         (1) DAD link-local
1.000 IP6 fe80::3e52:82ff:fe1a:2b3c > ff02::2: ICMP6, router solicitation                                  (2) RS
1.004 IP6 fe80::da47:32ff:fe0a:b0c > ff02::1: ICMP6, router advertisement                                  (3) RA
1.005 IP6 :: > ff02::1:ff9a:8877: ICMP6, neighbor solicitation, who has 2001:db8:1234:5678:4f3e:2d1c:0b9a:8877   (4) DAD for the stable one
1.005 IP6 :: > ff02::1:fff6:789a: ICMP6, neighbor solicitation, who has 2001:db8:1234:5678:a1b2:c3d4:e5f6:789a   (4) DAD for the temporary one
2.010 IP6 fe80::3e52:82ff:fe1a:2b3c > ff02::1:ff0a:b0c: ICMP6, neighbor solicitation, who has fe80::da47:32ff:fe0a:b0c   (5) NS to the router
2.010 IP6 fe80::da47:32ff:fe0a:b0c > fe80::3e52:82ff:fe1a:2b3c: ICMP6, neighbor advertisement, tgt is fe80::da47:32ff:fe0a:b0c
  1. The interface came up. The host built a link-local fe80:: + IID and started DAD: an NS from :: to the candidate's solicited-node group. A second of silence, and the link-local address is usable.
  2. From the link-local address the host sends a Router Solicitation to all routers.
  3. The router answers with an RA. It has the prefix with the A flag, lifetimes, RDNSS, MTU, hop limit, and the RA itself came from the router's link-local address.
  4. The host makes two addresses from the prefix — stable and temporary — and runs DAD for each. The routing table gets 2001:db8:1234:5678::/64 dev eth0 (L flag: neighbors directly) and default via fe80::da47:… proto ra (router lifetime > 0). DNS from RDNSS goes into resolv.conf (via systemd-resolved or NetworkManager).
  5. The first packet out: an NS to the router for its MAC, an NA in reply, and the packet is on its way.

Not a single server, not a single record anywhere: the router does not know the hosts' addresses, and the hosts do not know about each other. Hence stateless.

Flags that control behavior

The four RA flags a host reads when deciding:

Flag Where What it means
A (autonomous) prefix info an address can be built from this prefix via SLAAC
L (on-link) prefix info addresses from this prefix are neighbors, send directly
M (managed) RA header get addresses via DHCPv6 (part 16)
O (other) RA header get other parameters (DNS, if there is no RDNSS) from DHCPv6

Combinations: A = 1, M = 0, O = 0 — pure SLAAC, the most common home setup; A = 0, M = 1 — addresses only via DHCPv6, the corporate setup with accounting; A = 1, M = 1 — the host makes an address itself and also asks DHCPv6 for one, ending up with two; A = 1, O = 1 — address by itself, DNS from DHCPv6, the setup from before RDNSS existed. The L flag is separate from A: a prefix can be on-link without autoconfiguration (DHCPv6 assigns addresses, but neighbors are still neighbors), and vice versa.

The default route, meanwhile, always comes from the RA — DHCPv6, as we will see in the next part, has no "gateway" option at all.


On the router

On Linux, RAs are sent by the radvd daemon; on home routers the same thing hides behind an "IPv6: automatic" checkbox. A configuration matching the capture above:

interface eth0 {
    AdvSendAdvert on;
    MinRtrAdvInterval 200;
    MaxRtrAdvInterval 600;
    AdvDefaultLifetime 1800;
    AdvLinkMTU 1500;

    prefix 2001:db8:1234:5678::/64 {
        AdvOnLink on;
        AdvAutonomous on;
        AdvValidLifetime 86400;
        AdvPreferredLifetime 14400;
    };

    RDNSS 2001:db8:1234:5678::1 { AdvRDNSSLifetime 600; };
    DNSSL example.com { AdvDNSSLLifetime 600; };
};

The router must forward packets (net.ipv6.conf.all.forwarding = 1), and from that moment Linux by default stops accepting other RAs on its interfaces — a router is not supposed to configure itself from a neighboring router. If the router itself gets its uplink via RA from the provider, set accept_ra = 2 on the external interface: "accept even though I am a router".

Where does a home router get the prefix 2001:db8:1234:5678::/64? The provider gave it a /56 block via DHCPv6 prefix delegation (part 16), and the router picked one /64 from it for the LAN and put it into the RA itself. radvd has a prefix ::/64 entry for this: the prefix is taken from the interface's own address, whatever it is; but it is simpler to hand everything to dhcpcd or odhcpd, which get the block from the provider and configure RA in one go.


What SLAAC can't do

Being serverless is also a limitation.

  • No accounting. No one records which device had which address yesterday at 15:40. At home that does not matter; for a company that has to "find out who downloaded it", it is a reason to use DHCPv6, at least in addition.
  • Few options. An RA carries prefixes, MTU, hop limit, DNS, search domains and, per RFC 4191, additional routes. Time servers, the boot server address, parameters for phones — only via DHCPv6.
  • No name registration. A DHCP server could write the host name into DNS; with SLAAC there is no one to do it.
  • No control over the host's address. The host chooses the identifier, and the router cannot predict the temporary address it uses to go out. Firewall rules like "this host is allowed" are written for the prefix or for the stable address.

And the reverse limitation, which is why you cannot do without SLAAC: Android does not support DHCPv6. A network with A = 0 and M = 1 will leave phones without IPv6. So the general recommendation is SLAAC with RDNSS for everyone, and DHCPv6 on top for those who need accounting.


Hands-on

How the identifier is generated and how temporary addresses behave — sysctl on each interface:

$ sysctl net.ipv6.conf.eth0.addr_gen_mode     # 0 EUI-64, 2 stable-privacy (default), 1 — no link-local
$ sysctl net.ipv6.conf.eth0.use_tempaddr      # 0 no, 1 yes, 2 yes and prefer for outgoing
$ sysctl net.ipv6.conf.eth0.temp_prefered_lft # 86400 — one day
$ sysctl net.ipv6.conf.eth0.accept_ra         # 0 ignore RA, 1 accept, 2 accept even on a router
$ sysctl net.ipv6.conf.eth0.autoconf          # whether to make addresses from prefixes with A

On Windows: netsh interface ipv6 show global shows Randomize Identifiers and Use Temporary Addresses, and netsh interface ipv6 set privacy state=disabled turns off temporary addresses if they get in the way (for example, on a server that should have one well-known address).

To see which addresses are in which state, including the tentative and deprecated flags:

$ ip -6 addr show eth0

To force the whole thing to happen again without touching the cable: ip link set eth0 down && ip link set eth0 up with tcpdump on icmp6 in another window. If there is no global address afterwards, rdisc6 eth0 will show whether an RA arrives at all and whether it has a prefix with Autonomous address conf.: Yes.


Key takeaways

  • SLAAC: address = a /64 prefix from an RA with the A flag + an interface identifier the host makes up itself, then DAD. No server, no state.
  • Identifier: EUI-64 from the MAC (ff:fe in the middle, U/L bit flipped) — predictable and trackable; stable random — one per network, different across networks, the default; temporary — random, changes daily, for outgoing connections.
  • Two lifetimes: preferred (for new connections) and valid (at all). A deprecated address serves out its old connections. Every RA refreshes the lifetimes; a router withdraws a prefix by zeroing preferred.
  • The A and L flags in the prefix and M and O in the header decide what to do yourself and what to ask DHCPv6 for. The gateway always comes from the RA.
  • SLAAC gives no accounting, few options and no name registration; DHCPv6 does not replace it, because Android only does SLAAC.

Next — Part 16: DHCPv6: why it exists when there is SLAAC, how it differs from DHCPv4 (DUID instead of MAC, multicast instead of broadcast, no gateway), stateful and stateless modes, the Solicit, Advertise, Request, Reply exchange, and prefix delegation, with which a provider gives a router a whole block of networks.

Only registered users can leave a comment.

Sign in or register to leave a comment.