IPv6 Stateful Firewall with netfilter/ip6tables

IPv6 is coming!

Ok, that was just a provocation. If you have some interest in networking technologies you’ll know for sure that stable IPv6 implementation are around for many years now but very few providers give the user IPv6 connectivity. You can follow the IPv6 deployment status around the world on this Wikipedia page.

Anyway, for those of you who are lucky enough to have a real IPv6 connection, it’s time to add the “6” to some network utility, and that includes ip6tables!

This script is a stateful firewall for an IPv6 standalone and router installation, which provides the same level of security given by an IPv4 NAT router.


This script assumes that there are two wired networks: LAN is the internal one, like a local Ethernet network, and WAN is the external, like a tunnel broker.

The internal network, as well as any WiFi networks, are considered trusted, while WAN is filtered.

Assuming you are running an IPv6 router, you want the IPv6 routing activated on the stack. You can do that with the command:

echo 1 > /proc/sys/net/ipv6/conf/all/forwarding


These are the rules that filters packets directed to the host. If this is the external node of your network, you’ll probably want to keep only the essential services open.

So, first set the default input policy to drop:

ip6tables -P INPUT DROP

then, ACCEPT all the packets from trusted networks:

ip6tables -A INPUT -i lo -j ACCEPT
ip6tables -A INPUT -i $LAN -j ACCEPT
ip6tables -A INPUT -i $WLAN -j ACCEPT

also ACCEPT any packet on the external network for trusted services (SSH and ICMPv6), remember that IPv6 uses ICMPv6 for MAC address resolution:

ip6tables -A INPUT -i $WAN -p tcp --dport ssh -j ACCEPT
ip6tables -A INPUT -i $WAN -p icmpv6 -j ACCEPT

lastly, to implement the stateful firewall, ACCEPT every known connection:

ip6tables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

Keep in mind that you can check IPv6 input policy using the link-local IPv6 addresses with the “%interface” suffix. As an example, to ping a link-local address of the host fe80::221:f3ff:fe83:1500 on the “wlan0” interface, use the command:

ping6  fe80::221:f3ff:fe83:1500%wlan0


If your host is routing IPv6 packets between interfaces, the external network have complete direct visibility of internal hosts. The NAT-like security, where all internal hosts are inaccessible from the outside, except for some trusted service, is implemented as following:

first, DROP as default FORWARD policy:

ip6tables -P FORWARD DROP

then, ACCEPT all the packets originated by the internal hosts (this will mark the conntrack entry for that connection as ESTABLISHED):

ip6tables -A FORWARD -i $LAN -o $WAN -j ACCEPT

now, accept all the trusted services (just ICMPv6):

ip6tables -A FORWARD -i $WAN -o $LAN -p icmpv6 -j ACCEPT

finally, accept packets of known connections:

ip6tables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT

That’s it! You can find the complete script, named rc.firewall6, on GitHub!


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s