#! /bin/sh
#
# default updown script for use with NETKEY(XFRM)
#
# Copyright (C) 2003-2004 Nigel Metheringham
# Copyright (C) 2002-2007 Michael Richardson <mcr@xelerance.com>
# Copyright (C) 2007-2008 Paul Wouters <paul@xelerance.com>
# Copyright (C) 2003-2020 Tuomo Soini <tis@foobar.fi>
# Copyright (C) 2011-2016 Paul Wouters <pwouters@redhat.com>
# Copyright (C) 2016 Antony Antony <antony@phenome.org>
#
# 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 <https://www.gnu.org/licenses/gpl2.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.

# CAUTION:  Installing a new version of Libreswan will install a new
# copy of this script, wiping out any custom changes you make.  If
# you need changes, make a copy of this under another name, and customize
# that, and use the (left/right)updown= parameters in ipsec.conf to make
# Libreswan use your modified updown script instead of this default one.

test ${IPSEC_INIT_SCRIPT_DEBUG} && set -v -x

LC_ALL=C
export LC_ALL

# Things that this script gets (from ipsec_pluto(8) man page)
#
#
#	PLUTO_VERSION
#		indicates  what  version of this interface is being
#		used.  This document describes version  1.1.   This
#		is upwardly compatible with version 1.0.
#
#	PLUTO_VERB
#		specifies the name of the operation to be performed
#		(prepare-host, prepare-client, up-host, up-client,
#		down-host, or down-client).  If the address family
#		for security gateway to security gateway
#		communications is IPv6, then a suffix of -v6 is added
#		to the verb.
#
#	PLUTO_CONNECTION
#		is the name of the  connection	for  which  we	are
#		routing.
#
#	PLUTO_CONN_POLICY
#		the policy of the connection, as in:
#		RSASIG+ENCRYPT+TUNNEL+PFS+DONTREKEY+OPPORTUNISTIC
#		+failureDROP+lKOD+rKOD
#
#	PLUTO_NEXT_HOP
#		is the next hop to which packets bound for the peer
#		must be sent.
#
#	PLUTO_INTERFACE
#		is the name of the real interace used for encrypted, IKE traffic
#
#	PLUTO_ME
#		is the IP address of our host.
#
#	PLUTO_METRIC
#		is the metric to set for the route
#
#	PLUTO_MTU
#		is the mtu to set for the route
#
#	PLUTO_MY_CLIENT
#		is the IP address / count of our client subnet.	 If
#		the  client  is	 just  the  host,  this will be the
#		host's own IP address / mask (where max	is  32	for
#		IPv4 and 128 for IPv6).
#
#	PLUTO_MY_CLIENT_NET
#		is the IP address of our client net.  If the client
#		is just the host, this will be the  host's  own	 IP
#		address.
#
#	PLUTO_MY_CLIENT_MASK
#		is  the	 mask for our client net.  If the client is
#		just the host, this will be 255.255.255.255.
#
#	PLUTO_MY_SOURCEIP
#		if non-empty, then the source address for the route will be
#		set to this IP address.
#
#	PLUTO_MY_PROTOCOL
#		is the protocol	 for this  connection.	Useful	for
#		firewalling.
#
#	PLUTO_MY_PORT
#		is the port. Useful for firewalling.
#
#	PLUTO_PEER
#		is the IP address of our peer.
#
#	PLUTO_PEER_CLIENT
#		is the IP address / count of the peer's client subnet.
#		If the client is just the peer, this will be
#		the peer's own IP address / mask (where	max  is	 32
#		for IPv4 and 128 for IPv6).
#
#	PLUTO_PEER_CLIENT_NET
#		is the IP address of the peer's client net.  If the
#		client is just the peer, this will  be	the  peer's
#		own IP address.
#
#	PLUTO_PEER_CLIENT_MASK
#		is  the	 mask  for  the	 peer's client net.  If the
#		client	 is   just   the   peer,   this	  will	 be
#		255.255.255.255.
#
#	PLUTO_PEER_PROTOCOL
#		is  the	 protocol  set	for  remote  end  with port
#		selector.
#
#	PLUTO_PEER_PORT
#		is the peer's port. Useful for firewalling.
#
#	PLUTO_CFG_CLIENT=0|1
#		is MODECFG or IKEv2 Config client.
#
#	PLUTO_CFG_SERVER=0|1
#		is MODECFG or IKEv2 Config server.
#
#	PLUTO_CONNECTION_TYPE
#
#	PLUTO_CONN_ADDRFAMILY
#		is the family type, "ipv4" or "ipv6"
#
#	PLUTO_PROTO_STACK
#		is the local IPsec kernel stack used, eg KLIPS, NETKEY,
#		MAST, NOSTACK
#
#	PLUTO_IS_PEER_CISCO=0|1
#		remote server type is cisco. Add support for cisco extensions
#		when used with xauth.
#
#	PLUTO_NM_CONFIGURED=0|1
#		is NetworkManager used for resolv.conf update
#
#	PLUTO_SA_REQID
#		When using KAME or XFRM/NETKEY, the IPsec SA reqid base value.
#		ESP/AH out is base, ESP/AH in = base + 1
#		IPCOMP is base + 2 plus for inbound + 1
#
#	PLUTO_SA_TYPE
#		The type of IPsec SA (ESP or AH)
#
#	PLUTO_USERNAME
#		The username (XAUTH or GSSAPI) that was authenticated (if any)
#		for this SA
#
# 	PLUTO_VIRT_INTERFACE
#		is the name of the real interace used for encrypted, IKE traffic
#
#
#	XAUTH_FAILED
#		If xauthfail=soft this will be set to 1 if XAUTH authentication
#		failed. If xauthfail=hard, the updown scripts never run.
#
#	CONNMARK
#		If mark= is set on the connection, this variable will be
#		set with the value. It can be used for iptables or VTI.
#
#	VTI_IFAC=iface
#		Name of VTI interface to create
#
#	VTI_ROUTING=yes|no
#		Whether or not to perform ip rule and ip route commands
#		covering the IPsec SA address ranges to route those packets
#		into the VTI_IFACE interface. This should be enabled unless
#		the IPsec SA covers 0.0.0.0/0 <-> 0.0.0.0/0
#
#	VTI_SHARED=yes|no
#		Whether or not more conns (or instances) share a VTI device.
#               If not shared, the VTI device is deleted when tunnel goes down.
#
#	SPI_IN / SPI_OUT
#		The inbound and outbound SPI's of the connection.

