in Internet Services

Getting IPv6 working for World IPv6 Day

World IPv6 Day is tomorrow, June 8 — which also happens to be my birthday. I took it as a personal challenge to see if I could get IPv6 working at home, and to report back on how difficult it was. The answer: Extraordinarily difficult and beyond the reach of the average consumer.

Neglecting the fact that no major ISP (aside from Comcast, perhaps) provides native IPv6 service to the home, one is forced to use a tunnel broker, like SiXXS or Hurricane Electric. These organizations will let you establish an IPv6 tunnel between two IPv4 endpoints (i.e. their server and your router), and they will also assign and route you an IPv6 /64 subnet. The rest — getting all the moving parts up and running — is up to you.

Warning: Nerd Alert

The rest of this post contains highly technical information, so if you’re not a nerd, you may want to stop reading here.

In order to conduct my IPv6 experiment, I needed to flash our router with a third-party firmware that would let me hack around with it. I also didn’t want to completely destroy the QoS rules I’d carefully configured for VoIP, so I purchased a new Asus RT-N16 router and flashed it with TomatoUSB, a variant on the Tomato firmware. Purchasing a new router also ensured that if I bricked it by repeated re-flashing, I’d at least have a backup.

TomatoUSB supposedly supports IPv6 out of the box, which was partially true; there was no GUI support for IPv6, but all the requisite Linux kernel modules I needed were present (sit.ko and ipv6.ko being key among them). So, I dutifully downloaded the stock Build 54 of TomatoUSB, flashed the router, and proceeded to set up IPv6.

Tomato always boots with IPv6 loaded but disabled, so I put an init script in the GUI to do the following:

echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6

The tunnel itself was very easy to set up: the commands provided by Hurricane Electric almost worked out-of-the-box, except for the fact that I had to modprobe sit instead of modprobe ipv6 (in my situation, IPv6 support was already compiled into the kernel, but tunnelling was not). Here’s the full recipe:


modprobe sit
ip tunnel add he-ipv6 mode sit remote 
  local ttl 255
ip link set he-ipv6 up
ip addr add 2001:470:1c:59a::2/64 dev he-ipv6
ip route add ::/0 dev he-ipv6
ip -f inet6 addr

Setting up Stateless Autoconfiguration with radvd

Now I needed to advertise my Hurricane Electric-assigned /64 to the local LAN by using the Linux IPv6 Router Advertisement Daemon, radvd. Otherwise, all the computers on the LAN would only have link-local IPv6 addresses, not global ones. My radvd configuration is pretty simple (br0 is the bridged LAN interface on the TomatoUSB router, which combines both the wired and wireless interfaces):

