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 mtu_set $h1 10000 57 tc qdisc replace dev $h1 root handle 1: tbf \ 58 rate 10Mbit burst 10K limit 1M 59} 60 61h1_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 68h2_create() 69{ 70 simple_if_init $h2 192.0.2.2/28 71 mtu_set $h2 10000 72} 73 74h2_destroy() 75{ 76 mtu_restore $h2 77 simple_if_fini $h2 192.0.2.2/28 78} 79 80h3_create() 81{ 82 simple_if_init $h3 192.0.2.3/28 83 mtu_set $h3 10000 84} 85 86h3_destroy() 87{ 88 mtu_restore $h3 89 simple_if_fini $h3 192.0.2.3/28 90} 91 92switch_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 108switch_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 123setup_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 144cleanup() 145{ 146 pre_cleanup 147 148 switch_destroy 149 h3_destroy 150 h2_destroy 151 h1_destroy 152 153 vrf_cleanup 154} 155 156ping_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 162get_qdisc_backlog() 163{ 164 qdisc_stats_get $swp3 11: .backlog 165} 166 167get_nmarked() 168{ 169 qdisc_stats_get $swp3 11: .marked 170} 171 172get_qdisc_npackets() 173{ 174 qdisc_stats_get $swp3 11: .packets 175} 176 177get_nmirrored() 178{ 179 link_stats_get _drop_test tx packets 180} 181 182send_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. 193build_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 218check_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 236check_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 254ecn_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 283do_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 306do_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 329do_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 360do_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 404do_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 436install_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 446uninstall_qdisc() 447{ 448 tc qdisc del dev $swp3 parent 1:1 449} 450 451ecn_test() 452{ 453 install_qdisc ecn 454 do_ecn_test $BACKLOG 455 uninstall_qdisc 456} 457 458ecn_nodrop_test() 459{ 460 install_qdisc ecn nodrop 461 do_ecn_nodrop_test $BACKLOG 462 uninstall_qdisc 463} 464 465red_test() 466{ 467 install_qdisc 468 do_red_test $BACKLOG 469 uninstall_qdisc 470} 471 472red_qevent_test() 473{ 474 install_qdisc qevent early_drop block 10 475 do_red_qevent_test $BACKLOG 476 uninstall_qdisc 477} 478 479ecn_qevent_test() 480{ 481 install_qdisc ecn qevent mark block 10 482 do_ecn_qevent_test $BACKLOG 483 uninstall_qdisc 484} 485 486trap cleanup EXIT 487 488setup_prepare 489setup_wait 490 491tests_run 492 493exit $EXIT_STATUS 494