git: 89437732d361 - stable/13 - pf tests: test syncookies on IPv6

From: Kristof Provost <kp_at_FreeBSD.org>
Date: Mon, 28 Aug 2023 12:03:22 UTC
The branch stable/13 has been updated by kp:

URL: https://cgit.FreeBSD.org/src/commit/?id=89437732d361d9b761660021cefc3f362c9ca672

commit 89437732d361d9b761660021cefc3f362c9ca672
Author:     Kristof Provost <kp@FreeBSD.org>
AuthorDate: 2023-08-21 06:06:50 +0000
Commit:     Kristof Provost <kp@FreeBSD.org>
CommitDate: 2023-08-28 08:17:18 +0000

    pf tests: test syncookies on IPv6
    
    MFC after:      1 week
    Sponsored by:   Rubicon Communications, LLC ("Netgate")
    
    (cherry picked from commit 1fd8c845b8b77f208f481901823fb87df04f8add)
---
 tests/sys/netpfil/common/pft_synflood.py |   8 +-
 tests/sys/netpfil/pf/syncookie.sh        | 151 ++++++++++++++++++++++++++++++-
 2 files changed, 157 insertions(+), 2 deletions(-)

diff --git a/tests/sys/netpfil/common/pft_synflood.py b/tests/sys/netpfil/common/pft_synflood.py
index 67a5bba0def7..f73caa1b6aa6 100644
--- a/tests/sys/netpfil/common/pft_synflood.py
+++ b/tests/sys/netpfil/common/pft_synflood.py
@@ -35,7 +35,10 @@ def syn_flood(args):
 
 	# Set a src mac, to avoid doing lookups which really slow us down.
 	ether = sp.Ether(src='01:02:03:04:05')
-	ip = sp.IP(dst=args.to[0])
+	if args.ip6:
+		ip = sp.IPv6(dst=args.to[0])
+	else:
+		ip = sp.IP(dst=args.to[0])
 	for i in range(int(args.count[0])):
 		tcp = sp.TCP(flags='S', sport=1+i, dport=22, seq=500+i)
 		pkt = ether / ip / tcp
@@ -44,6 +47,9 @@ def syn_flood(args):
 def main():
 	parser = argparse.ArgumentParser("pft_synflood.py",
 		description="SYN flooding tool")
+	parser.add_argument('--ip6',
+		action='store_true',
+		help='Use IPv6 rather than IPv4')
 	parser.add_argument('--sendif', nargs=1,
 		required=True,
 		help='The interface through which the packet(s) will be sent')
diff --git a/tests/sys/netpfil/pf/syncookie.sh b/tests/sys/netpfil/pf/syncookie.sh
index 290b61817471..814593adbf98 100644
--- a/tests/sys/netpfil/pf/syncookie.sh
+++ b/tests/sys/netpfil/pf/syncookie.sh
@@ -71,7 +71,6 @@ basic_body()
 		atf_fail "Failed to connect to syncookie protected echo daemon"
 	fi
 
-
 	# Check that status shows syncookies as being active
 	active=$(syncookie_state alcatraz)
 	if [ "$active" != "active" ];
@@ -86,6 +85,55 @@ basic_cleanup()
 	pft_cleanup
 }
 
+atf_test_case "basic_v6" "cleanup"
+basic_v6_head()
+{
+	atf_set descr 'Basic syncookie IPv6 test'
+	atf_set require.user root
+}
+
+basic_v6_body()
+{
+	pft_init
+
+	epair=$(vnet_mkepair)
+
+	vnet_mkjail alcatraz ${epair}b
+	jexec alcatraz ifconfig ${epair}b inet6 2001:db8::1/64 up no_dad
+	jexec alcatraz /usr/sbin/inetd -p inetd-alcatraz.pid \
+	    $(atf_get_srcdir)/echo_inetd.conf
+
+	ifconfig ${epair}a inet6 2001:db8::2/64 up no_dad
+
+	jexec alcatraz pfctl -e
+	pft_set_rules alcatraz \
+		"set syncookies always" \
+		"pass in" \
+		"pass out"
+
+	# Sanity check
+	atf_check -s exit:0 -o ignore ping6 -c 1 2001:db8::1
+
+	reply=$(echo foo | nc -N -w 5 2001:db8::1 7)
+	if [ "${reply}" != "foo" ];
+	then
+		atf_fail "Failed to connect to syncookie protected echo daemon"
+	fi
+
+	# Check that status shows syncookies as being active
+	active=$(syncookie_state alcatraz)
+	if [ "$active" != "active" ];
+	then
+		atf_fail "syncookies not active"
+	fi
+}
+
+basic_v6_cleanup()
+{
+	rm -f inetd-alcatraz.pid
+	pft_cleanup
+}
+
 atf_test_case "forward" "cleanup"
 forward_head()
 {
@@ -137,6 +185,57 @@ forward_cleanup()
 	pft_cleanup
 }
 
