Router advertisements and SLAAC
With a /64 on each VLAN, the router sends router advertisements (RAs): periodically, and straight away when a device asks with a router solicitation. A device that has just connected asks, and gets one straight away. The RA carries:
- The prefix (2001:db8:ab00:510::/64 for staff) with the autonomous flag: "build your own address from this".
- How long the prefix is valid and preferred.
- The router lifetime: how long to use this router as the default gateway.
From that, the staff PC builds its own address (SLAAC, stateless address autoconfiguration): the prefix plus an interface ID derived from its MAC address, giving 2001:db8:ab00:510:0:ff:fe00:1020. Its default route points at the router's link-local address, fe80::ff:fe00:101, which is how IPv6 always names its gateway.
Testing the policy
The staff PC reaches the web server over IPv6, and the server's log shows the PC's own address, not the router's: no NAT.
The same policy as IPv4 applies, unchanged:
- Staff reach the file server's IPv6 address.
- A guest, with its own address in the guest /64, reaches the internet but not the file server.
- From the internet, the web server can't reach the file server at all, even though it now has a public address. The forward chain's final counter shows those attempts dropped.
(The file server got its address by SLAAC too. Servers usually need a fixed address: give them one in their own configuration, using the VLAN's prefix.)
DNS still works over IPv4 through dnsmasq, which is enough: the DNS server's address and the addresses it answers with are separate things. To have devices use an IPv6 DNS server too, add DNS= to the [IPv6SendRA] section of each VLAN's file.
root@staff:~# timeout 15 tcpdump -lnvi eth0.10 -c 1 "icmp6 and ip6[40] == 134"
tcpdump: listening on eth0.10, link-type EN10MB (Ethernet), snapshot length 262144 bytes
22:15:30.300768 IP6 (flowlabel 0xce90d, hlim 255, next-header ICMPv6 (58) payload length: 56) fe80::ff:fe00:101 > fe80::ff:fe00:1020: [icmp6 sum ok] ICMP6, router advertisement, length 56
hop limit 0, Flags [none], pref medium, router lifetime 1800s, reachable time 0ms, retrans timer 0ms
source link-address option (1), length 8 (1): 02:00:00:00:01:01
prefix info option (3), length 32 (4): 2001:db8:ab00:510::/64, Flags [onlink, auto], valid time 3600s, pref. time 1800s
1 packet captured
1 packet received by filter
0 packets dropped by kernel- Lines 3–4: From the router's link-local address: use me as a default router for 1800 seconds.
- Line 5: The router's MAC address, so the PC doesn't need to ask.
- Line 6: The prefix, with on-link and autonomous (build your own address) flags, and its lifetimes.
root@staff:~# ip -6 -br addr show eth0.10; ip -6 route show default
eth0.10@eth0 UP 2001:db8:ab00:510:0:ff:fe00:1020/64 fe80::ff:fe00:1020/64
default via fe80::ff:fe00:101 dev eth0.10 proto ra metric 1024 expires 1756sec pref medium
root@staff:~# ping -6 -c 2 -W 1 2001:db8:e::10 | tail -2; curl -6 -s -o /dev/null -w "%{http_code}\n" -m 2 "http://[2001:db8:e::10]/"
2 packets transmitted, 2 received, 0% packet loss, time 1026ms
rtt min/avg/max/mdev = 0.095/0.121/0.148/0.026 ms
200
root@web:~# tail -1 /tmp/web.log
2001:db8:ab00:510:0:ff:fe00:1020 - - [04/Nov/2026 22:15:04] "GET / HTTP/1.1" 200 -- Line 2: Its own address: the VLAN's /64 plus an interface ID from its MAC address.
- Line 3: Default route via the router's link-local address, learned from the RA.
- Lines 5–8: IPv6 to the internet works.
- Line 11: The web server sees the PC's own address: no NAT.
root@staff:~# curl -6 -s -o /dev/null -w "%{http_code}\n" -m 2 'http://[2001:db8:ab00:530:0:ff:fe00:3010]/'
200
root@guest:~# ip -6 -br addr show eth0.20; curl -6 -s -o /dev/null -w "%{http_code}\n" -m 2 'http://[2001:db8:e::10]/'; curl -6 -s -o /dev/null -w "%{http_code}\n" -m 2 'http://[2001:db8:ab00:530:0:ff:fe00:3010]/'
eth0.20@eth0 UP 2001:db8:ab00:520:0:ff:fe00:2020/64 fe80::ff:fe00:2020/64
200
000
root@web:~# curl -6 -s -o /dev/null -w "%{http_code}\n" -m 2 'http://[2001:db8:ab00:530:0:ff:fe00:3010]/'; ping -6 -c 1 -W 1 2001:db8:ab00:530:0:ff:fe00:3010 | tail -2
000
1 packets transmitted, 0 received, 100% packet loss, time 0ms
root@router:~# nft list chain inet filter forward | grep counter
counter packets 5 bytes 424 comment "every other pair is dropped"- Lines 1–2: Staff to the file server: allowed.
- Lines 5–7: A guest has its own /64, reaches the internet, but not the file server.
- Lines 9–11: From the internet to the file server's public address: nothing.
- Lines 14–15: The forward chain's final counter caught the attempts.