Dave Landers

Dave’s thoughts (such as they are)

Network Location Switching

Been quite a long time since my last post, and I am not going to try to catch things up.  But I did want to archive this information:

At work, we have an http proxy – an idea from the 18th century it seems.  So I have to have two Network  Locations (one for normal configurations, another for the work proxy).  We also use Cisco AnyConnect VPN to connect from home.

I had been using MarcoPolo for switching locations.  This worked great for home and work, but I wasn’t happy with how it worked with the VPN.  All I could do was detect if the VPN application was running or not – so I couldn’t leave the VPN app open and connect/disconnect.  Also, the process was to launch the VPN app, wait for MarcoPolo to switch the network to VPN, make sure it was stable (not switching back and forth), and then connect to the VPN. Disconnecting has to be followed by exiting the VPN else MarcoPolo wouldn’t switch things back to my no-proxy Location.

I took inspiration from this and created a LaunchAgent and a simple script to switch Network Locations.

The LaunchAgent plist file goes in ~/Library/LaunchAgents/com.srednal.netswitch.plist, and looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
    <dict>
        <key>Label</key>
        <string>com.srednal.netswitch</string>
        <key>EnableGlobbing</key>
        <true/>
        <key>ProgramArguments</key>
        <array>
            <string>~/bin/netswitch</string>
        </array>
        <key>WatchPaths</key>
        <array>
            <string>/Library/Preferences/SystemConfiguration</string>
        </array>
    </dict>
</plist>

The script, in ~/bin/netswitch, is something like this (names and addresses may have been changed):

#!/usr/bin/env ruby
# Test network and switch locations based on vpn and ip address
require 'ipaddr'

# see if VPN is connected
def vpn?
    connected = false
    IO.popen( '/opt/cisco/vpn/bin/vpn status' ) do |out|
        out.each do |line|
            connected = true if line =~ />> state: Connected/
        end
    end
    connected
end

# are we on work network?
def work?
    # Work IP will be within  10.345.678.00/24 - obviously this is something you need to tweak
    work = IPAddr.new('10.345.678.00/24')
    work.include?(local_ip)
end

# lookup local ip addr
def local_ip
  orig, Socket.do_not_reverse_lookup = Socket.do_not_reverse_lookup, true  # turn off reverse DNS resolution temporarily
  UDPSocket.open do |s|
    s.connect '74.125.45.99', 1  # IP is anything
    s.addr.last
  end
ensure
  Socket.do_not_reverse_lookup = orig
end

# set network location config
def location(name)
    system "/usr/sbin/scselect '#{name}'"
end

if __FILE__ == $0
  # let things settle down
  sleep 2
  if (vpn? || work?) then
    location 'Proxy'
  else
    location 'Automatic'
  end
end

The script can be run manually to test things.  Then load the launch agent with launchctl load ~/Library/LaunchAgents/com.srednal.netswitch.plist.

After I got that working, I went into the Info.plist file of the AnyConnect client app, and added (to the dict element)

<key>LSUIElement</key>
<string>1</string>

That makes the Dock icon go away (but leaves the icon in the status bar), so now I can leave the AnyConnect client running and just connect/disconnect as needed – the LaunchAgent and script keep my network location set right.

1 comment

1 Comment so far

  1. mirna March 18th, 2011 3:07 am

    cool
    :)