##
# SPDX-License-Identifier: BSD-2-Clause
#
# Copyright (c) 2022 Rubicon Communications, LLC ("Netgate")
#
# 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.

. $(atf_get_srcdir)/utils.subr
. $(atf_get_srcdir)/../../netpfil/pf/utils.subr

atf_test_case "4in4" "cleanup"
4in4_head()
{
	atf_set descr 'IPv4 in IPv4 tunnel'
	atf_set require.user root
	atf_set require.progs openvpn
}

4in4_body()
{
	ovpn_init

	l=$(vnet_mkepair)

	vnet_mkjail a ${l}a
	jexec a ifconfig ${l}a 192.0.2.1/24 up
	vnet_mkjail b ${l}b
	jexec b ifconfig ${l}b 192.0.2.2/24 up

	# Sanity check
	atf_check -s exit:0 -o ignore jexec a ping -c 1 192.0.2.2

	ovpn_start a "
		dev ovpn0
		dev-type tun
		proto udp4

		cipher AES-256-GCM
		auth SHA256

		local 192.0.2.1
		server 198.51.100.0 255.255.255.0
		ca $(atf_get_srcdir)/ca.crt
		cert $(atf_get_srcdir)/server.crt
		key $(atf_get_srcdir)/server.key
		dh $(atf_get_srcdir)/dh.pem

		mode server
		script-security 2
		auth-user-pass-verify /usr/bin/true via-env
		topology subnet

		keepalive 100 600
	"
	ovpn_start b "
		dev tun0
		dev-type tun

		client

		remote 192.0.2.1
		auth-user-pass $(atf_get_srcdir)/user.pass

		ca $(atf_get_srcdir)/ca.crt
		cert $(atf_get_srcdir)/client.crt
		key $(atf_get_srcdir)/client.key
		dh $(atf_get_srcdir)/dh.pem

		keepalive 100 600
	"

	# Give the tunnel time to come up
	sleep 10

	atf_check -s exit:0 -o ignore jexec b ping -c 1 198.51.100.1

	echo 'foo' | jexec b nc -u -w 2 192.0.2.1 1194
	atf_check -s exit:0 -o ignore jexec b ping -c 3 198.51.100.1

	# Test routing loop protection
	jexec b route add 192.0.2.1 198.51.100.1
	atf_check -s exit:2 -o ignore jexec b ping -t 1 -c 1 198.51.100.1
}

4in4_cleanup()
{
	ovpn_cleanup
}

atf_test_case "bz283426" "cleanup"
bz283426_head()
{
	atf_set descr 'FreeBSD Bugzilla 283426'
	atf_set require.user root
	atf_set require.progs openvpn python3
}

bz283426_body()
{
	ovpn_init

	l=$(vnet_mkepair)

	vnet_mkjail a ${l}a
	jexec a ifconfig ${l}a 192.0.2.1/24 up
	vnet_mkjail b ${l}b
	jexec b ifconfig ${l}b 192.0.2.2/24 up

	# Sanity check
	atf_check -s exit:0 -o ignore jexec a ping -c 1 192.0.2.2

	ovpn_start a "
		dev ovpn0
		dev-type tun
		proto udp4

		cipher AES-256-GCM
		auth SHA256

		bind 0.0.0.0:1194
		server 198.51.100.0 255.255.255.0
		ca $(atf_get_srcdir)/ca.crt
		cert $(atf_get_srcdir)/server.crt
		key $(atf_get_srcdir)/server.key
		dh $(atf_get_srcdir)/dh.pem

		mode server
		script-security 2
		auth-user-pass-verify /usr/bin/true via-env
		topology subnet

		keepalive 100 600
	"
	ovpn_start b "
		dev tun0
		dev-type tun

		client

		remote 192.0.2.1
		auth-user-pass $(atf_get_srcdir)/user.pass

		ca $(atf_get_srcdir)/ca.crt
		cert $(atf_get_srcdir)/client.crt
		key $(atf_get_srcdir)/client.key
		dh $(atf_get_srcdir)/dh.pem

		keepalive 100 600
	"

	# Give the tunnel time to come up
	sleep 10

	atf_check -s exit:0 -o ignore jexec b ping -c 1 198.51.100.1

	# Send a broadcast packet in the outer link.
	echo "import socket as sk
s = sk.socket(sk.AF_INET, sk.SOCK_DGRAM)
s.setsockopt(sk.SOL_SOCKET, sk.SO_BROADCAST, 1)
s.sendto(b'x' * 1000, ('192.0.2.255', 1194))" | jexec b python3

	atf_check -s exit:0 -o ignore jexec b ping -c 3 198.51.100.1
}

bz283426_cleanup()
{
	ovpn_cleanup
}

atf_test_case "4mapped" "cleanup"
4mapped_head()
{
	atf_set descr 'IPv4 mapped addresses'
	atf_set require.user root
	atf_set require.progs openvpn
}