# rpm based systems
if [ -f /etc/sysconfig/pluto_updown ]; then
    . /etc/sysconfig/pluto_updown
# deb based systems
elif [ -f /etc/default/pluto_updown ]; then
    . /etc/default/pluto_updown
fi

BACKUP_RESOLV_CONF=/run/pluto/libreswan-resolv-conf-backup
ETC_RESOLV_CONF=/etc/resolv.conf

case "${PLUTO_CONN_ADDRFAMILY}" in
    ipv4)
	FAMILY=4
	MAX_CIDR=32
	SCOPE=50	# Use scope 50 to verify ip was added by addsource()
	;;
    ipv6)
	FAMILY=6
	MAX_CIDR=128
	SCOPE=global
	;;
    *)
	echo "unkown address family \"${PLUTO_CONN_ADDRFAMILY}\"" >&2
	exit 1
	;;
esac
export FAMILY MAX_CIDR SCOPE

# Ignore parameter custom
if [ "${1}" = "custom" ]; then
    shift
fi

while [ $# -gt 0 ]; do
    case ${1} in
	--route)
	    case ${2} in
		[Yy]*)
		    ROUTE=yes
		    PROXY_ARP_ROUTE=no
		    ;;
		*)
		    ROUTE=
		    PROXY_ARP_ROUTE=
		    ;;
	    esac
	    shift; shift
	    ;;
	--iproute)
	    IPRARGS="${2}"
	    shift; shift
	    ;;
	*)
	    echo "$0: Unknown argument \"${1}\"" >&2
	    exit 1
	    ;;
    esac
done

# utility functions for route manipulation
# Meddling with this stuff should not be necessary and requires great care.
uproute() {
    doproxyarp add
    doroute replace
}

downroute() {
    doroute del
    doproxyarp delete
}

downrule() {
    if [ -n "${PLUTO_MY_SOURCEIP}" -a 0${PLUTO_IS_PEER_CISCO} -eq 1 ]; then
	doroute del
    fi
}

