#!/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")