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