xref: /linux/tools/testing/selftests/net/forwarding/sch_red.sh (revision 7f71507851fc7764b36a3221839607d3a45c2025)
1#!/bin/bash
2# SPDX-License-Identifier: GPL-2.0
3
4# This test sends one stream of traffic from H1 through a TBF shaper, to a RED
5# within TBF shaper on $swp3. The two shapers have the same configuration, and
6# thus the resulting stream should fill all available bandwidth on the latter
7# shaper. A second stream is sent from H2 also via $swp3, and used to inject
8# additional traffic. Since all available bandwidth is taken, this traffic has
9# to go to backlog.
10#
11# +--------------------------+                     +--------------------------+
12# | H1                       |                     | H2                       |
13# |     + $h1                |                     |     + $h2                |
14# |     | 192.0.2.1/28       |                     |     | 192.0.2.2/28       |
15# |     | TBF 10Mbps         |                     |     |                    |
16# +-----|--------------------+                     +-----|--------------------+
17#       |                                                |
18# +-----|------------------------------------------------|--------------------+
19# | SW  |                                                |                    |
20# |  +--|------------------------------------------------|----------------+   |
21# |  |  + $swp1                                          + $swp2          |   |
22# |  |                               BR                                   |   |
23# |  |                                                                    |   |
24# |  |                                + $swp3                             |   |
25# |  |                                | TBF 10Mbps / RED                  |   |
26# |  +--------------------------------|-----------------------------------+   |
27# |                                   |                                       |
28# +-----------------------------------|---------------------------------------+
29#                                     |
30#                               +-----|--------------------+
31#			        | H3  |                    |
32#			        |     + $h1                |
33#			        |       192.0.2.3/28       |
34#			        |                          |
35#			        +--------------------------+
36
37ALL_TESTS="
38	ping_ipv4
39	ecn_test
40	ecn_nodrop_test
41	red_test
42	red_qevent_test
43	ecn_qevent_test
44"
45
46NUM_NETIFS=6
47CHECK_TC="yes"
48source lib.sh
49
50BACKLOG=30000
51PKTSZ=1400
52
53h1_create()
54{
55	simple_if_init $h1 192.0.2.1/28
56	defer simple_if_fini $h1 192.0.2.1/28
57
58	mtu_set $h1 10000
59	defer mtu_restore $h1
60
61	tc qdisc replace dev $h1 root handle 1: tbf \
62	   rate 10Mbit burst 10K limit 1M
63	defer tc qdisc del dev $h1 root
64}
65
66h2_create()
67{
68	simple_if_init $h2 192.0.2.2/28
69	defer simple_if_fini $h2 192.0.2.2/28
70
71	mtu_set $h2 10000
72	defer mtu_restore $h2
73}
74
75h3_create()
76{
77	simple_if_init $h3 192.0.2.3/28
78	defer simple_if_fini $h3 192.0.2.3/28
79
80	mtu_set $h3 10000
81	defer mtu_restore $h3
82}
83
84switch_create()
85{
86	ip link add dev br up type bridge
87	defer ip link del dev br
88
89	ip link set dev $swp1 up master br
90	defer ip link set dev $swp1 down nomaster
91
92	ip link set dev $swp2 up master br
93	defer ip link set dev $swp2 down nomaster
94
95	ip link set dev $swp3 up master br
96	defer ip link set dev $swp3 down nomaster
97
98	mtu_set $swp1 10000
99	defer mtu_restore $h1
100
101	mtu_set $swp2 10000
102	defer mtu_restore $h2
103
104	mtu_set $swp3 10000
105	defer mtu_restore $h3
106
107	tc qdisc replace dev $swp3 root handle 1: tbf \
108	   rate 10Mbit burst 10K limit 1M
109	defer tc qdisc del dev $swp3 root
110
111	ip link add name _drop_test up type dummy
112	defer ip link del dev _drop_test
113}
114
115setup_prepare()
116{
117	h1=${NETIFS[p1]}
118	swp1=${NETIFS[p2]}
119
120	h2=${NETIFS[p3]}
121	swp2=${NETIFS[p4]}
122
123	swp3=${NETIFS[p5]}
124	h3=${NETIFS[p6]}
125
126	h3_mac=$(mac_get $h3)
127
128	vrf_prepare
129	defer vrf_cleanup
130
131	h1_create
132	h2_create
133	h3_create
134	switch_create
135}
136
137ping_ipv4()
138{
139	ping_test $h1 192.0.2.3 " from host 1"
140	ping_test $h2 192.0.2.3 " from host 2"
141}
142
143get_qdisc_backlog()
144{
145	qdisc_stats_get $swp3 11: .backlog
146}
147
148get_nmarked()
149{
150	qdisc_stats_get $swp3 11: .marked
151}
152
153get_qdisc_npackets()
154{
155	qdisc_stats_get $swp3 11: .packets
156}
157
158get_nmirrored()
159{
160	link_stats_get _drop_test tx packets
161}
162
163send_packets()
164{
165	local proto=$1; shift
166	local pkts=$1; shift
167
168	$MZ $h2 -p $PKTSZ -a own -b $h3_mac -A 192.0.2.2 -B 192.0.2.3 -t $proto -q -c $pkts "$@"
169}
170
171# This sends traffic in an attempt to build a backlog of $size. Returns 0 on
172# success. After 10 failed attempts it bails out and returns 1. It dumps the
173# backlog size to stdout.
174build_backlog()
175{
176	local size=$1; shift
177	local proto=$1; shift
178
179	local i=0
180
181	while :; do
182		local cur=$(get_qdisc_backlog)
183		local diff=$((size - cur))
184		local pkts=$(((diff + PKTSZ - 1) / PKTSZ))
185
186		if ((cur >= size)); then
187			echo $cur
188			return 0
189		elif ((i++ > 10)); then
190			echo $cur
191			return 1
192		fi
193
194		send_packets $proto $pkts "$@"
195		sleep 1
196	done
197}
198
199check_marking()
200{
201	local cond=$1; shift
202
203	local npackets_0=$(get_qdisc_npackets)
204	local nmarked_0=$(get_nmarked)
205	sleep 5
206	local npackets_1=$(get_qdisc_npackets)
207	local nmarked_1=$(get_nmarked)
208
209	local nmarked_d=$((nmarked_1 - nmarked_0))
210	local npackets_d=$((npackets_1 - npackets_0))
211	local pct=$((100 * nmarked_d / npackets_d))
212
213	echo $pct
214	((pct $cond))
215}
216
217check_mirroring()
218{
219	local cond=$1; shift
220
221	local npackets_0=$(get_qdisc_npackets)
222	local nmirrored_0=$(get_nmirrored)
223	sleep 5
224	local npackets_1=$(get_qdisc_npackets)
225	local nmirrored_1=$(get_nmirrored)
226
227	local nmirrored_d=$((nmirrored_1 - nmirrored_0))
228	local npackets_d=$((npackets_1 - npackets_0))
229	local pct=$((100 * nmirrored_d / npackets_d))
230
231	echo $pct
232	((pct $cond))
233}
234
235ecn_test_common()
236{
237	local name=$1; shift
238	local limit=$1; shift
239	local backlog
240	local pct
241
242	# Build the below-the-limit backlog using UDP. We could use TCP just
243	# fine, but this way we get a proof that UDP is accepted when queue
244	# length is below the limit. The main stream is using TCP, and if the
245	# limit is misconfigured, we would see this traffic being ECN marked.
246	RET=0
247	backlog=$(build_backlog $((2 * limit / 3)) udp)
248	check_err $? "Could not build the requested backlog"
249	pct=$(check_marking "== 0")
250	check_err $? "backlog $backlog / $limit Got $pct% marked packets, expected == 0."
251	log_test "$name backlog < limit"
252
253	# Now push TCP, because non-TCP traffic would be early-dropped after the
254	# backlog crosses the limit, and we want to make sure that the backlog
255	# is above the limit.
256	RET=0
257	backlog=$(build_backlog $((3 * limit / 2)) tcp tos=0x01)
258	check_err $? "Could not build the requested backlog"
259	pct=$(check_marking ">= 95")
260	check_err $? "backlog $backlog / $limit Got $pct% marked packets, expected >= 95."
261	log_test "$name backlog > limit"
262}
263
264do_ecn_test()
265{
266	local limit=$1; shift
267	local name=ECN
268
269	$MZ $h1 -p $PKTSZ -A 192.0.2.1 -B 192.0.2.3 -c 0 \
270		-a own -b $h3_mac -t tcp -q tos=0x01 &
271	defer stop_traffic $!
272	sleep 1
273
274	ecn_test_common "$name" $limit
275
276	# Up there we saw that UDP gets accepted when backlog is below the
277	# limit. Now that it is above, it should all get dropped, and backlog
278	# building should fail.
279	RET=0
280	build_backlog $((2 * limit)) udp >/dev/null
281	check_fail $? "UDP traffic went into backlog instead of being early-dropped"
282	log_test "$name backlog > limit: UDP early-dropped"
283}
284
285do_ecn_nodrop_test()
286{
287	local limit=$1; shift
288	local name="ECN nodrop"
289
290	$MZ $h1 -p $PKTSZ -A 192.0.2.1 -B 192.0.2.3 -c 0 \
291		-a own -b $h3_mac -t tcp -q tos=0x01 &
292	defer stop_traffic $!
293	sleep 1
294
295	ecn_test_common "$name" $limit
296
297	# Up there we saw that UDP gets accepted when backlog is below the
298	# limit. Now that it is above, in nodrop mode, make sure it goes to
299	# backlog as well.
300	RET=0
301	build_backlog $((2 * limit)) udp >/dev/null
302	check_err $? "UDP traffic was early-dropped instead of getting into backlog"
303	log_test "$name backlog > limit: UDP not dropped"
304}
305
306do_red_test()
307{
308	local limit=$1; shift
309	local backlog
310	local pct
311
312	# Use ECN-capable TCP to verify there's no marking even though the queue
313	# is above limit.
314	$MZ $h1 -p $PKTSZ -A 192.0.2.1 -B 192.0.2.3 -c 0 \
315		-a own -b $h3_mac -t tcp -q tos=0x01 &
316	defer stop_traffic $!
317
318	# Pushing below the queue limit should work.
319	RET=0
320	backlog=$(build_backlog $((2 * limit / 3)) tcp tos=0x01)
321	check_err $? "Could not build the requested backlog"
322	pct=$(check_marking "== 0")
323	check_err $? "backlog $backlog / $limit Got $pct% marked packets, expected == 0."
324	log_test "RED backlog < limit"
325
326	# Pushing above should not.
327	RET=0
328	backlog=$(build_backlog $((3 * limit / 2)) tcp tos=0x01)
329	check_fail $? "Traffic went into backlog instead of being early-dropped"
330	pct=$(check_marking "== 0")
331	check_err $? "backlog $backlog / $limit Got $pct% marked packets, expected == 0."
332	log_test "RED backlog > limit"
333}
334
335do_red_qevent_test()
336{
337	local limit=$1; shift
338	local backlog
339	local base
340	local now
341	local pct
342
343	RET=0
344
345	$MZ $h1 -p $PKTSZ -A 192.0.2.1 -B 192.0.2.3 -c 0 \
346		-a own -b $h3_mac -t udp -q &
347	defer stop_traffic $!
348	sleep 1
349
350	tc filter add block 10 pref 1234 handle 102 matchall skip_hw \
351	   action mirred egress mirror dev _drop_test
352
353	# Push to the queue until it's at the limit. The configured limit is
354	# rounded by the qdisc, so this is the best we can do to get to the real
355	# limit.
356	build_backlog $((3 * limit / 2)) udp >/dev/null
357
358	base=$(get_nmirrored)
359	send_packets udp 100
360	sleep 1
361	now=$(get_nmirrored)
362	((now >= base + 100))
363	check_err $? "Dropped packets not observed: 100 expected, $((now - base)) seen"
364
365	tc filter del block 10 pref 1234 handle 102 matchall
366
367	base=$(get_nmirrored)
368	send_packets udp 100
369	sleep 1
370	now=$(get_nmirrored)
371	((now == base))
372	check_err $? "Dropped packets still observed: 0 expected, $((now - base)) seen"
373
374	log_test "RED early_dropped packets mirrored"
375}
376
377do_ecn_qevent_test()
378{
379	local limit=$1; shift
380	local name=ECN
381
382	RET=0
383
384	$MZ $h1 -p $PKTSZ -A 192.0.2.1 -B 192.0.2.3 -c 0 \
385		-a own -b $h3_mac -t tcp -q tos=0x01 &
386	defer stop_traffic $!
387	sleep 1
388
389	tc filter add block 10 pref 1234 handle 102 matchall skip_hw \
390	   action mirred egress mirror dev _drop_test
391
392	backlog=$(build_backlog $((2 * limit / 3)) tcp tos=0x01)
393	check_err $? "Could not build the requested backlog"
394	pct=$(check_mirroring "== 0")
395	check_err $? "backlog $backlog / $limit Got $pct% mirrored packets, expected == 0."
396
397	backlog=$(build_backlog $((3 * limit / 2)) tcp tos=0x01)
398	check_err $? "Could not build the requested backlog"
399	pct=$(check_mirroring ">= 95")
400	check_err $? "backlog $backlog / $limit Got $pct% mirrored packets, expected >= 95."
401
402	tc filter del block 10 pref 1234 handle 102 matchall
403
404	log_test "ECN marked packets mirrored"
405}
406
407install_qdisc()
408{
409	local -a args=("$@")
410
411	tc qdisc replace dev $swp3 parent 1:1 handle 11: red \
412	   limit 1M avpkt $PKTSZ probability 1 \
413	   min $BACKLOG max $((BACKLOG + 1)) burst 38 "${args[@]}"
414	sleep 1
415}
416
417uninstall_qdisc()
418{
419	tc qdisc del dev $swp3 parent 1:1
420}
421
422ecn_test()
423{
424	install_qdisc ecn
425	defer uninstall_qdisc
426	xfail_on_slow do_ecn_test $BACKLOG
427}
428
429ecn_nodrop_test()
430{
431	install_qdisc ecn nodrop
432	defer uninstall_qdisc
433	xfail_on_slow do_ecn_nodrop_test $BACKLOG
434}
435
436red_test()
437{
438	install_qdisc
439	defer uninstall_qdisc
440	xfail_on_slow do_red_test $BACKLOG
441}
442
443red_qevent_test()
444{
445	install_qdisc qevent early_drop block 10
446	defer uninstall_qdisc
447	xfail_on_slow do_red_qevent_test $BACKLOG
448}
449
450ecn_qevent_test()
451{
452	install_qdisc ecn qevent mark block 10
453	defer uninstall_qdisc
454	xfail_on_slow do_ecn_qevent_test $BACKLOG
455}
456
457trap cleanup EXIT
458
459setup_prepare
460setup_wait
461
462tests_run
463
464exit $EXIT_STATUS
465