This provides an overview of how you can use the systemd-networkd service in Ubuntu 22.04 to obtain an IPv6 DHCPv6-PD prefix (eg, a /56) from an upstream ISP and subdivide it across local subnets, providing a /64 per subnet.
Note that this post assumes you already have extensive experience with Linux and IPv6. Most people are much better off using a distribution such as OpenWRT that handles all of this complexity for you. If you want to know why IPv6 matters, check out Akamai's blog post on "10 Years Since World IPv6 Launch".
The systemd-networkd service in Ubuntu 22.04 has enough support for DHCPv6-PD to obtain a prefix from an upstream ISP and subdivide it across local subnets, providing a /64 per subnet. For example, Comcast provides a /60 which has enough space for 16 subnets.
This includes substantial improvements since Ubuntu 20.04 (which I wrote about previously although the syntax for some of the configuration has changed. This is an updated version and assumes Ubuntu 22.04 Jaunty (or equivalent).
Because netplan doesn't have enough features to configure DHCPv6-PD yet (see https://bugs.launchpad.net/netplan/+bug/1771886), some of the configuration needs to live in systemd-networkd overrides.
Configuring the Interfaces
I am assuming starting with a netplan configuration like the following:
network: ethernets: eno1: # ISP1 dhcp4: true dhcp6: true accept-ra: true ipv6-privacy: false nameservers: search: [ example.org ] dhcp4-overrides: use-dns: false use-hostname: false use-ntp: false dhcp6-overrides: use-dns: false use-hostname: false use-ntp: false eno2: # LAN dhcp4: false optional: true version: 2 vlans: en-main: id: 6 link: eno2 dhcp4: no addresses: - 10.1.0.0/24 optional: true en-guest: id: 7 link: eno2 dhcp4: no addresses: - 10.2.0.0/24 optional: true
This has interfaces:
- eno1 = WAN
- eno2 = Primary LAN
- en-main = Main VLAN
- en-guest = Guest VLAN
While netplan and systemd-networkd somehow collaborate to put this
netplan config into files such as
/run/systemd/network/10-netplan-eno1.network, there is no way to get
the rules needed for enabling DHCPv6-PD into those dynamically
generated files in /run.
Thankfully, you can put overrides in that systemd-networkd will read.
systemd.network(5) man page for more details on all of the options available
for these files.
To configure a DHCPv6-PD client on the WAN interface (eno1 in my case),
[Match] Name=eno1 [DHCPv6] PrefixDelegationHint=::/56
You can specify a smaller or larger value for PrefixDelegationHint if needed (eg, /60 for Comcast Xfinity Residential).
For each of the LAN interfaces, create files such as
/etc/systemd/network/10-netplan-en-main.network.d/override.conf with the following. Note that the filename
prefixes must match those in
/run/systemd/network/ (ie, don't change the "10-").
[Match] Name=en-main [Network] IPv6PrefixDelegation=dhcpv6 IPv6DuplicateAddressDetection=1 LinkLocalAddressing=ipv6 [DHCPv6PrefixDelegation] ## Must be unique per subnet SubnetId=6
The "SubnetId" here must be unique per subnet and is used to construct the address. The allowed values depend on what size prefix was delegated by DHCPv6-PD. If you have a /60, this can be a unique hex value in the range 0-f. If you have a /56, this can be a unique hex vale in the range of 0-ff.
For example, if you have 2001:db8:abcd:3400::/56 and have SubnetId of "7" then the subnet will be assigned the prefix 2001:db8:abcd:3407::/56.
It is critical to have this here as otherwise these will be assigned in different orders each time systemd-networkd restarts. It doesn't keep track of what prefixes it has advertised to links, so hosts on various links/subnets could end up with stale prefix advertisements for the wrong subnets and broken connectivity.
As a variation on the above, the following also: * Assigns a ULA prefix on the subnet which remains up even if you lack external network connectivity. Make sure to replace the "XX:XXXX:XXXX" part of "fdXX:XXXX:XXXX:YYYY::/64" with a sufficiently random value, and then the "YYYY" may wish to be the same as the SubnetId as above (but does not need to be). * Has the router also assign itself an address on the subnet with a token of "::7777", so it would assign itself 2001:db8:abcd:3407::7777.
[Match] Name=en-main [Network] ## use both DHCPv6 and static ULA prefixes ("yes") IPv6PrefixDelegation=yes IPv6DuplicateAddressDetection=1 LinkLocalAddressing=ipv6 [DHCPv6PrefixDelegation] ## Must be unique per subnet SubnetId=6 Token=::7777 Assign=true [IPv6Prefix] Prefix=fdXX:XXXX:XXXX:YYYY::/64
Note the filename prefixes in
/etc/systemd/network/ need to match those in
For more details on the options and their meanings, see the man page
You can either reboot, or you can reapply netplan and reload systemd-networkd configuration:
netplan apply networkctl reload
Sometimes it can help to also restart restart systemd-networkd with:
service systemd-networkd restart
Caveats and notes
Some additional notes:
- The IPv6 address on the WAN link (eg, eno1) comes from an individual RA ND or DHCPv6 assignment, not a DHCPv6-PD prefix.
Testing and Debugging
There's plenty more information than existed in older versions,
especially by running
networkctl status. This will show the various
assigned addresses as well as some recent log entries. For example,
you can see which prefix was obtained via DHCPv6-PD and what was assigned
to various links:
eno1: DHCPv6 address 2001:db8:501:b9:1111:2222:3333:4444/128 (valid for 3d 40min 1s, preferred for 3d 40min 1s) eno1: DHCP6: received PD Prefix 2001:db8:1234:950::/60 en-main: DHCPv6-PD address 2001:db8:1234:956::7777/64 (valid for 3d 40min 1s, preferred for 3d 40min 1s) en-guest: DHCPv6-PD address 2001:db8:1234:957::7777/64 (valid for 3d 40min 1s, preferred for 3d 40min 1s)
Here the DHCPv6-PD prefix of 2001:db8:1234:950::/60 was assigned, and then 2001:db8:1234:956::/64 was assigned out to the link with SubnetId "6".
Some things that may be helpful:
ip -6 route listwill list the prefixes assigned to the interfaces
rdisc6 en-guestwill do router discovery on that interface (eg, en-guest in this case) and will show the assigned prefix
- Doing a
tcpdump -s 1500 -vvv -i eno1 port 546 or port 547and then a
systemctl restart systemd-networkdwill show the DHCPv6-PD lease including prefix length received from the WAN ISP
journalctl -xeu systemd-networkdwill give some logging information from systemd-networkd but I have yet to figure out how to get the most relevant parts logged.
radvdumpwill show the RAs showing up on an interface.
More network logging
systemctl edit systemd-networkd to add in:
And restart systemd-networkd service and looking at logs:
systemctl restart systemd-networkd journalctl -b -u systemd-networkd
Multi-Homing and clearing bad prefixes
I've also started to play more with Primary/Backup failover between two links. Using the approach from RFC8475 - Using Conditional Router Advertisements for Enterprise Multihoming seems to work, although I additionally configure NAT66 for cases where traffic tries to use the wrong source address for the wrong link.
One thing that is useful is to use the
ra6 tool from
set the preferred lifetime down to "0" to deprecate a prefix on a
link. For example, this sets the preferred lifetime to "0" for an old
prefix "2001:db8:5050:553::/64" which had been on a link with
interface "en-guest", sending it to the all-hosts multicast (ff02::1):
ra6 -v -i en-guest -e -P 2001:db8:5050:553::/64#AL#7200#0 --lifetime 300 -r 0 -x 0 -c 0 -p -1 -d ff02::1
It is possible not all of these arguments are strictly needed.
You may need to send this periodically (for the valid lifetime that had been set previously) as hosts may be offline or may be rate-limiting RAs.
This can be useful for misconfigurations (eg, if changing SubnetId), cleaning up from botched ISP renumberings, and for primary/backup failovers.
It would be nice if systemd-networkd kept track of IPv6 prefixes it had assigned to links so that it could deprecate those if they were no longer in-use (eg, when an ISP does a renumbering), and to also support Conditional RAs for primary/backup failovers between multiple uplinks.
It would also be nice if all of this could be configured via netplan rather than with overrides.
I also plan to setup an IPv6-only subnet using NAT64+DNS64 and will have a follow-on blog post on the details there.
- 2023-08-26: removed "ForceDHCPv6PDOtherInformation" which has been deprecated