Part IV — The Internet Layer (IPv6)
IPv6 is not "IPv4 with more bits." Auto-configuration, the death of NAT and broadcast, mandatory ICMPv6, and a fixed header change day-to-day operations.
16. IPv6 addressing and notation
16.1 The address
128 bits, written as eight groups of four hex digits:
2001:0db8:0000:0000:0000:ff00:0042:8329.
Compression rules:
- Drop leading zeros per group:
2001:db8:0:0:0:ff00:42:8329. - Replace one run of all-zero groups with
:::2001:db8::ff00:42:8329. (Only once —::twice would be ambiguous.) - Hex is case-insensitive; lowercase is the norm (RFC 5952).
Prefix length is always CIDR: 2001:db8:abcd:12::/64.
16.2 Structure of a typical unicast address
| 48 bits global routing prefix | 16 bits subnet | 64 bits interface ID |
| 2001:0db8:abcd : 0012 : 0000:0000:0000:0001 |
|<---------- given to you by your ISP/RIR ------->|<-- you pick per host -->|
- The /64 is sacred: SLAAC, many implementations, and lots of tooling assume
every LAN is exactly a
/64. Don't subnet longer than /64 for normal LANs. - A site typically gets /48 (65 536 subnets) or /56 (256 subnets) from its ISP. A single home often gets a /56 or /60 via DHCPv6-PD (prefix delegation).
- Interface ID (last 64 bits): historically EUI-64 (derived from the MAC:
split it, insert
ff:fe, flip the U/L bit) — but that leaks your MAC and lets you be tracked across networks, so modern hosts use RFC 8981 temporary privacy addresses (random, rotated daily) for outbound and often a stable random address for inbound.
16.3 Address types (there is no broadcast)
| Prefix | Type | Notes |
|---|---|---|
::1/128 |
Loopback | equals IPv4 127.0.0.1 |
::/128 |
Unspecified | "no address yet" (used as source during DAD) |
fe80::/10 |
Link-local | Auto-configured on every IPv6 interface, always present, never routed. Used by NDP, OSPFv3, and as next-hop. You must specify the zone: ping6 fe80::1%eth0. |
fc00::/7 (fd00::/8 in practice) |
Unique Local (ULA) | The IPv6 "private" range; generate a random /48. Not globally routed. |
2000::/3 |
Global unicast (GUA) | Internet-routable. |
ff00::/8 |
Multicast | Replaces broadcast. Scoped: ff02::1 all-nodes (link), ff02::2 all-routers, ff02::fb mDNS, ff02::1:ffXX:XXXX solicited-node (used by NDP). |
2002::/16 |
6to4 | legacy transition, deprecated |
2001:db8::/32 |
Documentation | safe for examples |
64:ff9b::/96 |
NAT64 well-known prefix | maps IPv4 into IPv6 (Ch. 18) |
::ffff:0:0/96 |
IPv4-mapped | ::ffff:192.0.2.1 — how a dual-stack socket represents an IPv4 peer |
An interface normally has several addresses at once: one fe80:: link-local,
maybe a ULA, one or more GUAs (stable + temporary). This is normal, not a
misconfig.
16.4 The IPv6 header (fixed 40 bytes)
|Version| Traffic Class | Flow Label |
| Payload Length | Next Header | Hop Limit |
| Source Address (128 bits) |
| Destination Address (128 bits) |
Differences from IPv4 that matter:
- No header checksum (link layer + transport checksums suffice; and IPv6 requires the UDP checksum, unlike IPv4).
- No IHL / options in the base header — extension headers chain via Next Header (Hop-by-Hop, Routing, Fragment, ESP, AH, Destination). Firewalls sometimes drop long extension-header chains.
- Hop Limit = IPv4's TTL, renamed.
- Flow Label (20 bits): lets routers ECMP-hash a flow consistently without parsing L4; also used by QUIC.
- No fragmentation fields — moved to an extension header, source-only.
16.5 War story
An app "worked on IPv4, hung on IPv6" for some users. getaddrinfo returned a
AAAA, the app connected to a GUA, but the user's ISP had broken IPv6 (RA present,
no working upstream). The app didn't implement Happy Eyeballs (Ch. 18) so it
waited a full 75 s connect timeout before trying IPv4. Fix: use a client library
with Happy Eyeballs v2, or lower the connect timeout and race both families.
Lesson: in a dual-stack world, always race A and AAAA; never let one broken
family block the other.
Try it yourself
ip -6 addr show # all v6 addresses (note multiple per iface)
ip -6 route
ping6 -c3 2606:4700:4700::1111 # Cloudflare v6
ping6 fe80::1%eth0 # link-local needs a zone id
sysctl net.ipv6.conf.all.use_tempaddr # 2 = prefer privacy addresses