#!/bin/sh
# IPSEC startup and shutdown script
# Copyright (C) 1998, 1999  Henry Spencer.
# 
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2 of the License, or (at your
# option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
# 
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
# for more details.
#
# RCSID $Id: setup,v 1.47 1999/04/10 22:51:29 henry Exp $
#
# ipsec         init.d script for starting and stopping
#               the IPSEC security subsystem (KLIPS and Pluto).
#
# This script becomes /etc/rc.d/init.d/ipsec and is also accessible via
# "ipsec setup".
#
# The startup and shutdown times are a difficult compromise (in particular,
# it is almost impossible to reconcile them with the insanely early/late
# times of NFS filesystem startup/shutdown).  Startup is after startup of
# syslog and pcmcia support; shutdown is just before shutdown of syslog.
#
# chkconfig: 2345 47 68
# description: IPSEC provides encrypted and authenticated communications; \
# KLIPS is the kernel half of it, Pluto is the user-level management daemon.

# With any luck, this is enough PATH directories.
PATH=/usr/local/sbin:/sbin:/usr/sbin:/usr/local/bin:/bin:/usr/bin
export PATH

# Check that the ipsec command is available.
found=
for dir in `echo $PATH | tr ':' ' '`
do
	if test -f $dir/ipsec -a -x $dir/ipsec
	then
		found=yes
		break			# NOTE BREAK OUT
	fi
done
if ! test "$found"
then
	echo "cannot find ipsec command -- aborting" |
		logger -s -p daemon.error -t ipsec_setup
	exit 1
fi

# Pick up IPSEC configuration (until we have done this, successfully, we
# do not know where errors should go, hence the explicit "daemon.error"s.)
eval `ipsec _confread --varprefix IPSEC --type config setup`
if test " $IPSEC_confreadstatus" != " "
then
	echo "$IPSEC_confreadstatus -- aborting" |
		logger -s -p daemon.error -t ipsec_setup
	exit 1
fi



# some shell functions, to clarify the actual code

# start KLIPS
startklips() {
	# load module if necessary
	if ! ls /proc/net | egrep '^ipsec_' >/dev/null
	then
		echo "Loading KLIPS module:"
		depmod -a >/dev/null 2>&1 && modprobe ipsec
	fi

	# clear tables out in case dregs have been left over
	ipsec eroute --clear
	ipsec spi --clear

	# figure out debugging flags
	case "$IPSECklipsdebug" in
	'')	IPSECklipsdebug=none	;;
	esac
	echo "KLIPS debug \`$IPSECklipsdebug'"
	case "$IPSECklipsdebug" in
	none)	ipsec klipsdebug --none	;;
	all)	ipsec klipsdebug --all	;;
	*)	ipsec klipsdebug --none
		for d in $IPSECklipsdebug
		do
			ipsec klipsdebug --set $d
		done
		;;
	esac

	# figure out interfaces
	for i in $IPSECinterfaces
	do
		case "$i" in
		ipsec*=?*)	klipsinterface $i	;;
		*)	echo "$0: interface \`$i' not understood, ignored" >&2
			;;
		esac
	done
}

# set up a Klips interface
klipsinterface() {
	# pull apart the interface spec
	virt=`expr $1 : '\([^=]*\)=.*'`
	phys=`expr $1 : '[^=]*=\(.*\)'`

	# figure out ifconfig for interface
	eval `ifconfig $phys | sed 's/:/ /g' |
		awk '$1 == "inet" && $2 == "addr" && $6 == "Mask" {
			print "addr=" $3
			if ($4 == "Bcast")
				print "type=broadcast"
			else if ($4 == "P-t-P")
				print "type=pointopoint"
			else
				print "type="
			print "otheraddr=" $5
			print "mask=" $7
		}'`
	if test " $addr" = " "
	then
		echo "$0: unable to determine address of \`$phys'" >&2
		exit 1
	fi
	if test " $type" = " "
	then
		echo "$0: \`$phys' is of an unknown type, can't use it" >&2
		return
	fi
	echo "KLIPS $virt on $phys $addr/$mask $type $otheraddr"

	# attach the interface and bring it up
	ipsec tncfg --attach --virtual $virt --physical $phys
	ifconfig $virt inet $addr $type $otheraddr netmask $mask
}

# set up manually-keyed connections
manualconns() {
	if test " $IPSECmanualstart" != " "
	then
		for tu in $IPSECmanualstart
		do
			echo "Manually-keyed connection \`$tu':"
			ipsec manual $tu up
		done
	fi
}

# internal setup for Pluto start
plutofix() {
	# searches, if needed
	if test " $IPSECplutoload" = " %search"
	then
		eval `ipsec _confread --varprefix PLUTO --search auto add start`
		if test " $PLUTO_confreadstatus" != " "
		then
			echo "$PLUTO_confreadstatus (load search) -- aborting" |
									logit
			exit 1
		fi
		IPSECplutoload="$PLUTO_confreadnames"
	fi
	if test " $IPSECplutostart" = " %search"
	then
		eval `ipsec _confread --varprefix PLUTO --search auto start`
		if test " $PLUTO_confreadstatus" != " "
		then
			echo "$PLUTO_confreadstatus (start search) -- aborting" |
									logit
			exit 1
		fi
		IPSECplutostart="$PLUTO_confreadnames"
	fi

	# ensure plutoload is a superset of plutostart
	for s in $IPSECplutostart
	do
		found=
		for lo in $IPSECplutoload
		do
			if test " $lo" = " $s"
			then
				found=yes
			fi
		done
		if test ! "$found"
		then
			IPSECplutoload="$IPSECplutoload $s"
		fi
	done
}

