Native IPv6 in dd-wrt

As I mentioned in a previous post, I installed dd-wrt (kernel 2.6, VOIP, build 14896) on my wireless router (Linksys WRT320N), which connected to a VDSL modem using PPPoE. After that, it worked fine for IPv4, so I had the same functionality as the original Linksys firmware. However, the purpose of the exercise was to get IPv6 support: this turned out to be easier said than done. I was eventually able to get it working, so if you only want the short answer and aren’t interested in all the troubleshooting steps that I went through, scroll down to the Conclusion section at the bottom of this post.

Please refer to my IPv6 router post to get an overview of what I’m trying to achieve here. Most of the documentation that I’ve found assumes that you’re using a tunnel: this is similar to a proxy server, where you have an IPv4 connection to a machine on the internet, then that machine connects to your real destination using IPv6. However, I have native IPv6 connectivity from my ISP.

In theory, this looked pretty simple. I logged into the router’s website, went to Administration | Management, then the IPv6 Support section. Initially it looked like this:

IPv6a

After I enabled IPv6, extra options appeared, so I enabled Radvd (router advertisements) too.

IPv6b

This matches the IPv6 (tutorial) on the dd-wrt wiki.

I saved my change and applied the settings, but it didn’t make any difference, i.e. I couldn’t access the “Loops of Zen” website from a client computer. When I ran ipconfig /all on a Windows client, the only IPv6 address was link-local and it didn’t display an IPv6 default gateway. So, that meant that the client hadn’t received any Router Advertisement (RA) packets. I tried rebooting the router and the PC, but that didn’t help.

At this point, you need to turn to the command line; I’ve been using SSH for all this. You can use Administration | Commands on the website for some of the commands if you prefer, but I’ve found that it doesn’t always work correctly, e.g. the output of top keeps changing and the website doesn’t display any results.

Going back to the dd-wrt forum, I found a discussion about IPv6 in v24. In particular, crushedhat’s post was later copied to the wiki (IPv6 on v24). According to that, the “Enable IPv6” option doesn’t actually do anything, so you need to use a startup script to load the module. The lsmod command displays a list of all the modules which are currently loaded into the router’s memory. I saw “ipv6” in that list, so I knew that it was working correctly. crushedhat was using an older build (9856), so maybe the dd-wrt developers fixed this problem in a subsequent build.

The equivalent command for daemons is ps, which lists all the currently running processes. I didn’t see “radvd” in that list, so I think that part of crushedhat’s analysis is still relevant to build 14896: “It seems that the radvd enabled by the GUI option starts in a different thread from the startup script, so that it exits immediately upon discovering IPv6 missing in the kernel or an incomplete config.”

I tried to start the process manually, but I got an error message:
root@DD-WRT:~#radvd
radvd: can't open /usr/local/etc/radvd.conf: No such file or directory

So, apparently you need a configuration file. Going back to the Administration | Management webpage, there’s a box to enter “Radvd config”. Here’s the bare minimum that you need:

interface br0
{
    AdvSendAdvert on;
    prefix fd32:d2c1:5bd7:1234::/64
    {
    };
    prefix 2001:db8:abcd:1::/64
    {
    };
};

You can list all the active interfaces on the router by running ifconfig. In particular, br0 is the bridge and lo is the loopback. (Unlike Windows/IOS, dd-wrt doesn’t list Ethernet ports unless there’s a cable plugged in; it also only lists the “ppp0” interface while there’s a PPPoE session in progress.)

I’ve specified 2 prefixes: unique local and global unique. You should replace these with whatever you’re using on your network.
NB Don’t put a semi-colon after the prefix. It goes after the inner set of curly brackets, so that they’re all part of the same command. If you want any prefix-specific options then they go inside the inner curly brackets. You can’t omit the curly brackets, even if you don’t specify any options there.

