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