4mapped_body()
{
	ovpn_init

	l=$(vnet_mkepair)

	vnet_mkjail a ${l}a
	jexec a ifconfig ${l}a 192.0.2.1/24 up
	vnet_mkjail b ${l}b
	jexec b ifconfig ${l}b 192.0.2.2/24 up

	# Sanity check
	atf_check -s exit:0 -o ignore jexec a ping -c 1 192.0.2.2

	#jexec a ifconfig ${l}a

	ovpn_start a "
		dev ovpn0
		dev-type tun

		cipher AES-256-GCM
		auth SHA256

		server 198.51.100.0 255.255.255.0
		ca $(atf_get_srcdir)/ca.crt
		cert $(atf_get_srcdir)/server.crt
		key $(atf_get_srcdir)/server.key
		dh $(atf_get_srcdir)/dh.pem

		mode server
		script-security 2
		auth-user-pass-verify /usr/bin/true via-env
		topology subnet

		keepalive 100 600
	"
	ovpn_start b "
		dev tun0
		dev-type tun

		client

		remote 192.0.2.1
		auth-user-pass $(atf_get_srcdir)/user.pass

		ca $(atf_get_srcdir)/ca.crt
		cert $(atf_get_srcdir)/client.crt
		key $(atf_get_srcdir)/client.key
		dh $(atf_get_srcdir)/dh.pem

		keepalive 100 600
	"

	# Give the tunnel time to come up
	sleep 10

	atf_check -s exit:0 -o ignore jexec b ping -c 3 198.51.100.1
}

4mapped_cleanup()
{
	ovpn_cleanup
}

atf_test_case "6in4" "cleanup"
6in4_head()
{
	atf_set descr 'IPv6 in IPv4 tunnel'
	atf_set require.user root
	atf_set require.progs openvpn
}

6in4_body()
{
	ovpn_init

	l=$(vnet_mkepair)

	vnet_mkjail a ${l}a
	jexec a ifconfig ${l}a 192.0.2.1/24 up
	vnet_mkjail b ${l}b
	jexec b ifconfig ${l}b 192.0.2.2/24 up

	# Sanity check
	atf_check -s exit:0 -o ignore jexec a ping -c 1 192.0.2.2

	ovpn_start a "
		dev ovpn0
		dev-type tun
		proto udp

		cipher AES-256-GCM
		auth SHA256

		local 192.0.2.1
		server-ipv6 2001:db8:1::/64

		ca $(atf_get_srcdir)/ca.crt
		cert $(atf_get_srcdir)/server.crt
		key $(atf_get_srcdir)/server.key
		dh $(atf_get_srcdir)/dh.pem

		mode server
		script-security 2
		auth-user-pass-verify /usr/bin/true via-env
		topology subnet

		keepalive 100 600
	"
	ovpn_start b "
		dev tun0
		dev-type tun

		client

		remote 192.0.2.1
		auth-user-pass $(atf_get_srcdir)/user.pass

		ca $(atf_get_srcdir)/ca.crt
		cert $(atf_get_srcdir)/client.crt
		key $(atf_get_srcdir)/client.key
		dh $(atf_get_srcdir)/dh.pem

		keepalive 100 600
	"

	# Give the tunnel time to come up
	sleep 10

	atf_check -s exit:0 -o ignore jexec b ping6 -c 3 2001:db8:1::1
}

6in4_cleanup()
{
	ovpn_cleanup
}

atf_test_case "4in6" "cleanup"
4in6_head()
{
	atf_set descr 'IPv4 in IPv6 tunnel'
	atf_set require.user root
	atf_set require.progs openvpn
}

4in6_body()
{
	ovpn_init

	l=$(vnet_mkepair)

	vnet_mkjail a ${l}a
	jexec a ifconfig ${l}a inet6 2001:db8::1/64 up no_dad
	vnet_mkjail b ${l}b
	jexec b ifconfig ${l}b inet6 2001:db8::2/64 up no_dad

	# Sanity check
	atf_check -s exit:0 -o ignore jexec a ping6 -c 1 2001:db8::2

	ovpn_start a "
		dev ovpn0
		dev-type tun
		proto udp6

		cipher AES-256-GCM
		auth SHA256

		local 2001:db8::1
		server 198.51.100.0 255.255.255.0
		ca $(atf_get_srcdir)/ca.crt
		cert $(atf_get_srcdir)/server.crt
		key $(atf_get_srcdir)/server.key
		dh $(atf_get_srcdir)/dh.pem

		mode server
		script-security 2
		auth-user-pass-verify /usr/bin/true via-env
		topology subnet

		keepalive 100 600
	"
	ovpn_start b "
		dev tun0
		dev-type tun

		client

		remote 2001:db8::1
		auth-user-pass $(atf_get_srcdir)/user.pass

		ca $(atf_get_srcdir)/ca.crt
		cert $(atf_get_srcdir)/client.crt
		key $(atf_get_srcdir)/client.key
		dh $(atf_get_srcdir)/dh.pem

		keepalive 100 600
	"

	dd if=/dev/random of=test.img bs=1024 count=1024
	cat test.img | jexec a nc -N -l 1234 &

	# Give the tunnel time to come up
	sleep 10

	atf_check -s exit:0 -o ignore jexec b ping -c 3 198.51.100.1

	# MTU sweep
	for i in `seq 1000 1500`
	do
		atf_check -s exit:0 -o ignore jexec b \
		    ping -c 1 -s $i 198.51.100.1
	done

	rcvmd5=$(jexec b nc -N -w 3 198.51.100.1 1234 | md5)
	md5=$(md5 test.img)

	if [ $md5  != $rcvmd5 ];
	then
		atf_fail "Transmit corruption!"
	fi
}

