xref: /freebsd/tests/sys/netpfil/common/dummynet.sh (revision ebacd8013fe5f7fdf9f6a5b286f6680dd2891036)
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. $(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 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 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 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
206queue_head()
207{
208	atf_set descr 'Basic queue test'
209	atf_set require.user root
210}
211
212queue_body()
213{
214	fw=$1
215
216	if [ $fw = "ipfw" ] && [ "$(atf_config_get ci false)" = "true" ]; then
217		atf_skip "https://bugs.freebsd.org/264805"
218	fi
219
220	firewall_init $fw
221	dummynet_init $fw
222
223	epair=$(vnet_mkepair)
224	vnet_mkjail alcatraz ${epair}b
225
226	ifconfig ${epair}a 192.0.2.1/24 up
227	jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
228	jexec alcatraz /usr/sbin/inetd -p inetd-alcatraz.pid \
229	    $(atf_get_srcdir)/../pf/echo_inetd.conf
230
231	# Sanity check
232	atf_check -s exit:0 -o ignore ping -i .1 -c 3 -s 1200 192.0.2.2
233	reply=$(echo "foo" | nc -N 192.0.2.2 7)
234	if [ "$reply" != "foo" ];
235	then
236		atf_fail "Echo sanity check failed"
237	fi
238
239	jexec alcatraz dnctl pipe 1 config bw 1MByte/s
240	jexec alcatraz dnctl sched 1 config pipe 1 type wf2q+
241	jexec alcatraz dnctl queue 100 config sched 1 weight 99 mask all
242	jexec alcatraz dnctl queue 200 config sched 1 weight 1 mask all
243
244	firewall_config alcatraz ${fw} \
245		"ipfw"	\
246			"ipfw add 1000 queue 100 tcp from 192.0.2.2 to any out" \
247			"ipfw add 1001 queue 200 icmp from 192.0.2.2 to any out" \
248			"ipfw add 1002 allow ip from any to any" \
249		"pf"	\
250			"pass in proto tcp dnqueue (0, 100)" \
251			"pass in proto icmp dnqueue (0, 200)"
252
253	# Single ping succeeds
254	atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
255
256	# Unsaturated TCP succeeds
257	reply=$(echo "foo" | nc -w 5 -N 192.0.2.2 7)
258	if [ "$reply" != "foo" ];
259	then
260		atf_fail "Unsaturated echo failed"
261	fi
262
263	# Saturate the link
264	ping -f -s 1300 192.0.2.2 &
265
266	# Allow this to fill the queue
267	sleep 1
268
269	# TCP should still just pass
270	fails=0
271	for i in `seq 1 3`
272	do
273		result=$(dd if=/dev/zero bs=1024 count=2000 | timeout 3 nc -w 5 -N 192.0.2.2 7 | wc -c)
274		if [ $result -ne 2048000 ];
275		then
276			echo "Failed to prioritise TCP traffic. Got only $result bytes"
277			fails=$(( ${fails} + 1 ))
278		fi
279	done
280	if [ ${fails} -gt 0 ];
281	then
282		atf_fail "We failed prioritisation ${fails} times"
283	fi
284
285	# This will fail if we reverse the pola^W priority
286	firewall_config alcatraz ${fw} \
287		"ipfw"	\
288			"ipfw add 1000 queue 200 tcp from 192.0.2.2 to any out" \
289			"ipfw add 1001 queue 100 icmp from 192.0.2.2 to any out" \
290			"ipfw add 1002 allow ip from any to any" \
291		"pf"	\
292			"pass in proto tcp dnqueue (0, 200)" \
293			"pass in proto icmp dnqueue (0, 100)"
294
295	jexec alcatraz ping -f -s 1300 192.0.2.1 &
296	sleep 1
297
298	fails=0
299	for i in `seq 1 3`
300	do
301		result=$(dd if=/dev/zero bs=1024 count=2000 | timeout 3 nc -w 5 -N 192.0.2.2 7 | wc -c)
302		if [ $result -ne 2048000 ];
303		then
304			echo "Failed to prioritise TCP traffic. Got only $result bytes"
305			fails=$(( ${fails} + 1 ))
306		fi
307	done
308	if [ ${fails} -lt 3 ];
309	then
310		atf_fail "We failed reversed prioritisation only ${fails} times."
311	fi
312}
313
314queue_cleanup()
315{
316	firewall_cleanup $1
317}
318
319queue_v6_head()
320{
321	atf_set descr 'Basic queue test'
322	atf_set require.user root
323}
324
325queue_v6_body()
326{
327	fw=$1
328	firewall_init $fw
329	dummynet_init $fw
330
331	epair=$(vnet_mkepair)
332	vnet_mkjail alcatraz ${epair}b
333
334	ifconfig ${epair}a inet6 2001:db8:42::1/64 no_dad up
335	jexec alcatraz ifconfig ${epair}b inet6 2001:db8:42::2 no_dad up
336	jexec alcatraz /usr/sbin/inetd -p inetd-alcatraz.pid \
337	    $(atf_get_srcdir)/../pf/echo_inetd.conf
338
339	# Sanity check
340	atf_check -s exit:0 -o ignore ping6 -i .1 -c 3 -s 1200 2001:db8:42::2
341	reply=$(echo "foo" | nc -N 2001:db8:42::2 7)
342	if [ "$reply" != "foo" ];
343	then
344		atf_fail "Echo sanity check failed"
345	fi
346
347	jexec alcatraz dnctl pipe 1 config bw 1MByte/s
348	jexec alcatraz dnctl sched 1 config pipe 1 type wf2q+
349	jexec alcatraz dnctl queue 100 config sched 1 weight 99 mask all
350	jexec alcatraz dnctl queue 200 config sched 1 weight 1 mask all
351
352	firewall_config alcatraz ${fw} \
353		"ipfw"	\
354			"ipfw add 1001 queue 100 tcp from 2001:db8:42::2 to any out" \
355			"ipfw add 1000 queue 200 ipv6-icmp from 2001:db8:42::2 to any out" \
356			"ipfw add 1002 allow ip6 from any to any" \
357		"pf" \
358			"pass in proto tcp dnqueue (0, 100)"	\
359			"pass in proto icmp6 dnqueue (0, 200)"
360
361	# Single ping succeeds
362	atf_check -s exit:0 -o ignore ping6 -c 1 2001:db8:42::2
363
364	# Unsaturated TCP succeeds
365	reply=$(echo "foo" | nc -w 5 -N 2001:db8:42::2 7)
366	if [ "$reply" != "foo" ];
367	then
368		atf_fail "Unsaturated echo failed"
369	fi
370
371	# Saturate the link
372	ping6 -f -s 1200 2001:db8:42::2 &
373
374	# Allow this to fill the queue
375	sleep 1
376
377	# TCP should still just pass
378	fails=0
379	for i in `seq 1 3`
380	do
381		result=$(dd if=/dev/zero bs=1024 count=1000 | timeout 3 nc -w 5 -N 2001:db8:42::2 7 | wc -c)
382		if [ $result -ne 1024000 ];
383		then
384			echo "Failed to prioritise TCP traffic. Got only $result bytes"
385			fails=$(( ${fails} + 1 ))
386		fi
387	done
388	if [ ${fails} -gt 0 ];
389	then
390		atf_fail "We failed prioritisation ${fails} times"
391	fi
392
393	# What happens if we prioritise ICMP over TCP?
394	firewall_config alcatraz ${fw} \
395		"ipfw"	\
396			"ipfw add 1001 queue 200 tcp from 2001:db8:42::2 to any out" \
397			"ipfw add 1000 queue 100 ipv6-icmp from 2001:db8:42::2 to any out" \
398			"ipfw add 1002 allow ip6 from any to any" \
399		"pf" \
400			"pass in proto tcp dnqueue (0, 200)"	\
401			"pass in proto icmp6 dnqueue (0, 100)"
402
403	fails=0
404	for i in `seq 1 3`
405	do
406		result=$(dd if=/dev/zero bs=1024 count=1000 | timeout 3 nc -w 5 -N 2001:db8:42::2 7 | wc -c)
407		if [ $result -ne 1024000 ];
408		then
409			echo "Failed to prioritise TCP traffic. Got only $result bytes"
410			fails=$(( ${fails} + 1 ))
411		fi
412	done
413	if [ ${fails} -lt 3 ];
414	then
415		atf_fail "We failed reversed prioritisation only ${fails} times."
416	fi
417}
418
419queue_v6_cleanup()
420{
421	firewall_cleanup $1
422}
423
424nat_head()
425{
426	atf_set descr 'Basic dummynet + NAT test'
427	atf_set require.user root
428}
429
430nat_body()
431{
432	fw=$1
433	firewall_init $fw
434	dummynet_init $fw
435	nat_init $fw
436
437	epair=$(vnet_mkepair)
438	epair_two=$(vnet_mkepair)
439
440	ifconfig ${epair}a 192.0.2.2/24 up
441	route add -net 198.51.100.0/24 192.0.2.1
442
443	vnet_mkjail gw ${epair}b ${epair_two}a
444	jexec gw ifconfig ${epair}b 192.0.2.1/24 up
445	jexec gw ifconfig ${epair_two}a 198.51.100.1/24 up
446	jexec gw sysctl net.inet.ip.forwarding=1
447
448	vnet_mkjail srv ${epair_two}b
449	jexec srv ifconfig ${epair_two}b 198.51.100.2/24 up
450
451	jexec gw dnctl pipe 1 config bw 300Byte/s
452
453	firewall_config gw $fw \
454		"pf"	\
455			"nat on ${epair_two}a inet from 192.0.2.0/24 to any -> (${epair_two}a)" \
456			"pass dnpipe 1"
457
458	# We've deliberately not set a route to 192.0.2.0/24 on srv, so the
459	# only way it can respond to this is if NAT is applied correctly.
460	atf_check -s exit:0 -o ignore ping -c 1 198.51.100.2
461}
462
463nat_cleanup()
464{
465	firewall_cleanup $1
466}
467
468setup_tests		\
469	interface_removal	\
470		ipfw	\
471		pf	\
472	pipe		\
473		ipfw	\
474		pf	\
475	pipe_v6		\
476		ipfw	\
477		pf	\
478	codel		\
479		ipfw	\
480		pf	\
481	queue		\
482		ipfw	\
483		pf	\
484	queue_v6	\
485		ipfw	\
486		pf	\
487	nat		\
488		pf
489