Skip to content

IPv6 Firewall: Rules, Best Practices, and RA Guard

IPv6 Firewall: Rules, Best Practices, and RA Guard

Section titled “IPv6 Firewall: Rules, Best Practices, and RA Guard”

IPv6 firewall in RouterOS operates under /ipv6 firewall filter — a separate table from the IPv4 /ip firewall filter. The chain model is identical (input, forward, output), but IPv6 introduces protocol-specific requirements: ICMPv6 is critical to IPv6 operation and must not be blanket-blocked, RA Guard protects against rogue router advertisements, and extension headers add matching capabilities absent in IPv4.

A minimal stateful IPv6 firewall protecting the router and forwarded traffic:

/ipv6 firewall filter
# Stateful base
add chain=input action=accept connection-state=established,related,untracked comment="accept established/related"
add chain=input action=drop connection-state=invalid comment="drop invalid"
add chain=forward action=accept connection-state=established,related,untracked comment="fwd accept established/related"
add chain=forward action=drop connection-state=invalid comment="fwd drop invalid"
# Essential ICMPv6 — do not skip, IPv6 breaks without these
add chain=input action=accept protocol=icmpv6 comment="accept ICMPv6"
add chain=forward action=accept protocol=icmpv6 comment="fwd accept ICMPv6"
# DHCPv6 prefix delegation client traffic from ISP
add chain=input action=accept protocol=udp dst-port=546 src-address=fe80::/10 \
in-interface-list=WAN comment="accept DHCPv6-PD from WAN"
# Drop all other input from WAN
add chain=input action=drop in-interface-list=WAN comment="drop WAN input"
add chain=input action=drop comment="drop all other input"
add chain=forward action=drop comment="drop all other forward"
AspectIPv4IPv6
Command path/ip firewall filter/ipv6 firewall filter
ICMP protocol nameprotocol=icmpprotocol=icmpv6
TTL/hop-limitttl matcherhop-limit matcher
Extension headersNot applicableipv6-header, routing-header matchers
Fragment matchingfragment flagipv6-header=fragment
Bogon listRFC1918, etc.Different reserved ranges (see below)
NDP/SLAACNo equivalentICMPv6 types 133–136 required

Address notation in rules uses standard IPv6 CIDR format:

/ipv6 firewall filter add chain=input src-address=2001:db8::/32 action=drop

The three built-in chains behave identically to IPv4:

ChainTrafficTypical Use
inputDestined for the routerProtect management, NDP to router
forwardRouted through the routerLAN-to-WAN, inter-VLAN
outputOriginating from the routerRarely needed

Never drop all ICMPv6. IPv6 relies on ICMPv6 for neighbor resolution (NDP), path MTU discovery (PMTU), and address assignment (SLAAC). Blocking ICMPv6 indiscriminately breaks IPv6 connectivity silently.

ICMPv6 TypeNameRequired For
1Destination UnreachableError signaling
2Packet Too BigPMTU discovery (critical)
3Time ExceededTraceroute, loop detection
4Parameter ProblemMalformed packet signaling
128Echo RequestPing
129Echo ReplyPing responses
133Router SolicitationSLAAC, default gateway
134Router AdvertisementSLAAC, default gateway
135Neighbor SolicitationNDP address resolution
136Neighbor AdvertisementNDP address resolution
ICMPv6 TypeNameGuidance
130–132, 143MLD (Multicast Listener Discovery)Allow on LAN; not required from WAN
137RedirectAllow only on trusted segments; block from WAN

Granular ICMPv6 Rules by Interface Direction

Section titled “Granular ICMPv6 Rules by Interface Direction”

For environments requiring per-type control rather than a blanket ICMPv6 accept:

