Split Routing with OpenVPN

My place of work has installed a VPN that moderates our access to the server network using the OpenVPN protocol. This is a good thing, but in its default configuration it would send all traffic — even that not destined for the machine room network — through the VPN. Since most of what I do doesn’t involve servers in the machine room, I wanted to change the configuration of the OpenVPN client to only send the machine room traffic through the VPN and everything else through the (original) default gateway. As it turns out, this involves tweaking the routing tables.

In its default configuration, the OpenVPN client establishes a default route pointing to the OpenVPN server as the gateway. What I needed to do is remove that default route to the OpenVPN server gateway, recreate the original default route to the underlying interface’s gateway, and add a new specific route for the machine room network using the OpenVPN server gateway. These additions to the “ovpn” file were:

route-delay 2
route-up "/some/location/openvpn-default-route-reset.sh"
route vpn_gateway

The “route-delay” line forces the two subsequent changes to happen after all of the OpenVPN-driven routing changes are made. The “route-up” line runs a shell script that deletes the OpenVPN-supplied default route and adds the one pointing back to the underlying interface’s gateway. (More on this shell script below.) The “route” line adds the machine room specific network through the OpenVPN tunnel. 1 (The “vpn_gateway” is a keyword in the configuration file that is replaced by the gateway address of the OpenVPN tunnel at runtime.)

For reasons I don’t understand, I couldn’t delete and re-add the default route via the OpenVPN configuration file. Instead, I needed to create an external shell script with those commands and execute that script via the “route-up” configuration line. The contents of the shell script are really simple:

/sbin/route delete default
/sbin/route add default $route_net_gateway

OpenVPN will push a bunch of environment variables in to the subprocess, and one of them is $route_net_gateway that gets the “pre-existing default IP gateway in the system routing table.” That value is used in the third line to reset the default gateway. This script gets run as root, so perform due diligence to protect the script (chmod it to “700″ and chown it to “root”).

With that in place, my network routing tables look something like this:

Destination        Gateway            Flags        Refs      Use   Netif Expire
default          UGSc           40        0     en1         UGSc            0        0    tun0        UH              4        1    tun0
127                UCS             0        0     lo0          UH             12 15704029     lo0
192.168.2          link#5             UCS             2        0     en1        0:c0:49:ff:8c:c5   UHLWI          42      108     en1   1158          UHS             0        9     lo0
193.20.135         UGSc            2        0    tun0        UGSc            1        0     en1


  1. Note, this really isn’t the machine room network of my place of work. []