Internode IPv6 on Linux with systemd-networkd

Internode’s instructions for configuring IPv6 routing on linux are pretty terse, merely giving some suggestions for using WIDE DHCPv6 and radvd. On modern Linux with systemd, these extra daemons are unnecessary; all that’s required is systemd-networkd. This post describes the problems and how I got dual-stack IPv4/IPv6 routing over Internode’s NBN service, using a Raspberry Pi running Debian Buster.

Firstly, here’s all the things we want the router to do:

  • Create a PPPoE connection to Internode via the NBN (HFC).
  • Establish the IPv6 address of the upstream router and create a default route there.
  • Issue Router Advertisements (RA) to other hosts on the LAN so that they can configure their global IPv6 address and default route using SLAAC.
  • Act as a DHCPv6 client for Prefix Delegation. It’s not obvious why this is required: Internode allocates a static /56 prefix, and I can statically configure my router to advertise that in its RA. However, it turns out that without the DHCP lease, upstream routes aren’t created for hosts in the /56 prefix!

Glen Turner has an excellent guide for a setup on Debian Wheezy consistent with Internode’s recommendation, using the radvd and wide-dhcpv6-client packages. But on Debian Buster, this feels quite outdated: wide-dhcp6cd will exit immediately (rather than wait) if its config mentions an interface that doesn’t exist. Hence systemd disables it because of the fail-loop it enters on system boot when ppp0 doesn’t exist. On top of this, there’s no good logging level for wide-dhcp6cd; the “debug” level pours out minutiae of config-file parsing, while the merely “verbose” level gives no indication about what the DHCP replies contain. It’s also using a SysV-style init script rather than a modern systemd service.

I’ve also avoided using GNOME’s NetworkManager, which I feel is too bloated for a headless router in a stable network topology.

My configs follow.

eth0 is my LAN, eth1 connects to the NTD, and ppp0 will be the PPP interface. Internode uses 802.1q for NBN as I’ve discussed previously, so in addition I’ll also need a eth1.2 VLAN and MTUs of 1492 for any packets that might get forwarded over PPP.

LAN and Router Advertisements

Here’s the systemd network config for the LAN. Beware that this is written for systemd v241 (the version in Buster), and the IPv6-related configuration options have changed significantly in more recent versions of systemd. This configuration will cause systemd to send RAs (and respond to solicitations) on the LAN.

# /etc/systemd/network/20-eth0.network
[Match]
Name=eth0

[Network]
# Private, static IPv4 address.
Address=192.168.x.x/24
LinkLocalAddressing=ipv6
IPv6AcceptRA=no
IPv6MTUBytes=1492
IPv6PrefixDelegation=yes

[Link]
MTUBytes=1492

[IPv6PrefixDelegation]
RouterLifetimeSec=3600

PPP

systemd-networkd doesn’t have built-in support for PPP, so the solution is to create your own service that runs pppd. Unfortunately this means the config is more complex than when using Debian’s venerable ifupdown via /etc/network/interfaces.

# /etc/systemd/system/pppd@ppp0.service
[Unit]
Description=PPP connection for %I
Documentation=man:pppd(8)

[Service]
Type=forking
ExecStartPre=-/bin/ip link add link eth1 eth1.2 type vlan id 2
ExecStart=/usr/sbin/pppd call %I linkname %i updetach
ExecStop=/bin/kill $MAINPID
ExecReload=/bin/kill -HUP $MAINPID
StandardOutput=null
Restart=always
RestartSec=30
PrivateTmp=yes
ProtectHome=yes
ProtectSystem=strict
ReadWritePaths=/run/
ProtectKernelTunables=yes
ProtectControlGroups=yes
SystemCallFilter=@system-service
SystemCallArchitectures=native
LockPersonality=yes
MemoryDenyWriteExecute=yes
RestrictRealtime=yes

[Install]
WantedBy=sys-devices-virtual-net-%i.device
WantedBy=multi-user.target
WantedBy=network-online.target

Note that Debian’s ppp package will probably ship with a similar service in the future.

This service should be installed so that it starts at boot:

sudo systemctl enable pppd@ppp0.service

The config for the pppd provider should specify the +ipv6 option. Notably, I don’t specify the “ipv6 , ” option as recommended by Internode, because the IPv6 addresses are established in userspace by systemd-networkd.

# /etc/ppp/peers/ppp0

# ... various other options

mtu 1492
plugin rp-pppoe.so eth1.2
+ipv6

DHCPv6 client and Prefix Delegation

The systemd network unit for ppp0 sets up the DHCPv6 client, and the prefix delegation requested in the eth0 unit will trigger prefix requests. Again, beware that this config is for systemd v241 and that the options have changed in more recent versions.

# /etc/systemd/network/60-ppp0.network
[Match]
Name=ppp0

[Network]
DHCP=ipv6

[DHCP]
ForceDHCPv6PDOtherInformation=yes

Kernel Routing

I won’t go into all the other things you should do for a router like set up a firewall and traffic shaping, but at a minimum you need to enable forwarding for IPv6 in the kernel. One such way:

# /etc/sysctl.conf

net.ipv6.conf.default.forwarding=1
net.ipv6.conf.all.forwarding=1

Note that it’s not necessary to set accept_ra; systemd-networkd will take care of soliciting an RA on the PPP link.

One thought on “Internode IPv6 on Linux with systemd-networkd”

  1. Will there be a configuration example for Debian 12(v252)? This incompatibility among systemd versions really made me frustrated.

Leave a Reply

Your email address will not be published. Required fields are marked *