# stall for any leftover connections that would impede Pluto startup
plutostall() {
	first=yes
	while `netstat -n | egrep '[ 	]127\.0\.0\.1:500[ 	].*TIME_WAIT' >/dev/null`
	do
		if test "$first"
		then
			echo "Waiting for dregs of old Pluto to time out (may take 60s or so)..."
			first=
		fi
		sleep 5
	done
}

# start Pluto daemon
plutodaemon() {
	# figure out debug options
	pd=
	for d in $IPSECplutodebug
	do
		pd="$pd --debug-$d"
	done

	# do it
	echo "Starting Pluto (debug \`$IPSECplutodebug'):"
	if test " $IPSECdump" != " yes"
	then
		ulimit -c 0			# preclude core dumps
	fi
	ipsec pluto $pd
}

# Pluto subsystem startup, after daemon is running
plutopost() {
	# database load
	if test " $IPSECplutoload" != " "
	then
		echo "Loading Pluto database \`$IPSECplutoload':"
		for tu in $IPSECplutoload
		do
			ipsec auto --add $tu
		done
	fi

	# enable listening
	echo "Enabling Pluto negotiation:"
	ipsec auto --ready

	# quickly establish routing
	if test " $IPSECplutostart" != " "
	then
		echo "Routing for Pluto conns \`$IPSECplutostart':"
		for tu in $IPSECplutostart
		do
			ipsec auto --route $tu
		done
	fi
}

# Pluto negotiation start (if any)
plutogo() {
	# tunnel initiation, which may take a while
	if test " $IPSECplutostart" != " "
	then
		async=
		p=
		if test " $IPSECplutowait" = " no"
		then
			async="--asynchronous"
			p=" (asynchronously)"
		fi
		for tu in $IPSECplutostart
		do
			echo "Initiating Pluto tunnel \`$tu'$p:"
			ipsec auto --up $async $tu
		done
	fi
}

# logging control
logit() {
	IPSECsyslog=${IPSECsyslog-daemon.error}
	logger -s -p $IPSECsyslog -t ipsec_setup 2>&1
}



# mainline code
case "$1" in
  start)
	# Start things rolling.
	# (Warning, changes to this log message may affect barf.)
	echo "Starting FreeS/WAN IPSEC..." | logit
	startklips 2>&1 | logit
	touch /var/lock/subsys/ipsec
	manualconns 2>&1 | logit
	if test " $IPSECpluto" != " no"
	then
		plutofix
		# plutostall 2>&1 | logit
		plutodaemon 2>&1 | logit
		sleep 1				# give it a moment to get going
		plutopost 2>&1 | logit
	fi
	if test " $IPSECforwardcontrol" = " yes"
	then
		echo "Forwarding up:" | logit
		echo 1 >/proc/sys/net/ipv4/ip_forward
	fi
	if test " $IPSECpluto" != " no"
	then
		plutogo 2>&1 | logit
	fi
	echo "...FreeS/WAN IPSEC started" | logit
	;;

  stop)
	# Shut things down.
	echo "Stopping FreeS/WAN IPSEC..." | logit
	if test " $IPSECforwardcontrol" = " yes"
	then
		echo "Forwarding down:" | logit
		echo 0 >/proc/sys/net/ipv4/ip_forward
	fi
	if test ! -f /var/run/pluto.pid
	then
		: nothing
	elif test ! -s /var/run/pluto.pid
	then
		echo "Removing empty pluto.pid -- pluto still running?" | logit
		rm -f /var/run/pluto.pid
	elif kill -0 `cat /var/run/pluto.pid` 2>/dev/null
	then
		echo "Shutting down Pluto:" | logit
		ipsec whack --shutdown | awk '$1 != "002"' | logit
		sleep 1			# general paranoia
		if test -s /var/run/pluto.pid
		then
			echo "Pluto did not go away!  Killing it:" | logit
			kill `cat /var/run/pluto.pid` 2>&1 | logit
			sleep 5
		fi
		rm -f /var/run/pluto.pid	# harmless if already gone
	else
		echo "Removing orphaned pluto.pid file:" | logit
		rm -f /var/run/pluto.pid
	fi
	for i in `ifconfig | awk '/^ipsec/ { print $1 }'`
	do
		echo "Taking $i down:" | logit
		ifconfig $i down 2>&1 | logit
		ipsec tncfg --detach --virtual $i 2>&1 | logit
	done
	echo "Misc cleanout:" | logit
	ipsec klipsdebug --none 2>&1 | logit
	ipsec eroute --clear 2>&1 | logit
	ipsec spi --clear 2>&1 | logit
	rmmod ipsec >/dev/null 2>&1
	rm -f /var/lock/subsys/ipsec
	echo "...FreeS/WAN IPSEC stopped" | logit
	;;

  restart)
	$0 stop
	$0 start
	;;

  --version)
	echo "1"
	exit 0
	;;

  --help)
	echo "Usage: $0 {start|stop|restart}"
	exit 0
	;;

  *)
	echo "Usage: $0 {start|stop|restart}" >&2
	exit 2
esac

exit 0
