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