xref: /freebsd/tests/sys/netpfil/pf/table.sh (revision 0749d8134300b8e3c956e161890ab496247d2542)
1#
2# SPDX-License-Identifier: BSD-2-Clause
3#
4# Copyright (c) 2020 Mark Johnston <markj@FreeBSD.org>
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
29TABLE_STATS_ZERO_REGEXP='Packets: 0[[:space:]]*Bytes: 0[[:space:]]'
30TABLE_STATS_NONZERO_REGEXP='Packets: [1-9][0-9]*[[:space:]]*Bytes: [1-9][0-9]*[[:space:]]'
31
32atf_test_case "v4_counters" "cleanup"
33v4_counters_head()
34{
35	atf_set descr 'Verify per-address counters for v4'
36	atf_set require.user root
37}
38
39v4_counters_body()
40{
41	pft_init
42
43	epair_send=$(vnet_mkepair)
44	ifconfig ${epair_send}a 192.0.2.1/24 up
45
46	vnet_mkjail alcatraz ${epair_send}b
47	jexec alcatraz ifconfig ${epair_send}b 192.0.2.2/24 up
48	jexec alcatraz pfctl -e
49
50	pft_set_rules alcatraz \
51	    "table <foo> counters { 192.0.2.1 }" \
52	    "block all" \
53	    "pass in from <foo> to any" \
54	    "pass out from any to <foo>" \
55	    "set skip on lo"
56
57	atf_check -s exit:0 -o ignore ping -c 3 192.0.2.2
58
59	atf_check -s exit:0 -e ignore \
60	    -o match:'In/Block:.*'"$TABLE_STATS_ZERO_REGEXP" \
61	    -o match:'In/Pass:.*'"$TABLE_STATS_NONZERO_REGEXP" \
62	    -o match:'Out/Block:.*'"$TABLE_STATS_ZERO_REGEXP" \
63	    -o match:'Out/Pass:.*'"$TABLE_STATS_NONZERO_REGEXP" \
64	    jexec alcatraz pfctl -t foo -T show -vv
65}
66
67v4_counters_cleanup()
68{
69	pft_cleanup
70}
71
72atf_test_case "v6_counters" "cleanup"
73v6_counters_head()
74{
75	atf_set descr 'Verify per-address counters for v6'
76	atf_set require.user root
77}
78
79v6_counters_body()
80{
81	pft_init
82
83	epair_send=$(vnet_mkepair)
84	ifconfig ${epair_send}a inet6 2001:db8:42::1/64 up no_dad -ifdisabled
85
86	vnet_mkjail alcatraz ${epair_send}b
87	jexec alcatraz ifconfig ${epair_send}b inet6 2001:db8:42::2/64 up no_dad
88	jexec alcatraz pfctl -e
89
90	pft_set_rules alcatraz \
91	    "table <foo6> counters { 2001:db8:42::1 }" \
92	    "block all" \
93	    "pass in from <foo6> to any" \
94	    "pass out from any to <foo6>" \
95	    "set skip on lo"
96
97	atf_check -s exit:0 -o ignore ping -6 -c 3 2001:db8:42::2
98
99	atf_check -s exit:0 -e ignore \
100	    -o match:'In/Block:.*'"$TABLE_STATS_ZERO_REGEXP" \
101	    -o match:'In/Pass:.*'"$TABLE_STATS_NONZERO_REGEXP" \
102	    -o match:'Out/Block:.*'"$TABLE_STATS_ZERO_REGEXP" \
103	    -o match:'Out/Pass:.*'"$TABLE_STATS_NONZERO_REGEXP" \
104	    jexec alcatraz pfctl -t foo6 -T show -vv
105}
106
107v6_counters_cleanup()
108{
109	pft_cleanup
110}
111
112atf_test_case "zero_one" "cleanup"
113zero_one_head()
114{
115	atf_set descr 'Test zeroing a single address in a table'
116	atf_set require.user root
117}
118
119pft_cleared_ctime()
120{
121	jexec "$1" pfctl -t "$2" -vvT show | awk -v ip="$3" '
122	  ($1 == ip) { m = 1 }
123	  ($1 == "Cleared:" && m) {
124	    sub("[[:space:]]*Cleared:[[:space:]]*", ""); print; exit }'
125}
126
127ctime_to_unixtime()
128{
129	# NB: it's not TZ=UTC, it's TZ=/etc/localtime
130	date -jf '%a %b %d %H:%M:%S %Y' "$1" '+%s'
131}
132
133zero_one_body()
134{
135	epair_send=$(vnet_mkepair)
136	ifconfig ${epair_send}a 192.0.2.1/24 up
137	ifconfig ${epair_send}a inet alias 192.0.2.3/24
138
139	vnet_mkjail alcatraz ${epair_send}b
140	jexec alcatraz ifconfig ${epair_send}b 192.0.2.2/24 up
141	jexec alcatraz pfctl -e
142
143	pft_set_rules alcatraz \
144	    "table <foo> counters { 192.0.2.1, 192.0.2.3 }" \
145	    "block all" \
146	    "pass in from <foo> to any" \
147	    "pass out from any to <foo>" \
148	    "set skip on lo"
149
150	atf_check -s exit:0 -o ignore ping -c 3 -S 192.0.2.1 192.0.2.2
151	atf_check -s exit:0 -o ignore ping -c 3 -S 192.0.2.3 192.0.2.2
152
153	jexec alcatraz pfctl -t foo -T show -vv
154
155	atf_check -s exit:0 -e ignore \
156	    -o match:'In/Block:.*'"$TABLE_STATS_ZERO_REGEXP" \
157	    -o match:'In/Pass:.*'"$TABLE_STATS_NONZERO_REGEXP" \
158	    -o match:'Out/Block:.*'"$TABLE_STATS_ZERO_REGEXP" \
159	    -o match:'Out/Pass:.*'"$TABLE_STATS_NONZERO_REGEXP" \
160	    jexec alcatraz pfctl -t foo -T show -vv
161
162	local uniq base ts1 ts3
163	uniq=`jexec alcatraz pfctl -t foo -vvT show | sort -u | grep -c Cleared`
164	atf_check_equal 1 "$uniq" # time they were added
165
166	base=`pft_cleared_ctime alcatraz foo 192.0.2.1`
167
168	atf_check -s exit:0 -e ignore \
169	    jexec alcatraz pfctl -t foo -T zero 192.0.2.3
170
171	ts1=`pft_cleared_ctime alcatraz foo 192.0.2.1`
172	atf_check_equal "$base" "$ts1"
173
174	ts3=`pft_cleared_ctime alcatraz foo 192.0.2.3`
175	atf_check test "$ts1" != "$ts3"
176
177	ts1=`ctime_to_unixtime "$ts1"`
178	ts3=`ctime_to_unixtime "$ts3"`
179	atf_check test $(( "$ts3" - "$ts1" )) -lt 10 # (3 pings * 2) + epsilon
180	atf_check test "$ts1" -lt "$ts3"
181
182	# We now have a zeroed and a non-zeroed counter, so both patterns
183	# should match
184	atf_check -s exit:0 -e ignore \
185	    -o match:'In/Pass:.*'"$TABLE_STATS_NONZERO_REGEXP" \
186	    -o match:'Out/Pass:.*'"$TABLE_STATS_NONZERO_REGEXP" \
187	    -o match:'In/Pass:.*'"$TABLE_STATS_ZERO_REGEXP" \
188	    -o match:'Out/Pass:.*'"$TABLE_STATS_ZERO_REGEXP" \
189	    jexec alcatraz pfctl -t foo -T show -vv
190}
191
192zero_one_cleanup()
193{
194	pft_cleanup
195}
196
197atf_test_case "reset_nonzero" "cleanup"
198reset_nonzero_head()
199{
200	atf_set descr 'Test zeroing an address with non-zero counters'
201	atf_set require.user root
202}
203
204reset_nonzero_body()
205{
206	epair_send=$(vnet_mkepair)
207	ifconfig ${epair_send}a 192.0.2.1/24 up
208	ifconfig ${epair_send}a inet alias 192.0.2.3/24
209
210	vnet_mkjail alcatraz ${epair_send}b
211	jexec alcatraz ifconfig ${epair_send}b 192.0.2.2/24 up
212	jexec alcatraz pfctl -e
213
214	pft_set_rules alcatraz \
215	    "table <foo> counters { 192.0.2.1, 192.0.2.3 }" \
216	    "table <bar> counters { }" \
217	    "block all" \
218	    "pass in from <foo> to any" \
219	    "pass out from any to <foo>" \
220	    "pass on notReallyAnIf from <bar> to <bar>" \
221	    "set skip on lo"
222
223	# Nonexisting table can't be reset, following `-T show`.
224	atf_check -o ignore \
225	    -s not-exit:0 \
226	    -e inline:"pfctl: Table does not exist.\n" \
227	    jexec alcatraz pfctl -t nonexistent -T reset
228
229	atf_check -o ignore \
230	    -s exit:0 \
231	    -e inline:"0/0 stats cleared.\n" \
232	    jexec alcatraz pfctl -t bar -T reset
233
234	# No-op is a valid operation.
235	atf_check -s exit:0 \
236	    -e inline:"0/2 stats cleared.\n" \
237	    jexec alcatraz pfctl -t foo -T reset
238
239	atf_check -s exit:0 -o ignore ping -c 3 -S 192.0.2.3 192.0.2.2
240
241	atf_check -s exit:0 -e ignore \
242	    -o match:'In/Pass:.*'"$TABLE_STATS_ZERO_REGEXP" \
243	    -o match:'In/Pass:.*'"$TABLE_STATS_NONZERO_REGEXP" \
244	    -o match:'Out/Pass:.*'"$TABLE_STATS_ZERO_REGEXP" \
245	    -o match:'Out/Pass:.*'"$TABLE_STATS_NONZERO_REGEXP" \
246	    jexec alcatraz pfctl -t foo -vvT show
247
248	local clrd uniq
249	clrd=`jexec alcatraz pfctl -t foo -vvT show | grep -c Cleared`
250	uniq=`jexec alcatraz pfctl -t foo -vvT show | sort -u | grep -c Cleared`
251	atf_check_equal "$clrd" 2
252	atf_check_equal "$uniq" 1 # time they were added
253
254	atf_check -s exit:0 -e ignore \
255	    -e inline:"1/2 stats cleared.\n" \
256	    jexec alcatraz pfctl -t foo -T reset
257
258	clrd=`jexec alcatraz pfctl -t foo -vvT show | grep -c Cleared`
259	uniq=`jexec alcatraz pfctl -t foo -vvT show | sort -u | grep -c Cleared`
260	atf_check_equal "$clrd" 2
261	atf_check_equal "$uniq" 2 # 192.0.2.3 should get new timestamp
262
263	atf_check -s exit:0 -e ignore \
264	    -o not-match:'In/Pass:.*'"$TABLE_STATS_NONZERO_REGEXP" \
265	    -o not-match:'Out/Pass:.*'"$TABLE_STATS_NONZERO_REGEXP" \
266	    -o match:'In/Pass:.*'"$TABLE_STATS_ZERO_REGEXP" \
267	    -o match:'Out/Pass:.*'"$TABLE_STATS_ZERO_REGEXP" \
268	    jexec alcatraz pfctl -t foo -vvT show
269}
270
271reset_nonzero_cleanup()
272{
273	pft_cleanup
274}
275
276atf_test_case "pr251414" "cleanup"
277pr251414_head()
278{
279	atf_set descr 'Test PR 251414'
280	atf_set require.user root
281}
282
283pr251414_body()
284{
285	pft_init
286
287	epair_send=$(vnet_mkepair)
288	ifconfig ${epair_send}a 192.0.2.1/24 up
289
290	vnet_mkjail alcatraz ${epair_send}b
291	jexec alcatraz ifconfig ${epair_send}b 192.0.2.2/24 up
292	jexec alcatraz pfctl -e
293
294	pft_set_rules alcatraz \
295		"pass all" \
296		"table <tab> { self }" \
297		"pass in log to <tab>"
298
299	pft_set_rules noflush alcatraz \
300		"pass all" \
301		"table <tab> counters { self }" \
302		"pass in log to <tab>"
303
304	atf_check -s exit:0 -o ignore ping -c 3 192.0.2.2
305
306	jexec alcatraz pfctl -t tab -T show -vv
307}
308
309pr251414_cleanup()
310{
311	pft_cleanup
312}
313
314atf_test_case "automatic" "cleanup"
315automatic_head()
316{
317	atf_set descr "Test automatic - optimizer generated - tables"
318	atf_set require.user root
319}
320
321automatic_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 \
333		"block in" \
334		"pass in proto icmp from 192.0.2.1" \
335		"pass in proto icmp from 192.0.2.3" \
336		"pass in proto icmp from 192.0.2.4" \
337		"pass in proto icmp from 192.0.2.5" \
338		"pass in proto icmp from 192.0.2.6" \
339		"pass in proto icmp from 192.0.2.7" \
340		"pass in proto icmp from 192.0.2.8" \
341		"pass in proto icmp from 192.0.2.9"
342
343	atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
344}
345
346automatic_cleanup()
347{
348	pft_cleanup
349}
350
351atf_test_case "network" "cleanup"
352network_head()
353{
354	atf_set descr 'Test <ifgroup>:network'
355	atf_set require.user root
356}
357
358network_body()
359{
360	pft_init
361
362	epair=$(vnet_mkepair)
363	ifconfig ${epair}a 192.0.2.1/24 up
364
365	vnet_mkjail alcatraz ${epair}b
366	jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
367	jexec alcatraz pfctl -e
368
369	pft_set_rules alcatraz \
370		"table <allow> const { epair:network }"\
371		"block in" \
372		"pass in from <allow>"
373
374	atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
375}
376
377network_cleanup()
378{
379	pft_cleanup
380}
381
382atf_test_case "pr259689" "cleanup"
383pr259689_head()
384{
385	atf_set descr 'Test PR 259689'
386	atf_set require.user root
387}
388
389pr259689_body()
390{
391	pft_init
392
393	vnet_mkjail alcatraz
394	jexec alcatraz pfctl -e
395
396	pft_set_rules alcatraz \
397	    "pass in" \
398	    "block in inet from { 1.1.1.1, 1.1.1.2, 2.2.2.2, 2.2.2.3, 4.4.4.4, 4.4.4.5 }"
399
400	atf_check -o match:'block drop in inet from <__automatic_.*:6> to any' \
401	    -e ignore \
402	    jexec alcatraz pfctl -sr -vv
403}
404
405pr259689_cleanup()
406{
407	pft_cleanup
408}
409
410atf_test_case "precreate" "cleanup"
411precreate_head()
412{
413	atf_set descr 'Test creating a table without counters, then loading rules that add counters'
414	atf_set require.user root
415}
416
417precreate_body()
418{
419	pft_init
420
421	vnet_mkjail alcatraz
422
423	jexec alcatraz pfctl -t foo -T add 192.0.2.1
424	jexec alcatraz pfctl -t foo -T show
425
426	pft_set_rules noflush alcatraz \
427		"table <foo> counters persist" \
428		"pass in from <foo>"
429
430	# Expect all counters to be zero
431	atf_check -s exit:0 -e ignore \
432	    -o match:'In/Block:.*'"$TABLE_STATS_ZERO_REGEXP" \
433	    -o match:'In/Pass:.*'"$TABLE_STATS_ZERO_REGEXP" \
434	    -o match:'Out/Block:.*'"$TABLE_STATS_ZERO_REGEXP" \
435	    -o match:'Out/Pass:.*'"$TABLE_STATS_ZERO_REGEXP" \
436	    jexec alcatraz pfctl -t foo -T show -vv
437
438}
439
440precreate_cleanup()
441{
442	pft_cleanup
443}
444
445atf_test_case "anchor" "cleanup"
446anchor_head()
447{
448	atf_set descr 'Test tables in anchors'
449	atf_set require.user root
450}
451
452anchor_body()
453{
454	pft_init
455
456	epair=$(vnet_mkepair)
457	ifconfig ${epair}a 192.0.2.1/24 up
458
459	vnet_mkjail alcatraz ${epair}b
460	jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
461	jexec alcatraz pfctl -e
462
463	(echo "table <testtable> persist"
464	 echo "block in quick from <testtable> to any"
465	) | jexec alcatraz pfctl -a anchorage -f -
466
467	pft_set_rules noflush alcatraz \
468		"pass" \
469		"anchor anchorage"
470
471	atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
472
473	# Tables belong to anchors, so this is a different table and won't affect anything
474	jexec alcatraz pfctl -t testtable -T add 192.0.2.1
475	atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
476
477	# But when we add the address to the table in the anchor it does block traffic
478	jexec alcatraz pfctl -a anchorage -t testtable -T add 192.0.2.1
479	atf_check -s exit:2 -o ignore ping -c 1 192.0.2.2
480}
481
482anchor_cleanup()
483{
484	pft_cleanup
485}
486
487atf_init_test_cases()
488{
489	atf_add_test_case "v4_counters"
490	atf_add_test_case "v6_counters"
491	atf_add_test_case "zero_one"
492	atf_add_test_case "reset_nonzero"
493	atf_add_test_case "pr251414"
494	atf_add_test_case "automatic"
495	atf_add_test_case "network"
496	atf_add_test_case "pr259689"
497	atf_add_test_case "precreate"
498	atf_add_test_case "anchor"
499}
500