interface br0 { 
        AdvSendAdvert on;
        MinRtrAdvInterval 3; 
        MaxRtrAdvInterval 10;
        prefix 2001:470:1d:59a::/64 { 
            AdvOnLink on; 
            AdvAutonomous on; 

radvd was quite helpful with its error messages: it told me I didn’t have IPv6 forwarding turned on, so it wouldn’t start. It even told me specifically that I needed to run:

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

and start radvd again.

Trouble in Tomatoland

Great, now the LAN computers had global IPv6 addresses. But when I tried to pass IPv6 traffic through the router, it would kernel panic and reboot. Since my wife was getting mad at me for killing her wireless all the time, I needed to do more research — and also wait until she was out of town.

After much digging through the LinksysInfo forums, I discovered that this is a known problem in the stock Build 54 of TomatoUSB. I decided to take a deep breath and re-flash the router with a Toastman build of TomatoUSB; this is about as close to the bleeding edge as you can get. So now I’m running Toastman TomatoUSB 1.28.7475.2 which was only compiled a few days ago, on June 2!

There are a few problems with this revision as respects IPv6. You’ll recall previously that I have a router init script to enable IPv6 support. However, something in the Toastman build disables IPv6 again right before the interfaces are configured. (I know the script runs properly because I added a logger statement to print out the value of the /proc entry to /var/log/messages right after it’s set.) Consequently, none of the interfaces even gets a link-local address, so radvd won’t start. The workaround for now is to /sbin/ifconfig ... down one of the interfaces in the bridge (I usually choose eth1 since it’s the wireless), run the aforementioned script by hand, and then /sbin/ifconfig ... up the same interface. This forces assignment of link-local addresses.

Another annoying thing is that the IPv6 firewall won’t work, because Toastman has neglected to compile in the requisite kernel modules to track IPv6 connections — among them, nf_conntrack_ipv6.ko. Fortunately (for both me, and for hackers, I suppose) the default policy on all IPv6 chains is ACCEPT. (I’ve locked down the IPv6-enabled workstations and servers on the LAN as a result.)

Now I was very close to a working setup. I could make IPv6 requests from the router, but not from the clients on the LAN. What else could be wrong? After looking at my routing table, my colleague Blake Crosby pointed out that I didn’t have any IPv6 routes to send traffic for my IPv6 /48 back to the LAN on interface br0. After running

ip -6 addr add 2001:470:1d:59a::/64 dev br0
ip -6 route add 2001:470:1d:59a::/64 dev br0

I’m finally in business!

IPv6 Test Results screenshot

Note: I had to enable IPv6 in Firefox before I could get this to load. Firefox 4 ships with IPv6 disabled by default.


After a lot of sweating, I finally managed to get IPv6 working. But clearly, this is beyond the reach of consumers. World IPv6 Day is an excellent public relations and marketing exercise, but it will be several years before users can just plug in their off-the-shelf D-Link/Netgear/Linksys routers, click a couple of buttons, and be IPv6-enabled. As a "call to action" for consumers, the day is completely useless.

Nevertheless, I hope the discussions around IPv6 put pressure on both router manufacturers and ISPs to implement IPv6. I still feel that the protocol design is needlessly complicated for the average consumer to understand (“link-local versus global, stateless autoconfiguration whaaaaaa?!!?”). However, smart engineers will undoubtedly abstract all of this into easy-to-use web interfaces (hello, Apple). Frankly, it can’t come soon enough.

Write a Comment


  1. I've not been able to get link-local address to be assigned. Here's my current script:

    echo -n "Loading IPv6 SIT module…"
    modprobe sit
    echo " done."
    ifconfig eth2 down
    echo -n "Enabling IPv6…"
    echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6
    echo 0 > /proc/sys/net/ipv6/conf/default/disable_ipv6
    echo " done."
    echo -n "Enabling forwarding on all interfaces…"
    echo 1 > /proc/sys/net/ipv6/conf/all/forwarding
    echo 1 > /proc/sys/net/ipv6/conf/default/forwarding
    echo " done."
    ifconfig eth2 up
    echo -n "Looking up 6rd host…"
    HOST6RD=$(nslookup |grep "Address"|awk '{ print $3 }'|grep -v -m1)
    echo " done. 6rd host: ${HOST6RD}"
    WANIP=$(ip -4 addr show dev vlan2 | grep 'inet ' | awk '{print $2}' | cut -d/ -f1)
    echo "Our WAN IP address: ${WANIP}"
    if [ -n "$WANIP" ]; then
    V6PREFIX=$(printf ' 2602:100:%02x%02x:%02x%02x' $(echo ${WANIP} | tr . ' '))
    echo "Our IPv6 prefix: ${V6PREFIX}"
    ip tunnel add tun6rd mode sit ttl 255 remote any local ${WANIP}
    ip link set tun6rd mtu 1280
    ip link set tun6rd up
    echo 0 > /proc/sys/net/ipv6/conf/tun6rd/disable_ipv6
    ip addr add ${V6PREFIX}:0::1/32 dev tun6rd
    echo 0 > /proc/sys/net/ipv6/conf/br0/disable_ipv6
    ip addr add ${V6PREFIX}:1::1/64 dev br0
    ip -6 route add 2000::/3 via ::${HOST6RD} dev tun6rd
    if [ -f /var/run/ ]; then
    echo -n "Killing old radvd…"
    kill $(cat /var/run/
    echo " done."
    echo -n "Writing radvd configuration to /tmp/radvd.conf…"
    echo "interface br0 {
    MinRtrAdvInterval 3;
    MaxRtrAdvInterval 10;
    AdvLinkMTU 1280;
    AdvSendAdvert on;
    prefix ${V6PREFIX}::/64 {
    AdvOnLink on;
    AdvAutonomous on;
    AdvValidLifetime 86400;
    AdvPreferredLifetime 86400;
    };" > /tmp/radvd.conf
    echo " done."
    echo -n "Starting radvd…"
    radvd -C /tmp/radvd.conf
    echo " done."
    (Yes, it's very verbose)

  2. Just found the solution to my problem. I put the following in the "init" script:
    echo 0 > /proc/sys/net/ipv6/conf/default/disable_ipv6
    echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6

    Most interfaces are now assigned a link-local address on startup.

  3. Same situation here. Wife gets mad when I kill her wireless.
    Anyway, I'm down to just squid and ipv6 on my Belkin Playmax.
    Good article!