+atf_test_case "forward_v6" "cleanup"
+forward_v6_head()
+{
+	atf_set descr 'Syncookies for forwarded hosts'
+	atf_set require.user root
+}
+
+forward_v6_body()
+{
+	pft_init
+
+	epair_in=$(vnet_mkepair)
+	epair_out=$(vnet_mkepair)
+
+	vnet_mkjail fwd ${epair_in}b ${epair_out}a
+	vnet_mkjail srv ${epair_out}b
+
+	jexec fwd ifconfig ${epair_in}b inet6 2001:db8::1/64 up no_dad
+	jexec fwd ifconfig ${epair_out}a inet6 2001:db8:1::1/64 up no_dad
+	jexec fwd sysctl net.inet6.ip6.forwarding=1
+
+	jexec srv ifconfig ${epair_out}b inet6 2001:db8:1::2/64 up no_dad
+	jexec srv route -6 add default 2001:db8:1::1
+	jexec srv /usr/sbin/inetd -p inetd-alcatraz.pid \
+	    $(atf_get_srcdir)/echo_inetd.conf
+
+	ifconfig ${epair_in}a inet6 2001:db8::2/64 up no_dad
+	route -6 add -net 2001:db8:1::/64 2001:db8::1
+
+	jexec fwd pfctl -e
+	pft_set_rules fwd \
+		"set syncookies always" \
+		"pass in" \
+		"pass out"
+
+	# Sanity check
+	atf_check -s exit:0 -o ignore ping6 -c 1 2001:db8:1::2
+
+	reply=$(echo foo | nc -N -w 5 2001:db8:1::2 7)
+	if [ "${reply}" != "foo" ];
+	then
+		atf_fail "Failed to connect to syncookie protected echo daemon"
+	fi
+}
+
+forward_v6_cleanup()
+{
+	rm -f inetd-alcatraz.pid
+	pft_cleanup
+}
+
 atf_test_case "nostate" "cleanup"
 nostate_head()
 {
@@ -183,6 +282,53 @@ nostate_cleanup()
 	pft_cleanup
 }
 
+atf_test_case "nostate_v6" "cleanup"
+nostate_v6_head()
+{
+	atf_set descr 'Ensure that we do not create until SYN|ACK'
+	atf_set require.user root
+	atf_set require.progs scapy
+}
+
+nostate_v6_body()
+{
+	pft_init
+
+	epair=$(vnet_mkepair)
+	ifconfig ${epair}a inet6 2001:db8::2/64 up no_dad
+
+	vnet_mkjail alcatraz ${epair}b
+	jexec alcatraz ifconfig ${epair}b inet6 2001:db8::1/64 up no_dad
+
+	jexec alcatraz pfctl -e
+	pft_set_rules alcatraz \
+		"set syncookies always" \
+		"pass in" \
+		"pass out"
+
+	# Sanity check
+	atf_check -s exit:0 -o ignore ping6 -c 1 2001:db8::1
+
+	# Now syn flood to create many states
+	${common_dir}/pft_synflood.py \
+        --ip6 \
+		--sendif ${epair}a \
+		--to 2001:db8::2 \
+		--count 20
+
+	states=$(jexec alcatraz pfctl -ss | grep tcp)
+	if [ -n "$states" ];
+	then
+		echo "$states"
+		atf_fail "Found unexpected state"
+	fi
+}
+
+nostate_v6_cleanup()
+{
+	pft_cleanup
+}
+
 atf_test_case "adaptive" "cleanup"
 adaptive_head()
 {
@@ -305,8 +451,11 @@ port_reuse_cleanup()
 atf_init_test_cases()
 {
 	atf_add_test_case "basic"
+	atf_add_test_case "basic_v6"
 	atf_add_test_case "forward"
+	atf_add_test_case "forward_v6"
 	atf_add_test_case "nostate"
+	atf_add_test_case "nostate_v6"
 	atf_add_test_case "adaptive"
 	atf_add_test_case "port_reuse"
 }