#!/usr/bin/env ruby
require 'ostruct'
VERBOSE = true
def die(msg = nil)
puts msg if msg
exit 1
end
# Parse netstat output
# Select only routes that are 'up' (flagged U)
routes = []
begin
res = %x{netstat -rn}
res = res.split(/\n/).reject {|n| n =~ /^Kernel/ }
names = res.shift.split(/\s+/).map {|name| name.downcase.to_sym }
routes = res.map {|row|
OpenStruct.new(Hash[*names.zip(row.split(/\s+/)).flatten])
}.select {|route|
route.flags =~ /U/
}
end
# Find default gateway (flagged G)
default = routes.select {|route|
route.destination == '0.0.0.0' and
route.genmask == '0.0.0.0' and
route.flags =~ /G/
}.first
# Sometimes the VPN will set a default route that's stupid, but works for point-to-point links
if default.nil?
default = routes.select {|route|
route.destination == '0.0.0.0' and
route.genmask == '0.0.0.0' and
route.gateway == '0.0.0.0'
}.first
end
# Find the gateway used by the VPN (flagged G and H).
# Ignore any host routes on the same iface as the default gateway, as they may be added by the VPN itself.
vpn = routes.select {|route| route.flags =~ /G/ and route.flags =~ /H/ and route.iface != default.iface }
# Should have exactly one hostroute here.
die "Couldn't guess route! Set manually." if vpn.length == 0
die "More than one possible route! Set manually." if vpn.length > 1
vpn = vpn.first
# Nuke the existing default route
printf("%s (%s)", default.gateway, default.iface)
printf(" -> %s (%s)\n", vpn.gateway, vpn.iface)
puts %w{/sbin/route delete -net default}.join(" ") if VERBOSE
system(*%w{/sbin/route delete -net default}) || die("Route delete failed")
puts %w{/sbin/route add -net default gw}.push(vpn.gateway).push('dev').push(vpn.iface).join(" ") if VERBOSE
system(*%w{/sbin/route add -net default gw}.push(vpn.gateway).push('dev').push(vpn.iface)) || die("Route add failed")