/ipv6 firewall filter
# Error types — allow from anywhere
add chain=input action=accept protocol=icmpv6 icmp-options=1:0-255 comment="ICMPv6 dest unreachable"
add chain=input action=accept protocol=icmpv6 icmp-options=2:0-255 comment="ICMPv6 packet too big"
add chain=input action=accept protocol=icmpv6 icmp-options=3:0-255 comment="ICMPv6 time exceeded"
add chain=input action=accept protocol=icmpv6 icmp-options=4:0-255 comment="ICMPv6 param problem"
add chain=input action=accept protocol=icmpv6 icmp-options=128:0-255 comment="ICMPv6 echo request"
add chain=input action=accept protocol=icmpv6 icmp-options=129:0-255 comment="ICMPv6 echo reply"
# NDP — allow from link-local (expected source for ND)
add chain=input action=accept protocol=icmpv6 icmp-options=133:0-255 src-address=fe80::/10 comment="RS"
add chain=input action=accept protocol=icmpv6 icmp-options=134:0-255 src-address=fe80::/10 comment="RA"
add chain=input action=accept protocol=icmpv6 icmp-options=135:0-255 comment="NS"
add chain=input action=accept protocol=icmpv6 icmp-options=136:0-255 comment="NA"
# MLD — LAN only
add chain=input action=accept protocol=icmpv6 icmp-options=130:0-255 in-interface-list=LAN comment="MLD Query"
add chain=input action=accept protocol=icmpv6 icmp-options=131:0-255 in-interface-list=LAN comment="MLD Report"
add chain=input action=accept protocol=icmpv6 icmp-options=132:0-255 in-interface-list=LAN comment="MLD Done"
add chain=input action=accept protocol=icmpv6 icmp-options=143:0-255 in-interface-list=LAN comment="MLDv2 Report"

RouterOS ships with a bad_ipv6 address-list used in the default firewall configuration. These are reserved, documentation, or invalid ranges that should never appear as legitimate traffic sources or destinations.

/ipv6 firewall address-list
add list=bad_ipv6 address=::/128 comment="unspecified"
add list=bad_ipv6 address=::1/128 comment="loopback"
add list=bad_ipv6 address=::ffff:0.0.0.0/96 comment="IPv4-mapped"
add list=bad_ipv6 address=::/96 comment="IPv4-compatible (deprecated)"
add list=bad_ipv6 address=100::/64 comment="discard prefix (RFC6666)"
add list=bad_ipv6 address=2001:db8::/32 comment="documentation (RFC3849)"
add list=bad_ipv6 address=2001:10::/28 comment="deprecated ORCHID"
add list=bad_ipv6 address=3ffe::/16 comment="6bone (decommissioned)"
add list=bad_ipv6 address=fc00::/7 comment="ULA — block on WAN (anti-spoof)"

Note on ULA (fc00::/7): ULA addresses are for internal use only and should never appear as traffic sources on a WAN interface. Adding fc00::/7 to bad_ipv6 for WAN anti-spoof rules is a hardening addition — it is not in the RouterOS factory default list.

/ipv6 firewall filter
# --- input chain ---
add chain=input action=accept connection-state=established,related,untracked \
comment="defconf: accept established,related,untracked"
add chain=input action=drop connection-state=invalid \
comment="defconf: drop invalid"
add chain=input action=accept protocol=icmpv6 \
comment="defconf: accept ICMPv6"
add chain=input action=accept protocol=udp port=33434-33534 \
comment="defconf: accept UDP traceroute"
add chain=input action=accept protocol=udp dst-port=546 src-address=fe80::/10 \
in-interface-list=WAN comment="defconf: accept DHCPv6-PD from WAN"
add chain=input action=drop in-interface-list=!LAN \
comment="defconf: drop non-LAN to router"
add chain=input action=drop \
comment="defconf: drop all other input"
# --- forward chain ---
add chain=forward action=accept connection-state=established,related,untracked \
comment="defconf: accept established,related,untracked"
add chain=forward action=drop connection-state=invalid \
comment="defconf: drop invalid"
add chain=forward action=drop protocol=icmpv6 hop-limit=equal:1 \
in-interface-list=WAN comment="defconf: drop hop-limit=1 from WAN"
add chain=forward action=accept protocol=icmpv6 \
comment="defconf: accept ICMPv6"
add chain=forward action=accept protocol=139 \
comment="defconf: accept HIP"
add chain=forward action=accept protocol=udp dst-port=500,4500 \
comment="defconf: accept IKE"
add chain=forward action=accept protocol=ipsec-ah \
comment="defconf: accept AH"
add chain=forward action=accept protocol=ipsec-esp \
comment="defconf: accept ESP"
add chain=forward action=accept ipsec-policy=in,ipsec \
comment="defconf: accept inbound IPsec policy"
add chain=forward action=drop src-address-list=bad_ipv6 \
comment="defconf: drop bad src IPv6"
add chain=forward action=drop dst-address-list=bad_ipv6 \
comment="defconf: drop bad dst IPv6"
add chain=forward action=drop \
comment="defconf: drop all other forward"

