· 7 min read ·

IPv6 Is Not Hard to Learn. It Is Hard Because It Does More

Source: lobsters

Brian Carpenter’s document “Why is IPv6 so complicated?” is worth reading because Carpenter is not a random blogger venting frustration. He has been involved in Internet architecture work since before most people had email addresses, and when he asks that question, he is asking it seriously. The answer is not “because it was designed badly.” The answer is that IPv6 is doing more than IPv4 ever did, and the complexity is mostly load-bearing.

Let me try to make that concrete.

The Address Is the Easy Part

Everyone learns that IPv6 addresses are 128 bits wide and written in hex groups separated by colons:

2001:0db8:85a3:0000:0000:8a2e:0370:7334

The shorthand rules (omit leading zeros in each group, collapse one run of all-zero groups with ::) are learnable in ten minutes. What takes longer is internalizing that an interface does not have one address, it has several, and each one means something different.

A typical Linux interface running IPv6 will show you at least two addresses without any manual configuration:

$ ip -6 addr show eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP>
    inet6 2001:db8:1::a1b2:c3d4:e5f6:7890/64 scope global dynamic mngtmpaddr
       valid_lft 86392sec preferred_lft 14392sec
    inet6 fe80::1/64 scope link
       valid_lft forever preferred_lft forever

The fe80::/10 address is a link-local address. Every IPv6 interface gets one, derived from the MAC address or generated randomly. It is only valid on that specific link, and it is used by the protocol machinery before any global address is configured. The global 2001:db8:... address is the one routable on the wider network.

But there is also unique-local (fc00::/7, roughly analogous to RFC 1918 private space), and multicast (ff00::/8, which replaces broadcast and does so with a more complex group membership model). Every node also has several multicast addresses it joins implicitly, including the all-nodes address ff02::1 and a solicited-node multicast address derived from its own unicast address, which is how NDP works.

None of this is arbitrary. Each address type serves a purpose. But you now need to understand scope, lifetime, and the role each address plays before you can reason clearly about what a packet will actually do.

NDP Replaced ARP With Something That Does Much More

In IPv4, ARP is simple to understand. You want to know the MAC address for 192.168.1.5, you broadcast a request, the owner replies. It is a flat lookup with no moving parts.

RFC 4861 defines the Neighbor Discovery Protocol, which replaces ARP but also replaces a half-dozen other things that were handled separately in IPv4: router discovery, prefix advertisement, address autoconfiguration bootstrapping, duplicate address detection, and redirect messages. NDP uses ICMPv6 messages sent to multicast addresses rather than broadcasts, which is more efficient on large networks but harder to observe and debug.

Stateless Address Autoconfiguration (RFC 4862), usually called SLAAC, is the mechanism by which a host configures its own global address without a DHCP server. A router periodically sends Router Advertisement messages that include the network prefix. The host appends an interface identifier to that prefix and has a globally routable address. Before it can use that address, it runs Duplicate Address Detection: it sends a Neighbor Solicitation to the solicited-node multicast address for its tentative address and waits to see if anyone responds.

SLAAC is elegant. It is also stateless, which means the router does not know which address any given host chose. If you need to correlate an address to a hostname, you need either reverse DNS or DHCPv6, which are separate mechanisms that do not automatically interoperate with SLAAC.

Two Configuration Protocols, Neither Complete

The SLAAC-vs-DHCPv6 situation is one of the genuinely uncomfortable parts of IPv6 deployment. DHCPv6 (RFC 8415) can hand out addresses and configuration data, but it cannot by itself tell a host the default gateway. That information only comes from NDP Router Advertisements. So even in a DHCPv6 environment, you cannot turn off NDP; you still need Router Advertisements for the gateway.

The practical result is that network operators who want centralized address tracking need to run both SLAAC (or the RA mechanism at minimum) and DHCPv6, parse logs from both, and reconcile them. Tools like kea-dhcp6 from ISC support this, but the operational model is genuinely more complex than the IPv4 DHCP-only world.

On top of this, RFC 4941 defines privacy extensions, which cause hosts to generate random temporary addresses for outbound connections in addition to their stable addresses. This is a meaningful privacy improvement (the EUI-64 derived addresses that SLAAC originally used embedded the MAC address, readable by any server you connected to), but it means a single host may cycle through several global addresses over time, each valid for different durations.

RFC 7217 added yet another option: semantically opaque stable interface identifiers, which are random-looking but stable per-network. Linux has supported this since kernel 4.8 via net.ipv6.conf.*.addr_gen_mode. Choosing among these options requires understanding what threat model you care about.

