NET-110 · Lesson 6 of 12 · 28 min

IPv6

▶ Video coming soon A video version of this lesson is on the way.

When Sam logs into the firewall-router to look at Ridgeline's connection, the status page shows two addresses for the provider link: the familiar 203.0.113.42, and a much longer one beginning 2001:db8:5a00 with colons instead of dots. The provider has been offering IPv6 for over a year, and half of the phones that join the guest Wi-Fi already prefer it. Nobody at Ridgeline turned it on deliberately; the firewall's default settings did. Sam realizes that a network plan which ignores IPv6 describes only part of the network.

This lesson explains why IPv6 exists, how its 128-bit addresses are written and shortened, how they are divided into a routing prefix, a subnet and an interface identifier, the main address types, how devices configure their own addresses automatically, how IPv6 handles the jobs that ARP and broadcast do in IPv4, and how the two versions run side by side during the long transition.

Why IPv6?

IPv4's 32-bit address space holds about 4.3 billion addresses, fewer than one per person alive today, and far fewer than the number of phones, laptops, servers, cars, cameras and sensors that want to be online. The regional registries that hand out addresses ran out of new IPv4 blocks between 2011 and 2019. The internet has survived the shortage largely through network address translation, which lets a whole office or home share one public address, and through carrier-grade NAT inside providers' networks. Translation works, but it breaks the original design of the internet, in which any host could reach any other directly, and it adds cost and complexity to providers and applications alike.

IPv6, standardized in the late 1990s and updated since, uses 128-bit addresses: about 3.4 times 10 to the power 38 of them, enough to give every device its own globally unique address with plenty to spare. IPv6 also simplifies the packet header, drops broadcast in favour of multicast, builds automatic address configuration into the protocol, and removes fragmentation by routers. Adoption has grown steadily: large mobile networks, major content providers and many residential providers run IPv6 by default, and well over a third of the traffic reaching large websites now arrives over it.

Writing IPv6 addresses

An IPv6 address is written as eight groups of four hexadecimal digits separated by colons, each group representing 16 bits: 2001:0db8:5a00:0020:0000:0000:0000:0015. Two rules shorten that. First, leading zeros in any group may be dropped, so 0db8 becomes db8 and 0020 becomes 20 and 0000 becomes 0. Second, one run of consecutive all-zero groups may be replaced by a double colon. Applying both rules gives 2001:db8:5a00:20::15. The double colon may be used only once in an address, because otherwise the reader could not tell how many groups each one replaced; when there are two runs of zeros, the longer one is compressed, or the first if they are equal.

Shortening IPv6 addresses
Full formCompressed formNotes
2001:0db8:5a00:0020:0000:0000:0000:00152001:db8:5a00:20::15Leading zeros dropped, longest zero run replaced
fe80:0000:0000:0000:3e22:fbff:fe10:0021fe80::3e22:fbff:fe10:21A link-local address
0000:0000:0000:0000:0000:0000:0000:0001::1The loopback address
0000:0000:0000:0000:0000:0000:0000:0000::The unspecified address
2001:0db8:0000:0000:0001:0000:0000:00012001:db8::1:0:0:1Two equal zero runs: only the first is compressed

RFC 5952 recommends a single canonical way to write each address: lowercase letters, leading zeros removed, the longest run of two or more zero groups compressed. Python's ipaddress module follows those rules, so it is a handy way to check that addresses in documentation and configuration files are written consistently, and to expand a compressed address back to its full form when you need to count groups.

Canonical compressed form and fully expanded form of the same addresses
import ipaddress

for text in ('2001:0DB8:5A00:0020:0000:0000:0000:0015', 'fe80::3e22:fbff:fe10:21', '2001:db8:0:0:1:0:0:1', '::1'):
    a = ipaddress.ip_address(text)
    print(str(a).ljust(26), a.exploded)
Output
2001:db8:5a00:20::15       2001:0db8:5a00:0020:0000:0000:0000:0015
fe80::3e22:fbff:fe10:21    fe80:0000:0000:0000:3e22:fbff:fe10:0021
2001:db8::1:0:0:1          2001:0db8:0000:0000:0001:0000:0000:0001
::1                        0000:0000:0000:0000:0000:0000:0000:0001

Try it yourself

Expand a compressed IPv6 address

Without using the ipaddress module, write expand(addr) that returns the full form of an IPv6 address: eight groups of exactly four lowercase hexadecimal digits separated by colons. The input may drop leading zeros and may contain one ::.