updateresolvconf() {
    local domain
    local nameserver
    local new_nameserver
    local new_resolv_conf
    local new_search
    local orig_domain
    local orig_nameserver
    local rc
    rc=0
    if [ 0${PLUTO_CFG_CLIENT} -eq 0 ]; then
	return ${rc}
    fi
    if [ -n "$(pidof unbound)" -a \
	-n "${PLUTO_PEER_DNS_INFO}" -a \
	-n "${PLUTO_PEER_DOMAIN_INFO}" ]
    then
	for domain in ${PLUTO_PEER_DOMAIN_INFO}; do
	    echo "updating local nameserver for ${domain} with ${PLUTO_PEER_DNS_INFO}"
	    unbound-control forward_add ${domain} \
		${PLUTO_PEER_DNS_INFO}
	    unbound-control flush_zone ${domain}
	    unbound-control flush_requestlist
	done
	rc=$?
    elif [ 0${PLUTO_NM_CONFIGURED} -eq 0 -a \
	-n "${PLUTO_PEER_DNS_INFO}" ]
    then
	echo "updating resolvconf"

	if [ ! -e "${ETC_RESOLV_CONF}" ]; then
	    echo "resolv.conf does not exist, so doing nothing"
	    return 0
	fi

	if [ -e "${BACKUP_RESOLV_CONF}" ]; then
	    if grep -q Libreswan "${ETC_RESOLV_CONF}"; then
		echo "Current resolv.conf is generated by Libreswan, and backup resolv.conf already exists, so doing nothing"
		return 0
	    else
		echo "backup resolv.conf exists, but current resolv.conf is not generated by Libreswan"
	    fi
	fi

	rm -f -- "${BACKUP_RESOLV_CONF}"
	cp -- "${ETC_RESOLV_CONF}" "${BACKUP_RESOLV_CONF}"

	new_resolv_conf="# Generated by Libreswan (IPsec)"

	orig_domain="$(grep ^domain "${ETC_RESOLV_CONF}" 2>/dev/null | \
	    awk '{ print $2 }')"

	orig_search=$(grep ^search "${ETC_RESOLV_CONF}" 2>/dev/null | \
	    sed 's/^search[[:space:]]\+//;s/[[:space:]]*\#.*//')

	if [ -n "${orig_domain}" ]; then
	    new_resolv_conf="${new_resolv_conf}
domain ${orig_domain}"
	fi

	if [ -n "${orig_search}" ]; then
	    new_search="${orig_search}"
	elif [ -n "${orig_domain}" ]; then
	    new_search="${orig_domain}"
	fi

	if [ -n "${PLUTO_PEER_DOMAIN_INFO}" ]; then
	    if [ -n "${new_search}" ]; then
		new_search=$(echo $(echo "${new_search} ${PLUTO_PEER_DOMAIN_INFO}" | tr [:space:] '\n' | awk '!a[$0]++'))
	    else
		new_search="${PLUTO_PEER_DOMAIN_INFO}"
	    fi
	fi

	if [ -n "${new_search}" ]; then
	    new_resolv_conf="${new_resolv_conf}
search ${new_search}"
	fi

	orig_nameserver=$(grep -m 1 ^nameserver "${ETC_RESOLV_CONF}" | \
	    sed 's/^nameserver[[:space:]]\+//;s/[[:space:]]*\#.*//')
	if [ -n "${orig_nameserver}" ]; then
	    new_nameserver=$(echo $(echo "${PLUTO_PEER_DNS_INFO} ${orig_nameserver}" | tr [:space:] '\n' | awk '!a[$0]++'))
	else
	    new_nameserver="${PLUTO_PEER_DNS_INFO}"
	fi

	for nameserver in ${new_nameserver}; do
	    new_resolv_conf="${new_resolv_conf}
nameserver ${nameserver}"
	done

	echo "${new_resolv_conf}" > "${ETC_RESOLV_CONF}"
	rc=$?
    fi
    return ${rc}
}

restoreresolvconf() {
    local domain
    local rc
    rc=0
    if [ 0${PLUTO_CFG_CLIENT} -eq 0 ]; then
	return ${rc}
    fi
    if [ -n "$(pidof unbound)" -a \
	-n "${PLUTO_PEER_DNS_INFO}" -a \
	-n "${PLUTO_PEER_DOMAIN_INFO}" ]
    then
	for domain in ${PLUTO_PEER_DOMAIN_INFO}; do
	    echo "flushing local nameserver of ${domain}"
	    unbound-control forward_remove ${domain}
	    unbound-control flush_zone ${domain}
	    unbound-control flush_requestlist
	done
	rc=$?
    elif [ 0${PLUTO_NM_CONFIGURED} -eq 0 ]; then
	# We only restore if current resolv.conf is made by us.
	if grep -q Libreswan "${ETC_RESOLV_CONF}" 2>/dev/null; then
	    # And if there is a backup...
	    if [ -e "${BACKUP_RESOLV_CONF}" ]; then
		echo "restoring resolvconf"
	    else
		return 0
	    fi
	    cp -- "${BACKUP_RESOLV_CONF}" "${ETC_RESOLV_CONF}"
	fi
	rm -f -- "${BACKUP_RESOLV_CONF}"
	rc=0
    fi
    return ${rc}
}

