Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests: forwarding: Add a RED test for SW datapath

This test is inspired by the mlxsw RED selftest. It is much simpler to set
up (also because there is no point in testing PRIO / RED encapsulation). It
tests bare RED, ECN and ECN+nodrop modes of operation. On top of that it
tests RED early_drop and mark qevents.

Signed-off-by: Petr Machata <petrm@mellanox.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Petr Machata and committed by
David S. Miller
6cf0291f aee9caa0

+492
+492
tools/testing/selftests/net/forwarding/sch_red.sh
··· 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 + 36 + ALL_TESTS=" 37 + ping_ipv4 38 + ecn_test 39 + ecn_nodrop_test 40 + red_test 41 + red_qevent_test 42 + ecn_qevent_test 43 + " 44 + 45 + NUM_NETIFS=6 46 + CHECK_TC="yes" 47 + source lib.sh 48 + 49 + BACKLOG=30000 50 + PKTSZ=1400 51 + 52 + h1_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 + 60 + h1_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 + 67 + h2_create() 68 + { 69 + simple_if_init $h2 192.0.2.2/28 70 + mtu_set $h2 10000 71 + } 72 + 73 + h2_destroy() 74 + { 75 + mtu_restore $h2 76 + simple_if_fini $h2 192.0.2.2/28 77 + } 78 + 79 + h3_create() 80 + { 81 + simple_if_init $h3 192.0.2.3/28 82 + mtu_set $h3 10000 83 + } 84 + 85 + h3_destroy() 86 + { 87 + mtu_restore $h3 88 + simple_if_fini $h3 192.0.2.3/28 89 + } 90 + 91 + switch_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 + 107 + switch_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 + 122 + setup_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 + 143 + cleanup() 144 + { 145 + pre_cleanup 146 + 147 + switch_destroy 148 + h3_destroy 149 + h2_destroy 150 + h1_destroy 151 + 152 + vrf_cleanup 153 + } 154 + 155 + ping_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 + 161 + get_qdisc_backlog() 162 + { 163 + qdisc_stats_get $swp3 11: .backlog 164 + } 165 + 166 + get_nmarked() 167 + { 168 + qdisc_stats_get $swp3 11: .marked 169 + } 170 + 171 + get_qdisc_npackets() 172 + { 173 + qdisc_stats_get $swp3 11: .packets 174 + } 175 + 176 + get_nmirrored() 177 + { 178 + link_stats_get _drop_test tx packets 179 + } 180 + 181 + send_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. 192 + build_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 + 217 + check_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 + 235 + check_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 + 253 + ecn_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 + 282 + do_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 + 305 + do_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 + 328 + do_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 + 359 + do_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 + 403 + do_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 + 435 + install_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 + 445 + uninstall_qdisc() 446 + { 447 + tc qdisc del dev $swp3 parent 1:1 448 + } 449 + 450 + ecn_test() 451 + { 452 + install_qdisc ecn 453 + do_ecn_test $BACKLOG 454 + uninstall_qdisc 455 + } 456 + 457 + ecn_nodrop_test() 458 + { 459 + install_qdisc ecn nodrop 460 + do_ecn_nodrop_test $BACKLOG 461 + uninstall_qdisc 462 + } 463 + 464 + red_test() 465 + { 466 + install_qdisc 467 + do_red_test $BACKLOG 468 + uninstall_qdisc 469 + } 470 + 471 + red_qevent_test() 472 + { 473 + install_qdisc qevent early_drop block 10 474 + do_red_qevent_test $BACKLOG 475 + uninstall_qdisc 476 + } 477 + 478 + ecn_qevent_test() 479 + { 480 + install_qdisc ecn qevent mark block 10 481 + do_ecn_qevent_test $BACKLOG 482 + uninstall_qdisc 483 + } 484 + 485 + trap cleanup EXIT 486 + 487 + setup_prepare 488 + setup_wait 489 + 490 + tests_run 491 + 492 + exit $EXIT_STATUS