This config should advertise the prefixes via that interface. So, I clicked Save, then Apply Settings. However, radvd still didn’t show up in the list of running processes, and I got the same error when I tried to run it. The issue here is that the website doesn’t store the config file in the place that the daemon is looking for it. Instead, it gets saved in the /tmp folder. In fact, the /usr/local/etc folder doesn’t even exist! However, you can run the daemon if you specify the correct location as a parameter:
root@DD-WRT:~#radvd -C /tmp/radvd.conf
If the command succeeds then it doesn’t display any message, but you will see that command (including the parameter) in the list of processes.

That did the trick for me: my clients got IPv6 addresses with the correct prefixes. (In fact, they got 2 addresses for each prefix, because one of them is a temporary address.) They also got the router’s link local address as an IPv6 default gateway (in addition to the existing IPv4 default gateway).

Back on the router, I ran radvdump. This took a couple of minutes to display anything, because I had to wait for the next router advertisement; by default, these are issued every 200 seconds. It then displayed a longer version of the radvd config, including the default values for each option. The Linux man page has a list of all the options, so you can use that to add extra lines to your config file if appropriate. For instance, AdvManagedFlag and AdvOtherConfigFlag handle the M and O flags respectively; I’m leaving them both turned off until/unless I set up a DHCPv6 server.

However, when I ran ip -6 addr show, the router didn’t have any unique local or global unique addresses:

root@DD-WRT:~# ip -6 addr show
1: lo:  mtu 16436
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: eth0:  mtu 1500
    inet6 fe80::b42a:059e:4ac6:7e86/64 scope link
       valid_lft forever preferred_lft forever
3: eth1:  mtu 1500
    inet6 fe80::8929:27a2:a8cd:2cfb/64 scope link
       valid_lft forever preferred_lft forever
7: vlan1@eth0:  mtu 1500
    inet6 fe80::0078:9f23:ab21:c015/64 scope link
       valid_lft forever preferred_lft forever
8: vlan2@eth0:  mtu 1500
    inet6 fe80::3832:0340:9767:02dd/64 scope link
       valid_lft forever preferred_lft forever
10: br0:  mtu 1500
    inet6 fe80::b939:a1ae:7d44:391b/64 scope link
       valid_lft forever preferred_lft forever

The loopback interface has a special address (::1/128) and every other interface has a link local address.

So, I manually assigned a unique local and global unique address to the router’s LAN interface, using the same prefixes as before:
root@DD-WRT:~# ip -6 addr add fd32:d2c1:5bd7:1234::1/64 dev br0
root@DD-WRT:~# ip -6 addr add 2001:db8:abcd:1::1/64 dev br0
root@DD-WRT:~#

This succeeded silently, and I saw those new addresses when I ran ip -6 addr show again. However, these commands don’t persist after a reboot, so you need to repeat them each time (similar to radvd).
NB You have to put /64 after each address, otherwise it defaults to /128 and the router won’t communicate with the rest of the subnet.

At this point, all the machines had link local, unique local, and global unique IPv6 addresses, but the client computers still didn’t have IPv6 internet access. I used ping to investigate further, which opened a whole new can of worms.

Turning to Windows for a moment, there are several ways to ping another machine:

  • If you specify an IPv4 address, it will ping the machine using IPv4.
  • If you specify an IPv6 address, it will ping the machine using IPv6.
  • If you specify a name, it will try to resolve that name to an address. Then:
    • If there is an IPv6 address (or you specify the “-6” parameter), it will ping the machine using IPv6.
      NB If the machine has an IPv6 and an IPv4 address, Windows will favour IPv6 by default.
    • If there is only an IPv4 address (or you specify the “-4” parameter), it will ping the machine using IPv4.
    • If there are no DNS records for that name, it will display an error message.

So, I was able to ping the router from a Windows client using all 3 of its IPv6 addresses (link local, unique local, and global unique).

I would expect the same functionality from Linux. However, when I tried it, I actually got silent failures. For instance:
root@DD-WRT:~# ping ipv6.google.com
root@DD-WRT:~#

So, it didn’t say that it can’t resolve the FQDN, or that the machine is unreachable, but it didn’t display any successful results either. dd-wrt will complain if I type in a command that doesn’t exist, e.g.
root@DD-WRT:/tmp# ipconfig
-sh: ipconfig: not found

So, this is specific to the ping command.