notifyNM() {
    # This will be called whenever a connection is established or
    # fails to establish (either phase 1, xauth phase, or phase 2)
    # or whenever an already established connection is being terminated.
    # This will send a singal to NetworkManager over dbus so that NM
    # can keep track of the coonnections.

    if [ 0${PLUTO_NM_CONFIGURED} -eq 1 ]; then
	echo "sending $1 signal to NetworkManager"
	libreswan_reason=$1
	export libreswan_reason
	export PLUTO_PEER_DOMAIN_INFO
	export PLUTO_PEER_DNS_INFO
	export PLUTO_PEER_BANNER
	export PLUTO_MY_SOURCEIP
	export PLUTO_PEER
	[ -x /usr/libexec/nm-libreswan-service-helper ] && \
	    /usr/libexec/nm-libreswan-service-helper
    fi
    return 0
}

addsource() {
    local interface
    local st
    interface=lo
    st=0

    if [ -z "${PLUTO_MY_SOURCEIP}" ]; then
        return ${st}
    fi
    # check if given sourceip is local and add as alias if not
    if ! ip -${FAMILY} -o route get ${PLUTO_MY_SOURCEIP} | grep -q ^local; then
	if [ -n "${VTI_IFACE}" -a "${VTI_ROUTING}" = yes ]; then
	    interface="${VTI_IFACE}"
	elif [ -n "${PLUTO_XFRMI_ROUTE}" ]; then
	    interface=${PLUTO_VIRT_INTERFACE}
	fi
	it="ip addr add ${PLUTO_MY_SOURCEIP}/${MAX_CIDR} dev ${interface} scope ${SCOPE}"
	oops="$(eval ${it} 2>&1)"
	st=$?
	if [ -z "${oops}" -a ${st} -ne 0 ]; then
	    oops="silent error, exit status ${st}"
	fi
	case "${oops}" in
	    'RTNETLINK answers: File exists'*)
		# should not happen, but ... ignore if the
		# address was already assigned on interface
		oops=""
		st=0
		;;
	esac
	if [ -n "${oops}" -o ${st} -ne 0 ]; then
	    echo "$0: addsource \"${it}\" failed (${oops})" >&2
	fi
    fi
    return ${st}
}

delsource() {
    local interface
    local oops
    local st
    interface=lo
    st=0
    if [ -z "${PLUTO_MY_SOURCEIP}" ]; then
        return ${st}
    fi
    # Remove source ip if it's not used any more.
    if [ -z "$(ip -${FAMILY} -o route list src ${PLUTO_MY_SOURCEIP})" ]; then
	if [ -n "${VTI_IFACE}" -a "${VTI_ROUTING}" = yes ]; then
	    interface="${VTI_IFACE}"
	elif [ -n "${PLUTO_XFRMI_ROUTE}" ]; then
	    interface=${PLUTO_VIRT_INTERFACE}
	fi
	# If there is no ip we just return
	if ! ip -${FAMILY} -o addr list dev ${interface} scope ${SCOPE} | \
	    grep -q ${PLUTO_MY_SOURCEIP}/${MAX_CIDR}
	then
	    return ${st}
	fi

	if [ -n "${PLUTO_MOBIKE_EVENT}" ] ; then
		return ${st}
	fi

	it="ip -${FAMILY} addr del ${PLUTO_MY_SOURCEIP}/${MAX_CIDR} dev ${interface}"
	oops="$(eval ${it} 2>&1)"
	st=$?
	if [ -z "${oops}" -a ${st} -ne 0 ]; then
	    oops="silent error, exit status ${st}"
	fi
	case "${oops}" in
	    'RTNETLINK answers: File exists'*)
		# should not happen, but ... ignore if the
		# address was already assigned on interface
		oops=""
		st=0
		;;
	    'RTNETLINK answers: Cannot assign'*)
		# Address is not there to remove or is there with different
		# netmask and in that case we must not remove it so we ignore
		# the error.
		oops=""
		st=0
		;;
	esac
	if [ -n "${oops}" -o ${st} -ne 0 ]; then
	    echo "$0: delsource \"${it}\" failed (${oops})" >&2
	fi
    fi
    return ${st}
}

