This guide is for homelab admins who understand IPv4s well but find setting up IPv6 hard or annoying because things work differently. In some ways, managing an IPv6 network can be simpler than IPv4, one just needs to learn some new concepts and discard some old ones.
Let’s begin.
First of all, there are some concepts that one must unlearn from ipv4:
Concept 1
Don’t pick numbers. Because IPv4 space is small, we are used to managing them, thinking about them, memorizing them. So we pick numbers like 1, 10, 20, 101, etc. However, IPv6 address space is so big, it’s better to let the computers pick the numbers randomly from the large address space, and not limit yourself to small numbers. Use DNS, so you can remember names and not numbers. Therefore, never pick your own numbers! This is fundamental to IPv6, and going against this concept will make everything harder, so don’t do it.
Concept 2
IPv6 will have multiple addresses per interface. This is normal. Fighting this will also make everything harder. Just accept the fact that a host will have multiple addresses.
The above concepts are what makes IPv6 different from IPv4, so it’s better to let them go as soon as possible.
Ok, now on to IPv6.
Concept 3
IPv6 addresses are 128 bits long. You have the first 64 bits as the routable part, called the prefix, and the last 64 bits as the interface identifier. Your ISP will give you the first 64 bits, and your host machine will have the last 64 bits. Furthermore, IPv6 subnets are always 64 bits or /64. This will make life much simpler for you as a network admin—no more thinking about subnet masks.
The addresses are displayed as 8 groups of hexadecimals, like this: 1111:2222:3333:4444:aaaa:bbbb:cccc:dddd. The first 4 groups (1111:2222:3333:4444) is the prefix, and the last 4 groups is the machine or interface identifier (aaaa:bbbb:cccc:dddd). Some runs of zeros can be condensed as ::.
There are some common prefixes to remember. Globally routable addresses start with a 2 or possibly 3, like 2xxx::. Link local start with fe80:: and ULA (Unique Local Address, similar to private addresses in IPv4) start with fdxx::. Memorize these. These are not any harder to remember than knowing 192.168.x.x and 10.x.x.x are private IPv4 addresses.
Concept 4
IPv6 uses Router Advertisement (RA), not DHCP. The difference is that whereas DHCP will give the asking client the exact IP address, while keeping track of a lot of state (like MAC address, lease time, etc etc), RA just advertises the prefix. When the client sees the RA, it will self-configure its own IPv6 address using the advertised prefix plus its own unique identifier. This is called Stateless Address Auto Configuration (SLAAC), and it removes the need for a stateful DHCPD server. For a given prefix, the interface will always pick the same identifier, (in fact, the eui-64 algorithm will pick the same identifier across multiple prefixes), so once a client picks an IPv6 address, you can assume it’s stable. A slightly different algorithm will pick a different identifier for different prefixes, but these will also be stable for each prefix.
There exists stateful DHCPv6 servers which can hand out full IPv6 addresses just like IPv4 DHCP, but Android does not support these, and are not needed for a fully functional IPv6 setup. As tempting as it is, there is no need for an IPv6 DHCP server on your LAN.
Concept 5
Prefix Delegation. As seen from concept 4, routing is done by advertising the prefix. So how do I get this prefix from the ISP? You ask for it via prefix delegation (PD). With IPv4, when your router connects to your ISP, you get one public address for the WAN, and you use a picked private address like 192.168.0.0/24 for your LAN. With IPv6, since you want a globally routable address for hosts on your LAN also, you need to ask the ISP for a routable prefix.
After your router connects to your ISP, the router can ask for prefix delegation. RFC suggests that the ISP should give you a /56 address space, giving you 255 routable subnets, but some (looking at you, Comcast) gives you only one /64 by default, which is just one routable subnet. Comcast can give you a /60, or 16 routable subnets, but you have to ask for it via config.
As a side note, if you get a /56 PD, your prefix will be something like 1111:2222:3333:4400, where the zeros can be replaced by a hexadecimal number of your choosing, allowing you to have 256 subnets. A /60 prefix delegation is something like 1111:2222:3333:4440, where the zero can be replaced by one hexadecimal number, giving you 16 subnets.
Once you have the prefix, your dhcpv6 client will assign that prefix+address to your LAN interface, and something like radvd can Route Advertise that prefix on that interface. Other hosts on the LAN can now automatically configure their own prefix+identifier IPv6 addresses.
Concept 6
ULA (unique local addresses). These are like private addresses from IPv4, but with a much larger address space. Remember Concept 1: don’t pick numbers. Use a website to randomly generate a ULA prefix. For company level LANs, this will allow multiple LANs to be merged in the future without conflict.
Once you have a ULA, assign it to your LAN interface. Radvd will pick it up and advertise it to the rest of your LAN, giving all your hosts a second IPv6 address.
You want to use these ULA for all your LAN communication. If you want to reach your printer or a media server, put their ULAs in the DNS and not the globally routable one. The reasons are that 1) for residential internet, the globally routable prefix can change and 2) if your ISP goes down for some reason, your LAN can function without disruption.
Now that we’ve gone through the concepts, let’s setup the LAN.
-
Configure your router to get IPv6 from your ISP. Most Linux distros will have instructions on setting up IPv6, RA, and PD on your Linux router.
-
Configure your LAN with a ULA. Now your hosts will have a globally routable address and a local address.
-
For servers on your LAN that you want to talk to, add their ULA to the LAN DNS. Heck, you could even add ULA to the global DNS, but if your internet goes out, that will stop working.
-
For your externally visible servers, use dyndns type service, and update it with the globally routable address. Residential internet will rotate the prefix over time (the prefix is dynamic), so this is the same as running a server on a residential dynamic IPv4.
-
For firewall, drop all inbound packets by default. For externally visible servers, you can match the last 64 bits only, so that changing prefixes don’t affect the firewall rules. For nftables, the syntax to match ssh and ping on ipv6 identifier is
chain forward {
type filter hook forward priority 0;
policy drop;
ip6 daddr & ::ffff:ffff:ffff:ffff == ::aaaa:bbbb:cccc:dddd tcp dport 22 accept
ip6 daddr & ::ffff:ffff:ffff:ffff == ::aaaa:bbbb:cccc:dddd icmpv6 type {echo-request} accept
### other stuff ###
}