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 $IP rule show | grep -q l3mdev 39 if [ $? -eq 0 ]; then 40 msg="$msg (VRF)" 41 fi 42 43 if [ ${rc} -eq ${expected} ]; then 44 nsuccess=$((nsuccess+1)) 45 printf "\n TEST: %-60s [ OK ]\n" "${msg}" 46 else 47 ret=1 48 nfail=$((nfail+1)) 49 printf "\n TEST: %-60s [FAIL]\n" "${msg}" 50 if [ "${PAUSE_ON_FAIL}" = "yes" ]; then 51 echo 52 echo "hit enter to continue, 'q' to quit" 53 read a 54 [ "$a" = "q" ] && exit 1 55 fi 56 fi 57} 58 59log_section() 60{ 61 echo 62 echo "######################################################################" 63 echo "TEST SECTION: $*" 64 echo "######################################################################" 65} 66 67check_nettest() 68{ 69 if which nettest > /dev/null 2>&1; then 70 return 0 71 fi 72 73 # Add the selftest directory to PATH if not already done 74 if [ "${SELFTEST_PATH}" = "" ]; then 75 SELFTEST_PATH="$(dirname $0)" 76 PATH="${PATH}:${SELFTEST_PATH}" 77 78 # Now retry with the new path 79 if which nettest > /dev/null 2>&1; then 80 return 0 81 fi 82 83 if [ "${ret}" -eq 0 ]; then 84 ret="${ksft_skip}" 85 fi 86 echo "nettest not found (try 'make -C ${SELFTEST_PATH} nettest')" 87 fi 88 89 return 1 90} 91 92setup() 93{ 94 set -e 95 setup_ns testns 96 IP="ip -netns $testns" 97 98 $IP link add dummy0 type dummy 99 $IP link set dev dummy0 up 100 $IP address add $DEV_ADDR/24 dev dummy0 101 $IP -6 address add $DEV_ADDR6/64 dev dummy0 102 103 set +e 104} 105 106cleanup() 107{ 108 $IP link del dev dummy0 &> /dev/null 109 cleanup_ns $testns 110} 111 112setup_peer() 113{ 114 set -e 115 116 setup_ns peerns 117 IP_PEER="ip -netns $peerns" 118 $IP_PEER link set dev lo up 119 120 ip link add name veth0 netns $testns type veth \ 121 peer name veth1 netns $peerns 122 $IP link set dev veth0 up 123 $IP_PEER link set dev veth1 up 124 125 $IP address add 192.0.2.10 peer 192.0.2.11/32 dev veth0 126 $IP_PEER address add 192.0.2.11 peer 192.0.2.10/32 dev veth1 127 128 $IP address add 2001:db8::10 peer 2001:db8::11/128 dev veth0 nodad 129 $IP_PEER address add 2001:db8::11 peer 2001:db8::10/128 dev veth1 nodad 130 131 $IP_PEER address add 198.51.100.11/32 dev lo 132 $IP route add table $RTABLE_PEER 198.51.100.11/32 via 192.0.2.11 133 134 $IP_PEER address add 2001:db8::1:11/128 dev lo 135 $IP route add table $RTABLE_PEER 2001:db8::1:11/128 via 2001:db8::11 136 137 set +e 138} 139 140cleanup_peer() 141{ 142 $IP link del dev veth0 143 ip netns del $peerns 144} 145 146setup_vrf() 147{ 148 $IP link add name vrf0 up type vrf table $RTABLE_VRF 149 $IP link set dev $DEV master vrf0 150} 151 152cleanup_vrf() 153{ 154 $IP link del dev vrf0 155} 156 157fib_check_iproute_support() 158{ 159 ip rule help 2>&1 | grep -q $1 160 if [ $? -ne 0 ]; then 161 echo "SKIP: iproute2 iprule too old, missing $1 match" 162 return 1 163 fi 164 165 ip route get help 2>&1 | grep -q $2 166 if [ $? -ne 0 ]; then 167 echo "SKIP: iproute2 get route too old, missing $2 match" 168 return 1 169 fi 170 171 return 0 172} 173 174fib_rule6_del() 175{ 176 $IP -6 rule del $1 177 log_test $? 0 "rule6 del $1" 178} 179 180fib_rule6_del_by_pref() 181{ 182 pref=$($IP -6 rule show $1 table $RTABLE | cut -d ":" -f 1) 183 $IP -6 rule del pref $pref 184} 185 186fib_rule6_test_match_n_redirect() 187{ 188 local match="$1" 189 local getmatch="$2" 190 local description="$3" 191 192 $IP -6 rule add $match table $RTABLE 193 $IP -6 route get $GW_IP6 $getmatch | grep -q "table $RTABLE" 194 log_test $? 0 "rule6 check: $description" 195 196 fib_rule6_del_by_pref "$match" 197 log_test $? 0 "rule6 del by pref: $description" 198} 199 200fib_rule6_test_reject() 201{ 202 local match="$1" 203 local rc 204 205 $IP -6 rule add $match table $RTABLE 2>/dev/null 206 rc=$? 207 log_test $rc 2 "rule6 check: $match" 208 209 if [ $rc -eq 0 ]; then 210 $IP -6 rule del $match table $RTABLE 211 fi 212} 213 214fib_rule6_test() 215{ 216 local getmatch 217 local match 218 local cnt 219 220 # setup the fib rule redirect route 221 $IP -6 route add table $RTABLE default via $GW_IP6 dev $DEV onlink 222 223 match="oif $DEV" 224 fib_rule6_test_match_n_redirect "$match" "$match" "oif redirect to table" 225 226 match="from $SRC_IP6 iif $DEV" 227 fib_rule6_test_match_n_redirect "$match" "$match" "iif redirect to table" 228 229 # Reject dsfield (tos) options which have ECN bits set 230 for cnt in $(seq 1 3); do 231 match="dsfield $cnt" 232 fib_rule6_test_reject "$match" 233 done 234 235 # Don't take ECN bits into account when matching on dsfield 236 match="tos 0x10" 237 for cnt in "0x10" "0x11" "0x12" "0x13"; do 238 # Using option 'tos' instead of 'dsfield' as old iproute2 239 # versions don't support 'dsfield' in ip rule show. 240 getmatch="tos $cnt" 241 fib_rule6_test_match_n_redirect "$match" "$getmatch" \ 242 "$getmatch redirect to table" 243 done 244 245 match="fwmark 0x64" 246 getmatch="mark 0x64" 247 fib_rule6_test_match_n_redirect "$match" "$getmatch" "fwmark redirect to table" 248 249 fib_check_iproute_support "uidrange" "uid" 250 if [ $? -eq 0 ]; then 251 match="uidrange 100-100" 252 getmatch="uid 100" 253 fib_rule6_test_match_n_redirect "$match" "$getmatch" "uid redirect to table" 254 fi 255 256 fib_check_iproute_support "sport" "sport" 257 if [ $? -eq 0 ]; then 258 match="sport 666 dport 777" 259 fib_rule6_test_match_n_redirect "$match" "$match" "sport and dport redirect to table" 260 fi 261 262 fib_check_iproute_support "ipproto" "ipproto" 263 if [ $? -eq 0 ]; then 264 match="ipproto tcp" 265 fib_rule6_test_match_n_redirect "$match" "$match" "ipproto match" 266 fi 267 268 fib_check_iproute_support "ipproto" "ipproto" 269 if [ $? -eq 0 ]; then 270 match="ipproto ipv6-icmp" 271 fib_rule6_test_match_n_redirect "$match" "$match" "ipproto ipv6-icmp match" 272 fi 273} 274 275fib_rule6_vrf_test() 276{ 277 setup_vrf 278 fib_rule6_test 279 cleanup_vrf 280} 281 282# Verify that the IPV6_TCLASS option of UDPv6 and TCPv6 sockets is properly 283# taken into account when connecting the socket and when sending packets. 284fib_rule6_connect_test() 285{ 286 local dsfield 287 288 if ! check_nettest; then 289 echo "SKIP: Could not run test without nettest tool" 290 return 291 fi 292 293 setup_peer 294 $IP -6 rule add dsfield 0x04 table $RTABLE_PEER 295 296 # Combine the base DS Field value (0x04) with all possible ECN values 297 # (Not-ECT: 0, ECT(1): 1, ECT(0): 2, CE: 3). 298 # The ECN bits shouldn't influence the result of the test. 299 for dsfield in 0x04 0x05 0x06 0x07; do 300 nettest -q -6 -B -t 5 -N $testns -O $peerns -U -D \ 301 -Q "${dsfield}" -l 2001:db8::1:11 -r 2001:db8::1:11 302 log_test $? 0 "rule6 dsfield udp connect (dsfield ${dsfield})" 303 304 nettest -q -6 -B -t 5 -N $testns -O $peerns -Q "${dsfield}" \ 305 -l 2001:db8::1:11 -r 2001:db8::1:11 306 log_test $? 0 "rule6 dsfield tcp connect (dsfield ${dsfield})" 307 done 308 309 $IP -6 rule del dsfield 0x04 table $RTABLE_PEER 310 cleanup_peer 311} 312 313fib_rule4_del() 314{ 315 $IP rule del $1 316 log_test $? 0 "del $1" 317} 318 319fib_rule4_del_by_pref() 320{ 321 pref=$($IP rule show $1 table $RTABLE | cut -d ":" -f 1) 322 $IP rule del pref $pref 323} 324 325fib_rule4_test_match_n_redirect() 326{ 327 local match="$1" 328 local getmatch="$2" 329 local description="$3" 330 331 $IP rule add $match table $RTABLE 332 $IP route get $GW_IP4 $getmatch | grep -q "table $RTABLE" 333 log_test $? 0 "rule4 check: $description" 334 335 fib_rule4_del_by_pref "$match" 336 log_test $? 0 "rule4 del by pref: $description" 337} 338 339fib_rule4_test_reject() 340{ 341 local match="$1" 342 local rc 343 344 $IP rule add $match table $RTABLE 2>/dev/null 345 rc=$? 346 log_test $rc 2 "rule4 check: $match" 347 348 if [ $rc -eq 0 ]; then 349 $IP rule del $match table $RTABLE 350 fi 351} 352 353fib_rule4_test() 354{ 355 local getmatch 356 local match 357 local cnt 358 359 # setup the fib rule redirect route 360 $IP route add table $RTABLE default via $GW_IP4 dev $DEV onlink 361 362 match="oif $DEV" 363 fib_rule4_test_match_n_redirect "$match" "$match" "oif redirect to table" 364 365 # need enable forwarding and disable rp_filter temporarily as all the 366 # addresses are in the same subnet and egress device == ingress device. 367 ip netns exec $testns sysctl -qw net.ipv4.ip_forward=1 368 ip netns exec $testns sysctl -qw net.ipv4.conf.$DEV.rp_filter=0 369 match="from $SRC_IP iif $DEV" 370 fib_rule4_test_match_n_redirect "$match" "$match" "iif redirect to table" 371 ip netns exec $testns sysctl -qw net.ipv4.ip_forward=0 372 373 # Reject dsfield (tos) options which have ECN bits set 374 for cnt in $(seq 1 3); do 375 match="dsfield $cnt" 376 fib_rule4_test_reject "$match" 377 done 378 379 # Don't take ECN bits into account when matching on dsfield 380 match="tos 0x10" 381 for cnt in "0x10" "0x11" "0x12" "0x13"; do 382 # Using option 'tos' instead of 'dsfield' as old iproute2 383 # versions don't support 'dsfield' in ip rule show. 384 getmatch="tos $cnt" 385 fib_rule4_test_match_n_redirect "$match" "$getmatch" \ 386 "$getmatch redirect to table" 387 done 388 389 match="fwmark 0x64" 390 getmatch="mark 0x64" 391 fib_rule4_test_match_n_redirect "$match" "$getmatch" "fwmark redirect to table" 392 393 fib_check_iproute_support "uidrange" "uid" 394 if [ $? -eq 0 ]; then 395 match="uidrange 100-100" 396 getmatch="uid 100" 397 fib_rule4_test_match_n_redirect "$match" "$getmatch" "uid redirect to table" 398 fi 399 400 fib_check_iproute_support "sport" "sport" 401 if [ $? -eq 0 ]; then 402 match="sport 666 dport 777" 403 fib_rule4_test_match_n_redirect "$match" "$match" "sport and dport redirect to table" 404 fi 405 406 fib_check_iproute_support "ipproto" "ipproto" 407 if [ $? -eq 0 ]; then 408 match="ipproto tcp" 409 fib_rule4_test_match_n_redirect "$match" "$match" "ipproto tcp match" 410 fi 411 412 fib_check_iproute_support "ipproto" "ipproto" 413 if [ $? -eq 0 ]; then 414 match="ipproto icmp" 415 fib_rule4_test_match_n_redirect "$match" "$match" "ipproto icmp match" 416 fi 417} 418 419fib_rule4_vrf_test() 420{ 421 setup_vrf 422 fib_rule4_test 423 cleanup_vrf 424} 425 426# Verify that the IP_TOS option of UDPv4 and TCPv4 sockets is properly taken 427# into account when connecting the socket and when sending packets. 428fib_rule4_connect_test() 429{ 430 local dsfield 431 432 if ! check_nettest; then 433 echo "SKIP: Could not run test without nettest tool" 434 return 435 fi 436 437 setup_peer 438 $IP -4 rule add dsfield 0x04 table $RTABLE_PEER 439 440 # Combine the base DS Field value (0x04) with all possible ECN values 441 # (Not-ECT: 0, ECT(1): 1, ECT(0): 2, CE: 3). 442 # The ECN bits shouldn't influence the result of the test. 443 for dsfield in 0x04 0x05 0x06 0x07; do 444 nettest -q -B -t 5 -N $testns -O $peerns -D -U -Q "${dsfield}" \ 445 -l 198.51.100.11 -r 198.51.100.11 446 log_test $? 0 "rule4 dsfield udp connect (dsfield ${dsfield})" 447 448 nettest -q -B -t 5 -N $testns -O $peerns -Q "${dsfield}" \ 449 -l 198.51.100.11 -r 198.51.100.11 450 log_test $? 0 "rule4 dsfield tcp connect (dsfield ${dsfield})" 451 done 452 453 $IP -4 rule del dsfield 0x04 table $RTABLE_PEER 454 cleanup_peer 455} 456 457run_fibrule_tests() 458{ 459 log_section "IPv4 fib rule" 460 fib_rule4_test 461 log_section "IPv6 fib rule" 462 fib_rule6_test 463} 464################################################################################ 465# usage 466 467usage() 468{ 469 cat <<EOF 470usage: ${0##*/} OPTS 471 472 -t <test> Test(s) to run (default: all) 473 (options: $TESTS) 474EOF 475} 476 477################################################################################ 478# main 479 480while getopts ":t:h" opt; do 481 case $opt in 482 t) TESTS=$OPTARG;; 483 h) usage; exit 0;; 484 *) usage; exit 1;; 485 esac 486done 487 488if [ "$(id -u)" -ne 0 ];then 489 echo "SKIP: Need root privileges" 490 exit $ksft_skip 491fi 492 493if [ ! -x "$(command -v ip)" ]; then 494 echo "SKIP: Could not run test without ip tool" 495 exit $ksft_skip 496fi 497 498# start clean 499cleanup &> /dev/null 500setup 501for t in $TESTS 502do 503 case $t in 504 fib_rule6_test|fib_rule6) fib_rule6_test;; 505 fib_rule4_test|fib_rule4) fib_rule4_test;; 506 fib_rule6_connect_test|fib_rule6_connect) fib_rule6_connect_test;; 507 fib_rule4_connect_test|fib_rule4_connect) fib_rule4_connect_test;; 508 fib_rule6_vrf_test|fib_rule6_vrf) fib_rule6_vrf_test;; 509 fib_rule4_vrf_test|fib_rule4_vrf) fib_rule4_vrf_test;; 510 511 help) echo "Test names: $TESTS"; exit 0;; 512 513 esac 514done 515cleanup 516 517if [ "$TESTS" != "none" ]; then 518 printf "\nTests passed: %3d\n" ${nsuccess} 519 printf "Tests failed: %3d\n" ${nfail} 520fi 521 522exit $ret 523