Trying out more options:
root@DD-WRT:/tmp# ping
root@DD-WRT:/tmp# ping fred
root@DD-WRT:/tmp# ping 192.168.169.2
PING 192.168.169.2 (192.168.169.2): 56 data bytes
64 bytes from 192.168.169.2: seq=0 ttl-128 time=1.589 ms
64 bytes from 192.168.169.2: seq=1 ttl-128 time=0.909 ms
64 bytes from 192.168.169.2: seq=2 ttl-128 time=0.893 ms
64 bytes from 192.168.169.2: seq=3 ttl-128 time=0.957 ms

--- 192.168.169.2 ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max = 0.893/1.087/1.589 ms
root@DD-WRT:/tmp# ping 2a00:1450:4009:802::1012
root@DD-WRT:/tmp# ping -4
root@DD-WRT:/tmp# ping -6
ping: illegal option -- 6
root@DD-WRT:/tmp#

So, it could handle IPv4 addresses but not IPv6 addresses.
NB When I entered an IPv4 address on the local subnet (192.168.169.2), dd-wrt kept pinging it indefinitely; unlike Windows, it doesn’t stop automatically after 4 attempts, so I pressed Ctrl+C to get back to the command prompt.

Going back to the dd-wrt forum, I found this discussion: can NOT get ping to work. According to a response there, “its compiled without v6 support.” I think the situation is actually a bit more complex than that: I have partial IPv6 support. For instance, the router would respond to ping requests (over IPv4 and IPv6) from other machines in the subnet, it just couldn’t initiate them over IPv6. Similarly, I was able to add IPv6 addresses, and I could view the IPv6 routing table with this command:
ip -6 route show

So, although this build is supposed to include IPv6, it looks as if the developers cut a few corners. For instance, you can enter and view IPv4 addresses via the built-in website, but you can’t do that for IPv6. To be fair, I realise that they are under space constraints: fitting an operating system into 4 MB is more of a challenge than having several GB at your disposal. However, it seems odd that they would consider AnchorFree (advertising for your wireless hotspot) to be core functionality, while ping was treated as an optional extra.

The wiki offers a solution to this problem. Going back to the IPv6 tutorial, they suggest running this command:
root@DD-WRT:~# ipkg -force-depends install http://downloads.openwrt.org/kamikaze/8.09.2/brcm47xx/packages/iputils-ping6_20071127-1_mipsel.ipk

You may notice that this refers to the OpenWRT website (rather than dd-wrt). That’s why you have to use the “force-depends” parameter, which converts errors into warnings: the package was never intended to work with this distribution of Linux.

However, before you can even run the ipkg command, you need to enable JFFS. Quoting from the wiki: “Journalling Flash File System (JFFS/JFFS2) is a re-writable area within a DD-WRT-enabled device.” In other words, it gives you a bit of storage space so that you can download extra files.

So, go back to the Administration | Management page on the website (where you enabled IPv6), and find the JFFS2 Support section. Initially, it will look like this:

JFFSa

NB The instructions from the forum differ slightly from the wiki. I took a hybrid approach, and there may be a smoother way of doing this.

First, I selected “Enable” for “JFFS2” to reveal extra settings, then clicked Save (but not Apply Settings). Next, I selected “Enable” for “Clean JFFS2”, then clicked Apply Settings. According to the wiki, this should format the available space, then you should disable the “Clean JFFS2” option again. I don’t know whether I actually did that (it’s not in my notes), but the option is definitely disabled now; so, my advice is to disable it yourself if it doesn’t happen automatically.

Looking at the bottom of the JFFS2 Support section, the web page then displayed this text:
“Total / Free Size 2,816.00 KB / 0”
That wasn’t encouraging, because I obviously need some free space to install the new package.

According to the forum post, the router should automatically reboot, but this didn’t happen for me. The wiki says that you should click Save (which I did), and then maybe reboot to be on the safe side (which I also did). After the reboot, I still saw the same text (free size 0 bytes).

The forum post then says: “I, after this reboot, power router off and on.” That implies another reboot, and I think that’s what I must have done; I haven’t explicitly documented it in my notes (sorry), but later on the web page displayed a better message:
“Total / Free Size 3,776.00 KB / 3,496.00 KB”