doproxyarp() {
    local cmd
    local iface
    cmd=${1}
    # Check if client has a single ip only client net
    if [ ${PLUTO_PEER_CLIENT#*/} = ${MAX_CIDR} ]; then
	# Skip OE special connections and direct host-host connections
	if [ "${PLUTO_PEER_CLIENT_NET}" = "0.0.0.0" -o \
	    "${PLUTO_PEER_CLIENT_NET}" = "::" -o \
	    "${PLUTO_PEER_CLIENT_NET}" = "${PLUTO_PEER}" -o \
	    "${PLUTO_MY_CLIENT_NET}" = "${PLUTO_ME}" ]
	then
	    return 0
	fi
	# check if client is routeable
	if ip -${FAMILY} -o route get ${PLUTO_PEER_CLIENT_NET} 2>/dev/null | \
	    grep -E -q -s -v " via |^local"
	then
	    iface=$(ip -${FAMILY} -o route get ${PLUTO_PEER_CLIENT_NET} 2>/dev/null | \
		awk '{print $3}')
	    if [ -r /sys/class/net/${iface}/address ]; then
		macaddr=$(cat /sys/class/net/${iface}/address)
	    fi
	    # add/remove arp entry for the client on ethernet devices only
	    if [ -n "${macaddr}" ]; then
		if [ "${cmd}" = "add" ]; then
		    ip -${FAMILY} neigh add proxy ${PLUTO_PEER_CLIENT_NET} dev ${iface} \
			lladdr ${macaddr} nud permanent
		    # Force routing, required for proxyarp to work
		    PROXY_ARP_ROUTE=yes
		    export PROXY_ARP_ROUTE
		else
		    ip -${FAMILY} neigh del proxy ${PLUTO_PEER_CLIENT_NET} dev ${iface}
		fi
	    fi
	fi
    fi
}

do_ip()
{
    local cmd="$1"

    oops="$(eval ${cmd} 2>&1)"
    st=$?

    if [ -z "${oops}" -a ${st} -ne 0 ]; then
	oops="silent error, exit status ${st}"
    fi

    case "${oops}" in
	'RTNETLINK answers: No such process'*)
	    # should not happen, but ... ignore if the
	    # route was already removed
	    oops=""
	    st=0
	    ;;
    esac

    if [ -n "${oops}" -a ${st} -ne 0 ]; then
	echo "$0: doroute \"${cmd}\" failed (${oops})" >&2
    fi

    return ${st}
}

doroute() {
    local cmd
    local esp_nexthop
    local esp_peer_interface
    local espipro
    local ipru
    local route_table
    local oops
    local parms
    local parms2
    local st
    local xfrmi_route
    local xfrmi_rule
    cmd=${1}
    route_table=50
    st=0
    xfrmi_route="${PLUTO_XFRMI_ROUTE}"

    if [ ${cmd} != del ]; then
	oops="$(ip -${FAMILY} route get ${PLUTO_PEER_CLIENT_NET} 2>&1)"
	case "${oops}" in
	    'RTNETLINK answers: No route to host'*)
		if [ -z "${PLUTO_XFRMI_ROUTE}" ]; then
		    ROUTE=yes	# Routing is mandatory for IPsec
		fi
		;;
	esac
    fi

    if [ -n "${PLUTO_XFRMI_FWMARK}" ]; then
	xfrmi_rule=yes	# we have to add "ip rules" and "ip route table"
	ROUTE=no	# xfrmi_route will add the route
    fi

    # skip routing if it's not enabled or necessary
    if [ -z "${PLUTO_MY_SOURCEIP}" -a \
	-z "${PLUTO_MTU}" -a \
	"${PROXY_ARP_ROUTE}" != yes -a \
	"${cmd}" != "del" ]
    then
	PROXY_ARP_ROUTE=no
    fi

    if [ -n "${PLUTO_MY_SOURCEIP}" -o -n "${PLUTO_MTU}" ]; then
	ROUTE=yes
    fi

    if [ "${PLUTO_PEER_CLIENT}" =  "${PLUTO_MY_CLIENT}" -a \
	"${PLUTO_XFRMI_ROUTE}" = yes ]
    then
	xfrmi_route="samesubnets";
	echo "leftsubet == rightsubnet = ${PLUTO_PEER_CLIENT} can not add route"
    fi

    parms="${PLUTO_PEER_CLIENT}"
    parms2=${IPRARGS}
    # nexthop is not needed on ppp interfaces. unset it to make cases
    # work, where left is set but no leftnexthop (e.g. left=%defaultroute)
    if ip link show "${PLUTO_INTERFACE%:*}" | grep -q POINTOPOINT; then
	POINTPOINT=yes
    fi
    # use nexthop if nexthop is not %direct and POINTPOINT is not set
    if [ "${PLUTO_NEXT_HOP}" != "${PLUTO_PEER}" -a -z "${POINTPOINT}" ]; then
	# XFRM interface needs no nexthop
	if [ -z "${PLUTO_XFRMI_ROUTE}"  ]; then
	   parms2="via ${PLUTO_NEXT_HOP}"
	fi
	esp_nexthop="via ${PLUTO_NEXT_HOP} "
    fi
    # route via proper interface according to routing table
    if [ "${cmd}" = "del" ]; then
	case "${PLUTO_PEER_CLIENT}" in
	    "0.0.0.0/0")
		# in case of default route we use half routes
		peer_interface=$(ip -${FAMILY} -o route list exact 0.0.0.0/1 | \
		    sed "s/^.*dev \([^ ]*\) .*/\1/")
		;;
	    "::/0")
		# in case of default route we use half routes
		peer_interface=$(ip -${FAMILY} -o route list exact 2000::/3 | \
		    sed "s/^.*dev \([^ ]*\) .*/\1/")
		;;
	    *)
		peer_interface=$(ip -${FAMILY} -o route get ${PLUTO_PEER_CLIENT_NET} | \
		    sed "s/^.*dev \([^ ]*\) .*/\1/")
		;;
	esac
    else
	peer_interface=$(ip -o route get ${PLUTO_NEXT_HOP} | \
	    sed "s/^.*dev \([^ ]*\) .*/\1/")
    fi

    esp_peer_interface=$(ip -${FAMILY} -o route get ${PLUTO_NEXT_HOP} \
	from ${PLUTO_ME} | sed "s/^.*\(dev [^ ]*\) .*/\1/")
    if [ -z "${esp_peer_interface}" ]; then
	esp_peer_interface="dev ${PLUTO_INTERFACE}"
    fi

    if [ -z "${peer_interface}" ]; then
	peer_interface=${PLUTO_INTERFACE}
    fi

    if [ "${PLUTO_XFRMI_ROUTE}" = "yes" ]; then
	peer_interface=${PLUTO_VIRT_INTERFACE}
    fi

    if [ -n "${VTI_IFACE}" ]; then
	addsource
	peer_interface="${VTI_IFACE}"
    fi

    parms2="${parms2}${PLUTO_MTU:+ mtu ${PLUTO_MTU}}"
    parms2="${parms2}${PLUTO_METRIC:+ metric ${PLUTO_METRIC}} ${IPROUTEARGS}"

    parms2="${parms2} dev ${peer_interface%:*}"

    # make sure whe have sourceip locally in this machine
    if [ "${cmd}" = "replace" -a -n "${PLUTO_MY_SOURCEIP}" ]; then
	addsource
	# use sourceip as route default source
	parms2="${parms2} src ${PLUTO_MY_SOURCEIP}"
    fi

    case "${PLUTO_PEER_CLIENT}" in
	"0.0.0.0/0")
	    # need to provide route that eclipses default, without
	    # replacing it.
	    it="ip -${FAMILY} route ${cmd} 0.0.0.0/1 ${parms2} && \
		ip -${FAMILY} route ${cmd} 128.0.0.0/1 ${parms2}"
	    ;;
	"::/0")
	    # need to provide route that eclipses default, without
	    # replacing it.
	    it="ip -${FAMILY} route ${cmd} 2000::/3 ${parms2}"
	    ;;
	*)
	    it="ip -${FAMILY} route ${cmd} ${parms} ${parms2}"
	    ;;
    esac

    if [ "${ROUTE}" = yes -o \
	"${xfrmi_route}" = yes -o \
	"${PROXY_ARP_ROUTE}" = yes ]
    then
	do_ip "${it}"
	st=$?
	if [ ${st} -ne 0 ]; then
	    return ${st}
	fi
    fi

    if [ "${xfrmi_rule}" = "yes" ]; then
	espipro="ip -${FAMILY} route ${cmd} ${PLUTO_PEER}/${MAX_CIDR} ${esp_nexthop} ${esp_peer_interface%:*} table ${route_table}"
	do_ip "${espipro}"

	st=$?
        if [ ${st} -ne 0 ]; then
            return ${st}
        fi

	iprulecmd="${cmd}"
	if [ "${cmd}" = "replace" ]; then
	    iprulecmd="add"
	fi

	ipru="ip -${FAMILY} rule ${iprulecmd} prio 100 to ${parms}"
	ipru="${ipru} fwmark ${PLUTO_XFRMI_FWMARK} lookup ${route_table}"

	do_ip "${ipru}"
	st=$?
	if [ ${st} -ne 0 ]; then
	    return ${st}
	fi

    fi
    return 0
}