4in6_cleanup()
{
	ovpn_cleanup
}

atf_test_case "6in6" "cleanup"
6in6_head()
{
	atf_set descr 'IPv6 in IPv6 tunnel'
	atf_set require.user root
	atf_set require.progs openvpn
}

6in6_body()
{
	ovpn_init

	l=$(vnet_mkepair)

	vnet_mkjail a ${l}a
	jexec a ifconfig ${l}a inet6 2001:db8::1/64 up no_dad
	vnet_mkjail b ${l}b
	jexec b ifconfig ${l}b inet6 2001:db8::2/64 up no_dad

	# Sanity check
	atf_check -s exit:0 -o ignore jexec a ping6 -c 1 2001:db8::2

	ovpn_start a "
		dev ovpn0
		dev-type tun
		proto udp6

		cipher AES-256-GCM
		auth SHA256

		local 2001:db8::1
		server-ipv6 2001:db8:1::/64

		ca $(atf_get_srcdir)/ca.crt
		cert $(atf_get_srcdir)/server.crt
		key $(atf_get_srcdir)/server.key
		dh $(atf_get_srcdir)/dh.pem

		mode server
		script-security 2
		auth-user-pass-verify /usr/bin/true via-env
		topology subnet

		keepalive 100 600
	"
	ovpn_start b "
		dev tun0
		dev-type tun

		client

		remote 2001:db8::1
		auth-user-pass $(atf_get_srcdir)/user.pass

		ca $(atf_get_srcdir)/ca.crt
		cert $(atf_get_srcdir)/client.crt
		key $(atf_get_srcdir)/client.key
		dh $(atf_get_srcdir)/dh.pem

		keepalive 100 600
	"

	# Give the tunnel time to come up
	sleep 10

	atf_check -s exit:0 -o ignore jexec b ping6 -c 3 2001:db8:1::1
	atf_check -s exit:0 -o ignore jexec b ping6 -c 3 -z 16 2001:db8:1::1

	# Test routing loop protection
	jexec b route add -6 2001:db8::1 2001:db8:1::1
	atf_check -s exit:2 -o ignore jexec b ping6 -t 1 -c 3 2001:db8:1::1
}

6in6_cleanup()
{
	ovpn_cleanup
}

atf_test_case "linklocal" "cleanup"
linklocal_head()
{
	atf_set descr 'Use IPv6 link-local addresses'
	atf_set require.user root
	atf_set require.progs openvpn
}

linklocal_body()
{
	ovpn_init
	ovpn_check_version 2.7.0

	l=$(vnet_mkepair)

	vnet_mkjail a ${l}a
	jexec a ifconfig ${l}a inet6 fe80::a/64 up no_dad
	vnet_mkjail b ${l}b
	jexec b ifconfig ${l}b inet6 fe80::b/64 up no_dad

	# Sanity check
	atf_check -s exit:0 -o ignore jexec a ping6 -c 1 fe80::b%${l}a

	ovpn_start a "
		dev ovpn0
		dev-type tun
		proto udp6

		cipher AES-256-GCM
		auth SHA256

		local fe80::a%${l}a
		server-ipv6 2001:db8:1::/64

		ca $(atf_get_srcdir)/ca.crt
		cert $(atf_get_srcdir)/server.crt
		key $(atf_get_srcdir)/server.key
		dh $(atf_get_srcdir)/dh.pem

		mode server
		script-security 2
		auth-user-pass-verify /usr/bin/true via-env
		topology subnet

		keepalive 100 600
	"
	ovpn_start b "
		dev tun0
		dev-type tun

		client

		remote fe80::a%${l}b
		auth-user-pass $(atf_get_srcdir)/user.pass

		ca $(atf_get_srcdir)/ca.crt
		cert $(atf_get_srcdir)/client.crt
		key $(atf_get_srcdir)/client.key
		dh $(atf_get_srcdir)/dh.pem

		keepalive 100 600
	"

	# Give the tunnel time to come up
	sleep 10
	jexec a ifconfig

	atf_check -s exit:0 -o ignore jexec b ping6 -c 3 2001:db8:1::1
	atf_check -s exit:0 -o ignore jexec b ping6 -c 3 -z 16 2001:db8:1::1
}

linklocal_cleanup()
{
	ovpn_cleanup
}

atf_test_case "timeout_client" "cleanup"
timeout_client_head()
{
	atf_set descr 'IPv4 in IPv4 tunnel'
	atf_set require.user root
	atf_set require.progs openvpn
}

