#!/bin/sh
# Automagical vpnc wrapper for FreeBSD to establish a VPN tunnel and routes
# 
# Copyright 2003-2004, Daniel Roethlisberger <daniel@roe.ch>
# All rights reserved.
# 
# Redistribution and use, with or without modification, are permitted
# provided that the following conditions are met:
# 1. Redistributions must retain the above copyright notice, this list of
#    conditions and the following disclaimer.
# 2. The name of the author may not be used to endorse or promote products
#    derived from this software without specific prior written permission.
# 
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# 
# $Id: vpnc-wrapper,v 1.10 2004/05/12 13:39:37 roe Exp $


# This script automatically detects your default gateway by looking at your
# route tables. It is designed to be run manually after network connectivity
# is up, and a working default route is set. It assumes that the VPN gateway
# is reachable via the default gateway.
# 
# This script does not need to know any IP addresses or interface names.
# You should only need to configure vpnc with the correct address of the
# VPN gateway in vpnc.conf -- this script will grep it from there.
# 
# This script does not modify /etc/resolv.conf in any way. Depending on
# your setup, you might need to do that manually.
# 
# Exit status of 1 indicates permanent errors (eg., configuration problems)
# Exit status of 2 indicates temporary errors (eg., no default route found)


# Tested with:
#  FreeBSD
#   - 5.2-RELEASE to 5.2.1-RELEASE-p6
#   - 5.1-RELEASE
#   - 4.9-RELEASE
#  vpnc
#   - 0.2-rm+zomb-pre8 + libgcrypt-1.2.0
#   - 0.2-rm+zomb-pre7 + libgcrypt-1.1.91
#   - 0.2-rm+zomb-pre6 + libgcrypt-1.1.12


# Known bugs and limitations:
#  - This script assumes that you want to route everything through the
#    VPN tunnel. This implies that there is only a single vpnc tunnel
#    connected at any given time, and the script will enforce that.
#    For more sophisticated routing, such as only routing a certain
#    subnet through the VPN tunnel, you might find it easier to use
#    vpnc.sh from the security/vpnc port as a starting point.


# configuration
PREFIX=/usr/local
VPNC=vpnc
DEFAULT_CONF=$PREFIX/etc/$VPNC.conf
SBIN_VPNC=$PREFIX/sbin/$VPNC
PIDFILE=/var/run/$VPNC.pid
# end of configuration


############################################################################
# Don't touch anything below this line unless you know what you're doing...

SELF=`basename -- $0`
SELF=${SELF:-$0}

DEFAULT_IFTUN=tun0

# ./configure for the system we run on
# as a side effect, this ensures that the script is adapted to accomodate
# for the particular flavour of BSD we run on. please send me required diffs.
sysver=`uname -sr`
case "$sysver" in
FreeBSD\ 5.*)
	PING_OPTS="-n -t 3 -o"
	;;
FreeBSD\ 4.*)
	PING_OPTS="-n -t 3"
	;;
*)
	echo "${SELF}: Your system is not supported yet ($sysver)!" >&2
	echo "Please edit the script and submit necessary patches. Aborting..." >&2
	exit 1
	;;
esac

# must be superuser
if [ `id -u` -ne 0 ]; then
	echo "${SELF}: Need superuser privs! Aborting..." >&2
	exit 1
fi

# check on system binaries
SBIN_ROUTE=`which route 2>/dev/null`
SBIN_ROUTE=${SBIN_ROUTE:-/sbin/route}
SBIN_PING=`which ping 2>/dev/null`
SBIN_PING=${SBIN_PING:-/sbin/ping}
SBIN_KLDSTAT=`which kldstat 2>/dev/null`
SBIN_KLDSTAT=${SBIN_KLDSTAT:-/sbin/kldstat}
SBIN_KLDLOAD=`which kldload 2>/dev/null`
SBIN_KLDLOAD=${SBIN_KLDLOAD:-/sbin/kldload}
for binary in $SBIN_VPNC $SBIN_ROUTE $SBIN_PING $SBIN_KLDSTAT $SBIN_KLDLOAD; do
	if [ ! -x $binary ]; then
		echo "${SELF}: Cannot run $binary! Aborting..." >&2
		exit 1
	fi
done

# check config file
CONF="${2:-$DEFAULT_CONF}"
if [ ! -r $CONF ]; then
	echo "${SELF}: Cannot read $CONF! Aborting..." >&2
	exit 1
fi

# get tunnel interface
IFTUN=`cat $CONF|awk '/^Interface name / { print $3 }'|head -1`
IFTUN="${IFTUN:-$DEFAULT_IFTUN}"

# get VPN gateway
VPNGW=`cat $CONF|awk '/^IPSec gateway / { print $3 }'|head -1`
if [ "x$VPNGW" = "x" ]; then
	echo "${SELF}: No VPN gateway in $CONF! Aborting..." >&2
	exit 1
fi

# get pid from pidfile, if any
PID=`cat $PIDFILE 2>/dev/null`

# do the work
case "$1" in
start)
	ps -p $PID >/dev/null 2>&1 && {
		echo "${SELF}: $VPNC is already running! Aborting..." >&2
		exit 2
	}
	ROUTER=`$SBIN_ROUTE -n get default|awk '/gateway:/ { print $2 }'`
	if [ "x$ROUTER" = "x" ]; then
		echo "${SELF}: Cannot find default gateway! Aborting..." >&2
		exit 2
	fi
	$SBIN_PING $PING_OPTS $VPNGW >/dev/null || {
		echo "${SELF}: Cannot ping VPN gateway $VPNGW! Aborting..." >&2
		exit 2
	}
	if [ "x`$SBIN_KLDSTAT|grep if_tun`" = "x" ]; then
		$SBIN_KLDLOAD if_tun
	fi
	echo "${SELF}: Starting $VPNC daemon..." &&
	$SBIN_VPNC --pid-file $PIDFILE $CONF &&
	echo "${SELF}: Changing route table..." &&
	$SBIN_ROUTE add -host $VPNGW $ROUTER &&
	$SBIN_ROUTE delete default &&
	$SBIN_ROUTE add default -interface $IFTUN &&
	echo "${SELF}: done."
	;;

stop)
	ps -p $PID >/dev/null 2>&1 || {
		echo "${SELF}: $VPNC is not running! Aborting..." >&2
		exit 2
	}
	ROUTER=`$SBIN_ROUTE -n get $VPNGW|awk '/gateway:/ { print $2 }'`
	if [ "x$ROUTER" = "x" ]; then
		echo "${SELF}: Cannot find route to $VPNGW! Aborting..." >&2
		exit 2
	fi
	echo "${SELF}: Killing $VPNC daemon..." &&
	kill `cat $PIDFILE` &&
	echo "${SELF}: Changing route table..." &&
	$SBIN_ROUTE delete default &&
	$SBIN_ROUTE add default $ROUTER &&
	$SBIN_ROUTE delete -host $VPNGW &&
	echo "${SELF}: done."
	;;

kill)
	echo "${SELF}: Trying to kill the $VPNC daemon process..."
	kill `cat $PIDFILE`
	echo "${SELF}: Removing default route..."
	$SBIN_ROUTE delete default
	echo "${SELF}: done. You will have to restore the default route manually."
	;;

*)
	echo "Usage: ${SELF} {start|stop|kill} [config-file]" >&2
	exit 1
	;;

esac