Hint: split on :: first. The groups before and after it are known; fill the gap with as many 0000 groups as are needed to make eight in total.

def expand(addr):
    return ':'.join(group.zfill(4) for group in addr.split(':'))

print(expand('2001:db8:5a00:20::15'))

Prefixes, subnets and interface identifiers

IPv6 uses prefix lengths exactly as CIDR does for IPv4, but the conventional sizes are different and much more generous. A provider typically gives a business a /48 or a home a /56, and every LAN subnet is a /64. In a /64 subnet the first 64 bits are the network prefix and the last 64 bits are the interface identifier, which identifies the host. A /48 therefore leaves 16 bits, the fourth group, for numbering subnets: 65,536 subnets of /64 each, every one with room for 18 quintillion hosts.

The address 2001:db8:5a00:20::15 drawn as eight groups. The first three groups, 2001:db8:5a00, are shaded and labelled global routing prefix, /48 from the provider. The fourth group, 0020, is labelled subnet ID, chosen by Ridgeline to match VLAN 20. The last four groups are labelled interface ID, 64 bits identifying the host.

Ridgeline's provider assigned 2001:db8:5a00::/48. Sam's plan simply reuses the VLAN numbers as subnet IDs: VLAN 10, the office, becomes 2001:db8:5a00:10::/64; VLAN 20, payments, becomes 2001:db8:5a00:20::/64; and so on. There is no need for subnetting arithmetic, no need to size subnets for their host counts and no risk of running out: every LAN gets a /64, always. That simplicity is one of IPv6's quiet advantages. (The prefix 2001:db8::/32 is reserved for documentation, which is why it appears in this course instead of a real provider's prefix.)

One /64 per VLAN from a /48, using the VLAN number as the subnet ID
import ipaddress

site = ipaddress.ip_network('2001:db8:5a00::/48')
vlans = {10: 'office', 20: 'payments', 30: 'guest Wi-Fi', 40: 'roastery', 99: 'management'}
for vlan, name in vlans.items():
    subnet = ipaddress.ip_network((int(site.network_address) + (int(str(vlan), 16) << 64), 64))
    print(str(vlan).rjust(3), name.ljust(12), subnet)
print('subnets available in the /48:', 2 ** (64 - 48))
Output
 10 office       2001:db8:5a00:10::/64
 20 payments     2001:db8:5a00:20::/64
 30 guest Wi-Fi  2001:db8:5a00:30::/64
 40 roastery     2001:db8:5a00:40::/64
 99 management   2001:db8:5a00:99::/64
subnets available in the /48: 65536

The subnet ID here is the VLAN number written as hexadecimal digits that look the same as the decimal number: VLAN 20 becomes the group 20, which is really hexadecimal 0x20, decimal 32. That is purely a convenience for people reading the addresses, and it works for VLAN numbers made only of the digits 0 to 9.

Address types

IPv6 has several kinds of unicast address, plus multicast and anycast. A single interface normally has more than one IPv6 address at the same time, which surprises people used to IPv4.

IPv6 address types
TypePrefixScope and use
Global unicast2000::/3 (addresses starting 2 or 3)Routable on the internet, like a public IPv4 address
Link-localfe80::/10Valid only on one link; every IPv6 interface has one; used for neighbour discovery and as the router's next hop
Unique localfc00::/7 (in practice fd00::/8)Private addressing inside an organization, similar in purpose to RFC 1918
Loopback::1/128The host itself
Unspecified::/128No address yet, used as a source while a host configures itself
Multicastff00::/8Delivery to a group; replaces broadcast
AnycastAny unicast prefixThe same address on several devices; packets go to the nearest one

A few multicast addresses are worth recognizing: ff02::1 is all nodes on the link, ff02::2 is all routers on the link, and addresses beginning ff02::1:ff are solicited-node multicast groups used to find a neighbour's link-layer address efficiently. Anycast is used heavily by the root DNS servers and large public DNS resolvers: the same address is announced from dozens of locations, and the network delivers each query to the nearest one.

Try it yourself

What kind of IPv6 address is this?

Write ipv6_type(text) that returns one of 'loopback', 'link-local', 'unique local', 'multicast' or 'global' for an IPv6 address. Use the ipaddress module: check membership in the networks ::1/128, fe80::/10, fc00::/7 and ff00::/8; everything else counts as global for this exercise.

import ipaddress

def ipv6_type(text):
    if text.startswith('fe80'):
        return 'link-local'
    return 'global'

print(ipv6_type('FE80::1'), ipv6_type('fd12:3456::9'))

