IPv6 and SLAAC
How IPv6 differs from IPv4 beyond address length, what link-local means, and how a host configures its own address.
What sets IPv6 apart from IPv4 besides the address length, what link-local is, and how a host configures its own address.
Why this matters
IPv6 is already enabled almost everywhere: in clouds, with mobile carriers, in Docker and Kubernetes with dual-stack. A service that answers over IPv4 and stays silent over IPv6 is simply down for part of its users. And when a server "has no address", you need to understand where it was supposed to come from in the first place. This lesson covers how an IPv6 address is built, how a host gets one without a DHCP server, and what to check when autoconfiguration didn't work.
Address length and the network model
An IPv4 address takes 32 bits, that's about 4.3 billion addresses, and they ran out. That's where private networks and NAT from the previous lesson come from. An IPv6 address takes 128 bits: enough to give every device a public one.
IPv6 is written as eight groups of hex digits, 16 bits each. Leading zeros in a group can be
dropped, and one run of consecutive zero groups is replaced with :::
2001:0db8:0000:0000:0000:0000:0000:0001
2001:db8::1 # same thing, :: collapses the zeros once
:: never appears twice in one address: otherwise there's no way to tell how many zeros go in
each spot. The prefix 2001:db8::/32 is reserved for documentation, which is why it's in every
example in the course.
The change in model matters more than the length. In IPv6 a host usually has a global address, and NAT isn't needed. Reachability is now decided by the firewall, not a translation table. Anyone used to "we're invisible behind NAT" now has to configure filtering explicitly.
Link-local: the address you always have
Every interface with IPv6 enabled has a link-local address from the fe80::/10 prefix. The host
assembles it on its own from the interface identifier, with no router and no DHCP. It works only
within its own network segment; routers never forward it further.
An IPv6 address splits into two 64-bit halves: the network prefix and the interface identifier.
Here's the output from a container with MAC address 02:42:0a:d4:1f:02:
ip -6 addr show eth0
inet6 2001:db8:12:1:42:aff:fed4:1f02/64 scope global dynamic mngtmpaddr
valid_lft 86399sec preferred_lft 14399sec
inet6 fe80::42:aff:fed4:1f02/64 scope link
valid_lft forever preferred_lft forever
Here the identifier was assembled the old way, via EUI-64. The MAC is stretched to 64 bits by
inserting ff:fe in the middle, and the second bit of the first byte, the U/L (local/global) bit,
is flipped: 02 becomes 00. The result is 0042:0aff:fed4:1f02, written without leading zeros
as 42:aff:fed4:1f02. The MAC can be read straight out of such an address, which is why desktops
moved away from this scheme. Where a network is managed by NetworkManager, stable-privacy
(RFC 7217) usually runs instead: the identifier is a hash of the prefix, the interface name, and a
machine secret, with no ff:fe marker in it. The Linux kernel without NetworkManager uses EUI-64
by default.
Link-local addresses carry all of IPv6's internal machinery. Neighbor and router discovery (NDP,
replacing ARP) travels over them. ip -6 neigh shows the neighbor table, the equivalent of the ARP
table:
fe80::42:aff:fed4:1f03 dev eth0 lladdr 02:42:0a:d4:1f:03 router REACHABLE
A link-local address looks the same on any interface, so when you address one you specify the
interface with %: ping fe80::42:aff:fed4:1f03%eth0.
SLAAC: an address without a DHCP server
SLAAC (stateless address autoconfiguration) lets a host configure a global address on its own. The router periodically sends out a Router Advertisement (RA) with the network prefix. The host takes the prefix, adds its own interface identifier, and gets a ready address. There's no server keeping track of what was handed out, hence stateless.
The host also gets its default gateway from the RA. It becomes the router itself, addressed by its own link-local address.
For a prefix to turn into an address, three conditions have to line up:
- the prefix is exactly
/64long, since the interface identifier takes the other 64 bits; - the prefix in the RA has the A (autonomous) flag set, meaning "assemble an address yourself";
- the host accepts RA: on Linux
net.ipv6.conf.<interface>.accept_racontrols this.
SLAAC and DHCPv6 don't rule each other out. Flags in the RA tell the host what to do. The M (managed) flag means "get your address from DHCPv6". The O (other) flag means "assemble the address yourself, but get DNS servers and other settings from DHCPv6". The A flag on the prefix allows SLAAC. Both mechanisms work together fine on the same network.
You can see the RA's content right on the host. The radvdump utility from the radvd package
listens for RA and prints them out as configuration:
prefix 2001:db8:12:1::/64
{
AdvValidLifetime 86400;
AdvPreferredLifetime 14400;
AdvOnLink on;
AdvAutonomous on;
AdvRouterAddr off;
}; # End of prefix definition
AdvAutonomous here is the A flag.
What ip output looks like
The result of SLAAC shows up in plain ip, no extra utility needed. The default route with its
source marked:
ip -6 route show
2001:db8:12:1::/64 dev eth0 proto kernel metric 256 expires 86398sec pref medium
fe80::/64 dev eth0 proto kernel metric 256 pref medium
default via fe80::42:aff:fed4:1f03 dev eth0 proto ra metric 1024 expires 13sec hoplimit 64 pref medium
proto ra means the route came from a Router Advertisement, not from manual configuration. The
gateway is given as the router's link-local address. The default route's expires comes from the
router's lifetime in the RA: in this lab environment that's 15 seconds, refreshed with every new
RA.
What you see depends on who processes the RA. Here it's the kernel, and the route to the prefix is
marked proto kernel. On a machine running NetworkManager it sets the routes itself, and the
picture is different: proto ra on both routes, metric 100 instead of 1024.
We've already seen an address with the same history above: scope global dynamic and finite
valid_lft/preferred_lft. dynamic and a finite lifetime mean autoconfiguration. An address set
by hand, or one Docker assigned when creating the container, looks different: no dynamic, and
valid_lft forever.
Dual-stack
The move to IPv6 happens through both stacks running at once. A host has both an IPv4 and an IPv6 address, and the application picks whichever is available. A service, though, listens on separate sockets for each stack, and turning on IPv6 in the network doesn't automatically mean the service starts answering on it.
That gives a practical diagnostic rule: check both stacks. That's what ping and ping -6,
curl -4 and curl -6 are for. A service can be alive on one protocol and down on the other. In a
URL, an IPv6 address goes in square brackets:
curl -6 -g 'http://[2001:db8:12:2::20]/'
The -g flag turns off curl's handling of [] as a glob pattern; without it, it may interpret
the brackets in the address on its own. The single quotes are there for the shell: on macOS, zsh
without them treats the brackets as a filename glob and prints no matches found.
Common mistakes
- "There's no global address, so RA isn't getting through." Not necessarily. First check
ip -6 route show default: if there's aproto raroute there, RA is arriving, and the address failed to assemble for some other reason. The reasons are in the list of conditions from the SLAAC section. - On a machine that forwards packets itself (
forwarding=1), Linux ignores RA whenaccept_ra=1. You needaccept_ra=2. This comes up on hosts running Docker or Kubernetes. - Addressing a link-local address without
%interface: the command doesn't know which interface to use. - Checking only
curlby name. If a name has both an A and an AAAA record, the client picks the stack itself, hiding a failure on one of them. Check-4and-6explicitly. - A firewall configured only for IPv4.
iptablesrules don't apply to IPv6; it needsip6tablesor the sharedinettable innftables.
What you'll need for the tasks
docker exec <container> <command>: run a command inside a lab environment container;ip -6 addr show eth0andip -6 route show: IPv6 addresses and routes, thedynamicandproto ramarks;radvdump: exactly what the router is advertising in RA, interrupt with Ctrl+C;curl -4andcurl -6 -g 'http://[address]/': a request over a specific stack;netstat -ltn: which TCP ports a process listens on and on which addresses;- the "SLAAC: an address without a DHCP server", "Dual-stack", and "Common mistakes" sections.
Next lesson: DHCP, how a host gets an address on an IPv4 network.
Interview-style questions
Interview questions on this topic: 2
Included with practice access, $49 one-time. Get access
Answer out loud or in writing, then reveal the answer and honestly mark whether you knew it.
Done reading?
Mark the lesson as read to move your track progress forward.
Lesson marked as done.
Go through it with an AI agent
The agent explains anything unclear in plain words, gives you 2 labs for this lesson and drills you on interview questions. $49 one-time, lifetime access.
Tell the agent: use MCP izi, give me lesson networking-basics-12-ipv6-slaac
Progress saved in this browser
Clear your history or open this on your phone, and the marks will be gone. Leave your email and they'll move to your account. No password needed.
Done. moved to your account. You can see your progress on any device. My progress