#!/bin/sh
# Netkey startup script
# Copyright (C) 2007 Ken Bantoft <ken@xelerance.com>
# Copyright (C) 2007 - 2008 Paul Wouters <paul@xelerance.com>
# Copyright (C) 2008 Tuomo Soini <tis@foobar.fi>
# 
# 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.
#

test $IPSEC_INIT_SCRIPT_DEBUG && set -v -x

me='ipsec _startnetkey'		# for messages

modules=/proc/modules
kamepfkey=/proc/net/pfkey
ipsecpfkey=/proc/net/ipsec/version

info=/dev/null
log=daemon.error
for dummy
do
	case "$1" in
	--log)		log="$2" ; shift	;;
	--info)		info="$2" ; shift	;;
	--debug)	debug="$2" ; shift	;;
	--omtu)		omtu="$2" ; shift	;;
	--fragicmp)	fragicmp="$2" ; shift	;;
	--hidetos)	hidetos="$2" ; shift	;;
	--)	shift ; break	;;
	-*)	echo "$me: unknown option \`$1'" >&2 ; exit 2	;;
	*)	break	;;
	esac
	shift
done


logger -p $log -t ipsec_setup "Using NETKEY(XFRM) stack"


# log only to syslog, not to stdout/stderr
logonly() {
	logger -p $log -t ipsec_setup
}

defaultinterface() {
	local getphysdev getifaddr addr phys next multiaddr multidefault
	addr=
	phys=
	next=
	multiaddr=
	multidefault=

	getphysdev() {
	    local phys next addr
	    phys=
	    next=
	    addr=
	    while test $# -gt 0
	      do
	      case $1 in
		  dev)
		      phys=$2
		      shift; shift;
		      ;;
		  via)
		      next=$2
		      shift; shift;
		      ;;
		  src)
		      addr=$2
		      shift; shift
		      ;;
		  *)
		      shift
		      ;;
	      esac
	    done
	    echo "phys=$phys"
	    echo "next=$next"
	    echo "addr=$addr"
	}

	getifaddr() {
	    local addr peer
	    addr=
	    peer=
	    while test $# -gt 0
	      do
	      case $1 in
		  inet)
		      addr=$2
		      shift; shift
		      ;;
		  peer)
		      peer=$2
		      shift; shift
		      ;;
		  *)
		      shift
		      ;;
	      esac
	    done
	    if test -n "$peer"
	    then
		echo "next=${peer%/*}"
	    fi
	    echo "addr=${addr%/*}"
	}

	iproutedefault=`ip route list 0.0.0.0/0`
	if test -n "$iproutedefault"
	then
	  if test `echo "$iproutedefault" | wc -l` -gt 1
	  then
	    multidefault=1
	    iproutedefault=`echo "$iproutedefault" | head -1`
	  fi
	  eval `getphysdev $iproutedefault`
	  if test -z "$next" -o -z "$addr" -a -n "$phys"
	  then
	    ipaddrlist=`ip -o -f inet addr list dev $phys` 
	    if test `echo "$ipaddrlist" | wc -l` -gt 1
	    then
	      multiaddr=1
	      ipaddrlist=`echo "$ipaddrlist" | head -1`
	    fi
	    eval `getifaddr $ipaddrlist`
	  fi
	  if test -n "$multidefault"
	  then
	    echo "multiple default routes, using $next on $phys"
	  fi
	  if test -n "$multiaddr"
	  then
	    echo "multiple ip addresses, using  $addr on $phys"
	  fi
	else
	  echo "no default routes detected"
	fi
	# simulate old silly output
	echo "defaultroutephys=$phys" >> $info
	echo "defaultroutevirt=none" >> $info
	echo "defaultrouteaddr=$addr" >> $info
	echo "defaultroutenexthop=$next" >> $info
}

# main line

# since klips/mast now calls _startklips, we know we're called to use netkey
# so if we find klips/mast, try to unload it or die
if test -f $ipsecpfkey
then
	echo "Warning: found KLIPS/MAST stack loaded"
	echo -n Trying to unload KLIPS/MAST module:
	rmmod ipsec 2> /dev/null

	if test -f $ipsecpfkey
	then
		echo "FAILURE to unload KLIPS/MAST module. Use protostack=klips or protostack=mast, or recompile kernel without builtin KLIPS/MAST support"
                exit 1
        fi
fi

	if test -f $modules
	then
		# load hardware random related modules - some changed names over time
		modprobe -q hw_random 2>/dev/null
		modprobe -q hwrng 2>/dev/null
		modprobe -q amd-rng 2>/dev/null
		modprobe -q intel-rng 2>/dev/null

		# load all NETKEY modules
		for mod in ipcomp6 ipcomp xfrm6_tunnel xfrm6_mode_tunnel xfrm6_mode_beet xfrm6_mode_ro \
			xfrm6_mode_transport xfrm4_mode_transport xfrm4_mode_tunnel \
			xfrm4_tunnel xfrm4_mode_beet esp4 esp6 ah4 ah6 iaf_key
		   do
			#echo -n "$mod "
			modprobe -q $mod 2> /dev/null
		   done

		# xfrm_user is the old name for xfrm4_tunnel - backwards compatibility
		modprobe -q xfrm_user 2> /dev/null

		# padlock must load before aes module
		# padlock-aes must load before padlock-sha for some reason
		modprobe -q padlock 2>/dev/null
		modprobe -q padlock-aes 2>/dev/null
		modprobe -q padlock-sha 2>/dev/null

		# load the most common ciphers/algo's
		# aes-x86_64 has higher priority in via crypto api
		for crypto in aesni_intel es-x86_64 geode-aes aes aes_generic des sha512 sha256 md5 cbc xcbc ecb twofish blowfish serpent ccm gcm \
				ctr cts deflate cast5 cast6 lzo sha256_generic sha512_generic camellia
		   do
			#echo -n "$crypto "
			modprobe -q $crypto 2> /dev/null
		   done
	fi


	if ip xfrm state > /dev/null 2>&1
	then
		ip xfrm state flush
		ip xfrm policy flush
	elif type setkey > /dev/null 2>&1
	then
	 	# Check that the setkey command is available.
         	setkeycmd= 	 
         	PATH=$PATH:/usr/local/sbin 	 
         	for dir in `echo $PATH | tr ':' ' '` 	 
         	do 	 
                	if test -f $dir/setkey -a -x $dir/setkey 	 
                 	then
                         	setkeycmd=$dir/setkey
                         	break                   # NOTE BREAK OUT 
                	fi
         	done
        	$setkeycmd -F
        	$setkeycmd -FP
	else
	
        	echo "WARNING: cannot flush state/policy database -- \`$1'. Install a newer version of iproute/iproute2 or install the ipsec-tools package to obtain the setkey command." |
                	logger -s -p daemon.error -t ipsec_setup
	fi

# figure out interfaces for defaultroute
for i
do
        case "$i" in
        %defaultroute)  defaultinterface        ;;
        *)      echo "interface \`$i' not understood"
                exit 1
                ;;
        esac
done

exit 0
