- Mon 08 June 2026
- 16 min read
- Networking
- #ipv6, #networking, #slaac, #ndp, #icmp, #foundations

Let me start with the thing nobody wants to say out loud: IPv6 is not “the future of the internet”. It is the internet. It has been a finished, deployed, standards-track protocol since 1998. Major mobile networks run IPv6-only internally. More than half of the traffic Google sees from many countries is already IPv6. The “future” framing is a comfortable lie we tell ourselves so we can keep treating IPv4 as the default and IPv6 as the weird optional thing the network nerd insists on.
So let me reframe it for this article. IPv6 is the current version of the Internet Protocol. IPv4 is the legacy one. It is a 32-bit address space from 1981 that ran out of room more than a decade ago, kept alive on life support by NAT, carrier-grade NAT, and an aftermarket where people trade /24s like baseball cards. It works, in the way a 40-year-old car works: lovingly, expensively, and only because a lot of people refuse to let it die.
This is a foundations post. I am going to cover the actual basics: how an IPv6 address is built, how to shorten it, how it maps onto the IPv4 you already understand, and how a host configures itself with SLAAC. There is a short sidebar on NDP, and a small rant about why you must not block ICMP. Keep your shoes off, this is meant to be relaxed.
Table of Contents
- Table of Contents
- The address: 128 bits, written in hex
- Shortening: getting rid of the zeros
- Structure: the prefix and the interface
- What is the same as IPv4 (more than you would think)
- SLAAC: how a host configures itself
- Sidebar: NDP, and why ICMP is not optional
- Dual-stack is a burden, not a solution
- Where to go from here
The address: 128 bits, written in hex
An IPv4 address is 32 bits. We write it as four decimal numbers, one per byte, separated by dots: 192.0.2.10. That gives you about 4.3 billion addresses total, which sounded enormous in 1981 and is comically small for a planet with more connected devices than people.
An IPv6 address is 128 bits. That is not “four times bigger”, it is 2^128 versus 2^32, which is a number so large it stops being meaningful. You get 340 undecillion addresses, give or take. The standing joke, which is only barely a joke, is that there are enough IPv6 addresses to assign one to every atom on the surface of the Earth and still have plenty left over for several more planets.
Writing 128 bits as decimal bytes would be miserable, so IPv6 uses hexadecimal. The address is split into eight groups of 16 bits, each group written as up to four hex digits, separated by colons:
2001:0db8:0000:0000:0000:ff00:0042:8309
That is one full, uncompressed IPv6 address. Eight groups, four hex digits each, colons in between. Each hex digit is 4 bits, four digits is 16 bits, eight groups is 128 bits. The math is tidy even if the result looks intimidating at first.
2001:db8::/32 is the documentation range, by the way, the IPv6 equivalent of 192.0.2.0/24. I will use it throughout so I am not leaking real addresses.
Shortening: getting rid of the zeros
Nobody types the full form above, and you should not either. There are exactly two rules for shortening an IPv6 address, and once they click you will read these things at a glance.
Rule 1: drop leading zeros in each group. Within any single group, leading zeros are noise. 0db8 becomes db8. 0000 becomes 0. 0042 becomes 42. ff00 stays ff00 because there is nothing leading to drop. Apply that to the address above:
2001:db8:0:0:0:ff00:42:8309
Already much friendlier. Note that this is leading zeros only. You cannot touch trailing or middle zeros inside a group, because the position of a digit is its value. 8309 stays 8309.
Rule 2: collapse one run of all-zero groups with ::. A double colon means “as many all-zero groups as it takes to make the address 128 bits again”. Our address has a run of three 0 groups in the middle, so:
2001:db8::ff00:42:8309
That is the canonical, shortened form. A parser sees ::, counts the groups present on either side (here: two on the left, three on the right, five total), and fills in the missing three groups with zeros to reach eight. Clean.
The one catch, and it is the only rule that ever trips people up: you may use :: exactly once per address. If you used it twice, the parser could not know how many zero-groups belong to each gap. So 2001:db8:0:0:1:0:0:1 collapses to 2001:db8::1:0:0:1 or 2001:db8:0:0:1::1, but never 2001:db8::1::1. When there is a choice, convention says collapse the longest run, and if two runs tie, collapse the first one.
A few useful special cases fall straight out of these rules:
::1is the loopback address. The whole thing is zeros except the final bit. This is IPv6’s127.0.0.1, except it is a single address instead of an entire wasted /8.::(just the double colon, all zeros) is the unspecified address, used as a source while a host is still figuring out who it is.2001:db8::is2001:0db8:0000:...:0000, a prefix with everything after it zeroed.
Structure: the prefix and the interface
Here is where IPv6 quietly becomes easier than IPv4, not harder.
An IPv6 address has two halves. The left half is the network prefix (which subnet you are on), and the right half is the interface identifier (which host you are within that subnet). The slash notation works exactly like CIDR in IPv4: /64 means the first 64 bits are the network and the rest is the host.
And here is the gift: in the overwhelming majority of cases, the boundary is /64, full stop. A standard IPv6 subnet is a /64. Not “depends on how many hosts you have”, not “let me get the subnet calculator”. A /64. Every LAN, every VLAN, every normal segment gets a /64, which contains 2^64 addresses, which is roughly 18 quintillion hosts on a single subnet. You will not run out.
The prefixes you actually deal with as a network operator:
- /64 - one subnet. The atom of IPv6 addressing. SLAAC (below) requires a /64 to work.
- /56 - a typical home or small-site allocation. That is 256 /64 subnets, one prefix handed to your router by your ISP, plenty for VLANs, guest networks, and IoT segregation.
- /48 - a site allocation. 65,536 /64 subnets. This is what I run for my own AS; the prefix is
2a06:9801:1c::/48, carved into /64s per jail and per VLAN. If you want the BGP side of that story, see Running Your Own AS.
The mental shift is that you stop hoarding addresses. In IPv4 you agonize over whether a subnet should be a /27 or a /28 because every address is precious. In IPv6 you hand out /64s like business cards and spend your thinking budget on the network design instead of the arithmetic.
Address scopes you will meet
IPv6 leans on the idea that an interface has several addresses at once, each with a scope. The ones worth knowing:
- Link-local (
fe80::/10): every IPv6 interface has one, automatically, always. It is only valid on the local link and is never routed. This is the address NDP and your routing protocols actually talk over. If you have ever seenfe80::...%eth0, the%eth0is the zone index telling the kernel which link, because the same link-local address can exist on every interface. - Unique Local Addresses (
fc00::/7, in practicefd00::/8): the IPv6 answer to RFC 1918 private space (10.0.0.0/8and friends). Routable inside your site, not on the public internet. Useful, but do not build your whole world on them. - Global Unicast (
2000::/3): real, publicly routable addresses. The2001:db8::documentation range lives here, and so does every address your ISP delegates to you. - Multicast (
ff00::/8): IPv6 has no broadcast. None. Where IPv4 shouts at255.255.255.255, IPv6 uses targeted multicast groups, for exampleff02::1(all nodes on the link) andff02::2(all routers on the link). This is a genuine improvement: instead of waking every NIC on the segment, you talk only to the machines that signed up to listen.
What is the same as IPv4 (more than you would think)
It is easy to look at the colons and the hex and assume IPv6 is an alien protocol. It is not. It is Internet Protocol, the same job as IPv4, with the address field made sane. Almost everything you know carries straight over:
- It is still routed by longest-prefix match. A router looks at the destination, finds the most specific matching prefix in its table, forwards. Same algorithm, wider addresses.
- TCP and UDP ride on top unchanged. A socket is a socket. Your web server, your database, your SSH daemon do not care; they bind to an address and a port.
[2001:db8::1]:443is just an address and a port with brackets so the colons do not get confused with the port separator. - DNS works the same, you just publish
AAAArecords instead of (or alongside)Arecords.example.com. AAAA 2001:db8::1. Resolvers, zones, TTLs, all identical. - CIDR, prefixes, and routing tables behave the same.
/64,/48,/32, default route::/0instead of0.0.0.0/0. Same concepts. - The transport security, the application layer, TLS, HTTP, all of it sits on top and never knew which IP version carried the packets.
The differences are mostly removals, and they are removals of bad ideas:
- No NAT. Every device can have a real, globally routable address again, the way the internet was supposed to work before we ran out of room. End-to-end connectivity stops being a luxury. (You still want a firewall. A firewall is not NAT, and conflating the two is how a generation came to believe address translation was a security feature.)
- No broadcast, as mentioned. Multicast replaces it and does the job more politely.
- Routers do not fragment. In IPv4, a router could chop up a too-big packet. In IPv6 that is the sender’s job, via Path MTU Discovery, which leads directly into why you must not block ICMP. Hold that thought.
- The header is simpler and fixed-length with extension headers bolted on as needed, which makes high-speed forwarding easier.
SLAAC: how a host configures itself
This is the part that genuinely delights people the first time they see it. In IPv4-land, a host that wants an address asks a DHCP server, which hands out a lease from a pool it manages, tracks state, and hopes nobody unplugs anything. IPv6 can do that too (DHCPv6), but it also has something better for most cases: SLAAC, Stateless Address Autoconfiguration.
Here is the whole dance, and notice that there is no server keeping a database anywhere:
- The interface comes up and immediately gives itself a link-local address in
fe80::/10. No configuration, no asking. It derives the host part and checks it is unique on the link (Duplicate Address Detection, which is just NDP again). - The host sends a Router Solicitation (RS) to
ff02::2, the all-routers multicast group. Translation: “any routers out there, what is the prefix on this link?” - A router replies with a Router Advertisement (RA). The RA carries the
/64prefix for the link, the default gateway (the router’s own link-local address), and a set of flags. Routers also send RAs periodically and unprompted, so a host often does not even need to ask. - The host takes the advertised
/64prefix and generates the host half itself, then bolts the two together into a full global address. No server assigned it. The host made it up within the prefix it was told to use, and because a /64 has 18 quintillion slots, collisions are a non-issue (and DAD catches them anyway).
How the host picks its own interface ID has two schools:
- EUI-64: derive it deterministically from the MAC address. Stable, but it leaks your hardware address and lets you be tracked across networks. Largely out of fashion for client devices for exactly that reason.
- Privacy / temporary addresses (RFC 4941, and the more modern stable-but-opaque RFC 7217): generate a random-looking interface ID, rotate the temporary ones periodically. This is what your laptop and phone do today. You will routinely see a machine holding several IPv6 addresses at once: a stable one for inbound, a rotating temporary one for outbound. That is working as intended, not a bug.
“But where does DNS come from if there is no DHCP?” Modern RAs answer that too, via the RDNSS option (RFC 8106), which carries recursive DNS server addresses right inside the advertisement. So a host can come up on a fresh network and end up with an address, a gateway, and working DNS, having configured everything itself from a couple of multicast packets. No DHCP server, no lease database, no scope exhaustion at 3am. That is the part worth being a little smug about.
Sidebar: NDP, and why ICMP is not optional
The short version: In IPv6, ICMP is not a diagnostic afterthought you can firewall away. It is load-bearing. Block it and the network stops working, not “works worse”, stops.
In IPv4, ARP is its own separate protocol that maps IP addresses to MAC addresses with broadcast “who has 192.0.2.1?” shouts. ICMP, meanwhile, is the optional-feeling thing you use for ping and that a lot of paranoid admins reflexively block at the firewall “for security”.
IPv6 throws ARP away and replaces it with the Neighbor Discovery Protocol (NDP), and NDP is built entirely on top of ICMPv6. Everything I described in the SLAAC section is ICMPv6 messages:
- Router Solicitation / Router Advertisement (the RS/RA from SLAAC) are ICMPv6 types 133 and 134.
- Neighbor Solicitation / Neighbor Advertisement are ICMPv6 types 135 and 136. This is the ARP replacement: “who has
2001:db8::1?” answered with “I do, here is my MAC.” It rides on multicast instead of broadcast, but functionally it is ARP’s job done over ICMP. - Duplicate Address Detection and Redirects are NDP too.
So when someone copies their old IPv4 firewall habit and writes “drop all ICMP” on an IPv6 interface, they have not hardened anything. They have unplugged address resolution, router discovery, and autoconfiguration. The host cannot find its neighbors or its gateway. The network is dead, and the admin spends an afternoon confused.
It gets one notch worse because of the no-fragmentation-by-routers rule from earlier. IPv6 relies on Path MTU Discovery, where a router that cannot forward an oversized packet sends back an ICMPv6 “Packet Too Big” (type 2) message so the sender can shrink its packets. Block that message and you get the classic nightmare: small packets and pings work fine, but large transfers and TLS handshakes hang forever with no obvious cause. It is one of the most maddening failure modes in networking, and it is almost always someone over-filtering ICMPv6.
The rule is simple. Do not block ICMPv6 wholesale. Permit Neighbor Discovery, permit Router Advertisements where you expect them, permit Packet Too Big, permit the essentials (RFC 4890 spells out exactly which types to allow). Rate-limit if you must, filter specific types thoughtfully, but the blanket “drop ICMP” rule that was merely rude on IPv4 is actively self-destructive on IPv6. ICMPv6 is part of the control plane now, not a luxury.
Dual-stack is a burden, not a solution
The pitch for dual-stack is reasonable on its face: run IPv4 and IPv6 side by side, let clients prefer IPv6, and you have a smooth glide path. In practice, dual-stack is not a destination. It is the most expensive possible state, and we have been parked in it for over a decade.
Think about what running both actually costs you. Every firewall rule exists twice, in two syntaxes, with two chances to disagree. Every DNS name needs an A and an AAAA, and the day they point at different backends is the day you debug a “works for me” ticket for three hours. Every monitoring check, every ACL, every load-balancer config, every piece of address-parsing code in your stack has to handle two formats forever. Security review surface doubles. Happy Eyeballs papers over connection failures by racing both stacks, which is clever and also means a half-broken IPv6 path can hide for months because clients silently fall back to IPv4.
Dual-stack made sense as a transition mechanism. The problem is that “transition” implies you eventually arrive somewhere, and the somewhere is IPv6-only. Running both indefinitely is not having the best of both worlds, it is paying full price for two networks and getting the operational headaches of each. The mobile carriers worked this out years ago, which is why so many of them are IPv6-only at the core with IPv4 stapled on at the edge as a translation service for the legacy internet, rather than the other way around.
I am not telling you to rip IPv4 out tomorrow; the legacy internet still exists and your users still need to reach it. I am telling you to stop treating IPv4 as the foundation and IPv6 as the add-on. Flip the polarity. Design IPv6-first, make IPv6 the path that works without caveats, and treat IPv4 as the compatibility shim it has become, a thing you provide grudgingly at the edge for the parts of the world that have not caught up yet.
Where to go from here
That is the foundation: 128-bit addresses written in hex, two simple rules for crushing out the zeros, a /64 per subnet so you stop counting hosts, the same routing and DNS and transport you already know, SLAAC so machines configure themselves, and NDP-over-ICMPv6 holding it all together so you really, truly must not block ICMP.
If you want to go deeper, the natural next steps from here are getting your own globally routable space and announcing it, which I covered in Running Your Own AS, and then firewalling it properly on the platform of your choice. The point of this post was just to knock the intimidation off. IPv6 is not exotic. It is the normal way to do networking, and it has been for a long time. The hex is the only scary part, and now even that is not.