IPv6 addresses are broken up into two halves: the network prefix and the interface identifier. It looks something like this, in hex notation:
The alphabetical and bold parts of this are the network prefix and the numerical are the interface identifier.
The network prefix is then divided into a routing prefix (controlled by the ISP) and a subnet prefix (controlled by home network). ISPs will allocate a routing prefix of (in CIDR notation) /56, /60 or /64—meaning the first 56, 60 or 64 bits are reserved—and the rest are available to play with for the subnet. Routers generally request a /64 from upstream by default, which is one IPv6 network.
The interface identifier is picked by the device connecting to the network using SLAAC after being told which network prefixes are available from a router announcement. DHCPv6 is typically overkill and unnecessary on a home network.
In IPv4 a private address space like
192.168.0.0/16 is used locally and translated to a public address on the internet via NAT since there’s way too few IPv4 addresses for the number of devices that need to get on the internet. This NAT requirement also turned into a benefit in a home network as it is easier to use and memorize these shorter addresses.
Every IPv6 guide bemoans this private address desire and instead suggests adopting and using global addresses instead. The advice on the internet about IPv6 addressing is almost entirely focused on businesses who request and receive a static IPv6 prefix from a regional registry and completely overlook the benefits to home users without major infrastructure to handle configurations. However, as a home user:
- The global address may change. In fact, it’s guaranteed on my home network because the AT&T modem randomly hands out one of its non-reserved /64s depending on the mood it’s in.
- It’s a lot to memorize and type. Focusing on just the interface identifier portion is all that really matters in a local network. That part’s unlikely to change, and can be hard-coded on individual devices if desired.
Unlike IPv4, devices can and will have multiple simultaneous IPv6 addresses:
- One or two (or more) global addresses.
- One or more local addresses (ULAs). Yes, local addresses are a thing!
For global addresses, non-end-user devices will likely choose a random interface identifier and rotate it regularly to avoid external tracking. For servers and for local addresses, devices will use a modified version of their MAC address (EUI) to have a stable interface identifier.
It’s important, then, to allow devices to have a local address. This is shockingly not the default on consumer routers. The router should announce:
- A global address prefix allocated by the ISP. For example,
- A local address prefix. For example,
fd00::/64. There’s a suggested algorithm for generating these but that’s overkill for a home network unlikely to ever need to network with another privately. Keep it simple.
To implement this on pfSense, assuming “turn on IPv6” is checked:
- Set the WAN interface to use DHCPv6 to get a /64 prefix delegation.
- Set the LAN interface to “Track Interface” from the WAN interface.
- Add a virtual IP address (like
fd00::1/64) to the router.
- Add a router announcement subnet (under DHCPv6/RA) of
fd00::/64. The one the LAN tracks will automatically be announced, even if not entered.
- Set the DNS server (under DHCPv6/RA) to
- Make sure you’ve got IPv6-allowed-to-
*rules in your LAN.
The router will now periodically and when queried announce to the network which address prefixes to use, what DNS server to talk to, and various other configuration.
The LAN interface of the router can now be addressed at
fd00::1 and network devices will pick stable addresses in the
fd00::/64 network (e.g.
fd00::a9cd:efff:feab:cdef); they can be used in configurations without worrying about changing, and IPv6 will continue to work locally even without internet.
Some devices may also advertise their own local prefixes; Thread uses IPv6 so aggressively that its devices will often send out an RA of a random local prefix if they do not pick one up on their own. Apple HomePod minis are particularly guilty of this, and it was tough to narrow down that they were the cause.