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