xref: /freebsd/tests/sys/netpfil/pf/utils.subr (revision 6353f5d9a5c6f194bb014b8785a57f5314e8c652)
1# Utility functions
2##
3# SPDX-License-Identifier: BSD-2-Clause
4#
5# Copyright (c) 2017 Kristof Provost <kp@FreeBSD.org>
6# Copyright (c) 2023 Kajetan Staszkiewicz <vegeta@tuxpowered.net>
7#
8# Redistribution and use in source and binary forms, with or without
9# modification, are permitted provided that the following conditions
10# are met:
11# 1. Redistributions of source code must retain the above copyright
12#    notice, this list of conditions and the following disclaimer.
13# 2. Redistributions in binary form must reproduce the above copyright
14#    notice, this list of conditions and the following disclaimer in the
15#    documentation and/or other materials provided with the distribution.
16#
17# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27# SUCH DAMAGE.
28
29. $(atf_get_srcdir)/../../common/vnet.subr
30common_dir=$(atf_get_srcdir)/../common
31
32pft_onerror()
33{
34	status=$?
35
36	echo "Debug log."
37	echo "=========="
38	echo "Test exit status: $?"
39	echo
40
41	if [ -f created_jails.lst ]; then
42		for jailname in `cat created_jails.lst`
43		do
44			echo "Jail ${jailname}"
45			echo "----------------"
46			jexec ${jailname} ifconfig
47			jexec ${jailname} netstat -rn
48			jexec ${jailname} pfctl -sa -v
49		done
50	fi
51
52	echo "Created interfaces:"
53	echo "-------------------"
54	cat created_interfaces.lst
55
56	echo "Host interfaces:"
57	echo "----------------"
58	ifconfig
59}
60
61pft_init()
62{
63	if [ "$1" == "debug" ]
64	then
65		trap pft_onerror EXIT
66	fi
67
68	vnet_init
69
70	if [ ! -c /dev/pf ]; then
71		atf_skip "This test requires pf"
72	fi
73}
74
75pfsynct_init()
76{
77	pft_init
78
79	if ! kldstat -q -m pfsync; then
80		atf_skip "This test requires pfsync"
81	fi
82}
83
84pflog_init()
85{
86	pft_init
87
88	if ! kldstat -q -m pflog; then
89		atf_skip "This test requires pflog"
90	fi
91}
92
93pflow_init()
94{
95	pft_init
96
97	if ! kldstat -q -m pflow; then
98		atf_skip "This test requires pflow"
99	fi
100}
101
102dummynet_init()
103{
104	pft_init
105
106	if ! kldstat -q -m dummynet; then
107		atf_skip "This test requires dummynet"
108	fi
109}
110
111pft_set_rules()
112{
113	jname=$1
114	shift
115
116	if [ $jname == "noflush" ];
117	then
118		jname=$1
119		shift
120	else
121		# Flush all states, rules, fragments, ...
122		jexec ${jname} pfctl -F all
123	fi
124
125	while [ $# -gt 0 ]; do
126		printf "$1\n"
127		shift
128	done | jexec ${jname} pfctl -f -
129	if [ $? -ne 0 ];
130	then
131		atf_fail "Failed to set PF rules in ${jname}"
132	fi
133}
134
135pft_cleanup()
136{
137	vnet_cleanup
138}
139
140pfsynct_cleanup()
141{
142	pft_cleanup
143}
144
145is_altq_supported()
146{
147	sysctl -q kern.features.altq >/dev/null || \
148	    atf_skip "Test requires ALTQ"
149
150	while [ -n "$1" ]
151	do
152		sysctl -q kern.features.altq.${1} >/dev/null || \
153		    atf_skip "Test required ALTQ_${1}"
154		shift
155	done
156}
157
158altq_init()
159{
160	pft_init
161	is_altq_supported
162}
163
164altq_cleanup()
165{
166	pft_cleanup
167}
168
169# Create a bare router jail.
170# This function lacks target configuration.
171setup_router_ipv4()
172{
173	pft_init
174
175	epair_tester=$(vnet_mkepair)
176	epair_server=$(vnet_mkepair)
177
178	net_tester=192.0.2.0/24
179	net_tester_mask=24
180	net_tester_host_router=192.0.2.1
181	net_tester_host_tester=192.0.2.2
182
183	net_server=198.51.100.0/24
184	net_server_mask=24
185	net_server_host_router=198.51.100.1
186	net_server_host_server=198.51.100.2
187
188	vnet_mkjail router ${epair_tester}b ${epair_server}a
189
190	ifconfig ${epair_tester}a ${net_tester_host_tester}/${net_tester_mask} up
191	route add -net ${net_server} ${net_tester_host_router}
192
193	jexec router ifconfig ${epair_tester}b ${net_tester_host_router}/${net_tester_mask} up
194	jexec router sysctl net.inet.ip.forwarding=1
195	jexec router ifconfig ${epair_server}a ${net_server_host_router}/${net_server_mask} up
196
197	jexec router pfctl -e
198}
199
200# Create a router jail.
201# The target for tests does not exist but a static ARP entry does
202# so packets to it can be properly routed.
203setup_router_dummy_ipv4()
204{
205	setup_router_ipv4
206	jexec router arp -s ${net_server_host_server} 00:01:02:03:04:05
207	ifconfig ${epair_server}b up
208}
209
210# Create a router and a server jail.
211# The server is capable of responding to pings from the tester.
212setup_router_server_ipv4()
213{
214	setup_router_ipv4
215	vnet_mkjail server ${epair_server}b
216	jexec server ifconfig ${epair_server}b ${net_server_host_server}/${net_server_mask} up
217	jexec server route add -net ${net_tester} ${net_server_host_router}
218	inetd_conf=$(mktemp)
219	echo "discard stream tcp nowait root internal" > $inetd_conf
220	echo "echo stream tcp nowait root internal" >> $inetd_conf
221	jexec server inetd -p ${PWD}/inetd.pid $inetd_conf
222}
223
224# Create a bare router jail.
225# This function lacks target configuration.
226setup_router_ipv6()
227{
228	pft_init
229
230	epair_tester=$(vnet_mkepair)
231	epair_server=$(vnet_mkepair)
232
233	net_tester=2001:db8:42::/64
234	net_tester_mask=64
235	net_tester_host_router=2001:db8:42::1
236	net_tester_host_tester=2001:db8:42::2
237
238	net_server=2001:db8:43::/64
239	net_server_mask=64
240	net_server_host_router=2001:db8:43::1
241	net_server_host_server=2001:db8:43::2
242
243	vnet_mkjail router ${epair_tester}b ${epair_server}a
244
245	ifconfig ${epair_tester}a inet6 ${net_tester_host_tester}/${net_tester_mask}up no_dad
246	route add -6 ${net_server} ${net_tester_host_router}
247
248	jexec router ifconfig ${epair_tester}b inet6 ${net_tester_host_router}/${net_tester_mask} up no_dad
249	jexec router sysctl net.inet6.ip6.forwarding=1
250	jexec router ifconfig ${epair_server}a inet6 ${net_server_host_router}/${net_server_mask} up no_dad
251
252	jexec router pfctl -e
253}
254
255# Create a router jail.
256# The target for tests does not exist but a static NDP entry does
257# so packets to it can be properly routed.
258setup_router_dummy_ipv6()
259{
260	setup_router_ipv6
261	jexec router ndp -s ${net_server_host_server} 00:01:02:03:04:05
262	ifconfig ${epair_server}b up
263}
264
265# Create a router and a server jail.
266# The server is capable of responding to pings from tester.
267setup_router_server_ipv6()
268{
269	setup_router_ipv6
270	vnet_mkjail server ${epair_server}b
271	jexec server ifconfig ${epair_server}b inet6 ${net_server_host_server}/${net_server_mask} up no_dad
272	jexec server route add -6 ${net_tester} ${net_server_host_router}
273	inetd_conf=$(mktemp)
274	echo "discard stream tcp6 nowait root internal" > $inetd_conf
275	echo "echo stream tcp6 nowait root internal" >> $inetd_conf
276	jexec server inetd -p ${PWD}/inetd.pid $inetd_conf
277}
278
279# Create a router and 2 server jails for nat64 and prefer-ipv6-nexthop test
280# cases. The router is connected to servers, both are dual-stack, and to the
281# tester jail. All links are dual stack.
282setup_router_server_nat64()
283{
284	pft_init
285
286	epair_tester=$(vnet_mkepair)
287	epair_server1=$(vnet_mkepair)
288	epair_server2=$(vnet_mkepair)
289
290	# Funny how IPv4 address space is to small to even assign nice /24
291	# prefixes on all needed networks. On IPv6 we have a separate /64 for
292	# each link, loopback server, and client/SNAT pool. On IPv4 we must
293	# use small /28 prefixes, so even though we define all networks
294	# as variables we can't easily use them in tests if additional addresses
295	# are needed.
296
297	# IP addresses which can be used by the tester jail.
298	# Can be used as SNAT or as source with pft_ping.py. It is up to
299	# the test code to make them accessible from router.
300	net_clients_4=203.0.113
301	net_clients_4_mask=24
302	net_clients_6=2001:db8:44
303	net_clients_6_mask=64
304
305	# IP addresses on loopback interfaces of both servers. They can be
306	# accessed using the route-to targtet.
307	host_server_4=192.0.2.100
308	host_server_6=2001:db8:4203::100
309
310	net_tester_4=198.51.100
311	net_tester_4_mask=28
312	net_tester_4_host_router=198.51.100.1
313	net_tester_4_host_tester=198.51.100.2
314
315	net_tester_6=2001:db8:4200
316	net_tester_6_mask=64
317	net_tester_6_host_router=2001:db8:4200::1
318	net_tester_6_host_tester=2001:db8:4200::2
319
320	net_server1_4=198.51.100
321	net_server1_4_mask=28
322	net_server1_4_host_router=198.51.100.17
323	net_server1_4_host_server=198.51.100.18
324
325	net_server1_6=2001:db8:4201
326	net_server1_6_mask=64
327	net_server1_6_host_router=2001:db8:4201::1
328	net_server1_6_host_server=2001:db8:4201::2
329
330	net_server2_4=198.51.100
331	net_server2_4_mask=28
332	net_server2_4_host_router=198.51.100.33
333	net_server2_4_host_server=198.51.100.34
334
335	net_server2_6=2001:db8:4202
336	net_server2_6_mask=64
337	net_server2_6_host_router=2001:db8:4202::1
338	net_server2_6_host_server=2001:db8:4202::2
339
340	vnet_mkjail router ${epair_tester}b ${epair_server1}a ${epair_server2}a
341	jexec router ifconfig ${epair_tester}b inet  ${net_tester_4_host_router}/${net_tester_4_mask} up
342	jexec router ifconfig ${epair_tester}b inet6 ${net_tester_6_host_router}/${net_tester_6_mask} up no_dad
343	jexec router ifconfig ${epair_server1}a inet  ${net_server1_4_host_router}/${net_server1_4_mask} up
344	jexec router ifconfig ${epair_server1}a inet6 ${net_server1_6_host_router}/${net_server1_6_mask} up no_dad
345	jexec router ifconfig ${epair_server2}a inet  ${net_server2_4_host_router}/${net_server2_4_mask} up
346	jexec router ifconfig ${epair_server2}a inet6 ${net_server2_6_host_router}/${net_server2_6_mask} up no_dad
347	jexec router sysctl net.inet.ip.forwarding=1
348	jexec router sysctl net.inet6.ip6.forwarding=1
349	jexec router pfctl -e
350
351	ifconfig ${epair_tester}a inet  ${net_tester_4_host_tester}/${net_tester_4_mask} up
352	ifconfig ${epair_tester}a inet6 ${net_tester_6_host_tester}/${net_tester_6_mask} up no_dad
353	route add    0.0.0.0/0 ${net_tester_4_host_router}
354	route add -6 ::/0      ${net_tester_6_host_router}
355
356	inetd_conf=$(mktemp)
357	echo "discard stream tcp46 nowait root internal" >> $inetd_conf
358	echo "echo stream tcp46 nowait root internal" >> $inetd_conf
359
360	vnet_mkjail server1 ${epair_server1}b
361	jexec server1 /etc/rc.d/netif start lo0
362	jexec server1 ifconfig ${epair_server1}b inet  ${net_server1_4_host_server}/${net_server1_4_mask} up
363	jexec server1 ifconfig ${epair_server1}b inet6 ${net_server1_6_host_server}/${net_server1_6_mask} up no_dad
364	jexec server1 ifconfig lo0                     ${host_server_4}/32  alias
365	jexec server1 ifconfig lo0               inet6 ${host_server_6}/128 alias
366	jexec server1 inetd -p ${PWD}/inetd_1.pid $inetd_conf
367	jexec server1 route add    0.0.0.0/0 ${net_server1_4_host_router}
368
369	jexec server1 route add -6 ::/0      ${net_server1_6_host_router}
370	vnet_mkjail server2 ${epair_server2}b
371	jexec server2 /etc/rc.d/netif start lo0
372	jexec server2 ifconfig ${epair_server2}b inet  ${net_server2_4_host_server}/${net_server2_4_mask} up
373	jexec server2 ifconfig ${epair_server2}b inet6 ${net_server2_6_host_server}/${net_server2_6_mask} up no_dad
374	jexec server2 ifconfig lo0                     ${host_server_4}/32  alias
375	jexec server2 ifconfig lo0               inet6 ${host_server_6}/128 alias
376	jexec server2 inetd -p ${PWD}/inetd_2.pid $inetd_conf
377	jexec server2 route add    0.0.0.0/0 ${net_server2_4_host_router}
378	jexec server2 route add -6 ::/0      ${net_server2_6_host_router}
379}
380
381# Ping the dummy static NDP target.
382# Check for pings being forwarded through the router towards the target.
383ping_dummy_check_request()
384{
385	exit_condition=$1
386	shift
387	params=$@
388	atf_check -s ${exit_condition} ${common_dir}/pft_ping.py \
389	    --sendif ${epair_tester}a \
390	    --to ${net_server_host_server} \
391	    --recvif ${epair_server}b \
392	$params
393}
394
395# Ping the server jail.
396# Check for responses coming back throught the router back to the tester.
397ping_server_check_reply()
398{
399	exit_condition=$1
400	shift
401	params=$@
402	atf_check -s ${exit_condition} ${common_dir}/pft_ping.py \
403	    --sendif ${epair_tester}a \
404	    --to ${net_server_host_server} \
405	    --replyif ${epair_tester}a \
406	$params
407}
408
409normalize_pfctl_s()
410{
411	# `pfctl -s[rsS]` output is divided into sections. Each rule, state or
412	# source node starts with the beginning of a line and next lines with leading
413	# spaces are various parameters of said rule, state or source node.
414	# Convert it into a single line per entry, and remove multiple spaces,
415	# so that regular expressions for matching them in tests can be simpler.
416	awk '{ if ($0 ~ /^[^ ]/ && NR > 1) print(""); gsub(/ +/, " ", $0); printf("%s", $0); } END {print("");}'
417}
418