24.3 IPv6: Address Format, Link-Local, and Global Unicast
Alright, let’s talk about IPv6. You’ve probably heard the horror stories: addresses that look like a cat walked across your keyboard, configuration that feels like black magic. I’m here to tell you it’s not that bad. In fact, once you get past the initial shock of all those colons, it’s mostly just more of the same networking concepts, but with some genuinely good ideas baked in. The designers looked at the duct-tape-and-bailing-wire mess that IPv4 NAT created and said, “Let’s just give everyone more addresses than they could ever need.” And they did. The entire IPv4 address space is a rounding error in IPv6.
The first thing to wrap your head around is the address format. An IPv6 address is 128 bits long, which is four times the size of an IPv4 address. We represent it as eight groups of four hexadecimal digits, separated by colons. For example: 2001:0db8:85a3:0000:0000:8a2e:0370:7334. Yes, it’s a mouthful. But we have rules to make it slightly less horrifying.
Address Compression Rules
You can (and absolutely should) compress these addresses to maintain your sanity. The rules are simple:
- Leading zeros: Drop leading zeros in any 16-bit block. That
0db8becomes justdb8.0370becomes370. - The Double Colon: You can replace one and only one sequence of consecutive blocks that are all
0000with a double colon::. This is the “don’t make me type all these zeros” rule.
Let’s compress that monster from above:
2001:0db8:85a3:0000:0000:8a2e:0370:7334
Apply Rule 1: 2001:db8:85a3:0:0:8a2e:370:7334
Apply Rule 2: 2001:db8:85a3::8a2e:370:7334
See? Manageable. The key with the :: is that you can only use it once per address. If you use it twice, the address becomes ambiguous. The computer needs to be able to definitively know how many blocks of 0000 you replaced to expand it back to a full 128 bits.
The Two Addresses You’ll See on Every Single Host
Here’s where IPv6 gets clever. Every IPv6 interface must have at least two addresses. It’s not optional. This separation of duties solves a ton of problems before they even start.
Link-Local Addresses (fe80::/10)
This is your host’s “on-link” or “neighborhood” address. It’s only valid on the specific physical (or logical, like a VLAN) network segment it’s connected to. Routers will not forward packets with a link-local source or destination address. They are purely for talking to your immediate neighbors.
The brilliant part? They are self-configured. The host doesn’t need a DHCP server or anything else. It uses its MAC address to create a unique address on the link. The process is called EUI-64, and it works like this:
- Take the 48-bit MAC address:
00:11:22:aa:bb:cc - Split it in half:
00:11:22andaa:bb:cc - Stuff
ff:fein the middle:00:11:22:ff:fe:aa:bb:cc - Convert this to IPv6 format and prepend the link-local prefix
fe80:::fe80::211:22ff:feaa:bbcc - There’s one more step: the 7th bit in the first byte (the Universal/Local bit) is flipped. In this case,
00in binary is00000000. Flipping the 7th bit makes it00000010(binary), which is02in hex. So the final address becomes:fe80::211:22ff:feaa:bbcc
Yes, it’s a bit weird. But the result is that every host, the moment it boots up and its interface comes online, has a valid, routable (on the local link) address. This is why protocols like IPv6 Neighbor Discovery (the replacement for ARP) work instantly.
On Linux, you’ll see these fe80: addresses pop up all over the place.
$ ip -6 addr show dev eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 state UP qlen 1000
inet6 2001:db8:85a3::8a2e:370:7334/64 scope global
valid_lft forever preferred_lft forever
inet6 fe80::211:22ff:feaa:bbcc/64 scope link
valid_lft forever preferred_lft forever
Crucial Pitfall: When you want to ping or SSH to a link-local address, you must specify the outgoing interface. Since every interface has a fe80:: address, the OS has no idea which network you’re trying to reach. You do this with a % followed by the interface name or index.
# This will fail with "connect: Invalid argument"
$ ping6 fe80::211:22ff:feaa:bbcc
# This will work
$ ping6 fe80::211:22ff:feaa:bbcc%eth0
# Or using the interface index
$ ping6 fe80::211:22ff:feaa:bbcc%2
Global Unicast Addresses (2000::/3)
This is the IPv6 equivalent of a public IPv4 address. It’s globally routable, meaning it can travel across the entire internet. The prefix 2000::/3 (everything from 2000: to 3fff:) is assigned for this purpose, with most addresses currently coming from 2001::/16 (for providers) and 2001:db8::/32 (which is reserved for documentation, like the 192.0.2.0/24 of IPv6—you’re seeing it all over this chapter).
A host gets its global unicast address in one of two ways:
- Stateful DHCPv6: Works almost exactly like DHCP for IPv4. A server hands out addresses and other info.
- Stateless Address Autoconfiguration (SLAAC): This is the cool one. The router on the link sends out Router Advertisement (RA) messages that basically say, “Hey everyone, the network prefix on this link is
2001:db8:85a3::/64.” Your host hears this, takes the/64prefix, and generates the rest of the 64-bit interface identifier using the same EUI-64 method we used for the link-local address. It now has a full, globally routable/128address.
The best practice here? Your host ends up with multiple addresses. It will have its stable link-local address, and it will have one or more global addresses. The global ones have lifetimes (preferred and valid) and can change over time for privacy reasons (a feature called “privacy extensions”). This means you generally shouldn’t hardcode a host’s global IPv6 address in scripts or configs—use DNS. The link-local address, however, is permanent for that interface on that hardware, so it’s more stable for things like router peerings within a specific layer 2 domain.
The beauty of this two-address system is that the link-local address handles all the network plumbing (finding routers, neighbor discovery), while the global address handles your actual data. It’s a clean separation that makes networks more robust and easier to manage. You’re no longer praying for a DHCP lease just to talk to the router next to you.