xref: /freebsd/tests/sys/netpfil/pf/syncookie.sh (revision 5ca8e32633c4ffbbcd6762e5888b6a4ba0708c6c)
1#
2# SPDX-License-Identifier: BSD-2-Clause
3#
4# Copyright (c) 2021 Modirum MDPay
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
31syncookie_state()
32{
33	jail=$1
34
35	jexec $jail pfctl -si -v | grep -A 2 '^Syncookies' | grep active \
36	    | awk '{ print($2); }'
37}
38
39atf_test_case "basic" "cleanup"
40basic_head()
41{
42	atf_set descr 'Basic syncookie test'
43	atf_set require.user root
44}
45
46basic_body()
47{
48	pft_init
49
50	epair=$(vnet_mkepair)
51
52	vnet_mkjail alcatraz ${epair}b
53	jexec alcatraz ifconfig ${epair}b 192.0.2.1/24 up
54	jexec alcatraz /usr/sbin/inetd -p inetd-alcatraz.pid \
55	    $(atf_get_srcdir)/echo_inetd.conf
56
57	ifconfig ${epair}a 192.0.2.2/24 up
58
59	jexec alcatraz pfctl -e
60	pft_set_rules alcatraz \
61		"set syncookies always" \
62		"pass in" \
63		"pass out"
64
65	# Sanity check
66	atf_check -s exit:0 -o ignore ping -c 1 192.0.2.1
67
68	reply=$(echo foo | nc -N -w 5 192.0.2.1 7)
69	if [ "${reply}" != "foo" ];
70	then
71		atf_fail "Failed to connect to syncookie protected echo daemon"
72	fi
73
74	# Check that status shows syncookies as being active
75	active=$(syncookie_state alcatraz)
76	if [ "$active" != "active" ];
77	then
78		atf_fail "syncookies not active"
79	fi
80}
81
82basic_cleanup()
83{
84	rm -f inetd-alcatraz.pid
85	pft_cleanup
86}
87
88atf_test_case "basic_v6" "cleanup"
89basic_v6_head()
90{
91	atf_set descr 'Basic syncookie IPv6 test'
92	atf_set require.user root
93}
94
95basic_v6_body()
96{
97	pft_init
98
99	epair=$(vnet_mkepair)
100
101	vnet_mkjail alcatraz ${epair}b
102	jexec alcatraz ifconfig ${epair}b inet6 2001:db8::1/64 up no_dad
103	jexec alcatraz /usr/sbin/inetd -p inetd-alcatraz.pid \
104	    $(atf_get_srcdir)/echo_inetd.conf
105
106	ifconfig ${epair}a inet6 2001:db8::2/64 up no_dad
107
108	jexec alcatraz pfctl -e
109	pft_set_rules alcatraz \
110		"set syncookies always" \
111		"pass in" \
112		"pass out"
113
114	# Sanity check
115	atf_check -s exit:0 -o ignore ping6 -c 1 2001:db8::1
116
117	reply=$(echo foo | nc -N -w 5 2001:db8::1 7)
118	if [ "${reply}" != "foo" ];
119	then
120		atf_fail "Failed to connect to syncookie protected echo daemon"
121	fi
122
123	# Check that status shows syncookies as being active
124	active=$(syncookie_state alcatraz)
125	if [ "$active" != "active" ];
126	then
127		atf_fail "syncookies not active"
128	fi
129}
130
131basic_v6_cleanup()
132{
133	rm -f inetd-alcatraz.pid
134	pft_cleanup
135}
136
137atf_test_case "forward" "cleanup"
138forward_head()
139{
140	atf_set descr 'Syncookies for forwarded hosts'
141	atf_set require.user root
142}
143
144forward_body()
145{
146	pft_init
147
148	epair_in=$(vnet_mkepair)
149	epair_out=$(vnet_mkepair)
150
151	vnet_mkjail fwd ${epair_in}b ${epair_out}a
152	vnet_mkjail srv ${epair_out}b
153
154	jexec fwd ifconfig ${epair_in}b 192.0.2.1/24 up
155	jexec fwd ifconfig ${epair_out}a 198.51.100.1/24 up
156	jexec fwd sysctl net.inet.ip.forwarding=1
157
158	jexec srv ifconfig ${epair_out}b 198.51.100.2/24 up
159	jexec srv route add default 198.51.100.1
160	jexec srv /usr/sbin/inetd -p inetd-alcatraz.pid \
161	    $(atf_get_srcdir)/echo_inetd.conf
162
163	ifconfig ${epair_in}a 192.0.2.2/24 up
164	route add -net 198.51.100.0/24 192.0.2.1
165
166	jexec fwd pfctl -e
167	pft_set_rules fwd \
168		"set syncookies always" \
169		"pass in" \
170		"pass out"
171
172	# Sanity check
173	atf_check -s exit:0 -o ignore ping -c 1 198.51.100.2
174
175	reply=$(echo foo | nc -N -w 5 198.51.100.2 7)
176	if [ "${reply}" != "foo" ];
177	then
178		atf_fail "Failed to connect to syncookie protected echo daemon"
179	fi
180}
181
182forward_cleanup()
183{
184	rm -f inetd-alcatraz.pid
185	pft_cleanup
186}
187
188atf_test_case "forward_v6" "cleanup"
189forward_v6_head()
190{
191	atf_set descr 'Syncookies for forwarded hosts'
192	atf_set require.user root
193}
194
195forward_v6_body()
196{
197	pft_init
198
199	epair_in=$(vnet_mkepair)
200	epair_out=$(vnet_mkepair)
201
202	vnet_mkjail fwd ${epair_in}b ${epair_out}a
203	vnet_mkjail srv ${epair_out}b
204
205	jexec fwd ifconfig ${epair_in}b inet6 2001:db8::1/64 up no_dad
206	jexec fwd ifconfig ${epair_out}a inet6 2001:db8:1::1/64 up no_dad
207	jexec fwd sysctl net.inet6.ip6.forwarding=1
208
209	jexec srv ifconfig ${epair_out}b inet6 2001:db8:1::2/64 up no_dad
210	jexec srv route -6 add default 2001:db8:1::1
211	jexec srv /usr/sbin/inetd -p inetd-alcatraz.pid \
212	    $(atf_get_srcdir)/echo_inetd.conf
213
214	ifconfig ${epair_in}a inet6 2001:db8::2/64 up no_dad
215	route -6 add -net 2001:db8:1::/64 2001:db8::1
216
217	jexec fwd pfctl -e
218	pft_set_rules fwd \
219		"set syncookies always" \
220		"pass in" \
221		"pass out"
222
223	# Sanity check
224	atf_check -s exit:0 -o ignore ping6 -c 1 2001:db8:1::2
225
226	reply=$(echo foo | nc -N -w 5 2001:db8:1::2 7)
227	if [ "${reply}" != "foo" ];
228	then
229		atf_fail "Failed to connect to syncookie protected echo daemon"
230	fi
231}
232
233forward_v6_cleanup()
234{
235	rm -f inetd-alcatraz.pid
236	pft_cleanup
237}
238
239atf_test_case "nostate" "cleanup"
240nostate_head()
241{
242	atf_set descr 'Ensure that we do not create until SYN|ACK'
243	atf_set require.user root
244	atf_set require.progs scapy
245}
246
247nostate_body()
248{
249	pft_init
250
251	epair=$(vnet_mkepair)
252	ifconfig ${epair}a 192.0.2.2/24 up
253
254	vnet_mkjail alcatraz ${epair}b
255	jexec alcatraz ifconfig ${epair}b 192.0.2.1/24 up
256
257	jexec alcatraz pfctl -e
258	pft_set_rules alcatraz \
259		"set syncookies always" \
260		"pass in" \
261		"pass out"
262
263	# Sanity check
264	atf_check -s exit:0 -o ignore ping -c 1 192.0.2.1
265
266	# Now syn flood to create many states
267	${common_dir}/pft_synflood.py \
268		--sendif ${epair}a \
269		--to 192.0.2.2 \
270		--count 20
271
272	states=$(jexec alcatraz pfctl -ss | grep tcp)
273	if [ -n "$states" ];
274	then
275		echo "$states"
276		atf_fail "Found unexpected state"
277	fi
278}
279
280nostate_cleanup()
281{
282	pft_cleanup
283}
284
285atf_test_case "nostate_v6" "cleanup"
286nostate_v6_head()
287{
288	atf_set descr 'Ensure that we do not create until SYN|ACK'
289	atf_set require.user root
290	atf_set require.progs scapy
291}
292
293nostate_v6_body()
294{
295	pft_init
296
297	epair=$(vnet_mkepair)
298	ifconfig ${epair}a inet6 2001:db8::2/64 up no_dad
299
300	vnet_mkjail alcatraz ${epair}b
301	jexec alcatraz ifconfig ${epair}b inet6 2001:db8::1/64 up no_dad
302
303	jexec alcatraz pfctl -e
304	pft_set_rules alcatraz \
305		"set syncookies always" \
306		"pass in" \
307		"pass out"
308
309	# Sanity check
310	atf_check -s exit:0 -o ignore ping6 -c 1 2001:db8::1
311
312	# Now syn flood to create many states
313	${common_dir}/pft_synflood.py \
314        --ip6 \
315		--sendif ${epair}a \
316		--to 2001:db8::2 \
317		--count 20
318
319	states=$(jexec alcatraz pfctl -ss | grep tcp)
320	if [ -n "$states" ];
321	then
322		echo "$states"
323		atf_fail "Found unexpected state"
324	fi
325}
326
327nostate_v6_cleanup()
328{
329	pft_cleanup
330}
331
332atf_test_case "adaptive" "cleanup"
333adaptive_head()
334{
335	atf_set descr 'Adaptive mode test'
336	atf_set require.user root
337	atf_set require.progs scapy
338}
339
340adaptive_body()
341{
342	pft_init
343
344	epair=$(vnet_mkepair)
345	ifconfig ${epair}a 192.0.2.2/24 up
346
347	vnet_mkjail alcatraz ${epair}b
348	jexec alcatraz ifconfig ${epair}b 192.0.2.1/24 up
349
350	jexec alcatraz pfctl -e
351	pft_set_rules alcatraz \
352		"set limit states 100" \
353		"set syncookies adaptive (start 10%%, end 5%%)" \
354		"pass in" \
355		"pass out"
356
357	# Sanity check
358	atf_check -s exit:0 -o ignore ping -c 1 192.0.2.1
359
360	# Check that status shows syncookies as being inactive
361	active=$(syncookie_state alcatraz)
362	if [ "$active" != "inactive" ];
363	then
364		atf_fail "syncookies active when they should not be"
365	fi
366
367	# Now syn flood to create many states
368	${common_dir}/pft_synflood.py \
369		--sendif ${epair}a \
370		--to 192.0.2.2 \
371		--count 100
372
373	# Check that status shows syncookies as being active
374	active=$(syncookie_state alcatraz)
375	if [ "$active" != "active" ];
376	then
377		atf_fail "syncookies not active"
378	fi
379
380	# Adaptive mode should kick in and stop us from creating more than
381	# about 10 states
382	states=$(jexec alcatraz pfctl -ss | grep tcp | wc -l)
383	if [ "$states" -gt 20 ];
384	then
385		echo "$states"
386		atf_fail "Found unexpected states"
387	fi
388}
389
390adaptive_cleanup()
391{
392	pft_cleanup
393}
394
395atf_test_case "limits" "cleanup"
396limits_head()
397{
398	atf_set descr 'Ensure limit calculation works for low or high state limits'
399	atf_set require.user root
400}
401
402limits_body()
403{
404	pft_init
405
406	vnet_mkjail alcatraz
407
408	jexec alcatraz pfctl -e
409	pft_set_rules alcatraz \
410		"set limit states 1" \
411		"set syncookies adaptive (start 10%%, end 5%%)" \
412		"pass in" \
413		"pass out"
414
415	pft_set_rules alcatraz \
416		"set limit states 326000000" \
417		"set syncookies adaptive (start 10%%, end 5%%)" \
418		"pass in" \
419		"pass out"
420}
421
422limits_cleanup()
423{
424	pft_cleanup
425}
426
427atf_test_case "port_reuse" "cleanup"
428port_reuse_head()
429{
430	atf_set descr 'Test rapid port re-use'
431	atf_set require.user root
432}
433
434port_reuse_body()
435{
436	pft_init
437
438	epair=$(vnet_mkepair)
439
440	vnet_mkjail alcatraz ${epair}b
441	vnet_mkjail singsing
442	jexec alcatraz ifconfig ${epair}b 192.0.2.1/24 up
443	jexec alcatraz /usr/sbin/inetd -p ${HOME}/inetd-alcatraz.pid \
444	    $(atf_get_srcdir)/echo_inetd.conf
445
446	ifconfig ${epair}a 192.0.2.2/24 up
447
448	jexec alcatraz pfctl -e
449	jexec alcatraz pfctl -x loud
450	pft_set_rules alcatraz \
451		"set syncookies always" \
452		"pass in" \
453		"pass out"
454
455	# Sanity check
456	atf_check -s exit:0 -o ignore ping -c 1 192.0.2.1
457
458	reply=$(echo foo | nc -p 1234 -N -w 5 192.0.2.1 7)
459	if [ "${reply}" != "foo" ];
460	then
461		atf_fail "Failed to connect to syncookie protected echo daemon"
462	fi
463
464	# We can't re-use the source IP/port combo quickly enough, so we're
465	# going to play a really dirty trick, and move our interface to a new
466	# jail, and do it from there.
467	ifconfig ${epair}a vnet singsing
468	jexec singsing ifconfig ${epair}a 192.0.2.2/24 up
469	atf_check -s exit:0 -o ignore jexec singsing ping -c 1 192.0.2.1
470
471	reply=$(echo bar | jexec singsing nc -p 1234 -N -w 5 192.0.2.1 7)
472	if [ "${reply}" != "bar" ];
473	then
474		atf_fail "Failed to connect to syncookie protected echo daemon (2)"
475	fi
476}
477
478port_reuse_cleanup()
479{
480	pft_cleanup
481}
482
483atf_init_test_cases()
484{
485	atf_add_test_case "basic"
486	atf_add_test_case "basic_v6"
487	atf_add_test_case "forward"
488	atf_add_test_case "forward_v6"
489	atf_add_test_case "nostate"
490	atf_add_test_case "nostate_v6"
491	atf_add_test_case "adaptive"
492	atf_add_test_case "limits"
493	atf_add_test_case "port_reuse"
494}
495