Before you read this post, understand that PPTP is insecure. Don’t use PPTP to create a VPN to anything you care about. Really. Apple has even pulled PPTP support from macOS Sierra. Read all about PPTP’s Apple death here, and thanks to @scottm32768 for letting me know about it.
TL;DR
Skip to Solution #3.
Problem
When successfully making a PPTP connection to a remote VPN server with the built-in Mac OS X client, you find that you can’t connect to hosts on the other side of the VPN tunnel. You can still connect to the Internet and LAN hosts.
The root issue is that, by default, OS X has no reason to send traffic across the VPN tunnel. A reason must be provided.
Solution #1 – Setting Service Order
In System Preferences > Network, perform “Set Service Order” (the drop down gear icon), and move the PPTP connection to the top of the list.
This means that when the PPTP tunnel is up, traffic will flow through it before other network connections. This will gain you access to hosts on the other side of the VPN tunnel. It will also break everything else, unless the network on the other side of the PPTP tunnel can also service your Internet traffic. This is going to be a function of the VPN termination device as well as the firewall configuration at the remote site.
The issue here is that ALL traffic, even your Internet traffic, will be routed through the tunnel. Thus, Internet traffic on your system is tossed into the tunnel, pops out at the remote site, gets hairpinned back around right back out through the remote network’s firewall, hits the Internet server you were trying to get to, comes all the way back to the remote network, where it finally gets popped back into the tunnel to you. Not all firewalls or VPN termination devices will be configured to support this hairpin routing.
If you choose this method, remember to set a DNS server in your PPTP connection profile that can be reached via the VPN tunnel. Something public like Google’s 8.8.8.8 and 8.8.4.4 might work. This is important because there’s a good chance your local DNS server will become unreachable as soon as the tunnel comes up, leaving you without name resolution. You might have connectivity, but without name resolution, it will feel like you don’t.
Solution #2 – Disabling Split Tunneling
By default, OS X will “split tunnel” when using the built-in PPTP client. That is, traffic will follow OS X’s routing table. Networks on the other side of the tunnel flow via the tunnel, assuming there are routes that send appropriate traffic that way. Other traffic, such as local LAN or Internet, flows via the wifi or Ethernet connection directly – no tunnel. Therefore, traffic is “split” between the tunnel and physical network interfaces. You can check OS X’s routing table via netstat -rn.
The catch here is that bringing up a PPTP tunnel doesn’t automatically add routes to OS X’s routing table, which is why your PPTP tunnel doesn’t seem to be working and you’re reading this article. There’s a tunnel, but nothing instructing OS X to forward any traffic across that tunnel. Therefore, you’re going to check a box that defeats split tunneling, forcing all traffic into the tunnel.
In System Preference > Network, select the PPTP connection profile. Click the “Advanced…” button. Check “Send all traffic over VPN connection”. In this case, the service order doesn’t matter.
All the same caveats about hairpin routing and DNS as mentioned in solution #1 hold true.
Solution #3 (and my favorite) – /etc/ppp/ip-up
The script /etc/ppp/ip-up will automatically fire after a PPTP tunnel is brought up. This appears to be default behavior in *NIX kernels, based on this.
Once the PPP link is established, pppd looks for
/etc/ppp/ip-up
. If this script exists and is executable, the PPP daemon executes the script. This allows you to automate any special routing commands that may be necessary and any other actions that you want to occur every time the PPP link is activated.
This is definitely the behavior of OS X. When the PPTP tunnel comes up, the /etc/ppp/ip-up script fires. Therefore, you can use this script to add routes to the OS X routing table.
1. Create /etc/ppp/ip-up as sudo. If you aren’t a sudo-er on your Mac (i.e. not an admin equivalent), this is going to be an issue for you. You have to have root equivalent to edit this script. I use vi as my editor. Thus, sudo vi /etc/ppp/ip-up.
2. Let’s say there are two networks I care about on the other side of my PPTP tunnel: 10.10.10.0/24 and 10.10.20.0/24. An /etc/ppp/ip-up script to add them to the routing table could look as follows.
#!/bin/sh
/sbin/route add -net 10.10.10.0/24 -interface $1
/sbin/route add -net 10.10.20.0/24 -interface $1
3. We’re using the explicit path “/sbin/” to be certain that the script can find the route command.
4. The $1 is a variable representing the name of the interface used by PPPd.
5. Make sure root is the owner of /etc/ppp/ip-up. It should be by default. sudo chown root /etc/ppp/ip-up
6. Make sure the script is executable. It will not be by default. sudo chmod 755 /etc/ppp/ip-up
The next time you bring up a PPTP tunnel, /etc/ppp/ip-up will run, adding those two routes to the OS X routing table. Don’t forget that you can validate that the script ran by looking at netstat -rn.
With the routes added to the routing table, OS X knows to send traffic for those networks across the tunnel.
This isn’t a perfect solution, as the script is a blunt hammer that doesn’t distinguish between tunnels. This particular script will add those routes to the OS X routing table, no matter what PPTP server you access. You’d need a smarter script to support multiple PPTP sites, which is beyond my scope here. Maybe in a future post.