(Obselete) Using DHCPv6-PD on Ubuntu 20.04 with systemd-networkd to route multiple prefixes

Update: Ubuntu 22.04 has significant improvements in the systemd-networkd service that fix a number of issues from this blog post. The syntax has changed. See Using DHCPv6-PD on Ubuntu 22.04 Jaunty with systemd-networkd to route multiple prefixes for an updated version of this.

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".

Overview

The systemd-networkd service in Ubuntu 20.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.

As far as I can tell, the ability to debug what is going on here or obtain state is almost entirely lacking. This makes it very hard to use troubleshoot issues or even tell what is going on.

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. See the 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), create /etc/systemd/network/10-netplan-eno1.network.d/override.conf :

[Network]
IPv6PrivacyExtensions=no

[DHCPv6]
ForceDHCPv6PDOtherInformation=yes
PrefixDelegationHint=::/56

You can specify a smaller value for PrefixDelegationHint if needed (eg, /60 for Comcast Residential).

For each of the LAN interfaces, create files such as /etc/systemd/network/10-netplan-en-guest.network.d/override.conf and /etc/systemd/network/10-netplan-en-main.network.d/override.conf with:

[Network]
IPv6PrefixDelegation=dhcpv6
IPv6DuplicateAddressDetection=1
IPv6PrivacyExtensions=no
LinkLocalAddressing=ipv6

[IPv6PrefixDelegation]
RouterLifetimeSec=1800

Note that if you want to use both DHCPv6 provided addresses and your own (eg, to mix in ULA) this may be possible by setting IPv6PrefixDelegation=true, but I have not tried this. (This did not appear to work in Ubuntu 20.04.)

Note the filename prefixes in /etc/systemd/network/ need to match those in /run/systemd/network/.

Loading Changes

You can either reboot, or you can restart systemd-networkd with:

systemctl restart systemd-networkd

Caveats

Some big caveats I've noticed:

  • The IPv6 address on the WAN link (eg, eno1) comes from an RA ND assignment, not a DHCPv6 prefix.
  • The individual LANs/VLANs don't appear to get IPv6 addresses assigned. I haven't yet figured out how to get on-link addresses. As such, ip addr won't show addresses on those links. (At least un Ubuntu 20.04)
    • This seems fixed (or at least fixable) on Ubuntu 22.04.
  • A maximum of two interfaces is supported for delegating to on Ubuntu 20.04, but this is fixed in the version in 22.04.

Testing and Debugging

As of the systemd-networkd in Ubuntu 20.04 there aren't good ways to tell what is going on. In particular, networkctl status provides either no information or misleading information. (In Ubuntu 22.04 there is more debugging information.)

Some things that may be helpful:

  • ip -6 route list will list the prefixes assigned to the interfaces
  • rdisc6 en-guest will 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 547 and then a systemctl restart systemd-networkd will show the DHCPv6-PD lease including prefix length received from the WAN ISP
  • Doing journalctl -xeu systemd-networkd will give some logging information from systemd-networkd but I have yet to figure out how to get the most relevant parts logged.

More network logging

Doing systemctl edit systemd-networkd to add in:

[Service]
Environment=SYSTEMD_LOG_LEVEL=debug

And restart systemd-networkd service and looking at logs:

systemctl restart systemd-networkd
journalctl -b -u systemd-networkd

Shows a little more info, but still not some of the things that I'd hope to see from looking at the systemd-networkd code.

Future

I'll update this when I upgrade to Ubuntu 22.04 as hopefully it has some more features. If you have any suggestions for better ways of doing things above, they are welcome. I'm unfortunately unable to provide tech support for anything in the above, however...

tags: LinuxIPv6