:::note Platform availability The routing-header and ipv6-header matchers are available on physical RouterBOARD hardware with full IPv6 firewall support. These matchers may not be present on all builds (e.g., CHR). Verify available matchers with /ipv6 firewall filter add tab completion on your device. :::

IPv6 extension headers enable additional matching not available in IPv4.

RH0 was deprecated by RFC 5095 due to amplification attack potential. Drop it on hardware that supports the routing-header matcher:

/ipv6 firewall filter
add chain=forward action=drop routing-header=0 comment="drop RH0 (deprecated, RFC5095)"
add chain=input action=drop routing-header=0 comment="drop RH0 to router"

On hardware supporting the ipv6-header matcher:

/ipv6 firewall filter
add chain=forward action=drop ipv6-header=fragment protocol=tcp \
comment="drop fragmented TCP (abnormal)"

A rogue Router Advertisement (ICMPv6 type 134) from an unauthorized device on a LAN segment causes connected hosts to:

  • Learn a wrong default gateway
  • Configure incorrect IPv6 prefixes via SLAAC
  • Have their traffic hijacked or blackholed

:::note Hardware requirement The nd-suppression bridge port property is available on physical RouterBOARD hardware but may not be present on all RouterOS builds (e.g., CHR). Verify with /interface bridge port print on your device before using. :::

RouterOS provides per-bridge-port ND suppression on supported hardware. When enabled, the bridge drops ND/RA/RS/NS/NA traffic on that port (DHCPv6 is not suppressed). Use this on untrusted access ports while leaving uplinks/trusted ports unsuppressed.

/interface bridge
add name=br-lan igmp-snooping=yes
/interface bridge port
# Access ports — suppress ND (blocks rogue RA from hosts)
add bridge=br-lan interface=ether2 nd-suppression=yes
add bridge=br-lan interface=ether3 nd-suppression=yes
add bridge=br-lan interface=ether4 nd-suppression=yes
# Uplink / trusted router port — ND allowed, multicast-router learned dynamically
add bridge=br-lan interface=sfp-sfpplus1 multicast-router=temporary-query

nd-suppression is designed to be paired with multicast-router=temporary-query on the uplink so the bridge learns the legitimate multicast router rather than flooding ND to all ports.

For environments without bridge-port nd-suppression support, or as an additional layer, drop rogue RAs in the firewall raw table before conntrack:

/ipv6 firewall raw
add chain=prerouting action=drop protocol=icmpv6 icmp-options=134:0-255 \
in-interface-list=UNTRUSTED comment="drop rogue RA from untrusted interfaces"

Define UNTRUSTED as an interface list containing access-side ports where router advertisements should never originate.

PracticeReason
Accept established,related,untracked earlyStateful operation, performance
Drop invalid earlyConntrack anomalies, fragmented attack traffic
Never blanket-drop ICMPv6NDP, PMTU, SLAAC all depend on ICMPv6
Drop RH0 extension headerDeprecated, enables amplification
Drop hop-limit=1 from WAN on forwardPrevents routing loops leaking to LAN
Block bad_ipv6 list in forwardBogon/reserved ranges should never transit
Add fc00::/7 to WAN anti-spoofULA is not globally routable
Use nd-suppression on access portsPrevents rogue RA attacks at L2
Pair nd-suppression with MLD snoopingContains multicast/ND flooding domains
Allow DHCPv6-PD UDP 546 from fe80::/10Required if ISP delegates prefixes