xref: /freebsd/tests/sys/netpfil/common/dummynet.sh (revision f7c32ed617858bcd22f8d1b03199099d50125721)
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
31pipe_head()
32{
33	atf_set descr 'Basic pipe test'
34	atf_set require.user root
35}
36
37pipe_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 bw 30Byte/s
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	# Saturate the link
64	ping -i .1 -c 5 -s 1200 192.0.2.2
65
66	# We should now be hitting the limits and get this packet dropped.
67	atf_check -s exit:2 -o ignore ping -c 1 -s 1200 192.0.2.2
68}
69
70pipe_cleanup()
71{
72	firewall_cleanup $1
73}
74
75pipe_v6_head()
76{
77	atf_set descr 'Basic IPv6 pipe test'
78	atf_set require.user root
79}
80
81pipe_v6_body()
82{
83	fw=$1
84	firewall_init $fw
85	dummynet_init $fw
86
87	epair=$(vnet_mkepair)
88	vnet_mkjail alcatraz ${epair}b
89
90	ifconfig ${epair}a inet6 2001:db8:42::1/64 up no_dad
91	jexec alcatraz ifconfig ${epair}b inet6 2001:db8:42::2/64 up no_dad
92
93	# Sanity check
94	atf_check -s exit:0 -o ignore ping6 -i .1 -c 3 -s 1200 2001:db8:42::2
95
96	jexec alcatraz dnctl pipe 1 config bw 100Byte/s
97
98	firewall_config alcatraz ${fw} \
99		"ipfw"	\
100			"ipfw add 1000 pipe 1 ip6 from any to any" \
101		"pf"	\
102			"pass dnpipe 1"
103
104	# Single ping succeeds
105	atf_check -s exit:0 -o ignore ping6 -c 1 2001:db8:42::2
106
107	# Saturate the link
108	ping6 -i .1 -c 5 -s 1200 2001:db8:42::2
109
110	# We should now be hitting the limit and get this packet dropped.
111	atf_check -s exit:2 -o ignore ping6 -c 1 -s 1200 2001:db8:42::2
112}
113
114pipe_v6_cleanup()
115{
116	firewall_cleanup $1
117}
118
119queue_head()
120{
121	atf_set descr 'Basic queue test'
122	atf_set require.user root
123}
124
125queue_body()
126{
127	fw=$1
128	firewall_init $fw
129	dummynet_init $fw
130
131	epair=$(vnet_mkepair)
132	vnet_mkjail alcatraz ${epair}b
133
134	ifconfig ${epair}a 192.0.2.1/24 up
135	jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
136	jexec alcatraz /usr/sbin/inetd -p inetd-alcatraz.pid \
137	    $(atf_get_srcdir)/../pf/echo_inetd.conf
138
139	# Sanity check
140	atf_check -s exit:0 -o ignore ping -i .1 -c 3 -s 1200 192.0.2.2
141	reply=$(echo "foo" | nc -N 192.0.2.2 7)
142	if [ "$reply" != "foo" ];
143	then
144		atf_fail "Echo sanity check failed"
145	fi
146
147	jexec alcatraz dnctl pipe 1 config bw 1MByte/s
148	jexec alcatraz dnctl sched 1 config pipe 1 type wf2q+
149	jexec alcatraz dnctl queue 100 config sched 1 weight 99 mask all
150	jexec alcatraz dnctl queue 200 config sched 1 weight 1 mask all
151
152	firewall_config alcatraz ${fw} \
153		"ipfw"	\
154			"ipfw add 1000 queue 100 tcp from 192.0.2.2 to any out" \
155			"ipfw add 1001 queue 200 icmp from 192.0.2.2 to any out" \
156			"ipfw add 1002 allow ip from any to any" \
157		"pf"	\
158			"pass in proto tcp dnqueue (0, 100)" \
159			"pass in proto icmp dnqueue (0, 200)"
160
161	# Single ping succeeds
162	atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
163
164	# Unsaturated TCP succeeds
165	reply=$(echo "foo" | nc -w 5 -N 192.0.2.2 7)
166	if [ "$reply" != "foo" ];
167	then
168		atf_fail "Unsaturated echo failed"
169	fi
170
171	# Saturate the link
172	ping -f -s 1300 192.0.2.2 &
173
174	# Allow this to fill the queue
175	sleep 1
176
177	# TCP should still just pass
178	fails=0
179	for i in `seq 1 3`
180	do
181		result=$(dd if=/dev/zero bs=1024 count=2000 | timeout 3 nc -w 5 -N 192.0.2.2 7 | wc -c)
182		if [ $result -ne 2048000 ];
183		then
184			echo "Failed to prioritise TCP traffic. Got only $result bytes"
185			fails=$(( ${fails} + 1 ))
186		fi
187	done
188	if [ ${fails} -gt 0 ];
189	then
190		atf_fail "We failed prioritisation ${fails} times"
191	fi
192
193	# This will fail if we reverse the pola^W priority
194	firewall_config alcatraz ${fw} \
195		"ipfw"	\
196			"ipfw add 1000 queue 200 tcp from 192.0.2.2 to any out" \
197			"ipfw add 1001 queue 100 icmp from 192.0.2.2 to any out" \
198			"ipfw add 1002 allow ip from any to any" \
199		"pf"	\
200			"pass in proto tcp dnqueue (0, 200)" \
201			"pass in proto icmp dnqueue (0, 100)"
202
203	jexec alcatraz ping -f -s 1300 192.0.2.1 &
204	sleep 1
205
206	fails=0
207	for i in `seq 1 3`
208	do
209		result=$(dd if=/dev/zero bs=1024 count=2000 | timeout 3 nc -w 5 -N 192.0.2.2 7 | wc -c)
210		if [ $result -ne 2048000 ];
211		then
212			echo "Failed to prioritise TCP traffic. Got only $result bytes"
213			fails=$(( ${fails} + 1 ))
214		fi
215	done
216	if [ ${fails} -lt 3 ];
217	then
218		atf_fail "We failed reversed prioritisation only ${fails} times."
219	fi
220}
221
222queue_cleanup()
223{
224	firewall_cleanup $1
225}
226
227queue_v6_head()
228{
229	atf_set descr 'Basic queue test'
230	atf_set require.user root
231}
232
233queue_v6_body()
234{
235	fw=$1
236	firewall_init $fw
237	dummynet_init $fw
238
239	epair=$(vnet_mkepair)
240	vnet_mkjail alcatraz ${epair}b
241
242	ifconfig ${epair}a inet6 2001:db8:42::1/64 no_dad up
243	jexec alcatraz ifconfig ${epair}b inet6 2001:db8:42::2 no_dad up
244	jexec alcatraz /usr/sbin/inetd -p inetd-alcatraz.pid \
245	    $(atf_get_srcdir)/../pf/echo_inetd.conf
246
247	# Sanity check
248	atf_check -s exit:0 -o ignore ping6 -i .1 -c 3 -s 1200 2001:db8:42::2
249	reply=$(echo "foo" | nc -N 2001:db8:42::2 7)
250	if [ "$reply" != "foo" ];
251	then
252		atf_fail "Echo sanity check failed"
253	fi
254
255	jexec alcatraz dnctl pipe 1 config bw 1MByte/s
256	jexec alcatraz dnctl sched 1 config pipe 1 type wf2q+
257	jexec alcatraz dnctl queue 100 config sched 1 weight 99 mask all
258	jexec alcatraz dnctl queue 200 config sched 1 weight 1 mask all
259
260	firewall_config alcatraz ${fw} \
261		"ipfw"	\
262			"ipfw add 1001 queue 100 tcp from 2001:db8:42::2 to any out" \
263			"ipfw add 1000 queue 200 ipv6-icmp from 2001:db8:42::2 to any out" \
264			"ipfw add 1002 allow ip6 from any to any" \
265		"pf" \
266			"pass in proto tcp dnqueue (0, 100)"	\
267			"pass in proto icmp6 dnqueue (0, 200)"
268
269	# Single ping succeeds
270	atf_check -s exit:0 -o ignore ping6 -c 1 2001:db8:42::2
271
272	# Unsaturated TCP succeeds
273	reply=$(echo "foo" | nc -w 5 -N 2001:db8:42::2 7)
274	if [ "$reply" != "foo" ];
275	then
276		atf_fail "Unsaturated echo failed"
277	fi
278
279	# Saturate the link
280	ping6 -f -s 1200 2001:db8:42::2 &
281
282	# Allow this to fill the queue
283	sleep 1
284
285	# TCP should still just pass
286	fails=0
287	for i in `seq 1 3`
288	do
289		result=$(dd if=/dev/zero bs=1024 count=1000 | timeout 3 nc -w 5 -N 2001:db8:42::2 7 | wc -c)
290		if [ $result -ne 1024000 ];
291		then
292			echo "Failed to prioritise TCP traffic. Got only $result bytes"
293			fails=$(( ${fails} + 1 ))
294		fi
295	done
296	if [ ${fails} -gt 0 ];
297	then
298		atf_fail "We failed prioritisation ${fails} times"
299	fi
300
301	# What happens if we prioritise ICMP over TCP?
302	firewall_config alcatraz ${fw} \
303		"ipfw"	\
304			"ipfw add 1001 queue 200 tcp from 2001:db8:42::2 to any out" \
305			"ipfw add 1000 queue 100 ipv6-icmp from 2001:db8:42::2 to any out" \
306			"ipfw add 1002 allow ip6 from any to any" \
307		"pf" \
308			"pass in proto tcp dnqueue (0, 200)"	\
309			"pass in proto icmp6 dnqueue (0, 100)"
310
311	fails=0
312	for i in `seq 1 3`
313	do
314		result=$(dd if=/dev/zero bs=1024 count=1000 | timeout 3 nc -w 5 -N 2001:db8:42::2 7 | wc -c)
315		if [ $result -ne 1024000 ];
316		then
317			echo "Failed to prioritise TCP traffic. Got only $result bytes"
318			fails=$(( ${fails} + 1 ))
319		fi
320	done
321	if [ ${fails} -lt 3 ];
322	then
323		atf_fail "We failed reversed prioritisation only ${fails} times."
324	fi
325}
326
327queue_v6_cleanup()
328{
329	firewall_cleanup $1
330}
331
332nat_head()
333{
334	atf_set descr 'Basic dummynet + NAT test'
335	atf_set require.user root
336}
337
338nat_body()
339{
340	fw=$1
341	firewall_init $fw
342	dummynet_init $fw
343	nat_init $fw
344
345	epair=$(vnet_mkepair)
346	epair_two=$(vnet_mkepair)
347
348	ifconfig ${epair}a 192.0.2.2/24 up
349	route add -net 198.51.100.0/24 192.0.2.1
350
351	vnet_mkjail gw ${epair}b ${epair_two}a
352	jexec gw ifconfig ${epair}b 192.0.2.1/24 up
353	jexec gw ifconfig ${epair_two}a 198.51.100.1/24 up
354	jexec gw sysctl net.inet.ip.forwarding=1
355
356	vnet_mkjail srv ${epair_two}b
357	jexec srv ifconfig ${epair_two}b 198.51.100.2/24 up
358
359	jexec gw dnctl pipe 1 config bw 300Byte/s
360
361	firewall_config gw $fw \
362		"pf"	\
363			"nat on ${epair_two}a inet from 192.0.2.0/24 to any -> (${epair_two}a)" \
364			"pass dnpipe 1"
365
366	# We've deliberately not set a route to 192.0.2.0/24 on srv, so the
367	# only way it can respond to this is if NAT is applied correctly.
368	atf_check -s exit:0 -o ignore ping -c 1 198.51.100.2
369}
370
371nat_cleanup()
372{
373	firewall_cleanup $1
374}
375
376setup_tests		\
377	pipe		\
378		ipfw	\
379		pf	\
380	pipe_v6		\
381		ipfw	\
382		pf	\
383	queue		\
384		ipfw	\
385		pf	\
386	queue_v6	\
387		ipfw	\
388		pf	\
389	nat		\
390		pf
391