2011
02.24

I recently had to figure out how to add custom routes through a VPN connection under Mac OS X. It took me awhile to work through the problem, but I was thankfully able to find a reliable solution. Hopefully the notes below will help anyone else facing the same problem.

Configuring the Mac OS X VPN Client

Ethernet cable coming out of a keyholeI was working on a project that required me to use a VPN to connect to a remote network. I was provided with a pre-configured VPN setup to install on a Windows machine, but I also wanted to be able to connect using my Mac.

I was able to use the VPN client built into Mac OS X to setup the connection. (I also added the search domain “remote.main” for the VPN connection under the Advanced DNS settings.) The client is easy to use and even supports using an RSA SecurID token for authentication, but it does not provide any options for advanced networking routing. Everything would work fine if I configured the machine to send all traffic over the VPN connection, either by setting the service order or in the Advanced Options for the VPN connection.

The problem was that I didn’t want to send all of my traffic over the VPN, just the traffic between my machine and the remote network. The rest of my internet traffic for streaming music, instant messaging, Skype and web browsing would unnecessarily burden the VPN connection. This would not only slow down those activities of mine, but also slow down other people using the VPN.

If I didn’t tell the VPN client to send all traffic over the VPN, then I wasn’t able to connect to all of the machines on the remote network. I needed to explicitly control the network routing.

Analyzing the Problem at the Command-line

The first step was to open up the Terminal and understand what was happening. (I have omitted some of the extraneous text below, only the key lines are shown.) First I looked at how my network interfaces were configured:

%: ifconfig
…
en1: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
        inet 192.168.1.20 netmask 0xffffff00 broadcast 192.168.1.255
…
ppp0: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST>> mtu 1396
        inet 172.19.25.11 --> 172.19.25.1 netmask 0xffff0000

 

The en1 interface is my wireless connection with a local IP of 192.168.1.20 and the ppp0 interface is the VPN connection with an IP on the remote network of 172.19.25.11.

Next, I wanted to see how my various internet traffic was being routed. Were my connections to the internet using the VPN?

%: traceroute www.google.com
traceroute to www.l.google.com (74.125.230.112), 64 hops max, 52 byte packets
 1  192.168.1.1 (192.168.1.1)  55.163 ms  35.808 ms  14.201 ms
 2  83-169-169-58-isp.superkabel.de (83.169.169.58)  227.858 ms  29.495 ms  43.557 ms
…

 

That looks good, it goes directly to my local gateway and then out to the internet via my ISP.

What about my connections to the remote network? Is the VPN properly connected? For this I just used my assigned VPN IP address from above.

%: traceroute 172.19.25.11
traceroute to 172.19.25.11 (172.19.25.11), 64 hops max, 52 byte packets
 1  172.19.25.1 (172.19.25.1)  193.050 ms  164.557 ms  196.617 ms
 2  172.19.25.11 (172.19.25.11)  321.015 ms  538.553 ms  371.819 ms

 

That looks good too, no mention of my local gateway or my ISP. Now to look at a machine on the remote network that I was having trouble connecting to, I’ll call it host.remote.main:

%: traceroute host.remote.main
traceroute to host.remote.main (172.20.50.44), 64 hops max, 52 byte packets
 1  192.168.1.1 (192.168.1.1)  40.987 ms  26.728 ms  50.998 ms
 2  * * *
^C

 

That confirmed that my machine knew the IP address of the remote machine (172.20.50.44), but that instead of using the VPN connection it was trying to use my local gateway. To understand why I looked at the routing tables:

%: netstat -r
Routing tables

Internet:
Destination        Gateway            Flags        Refs      Use   Netif Expire
default            192.168.1.1        UGSc           68       51     en1
default            172.19.25.1        UGScI           0        0    ppp0
…
172.19             ppp0               USc             2       11    ppp0
172.19.25.1        172.19.25.11       UH              1        9    ppp0
…

 

I had two default entries, one for my wireless connection and one for my VPN. The wireless connection (en1) was listed first, meaning it took precedence. Unless any of the specific rules below applied, my traffic would be sent over the wireless connection. There were two rules that specified the use of my VPN connection (ppp0). The first of these rules had the Destination 172.19, meaning that it applied to any IP address that started with that value. I had seen that route work when I did a traceroute to 172.19.25.11. This rule did not apply to the machine I was having trouble connecting to, since its IP started with 172.20.

Adding a Route Over the VPN

To fix the problem I added a new route to the table to apply to all IPs starting with 172.20:

%: sudo route -v add -net 172.20 -interface ppp0
u: inet 172.20.0.0; u: link ppp0; RTM_ADD: Add Route: len 136, pid: 0, seq 1, …
locks:  inits:
sockaddrs: <DST ,GATEWAY,NETMASK>
 172.20.0.0 ppp0 (0) 0 ffff
add net 172.20: gateway ppp0

%: traceroute host.remote.main
traceroute to host.remote.main (172.20.50.44), 64 hops max, 52 byte packets
 1  172.19.25.1 (172.19.25.1)  61.289 ms  67.725 ms  126.306 ms
 2  172.19.1.1 (172.19.1.1)  364.456 ms  69.946 ms  106.473 ms
 3  192.168.100.9 (192.168.100.9)  98.471 ms  83.573 ms  120.419 ms
 4  host.remote.main (172.20.50.44)  74.289 ms  282.272 ms  90.462 ms

 

Now my connection was working! To get to the remote machine, the traffic was now being sent to the ppp0 Gateway of 172.19.25.1 and successfully routed through the remote network. The new route can be viewed by running netstat again and will be deleted when the VPN connection is lost. I didn’t find anyway to automatically recreate this route when the VPN connection is made, so I just keep the command handy and run it every time I reconnect the VPN.

8 comments so far

Add a comment
  1. Thanks for this elaborately written post!

    But anyway, does anybody know a way to interface with the vpn connection to set up the route automatically as soon as the vpn connection is established? apple script? whatever?

    Thanks!

    Jan

  2. Cool. I’ll have a look at ControlPlane :-)

    If it’s just about routes to add and delete, this should be no issue, because the routes over the interface ppp0 will be deleted automaticaly as soon as the interface diappears.

  3. Had a quick look at it. Thanks again. Great tool. This helps a lot.

    • You can create an executable shell script in /etc/ppp/ip-up that gets run whenever the VPN connects. It gets passed several arguments containing the VPN information, including the interface name as $0:

      #!/bin/sh
      /sbin/route add 10.50.0.0/16 -interface $1

  4. Scott, I hope that you find 1.0.4 fixes issue for you. Please let me know if it is still a problem

    • I tested ControlPlane 1.0.4 and it didn’t fix my issue, so I held Dustin’s comment back.

      Since then Dustin has updated ControlPlane several times, but I am no longer in a situation where I am using a VPN, so I haven’t been testing the latest releases. When we were trying to debug the original problem, Dustin wasn’t able to reproduce my problem.

      My hope would be that recent versions of ControlPlane will work for others. Any reports of success or failure would be appreciated!

  5. You don’t need Control plane; just create the file /etc/ppp/ip-up , put your commands in it and make it executable. This way the commands are executed when MacOS connects to the VPN.

Authenticate with any of the following: 
Or provide your details below:

Your comment:


Bad Behavior has blocked 168 access attempts in the last 7 days.