How IPv6 hosts get addresses

IPv6 hosts can configure themselves. When an interface comes up, it first creates a link-local address from fe80::/64 and checks that nobody else is using it, a step called duplicate address detection. It then sends a router solicitation to ff02::2, and routers reply with a router advertisement carrying the subnet's /64 prefix, the router's own link-local address to use as the default gateway, and flags saying how hosts should get the rest of their settings. With stateless address autoconfiguration, SLAAC, the host builds its own global address by combining the advertised prefix with an interface identifier it chooses itself.

Interface identifiers used to be derived from the MAC address by a method called modified EUI-64: split the 48-bit MAC in half, insert ff:fe in the middle and flip the seventh bit. That made addresses predictable and let devices be tracked across networks, so modern operating systems use random or stable-but-opaque identifiers instead, and also create short-lived temporary addresses for outgoing connections. The EUI-64 method is still used on many routers and still appears on exams.

Building an interface identifier from a MAC address with modified EUI-64
def eui64(mac):
    parts = [int(b, 16) for b in mac.split(':')]
    parts[0] ^= 0b00000010                       # flip the universal/local bit
    eui = parts[:3] + [0xff, 0xfe] + parts[3:]
    groups = [format(eui[i] << 8 | eui[i + 1], 'x') for i in range(0, 8, 2)]
    return ':'.join(groups)

mac = '3c:22:fb:10:00:21'
print('MAC address     ', mac)
print('interface ID    ', eui64(mac))
print('link-local      ', 'fe80::' + eui64(mac))
print('global (SLAAC)  ', '2001:db8:5a00:20:' + eui64(mac))
Output
MAC address      3c:22:fb:10:00:21
interface ID     3e22:fbff:fe10:21
link-local       fe80::3e22:fbff:fe10:21
global (SLAAC)   2001:db8:5a00:20:3e22:fbff:fe10:21

DHCPv6 also exists. In stateful DHCPv6, a server hands out addresses much as DHCP does for IPv4 and keeps a record of which host has which, which some organizations prefer for tracking. In stateless DHCPv6, hosts build their own addresses with SLAAC but ask a DHCPv6 server for other settings, such as DNS servers; router advertisements can also carry DNS servers directly. Ridgeline's firewall-router uses SLAAC with DNS servers included in its advertisements, the simplest arrangement and the default on most small business equipment.

Neighbour discovery replaces ARP

IPv6 has no ARP and no broadcast. Their jobs, and several others, are done by the Neighbor Discovery Protocol, part of ICMPv6. To find the link-layer address of a neighbour, a host sends a neighbour solicitation to the neighbour's solicited-node multicast address, which only a handful of devices listen to, and the neighbour answers with a neighbour advertisement containing its MAC address. Routers announce themselves with router advertisements, and hosts ask for them with router solicitations. Because these functions run over ICMPv6, firewalls must allow the necessary ICMPv6 messages; blocking ICMPv6 wholesale, a habit carried over from IPv4, breaks IPv6 completely.

Running IPv4 and IPv6 together

The transition from IPv4 to IPv6 has lasted more than two decades and will continue for many years. The most common approach is dual stack: every device and router runs both protocols at once, with both kinds of address, and applications use IPv6 when the destination supports it and IPv4 otherwise. Operating systems race the two in parallel and use whichever connects first, a technique called happy eyeballs, so a broken IPv6 path costs only a fraction of a second. Other mechanisms carry one protocol inside the other, called tunnelling, or translate between them, such as NAT64 combined with DNS64, which lets IPv6-only devices reach IPv4-only servers and is common on mobile networks.

Ridgeline runs dual stack: every VLAN will get both an IPv4 subnet and an IPv6 /64. The firewall must have rules for both. A rule that blocks guests from reaching the office over IPv4 is useless if the same traffic can flow over IPv6, a gap that is easy to leave open because the IPv6 side is often enabled by default and forgotten. Documentation, monitoring and troubleshooting must all cover both protocols.

Worked problem

Reading an interface with several IPv6 addresses

Sam runs the command that lists the office laptop's addresses and sees: 10.20.10.37/24, fe80::9a1c:4422:1b0e:7d10/64, 2001:db8:5a00:10:5c3e:91ff:2a40:8c11/64 and 2001:db8:5a00:10:e1d4:73b2:9a5c:4f02/64. Jordan asks whether the laptop is misconfigured. Explain each address.

