Years ago, I posted about how to get IPv6 routing working with PPP connections and Debian buster. Since then, the systemd-networkd configuration schema has changed significantly. Here are updated instructions for how I set up my router with Debian 13 (trixie), using systemd v257.
Here’s what I’ve configured the router to do to establish an internet connection:
- Create a PPPoE (
ppp0
) connection to the internet. My ISP, Internode, requires a 802.1q VLAN to be set on the ethernet frames, so in my case this will be overeth1.2
. - Establish its internet IPv4 address via PPP/IPCP.
- Solicit an IPv6 prefix via DHCPv6.
And in terms of providing routing for my LAN:
- Respond to DHCPv4 requests, setting itself as the gateway, and assigning IPv4 addresses on the LAN.
- Respond to IPv6 router solicitation requests, advertising the IPv6 prefix and SLAAC.
- NAT for IPv4.
- Packet forwarding for IPv6.
All of this can be achieved with systemd-networkd and pppd, without the need for any other DHCP client/server software, or NetworkManager.
systemd-networkd
# /etc/systemd/network/20-eth0.network [Match] Name=eth0 [Link] MTUBytes=1492 [Network] # Set up static IPs here. Address=192.168.x.x/24 LinkLocalAddressing=ipv6 # Limit for packets forwarded over PPPoE IPv6MTUBytes=1492 IPv6AcceptRA=no IPv6SendRA=yes DHCPPrefixDelegation=yes DHCPServer=yes [IPv6SendRA] OtherInformation=yes [DHCPServer] # Set MTU (DHCP Option 26) to 1492 bytes. SendOption=26:uint16:1492
# /etc/systemd/network/20-eth1.2.netdev [NetDev] Name=eth1.2 Kind=vlan [VLAN] Id=2
# /etc/systemd/network/20-eth1.network [Match] Name=eth1* [Link] MTUBytes=1500 [Network] LinkLocalAddressing=no IPv6AcceptRA=no LLDP=no VLAN=eth1.2
# /etc/systemd/network/60-ppp0.network [Match] Name=ppp0 [Network] LLDP=no DHCP=ipv6 DefaultRouteOnDevice=yes IPv6AcceptRA=yes IPv6SendRA=no [DHCPv6] WithoutRA=solicit
udev rules
# /etc/udev/rules.d/99-eth1-ppp0.rules # Start ppp0 when eth1 is connected SUBSYSTEM=="net", ACTION=="add", NAME=="eth1", ENV{SYSTEMD_WANTS}="pppd@ppp0.service"
pppd service and configuration
# /etc/systemd/system/pppd@ppp0.service [Unit] Description=PPP connection for interface %I Documentation=man:pppd(8) [Service] Type=forking ExecStart=/usr/sbin/pppd call %I linkname %i updetach nopersist ExecStop=/bin/kill $MAINPID ExecReload=/bin/kill -HUP $MAINPID StandardOutput=null Restart=always RestartSec=30 RestartPreventExitStatus=2 TimeoutStartSec=25 # Restrict permissions: PrivateTmp=yes ProtectHome=yes ProtectSystem=strict ReadWritePaths=/run/ # /etc/ppp/ip-up.d scripts need to write to /sys: ProtectKernelTunables=no ProtectControlGroups=yes # Need @module to load nft modules: SystemCallFilter=@system-service @module SystemCallArchitectures=native LockPersonality=yes MemoryDenyWriteExecute=yes RestrictRealtime=yes [Install] WantedBy=sys-devices-virtual-net-%i.device WantedBy=network-online.target
# /etc/ppp/peers/ppp0 # The username is required for internode. # There should be a matching entry in /etc/ppp/pap-secrets, # even though the password is not used. user "xxx@internode.on.net" # Required for PPPoE: noaccomp default-asyncmap mtu 1492 plugin pppoe.so eth1.2 # Let systemd-networkd configure IPv6. +ipv6
Enabling routing and NAT
# /etc/sysctl.d/local.conf # Maybe the same effects could be achieved via /etc/systemd/networkd.conf net.ipv4.ip_forward=1 net.ipv6.conf.default.forwarding=2 net.ipv6.conf.all.forwarding=2
#!/bin/sh # /etc/ppp/ip-up.d/nft # Note this is an illustrative example of enabling NAT - not a functional firewall! /sbin/modprobe -v -a nfnetlink nf_tables nf_nat || exit 1 /usr/sbin/nft -f - <<END_NFT add table ip nat add chain ip nat postrouting { type nat hook postrouting priority 100; policy accept; } add rule ip nat postrouting oifname $PPP_IFACE masquerade add rule inet filter input iif $PPP_IFACE ct state related,established accept END_NFT