timeout_client_body()
{
	ovpn_init

	l=$(vnet_mkepair)

	vnet_mkjail a ${l}a
	jexec a ifconfig ${l}a 192.0.2.1/24 up
	jexec a ifconfig lo0 127.0.0.1/8 up
	vnet_mkjail b ${l}b
	jexec b ifconfig ${l}b 192.0.2.2/24 up

	# Sanity check
	atf_check -s exit:0 -o ignore jexec a ping -c 1 192.0.2.2

	ovpn_start a "
		dev ovpn0
		dev-type tun
		proto udp4

		cipher AES-256-GCM
		auth SHA256

		local 192.0.2.1
		server 198.51.100.0 255.255.255.0
		ca $(atf_get_srcdir)/ca.crt
		cert $(atf_get_srcdir)/server.crt
		key $(atf_get_srcdir)/server.key
		dh $(atf_get_srcdir)/dh.pem

		mode server
		script-security 2
		auth-user-pass-verify /usr/bin/true via-env
		topology subnet

		keepalive 2 10

		management 192.0.2.1 1234
	"
	ovpn_start b "
		dev tun0
		dev-type tun

		client

		remote 192.0.2.1
		auth-user-pass $(atf_get_srcdir)/user.pass

		ca $(atf_get_srcdir)/ca.crt
		cert $(atf_get_srcdir)/client.crt
		key $(atf_get_srcdir)/client.key
		dh $(atf_get_srcdir)/dh.pem

		keepalive 2 10
	"

	# Give the tunnel time to come up
	sleep 10

	atf_check -s exit:0 -o ignore jexec b ping -c 3 198.51.100.1

	# Kill the client
	jexec b killall openvpn

	# Now wait for the server to notice
	sleep 15

	while echo "status" | jexec a nc -N 192.0.2.1 1234 | grep 192.0.2.2; do
		echo "Client disconnect not discovered"
		sleep 1
	done
}

timeout_client_cleanup()
{
	ovpn_cleanup
}

atf_test_case "explicit_exit" "cleanup"
explicit_exit_head()
{
	atf_set descr 'Test explicit exit notification'
	atf_set require.user root
	atf_set require.progs openvpn
}

explicit_exit_body()
{
	ovpn_init

	l=$(vnet_mkepair)

	vnet_mkjail a ${l}a
	jexec a ifconfig ${l}a 192.0.2.1/24 up
	jexec a ifconfig lo0 127.0.0.1/8 up
	vnet_mkjail b ${l}b
	jexec b ifconfig ${l}b 192.0.2.2/24 up

	# Sanity check
	atf_check -s exit:0 -o ignore jexec a ping -c 1 192.0.2.2

	ovpn_start a "
		dev ovpn0
		dev-type tun
		proto udp4

		cipher AES-256-GCM
		auth SHA256

		local 192.0.2.1
		server 198.51.100.0 255.255.255.0
		ca $(atf_get_srcdir)/ca.crt
		cert $(atf_get_srcdir)/server.crt
		key $(atf_get_srcdir)/server.key
		dh $(atf_get_srcdir)/dh.pem

		mode server
		script-security 2
		auth-user-pass-verify /usr/bin/true via-env
		topology subnet

		management 192.0.2.1 1234
	"
	ovpn_start b "
		dev tun0
		dev-type tun

		client

		remote 192.0.2.1
		auth-user-pass $(atf_get_srcdir)/user.pass

		ca $(atf_get_srcdir)/ca.crt
		cert $(atf_get_srcdir)/client.crt
		key $(atf_get_srcdir)/client.key
		dh $(atf_get_srcdir)/dh.pem

		explicit-exit-notify
	"

	# Give the tunnel time to come up
	sleep 10

	atf_check -s exit:0 -o ignore jexec b ping -c 3 198.51.100.1

	if ! echo "status" | jexec a nc -N 192.0.2.1 1234 | grep 192.0.2.2; then
		atf_fail "Client not found in status list!"
	fi

	# Kill the client
	jexec b killall openvpn

	while echo "status" | jexec a nc -N 192.0.2.1 1234 | grep 192.0.2.2; do
		jexec a ps auxf
		echo "Client disconnect not discovered"
		sleep 1
	done
}

explicit_exit_cleanup()
{
	ovpn_cleanup
}

atf_test_case "multi_client" "cleanup"
multi_client_head()
{
	atf_set descr 'Multiple simultaneous clients'
	atf_set require.user root
	atf_set require.progs openvpn
}

