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