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