xref: /freebsd/tests/sys/netpfil/pf/killstate.sh (revision edf8578117e8844e02c0121147f45e4609b30680)
1#
2# SPDX-License-Identifier: BSD-2-Clause
3#
4# Copyright (c) 2021 Rubicon Communications, LLC (Netgate)
5#
6# Redistribution and use in source and binary forms, with or without
7# modification, are permitted provided that the following conditions
8# are met:
9# 1. Redistributions of source code must retain the above copyright
10#    notice, this list of conditions and the following disclaimer.
11# 2. Redistributions in binary form must reproduce the above copyright
12#    notice, this list of conditions and the following disclaimer in the
13#    documentation and/or other materials provided with the distribution.
14#
15# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25# SUCH DAMAGE.
26
27. $(atf_get_srcdir)/utils.subr
28
29common_dir=$(atf_get_srcdir)/../common
30
31find_state()
32{
33	jexec alcatraz pfctl -ss | grep icmp | grep 192.0.2.2
34}
35
36find_state_v6()
37{
38	jexec alcatraz pfctl -ss | grep icmp | grep 2001:db8::2
39}
40
41
42atf_test_case "v4" "cleanup"
43v4_head()
44{
45	atf_set descr 'Test killing states by IPv4 address'
46	atf_set require.user root
47	atf_set require.progs scapy
48}
49
50v4_body()
51{
52	pft_init
53
54	epair=$(vnet_mkepair)
55	ifconfig ${epair}a 192.0.2.1/24 up
56
57	vnet_mkjail alcatraz ${epair}b
58	jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
59	jexec alcatraz pfctl -e
60
61	pft_set_rules alcatraz "block all" \
62		"pass in proto icmp" \
63		"set skip on lo"
64
65	# Sanity check & establish state
66	atf_check -s exit:0 -o ignore ${common_dir}/pft_ping.py \
67		--sendif ${epair}a \
68		--to 192.0.2.2 \
69		--replyif ${epair}a
70
71	# Change rules to now deny the ICMP traffic
72	pft_set_rules noflush alcatraz "block all"
73	if ! find_state;
74	then
75		atf_fail "Setting new rules removed the state."
76	fi
77
78	# Killing with the wrong IP doesn't affect our state
79	jexec alcatraz pfctl -k 192.0.2.3
80	if ! find_state;
81	then
82		atf_fail "Killing with the wrong IP removed our state."
83	fi
84
85	# Killing with one correct address and one incorrect doesn't kill the state
86	jexec alcatraz pfctl -k 192.0.2.1 -k 192.0.2.3
87	if ! find_state;
88	then
89		atf_fail "Killing with one wrong IP removed our state."
90	fi
91
92	# Killing with correct address does remove the state
93	jexec alcatraz pfctl -k 192.0.2.1
94	if find_state;
95	then
96		atf_fail "Killing with the correct IP did not remove our state."
97	fi
98}
99
100v4_cleanup()
101{
102	pft_cleanup
103}
104
105atf_test_case "v6" "cleanup"
106v6_head()
107{
108	atf_set descr 'Test killing states by IPv6 address'
109	atf_set require.user root
110	atf_set require.progs scapy
111}
112
113v6_body()
114{
115	pft_init
116
117	if [ "$(atf_config_get ci false)" = "true" ]; then
118		atf_skip "https://bugs.freebsd.org/260458"
119	fi
120
121	epair=$(vnet_mkepair)
122	ifconfig ${epair}a inet6 2001:db8::1/64 up no_dad
123
124	vnet_mkjail alcatraz ${epair}b
125	jexec alcatraz ifconfig ${epair}b inet6 2001:db8::2/64 up no_dad
126	jexec alcatraz pfctl -e
127
128	pft_set_rules alcatraz "block all" \
129		"pass in proto icmp6" \
130		"set skip on lo"
131
132	# Sanity check & establish state
133	atf_check -s exit:0 -o ignore ${common_dir}/pft_ping.py \
134		--sendif ${epair}a \
135		--to 2001:db8::2 \
136		--replyif ${epair}a
137
138	# Change rules to now deny the ICMP traffic
139	pft_set_rules noflush alcatraz "block all"
140	if ! find_state_v6;
141	then
142		atf_fail "Setting new rules removed the state."
143	fi
144
145	# Killing with the wrong IP doesn't affect our state
146	jexec alcatraz pfctl -k 2001:db8::3
147	if ! find_state_v6;
148	then
149		atf_fail "Killing with the wrong IP removed our state."
150	fi
151
152	# Killing with one correct address and one incorrect doesn't kill the state
153	jexec alcatraz pfctl -k 2001:db8::1 -k 2001:db8::3
154	if ! find_state_v6;
155	then
156		atf_fail "Killing with one wrong IP removed our state."
157	fi
158
159	# Killing with correct address does remove the state
160	jexec alcatraz pfctl -k 2001:db8::1
161	if find_state_v6;
162	then
163		atf_fail "Killing with the correct IP did not remove our state."
164	fi
165}
166
167v6_cleanup()
168{
169	pft_cleanup
170}
171
172atf_test_case "label" "cleanup"
173label_head()
174{
175	atf_set descr 'Test killing states by label'
176	atf_set require.user root
177	atf_set require.progs scapy
178}
179
180label_body()
181{
182	pft_init
183
184	epair=$(vnet_mkepair)
185	ifconfig ${epair}a 192.0.2.1/24 up
186
187	vnet_mkjail alcatraz ${epair}b
188	jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
189	jexec alcatraz pfctl -e
190
191	pft_set_rules alcatraz "block all" \
192		"pass in proto tcp label bar" \
193		"pass in proto icmp label foo" \
194		"set skip on lo"
195
196	# Sanity check & establish state
197	atf_check -s exit:0 -o ignore ${common_dir}/pft_ping.py \
198		--sendif ${epair}a \
199		--to 192.0.2.2 \
200		--replyif ${epair}a
201
202	# Change rules to now deny the ICMP traffic
203	pft_set_rules noflush alcatraz "block all"
204	if ! find_state;
205	then
206		atf_fail "Setting new rules removed the state."
207	fi
208
209	# Killing a label on a different rules keeps the state
210	jexec alcatraz pfctl -k label -k bar
211	if ! find_state;
212	then
213		atf_fail "Killing a different label removed the state."
214	fi
215
216	# Killing a non-existing label keeps the state
217	jexec alcatraz pfctl -k label -k baz
218	if ! find_state;
219	then
220		atf_fail "Killing a non-existing label removed the state."
221	fi
222
223	# Killing the correct label kills the state
224	jexec alcatraz pfctl -k label -k foo
225	if find_state;
226	then
227		atf_fail "Killing the state did not remove it."
228	fi
229}
230
231label_cleanup()
232{
233	pft_cleanup
234}
235
236atf_test_case "multilabel" "cleanup"
237multilabel_head()
238{
239	atf_set descr 'Test killing states with multiple labels by label'
240	atf_set require.user root
241	atf_set require.progs scapy
242}
243
244multilabel_body()
245{
246	pft_init
247
248	epair=$(vnet_mkepair)
249	ifconfig ${epair}a 192.0.2.1/24 up
250
251	vnet_mkjail alcatraz ${epair}b
252	jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
253	jexec alcatraz pfctl -e
254
255	pft_set_rules alcatraz "block all" \
256		"pass in proto icmp label foo label bar" \
257		"set skip on lo"
258
259	# Sanity check & establish state
260	atf_check -s exit:0 -o ignore ${common_dir}/pft_ping.py \
261		--sendif ${epair}a \
262		--to 192.0.2.2 \
263		--replyif ${epair}a
264
265	# Change rules to now deny the ICMP traffic
266	pft_set_rules noflush alcatraz "block all"
267	if ! find_state;
268	then
269		atf_fail "Setting new rules removed the state."
270	fi
271
272	# Killing a label on a different rules keeps the state
273	jexec alcatraz pfctl -k label -k baz
274	if ! find_state;
275	then
276		atf_fail "Killing a different label removed the state."
277	fi
278
279	# Killing the state with the last label works
280	jexec alcatraz pfctl -k label -k bar
281	if find_state;
282	then
283		atf_fail "Killing with the last label did not remove the state."
284	fi
285
286	pft_set_rules alcatraz "block all" \
287		"pass in proto icmp label foo label bar" \
288		"set skip on lo"
289
290	# Reestablish state
291	atf_check -s exit:0 -o ignore ${common_dir}/pft_ping.py \
292		--sendif ${epair}a \
293		--to 192.0.2.2 \
294		--replyif ${epair}a
295
296	# Change rules to now deny the ICMP traffic
297	pft_set_rules noflush alcatraz "block all"
298	if ! find_state;
299	then
300		atf_fail "Setting new rules removed the state."
301	fi
302
303	# Killing with the first label works too
304	jexec alcatraz pfctl -k label -k foo
305	if find_state;
306	then
307		atf_fail "Killing with the first label did not remove the state."
308	fi
309}
310
311multilabel_cleanup()
312{
313	pft_cleanup
314}
315
316atf_test_case "gateway" "cleanup"
317gateway_head()
318{
319	atf_set descr 'Test killing states by route-to/reply-to address'
320	atf_set require.user root
321	atf_set require.progs scapy
322}
323
324gateway_body()
325{
326	pft_init
327
328	epair=$(vnet_mkepair)
329	ifconfig ${epair}a 192.0.2.1/24 up
330
331	vnet_mkjail alcatraz ${epair}b
332	jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
333	jexec alcatraz pfctl -e
334
335	pft_set_rules alcatraz "block all" \
336		"pass in reply-to (${epair}b 192.0.2.1) proto icmp" \
337		"set skip on lo"
338
339	# Sanity check & establish state
340	# Note: use pft_ping so we always use the same ID, so pf considers all
341	# echo requests part of the same flow.
342	atf_check -s exit:0 -o ignore ${common_dir}/pft_ping.py \
343		--sendif ${epair}a \
344		--to 192.0.2.2 \
345		--replyif ${epair}a
346
347	# Change rules to now deny the ICMP traffic
348	pft_set_rules noflush alcatraz "block all"
349	if ! find_state;
350	then
351		atf_fail "Setting new rules removed the state."
352	fi
353
354	# Killing with a different gateway does not affect our state
355	jexec alcatraz pfctl -k gateway -k 192.0.2.2
356	if ! find_state;
357	then
358		atf_fail "Killing with a different gateway removed the state."
359	fi
360
361	# Killing states with the relevant gateway does terminate our state
362	jexec alcatraz pfctl -k gateway -k 192.0.2.1
363	if find_state;
364	then
365		atf_fail "Killing with the gateway did not remove the state."
366	fi
367}
368
369gateway_cleanup()
370{
371	pft_cleanup
372}
373
374atf_test_case "match" "cleanup"
375match_head()
376{
377	atf_set descr 'Test killing matching states'
378	atf_set require.user root
379}
380
381wait_for_state()
382{
383	jail=$1
384	addr=$2
385
386	while ! jexec $jail pfctl -s s | grep $addr >/dev/null;
387	do
388		sleep .1
389	done
390}
391
392match_body()
393{
394	pft_init
395
396	epair_one=$(vnet_mkepair)
397	ifconfig ${epair_one}a 192.0.2.1/24 up
398
399	epair_two=$(vnet_mkepair)
400
401	vnet_mkjail alcatraz ${epair_one}b ${epair_two}a
402	jexec alcatraz ifconfig ${epair_one}b 192.0.2.2/24 up
403	jexec alcatraz ifconfig ${epair_two}a 198.51.100.1/24 up
404	jexec alcatraz sysctl net.inet.ip.forwarding=1
405	jexec alcatraz pfctl -e
406
407	vnet_mkjail singsing ${epair_two}b
408	jexec singsing ifconfig ${epair_two}b 198.51.100.2/24 up
409	jexec singsing route add default 198.51.100.1
410	jexec singsing /usr/sbin/inetd -p inetd-echo.pid \
411	    $(atf_get_srcdir)/echo_inetd.conf
412
413	route add 198.51.100.0/24 192.0.2.2
414
415	pft_set_rules alcatraz \
416		"nat on ${epair_two}a from 192.0.2.0/24 -> (${epair_two}a)" \
417		"pass all"
418
419	nc 198.51.100.2 7 &
420	wait_for_state alcatraz 192.0.2.1
421
422	# Expect two states
423	states=$(jexec alcatraz pfctl -s s | grep 192.0.2.1 | wc -l)
424	if [ $states -ne 2 ] ;
425	then
426		atf_fail "Expected two states, found $states"
427	fi
428
429	# If we don't kill the matching NAT state one should be left
430	jexec alcatraz pfctl -k 192.0.2.1
431	states=$(jexec alcatraz pfctl -s s | grep 192.0.2.1 | wc -l)
432	if [ $states -ne 1 ] ;
433	then
434		atf_fail "Expected one states, found $states"
435	fi
436
437	# Flush
438	jexec alcatraz pfctl -F states
439
440	nc 198.51.100.2 7 &
441	wait_for_state alcatraz 192.0.2.1
442
443	# Kill matching states, expect all of them to be gone
444	jexec alcatraz pfctl -M -k 192.0.2.1
445	states=$(jexec alcatraz pfctl -s s | grep 192.0.2.1 | wc -l)
446	if [ $states -ne 0 ] ;
447	then
448		atf_fail "Expected zero states, found $states"
449	fi
450}
451
452match_cleanup()
453{
454	pft_cleanup
455}
456
457atf_test_case "interface" "cleanup"
458interface_head()
459{
460	atf_set descr 'Test killing states based on interface'
461	atf_set require.user root
462	atf_set require.progs scapy
463}
464
465interface_body()
466{
467	pft_init
468
469	epair=$(vnet_mkepair)
470	ifconfig ${epair}a 192.0.2.1/24 up
471
472	vnet_mkjail alcatraz ${epair}b
473	jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
474	jexec alcatraz pfctl -e
475
476	pft_set_rules alcatraz "block all" \
477		"pass in proto icmp" \
478		"set skip on lo"
479
480	# Sanity check & establish state
481	atf_check -s exit:0 -o ignore ${common_dir}/pft_ping.py \
482		--sendif ${epair}a \
483		--to 192.0.2.2 \
484		--replyif ${epair}a
485
486	# Change rules to now deny the ICMP traffic
487	pft_set_rules noflush alcatraz "block all"
488	if ! find_state;
489	then
490		atf_fail "Setting new rules removed the state."
491	fi
492
493	# Flushing states on a different interface doesn't affect our state
494	jexec alcatraz pfctl -i ${epair}a -Fs
495	if ! find_state;
496	then
497		atf_fail "Flushing on a different interface removed the state."
498	fi
499
500	# Flushing on the correct interface does (even with floating states)
501	jexec alcatraz pfctl -i ${epair}b -Fs
502	if find_state;
503	then
504		atf_fail "Flushing on a the interface did not remove the state."
505	fi
506}
507
508interface_cleanup()
509{
510	pft_cleanup
511}
512
513atf_test_case "id" "cleanup"
514id_head()
515{
516	atf_set descr 'Test killing states by id'
517	atf_set require.user root
518	atf_set require.progs scapy
519}
520
521id_body()
522{
523	pft_init
524
525	epair=$(vnet_mkepair)
526	ifconfig ${epair}a 192.0.2.1/24 up
527
528	vnet_mkjail alcatraz ${epair}b
529	jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
530	jexec alcatraz pfctl -e
531
532	pft_set_rules alcatraz "block all" \
533		"pass in proto tcp" \
534		"pass in proto icmp" \
535		"set skip on lo"
536
537	# Sanity check & establish state
538	atf_check -s exit:0 -o ignore ${common_dir}/pft_ping.py \
539		--sendif ${epair}a \
540		--to 192.0.2.2 \
541		--replyif ${epair}a
542
543	# Change rules to now deny the ICMP traffic
544	pft_set_rules noflush alcatraz "block all"
545	if ! find_state;
546	then
547		atf_fail "Setting new rules removed the state."
548	fi
549
550	# Get the state ID
551	id=$(jexec alcatraz pfctl -ss -vvv | grep -A 3 icmp |
552	    grep -A 3 192.0.2.2 | awk '/id:/ { printf("%s/%s", $2, $4); }')
553
554	# Kill the wrong ID
555	jexec alcatraz pfctl -k id -k 1
556	if ! find_state;
557	then
558		atf_fail "Killing a different ID removed the state."
559	fi
560
561	# Kill the correct ID
562	jexec alcatraz pfctl -k id -k ${id}
563	if find_state;
564	then
565		atf_fail "Killing the state did not remove it."
566	fi
567}
568
569id_cleanup()
570{
571	pft_cleanup
572}
573
574atf_init_test_cases()
575{
576	atf_add_test_case "v4"
577	atf_add_test_case "v6"
578	atf_add_test_case "label"
579	atf_add_test_case "multilabel"
580	atf_add_test_case "gateway"
581	atf_add_test_case "match"
582	atf_add_test_case "interface"
583	atf_add_test_case "id"
584}
585