I’m participating in a beta test with my ISP for IPv6 connectivity. The ISP deployed a basic IPv6 service that relies exclusively on SLAAC for address assignment. I can connect my end stations to a gigabit switch with direct connectivity to the provider or insert a router in a path. Without receiving a /64 for my office network via DHCPv6 prefix discovery, accessing the IPv6 Internet behind a router requires NAT66. In this post, I’ll describe my experience with NAT66 and point out a strange issue I encountered with IPv6 forwarding in Linux.
Terry Moës implemented stateless and stateful SNAT/DNAT for his thesis. He produced working code for Linux and an interesting paper on his work. His sourceforge page for the project includes:
- iptables extensions
- two NAT66 kernel modules
- modified netfilter and ipv6 header files for the kernel
Here is a logical diagram of the test environment. Stateful NAT66 is implemented on the debian router.
The debian router and end system are VMs. Incidentally, the end system is a vyatta 6.2 VM, which is great as a router or end system in virtualized proof-of-concept testing. The debian router connects directly to the Internet by bridging my ethernet interface on my VM host.
The most challenging aspect of creating the test bed was preparing and configuring the debian router. Kernel compilation is much more difficult in these days of package managers than it was in the late 1990s when I was building customized kernels. With Terry’s assistance, I was able to compile the kernel, modules, and iptables.
I struggled to figure out why the debian router wouldn’t use SLAAC to configure its external interface. I knew that enabling forwarding would result in the OS ignoring Router Advertisements (RAs). I tried tweaking the IPv6 variables in the /proc filesystem. No luck. Fortunately, I stumbled upon a blog post called Linux, IPv6, Router Advertisements, and Forwarding.
The author of the post highlights two methods for accepting RAs when forwarding is enabled.
- Kernel 2.6.37 and later – Set the “boolean” variable accept_ra to 2 (is this geek humor?) on the interface.
- Kernel 2.6.32 (current debian stable kernel) – Turn off forwarding on the interface that you want accept RAs. As long as forwarding is enabled globally, the system will route packets.
Soon after I made the change, I successfully obtained an IP address on the external debian router interface. I used the following ip6tables rules for stateful SNAT.
ip6tables -t nat66 -A POSTROUTING -o $OUTSIDE -j SNAT66 --to-range $EXTERNALIP
ip6tables -A FORWARD -i $OUTSIDE -o $INSIDE -m state --state RELATED,ESTABLISHED -j ACCEPT
ip6tables -A FORWARD -i INSIDE -o $OUTSIDE -j ACCEPT
I was ready for the big test: I pinged 2600:: from the end system. Success!
For NAT66 to be useful in my office, I’d have to port it to my Soekris 4501 or Linksys router. Cross-compiling in itself would take more time than I can invest. There is a subset of end hosts in my office that I can expose to the Internet after hardening the hosts. For my office to be completely IPv6-enabled, I’ll have to wait for my provider to implement DHCPv6 PD.
End-to-end principle be damned! I want to jam the NAT kludge through to the IPv6 Internet. I kid, of course. I encourage readers who haven’t gotten their hands dirty with IPv6 in Linux to pursue a small project. Whether it’s Terry Moës’s NAT66, Ecdysis NAT64, or something else, satisfy your network engineer curiosity. You’ll increase your IPv6 knowledge in the process.