TTR Networks

Beginner · IPv6 on VyOS: setup and routing · section 6 of 8

An IPv6 forward firewall

✓ Lab-tested 17 October 2026 on VyOS 2026.10.16-0024-rolling

Without NAT, everything on your LANs has a public address, and nothing stops the internet connecting to it. The lab proves it: the server opens client1's web server directly. IPv6 needs a forward firewall, from day one.

The policy has the same shape as the IPv4 one in the NAT lesson:

  1. Accept replies to existing connections, and drop invalid packets.
  2. Accept ICMPv6. IPv4 mostly survives a firewall that drops ICMP; IPv6 doesn't. Path MTU discovery relies entirely on ICMPv6 "packet too big" messages, because IPv6 routers never fragment. Error messages and ping are ICMPv6 too. Accepting all ICMPv6 is simple and common for small networks. RFC 4890 lists exactly which types to allow if you want to be stricter.
  3. Accept anything from LAN1, and from the networks behind R2 (arriving on eth2).
  4. Drop everything else coming in.

R1's firewall protects R2's networks too, because all of their internet traffic passes through R1.

After the commit, the server can no longer reach client1's web server, but it can still ping it, and both clients can still reach out.

Neighbor Discovery Protocol (NDP, the IPv6 replacement for ARP) and Router Advertisements are link-local, so they never pass through the forward filter. They do pass through the router's input filter. If you write an IPv6 input policy (see the VyOS BGP lesson), accept ICMPv6 there too, or neighbours and SLAAC stop working.

R1: IPv6 forward firewall
set firewall ipv6 forward filter default-action 'drop'
set firewall ipv6 forward filter rule 10 action 'accept'
set firewall ipv6 forward filter rule 10 state 'established'
set firewall ipv6 forward filter rule 10 state 'related'
set firewall ipv6 forward filter rule 20 action 'drop'
set firewall ipv6 forward filter rule 20 state 'invalid'
set firewall ipv6 forward filter rule 30 action 'accept'
set firewall ipv6 forward filter rule 30 description 'ICMPv6: IPv6 breaks without it'
set firewall ipv6 forward filter rule 30 protocol 'ipv6-icmp'
set firewall ipv6 forward filter rule 40 action 'accept'
set firewall ipv6 forward filter rule 40 description 'LAN1 out'
set firewall ipv6 forward filter rule 40 inbound-interface name 'eth1'
set firewall ipv6 forward filter rule 41 action 'accept'
set firewall ipv6 forward filter rule 41 description 'Networks behind R2 out'
set firewall ipv6 forward filter rule 41 inbound-interface name 'eth2'
  • Line 1: Drop anything not allowed below.
  • Lines 2–6: Replies to existing connections, and invalid packets.
  • Lines 7–9: All ICMPv6.
  • Lines 10–15: Anything from LAN1, and from R2's networks.
From the internet, before and after (real output)
root@server:~# curl -sS -m 5 http://[2001:db8:1000:1:c8e8:1bff:fef2:b85d]/
hello from client1
root@server:~# ping -6 -c 1 -W 2 2001:db8:1000:1:c8e8:1bff:fef2:b85d
PING 2001:db8:1000:1:c8e8:1bff:fef2:b85d(2001:db8:1000:1:c8e8:1bff:fef2:b85d) 56 data bytes
64 bytes from 2001:db8:1000:1:c8e8:1bff:fef2:b85d: icmp_seq=1 ttl=62 time=0.286 ms

--- 2001:db8:1000:1:c8e8:1bff:fef2:b85d ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.286/0.286/0.286/0.000 ms
root@server:~# curl -sS -m 5 http://[2001:db8:1000:1:c8e8:1bff:fef2:b85d]/
curl: (28) Connection timed out after 5006 milliseconds
root@server:~# ping -6 -c 1 -W 2 2001:db8:1000:1:c8e8:1bff:fef2:b85d
PING 2001:db8:1000:1:c8e8:1bff:fef2:b85d(2001:db8:1000:1:c8e8:1bff:fef2:b85d) 56 data bytes
64 bytes from 2001:db8:1000:1:c8e8:1bff:fef2:b85d: icmp_seq=1 ttl=62 time=7.94 ms

--- 2001:db8:1000:1:c8e8:1bff:fef2:b85d ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 7.940/7.940/7.940/0.000 ms
root@client1:~# curl -sS -m 5 http://[2001:db8:f000::10]/
hello from server
root@client2:~# curl -sS -m 5 http://[2001:db8:f000::10]/
hello from server
  • Lines 1–2: Before: the internet can open client1's web server directly.
  • Lines 10–11: After: dropped.
  • Lines 12–14: Ping still works.
  • Lines 19–22: Both clients can still reach out.
Firewall counters (real output)
vyos@R1:~$ show firewall ipv6 forward filter
Ruleset Information

---------------------------------
ipv6 Firewall "forward filter"

Rule     Action    Protocol      Packets    Bytes  Conditions
-------  --------  ----------  ---------  -------  -----------------------------------------
10       accept    all                19     1988  ct state { established, related }  accept
20       drop      all                 0        0  ct state invalid
30       accept    ipv6-icmp           1      104  meta l4proto ipv6-icmp  accept
40       accept    all                 1       80  iifname "eth1"  accept
41       accept    all                 1       80  iifname "eth2"  accept
default  drop      all                 5      400  comment "FWD-filter default-action "
  • Line 11: The server's ping.
  • Lines 12–13: New connections from each side.
  • Line 14: The server's web attempts (each retry counts).

Check your understanding

Why must an IPv6 firewall accept ICMPv6 when an IPv4 one can often get away without ICMP?

  • IPv6 path MTU discovery depends on ICMPv6, because IPv6 routers never fragment
  • ICMPv6 carries DNS
  • Because ping is required by the standard
  • It doesn't; dropping it is safer