multi_client_body()
{
	ovpn_init
	vnet_init_bridge

	bridge=$(vnet_mkbridge)
	srv=$(vnet_mkepair)
	one=$(vnet_mkepair)
	two=$(vnet_mkepair)

	ifconfig ${bridge} up

	ifconfig ${srv}a up
	ifconfig ${bridge} addm ${srv}a
	ifconfig ${one}a up
	ifconfig ${bridge} addm ${one}a
	ifconfig ${two}a up
	ifconfig ${bridge} addm ${two}a

	vnet_mkjail srv ${srv}b
	jexec srv ifconfig ${srv}b 192.0.2.1/24 up
	vnet_mkjail one ${one}b
	jexec one ifconfig ${one}b 192.0.2.2/24 up
	vnet_mkjail two ${two}b
	jexec two ifconfig ${two}b 192.0.2.3/24 up
	jexec two ifconfig lo0 127.0.0.1/8 up
	jexec two ifconfig lo0 inet alias 203.0.113.1/24

	# Sanity checks
	atf_check -s exit:0 -o ignore jexec one ping -c 1 192.0.2.1
	atf_check -s exit:0 -o ignore jexec two ping -c 1 192.0.2.1

	jexec srv sysctl net.inet.ip.forwarding=1

	ovpn_start srv "
		dev ovpn0
		dev-type tun
		proto udp4

		cipher AES-256-GCM
		auth SHA256

		local 192.0.2.1
		server 198.51.100.0 255.255.255.0

		push \"route 203.0.113.0 255.255.255.0 198.51.100.1\"

		ca $(atf_get_srcdir)/ca.crt
		cert $(atf_get_srcdir)/server.crt
		key $(atf_get_srcdir)/server.key
		dh $(atf_get_srcdir)/dh.pem

		mode server
		duplicate-cn
		script-security 2
		auth-user-pass-verify /usr/bin/true via-env
		topology subnet

		keepalive 100 600

		client-config-dir $(atf_get_srcdir)/ccd
	"
	ovpn_start one "
		dev tun0
		dev-type tun

		client

		remote 192.0.2.1
		auth-user-pass $(atf_get_srcdir)/user.pass

		ca $(atf_get_srcdir)/ca.crt
		cert $(atf_get_srcdir)/client.crt
		key $(atf_get_srcdir)/client.key
		dh $(atf_get_srcdir)/dh.pem

		keepalive 100 600
	"
	ovpn_start two "
		dev tun0
		dev-type tun

		client

		remote 192.0.2.1
		auth-user-pass $(atf_get_srcdir)/user.pass

		ca $(atf_get_srcdir)/ca.crt
		cert $(atf_get_srcdir)/client2.crt
		key $(atf_get_srcdir)/client2.key
		dh $(atf_get_srcdir)/dh.pem

		keepalive 100 600
	"

	# Give the tunnel time to come up
	sleep 10

	atf_check -s exit:0 -o ignore jexec one ping -c 3 198.51.100.1
	atf_check -s exit:0 -o ignore jexec two ping -c 3 198.51.100.1

	# Client-to-client communication
	atf_check -s exit:0 -o ignore jexec one ping -c 3 198.51.100.3
	atf_check -s exit:0 -o ignore jexec two ping -c 3 198.51.100.2

	# iroute test
	atf_check -s exit:0 -o ignore jexec one ping -c 3 203.0.113.1
}

multi_client_cleanup()
{
	ovpn_cleanup
}

atf_test_case "route_to" "cleanup"
route_to_head()
{
	atf_set descr "Test pf's route-to with OpenVPN tunnels"
	atf_set require.user root
	atf_set require.progs openvpn
}

route_to_body()
{
	pft_init
	ovpn_init

	l=$(vnet_mkepair)
	n=$(vnet_mkepair)

	vnet_mkjail a ${l}a
	jexec a ifconfig ${l}a 192.0.2.1/24 up
	vnet_mkjail b ${l}b ${n}a
	jexec b ifconfig ${l}b 192.0.2.2/24 up
	jexec b ifconfig ${n}a up

	# Sanity check
	atf_check -s exit:0 -o ignore jexec a ping -c 1 192.0.2.2

	ovpn_start a "
		dev ovpn0
		dev-type tun
		proto udp4

		cipher AES-256-GCM
		auth SHA256

		local 192.0.2.1
		server 198.51.100.0 255.255.255.0
		ca $(atf_get_srcdir)/ca.crt
		cert $(atf_get_srcdir)/server.crt
		key $(atf_get_srcdir)/server.key
		dh $(atf_get_srcdir)/dh.pem

		mode server
		script-security 2
		auth-user-pass-verify /usr/bin/true via-env
		topology subnet

		keepalive 100 600
	"
	ovpn_start b "
		dev tun0
		dev-type tun

		client

		remote 192.0.2.1
		auth-user-pass $(atf_get_srcdir)/user.pass

		ca $(atf_get_srcdir)/ca.crt
		cert $(atf_get_srcdir)/client.crt
		key $(atf_get_srcdir)/client.key
		dh $(atf_get_srcdir)/dh.pem

		keepalive 100 600
	"

	# Give the tunnel time to come up
	sleep 10
	jexec a ifconfig ovpn0 inet alias 198.51.100.254/24

	# Check the tunnel
	atf_check -s exit:0 -o ignore jexec b ping -c 1 -S 198.51.100.2 198.51.100.1
	atf_check -s exit:0 -o ignore jexec b ping -c 1 -S 198.51.100.2 198.51.100.254

	# Break our route to .254 so that we need a route-to to make things work.
	jexec b ifconfig ${n}a 203.0.113.1/24 up
	jexec b route add 198.51.100.254 -interface ${n}a

	# Make sure it's broken.
	atf_check -s exit:2 -o ignore jexec b ping -c 1 -S 198.51.100.2 198.51.100.254

	jexec b pfctl -e
	pft_set_rules b \
		"pass out route-to (tun0 198.51.100.1) proto icmp from 198.51.100.2 "
	atf_check -s exit:0 -o ignore jexec b ping -c 3 -S 198.51.100.2 198.51.100.254
}

route_to_cleanup()
{
	ovpn_cleanup
	pft_cleanup
}

atf_test_case "ra" "cleanup"
ra_head()
{
	atf_set descr 'Remote access with multiple clients'
	atf_set require.user root
	atf_set require.progs openvpn
}

