Using OpenWRT, connect to multiple OpenVPN instances and conditionally divert (split tunneling) one or more outgoing traffic to specific VPN route by destination host names or IP addresses.
If you connect to VPN from your computer, the VPN server usually pushes routes that makes your computer go through it for all outgoing connections. This is fine if this is what you really want. For most people, VPN is required only for visiting one or more websites. This computer networking concept is also known as split tunneling.
Taking it one step further, we may want to connect to multiple OpenVPN servers at the same time with different VPN server being used to serve specific outgoing traffic.
I have a working solution utilising OpenWRT and sharing the setup in this post.
You should be familiar with OpenWRT before attempting this as the complexity of this setup can be rather daunting.
The following are required:
openvpn-opensslfor establishing OpenVPN client connections
luci-app-openvpnfor GUI in LuCI which can be handy for starting/stopping VPN connections but I dislike using it for configuring
dnsmasq-fullfor IPset tagging so that we can route by host names, not just IP addresses
luci-app-mwan3for creating multiple virtual WAN adapters, one for each OpenVPN connection
Install the required packages using the following commands (or LuCI GUI if you prefer):
opkg update opkg install openvpn-easyrsa opkg install openvpn-openssl opkg install luci-app-openvpn opkg install dnsmasq-full opkg install mwan3 opkg install luci-app-mwan3
A gateway metric must be assigned for the default WAN, edit
config interface 'wan' ... option metric '10' ...
The metric of this must be smaller than metrics assigned to VPN connections configured later below.
When a DNS lookup is performed and that the host name matches specific names that we have defined, it will be tagged with an IPset. A load balancer can then be configured to route specific IPset tags to go through specific VPN route.
Even if you already have this configured, please read this section especially routing related details.
Below is an example of OpenVPN client configuration for PIA, I have setup two client instances. Edit
config openvpn 'usvpn' option nobind '1' option float '1' option client '1' option reneg_sec '0' option verb '1' option persist_tun '1' option persist_key '1' option remote_cert_tls 'server' option dev 'tun-usvpn' option proto 'udp' option crl_verify '/etc/openvpn/crl.rsa.2048.pem' option ca '/etc/openvpn/ca.rsa.2048.crt' option tls_client '1' option resolv_retry 'infinite' option auth 'SHA1' option auth_user_pass '/etc/openvpn/privateinternetaccess-userpass.txt' option route_metric '20' option route_nopull '1' option route '0.0.0.0 0.0.0.0 vpn_gateway 20' option remote 'xxx.privateinternetaccess.com' option enabled '1' option port '1198' option cipher 'AES-128-CBC' option comp_lzo 'yes' config openvpn 'ukvpn' option nobind '1' option float '1' option client '1' option reneg_sec '0' option verb '1' option persist_tun '1' option persist_key '1' option remote_cert_tls 'server' option dev 'tun-ukvpn' option proto 'udp' option crl_verify '/etc/openvpn/crl.rsa.2048.pem' option ca '/etc/openvpn/ca.rsa.2048.crt' option tls_client '1' option resolv_retry 'infinite' option auth 'SHA1' option auth_user_pass '/etc/openvpn/privateinternetaccess-userpass.txt' option route_metric '21' option route_nopull '1' option route '0.0.0.0 0.0.0.0 vpn_gateway 21' option remote 'xxx.privateinternetaccess.com' option enabled '1' option port '1198' option cipher 'AES-128-CBC' option comp_lzo 'yes'
Several configuration details that you must note:
/etc/openvpn/privateinternetaccess-userpass.txtwhere the first line contains the username and the second line contains the password.
route_metricmust not be zero and they must be unique for each OpenVPN instance. I suggest giving a number from 20 onwards.
'1'as we do not want the server to tell us what routes to use.
routemust then be set to
'0.0.0.0 0.0.0.0 vpn_gateway [metric]'where
[metric]must match the value set for
remotemust be set to servers of your choice.
After connecting to an OpenVPN server, the VPN network will have a gateway that you will be sending traffic to. This gateway is usually in the IP of
10.x.y.z. It is very important that multiple concurrent VPN networks do not share the same gateway IP subnet.
This is where OpenVPN LuCI GUI comes in handy. Start all instances of the OpenVPN clients.
For command line geeks:
Check the System Log to verify that VPN connections established successfully. Otherwise some errors will show up.
/etc/config/network. Add the following:
config interface 'usvpn' option proto 'none' option ifname 'tun-usvpn' option delegate '0' config interface 'ukvpn' option proto 'none' option ifname 'tun-ukvpn' option delegate '0'
/etc/config/firewall. Add the following:
config zone option input 'ACCEPT' option forward 'REJECT' option output 'ACCEPT' option name 'usvpn' option masq '1' option network 'usvpn' config zone option input 'ACCEPT' option forward 'REJECT' option output 'ACCEPT' option name 'ukvpn' option masq '1' option network 'ukvpn' config forwarding option dest 'usvpn' option src 'lan' config forwarding option dest 'ukvpn' option src 'lan'
For this purpose, we need to utilise
mwan3 load balancer as installed earlier. Edit
/etc/config/mwan3 and delete all lines then add the following:
config rule 'default_rule' option dest_ip '0.0.0.0/0' option proto 'all' option sticky '0' option use_policy 'wan_only' config policy 'wan_only' option last_resort 'unreachable' list use_member 'wan1' config policy 'usvpn_only' list use_member 'usvpn1' option last_resort 'unreachable' config policy 'ukvpn_only' list use_member 'ukvpn1' option last_resort 'unreachable' config member 'wan1' option interface 'wan' config member 'usvpn1' option interface 'usvpn' config member 'ukvpn1' option interface 'ukvpn' config interface 'wan' option enabled '1' option count '1' option timeout '2' option interval '5' option down '3' option up '8' option reliability '1' config interface 'usvpn' option enabled '1' option reliability '1' option count '1' option timeout '2' option interval '5' option down '3' option up '3' config interface 'ukvpn' option enabled '1' option reliability '1' option count '1' option timeout '2' option interval '5' option down '3' option up '3'
This serves as a base template having 1 WAN and 2 VPN interfaces. Redundant routes can be configured, i.e. load balancing between two or more OpenVPN routes but I will not be demonstrating that.
Go to MWAN Rule Configuration in LuCI. You should see one rule defined there named
default_rule. Rules placed below this will never be parsed. Useful for disabling rules without deleting them. Rules placed above will be parsed top-down.
mwan3 and ensure internet still works as usual:
I recommend using MWAN LuCI GUI for configuring. If you prefer editing the rules on file directly, it is at
Note that rule name must not be longer than 15 characters. Rules are matched top-down. Rule processing stops after the first match is found.
Usage: When you want to enforce all-time VPN for a particular LAN machine.
config rule 'machine1_usvpn' option src_ip '192.168.x.x/32' option proto 'all' option sticky '0' option use_policy 'usvpn_only'
As added protection, you should add a firewall rule to deny this particular machine by MAC address from ever going out via direct WAN. Edit
/etc/config/firewall and add the following:
config rule option src 'lan' option dest 'wan' option name 'Deny-machine1-WAN' option proto 'all' option src_mac 'xx:xx:xx:xx:xx:xx' option target 'REJECT'
Usage: When you know the destination IP and that never changes such as DNS server. An Anycast DNS server responds to lookup request with IP address of a server closest to it so performing lookup on a host name via default WAN or via a VPN may yield different results.
Do not use this on IP addresses that may change! There are better ways to handle that.
config rule 'pia_dns1' option dest_ip '126.96.36.199/32' option dest_port '53' option proto 'udp' option sticky '0' option use_policy 'usvpn_only'
Usage: When the domain name is known but the IP address may change over time. You are likely to use this a lot.
Firstly, ensure that your VPN provider is not blocked by the service; setup a direct VPN to verify this.
After verifying, we can now proceed to setting up the IPset rules. Starting with something simple, I will detail how to setup for Pandora Internet Radio.
/etc/dnsmasq.conf, add the following line:
This tags lookups made for pandora.com and its subdomains with
Note: You need to install
dnsmasq-full. The standard
dnsmasq package will not start with the presence of
ipset line. This also means you must have the pre-requisite packages installed before restoring backup (if you ever need to).
/etc/config/dhcp, add the following line above the entry where your preferred DNS servers are set. Example:
config dnsmasq ... list server '/pandora.com/188.8.131.52' list server '184.108.40.206'
In the example above,
220.127.116.11 is Google’s DNS server.
18.104.22.168 is Norton ConnectSafe DNS Server. What this setup means is if we are performing a lookup on pandora.com (including its subdomains), the lookup will be performed by Norton’s while other domains will be resolved using Google’s.
In the previous section, I have detailed how to send traffic to VPN by a specific IP address and destination port number. You will likely need to setup that as a pre-requisite for this technique to work as an AnyCast DNS server will likely provide you an IP address closer to you instead of your VPN server.
mwan3 with the following rule to send all traffic with IPset of
usvpn via the
config rule 'ipset_usvpn' option proto 'all' option sticky '0' option ipset 'usvpn' option use_policy 'usvpn_only'
Usage: When a website links to third party domains for content.
Firstly, we need to use tools to help us capture all domain names that are implicitly contacted when we browse a website; when we browse a website, it may then be loading content from server 1, server 2, etc. The list of these servers must be identified for this IPset method to work.
Using Chrome, navigate to chrome://net-internals/#dns. Clear the DNS cache. Some plugins are continuously pinging servers in the background. These should be disabled temporarily to prevent interference.
Navigate to the website using Chrome incognito mode and the list of DNS lookups made will begin to show up. You now need to setup IPset rules for these domains.
Try restarting mwan3,
/usr/sbin/mwan3 restart. If this fails to restore connectivity, reboot the router.