JFFSb

The wiki says that “your build must leave at least 324KB of flash free for the JFFS file system structure which will not be usable space.” I’m guessing that this explains the 280 KB difference between total size and free size, even though the figures don’t quite match.

Anyway, once that’s sorted out, you can install the new package. I modified the previous command slightly, based on the Ipkg tutorial:
root@DD-WRT:~# ipkg -d root install http://downloads.openwrt.org/kamikaze/8.09.2/brcm47xx/packages/iputils-ping6_20071127-1_mipsel.ipk
I used “-d” to specify a location (i.e. JFFS rather than RAM). I also got rid of the “-force-depends” parameter, because I wanted to see what the errors were; I intended to then run the command again with that parameter, but it turned out not to be necessary.

I only saw these 3 errors:

ERROR: File not found: /jffs/usr/lib/ipkg/lists/whiterussian
       You probably want to run 'ipkg update'
ERROR: File not found: /jffs/usr/lib/ipkg/lists/non-free
       You probably want to run 'ipkg update'
ERROR: File not found: /jffs/usr/lib/ipkg/lists/backports
       You probably want to run 'ipkg update'

I got the same errors when I tried to run ipkg list, and it’s basically saying that it doesn’t have a list of all the available packages. I tried running ipkg update, and this succeeded for the first 2 lists but failed on the backports list:
Downloading http://downloads.openwrt.org/backports/rc5/Packages ...
Connecting to downloads.openwrt.org (78.24.191.177:80)
ipkg download: ERROR: Failed to retrieve http://downloads.openwrt.org/backports/rc5/Packages, returning
ipkg_update: Error downloading http://downloads.openwrt.org/backports/rc5/Packages to /jffs/usr/lib/ipkg/lists/backports

When I try browsing to that site, the only sub-folder of http://downloads.openwrt.org/backports/ is “0.9” (rather than “rc5”), so it looks as if dd-wrt is out of sync with the OpenWRT website. Maybe a newer build has solved that problem, but I think that relying on a website which you don’t control is always going to be error-prone.

Anyway, after that I was able to ping IPv6 addresses. The existing ping command hadn’t changed, so I needed to use the new ping6 command for this. I tested one of my client machines, using a unique local address and a global unique address, and every packet came back (0% loss). However, I couldn’t ping the client via its link local address, and I couldn’t ping an IPv6 address on the internet (one of A&A’s DNS servers):

root@DD-WRT:~#ping6 fe80::5932:cab1:bf18:7bc9
connect: Invalid argument
root@DD-WRT:~# ping6 2001:8b0:2020
connect: Unknown host
root@DD-WRT:~# ping6 2001:8b0::2020
connect: Network is unreachable
root@DD-WRT:~#

On the plus side, ping6 actually gives more useful results than ping, even when it doesn’t work. For instance, my first attempt at typing in the DNS server address had a typo: I only put 1 colon between “8b0” and “2020”, so that wasn’t a complete address.

I can understand why the router would have trouble with the link local address: it has multiple interfaces, so it doesn’t know which one to pick. (In Windows, you can use the % sign to identify a network interface on a multi-homed machine.)

As for the DNS server (after I typed in the correct address), the error indicates a routing problem. I ran ip -6 route show to check the IPv6 routing table, and it didn’t have a default route, so I added one:
root@@DD-WRT:~# ip -6 route add ::/0 dev ppp0

I then tried pinging A&A’s DNS server again, but this time it failed a different way:

root@DD-WRT:/# ping6 2001:8b0::2020
PING 2001:8b0::2020(2001:8b0::2020) 56 data bytes

--- 2001:8b0::2020 ping statistics ---
40 packets transmitted, 0 received, 100% packet loss, time 39007ms

root@DD-WRT:/#

The time is so long (39 seconds) because it just sat there doing nothing until I pressed Ctrl+C. So, it was at least attempting to ping now, but it wasn’t getting anywhere.

