LinuxXXI · Advanced Linux NetworkingIPv6 advanced
IPv6 advanced - privacy, SLAAC details, and operational gotchas
What you'll learn
- Explain how SLAAC generates the interface identifier
- Recognise privacy extension addresses and their trade-offs
- Configure IPv6 on Linux with predictable addressing
- Avoid the common IPv6 operational gotchas
Prerequisites
Verified against Ubuntu 24.04 LTS · Debian 12 (Bookworm) · RHEL 9.x · Rocky Linux 9.x · AlmaLinux 9.x · Linux kernel 6.1 LTS / 6.6 LTS · systemd 255+ · OpenSSH 8.7p1 (RHEL 9) / 9.6p1 (Ubuntu 24.04) · nftables 1.0.x · chrony 4.x · Pacemaker 2.1.x · Corosync 3.1.x · 2026-08-09
A Linux host’s IPv6 address is often more complex than its IPv4 address. It has a link-local address (automatic), a global address (from SLAAC or DHCPv6), and possibly temporary addresses (privacy extensions). Understanding what each is for - and how to control them - is essential for production.
SLAAC and the interface identifier
SLAAC generates an interface identifier (IID, the lower 64 bits of the IPv6 address) from one of three sources:
- Modified EUI-64: derived from the MAC address. The
48-bit MAC is split down the middle, the two bytes
ff:feare inserted between the halves (48 + 16 = 64 bits), and the universal/local bit —0x02of the first octet — is flipped. - RFC 7217 opaque: stable but random, derived from a hash of network-specific values.
- RFC 4941 privacy extensions: temporary addresses that rotate periodically.
A MAC address aa:bb:cc:00:00:01 produces a Modified EUI-64
IID of a8bb:ccff:fe00:0001. Prefixed with the SLAAC prefix,
this becomes the global IPv6 address.
Privacy concerns: the EUI-64 IID embeds the MAC address, which is also the interface’s link-local IID. An observer on the same LAN can correlate the MAC with the IPv6 address across networks, breaking some privacy assumptions.
Privacy extensions (RFC 4941)
Privacy extensions generate temporary addresses in addition to the SLAAC address. The temporary addresses rotate (default 24 hours on Linux), making it harder to track a host across networks.
sysctl net.ipv6.conf.eth0.use_tempaddr
sysctl net.ipv6.conf.eth0.temp_valid_lft
sysctl net.ipv6.conf.eth0.temp_prefered_lft
A value of 2 for use_tempaddr means “use temporary
addresses for outgoing connections, but also keep the
publicly-known stable address”. This is the right setting for
most hosts.
For servers, privacy extensions are usually undesirable: the address is the host’s identity and should be stable. Disable them:
sudo sysctl -w net.ipv6.conf.eth0.use_tempaddr=0
For clients, privacy extensions are usually the right choice.
Configure a stable IPv6 address
Predictable addressing matters for servers. The recommended approach is to disable SLAAC and configure the address explicitly:
# /etc/systemd/network/10-eth0.network
[Match]
Name=eth0
[Network]
Address=2001:db8:85a3:1::10/64
Gateway=2001:db8:85a3:1::1
IPv6AcceptRA=false
Setting IPv6AcceptRA=false prevents the kernel from
auto-configuring addresses from router advertisements. With
SLAAC disabled, the host’s IPv6 address is exactly what the
configuration says.
The gateway is not optional
Gateway= is the line operators forget, and the omission is
silent. In IPv4 you get a default route from DHCP or you
configure it by hand, and its absence is obvious immediately.
In IPv6 the default route normally arrives inside a Router
Advertisement, not with the address. Turning off RA
acceptance therefore turns off the only source of your IPv6
default route.
A host configured with Address= but no Gateway= and
IPv6AcceptRA=false ends up in the worst possible state: it
holds a global IPv6 address, it publishes an AAAA record, it
answers inbound IPv6 connections perfectly, and it cannot
originate a single off-link IPv6 connection. Outbound calls
to dual-stack destinations stall until Happy Eyeballs times
out and retries over IPv4, so package mirrors, DNS resolvers,
telemetry and inter-service calls all pick up intermittent
multi-second delays. It presents as packet loss. It is a
missing route.
Verify explicitly after every change:
$ ip -6 route show defaultdefault via 2001:db8:85a3:1::1 dev eth0 proto static metric 1024 pref mediumIllustrative output
An empty result from ip -6 route show default means IPv6
egress is broken, whatever the address list says.
Many routers only offer a link-local next hop. That is
normal and correct for IPv6, and Gateway= accepts it:
[Network]
Address=2001:db8:85a3:1::10/64
Gateway=fe80::1
IPv6AcceptRA=false
A link-local address is only unique per link, so the next hop
is meaningless without an interface. In a systemd-networkd
file the interface comes from the [Match] stanza. On the
command line you must supply it yourself, or the command
fails: ip -6 route add default via fe80::1 dev eth0.
Disable SLAAC per interface
sudo sysctl -w net.ipv6.conf.eth0.autoconf=0
sudo sysctl -w net.ipv6.conf.eth0.accept_ra=0
autoconf=0 disables SLAAC for that interface.
accept_ra=0 prevents accepting router advertisements.
accept_ra=0 also stops the kernel installing the default
route it learned from those advertisements. The trap is that
routes already installed do not vanish the moment you set the
sysctl; they persist until their RA lifetime expires. So the
host keeps working, the change looks safe, and IPv6 egress
breaks hours later or at the next reboot. Set the sysctl, then
immediately confirm you still have a route you configured
yourself with ip -6 route show default.
For DHCPv6-only addressing you still want the RAs - they
carry the default route and the M/O flags that tell the
host to ask DHCPv6 in the first place. What you turn off is
SLAAC address generation, not RA acceptance:
sudo sysctl -w net.ipv6.conf.eth0.autoconf=0
sudo sysctl -w net.ipv6.conf.eth0.accept_ra=1
accept_ra=2 means something entirely different, and the
name misleads people into using it here. The values are:
| Value | Meaning |
|---|---|
0 | Do not accept Router Advertisements |
1 | Accept RAs if forwarding is disabled (the functional default) |
2 | Overrule forwarding behaviour - accept RAs even if forwarding is enabled |
None of them suppresses address autoconfiguration; that is
autoconf=0 alone.
Operational gotchas
-
Two addresses, one hostname: a host has both a stable and a temporary IPv6 address. A DNS AAAA record points to the stable address; outgoing connections may use the temporary one. Reverse the priority with
temp_prefered_lft=0on servers. -
RA-driven configuration surprise: a guest network or test rig accepts RAs and configures itself automatically. This is convenient but can override your static configuration. Disable with
accept_ra=0. -
Multicast listener exhaustion: a host that joins many multicast groups (IPv6 heavily uses multicast for ND) may exhaust the multicast listener table on a router. This is rare but documented.
-
Dual-stack listeners:
::and[::]are the same address. The brackets are only notation for writing an IPv6 address next to a port, which is whyssprints[::]:443and0.0.0.0:443. Whether a socket bound to::also accepts IPv4 is decided by theIPV6_V6ONLYsocket option, whose system default comes fromnet.ipv6.bindv6only(0= dual-stack, the Linux default). Most applications set it themselves - nginxlisten [::]:443 ipv6only=on, the JVM’s-Djava.net.preferIPv4Stack- so when a service is reachable over one family and not the other, read the application’s own setting first, thensysctl net.ipv6.bindv6only. -
Ping6 zone identifier: pinging a link-local address requires a zone identifier:
ping6 fe80::1%eth0. Forgetting%eth0produces “no route to host” errors. -
RA flooding: a misbehaving router may flood RAs and reconfigure hosts. Disable RA acceptance on servers.
Knowledge check
Knowledge check · 5 questions
Q1. What is the right sysctl to disable SLAAC on eth0?
Q2. A server with privacy extensions enabled has a stable, predictable IPv6 address.
Q3. Which of the following are operational IPv6 gotchas? Select all that apply.
Q4. A server is configured with Address=2001:db8:85a3:1::10/64 and IPv6AcceptRA=false, and no Gateway=. Users report intermittent multi-second stalls on outbound calls to dual-stack services. What is the fault?
Q5. Setting net.ipv6.conf.eth0.accept_ra=0 on a running host immediately removes the IPv6 default route it learned from Router Advertisements.
Passing score: 75%. Answers are checked in this browser.