IPv6 NAT with OpenWrt router

It is REAL IPv6 NAT supported by netfilter inside the Linux kernel. Tested devices include TL-WR740N v3 and DIR-505 using the latest OpenWRT stable release Barrier Breaker. However, there are some trick and catch.

Go to the following official wiki page

However, it has been quite a sarcasm that a bro named trevorj removed the very link back to this post in the Credits section. Good Job!

Came across this post ( in Chinese ) and led to netfilter’s official site, as it does write

  • all kinds of network address and port translation, e.g. NAT/NAPT (IPv4 and IPv6).

Barrier Breaker uses a newer Linux kernel and there is indeed a package named kmod-ipt-nat6 in the repository.

First of all, install kmod-ipt-nat6 and then do some tweaks

  1. Login to LuCI of OpenWrt via web browser
  2. Go to Network >> Interface
  3. Change IPv6 ULA-Prefix’s first letter from f -> d
  4. Click into LAN’s configuration page
  5. In the IPv6 Settings section
    • enable Router Advertisement-Service
    • disable DHCPv6-Service and NDP-Proxy
    • check Always announce default router

A script is wrote to enable IPv6 NAT after each boot

Do not forget to make it executable

Then add a line in /etc/rc.local before exit 0

Additionally, if upstream can allow temporary addresses, append the following lines to /root/nat6.sh

The script above is an example only suitable when WAN interface can obtain a native IPv6 address. If IPv6 is setup via ISATAP etc, please pay attention to route directive in the script this method does not work out seemingly.

After all done, a reboot is required.


  • The trick is that downstreams are assigned non-local addresses in compliance with /etc/gai.conf to prefer connections via ipv6, which is actually a cheat. Since it’s non-local, it may cause confliction but currently only 2000::/3 is globally used.
  • The catch is that it gets all pros and cons that NAT has.
Oct 28, 2017 @ 00:32

16 thoughts on “IPv6 NAT with OpenWrt router”

  1. Hi,

    thanks a lot for this! It helped me solve my issues of using an OpenWrt router behind another router that doesn’t support prefix delegation.

    I had one issue with your recipe, though:
    You recommend to disable DHCPv6. I did that first, and while it works for most of my client devices, others would still prefer the IPv4 route instead. E.g. my Android (5.1) smartphone only prefers IPv6 if DHCPv6 is also enabled (server mode).

    I posted (and linked) your solution with my changes on the OpenWrt forum:

    Another change I made was to the sleep time. I found that on my device, a delay of 5 seconds is enough (meaning IPv6 is up after that time and the while loop only executes once). But that certainly depends on your hardware and other software you may run on your router.



    1. Hi,

      Really glad that it works. I also read through your forum posts. It has been expanded in such detail that I believe it is of great help regarding this method.

      As for DHCPv6 issue, I guess that it is probably due to the lacking of SLAAC support and enabling server mode does solve this.

      For the while loop, I set the sleep time a bit longer because just in case that the router fails to obtain IPv6 address it will not eat much CPU when looping.



      1. About the while loop: Meanwhile I actually changed that a bit further in order to not loop infinitely. The way I have it now, the while loop exits the script if no IPv6 route is found within two minutes.

        It may be a bit hypothetical, but I thought it’s better just in case there’s a problem with my ISP or gateway that the OpenWrt router would not keep trying for hours to set up IPv6 NAT.

  2. Please include # chmod a+x /root/nat6.sh !
    I don’t know whether it’s the reason that after those steps my downstream device didn’t work, because I didn’t remember whether I did the chmod command that day. But it really works now with chmod. What’s more, I didn’t change the IPv6 ULA-Prefix.
    And thank you for your work.

      1. So sorry.
        I memorised, not clearly, that I might input the command chmod the first time because of Timo’s post https://forum.openwrt.org/viewtopic.php?id=60334. Then I came across this post ( in Chinese ) http://blog.csdn.net/cod1ng/article/details/45421025, which says enabling DHCPv6, and I successfully got responses by doing ping6 google.com, but the boot script he gave didn’t work and I found here again. Followed the steps you gave again and it works like a charm.
        It seems that the reason the first time it didn’t work may be DHCPv6 was disabled, though the second time I disabled it. It might have been effected by the configuration on DHCPv6. It just my supposition I haven’t tested.
        And thank you for your great work again.

  3. Hi!

    Thanks a lot.It works!
    But one thing I can’t understand is this:

    route -A inet6 add 2000::/3 route -A inet6 | grep ::/0 | awk 'NR==1{print"gw "$2" dev "$7}'

    Without executing this command, NAT not works. Pinging from downstream replies “Destination unreachable: Unknown code 5”. Just Print route on Openwrt router, Destination named “::/0” is exists. But after executing this, NAT works. I am confusing about that why Next Hop of “::/0” and “2000::/3” is same, but without rule of “2000::/3”, it not work. I can’t understand why route of “::/0” not works. If you try to use ping6 on route before adding this route rule, It’s OK. But without this, Pinging from downstream fail. Why?

    1. Hi,

      Actually I do not have solid explanation on this. Once I read linux.sh in package gogoc, it adds both ::/0 and 2000::/3.

      From IANA, it currently limits the IPv6 unicast address to the range of 2000::/3.


  4. It is sad to see so much effort in applying NAT to IPv6. There are _so_ many IPv6 addresses this shouldn’t be necessary, even if you have an upstream router than doesn’t support DHCPv6-PD.

    There is a simple protocol that can make this work, called bridging. Turning on bridging on your OpenWRT, would allow you to use the IPv6 address space of the upstream router, and eliminate all the problems that NAT entails.

    For more info on how you would do IPv6 bridging, see: https://github.com/cvmiller/v6brouter

    1. You are right and there is actually a post here I wrote before using bridge method for earlier version of OpenWRT.

      However, later version of OpenWRT no longer supports table broute in ebtables in its kernel by default so that NAT is thus brought out again.

      1. Actually broute support is in the Chaos Calmer (v15.05) kernel. I got it working yesterday, and have posted an OpenWRT specific version of a v6broute script to github.

        I wish I had seen your brouter article earlier, as I think it is the right way to “extend” IPv6 coverage when DHCPv6-PD support is not available.

        1. I confirmed as just I tested broute support in Chaos Calmer. I will update that old post regarding this information.

          Also cheers for your script specific for OpenWRT.

  5. The script doesn’t work with PPPoE dial case.

    For PPPoE, uci will return eth0.2 or whatever the physical interface.

    Not familiar with uci, so no good idea to fix it though, but manually specifying the interface to ‘pppoe-wan’ is good enough for me though.

    1. Sorry, my fault, got confused with the script in Openwrt Wiki.
      And since it’s getting default gateway interface, it will work with PPPoE.

      While I don’t understand why the last gateway command is needed.
      I tried without the last 2000::/3 route, but then Openwrt will fail to forward my icmpv6 packet.

      But since there is already a ::/0 route from dhcpv6, why it doesn’t work?


      1. Hi Adam,

        As far as I can explain, the more specific route allows to explicitly direct packets to given interface.

        You might also refer to this.


Leave a Reply

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

Please calculate * Time limit is exhausted. Please reload CAPTCHA.