ra_body()
{
	ovpn_init
	vnet_init_bridge

	bridge=$(vnet_mkbridge)
	srv=$(vnet_mkepair)
	lan=$(vnet_mkepair)
	one=$(vnet_mkepair)
	two=$(vnet_mkepair)

	ifconfig ${bridge} up

	ifconfig ${srv}a up
	ifconfig ${bridge} addm ${srv}a
	ifconfig ${one}a up
	ifconfig ${bridge} addm ${one}a
	ifconfig ${two}a up
	ifconfig ${bridge} addm ${two}a

	vnet_mkjail srv ${srv}b ${lan}a
	jexec srv ifconfig lo0 inet 127.0.0.1/8 up
	jexec srv ifconfig ${srv}b 192.0.2.1/24 up
	jexec srv ifconfig ${lan}a 203.0.113.1/24 up
	vnet_mkjail lan ${lan}b
	jexec lan ifconfig lo0 inet 127.0.0.1/8 up
	jexec lan ifconfig ${lan}b 203.0.113.2/24 up
	jexec lan route add default 203.0.113.1
	vnet_mkjail one ${one}b
	jexec one ifconfig lo0 inet 127.0.0.1/8 up
	jexec one ifconfig ${one}b 192.0.2.2/24 up
	vnet_mkjail two ${two}b
	jexec two ifconfig lo0 inet 127.0.0.1/8 up
	jexec two ifconfig ${two}b 192.0.2.3/24 up

	# Sanity checks
	atf_check -s exit:0 -o ignore jexec one ping -c 1 192.0.2.1
	atf_check -s exit:0 -o ignore jexec two ping -c 1 192.0.2.1
	atf_check -s exit:0 -o ignore jexec srv ping -c 1 203.0.113.2

	jexec srv sysctl net.inet.ip.forwarding=1

	ovpn_start srv "
		dev ovpn0
		dev-type tun
		proto udp4

		cipher AES-256-GCM
		auth SHA256

		local 192.0.2.1
		server 198.51.100.0 255.255.255.0

		push \"route 203.0.113.0 255.255.255.0\"

		ca $(atf_get_srcdir)/ca.crt
		cert $(atf_get_srcdir)/server.crt
		key $(atf_get_srcdir)/server.key
		dh $(atf_get_srcdir)/dh.pem

		mode server
		duplicate-cn
		script-security 2
		auth-user-pass-verify /usr/bin/true via-env
		topology subnet

		keepalive 100 600
	"
	ovpn_start one "
		dev tun0
		dev-type tun

		client

		remote 192.0.2.1
		auth-user-pass $(atf_get_srcdir)/user.pass

		ca $(atf_get_srcdir)/ca.crt
		cert $(atf_get_srcdir)/client.crt
		key $(atf_get_srcdir)/client.key
		dh $(atf_get_srcdir)/dh.pem

		keepalive 100 600
	"
	sleep 2
	ovpn_start two "
		dev tun0
		dev-type tun

		client

		remote 192.0.2.1
		auth-user-pass $(atf_get_srcdir)/user.pass

		ca $(atf_get_srcdir)/ca.crt
		cert $(atf_get_srcdir)/client2.crt
		key $(atf_get_srcdir)/client2.key
		dh $(atf_get_srcdir)/dh.pem

		keepalive 100 600
	"

	# Give the tunnel time to come up
	sleep 10

	atf_check -s exit:0 -o ignore jexec one ping -c 1 198.51.100.1
	atf_check -s exit:0 -o ignore jexec two ping -c 1 198.51.100.1

	# Client-to-client communication
	atf_check -s exit:0 -o ignore jexec one ping -c 1 198.51.100.3
	atf_check -s exit:0 -o ignore jexec one ping -c 1 198.51.100.2
	atf_check -s exit:0 -o ignore jexec two ping -c 1 198.51.100.2
	atf_check -s exit:0 -o ignore jexec two ping -c 1 198.51.100.3

	# RA test
	atf_check -s exit:0 -o ignore jexec one ping -c 1 203.0.113.1
	atf_check -s exit:0 -o ignore jexec two ping -c 1 203.0.113.1

	atf_check -s exit:0 -o ignore jexec srv ping -c 1 -S 203.0.113.1 198.51.100.2
	atf_check -s exit:0 -o ignore jexec srv ping -c 1 -S 203.0.113.1 198.51.100.3

	atf_check -s exit:0 -o ignore jexec one ping -c 1 203.0.113.2
	atf_check -s exit:0 -o ignore jexec two ping -c 1 203.0.113.2

	atf_check -s exit:0 -o ignore jexec lan ping -c 1 198.51.100.1
	atf_check -s exit:0 -o ignore jexec lan ping -c 1 198.51.100.2
	atf_check -s exit:0 -o ignore jexec lan ping -c 1 198.51.100.3
	atf_check -s exit:2 -o ignore jexec lan ping -c 1 198.51.100.4
}

ra_cleanup()
{
	ovpn_cleanup
}

