User Tools

Site Tools


"Split" VPN routing with OpenWRT/Tomato

This is an explanation of how I route traffic for specific LAN IP addresses (my NAS) through an OpenVPN connection on a router running OpenWRT/Tomato firmware. I'm also running “tinyproxy” on the NAS so other clients can use the VPN connection on the router as necessary.

I've never used awk before and didn't devote a lot of time to these scripts, so save your judgement please.

Pre-requisite: Optware

Get optware installed and mounting automatically on boot.

Set your USB support with the option Automatically mount all partitions to sub-directories in /mnt.

Set the USB Run after mounting script:

if [ -d /mnt/optware ]; then
  mount -o bind /mnt/optware /opt

Contents of Administration→Scripts→Init: (I don't use this anymore but figured I'd leave it documented)

#Mount optware
echo "LABEL=optware /opt ext3 defaults 1 1" >> /etc/fstab
/bin/mount /opt /opt

These are the packages I have installed:

ipkg-opt - 0.99.163-10 - The Itsy Package Manager
libcurl - 7.24.0-1 - Curl is a command line tool for transferring files with URL syntax, supporting FTP, FTPS, HTTP, HTTPS, GOPHER, TELNET, DICT, FI
libidn - 1.25-1 - GNU Libidn is an implementation of the Stringprep, Punycode and IDNA specifications defined by the IETF Internationalized Domai
netcat - 1.10pl32-5 - TCP/IP swiss army knife.
openssl - 0.9.7m-6 - Openssl provides the ssl implementation in libraries libcrypto and libssl, and is needed by many other applications and librari
uclibc-opt - 0.9.28-13 - micro C library for embedded Linux systems
wget-ssl - 1.12-2 - A network utility to retrieve files from the Web
zlib - 1.2.5-1 - zlib is a library implementing the 'deflate' compression system.

OpenVPN settings

  • Ensure the option “Create NAT on tunnel” is checked
  • Advanced settings:
auth-user-pass /opt/etc/pia/pia.txt
verb 1
reneg-sec 0
route-up /opt/etc/scripts/ 
down /opt/etc/scripts/

Two files are necessary:

Contents of pia.txt is VPN username on first line, password on second line.

Contents of pia_client_id is a random string: head -n 100 /dev/urandom | md5 > pia_client_id

VPN Scripts

The script sets a second routing table with ip route to direct traffic for ip's listed in “vpndhosts” (space delimited) through the VPN connection. It then calls a script to get a forwarding port from my VPN provider, which then calls a script to update the transmission client config that's running on the NAS.

I don't remember why I needed to use my ISP's DNS servers over the VPN's DNS servers, but there must be a good reason. Maybe it was because I couldn't figure out how to dynamically update the /etc/resolv.conf file on my NAS. If you don't need to use your ISPs DNS, comment out the third for loop.

Contents of

privateinternetaccess=$(nslookup |grep ^A |tail -1 |awk -F ' ' '{ print $3 }')
tun_iface=$(ifconfig|grep tun|awk '{ print $1 }')
tun_inet=$(ifconfig $tun_iface|grep P-t-P|awk -F ':' '{ print $2 }'|awk -F ' ' '{ print $1 }')
tun_ptp=$(ifconfig $tun_iface|grep P-t-P|awk -F ':' '{ print $3 }'|awk -F ' ' '{ print $1 }')
tun_gw=$(echo $tun_inet |awk -F '.' '{print $1"."$2"."$3".1"}')
tun_net=$(echo $tun_inet |awk -F '.' '{print $1"."$2"."$3".0/24"}')
ip route add via $tun_ptp dev $tun_iface table 10
ip route add via $tun_ptp dev $tun_iface table 10
ip route add $tun_gw via $tun_ptp dev $tun_iface metric 1 table 10
ip rule add from $tun_net table 10
ip rule add to $tun_net table 10
for host in $privateinternetaccess;do ip rule add from $host table 10;ip rule add to $host table 10;done
for host in $vpndhosts;do ip rule add from $host table 10;done
for host in $vpndhosts;do for server in $dnsservers;do ip rule add from $host to $server lookup main;ip rule add from $server to $host lookup main;done;done

/opt/etc/scripts/ &

Contents of