# TODO: We need to specify CIDR mask but our _MASK variables are in old school format
# TODO: Exclude udp 4500 traffic
addnflog() {
    if [ -n "${NFLOG}" ]; then
	iptables -I OUTPUT -m policy --dir out --pol ipsec \
	    -s ${PLUTO_MY_CLIENT} -d ${PLUTO_PEER_CLIENT} \
	    -j NFLOG --nflog-group ${NFLOG} --nflog-prefix ${PLUTO_CONNECTION}
	iptables -I INPUT  -m policy --dir in --pol ipsec \
	    -s ${PLUTO_PEER_CLIENT} -d ${PLUTO_MY_CLIENT} \
	    -j NFLOG --nflog-group ${NFLOG} --nflog-prefix ${PLUTO_CONNECTION}
    fi
}

delnflog() {
    if [ -n "${NFLOG}" ]; then
	iptables -D OUTPUT -m policy --dir out --pol ipsec \
	    -s ${PLUTO_MY_CLIENT} -d ${PLUTO_PEER_CLIENT} \
	    -j NFLOG --nflog-group ${NFLOG} --nflog-prefix ${PLUTO_CONNECTION}
	iptables -D INPUT  -m policy --dir in --pol ipsec \
	    -s ${PLUTO_PEER_CLIENT} -d ${PLUTO_MY_CLIENT} \
	    -j NFLOG --nflog-group ${NFLOG} --nflog-prefix ${PLUTO_CONNECTION}
    fi
}

