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 12GW_IP4=192.51.100.2 13SRC_IP=192.51.100.3 14GW_IP6=2001:db8:1::2 15SRC_IP6=2001:db8:1::3 16 17DEV_ADDR=192.51.100.1 18DEV_ADDR6=2001:db8:1::1 19DEV=dummy0 20TESTS="fib_rule6 fib_rule4 fib_rule6_connect fib_rule4_connect" 21 22SELFTEST_PATH="" 23 24log_test() 25{ 26 local rc=$1 27 local expected=$2 28 local msg="$3" 29 30 if [ ${rc} -eq ${expected} ]; then 31 nsuccess=$((nsuccess+1)) 32 printf "\n TEST: %-50s [ OK ]\n" "${msg}" 33 else 34 ret=1 35 nfail=$((nfail+1)) 36 printf "\n TEST: %-50s [FAIL]\n" "${msg}" 37 if [ "${PAUSE_ON_FAIL}" = "yes" ]; then 38 echo 39 echo "hit enter to continue, 'q' to quit" 40 read a 41 [ "$a" = "q" ] && exit 1 42 fi 43 fi 44} 45 46log_section() 47{ 48 echo 49 echo "######################################################################" 50 echo "TEST SECTION: $*" 51 echo "######################################################################" 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 133fib_check_iproute_support() 134{ 135 ip rule help 2>&1 | grep -q $1 136 if [ $? -ne 0 ]; then 137 echo "SKIP: iproute2 iprule too old, missing $1 match" 138 return 1 139 fi 140 141 ip route get help 2>&1 | grep -q $2 142 if [ $? -ne 0 ]; then 143 echo "SKIP: iproute2 get route too old, missing $2 match" 144 return 1 145 fi 146 147 return 0 148} 149 150fib_rule6_del() 151{ 152 $IP -6 rule del $1 153 log_test $? 0 "rule6 del $1" 154} 155 156fib_rule6_del_by_pref() 157{ 158 pref=$($IP -6 rule show $1 table $RTABLE | cut -d ":" -f 1) 159 $IP -6 rule del pref $pref 160} 161 162fib_rule6_test_match_n_redirect() 163{ 164 local match="$1" 165 local getmatch="$2" 166 local description="$3" 167 168 $IP -6 rule add $match table $RTABLE 169 $IP -6 route get $GW_IP6 $getmatch | grep -q "table $RTABLE" 170 log_test $? 0 "rule6 check: $description" 171 172 fib_rule6_del_by_pref "$match" 173 log_test $? 0 "rule6 del by pref: $description" 174} 175 176fib_rule6_test_reject() 177{ 178 local match="$1" 179 local rc 180 181 $IP -6 rule add $match table $RTABLE 2>/dev/null 182 rc=$? 183 log_test $rc 2 "rule6 check: $match" 184 185 if [ $rc -eq 0 ]; then 186 $IP -6 rule del $match table $RTABLE 187 fi 188} 189 190fib_rule6_test() 191{ 192 local getmatch 193 local match 194 local cnt 195 196 # setup the fib rule redirect route 197 $IP -6 route add table $RTABLE default via $GW_IP6 dev $DEV onlink 198 199 match="oif $DEV" 200 fib_rule6_test_match_n_redirect "$match" "$match" "oif redirect to table" 201 202 match="from $SRC_IP6 iif $DEV" 203 fib_rule6_test_match_n_redirect "$match" "$match" "iif 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 fib_rule6_test_match_n_redirect "$match" "$getmatch" \ 218 "$getmatch redirect to table" 219 done 220 221 match="fwmark 0x64" 222 getmatch="mark 0x64" 223 fib_rule6_test_match_n_redirect "$match" "$getmatch" "fwmark redirect to table" 224 225 fib_check_iproute_support "uidrange" "uid" 226 if [ $? -eq 0 ]; then 227 match="uidrange 100-100" 228 getmatch="uid 100" 229 fib_rule6_test_match_n_redirect "$match" "$getmatch" "uid redirect to table" 230 fi 231 232 fib_check_iproute_support "sport" "sport" 233 if [ $? -eq 0 ]; then 234 match="sport 666 dport 777" 235 fib_rule6_test_match_n_redirect "$match" "$match" "sport and dport redirect to table" 236 fi 237 238 fib_check_iproute_support "ipproto" "ipproto" 239 if [ $? -eq 0 ]; then 240 match="ipproto tcp" 241 fib_rule6_test_match_n_redirect "$match" "$match" "ipproto match" 242 fi 243 244 fib_check_iproute_support "ipproto" "ipproto" 245 if [ $? -eq 0 ]; then 246 match="ipproto ipv6-icmp" 247 fib_rule6_test_match_n_redirect "$match" "$match" "ipproto ipv6-icmp match" 248 fi 249} 250 251# Verify that the IPV6_TCLASS option of UDPv6 and TCPv6 sockets is properly 252# taken into account when connecting the socket and when sending packets. 253fib_rule6_connect_test() 254{ 255 local dsfield 256 257 if ! check_nettest; then 258 echo "SKIP: Could not run test without nettest tool" 259 return 260 fi 261 262 setup_peer 263 $IP -6 rule add dsfield 0x04 table $RTABLE_PEER 264 265 # Combine the base DS Field value (0x04) with all possible ECN values 266 # (Not-ECT: 0, ECT(1): 1, ECT(0): 2, CE: 3). 267 # The ECN bits shouldn't influence the result of the test. 268 for dsfield in 0x04 0x05 0x06 0x07; do 269 nettest -q -6 -B -t 5 -N $testns -O $peerns -U -D \ 270 -Q "${dsfield}" -l 2001:db8::1:11 -r 2001:db8::1:11 271 log_test $? 0 "rule6 dsfield udp connect (dsfield ${dsfield})" 272 273 nettest -q -6 -B -t 5 -N $testns -O $peerns -Q "${dsfield}" \ 274 -l 2001:db8::1:11 -r 2001:db8::1:11 275 log_test $? 0 "rule6 dsfield tcp connect (dsfield ${dsfield})" 276 done 277 278 $IP -6 rule del dsfield 0x04 table $RTABLE_PEER 279 cleanup_peer 280} 281 282fib_rule4_del() 283{ 284 $IP rule del $1 285 log_test $? 0 "del $1" 286} 287 288fib_rule4_del_by_pref() 289{ 290 pref=$($IP rule show $1 table $RTABLE | cut -d ":" -f 1) 291 $IP rule del pref $pref 292} 293 294fib_rule4_test_match_n_redirect() 295{ 296 local match="$1" 297 local getmatch="$2" 298 local description="$3" 299 300 $IP rule add $match table $RTABLE 301 $IP route get $GW_IP4 $getmatch | grep -q "table $RTABLE" 302 log_test $? 0 "rule4 check: $description" 303 304 fib_rule4_del_by_pref "$match" 305 log_test $? 0 "rule4 del by pref: $description" 306} 307 308fib_rule4_test_reject() 309{ 310 local match="$1" 311 local rc 312 313 $IP rule add $match table $RTABLE 2>/dev/null 314 rc=$? 315 log_test $rc 2 "rule4 check: $match" 316 317 if [ $rc -eq 0 ]; then 318 $IP rule del $match table $RTABLE 319 fi 320} 321 322fib_rule4_test() 323{ 324 local getmatch 325 local match 326 local cnt 327 328 # setup the fib rule redirect route 329 $IP route add table $RTABLE default via $GW_IP4 dev $DEV onlink 330 331 match="oif $DEV" 332 fib_rule4_test_match_n_redirect "$match" "$match" "oif redirect to table" 333 334 # need enable forwarding and disable rp_filter temporarily as all the 335 # addresses are in the same subnet and egress device == ingress device. 336 ip netns exec $testns sysctl -qw net.ipv4.ip_forward=1 337 ip netns exec $testns sysctl -qw net.ipv4.conf.$DEV.rp_filter=0 338 match="from $SRC_IP iif $DEV" 339 fib_rule4_test_match_n_redirect "$match" "$match" "iif redirect to table" 340 ip netns exec $testns sysctl -qw net.ipv4.ip_forward=0 341 342 # Reject dsfield (tos) options which have ECN bits set 343 for cnt in $(seq 1 3); do 344 match="dsfield $cnt" 345 fib_rule4_test_reject "$match" 346 done 347 348 # Don't take ECN bits into account when matching on dsfield 349 match="tos 0x10" 350 for cnt in "0x10" "0x11" "0x12" "0x13"; do 351 # Using option 'tos' instead of 'dsfield' as old iproute2 352 # versions don't support 'dsfield' in ip rule show. 353 getmatch="tos $cnt" 354 fib_rule4_test_match_n_redirect "$match" "$getmatch" \ 355 "$getmatch redirect to table" 356 done 357 358 match="fwmark 0x64" 359 getmatch="mark 0x64" 360 fib_rule4_test_match_n_redirect "$match" "$getmatch" "fwmark redirect to table" 361 362 fib_check_iproute_support "uidrange" "uid" 363 if [ $? -eq 0 ]; then 364 match="uidrange 100-100" 365 getmatch="uid 100" 366 fib_rule4_test_match_n_redirect "$match" "$getmatch" "uid redirect to table" 367 fi 368 369 fib_check_iproute_support "sport" "sport" 370 if [ $? -eq 0 ]; then 371 match="sport 666 dport 777" 372 fib_rule4_test_match_n_redirect "$match" "$match" "sport and dport redirect to table" 373 fi 374 375 fib_check_iproute_support "ipproto" "ipproto" 376 if [ $? -eq 0 ]; then 377 match="ipproto tcp" 378 fib_rule4_test_match_n_redirect "$match" "$match" "ipproto tcp match" 379 fi 380 381 fib_check_iproute_support "ipproto" "ipproto" 382 if [ $? -eq 0 ]; then 383 match="ipproto icmp" 384 fib_rule4_test_match_n_redirect "$match" "$match" "ipproto icmp match" 385 fi 386} 387 388# Verify that the IP_TOS option of UDPv4 and TCPv4 sockets is properly taken 389# into account when connecting the socket and when sending packets. 390fib_rule4_connect_test() 391{ 392 local dsfield 393 394 if ! check_nettest; then 395 echo "SKIP: Could not run test without nettest tool" 396 return 397 fi 398 399 setup_peer 400 $IP -4 rule add dsfield 0x04 table $RTABLE_PEER 401 402 # Combine the base DS Field value (0x04) with all possible ECN values 403 # (Not-ECT: 0, ECT(1): 1, ECT(0): 2, CE: 3). 404 # The ECN bits shouldn't influence the result of the test. 405 for dsfield in 0x04 0x05 0x06 0x07; do 406 nettest -q -B -t 5 -N $testns -O $peerns -D -U -Q "${dsfield}" \ 407 -l 198.51.100.11 -r 198.51.100.11 408 log_test $? 0 "rule4 dsfield udp connect (dsfield ${dsfield})" 409 410 nettest -q -B -t 5 -N $testns -O $peerns -Q "${dsfield}" \ 411 -l 198.51.100.11 -r 198.51.100.11 412 log_test $? 0 "rule4 dsfield tcp connect (dsfield ${dsfield})" 413 done 414 415 $IP -4 rule del dsfield 0x04 table $RTABLE_PEER 416 cleanup_peer 417} 418 419run_fibrule_tests() 420{ 421 log_section "IPv4 fib rule" 422 fib_rule4_test 423 log_section "IPv6 fib rule" 424 fib_rule6_test 425} 426################################################################################ 427# usage 428 429usage() 430{ 431 cat <<EOF 432usage: ${0##*/} OPTS 433 434 -t <test> Test(s) to run (default: all) 435 (options: $TESTS) 436EOF 437} 438 439################################################################################ 440# main 441 442while getopts ":t:h" opt; do 443 case $opt in 444 t) TESTS=$OPTARG;; 445 h) usage; exit 0;; 446 *) usage; exit 1;; 447 esac 448done 449 450if [ "$(id -u)" -ne 0 ];then 451 echo "SKIP: Need root privileges" 452 exit $ksft_skip 453fi 454 455if [ ! -x "$(command -v ip)" ]; then 456 echo "SKIP: Could not run test without ip tool" 457 exit $ksft_skip 458fi 459 460# start clean 461cleanup &> /dev/null 462setup 463for t in $TESTS 464do 465 case $t in 466 fib_rule6_test|fib_rule6) fib_rule6_test;; 467 fib_rule4_test|fib_rule4) fib_rule4_test;; 468 fib_rule6_connect_test|fib_rule6_connect) fib_rule6_connect_test;; 469 fib_rule4_connect_test|fib_rule4_connect) fib_rule4_connect_test;; 470 471 help) echo "Test names: $TESTS"; exit 0;; 472 473 esac 474done 475cleanup 476 477if [ "$TESTS" != "none" ]; then 478 printf "\nTests passed: %3d\n" ${nsuccess} 479 printf "Tests failed: %3d\n" ${nfail} 480fi 481 482exit $ret 483