Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
PPP VPN split-network/split-DNS script for OSX
#!/bin/sh
####################################################
## ##
## PPP VPN split-network/split-DNS script for OSX ##
## by Aaron Meriwether ##
## ##
## installation: ##
## sudo cp thisfile /etc/ppp/ip-up ##
## sudo chmod 755 /etc/ppp/ip-up ##
## ##
####################################################
##################
## begin config...
# Define per-VPN-server settings here...
SERVERS[0]="vpn.example.org" # must match the server name set in Network Preferences.
NETWORKS[0]="192.168.2.0/24 192.168.3.0/24" # only networks besides the VPN LAN itself!
DOMAINS[0]="example.org" # domains which should use the VPN's DNS resolver when connected.
SERVERS[1]="vpn.mit.edu"
NETWORKS[1]="" # an empty network list is acceptable if you are only interested in the DNS aspects
DOMAINS[1]="staff.mit.edu athena.mit.edu"
SERVERS[2]="vpn.rogers.com"
NETWORKS[2]="172.16.7.128/25 205.150.67.0/24"
DOMAINS[2]="" # likewise, an empty DNS list is acceptable if you are only interested in the network aspects
# set to /dev/null if you object to debugging output.
DEBUGFILE=/tmp/ip-up-debug.txt
## end config...
#################
# When the ppp link comes up, this script is called with the following
# parameters
# $1 the interface name used by pppd (e.g. ppp3)
# $2 the tty device name
# $3 the tty device speed
# $4 the local IP address for the interface
# $5 the remote IP address
# $6 the parameter specified by the 'ipparam' option to pppd
# (when used with a VPN configured via Network Preferences,
# $6 seems to contain the IP address of the gateway over
# which the VPN traffic passes. This might be useful in
# ip-up scripts which need to avoid breaking the route
# used by pppd itself.)
PATH=/usr/bin:/bin:/usr/sbin:/sbin
# Generic array operation.
# e.g.: indexOf "something" "${myArray[@]}"
# note the double quotes around the array and the use of [@] not [*]
indexOf() {
local s="$1"; shift; local a=( "$@" ) i
for (( i = 0; i < ${#@}; i++ )); do
if [ "${a[$i]}" == "$s" ]; then echo $i; return; fi
done
}
# Given an interface name, find the corresponding scutil service key.
if2service() {
local i
for i in $(echo "list State:/Network/Service/[^/]+/PPP" | scutil | cut -d/ -f4); do
if [ "$(echo show State:/Network/Service/$i/PPP | scutil | grep InterfaceName | cut -d' ' -f5)" == "$1" ]; then echo $i; return; fi
done
}
# Given a network service key (specifically a PPP service), get the server name (as set in Network Preferences).
getPppServerName() {
echo show Setup:/Network/Service/$1/PPP | scutil | grep CommRemoteAddress | cut -d' ' -f5
}
echo "$(date +'%Y-%m-%d %T %Z') $0 1=$1 2=$2 3=$3 4=$4 5=$5 6=$6" >> $DEBUGFILE
SERVICE="$(if2service $1)"
echo "Service: $SERVICE" >> $DEBUGFILE
SERVER="$(getPppServerName $SERVICE)"
echo "Server Name: $SERVER" >> $DEBUGFILE
i=$(indexOf "$SERVER" "${SERVERS[@]}")
if [ -z "$i" ]; then
echo "No special configuration found for this VPN server." >> $DEBUGFILE
exit
fi
if [ -n "$(echo show State:/Network/Service/$SERVICE/IPv4 | scutil | grep OverridePrimary)" ]; then
echo "Clearing the 'Send all traffic over VPN connection' flag" >> $DEBUGFILE
scutil <<-END
get State:/Network/Service/$SERVICE/IPv4
d.remove OverridePrimary
set State:/Network/Service/$SERVICE/IPv4
END
fi
if [ -n "${NETWORKS[$i]}" ]; then
for n in ${NETWORKS[$i]}; do
echo "Adding route for network $n" >> $DEBUGFILE
route -n add -net $n $5
done
fi
if [ -n "${DOMAINS[$i]}" ]; then
echo "Limiting DNS over the VPN to these domains: ${DOMAINS[$i]}"
scutil <<-END
get State:/Network/Service/$SERVICE/DNS
d.add SupplementalMatchDomains * ${DOMAINS[$i]}
set State:/Network/Service/$SERVICE/DNS
END
fi
@bdickason

This comment has been minimized.

Copy link

bdickason commented Jul 18, 2014

Created fork here that removes the example servers and adds support for EHV vpn. I haven't tested EHV yet (waiting on credentials) so I may need to modify the IP.

https://gist.github.com/bdickason/937fcf5cc975aecb484b

@donikatz

This comment has been minimized.

Copy link

donikatz commented Dec 17, 2014

Have you confirmed multiple search domains works? It doesn't for me with Yosemite, at least. Adding multiple search domains results in none added to the resolver. I haven't been able to get SupplementalMatchDomains to work at all.

@steve-jansen

This comment has been minimized.

Copy link

steve-jansen commented Aug 20, 2015

This is awesome stuff. Thank you for sharing! 😄

@aequitas

This comment has been minimized.

Copy link

aequitas commented May 17, 2016

Nice script, only I have problem running it under El Capitan getting the split-dns to work. I can see the SupplementalMatchDomains being set initially after the ipsec tunnel is up. But after a few seconds the PPP somehow seems to reset this change. From /var/log/ppp.log:

Tue May 17 13:39:56 2016 : Received acsp/dhcp dictionaries
Tue May 17 13:39:56 2016 : Committed PPP store on install command

Have you found any way to work around this?

@nicx

This comment has been minimized.

Copy link

nicx commented Oct 10, 2017

I have the same problem like @aequitas: its working directly after the ppp connection comes up, after 2 seconds the chage is reverted by the system. Any solution?

@nicx

This comment has been minimized.

Copy link

nicx commented Oct 13, 2017

just as a workaround I added a "sleep 20" between line 103 and 104. With this its working. I think the problem is Mac OS related:

Fri Oct 13 12:18:00 2017 : l2tp_wait_input: Address added. previous interface setting (name: en4, address: 10.18.89.2), current interface setting (name: ppp0, family: PPP, address: 192.168.1.1, subnet: 255.255.255.0, destination: 10.255.255.0).
Fri Oct 13 12:18:00 2017 : Committed PPP store on install command
Fri Oct 13 12:18:00 2017 : L2TP port-mapping for en4, interfaceIndex: 0, Protocol: None, Private Port: 0, Public Address: 0, Public Port: 0, TTL: 0.
Fri Oct 13 12:18:00 2017 : L2TP port-mapping for en4 inconsistent. is Connected: 1, Previous interface: 6, Current interface 0
Fri Oct 13 12:18:03 2017 : sent [IP data <src addr 192.168.1.1> <dst addr 255.255.255.255> <BOOTP Request> <type INFORM> <client id 0x08000000010000> <parameters = 0x6 0x2c 0x2b 0x1 0xf9 0xf>]
Fri Oct 13 12:18:09 2017 : sent [IP data <src addr 192.168.1.1> <dst addr 255.255.255.255> <BOOTP Request> <type INFORM> <client id 0x08000000010000> <parameters = 0x6 0x2c 0x2b 0x1 0xf9 0xf>]
Fri Oct 13 12:18:12 2017 : sent [IP data <src addr 192.168.1.1> <dst addr 255.255.255.255> <BOOTP Request> <type INFORM> <client id 0x08000000010000> <parameters = 0x6 0x2c 0x2b 0x1 0xf9 0xf>]
Fri Oct 13 12:18:15 2017 : No DHCP server replied

It seems that the Mac OS links the ppp interface to the en4 interface with some inconsistencies, and that is generating a new DHCP BOOTP request.

In the end it resets the changes of the script here. So my workaround is just a delayed run of the setting by the script.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.