Policy based routing for PPTP VPN client on DD-WRT router

ddwrt

This post is a change from my usual software programming related posts. I usually don’t write about networking related issues, but I struggled with this issue a bit recently so I thought I’d write about it.

In plain English, when I say policy based routing, I mean to accomplish the following: if you want a computer or device on your home or work network to go over a VPN connection, but you don’t want the rest of the computers or devices on your home or work network to use the VPN connection, you can accomplish that by setting up policy based routing on your router. I’ve also heard this being referred to as “source based routing” or “split tunneling”.

This post specifically addresses split tunneling on a DD-WRT router (an awesome Linux based router) that has a PPTP VPN client connection configured. I want some computers on my network to have all their traffic routed through VPN connection, but not the rest of the network.

First of all, you’ll need to configure the PPTP VPN client connection on your DD-WRT router. You can read up on doing that here: https://www.dd-wrt.com/wiki/index.php/Static_PPTP_VPN_Client.

Next, assuming your PPTP VPN client connection is configured and working properly, you’ll want to set up some rules for your router. (BTW, figuring out if the PPTP VPN client connection worked wasn’t easy or obvious–I had to ssh into the router, and run “ps w” to ensure pptp and pppd were running, and “ifconfig” to ensure the “ppp0” interface was set up, which was telling of a successful PPTP connection). Assuming your home or work network address is 192.168.0.0/255.255.255.0, and you have two computers that you want traffic routed for through the VPN: 192.168.0.105 and 192.168.0.106, you’ll need to run the following commands:

iptables –table nat –append POSTROUTING –out-interface ppp0 –jump MASQUERADE
ip rule add from 192.168.0.105 table 200
ip route add default via $(ifconfig ppp0 | sed -n ‘s/.*inet *addr:\([0-9\.]*\).*/\1/p’) dev ppp0 table 200
ip rule add from 192.168.0.106 table 201
ip route add default via $(ifconfig ppp0 | sed -n ‘s/.*inet *addr:\([0-9\.]*\).*/\1/p’) dev ppp0 table 201
ip route flush cache

The first command preps your router for forwarding (NAT’ing) packets over ppp0 (the interface for the PPTP VPN client). The next few commands add rules for all traffic from 192.168.0.105 and 192.168.0.106 to get sent over ppp0 and hence the VPN connection (note that the ifconfig/sed sub-command gets the IP address for ppp0, which is the IP given to your PPTP VPN client by the PPTP VPN server). And the final command flushes any cache on the routing tables on the router, which cleans it up to work for the new rules.

And that’s it!

However, you may not want to have to manually run these commands whenever your router is restarted. So, I set up the following “startup” script to run these commands for me, and also add them to the “ip-up” script for the PPTP client so they’re executed if the interface goes down and back up (due to network connectivity interruption or something else). You can enter the script commands below to your stratup script on your DD-WRT router, and they will ensure that if your router is ever restarted, or the PPTP client connection drops and gets reconnected, the special routes will get set up again. (You can create the startup script like so: https://www.dd-wrt.com/wiki/index.php/Startup_Scripts):

sleep 120
iptables –table nat –append POSTROUTING –out-interface ppp0 –jump MASQUERADE
ip rule add from 192.168.0.105 table 200
ip route add default via $(ifconfig ppp0 | sed -n ‘s/.*inet *addr:\([0-9\.]*\).*/\1/p’) dev ppp0 table 200
ip rule add from 192.168.0.106 table 201
ip route add default via $(ifconfig ppp0 | sed -n ‘s/.*inet *addr:\([0-9\.]*\).*/\1/p’) dev ppp0 table 201
ip route flush cache
head -n -1 /tmp/pptpd_client/ip-up > /tmp/pptpd_client/ip-up.new
mv /tmp/pptpd_client/ip-up.new /tmp/pptpd_client/ip-up
echo “iptables –table nat –append POSTROUTING –out-interface ppp0 –jump MASQUERADE” >> /tmp/pptpd_client/ip-up
echo “ip rule delete from 0/0 to 0/0 table 200” >> /tmp/pptpd_client/ip-up
echo “ip route delete table 200” >> /tmp/pptpd_client/ip-up
echo “ip rule add from 192.168.0.105 table 200” >> /tmp/pptpd_client/ip-up
echo “ip route add default via \$(ifconfig ppp0 | sed -n ‘s/.*inet *addr:\([0-9\.]*\).*/\1/p’) dev ppp0 table 200” >> /tmp/pptpd_client/ip-up
echo “ip rule delete from 0/0 to 0/0 table 201” >> /tmp/pptpd_client/ip-up
echo “ip route delete table 201” >> /tmp/pptpd_client/ip-up
echo “ip rule add from 192.168.0.105 table 201” >> /tmp/pptpd_client/ip-up
echo “ip route add default via \$(ifconfig ppp0 | sed -n ‘s/.*inet *addr:\([0-9\.]*\).*/\1/p’) dev ppp0 table 201” >> /tmp/pptpd_client/ip-up
chmod a+x /tmp/pptpd_client/ip-up
Just a quick breakdown of the script above: the first command, sleep 120, ensures that enough time passes for the router to boot up, and the PPTP VPN client to connect at least once. The next few commands are the same as above… they set up the policy based routing for the current PPTP connection. The next several commands write to the “/tmp/pptpd_client/ip-up” script, which is run whenever the PPTP connection is re-established (in case it gets dropped due to whatever temporary network connectivity issue). Note: the final line of the default “ip-up” script is “exit 0” so any commands appended to the script after that line won’t get executed. Hence, I have the head command to get everything from the current ip-up script except the last line. It’s also important to note that this ip-up script is re-created any time you restart the DD-WRT router, hence it’s important to re-append the commands to it on every restart when the startup script is called.

Leave a Reply