ovpn_algo_body()
{
	algo=$1

	ovpn_init

	l=$(vnet_mkepair)

	vnet_mkjail a ${l}a
	jexec a ifconfig ${l}a 192.0.2.1/24 up
	vnet_mkjail b ${l}b
	jexec b ifconfig ${l}b 192.0.2.2/24 up

	# Sanity check
	atf_check -s exit:0 -o ignore jexec a ping -c 1 192.0.2.2

	ovpn_start a "
		dev ovpn0
		dev-type tun
		proto udp4

		cipher ${algo}
		data-ciphers ${algo}
		auth SHA256

		local 192.0.2.1
		server 198.51.100.0 255.255.255.0
		ca $(atf_get_srcdir)/ca.crt
		cert $(atf_get_srcdir)/server.crt
		key $(atf_get_srcdir)/server.key
		dh $(atf_get_srcdir)/dh.pem

		mode server
		script-security 2
		auth-user-pass-verify /usr/bin/true via-env
		topology subnet

		keepalive 100 600
	"
	ovpn_start b "
		dev tun0
		dev-type tun

		client

		cipher ${algo}
		data-ciphers ${algo}

		remote 192.0.2.1
		auth-user-pass $(atf_get_srcdir)/user.pass

		ca $(atf_get_srcdir)/ca.crt
		cert $(atf_get_srcdir)/client.crt
		key $(atf_get_srcdir)/client.key
		dh $(atf_get_srcdir)/dh.pem

		keepalive 100 600
	"

	# Give the tunnel time to come up
	sleep 10

	atf_check -s exit:0 -o ignore jexec b ping -c 3 198.51.100.1
}

atf_test_case "chacha" "cleanup"
chacha_head()
{
	atf_set descr 'Test DCO with the chacha algorithm'
	atf_set require.user root
	atf_set require.progs openvpn
}

chacha_body()
{
	ovpn_algo_body CHACHA20-POLY1305
}

chacha_cleanup()
{
	ovpn_cleanup
}

atf_test_case "gcm_128" "cleanup"
gcm_128_head()
{
	atf_set descr 'Test DCO with AES-128-GCM'
	atf_set require.user root
	atf_set require.progs openvpn
}

gcm_128_body()
{
	ovpn_algo_body AES-128-GCM
}

gcm_128_cleanup()
{
	ovpn_cleanup
}

atf_test_case "destroy_unused" "cleanup"
destroy_unused_head()
{
	atf_set descr 'Destroy an if_ovpn interface before it is used'
	atf_set require.user root
}

destroy_unused_body()
{
	ovpn_init

	intf=$(ifconfig ovpn create)
	atf_check -s exit:0 \
	    ifconfig ${intf} destroy
}

destroy_unused_cleanup()
{
	ovpn_cleanup
}

atf_test_case "multihome4" "cleanup"
multihome4_head()
{
	atf_set descr 'Test multihome IPv4 with OpenVPN'
	atf_set require.user root
	atf_set require.progs openvpn
}

multihome4_body()
{
	pft_init
	ovpn_init

	l=$(vnet_mkepair)

	vnet_mkjail a ${l}a
	atf_check jexec a ifconfig ${l}a inet 192.0.2.1/24
	atf_check jexec a ifconfig ${l}a alias 192.0.2.2/24
	vnet_mkjail b ${l}b
	atf_check jexec b ifconfig ${l}b inet 192.0.2.3/24

	# Sanity check
	atf_check -s exit:0 -o ignore jexec b ping -c 1 192.0.2.1
	atf_check -s exit:0 -o ignore jexec b ping -c 1 192.0.2.2

	ovpn_start a "
		dev ovpn0
		dev-type tun
		proto udp4

		cipher AES-256-GCM
		auth SHA256

		multihome
		server 198.51.100.0 255.255.255.0
		ca $(atf_get_srcdir)/ca.crt
		cert $(atf_get_srcdir)/server.crt
		key $(atf_get_srcdir)/server.key
		dh $(atf_get_srcdir)/dh.pem

		mode server
		script-security 2
		auth-user-pass-verify /usr/bin/true via-env
		topology subnet

		keepalive 100 600
	"
	ovpn_start b "
		dev tun0
		dev-type tun

		client

		remote 192.0.2.2
		auth-user-pass $(atf_get_srcdir)/user.pass

		ca $(atf_get_srcdir)/ca.crt
		cert $(atf_get_srcdir)/client.crt
		key $(atf_get_srcdir)/client.key
		dh $(atf_get_srcdir)/dh.pem

		keepalive 100 600
	"

	# Block packets from the primary address, openvpn should only use the
	# configured remote address.
	jexec b pfctl -e
	pft_set_rules b \
		"block in quick from 192.0.2.1 to any" \
		"pass all"

	# Give the tunnel time to come up
	sleep 10

	atf_check -s exit:0 -o ignore jexec b ping -c 3 198.51.100.1
}

multihome4_cleanup()
{
	ovpn_cleanup
	pft_cleanup
}

multihome6_head()
{
	atf_set descr 'Test multihome IPv6 with OpenVPN'
	atf_set require.user root
	atf_set require.progs openvpn
}

