linuxiproutevpnIts a seemingly harmless task: You have multiple internet connections (e.g., your default DSL line and a VPN connection), and you want to route some of your traffic over the VPN (e.g., to access contents blocked in your country, or to access the company LAN) but not all traffic (e.g., because the VPN is slower than the default connection). In particular, you want to redirect traffic by its target domain, i.e., you want to specify a list of domains that should be accessed over the VPN. As it turns out, this is actually non-trivial.

Here I present one solution for this problem. Probably there are others, but I have not found this particular solution documented online. Also, I write this blog post as a reminder to myself :-)

My setup is a DSL router at 192.168.2.1, a gateway for the rest of the LAN at 192.168.2.5, and some other computers using this gateway for their internet conneciton.

My solution is based on three mechanisms:

First, there is a cronjob that takes a list of domains as input, queries the DNS server for the IP addresses of these domains, and adds these addresses to a so-called ipset

Second, iptables is told to mark any traffic to any of the IP addresses in this ipset with a specific identifier.

Third, using rule-based routing, any traffic with that identifier is routed through the VPN.

Now for the three components:

1. Sets of IP addresses

I created two sets of IP addresses with the following commands: ipset create vpnlist_usa hash:ip hashsize 4096
ipset create vpnlist_usa_tmp hash:ip hashsize 4096
Why two sets? This way, the cronjob can work with the "_tmp" list and only when it was successful, the two lists are swapped.

It takes the names of config files as input, which have to be in the following format:

HOSTS=("netflix.com""www.netflix.com")LIST=vpnlist_usa

This cron job is called every five minutes.

3. Rule-based Routing

First, a new routing table is created by adding the following entry to /etc/iproute2/rt_tables: 400 vpn_usa_redir
This creates a new routing table with the name vpn_usa_redir. Now we have to tell the system when to use it: ip rule add from all fwmark 120 lookup vpn_usa_redir
And finally, we have to give it some contents: ip r add default dev tun0 src 192.168.20.2 table vpn_usa_redir
Where tun0 is the VPN device and 192.168.20.2 is the VPN local IP address.

First, if multiple domains are hosted on one IP address, all these domains will be redirected over the VPN. In my case, I'm fine with that. Second, if the cronjob gets a different DNS response than the computer that actually accesses the domain, this solution doesn't work. To keep the probability of that happening low, I set up the same DNS server for both machines.