addvtiiface() {
    if [ -n "${VTI_IFACE}" ]; then
	if [ -z "${CONNMARK_IN}" -o -z "${CONNMARK_OUT}" ]; then
	    echo "vti-interface option ignored because no mark was configured"
	else
	    if [ ! -d "/proc/sys/net/ipv4/conf/${VTI_IFACE}" ]; then
		# echo "creating vti interface"
		vtipeer="${PLUTO_PEER}"
		if [ "${PLUTO_CONN_KIND}" = CK_INSTANCE -o "${VTI_SHARED}" = "yes" ]; then
		    vtipeer="0.0.0.0"
		fi
		ip tunnel add ${VTI_IFACE} mode vti local ${PLUTO_ME} \
		    remote ${vtipeer} okey ${CONNMARK_OUT%/*} \
		    ikey ${CONNMARK_IN%/*}
		sysctl -w net.ipv4.conf.${VTI_IFACE}.disable_policy=1
		sysctl -w net.ipv4.conf.${VTI_IFACE}.rp_filter=0
		sysctl -w net.ipv4.conf.${VTI_IFACE}.forwarding=1
		if [ -n "${VTI_IP}" ]; then
		   ip addr add ${VTI_IP} dev ${VTI_IFACE}
		fi
		ip link set ${VTI_IFACE} up
	    else
		# check there was no conflict if we are sharing - might be sensitive to /sbin/ip differences
		if [ "${VTI_SHARED}" = yes ]; then
		    #test: ip/ip remote 3.4.5.6 local 1.2.3.4 ttl inherit key 5
		    cur="$(ip tun show ${VTI_IFACE})"
		    new="${VTI_IFACE}: ip/ip  remote any  local ${PLUTO_ME}  ttl inherit  key ${CONNMARK_OUT%/*}"
		    if [ "${cur}" != "${new}" ]; then
			echo "vti interface \"${VTI_IFACE}\" already exists with conflicting setting"
			echo "existing: ${cur}"
			echo "wanted  : ${new}"
		    else
			# temp debug
			echo "vti interface already exists with identical parameters, OK"
		    fi
		else
		    echo "vti interface \"${VTI_IFACE}\" already exists with conflicting setting (perhaps need vti-sharing=yes ?"
		fi
	    fi
	fi
    fi
}

addvti() {
    if [ -n "${VTI_IFACE}" ]; then
	if [ -z "${CONNMARK_IN}" -o -z "${CONNMARK_OUT}" ]; then
	    echo "vti-interface option ignored because no mark was configured"
	else
	    if [ "${VTI_ROUTING}" = yes ]; then
		# Tuomo should improve this with using ${PLUTO_MY_CLIENT_NET}
		# echo "setting up vti routing"
		r=add
		ip route list | grep -q "${PLUTO_PEER_CLIENT%/*}" && r=change
		if [ "${r}" = change ]; then
		    # resolve LAN conflict by forcing host route for default gw
		    gw="$(ip ro li | grep ^default | awk '{ print $3;}')"
		    gwdev="$(ip ro li | grep ^default | awk '{ print $5;}')"
		    # echo "ip route add ${gw} dev ${gwdev}"
		    ip route add ${gw} dev ${gwdev} >/dev/null ||:
		fi
		srcip=""
		if [ -n "${PLUTO_MY_SOURCEIP}" ]; then
		    srcip=" src ${PLUTO_MY_SOURCEIP}"
		fi
		# echo "ip route ${r} ${PLUTO_PEER_CLIENT} dev ${VTI_IFACE} ${srcip}"
		ip route ${r} ${PLUTO_PEER_CLIENT} dev ${VTI_IFACE} ${srcip}
		echo "done ip route"
	    fi
	fi
    fi
}

delvti() {
    if [ -n "${VTI_IFACE}" -a -d /proc/sys/net/ipv4/conf/${VTI_IFACE} ]; then
	if [ "${VTI_ROUTING}" = yes ]; then
	    ip route del ${PLUTO_PEER_CLIENT} dev ${VTI_IFACE} \
		src ${PLUTO_MY_SOURCEIP} ||:
	fi
	# TODO: we can't delete vti interface because we don't have proper reference
	# counting.
	#if [ "${VTI_SHARED}" = no -a "${PLUTO_CONN_KIND}" != CK_INSTANCE ]; then
	#	ip tun del ${VTI_IFACE} ||:
	#fi
   fi
}

# Client Address Translation CAT
addcat() {
    if [ -n "${CAT}" ] && [ "${PLUTO_MY_CLIENT_NET}" != "0.0.0.0" ] ; then
	iptables -t nat -I POSTROUTING -m policy --dir out --pol ipsec \
	    -d ${PLUTO_PEER_CLIENT} -j SNAT --to-source ${PLUTO_MY_CLIENT_NET}
	iptables -t nat -I PREROUTING -m policy --dir in --pol ipsec \
	    -d ${PLUTO_MY_CLIENT_NET} -s ${PLUTO_PEER_CLIENT} \
	    -j DNAT --to-destination ${PLUTO_ME}
    fi
}

delcat() {
    if [ -n "${CAT}" ]; then
	iptables -t nat -D PREROUTING -m policy --dir in --pol ipsec  \
	    -d ${PLUTO_MY_CLIENT_NET} -s ${PLUTO_PEER_CLIENT} \
	    -j DNAT --to-destination ${PLUTO_ME}
	iptables -t nat -D POSTROUTING -m policy --dir out --pol ipsec \
	    -d ${PLUTO_PEER_CLIENT} -j SNAT --to-source ${PLUTO_MY_CLIENT_NET}
    fi
}

# the big choice
case "${PLUTO_VERB}" in
    prepare-host|prepare-client)
	addvtiiface
	;;
    route-host|route-client)
	# connection to me or my client subnet being routed
	addvti
	uproute
	addnflog
	;;
    unroute-host|unroute-client)
	# connection to me or my client subnet being unrouted
	downroute
	delsource
	;;
    up-host)
	# connection to me coming up
	# If you are doing a custom version, firewall commands go here.
	;;
    down-host)
	# connection to me going down
	downrule
	delnflog
	delcat
	delvti
	# If you are doing a custom version, firewall commands go here.
	;;
    up-client)
	# connection to my client subnet coming up
	addvtiiface
	updateresolvconf
	addcat
	addsource
	notifyNM connect
	addvti
	# If you are doing a custom version, firewall commands go here.
	;;
    down-client)
	# connection to my client subnet going down
	downrule
	delnflog
	delcat
	delvti
	restoreresolvconf
	notifyNM disconnect
	# If you are doing a custom version, firewall commands go here.
	;;
    #
    # IPv6
    #
    prepare-host-v6|prepare-client-v6)
	# prepare client for connection
	;;
    route-host-v6|route-client-v6)
	# connection to me or my client subnet being routed
	uproute
	;;
    unroute-host-v6|unroute-client-v6)
	# connection to me or my client subnet being unrouted
	downroute
	delsource
	;;
    up-host-v6)
	# connection to me coming up
	# If you are doing a custom version, firewall commands go here.
	;;
    down-host-v6)
	# connection to me going down
	# If you are doing a custom version, firewall commands go here.
	;;
    up-client-v6)
	# connection to my client subnet coming up
	addsource
	updateresolvconf
	notifyNM connect
	# If you are doing a custom version, firewall commands go here.
	;;
    down-client-v6)
	# connection to my client subnet going down
	restoreresolvconf
	notifyNM disconnect
	# If you are doing a custom version, firewall commands go here.
	;;
    *)	echo "$0: unknown verb \"${PLUTO_VERB}\" or parameter \"${1}\"" >&2
	exit 1
	;;
esac
