Background
My home Internet service is a dual stack setup with both IPv4 and IPv6 support. My Asus router can operate as a OpenVPN server but the stock setup only supports IPv4. When used from a client on a another dual stack network or a IPv6 native network like T-Mobile in the United States you end up with IPv6 leaks. There are a number of websites where you can test your IPv4 connectivity. I used the first I stumbled on.
T-Mobile uses 464XLAT to support IPv4 and whatever they are doing allows the OpenVPN client on my Android phone to correctly connect and provide a tunnel. But only IPv4 traffic goes through the tunnel. All the IPv6 traffic goes directly through T-Mobile defeating my ad blocking and reducing my privacy.
The phone does not support a TAP (Ethernet type) interface, only a TUN (IP level) interface. So many of the OpenVPN examples showing a pretty easy bridge setup were not relevant.
I spent considerable time attempting to get the router to transport IPv6 in a tunnel established via IPv4 and failed.
The samples I found for setting up IPv6 in OpenVPN seemed to need a separate IPv6 prefix for to allocate to devices connecting in via the VPN. I do not have a second public address prefix to use for that. In my case my ISP gives my router a IPv6 public address and one /64 public prefix range to distribute via stateless allocation to devices on my LAN.
One hint was to use an arbitrary network prefix and use Network Address Translation (NAT) to access the world. Their example used public prefix which seems dodgy to me.
Reading up on IPv6, it seemed that a Unique Local Address (ULA) range should be used for this type of thing. It is generally recommended to use the interface MAC address as part of the seed to generate your unique address prefix but I found a nice tool to generate an address range for me.
So I tried various things but nothing was working for me. I did, however, come up with useful if broken configuration: The remote was semi-correctly setup to try to send all IPv6 data to the server but the server was not handling it properly. The result was no IPv6 traffic was being leaked. If your browser, etc. try IPv6 first you’ll likely have a very slow experience waiting for timeouts. If you want to get there, simply drop something like the following lines into the “Custom Configuration” area of the OpenVpn server advanced settings area on your Asus router:
server-ipv6 fd03:d0af:0965:9872::/64 push tun-ipv6 ifconfig-ipv6 fd03:d0af:0965:9872::/64 fd03:d0af:0965:9872::2 push "route-ipv6 2000::/3"
Where “fd03:d0af:0965:9872::” is some ULA value generated from the above linked website. We are going to NAT these so it shouldn’t matter too much if they really are unique or not.
Eventually I noticed that IPv6 NAT support came to Linux in kernel version 3.7 but even though newly purchased this year and running the latest available firmware, my router’s kernel was too old to support this.
PiVPN on a Raspberry Pi
A quick check on the brand new Raspberry Pi, originally intended for DNS based ad blocking, showed a much newer kernel, one that supports IPv6 NAT, so I decided to try my luck there.
First, pick a port you want your VPN clients to connect on. Default is UPD port 1194. I decided to keep my not quite working VPN on the router so I selected a non-standard port. On the Asus router you need to:
- Assure the Raspberry Pi has a static address. I do that on my Asus Router by setting IPv4 assignments based on MAC address in the LAN->DHCP Server area.
- Also on the Asus Router, in the WAN->Virtual Server/Port Forwarding area, forward your selected port to the Raspberry Pi.
Next, install PiVPN on the Raspberry Pi. I won’t go through all the details as there seems to be many nearly identical instruction sets and basically all you have to do is select the defaults on each question. I have a dynamic DNS setup, so I use that DNS name rather than an IP address for my server. You may wish to to that too.
Create at least one user and export the OVPN file to your device(s). I use OpenVPN for Android.
At this time you should have a working IPv4 VPN with a massive IPv6 traffic leak. Go ahead and test it.
Setting up IPv6
Fortunately, PiVPN leaves all the various configuration files in the standard places.
Step one, enable IPv6 forwarding:
echo 1 > /proc/sys/net/ipv6/conf/all/forwarding
In /etc/sysctl.conf uncomment the following line so that forwarding will survive a reboot:
net.ipv6.conf.all.forwarding=1
Step two, figure out what your Pi’s public IPv6 address is by using ifconfig. It will be the IPv6 address starting with a 2 or maybe 3 on eth0 (assuming you are hardwired).
Step three add IPv6 stuff to the OpenVPN server configuration: Add the following lines to the end of /etc/openvpn/server.conf
server-ipv6 fd03:d0af:0965:9872::/64 push tun-ipv6 ifconfig-ipv6 fd03:d0af:0965:9872::1 fd03:d0af:0965:9872::2 push "route-ipv6 public_ipv6_address_for_pi/64" push "route-ipv6 2000::/3"
Again where “fd03:d0af:0965:9872::” is a ULA prefix and “public_ipv6_address_for_pi” is the public IPv6 you determined in step two.
Step four, enable NAT on the interface again using your ULA prefix and your public IPv6 address:
sudo ip6tables -t nat -A POSTROUTING -s fd03:d0af:0965:9872::/64 -j SNAT --to public_ipv6_address_for_pi
Step five, restart your OpenVPN server:
sudo service openvpn restart
Try connecting a client to your server and see if the the IPv6 traffic and the IPv4 traffic both seem to be coming from your PiVPN sever.
If things go amiss, looking at the log might help:
tail -f /var/log/openvpn.log
Not Perfect
This setup is fragile in that my ISP sometimes changes my IPv6 /64 prefix when my router reboots. And I haven’t yet figured out where/how/when the PiVPN scripts may re-write /etc/openvpn/server.conf so a power failure is likely to break the setup.
Ideally, the PiVPN scripts should self detect the local IPv6 address and correctly setup the ip6tables and /etc/openvpn/server.conf file. In addition it would be nice if it generated the ULA for the VPN using the official RFC procedures with a MAC address on one of the Pi’s interfaces rather than relying on an arbitrary website.
For the moment I am leaving that as a future exercise. If this VPN breaks because of a IPv6 prefix change I can still use the VPN on my router.
Addendum
Making things easier, I now have a script that should take a tested, working on IPv4 PiVPN setup and add support for IPv6 in the tunnel. You can use it like this:
> scp pivpn_ipv6 pi@raspberrypi.local:/home/pi/ > ssh pi@raspberrypi.local pi@raspberrypi:~ $ ./pivpn_ipv6
If you are worried about your ISP changing the IPv6 prefix, I guess you could setup crontab to run this periodically.