DIY Debian Router - Part 5: IPv6 configuration with systemd-networkd
Introduction
This is Part 5 of the DIY Debian Router series. See Part 1 for the series introduction and links to all parts.
IPv6 address autoconfiguration (RFC 4862) eliminates the hassle of DHCPv6 (which is still sometimes not supported by certain clients) while providing clients with globally routable addresses. This part covers IPv6 deployment using systemd-networkd's native support for DHCPv6 Prefix Delegation (DHCPv6-PD) and Router Advertisement (RA) generation.
⚠️ Parts of this post aren't fully tested: My ISP does not support native IPv6 (as mentioned below). Due to this, I haven't been able to fully test GUA configuration and full IPv6 functionality. Despite this, I've tried my best to provide a fully working configuration for internal and external IPv6 communication.
We'll cover:
- Native DHCPv6-PD client for obtaining delegated prefixes from the ISP
- Integrated Router Advertisement functionality (no separate
radvddaemon needed) - Automatic prefix assignment to LAN interface
- Dual addressing: Global Unicast Addresses (GUA) and Unique Local Addresses (ULA)
- DNS server advertisement via RDNSS (Recursive DNS Server) option
- Privacy extension support for client devices
- Stateless operation (no lease tracking required)
⚠️ ISP IPv6 Support Required: This guide assumes your ISP provides native IPv6 via DHCPv6-PD or SLAAC. Some major ISPs, notably Bell Canada (╯°□°)╯︵ ┻━┻, do not support IPv6 for residential networks. If your ISP lacks IPv6 support, you can still deploy ULA addressing internally, put external IPv6 traffic won't work.
IPv6 addressing architecture review
As discussed in Part 1, I intend to support dual IPv6 addressing:
Global Unicast Addresses (GUA)
We'll use DHCPv6 Prefix Delegation (PD) to obtain GUA prefixes from the ISP. The router's WAN interface runs systemd-networkd's built-in DHCPv6 client, which requests a delegated prefix (e.g., /56 or /60) from the ISP's DHCPv6 server. Simultaneously, the WAN interface autoconfigures its own IPv6 address via SLAAC from the ISP's Router Advertisements - these are two separate operations serving different purposes:
- DHCPv6-PD: Obtains a prefix that the router subdivides and assigns to its LAN interfaces
- SLAAC (WAN): Configures the WAN interface's own IPv6 address for communication with the ISP
The delegated prefix is automatically assigned to the LAN bridge interface (br0) and advertised to clients. When the ISP updates the delegated prefix, the configuration is automatically refreshed.
Clients on the LAN receive addresses from the delegated prefix, providing globally routable IPv6 connectivity. GUA addresses are Internet-accessible (subject to firewall rules, covered in Part 6).
Unique Local Addresses (ULA)
ULA provides stable internal addressing independent of ISP prefix changes. The fake example addresses I'm using for demonstration are:
Prefix: fd09:dead:beef::/64
Gateway: fd09:dead:beef::1
If real, this prefix would have been correctly randomly generated per RFC 4193 guidelines and should be treated as site-specific. ULA addresses are not routable on the Internet but are reachable within the local network, analogous to internal addresses in IPv4 (but without NAT).
SLAAC vs. DHCPv6
The choice between SLAAC and DHCPv6 for IPv6 address assignment has some implications:
| Aspect | SLAAC | DHCPv6 |
|---|---|---|
| Client support | Universal | Inconsistent |
| Configuration complexity | Low (RA only) | Higher (DHCPv6 server + RA) |
| Address tracking | None (stateless) | Full (lease database) |
| Privacy extensions | Supported | Supported |
| Static reservations | Not directly supported | Supported (via DUID/MAC binding) |
The primary limitation of SLAAC seems to be the lack of centralized address assignment/tracking. I don't need that, so SLAAC it is.
systemd-networkd IPv6 configuration
systemd-networkd includes native support for both DHCPv6-PD and Router Advertisement generation, eliminating the need for external daemons like wide-dhcpv6-client and radvd.
WAN interface DHCPv6-PD configuration
Update the WAN interface configuration to request prefix delegation from the ISP.
✏️ Replace
enp8s0with your actual WAN interface name.
Update /etc/systemd/network/20-wan.network to contain:
[Match]
enp8s0
[Network]
DHCP=yes
yes
[DHCPv4]
no
yes
100
[DHCPv6]
::/64
no
[IPv6AcceptRA]
no
100
always
[Link]
yes
New configuration elements:
[DHCPv6]section: Configures DHCPv6 client behaviorPrefixDelegationHint=::/64: Request a /64 prefix from the ISP (adjust based on ISP's delegation policy - common values are /56, /60, /64)UseDNS=no: Do not use DNS servers from DHCPv6 (router runs its own DNS resolver)DHCPv6Client=always: Always run DHCPv6 client, even if RA indicates not to (some ISPs require this for PD)
The DHCPv6 client will automatically request prefix delegation (IA_PD) and a non-temporary address (IA_NA) for the WAN interface.
LAN bridge IPv6 configuration
Update the LAN bridge configuration to receive delegated prefixes and advertise them to clients.
✏️ IMPORTANT: Per RFC 4862, the prefix length for SLAAC is required to be
/64. If you set it to anything else, your clients may ignore the RAs being sent.
Update /etc/systemd/network/40-br0.network to contain:
[Match]
br0
[Network]
fd09:dead:beef::1/64
yes
no
yes
yes
[IPv6SendRA]
no
no
DNS=fd09:dead:beef::1
absurdum.ca
[IPv6Prefix]
fd09:dead:beef::/64
3600
86400
[DHCPPrefixDelegation]
enp8s0
yes
yes
[Link]
carrier
Configuration breakdown:
IPv6SendRA=yes: Enable Router Advertisement transmission on this interfaceDHCPPrefixDelegation=yes: Request assignment of a subnet from the DHCPv6-PD delegated prefix[IPv6SendRA]section: Configures RA generationManaged=no: Do not set the "managed" flag (no DHCPv6 required for addresses)OtherInformation=no: Do not set the "other" flag (no DHCPv6 required for other config)DNS=fd09:dead:beef::1: Advertise router's ULA address as DNS server via RDNSS optionDomains=: Optionally advertise DNS search domain via DNSSL option
[IPv6Prefix]section: Defines the static ULA prefix to advertisePrefix=fd09:dead:beef::/64: ULA prefix, static and always advertised
[DHCPPrefixDelegation]section: Configures automatic assignment from DHCPv6-PD delegationAssign=yes: Automatically assign an address from the GUA prefix to the bridge interfaceAnnounce=yes: Advertise this delegated prefix in Router Advertisements
When systemd-networkd receives a delegated prefix from the ISP (e.g., 2001:db8:1234::/64), it automatically:
- Assigns a /64 subnet from the delegation to the bridge (e.g.,
2001:db8:1234:0::/64) - Configures an address for the router within that subnet (e.g.,
2001:db8:1234::1/64) - Advertises the prefix in Router Advertisements to LAN clients
- Updates the prefix advertisement when the delegation changes
This fully automates IPv6 prefix management with no manual intervention required.
Service activation
Restart systemd-networkd to apply configuration:
Verify DHCPv6-PD status on WAN interface:
Expected output should include:
IPv6 Address: 2001:db8:...::... (dhcp)
Check for delegated prefix:
Expected output:
inet6 2001:db8:1234::1/64 scope global dynamic
inet6 fd09:dead:beef::1/64 scope global
inet6 fe80::xxxx:xxxx:xxxx:xxxx/64 scope link
The first address (2001:db8:1234::1/64) is the GUA derived from the ISP-delegated prefix.
View detailed DHCPv6-PD information:
|
You should see messages indicating successful DHCPv6-PD acquisition.
Testing and verification
Router advertisement (RA)
Capture RAs on the LAN to verify systemd-networkd is transmitting correctly:
This captures ICMPv6 Router Advertisement packets (type 134). Expected output every 200-600 seconds, example:
ICMPv6, router advertisement, length 64
hop limit 64, Flags [none], pref medium, router lifetime 1800s
prefix info option (3), length 32 (4): fd09:dead:beef::/64, Flags [onlink, auto]
prefix info option (3), length 32 (4): 2001:db8:1234::/64, Flags [onlink, auto]
RDNSS option (25), length 24 (3): lifetime 1200s, addr: fd09:dead:beef::1
You should see both the ULA prefix and the dynamically delegated GUA prefix advertised.
Address assignment
To test address assignment, connect a client device to the LAN. The client should autoconfigure two or more IPv6 addresses:
Expected output:
inet6 2001:db8:1234::abcd:1234:5678:9abc/64 scope global dynamic
inet6 fd09:dead:beef::abcd:1234:5678:9abc/64 scope global dynamic
inet6 fe80::xxxx:xxxx:xxxx:xxxx/64 scope link
- GUA address:
2001:db8:1234::...(delegated by ISP, globally routable) - ULA address:
fd09:dead:beef::...(stable internal addressing) - Link-local address:
fe80::...(required for IPv6 operation)
Privacy extensions
Check for privacy extensions (client-side configuration) - clients supporting (RFC 4941) will generate additional temporary addresses:
inet6 2001:db8:1234::1234:5678:abcd:ef01/64 scope global temporary dynamic
inet6 fd09:dead:beef::1234:5678:abcd:ef01/64 scope global temporary dynamic
Temporary addresses are used for outbound connections to prevent tracking based on stable interface identifiers.
DNS configuration verification
Check that the client received RDNSS configuration:
You should see the configured IPv6 DNS server address, e.g., fd09:dead:beef::1.
IPv6 connectivity test
From the client, ping the router's ULA address:
Ping an external IPv6-enabled host (requires firewall configured in Part 6):
If GUA prefix is configured and firewall allows outbound traffic, you should see replies.
Test DNS resolution over IPv6:
Troubleshooting common issues
No IPv6 addresses assigned to clients
1. systemd-networkd not running on the router:
Verify service status:
If not running, check logs:
2. IPv6 forwarding disabled:
Verify forwarding is enabled (covered in Part 7):
Should return 1. If 0, enable temporarily:
3. Firewall blocking RAs:
Temporarily disable firewall for testing:
If IPv6 works with firewall disabled, add ICMPv6 rules (see Part 6).
4. Client IPv6 disabled:
On the client, verify IPv6 is enabled. The following should return 0:
Clients receive link-local only (fe80::)
If clients only configure link-local addresses, RAs are not being received. Possible causes:
1. IPv6SendRA not enabled on the router:
Verify /etc/systemd/network/40-br0.network has IPv6SendRA=yes.
Restart systemd-networkd:
2. Client Not Accepting RAs:
On the client, verify RA acceptance:
Linux:
Should return 1 or 2.
You can also trying running the tcpdump command given earlier above to look for RAs on the client.
IPv6 internet connectivity fails (GUA)
If clients have GUA addresses but cannot reach the Internet:
1. No default route:
Verify the client has a default IPv6 route:
Should see:
default via fe80::xxxx:xxxx:xxxx:xxxx dev eth0 proto ra metric 1024
If missing, RAs are not advertising the router as a default gateway. Verify IPv6SendRA=yes in br0 configuration.
2. Firewall blocking IPv6 forwarding:
See Part 6 for firewall rules. IPv6 forwarding must be permitted in nftables.
3. ISP does not provide IPv6:
Verify the WAN interface has an IPv6 GUA:
If no GUA is present, the ISP does not provide IPv6 connectivity. Switch ISP...
DHCPv6-PD not receiving delegated prefix
If systemd-networkd is running but no GUA prefix appears on br0:
1. Verify DHCPv6-PD configuration:
Check WAN interface configuration:
Ensure [DHCPv6] section includes PrefixDelegationHint.
Check logs:
|
Look for errors such as "DHCPv6 ADVERTISE timeout."
2. ISP Does not support DHCPv6-PD:
Some ISPs use SLAAC on the WAN interface instead of DHCPv6-PD. Verify if the WAN has a GUA via SLAAC:
If a GUA is present but no prefix delegation occurs, the ISP may not provide DHCPv6-PD. Solutions:
- Use the WAN GUA's /64 prefix directly (limited to single subnet)
- Switch ISP...
3. DHCPv6 server not responding:
Capture DHCPv6 traffic on the WAN to verify ADVERTISE messages are received:
Expecte to see SOLICIT messages from the router followed by ADVERTISE/REPLY from the ISP's DHCPv6 server. If no ADVERTISE is received, the ISP's DHCPv6-PD service may not exist or be unavailable.
Next steps
With IPv6 working, clients now have dual-stack connectivity (IPv4 via DHCP, IPv6 via SLAAC). However, the router currently has no firewall, leaving all services exposed. Part 6 covers nftables firewall configuration, implementing stateful packet filtering, NAT, and security policies for both IPv4 and IPv6.