published on in Uncategorized
tags: dd-wrt firewall ipv6

Home IPv6 Tunnel

The news of World IPv6 Launch Day last week prompted me to properly set up IPv6 for my home network. Unfortunately my ISP doesn’t offer native IPv6 connectivity — not many consumer ISPs do — but fortunately there are free services which will provide an IPv6 ‘tunnel’ over the IPv4 internet, to the wider IPv6 world. I signed up to Hurricane Electric’s service at and was soon up and running. Hurricane Electric will issue you a /64 block of IPv6 addresses, which you can allocate as you see fit. You need to set up one system to handle your end of the tunnel, and route all traffic destined for the wider IPv6 internet over the tunnel. I already had a Linux server running that was handling the local end, but since I’ve recently acquired an Asus RT-N16 router running DD-WRT, I decided to enable IPv6 support on it and use it as the tunnel endpoint, so it can serve as the default gateway for both IPv4 and IPv6 on my LAN.

To do this you need to be running one of the DD-WRT builds with IPv6 support, I am using the “mega” build which includes pretty much everything. As well as enabling IPv6 support in the DD-WRT admin web interface, you need to set up a custom script to bring up the tunnel when the router starts up. The script I used is below – you need to replace the following:

the User ID shown on the page when you log in
the MD5 hash of your password
the Tunnel ID of your tunnel
the IPv4 address of the remote end
the IPv6 address of the local end
one IPv6 address, out of your routed IPv6 /64, allocation that you want to use for your router on your LAN
insmod ipv6
WANIP=$(nvram get wan_ipaddr);
wget -O - ''
ip tunnel add he-ipv6 mode sit remote (remotev4) local $WANIP ttl 255
ip link set he-ipv6 up
ip -6 addr add (localv6) dev he-ipv6
ip -6 addr add (lanv6) dev br0
ip -6 route add ::/0 dev he-ipv6
echo 1 > /proc/sys/net/ipv6/conf/all/forwarding
radvd -C /tmp/radvd.conf

This image shows where to find some of the information in your page: configuration page

The setup script needs to be saved, as a custom startup script, in the Administration -> Commands section of the DD-WRT web interface.

I had also enabled radvd in the DD-WRT web interface to advertise the gateway to the wider IPv6 world on my LAN, but for me it failed to start on boot, so I added the radvd -C /tmp/radvd.conf line to make sure it starts after the tunnel is configured. The configuration file used for radvd is below, replace (routedv6) with your routed IPv6 /64 allocation, including the /64, and paste it into the box for radvd config in the DD-WRT web interface:

interface br0
   AdvSendAdvert on;
   prefix (routedv6)
       AdvOnLink on;
       AdvAutonomous on;

This setup means that on the other computers on the LAN (all Macs), I can set “Configure IPv6: Automatically” in the Network Preferences and IPv6 connectivity just works. The other machines on the LAN will configure themselves based on the information radvd sends out, using stateless autoconfiguration. However, this ease of use comes with a security risk…

We are used to having devices on a home network behind a home router, plugged into the DSL or Cable modem. The router normally does NAT to allow all the computers on the LAN to use a single IPv4 address that’s assigned by our ISP, and includes a simple firewall. Devices on the LAN have private IPs (e.g. in the range) and are by default not reachable from the wider Internet, unless you specifically forward ports on your router to them. However with the advent of IPv6 this changes somewhat. As I mentioned above, with IPv6 connectivity comes a /64 block of routable IPv6 addresses to assign to devices on the LAN – each of these is fully reachable from the outside Internet. And if you’ve set up the system hosting the tunnel endpoint as an IPv6 router, it will happily send packets between anyone on the Internet and devices on your LAN. This is generally a Bad Thing, as we might be running all kinds of services like file sharing or VNC on our home systems that we don’t want (or need) exposed to the outside world. What we need is a firewall which works without NAT, allowing each device to have its own routable IPv6 address while protecting it from intrusion.

There are two options; either run a firewall on each machine on the LAN individually, or only run a firewall on the gateway and filter traffic at the ingress point to the LAN. I went with the latter option, since it only requires configuring one system instead of several, and I’m more familiar with the Linux iptables firewall system than the Mac OS X pf firewall.

Unfortunately while DD-WRT has basic IPv6 support, it doesn’t include the necessary kernel modules or userspace tools for setting up an IPv6 firewall. I had to download the kernel modules and ip6tables packages, following instructions here and install them to the /jffs partition on the router with ipkg (you also have to enable JFFS2 support on the router for this to be possible).

