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