Thuta Learning
AdvancedDevOps & Toolsbeginner

IPv6 Essentials

What you'll walk away with

  • Explain the core ideas behind IPv6 Essentials
  • Read the diagram and trace how data actually moves through it
  • Run the sample code and verify its output

Build the mental model

IPv4 has 32 bits, about 4.3 billion addresses, and a large fraction of those were never usable. The regional registries ran out years ago. NAT bought the internet an extra two decades by letting an entire network hide behind one public address, but it did so by breaking the end-to-end assumption: a host behind NAT cannot be connected to unless something outside holds a mapping open, which is why peer-to-peer, VoIP, and games all carry elaborate traversal machinery that exists purely to work around NAT.

IPv6 uses 128 bits, written as eight groups of four hex digits separated by colons. Two rules shorten it: leading zeros in a group may be dropped, and exactly one run of consecutive all-zero groups may be replaced with a double colon. The exactly one is not a style preference, it is a parsing requirement. Two double colons in the same address would be ambiguous, because there would be no way to know how many zero groups belong on each side. One is solvable; two is not.

Every IPv6 interface also holds a link-local address starting fe80::, generated automatically and valid only on its own segment. It is never routed, and it is what neighbour discovery and router advertisements actually run over, so an interface can have a perfectly working link-local address and still have no internet connectivity at all.

Hosts get global addresses two ways. With SLAAC the router advertises a prefix and the host builds its own address, no server involved. DHCPv6 works more like IPv4's DHCP, with a server handing out specific addresses. Many networks run both.

Nobody flips a switch. The migration path is dual-stack: run both protocols, publish A and AAAA records, and let clients prefer IPv6 when it works.

text
IPV4 VS IPV6 ADDRESS STRUCTURE AND COMPRESSION
----------------------------------------------
IPv4 -- 32 bits, four decimal octets

    203  .   0   .  113  .   42
   +-----------------+---------+
   |     network     |  host   |   boundary set by the mask
   +-----------------+---------+   (here: /24)


IPv6 -- 128 bits, eight hex groups of 16 bits

   2001 : 0db8 : abcd : 1234 : 0000 : 0000 : 0000 : 0010
   +--------------------------+--------------------------+
   |      /64 prefix          |  interface identifier    |
   +--------------------------+--------------------------+
    routing prefix + subnet     the host's own 64 bits


COMPRESSION, STEP BY STEP

   full         2001:0db8:0000:0000:0000:ff00:0042:8329
   drop zeros   2001:db8:0:0:0:ff00:42:8329
   collapse     2001:db8::ff00:42:8329
                         ^^
                         one run of zero groups, ONE time

   INVALID      2001:db8::1::2
                how many zero groups go on the left, and
                how many on the right? unanswerable, so
                a second '::' is forbidden outright.


ADDRESS KINDS ON ONE INTERFACE

   fe80::...    link-local  auto-made, never routed off-link
   2001:db8:... global      routable, from SLAAC or DHCPv6
   ::1          loopback    the IPv6 127.0.0.1
   ff02::1      multicast   all nodes on this link

Connect it to a real scenario

The dual-stack failure mode you will actually meet is a service that is slow for some users after someone adds an AAAA record.

Here is the mechanism. Publishing an AAAA record tells every client that IPv6 is available. Clients following Happy Eyeballs try IPv6 first and fall back to IPv4 if the path is broken, but that fallback costs a timeout. So users on a partially broken IPv6 path see multi-second delays on their first connection while everyone else is fine. Adding an AAAA record is therefore a commitment: the address must work from the public internet, not merely exist on the interface.

Test it as a client, not as the server. From an outside network, resolve the AAAA record and connect to that address with curl -6. If that hangs while curl -4 succeeds, your IPv6 path is broken and the record should come down until it is fixed.

The other habit worth building early is writing code that does not assume 32 bits. Parse addresses with a library rather than a regex, size database columns for 45 characters, and remember that an IPv6 address in a URL must be bracketed, as in http://[2001:db8::1]:8080/, or the colons read as a port separator. The example below uses Python's ipaddress module for compression, expansion, prefix math, and a dual-stack check.

Try the working example

python
import ipaddress

# Every one of these is the SAME address written differently. The rules:
# drop leading zeros in a group, and collapse ONE run of all-zero groups
# to '::'. Python always prints the canonical shortest form.
FORMS = [
    "2001:0db8:0000:0000:0000:ff00:0042:8329",
    "2001:db8:0:0:0:ff00:42:8329",
    "2001:db8::ff00:42:8329",
]

print("all three of these are one address:")
for text in FORMS:
    addr = ipaddress.IPv6Address(text)
    print("  " + text.ljust(40) + " -> " + str(addr))

print("")
# exploded shows every group padded back out -- useful when you need to
# eyeball a prefix boundary.
one = ipaddress.IPv6Address("2001:db8::ff00:42:8329")
print("compressed: " + str(one))
print("exploded:   " + one.exploded)
print("128 bits as an integer: " + str(int(one)))

