Setting up custom routes depending on the network location

Recently on OS X Tiger (10.4)... I wanted to change the routing table depending on the network environment which I am using.

OS X has a pretty neat features that enables you to react to changes in the network (IP address changes, etc). This usually happens if you move around with your mac from one network to another. When a network change is detected, a utility called configd is waken up. This daemon sits in the background and waits for network changes to take place. When network changes do take place, it executes a certain set of commands. A more detailed description of this process can be found at the following links:

  1. http://www.afp548.com/article.php?story=20041015131913324 - description of configd
  2. http://culater.net/software/SambaX/SambaX.php - description of Kicker.xml (see below)

When configd kicks in, you usually get some log entries that look like this:


Mar 14 19:42:54 localhost configd[86]: executing /System/Library/SystemConfiguration/Kicker.bundle/Contents/Resources/set-hostname
Mar 14 19:42:54 localhost configd[86]: posting notification com.apple.system.config.network_change
Mar 14 19:42:54 localhost lookupd[1017]: lookupd (version 324.11) starting - Mon Mar 14 19:42:54 2005
Mar 14 19:42:55 localhost kernel: arp: 192.168.126.2 moved from 00:c0:26:75:3d:b2 to 00:01:02:1a:1a:48 on en1
Mar 14 19:42:55 localhost set-hostname[1029]: setting hostname to apfel.flint.sec
Mar 14 19:42:57 localhost configd[86]: AppleTalk startup complete

As described in the page [2.] from the link above, configd basically looks into the file Kicker.xml which is located in your mac at /System/Library/SystemConfiguration/Kicker.bundle/Contents/Resources/Kicker.xml. This XML file basically only says which programs should be executed upon a network change. The XML file is pretty straight forward and so I added the following lines to the end of it:

<dict>
	<key>execCommand</key>

	<string>$BUNDLE/Contents/Resources/routing.sh</string>
	<key>execUID</key>
	<integer>0</integer>
	<key>keys</key>

	<array>
		<string>State:/Network/Global/DNS</string>
		<string>State:/Network/Global/IPv4</string>
		<string>State:/Network/Global/NetInfo</string>

	</array>
	<key>name</key>
	<string>dotsec-routing</string>
</dict>

The variable $BUNDLE resolves to /System/Library/Systemconfiguration/Kicker.bundle/Resources. This is where I put my shell script routing.sh. Here it is:

#!/bin/sh
logger -i "dotsec route adder v0.1"
scselect_output=$(scselect 2>&1 | grep '^ \*' | sed -e 's:^[^(]*(\([^)]*\))$:\1:g')
desired_environment="Home"

if [ $scselect_output == $desired_environment ]; then
	logger -i "found environment $desired_environment"

	logger -i "changing route"
	route add -net 192.168 192.168.126.99
fi

What the script basically does is use the output of scselect. scselect is an OS X command line utility to change the network environment. You can also do this by clicking around in the System preferences, but scselect can do this from the command line which is much nicer in this case. The output of scselect is passed to sed and evaluated to get the current the name of the current environment (the default output of scselect is to output all the available environments and put a start [*] in front of the active networking environment). The variable desired_environment holds the value for the environment for which I want my routing. The two variables are compared and if they are equal, the route is added. Note since I advised configd to execute this script as UID 0, adding the route is no problem.

Note: at first it seemed as configd ignored my changes to Kicker.xml, but in fact it turned out that I had to completely kill configd and restart it. Even a reboot didn't bring the desired changes, but a 'killall configd && configd' (as root) solved the problem.