1#!/bin/bash 2# SPDX-License-Identifier: GPL-2.0 3 4# This test is for checking IPv4 and IPv6 FIB rules API 5 6source lib.sh 7ret=0 8PAUSE_ON_FAIL=${PAUSE_ON_FAIL:=no} 9 10RTABLE=100 11RTABLE_PEER=101 12RTABLE_VRF=102 13GW_IP4=192.51.100.2 14SRC_IP=192.51.100.3 15GW_IP6=2001:db8:1::2 16SRC_IP6=2001:db8:1::3 17 18DEV_ADDR=192.51.100.1 19DEV_ADDR6=2001:db8:1::1 20DEV=dummy0 21TESTS=" 22 fib_rule6 23 fib_rule4 24 fib_rule6_connect 25 fib_rule4_connect 26 fib_rule6_vrf 27 fib_rule4_vrf 28" 29 30SELFTEST_PATH="" 31 32log_test() 33{ 34 local rc=$1 35 local expected=$2 36 local msg="$3" 37 38 if [ ${rc} -eq ${expected} ]; then 39 nsuccess=$((nsuccess+1)) 40 printf " TEST: %-60s [ OK ]\n" "${msg}" 41 else 42 ret=1 43 nfail=$((nfail+1)) 44 printf " TEST: %-60s [FAIL]\n" "${msg}" 45 if [ "${PAUSE_ON_FAIL}" = "yes" ]; then 46 echo 47 echo "hit enter to continue, 'q' to quit" 48 read a 49 [ "$a" = "q" ] && exit 1 50 fi 51 fi 52} 53 54setup() 55{ 56 set -e 57 setup_ns testns 58 IP="ip -netns $testns" 59 60 $IP link add dummy0 type dummy 61 $IP link set dev dummy0 up 62 $IP address add $DEV_ADDR/24 dev dummy0 63 $IP -6 address add $DEV_ADDR6/64 dev dummy0 64 65 set +e 66} 67 68cleanup() 69{ 70 $IP link del dev dummy0 &> /dev/null 71 cleanup_ns $testns 72} 73 74setup_peer() 75{ 76 set -e 77 78 setup_ns peerns 79 IP_PEER="ip -netns $peerns" 80 $IP_PEER link set dev lo up 81 82 ip link add name veth0 netns $testns type veth \ 83 peer name veth1 netns $peerns 84 $IP link set dev veth0 up 85 $IP_PEER link set dev veth1 up 86 87 $IP address add 192.0.2.10 peer 192.0.2.11/32 dev veth0 88 $IP_PEER address add 192.0.2.11 peer 192.0.2.10/32 dev veth1 89 90 $IP address add 2001:db8::10 peer 2001:db8::11/128 dev veth0 nodad 91 $IP_PEER address add 2001:db8::11 peer 2001:db8::10/128 dev veth1 nodad 92 93 $IP_PEER address add 198.51.100.11/32 dev lo 94 $IP route add table $RTABLE_PEER 198.51.100.11/32 via 192.0.2.11 95 96 $IP_PEER address add 2001:db8::1:11/128 dev lo 97 $IP route add table $RTABLE_PEER 2001:db8::1:11/128 via 2001:db8::11 98 99 set +e 100} 101 102cleanup_peer() 103{ 104 $IP link del dev veth0 105 ip netns del $peerns 106} 107 108setup_vrf() 109{ 110 $IP link add name vrf0 up type vrf table $RTABLE_VRF 111 $IP link set dev $DEV master vrf0 112} 113 114cleanup_vrf() 115{ 116 $IP link del dev vrf0 117} 118 119fib_check_iproute_support() 120{ 121 ip rule help 2>&1 | grep -q $1 122 if [ $? -ne 0 ]; then 123 echo "SKIP: iproute2 iprule too old, missing $1 match" 124 return 1 125 fi 126 127 ip route get help 2>&1 | grep -q $2 128 if [ $? -ne 0 ]; then 129 echo "SKIP: iproute2 get route too old, missing $2 match" 130 return 1 131 fi 132 133 return 0 134} 135 136fib_rule6_del() 137{ 138 $IP -6 rule del $1 139 log_test $? 0 "rule6 del $1" 140} 141 142fib_rule6_del_by_pref() 143{ 144 pref=$($IP -6 rule show $1 table $RTABLE | cut -d ":" -f 1) 145 $IP -6 rule del pref $pref 146} 147 148fib_rule6_test_match_n_redirect() 149{ 150 local match="$1" 151 local getmatch="$2" 152 local getnomatch="$3" 153 local description="$4" 154 local nomatch_description="$5" 155 156 $IP -6 rule add $match table $RTABLE 157 $IP -6 route get $GW_IP6 $getmatch | grep -q "table $RTABLE" 158 log_test $? 0 "rule6 check: $description" 159 160 $IP -6 route get $GW_IP6 $getnomatch 2>&1 | grep -q "table $RTABLE" 161 log_test $? 1 "rule6 check: $nomatch_description" 162 163 fib_rule6_del_by_pref "$match" 164 log_test $? 0 "rule6 del by pref: $description" 165} 166 167fib_rule6_test_reject() 168{ 169 local match="$1" 170 local rc 171 172 $IP -6 rule add $match table $RTABLE 2>/dev/null 173 rc=$? 174 log_test $rc 2 "rule6 check: $match" 175 176 if [ $rc -eq 0 ]; then 177 $IP -6 rule del $match table $RTABLE 178 fi 179} 180 181fib_rule6_test() 182{ 183 local ext_name=$1; shift 184 local getnomatch 185 local getmatch 186 local match 187 local cnt 188 189 echo 190 echo "IPv6 FIB rule tests $ext_name" 191 192 # setup the fib rule redirect route 193 $IP -6 route add table $RTABLE default via $GW_IP6 dev $DEV onlink 194 195 match="oif $DEV" 196 getnomatch="oif lo" 197 fib_rule6_test_match_n_redirect "$match" "$match" "$getnomatch" \ 198 "oif redirect to table" "oif no redirect to table" 199 200 match="from $SRC_IP6 iif $DEV" 201 getnomatch="from $SRC_IP6 iif lo" 202 fib_rule6_test_match_n_redirect "$match" "$match" "$getnomatch" \ 203 "iif redirect to table" "iif no redirect to table" 204 205 # Reject dsfield (tos) options which have ECN bits set 206 for cnt in $(seq 1 3); do 207 match="dsfield $cnt" 208 fib_rule6_test_reject "$match" 209 done 210 211 # Don't take ECN bits into account when matching on dsfield 212 match="tos 0x10" 213 for cnt in "0x10" "0x11" "0x12" "0x13"; do 214 # Using option 'tos' instead of 'dsfield' as old iproute2 215 # versions don't support 'dsfield' in ip rule show. 216 getmatch="tos $cnt" 217 getnomatch="tos 0x20" 218 fib_rule6_test_match_n_redirect "$match" "$getmatch" \ 219 "$getnomatch" "$getmatch redirect to table" \ 220 "$getnomatch no redirect to table" 221 done 222 223 # Re-test TOS matching, but with input routes since they are handled 224 # differently from output routes. 225 match="tos 0x10" 226 for cnt in "0x10" "0x11" "0x12" "0x13"; do 227 getmatch="tos $cnt" 228 getnomatch="tos 0x20" 229 fib_rule6_test_match_n_redirect "$match" \ 230 "from $SRC_IP6 iif $DEV $getmatch" \ 231 "from $SRC_IP6 iif $DEV $getnomatch" \ 232 "iif $getmatch redirect to table" \ 233 "iif $getnomatch no redirect to table" 234 done 235 236 match="fwmark 0x64" 237 getmatch="mark 0x64" 238 getnomatch="mark 0x63" 239 fib_rule6_test_match_n_redirect "$match" "$getmatch" "$getnomatch" \ 240 "fwmark redirect to table" "fwmark no redirect to table" 241 242 fib_check_iproute_support "uidrange" "uid" 243 if [ $? -eq 0 ]; then 244 match="uidrange 100-100" 245 getmatch="uid 100" 246 getnomatch="uid 101" 247 fib_rule6_test_match_n_redirect "$match" "$getmatch" \ 248 "$getnomatch" "uid redirect to table" \ 249 "uid no redirect to table" 250 fi 251 252 fib_check_iproute_support "sport" "sport" 253 if [ $? -eq 0 ]; then 254 match="sport 666 dport 777" 255 getnomatch="sport 667 dport 778" 256 fib_rule6_test_match_n_redirect "$match" "$match" \ 257 "$getnomatch" "sport and dport redirect to table" \ 258 "sport and dport no redirect to table" 259 fi 260 261 fib_check_iproute_support "ipproto" "ipproto" 262 if [ $? -eq 0 ]; then 263 match="ipproto tcp" 264 getnomatch="ipproto udp" 265 fib_rule6_test_match_n_redirect "$match" "$match" \ 266 "$getnomatch" "ipproto tcp match" "ipproto udp no match" 267 fi 268 269 fib_check_iproute_support "ipproto" "ipproto" 270 if [ $? -eq 0 ]; then 271 match="ipproto ipv6-icmp" 272 getnomatch="ipproto tcp" 273 fib_rule6_test_match_n_redirect "$match" "$match" \ 274 "$getnomatch" "ipproto ipv6-icmp match" \ 275 "ipproto ipv6-tcp no match" 276 fi 277 278 fib_check_iproute_support "dscp" "tos" 279 if [ $? -eq 0 ]; then 280 match="dscp 0x3f" 281 getmatch="tos 0xfc" 282 getnomatch="tos 0xf4" 283 fib_rule6_test_match_n_redirect "$match" "$getmatch" \ 284 "$getnomatch" "dscp redirect to table" \ 285 "dscp no redirect to table" 286 287 match="dscp 0x3f" 288 getmatch="from $SRC_IP6 iif $DEV tos 0xfc" 289 getnomatch="from $SRC_IP6 iif $DEV tos 0xf4" 290 fib_rule6_test_match_n_redirect "$match" "$getmatch" \ 291 "$getnomatch" "iif dscp redirect to table" \ 292 "iif dscp no redirect to table" 293 fi 294} 295 296fib_rule6_vrf_test() 297{ 298 setup_vrf 299 fib_rule6_test "- with VRF" 300 cleanup_vrf 301} 302 303# Verify that the IPV6_TCLASS option of UDPv6 and TCPv6 sockets is properly 304# taken into account when connecting the socket and when sending packets. 305fib_rule6_connect_test() 306{ 307 local dsfield 308 309 echo 310 echo "IPv6 FIB rule connect tests" 311 312 setup_peer 313 $IP -6 rule add dsfield 0x04 table $RTABLE_PEER 314 315 # Combine the base DS Field value (0x04) with all possible ECN values 316 # (Not-ECT: 0, ECT(1): 1, ECT(0): 2, CE: 3). 317 # The ECN bits shouldn't influence the result of the test. 318 for dsfield in 0x04 0x05 0x06 0x07; do 319 nettest -q -6 -B -t 5 -N $testns -O $peerns -U -D \ 320 -Q "${dsfield}" -l 2001:db8::1:11 -r 2001:db8::1:11 321 log_test $? 0 "rule6 dsfield udp connect (dsfield ${dsfield})" 322 323 nettest -q -6 -B -t 5 -N $testns -O $peerns -Q "${dsfield}" \ 324 -l 2001:db8::1:11 -r 2001:db8::1:11 325 log_test $? 0 "rule6 dsfield tcp connect (dsfield ${dsfield})" 326 done 327 328 # Check that UDP and TCP connections fail when using a DS Field that 329 # does not match the previously configured FIB rule. 330 nettest -q -6 -B -t 5 -N $testns -O $peerns -U -D \ 331 -Q 0x20 -l 2001:db8::1:11 -r 2001:db8::1:11 332 log_test $? 1 "rule6 dsfield udp no connect (dsfield 0x20)" 333 334 nettest -q -6 -B -t 5 -N $testns -O $peerns -Q 0x20 \ 335 -l 2001:db8::1:11 -r 2001:db8::1:11 336 log_test $? 1 "rule6 dsfield tcp no connect (dsfield 0x20)" 337 338 $IP -6 rule del dsfield 0x04 table $RTABLE_PEER 339 340 ip rule help 2>&1 | grep -q dscp 341 if [ $? -ne 0 ]; then 342 echo "SKIP: iproute2 iprule too old, missing dscp match" 343 cleanup_peer 344 return 345 fi 346 347 $IP -6 rule add dscp 0x3f table $RTABLE_PEER 348 349 nettest -q -6 -B -t 5 -N $testns -O $peerns -U -D -Q 0xfc \ 350 -l 2001:db8::1:11 -r 2001:db8::1:11 351 log_test $? 0 "rule6 dscp udp connect" 352 353 nettest -q -6 -B -t 5 -N $testns -O $peerns -Q 0xfc \ 354 -l 2001:db8::1:11 -r 2001:db8::1:11 355 log_test $? 0 "rule6 dscp tcp connect" 356 357 nettest -q -6 -B -t 5 -N $testns -O $peerns -U -D -Q 0xf4 \ 358 -l 2001:db8::1:11 -r 2001:db8::1:11 359 log_test $? 1 "rule6 dscp udp no connect" 360 361 nettest -q -6 -B -t 5 -N $testns -O $peerns -Q 0xf4 \ 362 -l 2001:db8::1:11 -r 2001:db8::1:11 363 log_test $? 1 "rule6 dscp tcp no connect" 364 365 $IP -6 rule del dscp 0x3f table $RTABLE_PEER 366 367 cleanup_peer 368} 369 370fib_rule4_del() 371{ 372 $IP rule del $1 373 log_test $? 0 "del $1" 374} 375 376fib_rule4_del_by_pref() 377{ 378 pref=$($IP rule show $1 table $RTABLE | cut -d ":" -f 1) 379 $IP rule del pref $pref 380} 381 382fib_rule4_test_match_n_redirect() 383{ 384 local match="$1" 385 local getmatch="$2" 386 local getnomatch="$3" 387 local description="$4" 388 local nomatch_description="$5" 389 390 $IP rule add $match table $RTABLE 391 $IP route get $GW_IP4 $getmatch | grep -q "table $RTABLE" 392 log_test $? 0 "rule4 check: $description" 393 394 $IP route get $GW_IP4 $getnomatch 2>&1 | grep -q "table $RTABLE" 395 log_test $? 1 "rule4 check: $nomatch_description" 396 397 fib_rule4_del_by_pref "$match" 398 log_test $? 0 "rule4 del by pref: $description" 399} 400 401fib_rule4_test_reject() 402{ 403 local match="$1" 404 local rc 405 406 $IP rule add $match table $RTABLE 2>/dev/null 407 rc=$? 408 log_test $rc 2 "rule4 check: $match" 409 410 if [ $rc -eq 0 ]; then 411 $IP rule del $match table $RTABLE 412 fi 413} 414 415fib_rule4_test() 416{ 417 local ext_name=$1; shift 418 local getnomatch 419 local getmatch 420 local match 421 local cnt 422 423 echo 424 echo "IPv4 FIB rule tests $ext_name" 425 426 # setup the fib rule redirect route 427 $IP route add table $RTABLE default via $GW_IP4 dev $DEV onlink 428 429 match="oif $DEV" 430 getnomatch="oif lo" 431 fib_rule4_test_match_n_redirect "$match" "$match" "$getnomatch" \ 432 "oif redirect to table" "oif no redirect to table" 433 434 # Enable forwarding and disable rp_filter as all the addresses are in 435 # the same subnet and egress device == ingress device. 436 ip netns exec $testns sysctl -qw net.ipv4.ip_forward=1 437 ip netns exec $testns sysctl -qw net.ipv4.conf.$DEV.rp_filter=0 438 match="from $SRC_IP iif $DEV" 439 getnomatch="from $SRC_IP iif lo" 440 fib_rule4_test_match_n_redirect "$match" "$match" "$getnomatch" \ 441 "iif redirect to table" "iif no redirect to table" 442 443 # Reject dsfield (tos) options which have ECN bits set 444 for cnt in $(seq 1 3); do 445 match="dsfield $cnt" 446 fib_rule4_test_reject "$match" 447 done 448 449 # Don't take ECN bits into account when matching on dsfield 450 match="tos 0x10" 451 for cnt in "0x10" "0x11" "0x12" "0x13"; do 452 # Using option 'tos' instead of 'dsfield' as old iproute2 453 # versions don't support 'dsfield' in ip rule show. 454 getmatch="tos $cnt" 455 getnomatch="tos 0x20" 456 fib_rule4_test_match_n_redirect "$match" "$getmatch" \ 457 "$getnomatch" "$getmatch redirect to table" \ 458 "$getnomatch no redirect to table" 459 done 460 461 # Re-test TOS matching, but with input routes since they are handled 462 # differently from output routes. 463 match="tos 0x10" 464 for cnt in "0x10" "0x11" "0x12" "0x13"; do 465 getmatch="tos $cnt" 466 getnomatch="tos 0x20" 467 fib_rule4_test_match_n_redirect "$match" \ 468 "from $SRC_IP iif $DEV $getmatch" \ 469 "from $SRC_IP iif $DEV $getnomatch" \ 470 "iif $getmatch redirect to table" \ 471 "iif $getnomatch no redirect to table" 472 done 473 474 match="fwmark 0x64" 475 getmatch="mark 0x64" 476 getnomatch="mark 0x63" 477 fib_rule4_test_match_n_redirect "$match" "$getmatch" "$getnomatch" \ 478 "fwmark redirect to table" "fwmark no redirect to table" 479 480 fib_check_iproute_support "uidrange" "uid" 481 if [ $? -eq 0 ]; then 482 match="uidrange 100-100" 483 getmatch="uid 100" 484 getnomatch="uid 101" 485 fib_rule4_test_match_n_redirect "$match" "$getmatch" \ 486 "$getnomatch" "uid redirect to table" \ 487 "uid no redirect to table" 488 fi 489 490 fib_check_iproute_support "sport" "sport" 491 if [ $? -eq 0 ]; then 492 match="sport 666 dport 777" 493 getnomatch="sport 667 dport 778" 494 fib_rule4_test_match_n_redirect "$match" "$match" \ 495 "$getnomatch" "sport and dport redirect to table" \ 496 "sport and dport no redirect to table" 497 fi 498 499 fib_check_iproute_support "ipproto" "ipproto" 500 if [ $? -eq 0 ]; then 501 match="ipproto tcp" 502 getnomatch="ipproto udp" 503 fib_rule4_test_match_n_redirect "$match" "$match" \ 504 "$getnomatch" "ipproto tcp match" \ 505 "ipproto udp no match" 506 fi 507 508 fib_check_iproute_support "ipproto" "ipproto" 509 if [ $? -eq 0 ]; then 510 match="ipproto icmp" 511 getnomatch="ipproto tcp" 512 fib_rule4_test_match_n_redirect "$match" "$match" \ 513 "$getnomatch" "ipproto icmp match" \ 514 "ipproto tcp no match" 515 fi 516 517 fib_check_iproute_support "dscp" "tos" 518 if [ $? -eq 0 ]; then 519 match="dscp 0x3f" 520 getmatch="tos 0xfc" 521 getnomatch="tos 0xf4" 522 fib_rule4_test_match_n_redirect "$match" "$getmatch" \ 523 "$getnomatch" "dscp redirect to table" \ 524 "dscp no redirect to table" 525 526 match="dscp 0x3f" 527 getmatch="from $SRC_IP iif $DEV tos 0xfc" 528 getnomatch="from $SRC_IP iif $DEV tos 0xf4" 529 fib_rule4_test_match_n_redirect "$match" "$getmatch" \ 530 "$getnomatch" "iif dscp redirect to table" \ 531 "iif dscp no redirect to table" 532 fi 533} 534 535fib_rule4_vrf_test() 536{ 537 setup_vrf 538 fib_rule4_test "- with VRF" 539 cleanup_vrf 540} 541 542# Verify that the IP_TOS option of UDPv4 and TCPv4 sockets is properly taken 543# into account when connecting the socket and when sending packets. 544fib_rule4_connect_test() 545{ 546 local dsfield 547 548 echo 549 echo "IPv4 FIB rule connect tests" 550 551 setup_peer 552 $IP -4 rule add dsfield 0x04 table $RTABLE_PEER 553 554 # Combine the base DS Field value (0x04) with all possible ECN values 555 # (Not-ECT: 0, ECT(1): 1, ECT(0): 2, CE: 3). 556 # The ECN bits shouldn't influence the result of the test. 557 for dsfield in 0x04 0x05 0x06 0x07; do 558 nettest -q -B -t 5 -N $testns -O $peerns -D -U -Q "${dsfield}" \ 559 -l 198.51.100.11 -r 198.51.100.11 560 log_test $? 0 "rule4 dsfield udp connect (dsfield ${dsfield})" 561 562 nettest -q -B -t 5 -N $testns -O $peerns -Q "${dsfield}" \ 563 -l 198.51.100.11 -r 198.51.100.11 564 log_test $? 0 "rule4 dsfield tcp connect (dsfield ${dsfield})" 565 done 566 567 # Check that UDP and TCP connections fail when using a DS Field that 568 # does not match the previously configured FIB rule. 569 nettest -q -B -t 5 -N $testns -O $peerns -D -U -Q 0x20 \ 570 -l 198.51.100.11 -r 198.51.100.11 571 log_test $? 1 "rule4 dsfield udp no connect (dsfield 0x20)" 572 573 nettest -q -B -t 5 -N $testns -O $peerns -Q 0x20 \ 574 -l 198.51.100.11 -r 198.51.100.11 575 log_test $? 1 "rule4 dsfield tcp no connect (dsfield 0x20)" 576 577 $IP -4 rule del dsfield 0x04 table $RTABLE_PEER 578 579 ip rule help 2>&1 | grep -q dscp 580 if [ $? -ne 0 ]; then 581 echo "SKIP: iproute2 iprule too old, missing dscp match" 582 cleanup_peer 583 return 584 fi 585 586 $IP -4 rule add dscp 0x3f table $RTABLE_PEER 587 588 nettest -q -B -t 5 -N $testns -O $peerns -D -U -Q 0xfc \ 589 -l 198.51.100.11 -r 198.51.100.11 590 log_test $? 0 "rule4 dscp udp connect" 591 592 nettest -q -B -t 5 -N $testns -O $peerns -Q 0xfc \ 593 -l 198.51.100.11 -r 198.51.100.11 594 log_test $? 0 "rule4 dscp tcp connect" 595 596 nettest -q -B -t 5 -N $testns -O $peerns -D -U -Q 0xf4 \ 597 -l 198.51.100.11 -r 198.51.100.11 598 log_test $? 1 "rule4 dscp udp no connect" 599 600 nettest -q -B -t 5 -N $testns -O $peerns -Q 0xf4 \ 601 -l 198.51.100.11 -r 198.51.100.11 602 log_test $? 1 "rule4 dscp tcp no connect" 603 604 $IP -4 rule del dscp 0x3f table $RTABLE_PEER 605 606 cleanup_peer 607} 608################################################################################ 609# usage 610 611usage() 612{ 613 cat <<EOF 614usage: ${0##*/} OPTS 615 616 -t <test> Test(s) to run (default: all) 617 (options: $TESTS) 618EOF 619} 620 621################################################################################ 622# main 623 624while getopts ":t:h" opt; do 625 case $opt in 626 t) TESTS=$OPTARG;; 627 h) usage; exit 0;; 628 *) usage; exit 1;; 629 esac 630done 631 632if [ "$(id -u)" -ne 0 ];then 633 echo "SKIP: Need root privileges" 634 exit $ksft_skip 635fi 636 637if [ ! -x "$(command -v ip)" ]; then 638 echo "SKIP: Could not run test without ip tool" 639 exit $ksft_skip 640fi 641 642check_gen_prog "nettest" 643 644# start clean 645cleanup &> /dev/null 646setup 647for t in $TESTS 648do 649 case $t in 650 fib_rule6_test|fib_rule6) fib_rule6_test;; 651 fib_rule4_test|fib_rule4) fib_rule4_test;; 652 fib_rule6_connect_test|fib_rule6_connect) fib_rule6_connect_test;; 653 fib_rule4_connect_test|fib_rule4_connect) fib_rule4_connect_test;; 654 fib_rule6_vrf_test|fib_rule6_vrf) fib_rule6_vrf_test;; 655 fib_rule4_vrf_test|fib_rule4_vrf) fib_rule4_vrf_test;; 656 657 help) echo "Test names: $TESTS"; exit 0;; 658 659 esac 660done 661cleanup 662 663if [ "$TESTS" != "none" ]; then 664 printf "\nTests passed: %3d\n" ${nsuccess} 665 printf "Tests failed: %3d\n" ${nfail} 666fi 667 668exit $ret 669