Extension Headers and the Middlebox Problem

IPv6 eliminated the Options field from the fixed header and replaced it with a chain of extension headers between the base header and the upper-layer payload. The idea was clean: parsers that do not care about a given extension skip over it, only the endpoint processes what is relevant to it.

In practice, many middleboxes, firewalls, and load balancers do not follow that rule. They inspect packets up to a certain header count or byte offset, then either drop the packet or pass it without inspection if the chain extends further. RFC 7872 documented this empirically in 2016: packets with extension headers are dropped at measurably higher rates across the public Internet.

The Routing Header type 0 was deprecated by RFC 5095 because it could be used to amplify traffic in a manner similar to IP source routing attacks. This was a legitimate security concern, but it also illustrates how extension headers add a surface area that requires ongoing maintenance.

ICMPv6 Is Not Optional

In IPv4 networks, filtering ICMP is common and sometimes reasonable. In IPv6, filtering ICMPv6 breaks things at the protocol level. Path MTU Discovery, Neighbor Discovery, and SLAAC all depend on specific ICMPv6 types being delivered. RFC 4890 provides detailed guidance on which ICMPv6 messages must pass through firewalls, which can be filtered, and which should be filtered. Following it requires understanding the purpose of each message type rather than applying a blanket “block ICMP” rule.

The minimum MTU for IPv6 is 1280 bytes, and routers do not fragment packets in transit. Only the source host can fragment, and it should do so only when Path MTU Discovery fails. On networks where ICMPv6 type 2 (Packet Too Big) is filtered, connections to certain destinations silently stall after the TCP handshake because the data packets exceed the path MTU and the error is never delivered. This is the classic PMTUD black hole, and it is more prevalent in IPv6 than IPv4 because the protocol depends on it more fundamentally.

The Scoped Address API Problem

Link-local addresses have a scope problem that is often overlooked: fe80::1 on eth0 and fe80::1 on eth1 are different addresses on the same host, but the address string is the same. The way to disambiguate is the zone ID, appended with a percent sign: fe80::1%eth0.

This notation is specified in RFC 4007 and RFC 6874 (for URIs), but support is inconsistent. Many programs that accept addresses as command-line arguments handle it fine. Many URL parsers do not, because the % character has special meaning in URLs and the zone ID interacts awkwardly with percent-encoding. If you have ever tried to curl an IPv6 link-local address, you will have hit this:

# This usually fails or needs shell escaping
curl http://[fe80::1%eth0]:8080/

# Some versions need the zone ID percent-encoded
curl http://[fe80::1%25eth0]:8080/

The inconsistency across tools and libraries is a genuine operational friction that has nothing to do with the core protocol design.

Transition Is the Hardest Part

The thing that makes IPv6 deployment expensive is not learning IPv6. It is running both protocols simultaneously, for years. Dual-stack networks require every piece of network software to handle two address families, every monitoring system to correlate events across both, and every security policy to be expressed twice.

For carriers that cannot run dual-stack to end users, the options include NAT64 with DNS64, which translates IPv6-only client connections to IPv4 destinations at the network boundary, and 464XLAT (RFC 6877), which adds a stateless translator on the client side (CLAT) in front of a stateful translator at the carrier (PLAT). Mobile operators including T-Mobile in the United States have deployed 464XLAT at scale. It works, but the architecture involves three address translations that are invisible to the application developer and occasionally surface as subtle bugs when addresses are embedded in application payloads.

The Complexity Is Honest

When you lay it out like this, IPv6 is not complicated because the working groups made bad decisions. It is complicated because it is solving problems that did not exist when IPv4 was designed: auto-configuration without a server, privacy-preserving address generation, structured multicast at the protocol level, extensibility without fixed option fields. Each feature has a justification; the collective weight is real.

Carpenter’s framing in the linked document is charitable and accurate: the complexity often came from solving genuine problems, and many of the messier parts reflect the political and operational realities of deploying a new protocol on top of a running network. The dual-stack transition period was always going to be painful. After thirty years of IPv4 infrastructure investment, there was no other way.

What this means practically is that learning IPv6 is less like learning a new version of something and more like learning a different philosophy about what the network layer should do. The addresses, the neighbor discovery, the autoconfiguration, the multicast groups: they fit together if you understand the model. Getting to that understanding takes longer than reading one tutorial, and that is okay.

Was this interesting?