Setup IPv6 NAT on OpenWrt Router

Introduction

This tutorial is a HowTo for setting up IPv6 NAT (NAT66) on an OpenWrt router.

Why would anyone want NAT on IPv6, when the whole point of the protocol was to give every device a globally routable address? In my case the campus network (China Education and Research Network, CERNET) authenticates per device and only assigns an IPv6 address to a device that has logged in. Every other device behind the router is left without connectivity. NAT66 works around this: the router authenticates once, then masquerades all LAN traffic behind that single authenticated IPv6 address, so every device on the network reaches the IPv6 Internet through it.

This is deliberately a workaround. On a normal network you should let the upstream delegate a prefix (DHCPv6-PD) and route it rather than NAT it. NAT66 only makes sense when the upstream refuses to hand out a usable prefix, as in this captive, per-device-authenticated setup.

The environment used here:

Network Environment: China Education and Research Network (CERNET), dual-stack IPv6

Network Device: NETGEAR R6100 (128 MB RAM)

Firmware Version: OpenWrt Chaos Calmer 15.05

Note: This was done on OpenWrt Chaos Calmer 15.05. In newer OpenWrt releases the network.wan6.ifname option was renamed to network.wan6.device, so adjust the commands below if you are on a recent build.

Install Dependencies for OpenWrt

Log in to the OpenWrt router via SSH and run the following commands:

opkg update
opkg install ip6tables
opkg install kmod-ipt-nat6
opkg install iputils-tracepath6

kmod-ipt-nat6 is the kernel module that enables IPv6 NAT (it is not built into the stock firmware), ip6tables lets us add the masquerading rule, and iputils-tracepath6 is used later to discover the upstream IPv6 gateway.

Setup IPv6 for Local-area Network

Because we are not routing a public prefix, the LAN gets a private ULA address from the fc00::/7 range. Edit /etc/config/network and add the following line to config interface 'lan':

option ip6addr 'fc00:192:168:113::1/64'

Remove the following line if it exists, so it does not conflict with the static address above:

option ula_prefix [IPv6 Address here]

Next, edit /etc/config/dhcp and change config dhcp 'lan' to hand out IPv6 addresses and send router advertisements to LAN clients:

config dhcp 'lan'
    option interface 'lan'
    option start '100'
    option limit '150'
    option leasetime '12h'
    option dhcpv6 'server'
    option ra 'server'
    option ra_management '1'
    option ra_default '1'
    option ndp 'hybrid'

Here dhcpv6 'server' and ra 'server' make the router advertise itself and assign addresses, while ndp 'hybrid' proxies neighbor discovery between the LAN and WAN sides.

Setup Gateway for IPv6 Network

This is the core of the setup. Add the following line to /etc/firewall.user so the router masquerades outgoing IPv6 traffic behind its WAN6 address:

ip6tables -t nat -A POSTROUTING -o $(uci -q get network.wan6.ifname) -j MASQUERADE

The router still needs a default IPv6 route pointing at the upstream gateway. On CERNET that gateway is not advertised in a way OpenWrt picks up automatically, so we discover the first hop toward a known-reachable IPv6 host and add the route by hand. Create /etc/hotplug.d/iface/90-ipv6 with the following content so the route is re-added every time the WAN6 interface comes up:

#!/bin/sh

[ "$ACTION" = ifup ] || exit 0

[ "$INTERFACE" = wan6 ] && {
    route -A inet6 add ::/0 gw $(tracepath6 -n tv.byr.cn | grep ' 1: ' | awk 'NR==1 {print $2}') dev $(uci -q get network.wan6.ifname)
}

tv.byr.cn is just an IPv6 host that is reachable inside CERNET; tracepath6 reports the first hop toward it, which is our upstream gateway. On a different network, replace it with any IPv6 address you can reach upstream.

Then make the script executable:

chmod 755 /etc/hotplug.d/iface/90-ipv6

Complete Setup

Finish by restarting the router. Your computers on the LAN should now pick up an IPv6 address automatically and reach the IPv6 Internet through the router.

Enjoy!

References