xref: /freebsd/tests/sys/netpfil/common/dummynet.sh (revision 7ef62cebc2f965b0f640263e179276928885e33d)
1# $FreeBSD$
2#
3# SPDX-License-Identifier: BSD-2-Clause
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. $(atf_get_srcdir)/runner.subr
30
31interface_removal_head()
32{
33	atf_set descr 'Test removing interfaces with dummynet delayed traffic'
34	atf_set require.user root
35}
36
37interface_removal_body()
38{
39	fw=$1
40	firewall_init $fw
41	dummynet_init $fw
42
43	epair=$(vnet_mkepair)
44	vnet_mkjail alcatraz ${epair}b
45
46	ifconfig ${epair}a 192.0.2.1/24 up
47	jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
48
49	# Sanity check
50	atf_check -s exit:0 -o ignore ping -i .1 -c 3 -s 1200 192.0.2.2
51
52	jexec alcatraz dnctl pipe 1 config delay 1500
53
54	firewall_config alcatraz ${fw} \
55		"ipfw"	\
56			"ipfw add 1000 pipe 1 ip from any to any" \
57		"pf"	\
58			"pass on ${epair}b dnpipe 1"
59
60	# single ping succeeds just fine
61	atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
62
63	# Send traffic that'll still be pending when we remove the interface
64	ping -c 5 -s 1200 192.0.2.2 &
65	sleep 1 # Give ping the chance to start.
66
67	# Remove the interface, but keep the jail around for a bit
68	ifconfig ${epair}a destroy
69
70	sleep 3
71}
72
73interface_removal_cleanup()
74{
75	firewall_cleanup $1
76}
77
78pipe_head()
79{
80	atf_set descr 'Basic pipe test'
81	atf_set require.user root
82}
83
84pipe_body()
85{
86	fw=$1
87	firewall_init $fw
88	dummynet_init $fw
89
90	epair=$(vnet_mkepair)
91	vnet_mkjail alcatraz ${epair}b
92
93	ifconfig ${epair}a 192.0.2.1/24 up
94	jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
95
96	# Sanity check
97	atf_check -s exit:0 -o ignore ping -i .1 -c 3 -s 1200 192.0.2.2
98
99	jexec alcatraz dnctl pipe 1 config bw 30Byte/s
100
101	firewall_config alcatraz ${fw} \
102		"ipfw"	\
103			"ipfw add 1000 pipe 1 ip from any to any" \
104		"pf"	\
105			"pass on ${epair}b dnpipe 1"
106
107	# single ping succeeds just fine
108	atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
109
110	# Saturate the link
111	ping -i .1 -c 5 -s 1200 192.0.2.2
112
113	# We should now be hitting the limits and get this packet dropped.
114	atf_check -s exit:2 -o ignore ping -c 1 -s 1200 192.0.2.2
115}
116
117pipe_cleanup()
118{
119	firewall_cleanup $1
120}
121
122pipe_v6_head()
123{
124	atf_set descr 'Basic IPv6 pipe test'
125	atf_set require.user root
126}
127
128pipe_v6_body()
129{
130	fw=$1
131	firewall_init $fw
132	dummynet_init $fw
133
134	epair=$(vnet_mkepair)
135	vnet_mkjail alcatraz ${epair}b
136
137	ifconfig ${epair}a inet6 2001:db8:42::1/64 up no_dad
138	jexec alcatraz ifconfig ${epair}b inet6 2001:db8:42::2/64 up no_dad
139
140	# Sanity check
141	atf_check -s exit:0 -o ignore ping6 -i .1 -c 3 -s 1200 2001:db8:42::2
142
143	jexec alcatraz dnctl pipe 1 config bw 100Byte/s
144
145	firewall_config alcatraz ${fw} \
146		"ipfw"	\
147			"ipfw add 1000 pipe 1 ip6 from any to any" \
148		"pf"	\
149			"pass on ${epair}b dnpipe 1"
150
151	# Single ping succeeds
152	atf_check -s exit:0 -o ignore ping6 -c 1 2001:db8:42::2
153
154	# Saturate the link
155	ping6 -i .1 -c 5 -s 1200 2001:db8:42::2
156
157	# We should now be hitting the limit and get this packet dropped.
158	atf_check -s exit:2 -o ignore ping6 -c 1 -s 1200 2001:db8:42::2
159}
160
161pipe_v6_cleanup()
162{
163	firewall_cleanup $1
164}
165
166codel_head()
167{
168	atf_set descr 'FQ_CODEL basic test'
169	atf_set require.user root
170}
171
172codel_body()
173{
174	fw=$1
175	firewall_init $fw
176	dummynet_init $fw
177
178	epair=$(vnet_mkepair)
179	vnet_mkjail alcatraz ${epair}b
180
181	ifconfig ${epair}a 192.0.2.1/24 up
182	jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
183
184	# Sanity check
185	atf_check -s exit:0 -o ignore ping -i .1 -c 3 -s 1200 192.0.2.2
186
187	jexec alcatraz dnctl pipe 1 config  bw 10Mb queue 100 droptail
188	jexec alcatraz dnctl sched 1 config pipe 1 type fq_codel target 0ms interval 0ms quantum 1514 limit 10240 flows 1024 ecn
189	jexec alcatraz dnctl queue 1 config pipe 1 droptail
190
191	firewall_config alcatraz ${fw} \
192		"ipfw"	\
193			"ipfw add 1000 queue 1 ip from any to any" \
194		"pf"	\
195			"pass dnqueue 1"
196
197	# single ping succeeds just fine
198	atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
199}
200
201codel_cleanup()
202{
203	firewall_cleanup $1
204}
205
206wf2q_heap_head()
207{
208	atf_set descr 'Test WF2Q+, attempting to provoke use-after-free'
209	atf_set require.user root
210}
211
212wf2q_heap_body()
213{
214	fw=$1
215	firewall_init $fw
216	dummynet_init $fw
217
218       j=dummynet_wf2q_heap_${fw}_
219
220       epair=$(vnet_mkepair)
221       epair_other=$(vnet_mkepair)
222       vnet_mkjail ${j}a ${epair}a
223       vnet_mkjail ${j}b ${epair}b ${epair_other}b
224
225       jexec ${j}a ifconfig ${epair}a up mtu 9000
226       va=$(jexec ${j}a ifconfig vlan create vlan 42 vlandev ${epair}a)
227       jexec ${j}a ifconfig ${va} 192.0.2.1/24 up #mtu 8000
228
229       jexec ${j}b ifconfig ${epair}b up mtu 9000
230       vb=$(jexec ${j}b ifconfig vlan create vlan 42 vlandev ${epair}b)
231       jexec ${j}b ifconfig ${vb} 192.0.2.2/24 up #mtu 8000
232       jexec ${j}b ifconfig ${epair_other}b up
233
234       # Sanity check
235       atf_check -s exit:0 -o ignore \
236           jexec ${j}b ping -c 1 192.0.2.1
237
238       jexec ${j}b dnctl pipe 1 config bw 10Mb queue 100 delay 500 droptail
239       jexec ${j}b dnctl sched 1 config pipe 1 type wf2q+
240       jexec ${j}b dnctl queue 1 config pipe 1 droptail
241
242       firewall_config ${j}b ${fw} \
243               "pf"    \
244                       "pass dnqueue 1"
245
246       jexec ${j}a ping -f 192.0.2.2 &
247       sleep 1
248
249       jexec ${j}b ifconfig ${vb} destroy
250
251       sleep 2
252}
253
254wf2q_heap_cleanup()
255{
256	firewall_cleanup $1
257}
258
259queue_head()
260{
261	atf_set descr 'Basic queue test'
262	atf_set require.user root
263}
264
265queue_body()
266{
267	fw=$1
268
269	if [ $fw = "ipfw" ] && [ "$(atf_config_get ci false)" = "true" ]; then
270		atf_skip "https://bugs.freebsd.org/264805"
271	fi
272
273	firewall_init $fw
274	dummynet_init $fw
275
276	epair=$(vnet_mkepair)
277	vnet_mkjail alcatraz ${epair}b
278
279	ifconfig ${epair}a 192.0.2.1/24 up
280	jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
281	jexec alcatraz /usr/sbin/inetd -p inetd-alcatraz.pid \
282	    $(atf_get_srcdir)/../pf/echo_inetd.conf
283
284	# Sanity check
285	atf_check -s exit:0 -o ignore ping -i .1 -c 3 -s 1200 192.0.2.2
286	reply=$(echo "foo" | nc -N 192.0.2.2 7)
287	if [ "$reply" != "foo" ];
288	then
289		atf_fail "Echo sanity check failed"
290	fi
291
292	jexec alcatraz dnctl pipe 1 config bw 1MByte/s
293	jexec alcatraz dnctl sched 1 config pipe 1 type wf2q+
294	jexec alcatraz dnctl queue 100 config sched 1 weight 99 mask all
295	jexec alcatraz dnctl queue 200 config sched 1 weight 1 mask all
296
297	firewall_config alcatraz ${fw} \
298		"ipfw"	\
299			"ipfw add 1000 queue 100 tcp from 192.0.2.2 to any out" \
300			"ipfw add 1001 queue 200 icmp from 192.0.2.2 to any out" \
301			"ipfw add 1002 allow ip from any to any" \
302		"pf"	\
303			"pass in proto tcp dnqueue (0, 100)" \
304			"pass in proto icmp dnqueue (0, 200)"
305
306	# Single ping succeeds
307	atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
308
309	# Unsaturated TCP succeeds
310	reply=$(echo "foo" | nc -w 5 -N 192.0.2.2 7)
311	if [ "$reply" != "foo" ];
312	then
313		atf_fail "Unsaturated echo failed"
314	fi
315
316	# Saturate the link
317	ping -f -s 1300 192.0.2.2 &
318
319	# Allow this to fill the queue
320	sleep 1
321
322	# TCP should still just pass
323	fails=0
324	for i in `seq 1 3`
325	do
326		result=$(dd if=/dev/zero bs=1024 count=2000 | timeout 3 nc -w 5 -N 192.0.2.2 7 | wc -c)
327		if [ $result -ne 2048000 ];
328		then
329			echo "Failed to prioritise TCP traffic. Got only $result bytes"
330			fails=$(( ${fails} + 1 ))
331		fi
332	done
333	if [ ${fails} -gt 0 ];
334	then
335		atf_fail "We failed prioritisation ${fails} times"
336	fi
337
338	# This will fail if we reverse the pola^W priority
339	firewall_config alcatraz ${fw} \
340		"ipfw"	\
341			"ipfw add 1000 queue 200 tcp from 192.0.2.2 to any out" \
342			"ipfw add 1001 queue 100 icmp from 192.0.2.2 to any out" \
343			"ipfw add 1002 allow ip from any to any" \
344		"pf"	\
345			"pass in proto tcp dnqueue (0, 200)" \
346			"pass in proto icmp dnqueue (0, 100)"
347
348	jexec alcatraz ping -f -s 1300 192.0.2.1 &
349	sleep 1
350
351	fails=0
352	for i in `seq 1 3`
353	do
354		result=$(dd if=/dev/zero bs=1024 count=2000 | timeout 3 nc -w 5 -N 192.0.2.2 7 | wc -c)
355		if [ $result -ne 2048000 ];
356		then
357			echo "Failed to prioritise TCP traffic. Got only $result bytes"
358			fails=$(( ${fails} + 1 ))
359		fi
360	done
361	if [ ${fails} -lt 3 ];
362	then
363		atf_fail "We failed reversed prioritisation only ${fails} times."
364	fi
365}
366
367queue_cleanup()
368{
369	firewall_cleanup $1
370}
371
372queue_v6_head()
373{
374	atf_set descr 'Basic queue test'
375	atf_set require.user root
376}
377
378queue_v6_body()
379{
380	fw=$1
381	firewall_init $fw
382	dummynet_init $fw
383
384	epair=$(vnet_mkepair)
385	vnet_mkjail alcatraz ${epair}b
386
387	ifconfig ${epair}a inet6 2001:db8:42::1/64 no_dad up
388	jexec alcatraz ifconfig ${epair}b inet6 2001:db8:42::2 no_dad up
389	jexec alcatraz /usr/sbin/inetd -p inetd-alcatraz.pid \
390	    $(atf_get_srcdir)/../pf/echo_inetd.conf
391
392	# Sanity check
393	atf_check -s exit:0 -o ignore ping6 -i .1 -c 3 -s 1200 2001:db8:42::2
394	reply=$(echo "foo" | nc -N 2001:db8:42::2 7)
395	if [ "$reply" != "foo" ];
396	then
397		atf_fail "Echo sanity check failed"
398	fi
399
400	jexec alcatraz dnctl pipe 1 config bw 1MByte/s
401	jexec alcatraz dnctl sched 1 config pipe 1 type wf2q+
402	jexec alcatraz dnctl queue 100 config sched 1 weight 99 mask all
403	jexec alcatraz dnctl queue 200 config sched 1 weight 1 mask all
404
405	firewall_config alcatraz ${fw} \
406		"ipfw"	\
407			"ipfw add 1001 queue 100 tcp from 2001:db8:42::2 to any out" \
408			"ipfw add 1000 queue 200 ipv6-icmp from 2001:db8:42::2 to any out" \
409			"ipfw add 1002 allow ip6 from any to any" \
410		"pf" \
411			"pass in proto tcp dnqueue (0, 100)"	\
412			"pass in proto icmp6 dnqueue (0, 200)"
413
414	# Single ping succeeds
415	atf_check -s exit:0 -o ignore ping6 -c 1 2001:db8:42::2
416
417	# Unsaturated TCP succeeds
418	reply=$(echo "foo" | nc -w 5 -N 2001:db8:42::2 7)
419	if [ "$reply" != "foo" ];
420	then
421		atf_fail "Unsaturated echo failed"
422	fi
423
424	# Saturate the link
425	ping6 -f -s 1200 2001:db8:42::2 &
426
427	# Allow this to fill the queue
428	sleep 1
429
430	# TCP should still just pass
431	fails=0
432	for i in `seq 1 3`
433	do
434		result=$(dd if=/dev/zero bs=1024 count=1000 | timeout 3 nc -w 5 -N 2001:db8:42::2 7 | wc -c)
435		if [ $result -ne 1024000 ];
436		then
437			echo "Failed to prioritise TCP traffic. Got only $result bytes"
438			fails=$(( ${fails} + 1 ))
439		fi
440	done
441	if [ ${fails} -gt 0 ];
442	then
443		atf_fail "We failed prioritisation ${fails} times"
444	fi
445
446	# What happens if we prioritise ICMP over TCP?
447	firewall_config alcatraz ${fw} \
448		"ipfw"	\
449			"ipfw add 1001 queue 200 tcp from 2001:db8:42::2 to any out" \
450			"ipfw add 1000 queue 100 ipv6-icmp from 2001:db8:42::2 to any out" \
451			"ipfw add 1002 allow ip6 from any to any" \
452		"pf" \
453			"pass in proto tcp dnqueue (0, 200)"	\
454			"pass in proto icmp6 dnqueue (0, 100)"
455
456	fails=0
457	for i in `seq 1 3`
458	do
459		result=$(dd if=/dev/zero bs=1024 count=1000 | timeout 3 nc -w 5 -N 2001:db8:42::2 7 | wc -c)
460		if [ $result -ne 1024000 ];
461		then
462			echo "Failed to prioritise TCP traffic. Got only $result bytes"
463			fails=$(( ${fails} + 1 ))
464		fi
465	done
466	if [ ${fails} -lt 3 ];
467	then
468		atf_fail "We failed reversed prioritisation only ${fails} times."
469	fi
470}
471
472queue_v6_cleanup()
473{
474	firewall_cleanup $1
475}
476
477nat_head()
478{
479	atf_set descr 'Basic dummynet + NAT test'
480	atf_set require.user root
481}
482
483nat_body()
484{
485	fw=$1
486	firewall_init $fw
487	dummynet_init $fw
488	nat_init $fw
489
490	epair=$(vnet_mkepair)
491	epair_two=$(vnet_mkepair)
492
493	ifconfig ${epair}a 192.0.2.2/24 up
494	route add -net 198.51.100.0/24 192.0.2.1
495
496	vnet_mkjail gw ${epair}b ${epair_two}a
497	jexec gw ifconfig ${epair}b 192.0.2.1/24 up
498	jexec gw ifconfig ${epair_two}a 198.51.100.1/24 up
499	jexec gw sysctl net.inet.ip.forwarding=1
500
501	vnet_mkjail srv ${epair_two}b
502	jexec srv ifconfig ${epair_two}b 198.51.100.2/24 up
503
504	jexec gw dnctl pipe 1 config bw 300Byte/s
505
506	firewall_config gw $fw \
507		"pf"	\
508			"nat on ${epair_two}a inet from 192.0.2.0/24 to any -> (${epair_two}a)" \
509			"pass dnpipe 1"
510
511	# We've deliberately not set a route to 192.0.2.0/24 on srv, so the
512	# only way it can respond to this is if NAT is applied correctly.
513	atf_check -s exit:0 -o ignore ping -c 1 198.51.100.2
514}
515
516nat_cleanup()
517{
518	firewall_cleanup $1
519}
520
521setup_tests		\
522	interface_removal	\
523		ipfw	\
524		pf	\
525	pipe		\
526		ipfw	\
527		pf	\
528	pipe_v6		\
529		ipfw	\
530		pf	\
531	codel		\
532		ipfw	\
533		pf	\
534	wf2q_heap	\
535		pf	\
536	queue		\
537		ipfw	\
538		pf	\
539	queue_v6	\
540		ipfw	\
541		pf	\
542	nat		\
543		pf
544