multihome6_body()
{
	ovpn_init

	l=$(vnet_mkepair)

	vnet_mkjail a ${l}a
	atf_check jexec a ifconfig ${l}a inet6 2001:db8::1/64 no_dad
	atf_check jexec a ifconfig ${l}a inet6 alias 2001:db8::2/64 no_dad
	vnet_mkjail b ${l}b
	atf_check jexec b ifconfig ${l}b inet6 2001:db8::3/64 no_dad

	# Sanity check
	atf_check -s exit:0 -o ignore jexec b ping6 -c 1 2001:db8::1
	atf_check -s exit:0 -o ignore jexec b ping6 -c 1 2001:db8::2

	ovpn_start a "
		dev ovpn0
		dev-type tun
		proto udp6

		cipher AES-256-GCM
		auth SHA256

		multihome
		server-ipv6 2001:db8:1::/64

		ca $(atf_get_srcdir)/ca.crt
		cert $(atf_get_srcdir)/server.crt
		key $(atf_get_srcdir)/server.key
		dh $(atf_get_srcdir)/dh.pem

		mode server
		script-security 2
		auth-user-pass-verify /usr/bin/true via-env
		topology subnet

		keepalive 100 600
	"
	ovpn_start b "
		dev tun0
		dev-type tun

		client

		remote 2001:db8::2
		auth-user-pass $(atf_get_srcdir)/user.pass

		ca $(atf_get_srcdir)/ca.crt
		cert $(atf_get_srcdir)/client.crt
		key $(atf_get_srcdir)/client.key
		dh $(atf_get_srcdir)/dh.pem

		keepalive 100 600
	"

	# Block packets from the primary address, openvpn should only use the
	# configured remote address.
	jexec b pfctl -e
	pft_set_rules b \
		"block in quick from 2001:db8::1 to any" \
		"pass all"

	# Give the tunnel time to come up
	sleep 10

	atf_check -s exit:0 -o ignore jexec b ping6 -c 3 2001:db8:1::1
	atf_check -s exit:0 -o ignore jexec b ping6 -c 3 -z 16 2001:db8:1::1
}

multihome6_cleanup()
{
	ovpn_cleanup
}

atf_test_case "float" "cleanup"
float_head()
{
	atf_set descr 'Test peer float notification'
	atf_set require.user root
}

float_body()
{
	ovpn_init
	ovpn_check_version 2.7.0

	l=$(vnet_mkepair)

	vnet_mkjail a ${l}a
	jexec a ifconfig ${l}a 192.0.2.1/24 up
	jexec a ifconfig lo0 127.0.0.1/8 up
	vnet_mkjail b ${l}b
	jexec b ifconfig ${l}b 192.0.2.2/24 up

	# Sanity check
	atf_check -s exit:0 -o ignore jexec a ping -c 1 192.0.2.2

	ovpn_start a "
		dev ovpn0
		dev-type tun
		proto udp4

		cipher AES-256-GCM
		auth SHA256

		local 192.0.2.1
		server 198.51.100.0 255.255.255.0
		ca $(atf_get_srcdir)/ca.crt
		cert $(atf_get_srcdir)/server.crt
		key $(atf_get_srcdir)/server.key
		dh $(atf_get_srcdir)/dh.pem

		mode server
		script-security 2
		auth-user-pass-verify /usr/bin/true via-env
		topology subnet

		keepalive 2 10

		management 192.0.2.1 1234
	"
	ovpn_start b "
		dev tun0
		dev-type tun

		client

		remote 192.0.2.1
		auth-user-pass $(atf_get_srcdir)/user.pass

		ca $(atf_get_srcdir)/ca.crt
		cert $(atf_get_srcdir)/client.crt
		key $(atf_get_srcdir)/client.key
		dh $(atf_get_srcdir)/dh.pem

		keepalive 2 10
	"

	# Give the tunnel time to come up
	sleep 10

	atf_check -s exit:0 -o ignore jexec b ping -c 3 198.51.100.1

	# We expect the client on 192.0.2.2
	if ! echo "status" | jexec a nc -N 192.0.2.1 1234 | grep 192.0.2.2; then
		atf_fail "Client not found in status list!"
	fi

	# Now change the client IP
	jexec b ifconfig ${l}b 192.0.2.3/24 up

	# And wait for keepalives to trigger the float notification
	sleep 5

	# So the client now has the new address in userspace
	if ! echo "status" | jexec a nc -N 192.0.2.1 1234 | grep 192.0.2.3; then
		atf_fail "Client not found in status list!"
	fi
}

float_cleanup()
{
	ovpn_cleanup
}

atf_init_test_cases()
{
	atf_add_test_case "4in4"
	atf_add_test_case "bz283426"
	atf_add_test_case "4mapped"
	atf_add_test_case "6in4"
	atf_add_test_case "6in6"
	atf_add_test_case "4in6"
	atf_add_test_case "linklocal"
	atf_add_test_case "timeout_client"
	atf_add_test_case "explicit_exit"
	atf_add_test_case "multi_client"
	atf_add_test_case "route_to"
	atf_add_test_case "ra"
	atf_add_test_case "chacha"
	atf_add_test_case "gcm_128"
	atf_add_test_case "destroy_unused"
	atf_add_test_case "multihome4"
	atf_add_test_case "multihome6"
	atf_add_test_case "float"
}