Once all this is installed, another script is necessary to start and configure the firewall. I used the following:

export IP6TABLES_LIB_DIR=/jffs/usr/lib/iptables
export PATH=$PATH:/jffs/usr/sbin
insmod /jffs/lib/modules/
insmod /jffs/lib/modules/
insmod /jffs/lib/modules/
insmod /jffs/lib/modules/
insmod /jffs/lib/modules/
insmod /jffs/lib/modules/
insmod /jffs/lib/modules/
insmod /jffs/lib/modules/
insmod /jffs/lib/modules/
insmod /jffs/lib/modules/
insmod /jffs/lib/modules/
insmod /jffs/lib/modules/
insmod /jffs/lib/modules/
insmod /jffs/lib/modules/
insmod /jffs/lib/modules/
insmod /jffs/lib/modules/


#flush tables
ip6tables -F

#define policy
ip6tables -P INPUT DROP
ip6tables -P FORWARD DROP
ip6tables -P OUTPUT ACCEPT

# Input to the router
# Allow all loopback traffic
ip6tables -A INPUT -i lo -j ACCEPT

#Allow unrestricted access on internal network
ip6tables -A INPUT -i $LAN_IF -j ACCEPT

#Allow traffic related to outgoing connections
ip6tables -A INPUT -i $WAN_IF -m state --state RELATED,ESTABLISHED -j ACCEPT

# for multicast ping replies from link-local addresses (these don't have an
# associated connection and would otherwise be marked INVALID)
ip6tables -A INPUT -p icmpv6 --icmpv6-type echo-reply -s fe80::/10 -j ACCEPT

# Allow some useful ICMPv6 messages
ip6tables -A INPUT -p icmpv6 --icmpv6-type destination-unreachable -j ACCEPT
ip6tables -A INPUT -p icmpv6 --icmpv6-type packet-too-big -j ACCEPT
ip6tables -A INPUT -p icmpv6 --icmpv6-type time-exceeded -j ACCEPT
ip6tables -A INPUT -p icmpv6 --icmpv6-type parameter-problem -j ACCEPT
ip6tables -A INPUT -p icmpv6 --icmpv6-type echo-request -j ACCEPT
ip6tables -A INPUT -p icmpv6 --icmpv6-type echo-reply -j ACCEPT

# Forwarding through from the internal network
# Allow unrestricted access out from the internal network
ip6tables -A FORWARD -i $LAN_IF -j ACCEPT

# Allow some useful ICMPv6 messages
ip6tables -A FORWARD -p icmpv6 --icmpv6-type destination-unreachable -j ACCEPT
ip6tables -A FORWARD -p icmpv6 --icmpv6-type packet-too-big -j ACCEPT
ip6tables -A FORWARD -p icmpv6 --icmpv6-type time-exceeded -j ACCEPT
ip6tables -A FORWARD -p icmpv6 --icmpv6-type parameter-problem -j ACCEPT
ip6tables -A FORWARD -p icmpv6 --icmpv6-type echo-request -j ACCEPT
ip6tables -A FORWARD -p icmpv6 --icmpv6-type echo-reply -j ACCEPT

#Allow traffic related to outgoing connections
ip6tables -A FORWARD -i $WAN_IF -m state --state RELATED,ESTABLISHED -j ACCEPT

# allow SSH and HTTP(S) dport in from the Internet
ip6tables -A FORWARD -s 2000::/3 -i $WAN_IF -p tcp -m multiport --dports 22,80,443 -j ACCEPT

This script needs to be saved as a Firewall script, again in the Administration -) Commands section of DD-WRT’s web interface. This is a fairly simple setup which allows all LAN and outgoing traffic, but blocks all incoming traffic from the internet, to any computer on the LAN, other than some ICMPv6 messages and traffic destined for SSH, HTTP and HTTPS ports. There are services running on some machines on the LAN on these ports that need to be reachable from the outside world; if you don’t need this for your network then remove the last rule. You can also add rules like

ip6tables -A FORWARD -s 2000::/3 -d (lan address) -i $WAN_IF -p tcp -m multiport --dports 5269,5222,64738 -j ACCEPT

to allow traffic on certain ports through only to one machine on your LAN.

I can now run an IPv6 port scan (for example this one) against any machine in my network and see only the necessary ports open, and traffic to all others being dropped. I can also check out various cool IPv6 stuff on the internet, get my IPv6 badge: IPv6 Certification Badge for sigmaris

…and be content that all my traffic to and from and, among others, is now going via IPv6 :).