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.
Will there be a configuration example for Debian 12(v252)? This incompatibility among systemd versions really made me frustrated.