Show solution
  1. 10.20.10.37/24 is the IPv4 address in the office VLAN, from DHCP.
  2. fe80::9a1c:4422:1b0e:7d10 is the link-local address that every IPv6 interface creates; it is used on the local link only, for neighbour discovery and to talk to the router.
  3. The first 2001:db8:5a00:10:: address is a global address built by SLAAC from the office prefix with a stable interface identifier; it stays the same on this network.
  4. The second 2001:db8:5a00:10:: address is a temporary privacy address, used for outgoing connections and replaced every day or so.
  5. Nothing is wrong: several IPv6 addresses per interface is normal and expected.

Answer: The laptop is dual stack and correctly configured: one IPv4 address, one link-local, one stable global and one temporary global IPv6 address.

Common mistakes

  • MistakeUsing :: more than once in one address.

    InsteadCompress only one run of zero groups; the reader must be able to count the missing groups.

  • MistakeSubnetting LANs to sizes smaller than /64.

    InsteadUse a /64 for every LAN; SLAAC and other features depend on it.

  • MistakeBlocking all ICMPv6 at the firewall.

    InsteadAllow the ICMPv6 messages IPv6 needs, such as neighbour discovery and packet too big.

  • MistakeWriting firewall rules for IPv4 only on a dual-stack network.

    InsteadApply equivalent rules to IPv6, or the same traffic simply goes around them.

  • MistakeExpecting one IPv6 address per interface.

    InsteadInterfaces normally have a link-local address and one or more global addresses.

  • MistakeForgetting that hex digits a to f are part of IPv6 addresses.

    InsteadTreat each group as four hexadecimal digits, and write them in lowercase.

Summary and key terms

  • IPv6 uses 128-bit addresses to end the IPv4 shortage and restore end-to-end addressing.
  • Addresses are eight groups of four hex digits; drop leading zeros and replace one zero run with ::.
  • LAN subnets are /64: a 64-bit prefix (routing prefix plus subnet ID) and a 64-bit interface identifier.
  • Key types: global unicast (2000::/3), link-local (fe80::/10), unique local (fd00::/8), multicast (ff00::/8), loopback ::1.
  • Hosts configure themselves with SLAAC from router advertisements; DHCPv6 can add or replace this.
  • Neighbor Discovery over ICMPv6 replaces ARP; IPv6 has no broadcast.
  • Dual stack runs IPv4 and IPv6 together; security rules and documentation must cover both.
IPv6
Internet Protocol version 6, with 128-bit addresses.
Hextet
One 16-bit group of an IPv6 address, written as up to four hex digits.
Interface identifier
The host part of an IPv6 address, normally the last 64 bits.
Global unicast address
A publicly routable IPv6 address, from 2000::/3.
Link-local address
An address valid on one link only, from fe80::/10.
Unique local address
A private IPv6 address from fc00::/7, normally fd00::/8.
SLAAC
Stateless address autoconfiguration: hosts build their own addresses from advertised prefixes.
Router advertisement
An ICMPv6 message in which routers announce prefixes and themselves as gateways.
Neighbor Discovery Protocol
The ICMPv6-based protocol that replaces ARP and finds routers.
Dual stack
Running IPv4 and IPv6 at the same time on the same devices.

Practice problems

Work each problem on paper first, then reveal the answer to check.

  1. How many bits are in an IPv6 address?

    Show answer

    128.

  2. Compress 2001:0db8:0000:0000:0000:0000:0000:0042.

    Show answer

    2001:db8::42.

  3. Why can :: appear only once in an address?

    Show answer

    Otherwise you could not tell how many zero groups each one replaces.

  4. What prefix length is used for an IPv6 LAN?

    Show answer

    /64.

  5. How many /64 subnets fit in a /48?

    Show answer

    2^16 = 65,536.

  6. What does an address starting fe80 tell you?

    Show answer

    It is link-local, valid only on that link.

  7. Which IPv6 range is similar in purpose to RFC 1918 private addresses?

    Show answer

    Unique local addresses, fd00::/8 in practice.

  8. What replaces ARP in IPv6?

    Show answer

    Neighbor Discovery (neighbour solicitations and advertisements over ICMPv6).

  9. What does SLAAC need from the router?

    Show answer

    A router advertisement carrying the /64 prefix (and its own address as gateway).

  10. Why must IPv6 firewall rules be reviewed on a dual-stack network?

    Show answer

    Traffic blocked over IPv4 may still flow over IPv6 if equivalent rules are missing.

Lesson quiz · 5 questions

Score 60% or more to complete this lesson.

Take the quiz