A smart VPN gateway

My network setup at home is surprisingly pretty common: a DSL modem (VDSL2 actually) followed by a router featuring an ethernet switch and an 802.11n wifi access point, configured as a NAT gateway.

My home network setup before modifications
My home network setup. Nothing fancy here.

Let’s imagine I’m in a country that doesn’t care about the right to private life of its citizens and performs automated mass surveillance, on the pretext of fighting against terrorism or copyright infringement. A gloomy perspective for sure, but let’s keep that as our work hypothesis.

Of course, I could just set up on every computer a VPN whose gateway happens to be in a foreign and more respectful country. However, multiple VPNs on multiple computers are a highly impractical setup for various reasons:

  • VPN configuration has to be done multiple times, and I’m allergic to repetitive tasks
  • The maximum number of concurrent connections is restricted by VPN service providers
  • Access to resources on a local network at the same time is a hassle and need specific configuration, like DNS settings

So, why not install the VPN once and for all in a privacy-enhancing gateway? We will implement it in a clean, IPv6-compatible manner so we even have public addresses working on the hosts.

Our goal is to get a smart gateway behaving as follows:

  • Outgoing connections are routed through the VPN. We have to use Network Address Translation since the VPN provider only attributed us one IPv4 address and one IPv6 address.
  • Incoming connections are routed normally to the LAN, so we get IPv4 port forwarding and working IPv6 public addresses.
Packet streams inside the smart gateway
Routing inside the gateway: outgoing connections through VPN


We will use OpenVPN, so any serious VPN provider supporting the corresponding protocol and IPv6 will work. I recommend Mullvad, since they accept Bitcoin payment, which is rather handy, but a lot of other services are good too. As a side note, using an IPv4-only VPN client on an IPv6-enabled network without tampering with the settings will lead to IPv6 leakage, completely disclosing a part of your traffic.

Remember this is just a technical example, follow it at your own risk. I do not encourage illegal activities and I am not in any way liable to any damages caused by a setup inspired from this article.

First, let’s use the ethernet router as a simple switch and put a Linux box with two ethernet interfaces – simply use a USB ethernet dongle if you have only one – between this ethernet switch and the LAN.

My home network setup
My home network setup with the gateway in place

There is a trick here: the switch is actually a router with no WAN network. It runs dnsmasq so it acts as a DHCPv4 and a DNS server on the LAN, but the IPv4 gateway it advertises is not itself anymore but the actual new gateway. We could install dnsmasq on the new gateway, but this scheme allows for a simpler configuration, and DNS requests will automatically be redirected through the VPN.

Mine is flashed with OpenWRT, here are the corresponding configuration sections.

I assume here you get IPv4 addressing from your ISP with DHCPv4 and IPv6 addressing on a /64 through IPv6 Stateless Address Autoconfiguration (SLAAC). From experience it seems to be a pretty common setup nowadays, if your ISP is not one of those blatantly incompetent ones that have not even deployed IPv6 yet.

First step, set up the VPN service on the gateway. Follow the instructions given by the VPN provider, but basically you should get a config file, e.g. vpn.conf, along with certificates and a private key to put in /etc/openvpn/. Be sure to configure UDP as transport protocol for OpenVPN, TCP over TCP is an ugly thing.

Second step, configure the gateway. The preliminary requirement is of course setting the interfaces. My box runs Debian GNU/Linux, so I use the Debian-specific network configuration file, but you can adapt it easily to any Linux distribution. Note I have chosen static IPv6 configuration, but you could get away with SLAAC on eth1.

Apply the new network configuration:

# service networking restart

Now, we have to enable IPv4 and IPv6 forwarding, set Reverse Path Filter to loose mode so it doesn’t mess with our setup, and enable Neighbour Discovery Protocol Proxy for IPv6. To do so, edit /etc/sysctl.conf

Now load these new settings:

# sysctl -p

In order to enable SLAAC for hosts on the LAN, we need to install and configure radvd on the gateway’s LAN interface to broadcast the IPv6 public prefix. After installing the program, add the following in /etc/radvd.conf, where 2001:X:X:X::/64 is your IPv6 prefix.

# service radvd start

Then, we set up iptables and ip6tables to get a standard NAT configuration between LAN and WAN. We add a couple lines to the mangle table to save and restore packet marks so every packet on a single connection has the same mark, I will explain why at the next step. Note that with this configuration, IPv4 Internet access should work normally.

# chmod +x ./iptables.sh
# ./iptables.sh

We will need a specific routing table for traffic destined to go through the VPN, and we have to register it before continuing:

# echo "10 vpn" >> /etc/iproute2/rt_tables

Next, we add a VPN startup script /etc/openvpn/vpn_up.sh to start routing outgoing connections through the VPN as soon as it is connected.

Don’t forget to make it executable:

# chmod +x /etc/openvpn/vpn_up.sh

And declare it in the vpn.conf config file:

Now start openvpn (Don’t forget to edit /etc/default/openvpn first on Debian), and let the magic happen…

# service openvpn start

It works!
It works! Note outgoing connections use IPv4 and IPv6 adresses in Sweden. Yay for the winner of Eurovision 2015 :P

But there is a catch: incoming IPv6 connections do not get through to the LAN… Because we forgot to proxy IPv6 neighbours! What we would like to use is something like ip -6 neigh add proxy 2001:X:X:X::/64 dev eth1 to have the kernel answer neighbour requests coming from the ISP’s gateway for hosts on the LAN. Sadly, it doesn’t seem to exist, you can proxy a unique host but not an address range.

Yet, we can hack around this irritating issue with a script to do the job. It works, but it might not be the best practice. If a network guru comes around and has a better way… feel free to comment!

For now, let’s write a custom /root/proxyprefix.sh

# chmod +x /root/proxyprefix.sh

It has to be run at regular intervals, one way to do so is to add it to the crontab with crontab -e

It should work perfectly and make the hosts on the LAN reachable using IPv6. If you want, you can add some firewall rules for incoming connections (on the FORWARD chain).

For IPv4, forwarding a port is done the normal way with DNAT:

# iptables -t nat -A PREROUTING -i eth1 -p tcp --dport $MYPORT -j DNAT --to-destination $MYHOST

Once everything is working, don’t forget to set the firewall and NAT configuration to load at startup. A cleaner way to do it would be to use iptables-save and iptables-restore.

# mv ./iptables.sh /etc/network/if-pre-up.d/iptables

How about a nice cup of tea to celebrate?

Leave a Reply

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