Looking back at the list of IPv6 addresses (the output from ip -6 addr show), it didn’t include the ppp0 interface at all. When I ran ip addr show instead (to get IPv4 and IPv6 addresses), the output included these lines:

33: ppp0:  mtu 1492 qdisc pfifo_fast
    link/ppp
    inet 203.0.113.57 peer 81.187.81.187/32 brd 203.0.113.57 scope global ppp0

NB The index number (33) and the IPv4 addresses will vary between routers.

So, the ppp0 interface only had an IPv4 address. This is a big problem: it’s supposed to have a link local IPv6 address too. The explanation for this involves digging a bit deeper. Like radvd, pppd (the PPP daemon) uses a configuration file which is stored in the /tmp folder. Specifically: /tmp/ppp/options.pppoe

My copy looked like this:

plugin /usr/lib/rp-pppoe.so
nic-vlan2
noccp
nomppc
noipdefault
noauth
defaultroute
noaccomp
nobsdcomp
nodeflate
nopcomp
nomppe
usepeerdns
user 'REDACTED'
password 'REDACTED'
default-asyncmap
mtu 1492
mru 1492
persist
lcp-echo-interval 5
lcp-echo-failure 10

(I’ve removed my username and password, but they appear in plain text and match what I typed into the website, i.e. they’re the credentials for my internet account. If you have an A&A account, your username will be something like ‘foobar@a.1’.)

