#! /bin/sh
#
# Copyright (c) 2002 by Andreas Haakh / The FreeBSD Project
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
#
# $FreeBSD: $
#
#
# This script creates and/or updates a secure tunnel between two hosts
# with dynamic IP-addresses. It is run from cron with an crontab-entry like
#*/10	*	*	*	*	root	/mypath/myname >>logfile 2>&1
# in order to check (and update if necessary) the tunnel-IPs and the kernel
# keys every 10 minutes.
#
# You need a dynamic dns-service (like dyndns.org) in order to resolve the
# actual IP-addresses of both hosts. See /usr/ports/net/ddup for details on
# dynamic hostname-updates.
#
# Adapt 'hostName[A|B]' below and in the case statement to suit your needs
# and change the encryption keys.
#
# This script runs on both sides of the tunnel, supposed they both run
# FreeBSD 4.5 or above.

hostA=hostNameA.dyndns.org
hostB=hostNameB.dyndns.org

addrA="192.168.63.1"
addrB="192.168.29.1"

netmA="192.168.63.0/26"
netmB="192.168.29.0/27"

# Use manual keys in SAD. Didn't manage this case with racoon yet :-\
#
spi_A="0x1000A"
spi_B="0x1000B"

# We use 3des-cbc (192 bits / 24 Bytes) for ESP/tunnel/
#
key_A="kamekame12341234kame1234"
key_B="kamekame12341234kame1234"
#     "kamekame12341234kame1234"


myhostname=`uname -n`

case $myhostname in
	hostNameA*)
		src="A"
		dst="B"
		;;
	hostNameB*)
		src="B"
		dst="A"
		;;
	*)
		echo "You must change the above case-clauses and variables"
		echo "in order to run this script on this machine!"
		exit 1
		;;
esac

eval srchost=\$host${src}
eval srcaddr=\$addr${src}
eval src_net=\$netm${src}
eval src_spi=\$spi_${src}
eval src_key=\$key_${src}

eval dsthost=\$host${dst}
eval dstaddr=\$addr${dst}
eval dst_net=\$netm${dst}
eval dst_spi=\$spi_${dst}
eval dst_key=\$key_${dst}


srcpubl=`host ${srchost} | awk '{print $4}'`
dstpubl=`host ${dsthost} | awk '{print $4}'`

#echo "srchost ${srchost} on ${srcpubl}"
#echo "dsthost ${dsthost} on ${dstpubl}"

date

if [ ${srcpubl} = "" -o ${dstpubl} = "" ]; then
	echo "Could not resolve public IP-address..."
fi

# See if there is already an interface between srcaddr and dstaddr.
xc=`ifconfig |grep "${srcaddr}.*${dstaddr}"`

case $xc in
*inet\ *)
	eval `ifconfig |grep -B3 -A2 "${srcaddr}.*${dstaddr}" |awk '
		/^[[:alnum:]].*: / { ifname=$1 }
		/^[[:blank:]]+tunnel inet / { srcpubl=$3; dstpubl=$5 }
		/^[[:blank:]]+inet / { 
			srcaddr=$2; dstaddr=$4; 
			if (ifname!="" && srcpubl!="" && dstpubl!="" \
				&& srcaddr!="" && dstaddr!="")
				printf "ifname=%s\n isrcpubl=%s\n idstpubl=%s\n isrcaddr=%s\n idstaddr=%s\n",
				ifname,srcpubl,dstpubl,srcaddr,dstaddr;
		} '`
	ifname=`echo "${ifname}" |sed -e s,:,,`
	if [ "${ifname}X" = "X" ]; then
		echo "There is a non-tunnel interface between both hosts:"
		echo "inet ${srcaddr} --> ${dstaddr}"
		echo "I can't deal with this case..."
		exit 1
	fi
#	set|grep ^i|sort

	if [ ${srcpubl} = ${isrcpubl} -a ${dstpubl} = ${idstpubl} ]; then
		echo "No update necessary!"
		exit;
	fi

#	Now update the tunnel
	ifconfig ${ifname} tunnel ${srcpubl} ${dstpubl}
	;;
"")
	echo "Tunnel does not exist. Create it!"
	ifname=`ifconfig gif create`
	ifconfig ${ifname} tunnel ${srcpubl} ${dstpubl}
	ifconfig ${ifname} inet ${srcaddr} ${dstaddr} netmask 0xffffffff
	route add -net ${dst_net} -interface ${ifname}
	;;
esac

# The tunnel interface was created or updated.
#
# It would be nice if ifconfig could update SAD and SPD-entries..
#
# There is no way to update existing kernel ipsec-tables. So they must
# be flushed and installed again.
setkey -F
setkey -PF

# See if there is an ipsec.conf file and install its entries.
#
if [ -r /etc/ipsec.conf ] ; then
	setkey -f /etc/ipsec.conf
fi

# Now add the updated tables for this secure tunnel
#
setkey -c <<EOF
# SPDs
spdadd ${src_net} ${dst_net} any -P out ipsec
esp/tunnel/${srcpubl}-${dstpubl}/require;
spdadd ${dst_net} ${src_net} any -P in ipsec
esp/tunnel/${dstpubl}-${srcpubl}/require;
# SADs
add ${srcpubl} ${dstpubl} esp ${src_spi} -m tunnel
	-E 3des-cbc "${src_key}";
add ${dstpubl} ${srcpubl} esp ${dst_spi} -m tunnel
	-E 3des-cbc "${dst_key}";
EOF

#sleep 3 && setkey -D && setkey -PD
echo "Updated tunnel: ${srcpubl} --> ${dstpubl}"

exit
#
