#!/bin/bash
#
# L2TPv3 tunnel between 2 hosts
#
#            host-1          |   router   |     host-2
#                            |            |
#      lo          l2tp      |            |      l2tp           lo
#  fc00:101::1   fc00:1::1   |            |   fc00:1::2    fc00:101::2
#                  eth0      |            |     eth0
#              2001:db8:1::1 |            | 2001:db8:2::1

which ping6 > /dev/null 2>&1 && ping6=$(which ping6) || ping6=$(which ping)

################################################################################
# create namespaces and interconnects

create_ns()
{
	local ns=$1
	local addr=$2
	local addr6=$3

	[ -z "${addr}" ] && addr="-"
	[ -z "${addr6}" ] && addr6="-"

	ip netns add ${ns}

	ip -netns ${ns} link set lo up
	if [ "${addr}" != "-" ]; then
		ip -netns ${ns} addr add dev lo ${addr}
	fi
	if [ "${addr6}" != "-" ]; then
		ip -netns ${ns} -6 addr add dev lo ${addr6}
	fi

	# Eliminate the need for sleep after configuring IPs
	# Has no effect on this test case
	ip netns exec ${ns} sysctl -qw net.ipv6.conf.default.accept_dad=0

	ip -netns ${ns} ro add unreachable default metric 8192
	ip -netns ${ns} -6 ro add unreachable default metric 8192

	ip netns exec ${ns} sysctl -qw net.ipv4.ip_forward=1
	ip netns exec ${ns} sysctl -qw net.ipv6.conf.all.keep_addr_on_down=1
	ip netns exec ${ns} sysctl -qw net.ipv6.conf.all.forwarding=1
	ip netns exec ${ns} sysctl -qw net.ipv6.conf.default.forwarding=1
}

# create veth pair to connect namespaces and apply addresses.
connect_ns()
{
	local ns1=$1
	local ns1_dev=$2
	local ns1_addr=$3
	local ns1_addr6=$4
	local ns2=$5
	local ns2_dev=$6
	local ns2_addr=$7
	local ns2_addr6=$8

	ip -netns ${ns1} li add ${ns1_dev} type veth peer name tmp
	ip -netns ${ns1} li set ${ns1_dev} up
	ip -netns ${ns1} li set tmp netns ${ns2} name ${ns2_dev}
	ip -netns ${ns2} li set ${ns2_dev} up

	if [ "${ns1_addr}" != "-" ]; then
		ip -netns ${ns1} addr add dev ${ns1_dev} ${ns1_addr}
		ip -netns ${ns2} addr add dev ${ns2_dev} ${ns2_addr}
	fi

	if [ "${ns1_addr6}" != "-" ]; then
		ip -netns ${ns1} addr add dev ${ns1_dev} ${ns1_addr6}
		ip -netns ${ns2} addr add dev ${ns2_dev} ${ns2_addr6}
	fi
}

################################################################################
# test setup
setup()
{
	create_ns host-1
	create_ns host-2
	create_ns router

	connect_ns host-1 eth0 10.1.1.1/24 2001:db8:1::1/64 \
	           router eth1 10.1.1.2/24 2001:db8:1::2/64

	connect_ns host-2 eth0 10.1.2.1/24 2001:db8:2::1/64 \
	           router eth2 10.1.2.2/24 2001:db8:2::2/64

	ip -netns host-1 addr add dev lo fc00:101::1/128
	ip -netns host-2 addr add dev lo fc00:101::2/128
	#sleep 5

	ip -netns host-1 -6 ro add 2001:db8:2::/64 via 2001:db8:1::2
	ip -netns host-2 -6 ro add 2001:db8:1::/64 via 2001:db8:2::2

	#
	# configure l2tpv3 tunnel on host-1
	#
	ip -netns host-1 l2tp add tunnel tunnel_id 1234 peer_tunnel_id 1235 \
			 encap ip local 2001:db8:1::1 remote 2001:db8:2::1
	ip -netns host-1 l2tp add session name l2tp1 tunnel_id 1234 \
			 session_id 1234 peer_session_id 1235
	ip -netns host-1 link set dev l2tp1 up
	ip -netns host-1 addr add dev l2tp1 fc00:1::1 peer fc00:1::2

	#
	# configure l2tpv3 tunnel on host-2
	#
	ip -netns host-2 l2tp add tunnel tunnel_id 1235 peer_tunnel_id 1234 \
			 encap ip local 2001:db8:2::1 remote 2001:db8:1::1
	ip -netns host-2 l2tp add session name l2tp2 tunnel_id 1235 \
			 session_id 1235 peer_session_id 1234
	ip -netns host-2 link set dev l2tp2 up
	ip -netns host-2 addr add dev l2tp2 fc00:1::2 peer fc00:1::1

	#sleep 5

	#
	# add routes to loopback addresses
	#
	ip -netns host-1 -6 ro add fc00:101::2/128 via fc00:1::2
	ip -netns host-2 -6 ro add fc00:101::1/128 via fc00:1::1
}

################################################################################
# main

setup

# L2TP tunnel works fine here
ip netns exec host-1 ${ping6} -c1 -w1 fc00:1::2 >/dev/null || echo "L2TP tunnel not working"
ip netns exec host-1 ${ping6} -c1 -w1 fc00:101::2 >/dev/null || echo "L2TP tunnel not working"

# Configure IPsec
ip netns exec host-1 ip xfrm policy add src 2001:db8:1::1/128 dst 2001:db8:2::1/128 dir out tmpl proto esp mode transport

# Expected to fail in this test case because the remote side doesn't have ipsec
# configured, but actually fails to send any packets from 2001:db8:1::1
# regardless of whether the remote side is configured
ip netns exec host-1 ${ping6} -c1 -w1 fc00:1::2 >/dev/null && echo "L2TP w/ IPsec unexpectedly working???"

# Deconfigure IPsec
ip netns exec host-1 ip xfrm policy del src 2001:db8:1::1/128 dst 2001:db8:2::1/128 dir out

# L2TP tunnel should work since IPsec has been deconfigured,
# but the l2tp1 device in host-1 is permanently broken
ip netns exec host-1 ${ping6} -c1 -w1 fc00:1::2

ip netns del host-1
ip netns del host-2
ip netns del router