According to a post from Striek in another forum (TekSavvy IPv6 on DD-WRT v24), you can append “ipv6” to the end of the options.pppoe file and then restart the process. However, that didn’t work for me; when I tried it, the config file reverted to how it was before. In fact, even if I didn’t restart the process, the config file still reverted to the previous settings after 30 seconds. I found a support ticket (#2168) which confirms this: “The 30s change comes from the fact, that the redial process is called every 30s.”

I haven’t checked through the source code, but I’d speculate that when you start the daemon the logic goes something like this:

  1. Delete the config file (if it already exists).
  2. Generate a new config file based on the information from the website.
  3. Use the standard Linux code to read in the contents of that config file and establish a new PPPoE session.

So, whatever changes I made to the temporary copy would be discarded. I don’t know which build Striek was using, which may explain why they got different results.

Support ticket #2168 was closed as invalid, on the basis that this behaviour isn’t a bug. Someone then created a new bug report (#2835) specifically for the IPv6 option, and that was fixed in changeset 21530. According to the dd-wrt forum: “Those changeset numbers are what release numbers refer to. E.g. KongAC 26970 would be a build that included all changesets up to (and including) 26970.” So, you need build 21530 or later in order to use IPv6 over PPPoE.

This goes back to the issues that I mentioned in my previous post about installing dd-wrt: how do you choose which build to install? The Peacock post still recommends 14929 as the best build, and the idea seems to be that you can try anything newer at your own risk. Looking at the ftp site, the oldest build which includes changeset 21530 is 21676 (released on 2013-05-27). I found a few forum posts which support that choice, e.g. in the Latest working firmware for WRT320N thread, someone said: “I had to downgrade to 21676 because the wifi doesn’t work in 22118.” (I can see why that would be a bit of a drawback in firmware for a wireless router!)

Anyway, there are various sub-folders for 21676. I think most of them are trailed builds for specific hardware, but my router isn’t listed there, so I needed to go to the broadcom_K26 sub-folder (i.e. version 2.6 of the Linux kernel for Broadcom hardware). This then brought up a new problem: there isn’t a file for the VOIP option. They do have a “VOIP Small” file (dd-wrt.v24-21676_NEWD-2_K2.6_voip_small.bin) but according to the wiki that’s a different type which doesn’t support IPv6. In fact, I’ve looked at the files for various builds which were released in the past few years (2013-2015), and I can’t find any for VOIP. I did find a forum post from 2011, asking whether that type had been discontinued, but nobody answered; as I’ve come to expect, there’s no relevant info in the wiki or the router database.

According to the feature table on the wiki, these 6 types support IPv6:

  • STD NoKaid Small
  • STD USB NAS
  • VOIP
  • Big
  • Mega
  • (Giga)

As I mentioned in my previous post, Giga doesn’t exist yet, so that’s out. STD NoKaid Small doesn’t include JFFS2 (and therefore you can’t install extra modules), so I discarded that too. Along with VOIP, that eliminates 3 of the 6 options. Looking at file sizes for build 21676:

  • STD USB NAS – 5.91 MiB
  • Big – 7.47 MiB
  • Mega – 7.58 MiB

The WRT320N has 8 MiB of flash storage space, and the Big and Mega builds are getting quite close to that limit, which doesn’t leave much space to install any other packages or store data files. The STD USB NAS type includes all the features from the (former) VOIP build except for SIPatH/Milkfish, and adds a few extra. Since I’m not using my router for IP telephony, that seems like the best option for me.

So, I reset the router to factory settings, which triggered a reboot. After that, my laptop got a different IPv4 address (via DHCP), and all the IPv6 addresses disappeared except for the link local address. I then logged back into the website via its new address (http://192.168.1.1/), uploaded the new firmware, and told it to reset to factory defaults afterwards (just in case the new build has different defaults); that triggered another reboot. After that, I had to re-enter all of my previous settings through the GUI. As per my previous post, I was able to enter my basic settings (for IPv4 internet access) without any trouble, so I can confirm that build 21676 won’t brick a WRT320N router. However, I found some odd results when I tried to use IPv6.

As described above, I used the embedded website to enable ipv6; I left radvd disabled at first. When I inspected the /tmp/ppp/options.pppoe file, it looked exactly the same as before, i.e. it didn’t include a line for “ipv6”. When I ran ip addr show, it didn’t display any results at all. Looking online, I found support ticket 2590; according to the developers, this was a deliberate change to reduce the code size, and people should use the ifconfig command instead. Part of the problem here is that there aren’t any release notes for new builds of dd-wrt, so there’s nothing to announce a change like this unless you go digging through the forum etc. Also, I agree with the people who commented on that support ticket (e.g. psi-jack): ifconfig is part of the net-tools package, which was deprecated in 2009 in favour of the iproute2 suite (which includes the ip command). So, dd-wrt is effectively going backwards!

Anyway, looking at the output from ifconfig, it didn’t display any IPv6 addresses at all; I’d expect to see a link local address on each interface. I tried to add an IPv6 address to the br0 interface as before, but this produced an error:
root@DD-WRT:~# ip -6 addr add fd32:d2c1:5bd7:1234::1/64 dev br0
RTNETLINK answers: Operation not supported

I then checked the routing tables, using these 3 commands:

  • ip route
  • ip -4 route
  • ip -6 route

To my surprise, they all returned the same results, i.e. a list of IPv4 routes and no IPv6 routes. If this build didn’t support IPv6 at all, or if I’d left the IPv6 option disabled, it’s reasonable that the first 2 commands would only display IPv4 routes. However, it’s fundamentally wrong for the “ip -6” version of the command to display IPv4 information! I wondered whether the command was simply ignoring the parameter, but when I ran ip -5 route it didn’t display anything at all, so it knew that the “-5” parameter was invalid.

I then enabled radvd and entered the relevant commands for radvd, but my client computer didn’t get any new IPv6 addresses. When I ran radvdump on the router, I got another error message:
radvdump: can't create socket(AF_INET6): Address family not supported by protocol
open_icmpv6_socket: No such file or directory

Similarly, when I ran ps I didn’t see radvd in the list, and the /tmp/radvd.conf file didn’t exist.

At this point, I thought that the “STD USB NAS” build didn’t actually support IPv6 after all, despite what the wiki claimed. However, I then went back into the embedded website, checked the information on the Administration | Management page (without changing anything) and clicked Apply Settings again. After that, things suddenly started working. The /tmp/radvd.conf file now existed, and ps showed that the radvd -C /tmp/radvd.conf process was running. (In fact, for some reason there were 2 copies of it running, which contradicts the previous claim that the second instance would automatically exit.) So, my client then picked up the correct prefixes to assign its own IPv6 addresses via SLAAC. Also, the 3 commands which I tried earlier now ran successfully:

  • radvdump
  • ip -6 route
  • ip -6 addr add fd32:d2c1:5bd7:1234::1/64 dev br0


At this point, I also added the router’s global unique IPv6 address. When I checked the /tmp/ppp/options.pppoe file, it now included “ipv6 ,” on the final line. (I don’t think the comma is actually necessary, so that’s probably a coding glitch.)

This is all good news, but it’s bad behaviour by the router. My best guess is that when you click Apply Settings in the embedded website, it only applies the settings for the specific page that you’re looking at rather than applying all the settings. So, if you make changes on several pages then the safest (and slowest) option is probably to click Save and Apply Settings on each page. A reboot might also help, if that forces the router to re-apply all the saved settings.

I then did some ping tests: my client and router could both ping each other, using unique local and global unique addresses. As a side point, restoring my router to factory settings and then doing the firmware upgrade meant that the “jffs” option was disabled. I didn’t re-enable it, but the router still accepted ping6 as a valid command. It will also ping IPv6 addresses using the standard ping command, so I think that means that the IPv6 functionality is now part of the build and you no longer need to install an extra module. That’s definitely a step forward for dd-wrt, so well done to the developers on that. This may mean that the “STD NoKaid Small” build would also work, if you don’t need JFFS. However, I haven’t tested that; I’d be interested to hear any comments from people who do have that type.

At this point, pinging an internet address still didn’t work, although it failed in a slightly different way to before:
root@DD-WRT:~# ping 2001:8b0:2020
root@DD-WRT:~# ping 2001:8b0::2020
PING 2001:8b0::2020 (2001:8b0::2020): 56 data bytes
root@DD-WRT:~#

So, if you provide an invalid IPv6 address, it doesn’t display anything at all. If you provide a valid IPv6 address, it displays the first line as usual and then gives up. As before, this is because we need to add a default route:
root@@DD-WRT:~# ip -6 route add ::/0 dev ppp0

I then tried pinging A&A’s DNS server again, and this time it worked!

root@DD-WRT:/# ping 2001:8b0::2020
PING 2001:8b0::2020(2001:8b0::2020) 56 data bytes
64 bytes from 2001:8b0::2020: seq=0 ttl=62 time=11.959 ms
64 bytes from 2001:8b0::2020: seq=1 ttl=62 time=11.393 ms
64 bytes from 2001:8b0::2020: seq=2 ttl=62 time=11.657 ms
64 bytes from 2001:8b0::2020: seq=3 ttl=62 time=11.807 ms

--- 2001:8b0::2020 ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max = 11.393/11.704/11.959 ms
root@DD-WRT:/#

Similarly, I could ping that address from a client computer, and I could access IPv6-only websites (e.g. “Loops of Zen”).

At this point, it’s a good idea to create a startup script, so that you won’t have to re-enter the same commands each time you reboot the router. So, in the website, go to: Administration | Commands. In the Command Shell section, type the following lines into the text box:

ip -6 addr add fd32:d2c1:5bd7:1234::1/64 dev br0
ip -6 addr add 2001:db8:abcd:1::1/64 dev br0
ip -6 route add ::/0 dev ppp0

Then click Save Startup. This will add a new Startup section to the page, and you can edit the script later. You can also view the script by running this command:
root@DD-WRT:~# nvram get rc_startup

I’ve tested this with a reboot, and confirmed that you don’t need the radvd -C /tmp/radvd.conf command, so it looks as if the developers have fixed that bug.

However, there’s a problem if the DSL line is down when you reboot. I simulated this by pulling out the modem cable, but it could also apply if there’s a problem at the far end. When I plugged the cable back in, the router automatically got a default route for IPv4 (via the ppp0 interface) but not for IPv6, so I had to retype that command. Alternately, you could just reboot the router (e.g. by turning it off and then on again) in which case the startup script should handle that for you. So, this isn’t ideal, but it’s good enough for everyday use.

I’ve tried accessing the router’s other features via IPv6:

  • SSH works fine.
  • HTTP doesn’t work. (I put the IPv6 address inside square brackets in the web browser’s address bar.)
  • DNS doesn’t work. (I ran nslookup on a client, then changed the server.)

So, it’s only going to work in dual-stack mode, not in “pure IPv6” mode (at least in this build). That also means that there’s no point trying to set up a DHCPv6 server, because it won’t have any IPv6 addresses to hand out for DNS servers, unless you have another DNS server on your network. (If you have domain controllers on a Windows network, you may as well get them to act as DHCP servers too.)

Speaking of DHCP, I found another blog post: Adding DHCPv6-PD support to DD-WRT. In theory, this would pick up the global unique prefix from the ISP via prefix delegation. However, that looks similar to what I previously did with ping6, i.e. you have to install extra modules. So, I haven’t tried it; I’m willing to just type the prefix in 2 places (Radvd config and the startup script) and change it if/when necessary.

The final issue is firewall configuration. According to the wiki, you need to download extra modules for this; however, I think that information is out of date, i.e. it doesn’t apply to newer builds. Checking on my router:

root@DD-WRT:~# iptables
iptables v1.3.7: no command specified
root@DD-WRT:~# ip6tables
ip6tables v1.3.7: no command specified
Try 'ip6tables -h' or 'ip6tables --help' for more information.

So, iptables (for IPv4) and ip6tables (for IPv6) both seem to be installed. I haven’t configured them, because I’ve decided to use a separate device as a firewall. In the meantime, I figure that my wireless clients have to fend for themselves when I connect them to other networks (e.g. the average pub probably isn’t running a high security firewall), so I’m no worse off here.

Conclusion

So, here’s a quick recap on the relevant steps. If you have a WRT320N router, and you want to use dd-wrt to get native IPv6 internet access over PPPoE, here’s what you need to do:

  1. Make a note of all your current settings.
  2. Reset the Linksys firmware to factory settings.
  3. Install the trailed build (14471, mini).
  4. Do a hard reset.
  5. Install the non-trailed build (21676, STD USB NAS).
  6. Re-enter all your previous settings to get IPv4 internet access.
  7. Optional: Enable SSH and disable telnet.
  8. Enable IPv6 and Radvd, and enter the relevant commands for the Radvd config. (The bare minimum is just to advertise the prefixes on the relevant interface.)
  9. Add IPv6 addresses (unique local and global unique) and a default IPv6 route (via the ppp0 interface), then save these commands as a startup script.

It took me about 4 years to figure all of this out, so I hope that it can provide a useful shortcut to anyone else in the same situation!

3 thoughts on “Native IPv6 in dd-wrt”

    1. First off, thanks for posting the first real comment on this blog! (After hundreds of spam attempts…)

      I came across your blog while I was struggling with this issue. If I knew then what I know now, I never would have installed dd-wrt, and I certainly wouldn’t recommend it to anyone else. However, I was sufficiently stubborn that I wanted to identify exactly what the problem was before I gave up on it completely. I’ve now set up a separate Cisco router to handle internet access, and the dd-wrt box is just for my wireless network; I’ll probably replace that with a Cisco access point eventually.

  1. The problem with this article can be summed up with the words “I have native IPv6 connectivity from my ISP” Virtually nobody in the United States has this.

    What it is coming down to is that Comcast is using DHCPv6-PD & DHCPv6 and SLAAC and running it natively on their network internally while CenturyLink is using 6RD.

    When this post was put up the national ISPs in the US were still fiddling around trying to decide how to put out IPv6. Comcast just this year (2017) started offering static IPv6.

    Until the US got it’s act together, you simply didn’t see any real interest in IPv6 among the dd-wrt crowd because nobody in the US could really get it and the US is the largest Internet market.

    Today, dd-wrt has DHCP-PD support in the IPv6 Type field and at least until build 33555 (the latest in October 2017) IPv6 was working fine.

    The latest generation of AC routers has changed everything also. For example the Asus RT-AC66U has 128MB of flash and dd-wrt on it is only 18MB of flash.

    Your article was great for the pioneers and has great troubleshooting in it, but everything is much better in dd-wrt today. Of course, older gear is still limited in ram and flash and it will be a while to get past that but the newer gear is quite good now.

Leave a Reply

Your email address will not be published. Required fields are marked *