print("")
# Why only ONE '::' is allowed: two of them would be ambiguous.
print("why only one '::':")
print("  2001:db8::1::2  would be unparseable -- 'how many zero groups")
print("  went on the left, and how many on the right?' has no answer.")
try:
    ipaddress.IPv6Address("2001:db8::1::2")
except ipaddress.AddressValueError:
    print("  python agrees: AddressValueError")

print("")
# Prefix math. /64 is the near-universal subnet size, which is why the
# second half of an address is called the interface identifier.
net = ipaddress.IPv6Network("2001:db8:abcd:1234::/64")
print("network:     " + str(net))
print("first addr:  " + str(net[0]))
print("host bits:   " + str(net.max_prefixlen - net.prefixlen))
print("addresses:   " + str(net.num_addresses))

print("")
# Address categories a host actually holds at once.
CANDIDATES = [
    "fe80::1c2b:3aff:fe4d:5e6f",
    "2001:db8:abcd:1234::10",
    "::1",
    "ff02::1",
]
for text in CANDIDATES:
    a = ipaddress.IPv6Address(text)
    kind = "global unicast"
    if a.is_link_local:
        kind = "link-local (SLAAC, never routed off-link)"
    elif a.is_loopback:
        kind = "loopback"
    elif a.is_multicast:
        kind = "multicast (all-nodes)"
    print("  " + text.ljust(26) + kind)

print("")
# Dual-stack: one name, two families. Code must handle both.
print("dual-stack host:")
for text in ["203.0.113.42", "2001:db8:abcd:1234::10"]:
    a = ipaddress.ip_address(text)
    print("  " + text.ljust(26) + "IPv" + str(a.version) +
          "  " + str(a.max_prefixlen) + " bits")
You should see
all three of these are one address:
  2001:0db8:0000:0000:0000:ff00:0042:8329  -> 2001:db8::ff00:42:8329
  2001:db8:0:0:0:ff00:42:8329              -> 2001:db8::ff00:42:8329
  2001:db8::ff00:42:8329                   -> 2001:db8::ff00:42:8329

compressed: 2001:db8::ff00:42:8329
exploded:   2001:0db8:0000:0000:0000:ff00:0042:8329
128 bits as an integer: 42540766411282592856904265327123268393

why only one '::':
  2001:db8::1::2  would be unparseable -- 'how many zero groups
  went on the left, and how many on the right?' has no answer.
  python agrees: AddressValueError

network:     2001:db8:abcd:1234::/64
first addr:  2001:db8:abcd:1234::
host bits:   64
addresses:   18446744073709551616

  fe80::1c2b:3aff:fe4d:5e6f link-local (SLAAC, never routed off-link)
  2001:db8:abcd:1234::10    global unicast
  ::1                       loopback
  ff02::1                   multicast (all-nodes)

dual-stack host:
  203.0.113.42              IPv4  32 bits
  2001:db8:abcd:1234::10    IPv6  128 bits

5-minute try-it

Add 2001:db8:0:0:1:0:0:1 to FORMS and see which run of zeros Python collapses: when there are two equal runs, which one wins? Then split the /64 network with subnets(new_prefix=68) and print how many subnets you get.

One important caution

Publishing an AAAA record before the IPv6 path actually works end-to-end: clients try IPv6 first, so users eat a fallback timeout.

Validating addresses with a hand-written regex or sizing a database column at 15 characters: IPv6 text form needs up to 45 characters and has many valid spellings.

RFC 4291 - IP Version 6 Addressing ArchitectureComputer Networking

Easy traps

  • Publishing an AAAA record before the IPv6 path actually works end-to-end: clients try IPv6 first, so users eat a fallback timeout.
  • Validating addresses with a hand-written regex or sizing a database column at 15 characters: IPv6 text form needs up to 45 characters and has many valid spellings.
  • Validate sample code in a local or test environment before applying it to a production network.

Exercise

Add 2001:db8:0:0:1:0:0:1 to FORMS and see which run of zeros Python collapses: when there are two equal runs, which one wins? Then split the /64 network with subnets(new_prefix=68) and print how many subnets you get.

You'll know it worked when: all three of these are one address: 2001:0db8:0000:0000:0000:ff00:0042:8329 -> 2001:db8::ff00:42:8329 2001:db8:0:0:0:ff00:42:8329 -> 2001:db8::ff00:42:8329 2001:db8::ff00:42:8329 -> 2001:db8::ff00:42:8329 compressed: 2001:db8::ff00:42:8329 exploded: 2001:0db8:0000:0000:0000:ff00:0042:8329 128 bits as an integer: 42540766411282592856904265327123268393 why only one '::': 2001:db8::1::2 would be unparseable -- 'how many zero groups went on the left, and how many on the right?' has no answer. python agrees: AddressValueError network: 2001:db8:abcd:1234::/64 first addr: 2001:db8:abcd:1234:: host bits: 64 addresses: 18446744073709551616 fe80::1c2b:3aff:fe4d:5e6f link-local (SLAAC, never routed off-link) 2001:db8:abcd:1234::10 global unicast ::1 loopback ff02::1 multicast (all-nodes) dual-stack host: 203.0.113.42 IPv4 32 bits 2001:db8:abcd:1234::10 IPv6 128 bits

IPv6 Essentials | Thuta Learning