for rule in $(ip rule list |grep -v "all lookup"|awk -F ":" '{ print $1 }');do ip rule delete pref $rule;done

Port-forward Script

This is specific to Private Internet Access's method for getting a port forwarding port via an HTTPS request. It uses iptables PREROUTING with the NAT table to forward the port to the internal LAN address. Once the port is retrieved, if there's no forwarding enabled currently, it gets enabled. If it's different from what is currently forwarded, it replaces what's currently in place. Once the ports are setup it calls the script to update the transmission config.

Contents of


#to-do: get current nat preroute rule and compare port, if it's the same do nothing, if it's different, delete the old and add the new

pia_user=$(head -1 /opt/etc/pia/pia.txt)
pia_pw=$(tail -1 /opt/etc/pia/pia.txt)
pia_client_id=$(head -1 /opt/etc/pia/pia_client_id)
tun_iface=$(ifconfig | grep tun | awk '{ print $1 }')
echo $tun_iface
tun_inet=$(ifconfig $tun_iface|grep P-t-P|awk -F ':' '{ print $2 }'|awk -F ' ' '{ print $1 }')
echo $tun_inet

#get our forwarding port from PIA
forwarded_port=$(/opt/bin/wget --post-data="user=$pia_user&pass=$pia_pw&client_id=$pia_client_id&local_ip=$tun_inet" \
	--no-check-certificate \
	-q -O - \
	| awk -F ':' '{ print $2 }'| awk -F '}' '{ print $1 }')

echo $forwarded_port

current_port=$(iptables -t nat -L PREROUTING -vn |grep tun11 |awk -F ':' '{ print $2 }'|awk -F ' ' '{ print $1 }'|tail -1)
current_rule=$(iptables -t nat -L PREROUTING -vn --line-numbers|grep tun11 |cut -b1-2)

if [ -z "$current_port" ] && [ -z "$current_rule" ]
#we have no existing port forward and no existing current rule in place
echo inside 1
iptables -t nat -I PREROUTING -p tcp -i tun11 --dport $forwarded_port -j DNAT --to $insidehost:$forwarded_port
if [ "$current_port" -ne "$forwarded_port" ]
#current port forward and port returned by website do not match
echo inside 2

#delet the current rule and set forwarding to our NAS host
iptables -t nat -D PREROUTING $current_rule
iptables -t nat -I PREROUTING -p tcp -i tun11 --dport $forwarded_port -j DNAT --to $insidehost:$forwarded_port
/opt/etc/scripts/ $current_port $forwarded_port
else echo current and requested ports match

Transmission Update Script

Transmission supports updating client configuration settings on the fly via a kill -HUP. Open file handles remain open, so if you're like me and you have a download directory that is used only temporarily while something other than transmission moves your files around, no problem.

In order to ssh to your NAS from OpenWRT, you need a set of ssh keys. On OpenWRT that command is dropbearkey -t rsa -f ~/.ssh/id_rsa. Copy the .pub key file to your nas and append that key to your authorized_keys file. As always, make sure your .ssh dir is set to 700 and authorized_keys set to 600.

Contents of

echo $1 to $2

ssh -i /opt/etc/pia/id_rsa root@$transmissionhost cp $settingsfile $settingsfile.bak                         
#ssh -i /opt/etc/pia/id_rsa root@$transmissionhost ls -l $settingsfile $settingsfile.bak
ssh -i /opt/etc/pia/id_rsa root@$transmissionhost 'cat '"'$settingsfile.bak'"' | sed -e s#\ \ \ \"peer-port\"\:\ [0-9][0-9][0-9][0-9][0-9],#\ \ \ \"peer-port\"\:\ '"'$2'"',#g > '"'$settingsfile'"''
ssh -i /opt/etc/pia/id_rsa root@$transmissionhost grep peer-port $settingsfile $settingsfile.bak
ssh -i /opt/etc/pia/id_rsa root@$transmissionhost killall -HUP transmission-daemon

Scheduled port forward update

My VPN provider requires that you routinely refresh the request for a forwarded port. That is scheduled in OpenWRT via Administration→Scheduler. Execute /opt/etc/scripts/ every 30 minutes to keep the port enabled.


unix/networking/openwrt_routing.txt · Last modified: 2015/11/17 07:10 by ben