The Problem: Ethernet >>> AirPort

For whatever reason, my MacBook Pro doesn't get very good network performance over 802.11n AirPort. Since I routinely copy everything from half-hour MPEG videos (hundreds of megabytes) through full DVDs (several gigabytes) between it and my Linux server, I much prefer gigabit Ethernet.

It turns out Apple's AFP & SMB clients are smart enough to seamlessly migrate a network connection from one transport to another -- if you disconnect from Ethernet but AirPort remains up, Mac OS X will reconnect to the file server via AirPort. Contrawise, if Ethernet is connected when AirPort goes offline, the connection will switch back. Note that I haven't tested with Ethernet & AirPort in different subnets.

This means once I have reconnected the Ethernet cable, I have 2 ways to switch my SMB connection back: I can unmount all shares from that server and then remount, or I can bring down the AirPort connection and force the OS to migrate my connections over. I prefer not to bring down AirPort, because that breaks any other open AirPort connections, which might not reconnect (iChat reconnects; Safari downloads just fail), but I do this sometimes when a program is using the share and preventing the umount from succeeding.

For over a year, I've been using a shell alias to handle the disconnect/reconnect -- below is the final version. The grep was so I could confirm it worked -- if I see prowlere (Ethernet interface), I know I have a fast connection. If I see prowler instead, I still have a slow AirPort connection -- most likely because an open file prevented the umount.

alias remount='umount /Volumes/inspector; umount /Volumes/dvd; umount /Volumes/1tb; open smb://pepper@inspectore.rep.dom/inspector/; open /Volumes/inspector/home/pepper/tivo/; sleep 10; netstat -a | grep \.micro | grep ESTABLISHED'

The Improvement: Automation

Back in the Jaguar era, I had a script to run Plucker automatically. I had my laptop configured to run it both via cron and whenever I (dis)connected my Ethernet cable, but when the network trigger broke I just refined my crontab to compensate and forgot about it. In Leopard, launchd handles tasks like this, but it doesn't offer network triggers.

I wanted to recreate the automatic trigger on network reconnection, but wasn't sure how to do it. crankd could probably do the trick, but I don't know Python. Fortunately Jeremy Reichman pointed me to MarcoPolo, which fits the bill admirably.

I was initially confused by the fact that MarcoPolo automatically copied the system's Network 'Locations', which I don't want to change. Fortunately MarcoPolo is happy to work with its own 'Contexts', and leave the system Location untouched. I created a couple contexts, 'en0 online' and 'en0 offline', told MarcoPolo to use IP and NetworkLink as "Evidence Sources", and configured it to switch to 'en0 online' when the 'prowlere' IP comes online. On the other hand, "en0 (Ethernet) link inactive" switches to 'en0 offline'. This way if I connect to an outside network and get a different Ethernet IP, it won't try to connect to my home server. MarcoPolo can run shell scripts, so I converted my alias to a script, which runs on switching to 'en0 online':

pepper@prowler:~$ cat bin/remount 
#!/bin/sh
# remount: Reconnect to inspectore, hopefully via Ethernet rather than AirPort

umount /Volumes/inspector
umount /Volumes/dvd
umount /Volumes/1tb
open smb://pepper@inspectore.rep.dom/inspector/
sleep 10
netstat -a | grep micro | grep ESTABLISHED | awk '{print $4, $5}' | growlnotify -w

Future possibilities include launching iPhoto when I connect a camera memory card but not when I plug in the iPhone (I already had 37signals' workaround, but MarcoPolo would have been a simpler option), or switching iTunes to the upstairs speakers when I connect to the upstairs monitor (iTunes' output device is not currently scriptable, so I filed an RFE). Unfortunately, since these triggers and actions are all orthogonal to each other, I'd end up multiplying contexts to accommodate them, which is suboptimal.

David, thanks for MarcoPolo!