1#!/bin/bash 2# SPDX-License-Identifier: GPL-2.0 3# 4# Copyright (c) 2019 David Ahern <dsahern@gmail.com>. All rights reserved. 5# Copyright (c) 2020 Michael Jeanson <mjeanson@efficios.com>. All rights reserved. 6# 7# Requires CONFIG_NET_VRF, CONFIG_VETH, CONFIG_BRIDGE and CONFIG_NET_NS. 8# 9# 10# Symmetric routing topology 11# 12# blue red 13# +----+ .253 +----+ .253 +----+ 14# | h1 |-------------------| r1 |-------------------| h2 | 15# +----+ .1 +----+ .2 +----+ 16# 172.16.1/24 172.16.2/24 17# 2001:db8:16:1/64 2001:db8:16:2/64 18# 19# 20# Route from h1 to h2 and back goes through r1, incoming vrf blue has a route 21# to the outgoing vrf red for the n2 network and red has a route back to n1. 22# The red VRF interface has a MTU of 1400. 23# 24# The first test sends a ping with a ttl of 1 from h1 to h2 and parses the 25# output of the command to check that a ttl expired error is received. 26# 27# The second test runs traceroute from h1 to h2 and parses the output to check 28# for a hop on r1. 29# 30# The third test sends a ping with a packet size of 1450 from h1 to h2 and 31# parses the output of the command to check that a fragmentation error is 32# received. 33# 34# 35# Asymmetric routing topology 36# 37# This topology represents a customer setup where the issue with icmp errors 38# and VRF route leaking was initialy reported. The MTU test isn't done here 39# because of the lack of a return route in the red VRF. 40# 41# blue red 42# .253 +----+ .253 43# +----| r1 |----+ 44# | +----+ | 45# +----+ | | +----+ 46# | h1 |--------------+ +--------------| h2 | 47# +----+ .1 | | .2 +----+ 48# 172.16.1/24 | +----+ | 172.16.2/24 49# 2001:db8:16:1/64 +----| r2 |----+ 2001:db8:16:2/64 50# .254 +----+ .254 51# 52# 53# Route from h1 to h2 goes through r1, incoming vrf blue has a route to the 54# outgoing vrf red for the n2 network but red doesn't have a route back to n1. 55# Route from h2 to h1 goes through r2. 56# 57# The objective is to check that the incoming vrf routing table is selected 58# to send an ICMP error back to the source when the ttl of a packet reaches 1 59# while it is forwarded between different vrfs. 60 61source lib.sh 62PATH=$PWD:$PWD/tools/testing/selftests/net:$PATH 63VERBOSE=0 64PAUSE_ON_FAIL=no 65DEFAULT_TTYPE=sym 66 67H1_N1=172.16.1.0/24 68H1_N1_6=2001:db8:16:1::/64 69 70H1_N1_IP=172.16.1.1 71R1_N1_IP=172.16.1.253 72R2_N1_IP=172.16.1.254 73 74H1_N1_IP6=2001:db8:16:1::1 75R1_N1_IP6=2001:db8:16:1::253 76R2_N1_IP6=2001:db8:16:1::254 77 78H2_N2=172.16.2.0/24 79H2_N2_6=2001:db8:16:2::/64 80 81H2_N2_IP=172.16.2.2 82R1_N2_IP=172.16.2.253 83R2_N2_IP=172.16.2.254 84 85H2_N2_IP6=2001:db8:16:2::2 86R1_N2_IP6=2001:db8:16:2::253 87R2_N2_IP6=2001:db8:16:2::254 88 89################################################################################ 90# helpers 91 92log_section() 93{ 94 echo 95 echo "###########################################################################" 96 echo "$*" 97 echo "###########################################################################" 98 echo 99} 100 101log_test() 102{ 103 local rc=$1 104 local expected=$2 105 local msg="$3" 106 107 if [ "${rc}" -eq "${expected}" ]; then 108 printf "TEST: %-60s [ OK ]\n" "${msg}" 109 nsuccess=$((nsuccess+1)) 110 else 111 ret=1 112 nfail=$((nfail+1)) 113 printf "TEST: %-60s [FAIL]\n" "${msg}" 114 if [ "${PAUSE_ON_FAIL}" = "yes" ]; then 115 echo 116 echo "hit enter to continue, 'q' to quit" 117 read -r a 118 [ "$a" = "q" ] && exit 1 119 fi 120 fi 121} 122 123run_cmd() 124{ 125 local cmd="$*" 126 local out 127 local rc 128 129 if [ "$VERBOSE" = "1" ]; then 130 echo "COMMAND: $cmd" 131 fi 132 133 # shellcheck disable=SC2086 134 out=$(eval $cmd 2>&1) 135 rc=$? 136 if [ "$VERBOSE" = "1" ] && [ -n "$out" ]; then 137 echo "$out" 138 fi 139 140 [ "$VERBOSE" = "1" ] && echo 141 142 return $rc 143} 144 145run_cmd_grep() 146{ 147 local grep_pattern="$1" 148 shift 149 local cmd="$*" 150 local out 151 local rc 152 153 if [ "$VERBOSE" = "1" ]; then 154 echo "COMMAND: $cmd" 155 fi 156 157 # shellcheck disable=SC2086 158 out=$(eval $cmd 2>&1) 159 if [ "$VERBOSE" = "1" ] && [ -n "$out" ]; then 160 echo "$out" 161 fi 162 163 echo "$out" | grep -q "$grep_pattern" 164 rc=$? 165 166 [ "$VERBOSE" = "1" ] && echo 167 168 return $rc 169} 170 171################################################################################ 172# setup and teardown 173 174cleanup() 175{ 176 cleanup_ns $h1 $h2 $r1 $r2 177} 178 179setup_vrf() 180{ 181 local ns=$1 182 183 ip -netns "${ns}" rule del pref 0 184 ip -netns "${ns}" rule add pref 32765 from all lookup local 185 ip -netns "${ns}" -6 rule del pref 0 186 ip -netns "${ns}" -6 rule add pref 32765 from all lookup local 187} 188 189create_vrf() 190{ 191 local ns=$1 192 local vrf=$2 193 local table=$3 194 195 ip -netns "${ns}" link add "${vrf}" type vrf table "${table}" 196 ip -netns "${ns}" link set "${vrf}" up 197 ip -netns "${ns}" route add vrf "${vrf}" unreachable default metric 8192 198 ip -netns "${ns}" -6 route add vrf "${vrf}" unreachable default metric 8192 199 200 ip -netns "${ns}" addr add 127.0.0.1/8 dev "${vrf}" 201 ip -netns "${ns}" -6 addr add ::1 dev "${vrf}" nodad 202} 203 204setup_sym() 205{ 206 local ns 207 208 # make sure we are starting with a clean slate 209 cleanup 210 211 # 212 # create nodes as namespaces 213 setup_ns h1 h2 r1 214 for ns in $h1 $h2 $r1; do 215 if echo $ns | grep -q h[12]-; then 216 ip netns exec $ns sysctl -q -w net.ipv6.conf.all.forwarding=0 217 ip netns exec $ns sysctl -q -w net.ipv6.conf.all.keep_addr_on_down=1 218 else 219 ip netns exec $ns sysctl -q -w net.ipv4.ip_forward=1 220 ip netns exec $ns sysctl -q -w net.ipv6.conf.all.forwarding=1 221 fi 222 done 223 224 # 225 # create interconnects 226 # 227 ip -netns $h1 link add eth0 type veth peer name r1h1 228 ip -netns $h1 link set r1h1 netns $r1 name eth0 up 229 230 ip -netns $h2 link add eth0 type veth peer name r1h2 231 ip -netns $h2 link set r1h2 netns $r1 name eth1 up 232 233 # 234 # h1 235 # 236 ip -netns $h1 addr add dev eth0 ${H1_N1_IP}/24 237 ip -netns $h1 -6 addr add dev eth0 ${H1_N1_IP6}/64 nodad 238 ip -netns $h1 link set eth0 up 239 240 # h1 to h2 via r1 241 ip -netns $h1 route add ${H2_N2} via ${R1_N1_IP} dev eth0 242 ip -netns $h1 -6 route add ${H2_N2_6} via "${R1_N1_IP6}" dev eth0 243 244 # 245 # h2 246 # 247 ip -netns $h2 addr add dev eth0 ${H2_N2_IP}/24 248 ip -netns $h2 -6 addr add dev eth0 ${H2_N2_IP6}/64 nodad 249 ip -netns $h2 link set eth0 up 250 251 # h2 to h1 via r1 252 ip -netns $h2 route add default via ${R1_N2_IP} dev eth0 253 ip -netns $h2 -6 route add default via ${R1_N2_IP6} dev eth0 254 255 # 256 # r1 257 # 258 setup_vrf $r1 259 create_vrf $r1 blue 1101 260 create_vrf $r1 red 1102 261 ip -netns $r1 link set mtu 1400 dev eth1 262 ip -netns $r1 link set eth0 vrf blue up 263 ip -netns $r1 link set eth1 vrf red up 264 ip -netns $r1 addr add dev eth0 ${R1_N1_IP}/24 265 ip -netns $r1 -6 addr add dev eth0 ${R1_N1_IP6}/64 nodad 266 ip -netns $r1 addr add dev eth1 ${R1_N2_IP}/24 267 ip -netns $r1 -6 addr add dev eth1 ${R1_N2_IP6}/64 nodad 268 269 # Route leak from blue to red 270 ip -netns $r1 route add vrf blue ${H2_N2} dev red 271 ip -netns $r1 -6 route add vrf blue ${H2_N2_6} dev red 272 273 # Route leak from red to blue 274 ip -netns $r1 route add vrf red ${H1_N1} dev blue 275 ip -netns $r1 -6 route add vrf red ${H1_N1_6} dev blue 276 277 278 # Wait for ip config to settle 279 sleep 2 280} 281 282setup_asym() 283{ 284 local ns 285 286 # make sure we are starting with a clean slate 287 cleanup 288 289 # 290 # create nodes as namespaces 291 setup_ns h1 h2 r1 r2 292 for ns in $h1 $h2 $r1 $r2; do 293 if echo $ns | grep -q h[12]-; then 294 ip netns exec $ns sysctl -q -w net.ipv6.conf.all.forwarding=0 295 ip netns exec $ns sysctl -q -w net.ipv6.conf.all.keep_addr_on_down=1 296 else 297 ip netns exec $ns sysctl -q -w net.ipv4.ip_forward=1 298 ip netns exec $ns sysctl -q -w net.ipv6.conf.all.forwarding=1 299 fi 300 done 301 302 # 303 # create interconnects 304 # 305 ip -netns $h1 link add eth0 type veth peer name r1h1 306 ip -netns $h1 link set r1h1 netns $r1 name eth0 up 307 308 ip -netns $h1 link add eth1 type veth peer name r2h1 309 ip -netns $h1 link set r2h1 netns $r2 name eth0 up 310 311 ip -netns $h2 link add eth0 type veth peer name r1h2 312 ip -netns $h2 link set r1h2 netns $r1 name eth1 up 313 314 ip -netns $h2 link add eth1 type veth peer name r2h2 315 ip -netns $h2 link set r2h2 netns $r2 name eth1 up 316 317 # 318 # h1 319 # 320 ip -netns $h1 link add br0 type bridge 321 ip -netns $h1 link set br0 up 322 ip -netns $h1 addr add dev br0 ${H1_N1_IP}/24 323 ip -netns $h1 -6 addr add dev br0 ${H1_N1_IP6}/64 nodad 324 ip -netns $h1 link set eth0 master br0 up 325 ip -netns $h1 link set eth1 master br0 up 326 327 # h1 to h2 via r1 328 ip -netns $h1 route add ${H2_N2} via ${R1_N1_IP} dev br0 329 ip -netns $h1 -6 route add ${H2_N2_6} via "${R1_N1_IP6}" dev br0 330 331 # 332 # h2 333 # 334 ip -netns $h2 link add br0 type bridge 335 ip -netns $h2 link set br0 up 336 ip -netns $h2 addr add dev br0 ${H2_N2_IP}/24 337 ip -netns $h2 -6 addr add dev br0 ${H2_N2_IP6}/64 nodad 338 ip -netns $h2 link set eth0 master br0 up 339 ip -netns $h2 link set eth1 master br0 up 340 341 # h2 to h1 via r2 342 ip -netns $h2 route add default via ${R2_N2_IP} dev br0 343 ip -netns $h2 -6 route add default via ${R2_N2_IP6} dev br0 344 345 # 346 # r1 347 # 348 setup_vrf $r1 349 create_vrf $r1 blue 1101 350 create_vrf $r1 red 1102 351 ip -netns $r1 link set mtu 1400 dev eth1 352 ip -netns $r1 link set eth0 vrf blue up 353 ip -netns $r1 link set eth1 vrf red up 354 ip -netns $r1 addr add dev eth0 ${R1_N1_IP}/24 355 ip -netns $r1 -6 addr add dev eth0 ${R1_N1_IP6}/64 nodad 356 ip -netns $r1 addr add dev eth1 ${R1_N2_IP}/24 357 ip -netns $r1 -6 addr add dev eth1 ${R1_N2_IP6}/64 nodad 358 359 # Route leak from blue to red 360 ip -netns $r1 route add vrf blue ${H2_N2} dev red 361 ip -netns $r1 -6 route add vrf blue ${H2_N2_6} dev red 362 363 # No route leak from red to blue 364 365 # 366 # r2 367 # 368 ip -netns $r2 addr add dev eth0 ${R2_N1_IP}/24 369 ip -netns $r2 -6 addr add dev eth0 ${R2_N1_IP6}/64 nodad 370 ip -netns $r2 addr add dev eth1 ${R2_N2_IP}/24 371 ip -netns $r2 -6 addr add dev eth1 ${R2_N2_IP6}/64 nodad 372 373 # Wait for ip config to settle 374 sleep 2 375} 376 377check_connectivity() 378{ 379 ip netns exec $h1 ping -c1 -w1 ${H2_N2_IP} >/dev/null 2>&1 380 log_test $? 0 "Basic IPv4 connectivity" 381 return $? 382} 383 384check_connectivity6() 385{ 386 ip netns exec $h1 "${ping6}" -c1 -w1 ${H2_N2_IP6} >/dev/null 2>&1 387 log_test $? 0 "Basic IPv6 connectivity" 388 return $? 389} 390 391check_traceroute() 392{ 393 if [ ! -x "$(command -v traceroute)" ]; then 394 echo "SKIP: Could not run IPV4 test without traceroute" 395 return 1 396 fi 397} 398 399check_traceroute6() 400{ 401 if [ ! -x "$(command -v traceroute6)" ]; then 402 echo "SKIP: Could not run IPV6 test without traceroute6" 403 return 1 404 fi 405} 406 407ipv4_traceroute() 408{ 409 local ttype="$1" 410 411 [ "x$ttype" = "x" ] && ttype="$DEFAULT_TTYPE" 412 413 log_section "IPv4 ($ttype route): VRF ICMP error route lookup traceroute" 414 415 check_traceroute || return 416 417 setup_"$ttype" 418 419 check_connectivity || return 420 421 run_cmd_grep "${R1_N1_IP}" ip netns exec $h1 traceroute ${H2_N2_IP} 422 log_test $? 0 "Traceroute reports a hop on r1" 423} 424 425ipv4_traceroute_asym() 426{ 427 ipv4_traceroute asym 428} 429 430ipv6_traceroute() 431{ 432 local ttype="$1" 433 434 [ "x$ttype" = "x" ] && ttype="$DEFAULT_TTYPE" 435 436 log_section "IPv6 ($ttype route): VRF ICMP error route lookup traceroute" 437 438 check_traceroute6 || return 439 440 setup_"$ttype" 441 442 check_connectivity6 || return 443 444 run_cmd_grep "${R1_N1_IP6}" ip netns exec $h1 traceroute6 ${H2_N2_IP6} 445 log_test $? 0 "Traceroute6 reports a hop on r1" 446} 447 448ipv6_traceroute_asym() 449{ 450 ipv6_traceroute asym 451} 452 453ipv4_ping_ttl() 454{ 455 local ttype="$1" 456 457 [ "x$ttype" = "x" ] && ttype="$DEFAULT_TTYPE" 458 459 log_section "IPv4 ($ttype route): VRF ICMP ttl error route lookup ping" 460 461 setup_"$ttype" 462 463 check_connectivity || return 464 465 run_cmd_grep "Time to live exceeded" ip netns exec $h1 ping -t1 -c1 -W2 ${H2_N2_IP} 466 log_test $? 0 "Ping received ICMP ttl exceeded" 467} 468 469ipv4_ping_ttl_asym() 470{ 471 ipv4_ping_ttl asym 472} 473 474ipv4_ping_frag() 475{ 476 local ttype="$1" 477 478 [ "x$ttype" = "x" ] && ttype="$DEFAULT_TTYPE" 479 480 log_section "IPv4 ($ttype route): VRF ICMP fragmentation error route lookup ping" 481 482 setup_"$ttype" 483 484 check_connectivity || return 485 486 run_cmd_grep "Frag needed" ip netns exec $h1 ping -s 1450 -Mdo -c1 -W2 ${H2_N2_IP} 487 log_test $? 0 "Ping received ICMP Frag needed" 488} 489 490ipv4_ping_frag_asym() 491{ 492 ipv4_ping_frag asym 493} 494 495ipv6_ping_ttl() 496{ 497 local ttype="$1" 498 499 [ "x$ttype" = "x" ] && ttype="$DEFAULT_TTYPE" 500 501 log_section "IPv6 ($ttype route): VRF ICMP ttl error route lookup ping" 502 503 setup_"$ttype" 504 505 check_connectivity6 || return 506 507 run_cmd_grep "Time exceeded: Hop limit" ip netns exec $h1 "${ping6}" -t1 -c1 -W2 ${H2_N2_IP6} 508 log_test $? 0 "Ping received ICMP Hop limit" 509} 510 511ipv6_ping_ttl_asym() 512{ 513 ipv6_ping_ttl asym 514} 515 516ipv6_ping_frag() 517{ 518 local ttype="$1" 519 520 [ "x$ttype" = "x" ] && ttype="$DEFAULT_TTYPE" 521 522 log_section "IPv6 ($ttype route): VRF ICMP fragmentation error route lookup ping" 523 524 setup_"$ttype" 525 526 check_connectivity6 || return 527 528 run_cmd_grep "Packet too big" ip netns exec $h1 "${ping6}" -s 1450 -Mdo -c1 -W2 ${H2_N2_IP6} 529 log_test $? 0 "Ping received ICMP Packet too big" 530} 531 532ipv6_ping_frag_asym() 533{ 534 ipv6_ping_frag asym 535} 536 537ipv4_ping_local() 538{ 539 log_section "IPv4 (sym route): VRF ICMP local error route lookup ping" 540 541 setup_sym 542 543 check_connectivity || return 544 545 run_cmd ip netns exec $r1 ip vrf exec blue ping -c1 -w1 ${H2_N2_IP} 546 log_test $? 0 "VRF ICMP local IPv4" 547} 548 549ipv4_tcp_local() 550{ 551 log_section "IPv4 (sym route): VRF tcp local connection" 552 553 setup_sym 554 555 check_connectivity || return 556 557 run_cmd nettest -s -O "$h2" -l ${H2_N2_IP} -I eth0 -3 eth0 & 558 sleep 1 559 run_cmd nettest -N "$r1" -d blue -r ${H2_N2_IP} 560 log_test $? 0 "VRF tcp local connection IPv4" 561} 562 563ipv4_udp_local() 564{ 565 log_section "IPv4 (sym route): VRF udp local connection" 566 567 setup_sym 568 569 check_connectivity || return 570 571 run_cmd nettest -s -D -O "$h2" -l ${H2_N2_IP} -I eth0 -3 eth0 & 572 sleep 1 573 run_cmd nettest -D -N "$r1" -d blue -r ${H2_N2_IP} 574 log_test $? 0 "VRF udp local connection IPv4" 575} 576 577ipv6_ping_local() 578{ 579 log_section "IPv6 (sym route): VRF ICMP local error route lookup ping" 580 581 setup_sym 582 583 check_connectivity6 || return 584 585 run_cmd ip netns exec $r1 ip vrf exec blue ${ping6} -c1 -w1 ${H2_N2_IP6} 586 log_test $? 0 "VRF ICMP local IPv6" 587} 588 589ipv6_tcp_local() 590{ 591 log_section "IPv6 (sym route): VRF tcp local connection" 592 593 setup_sym 594 595 check_connectivity6 || return 596 597 run_cmd nettest -s -6 -O "$h2" -l ${H2_N2_IP6} -I eth0 -3 eth0 & 598 sleep 1 599 run_cmd nettest -6 -N "$r1" -d blue -r ${H2_N2_IP6} 600 log_test $? 0 "VRF tcp local connection IPv6" 601} 602 603ipv6_udp_local() 604{ 605 log_section "IPv6 (sym route): VRF udp local connection" 606 607 setup_sym 608 609 check_connectivity6 || return 610 611 run_cmd nettest -s -6 -D -O "$h2" -l ${H2_N2_IP6} -I eth0 -3 eth0 & 612 sleep 1 613 run_cmd nettest -6 -D -N "$r1" -d blue -r ${H2_N2_IP6} 614 log_test $? 0 "VRF udp local connection IPv6" 615} 616 617################################################################################ 618# usage 619 620usage() 621{ 622 cat <<EOF 623usage: ${0##*/} OPTS 624 625 -4 Run IPv4 tests only 626 -6 Run IPv6 tests only 627 -t TEST Run only TEST 628 -p Pause on fail 629 -v verbose mode (show commands and output) 630EOF 631} 632 633################################################################################ 634# main 635 636# Some systems don't have a ping6 binary anymore 637command -v ping6 > /dev/null 2>&1 && ping6=$(command -v ping6) || ping6=$(command -v ping) 638 639TESTS_IPV4="ipv4_ping_ttl ipv4_traceroute ipv4_ping_frag ipv4_ping_local ipv4_tcp_local 640ipv4_udp_local ipv4_ping_ttl_asym ipv4_traceroute_asym" 641TESTS_IPV6="ipv6_ping_ttl ipv6_traceroute ipv6_ping_local ipv6_tcp_local ipv6_udp_local 642ipv6_ping_ttl_asym ipv6_traceroute_asym" 643 644ret=0 645nsuccess=0 646nfail=0 647 648while getopts :46t:pvh o 649do 650 case $o in 651 4) TESTS=ipv4;; 652 6) TESTS=ipv6;; 653 t) TESTS=$OPTARG;; 654 p) PAUSE_ON_FAIL=yes;; 655 v) VERBOSE=1;; 656 h) usage; exit 0;; 657 *) usage; exit 1;; 658 esac 659done 660 661# 662# show user test config 663# 664if [ -z "$TESTS" ]; then 665 TESTS="$TESTS_IPV4 $TESTS_IPV6" 666elif [ "$TESTS" = "ipv4" ]; then 667 TESTS="$TESTS_IPV4" 668elif [ "$TESTS" = "ipv6" ]; then 669 TESTS="$TESTS_IPV6" 670fi 671 672for t in $TESTS 673do 674 case $t in 675 ipv4_ping_ttl|ping) ipv4_ping_ttl;;& 676 ipv4_ping_ttl_asym|ping) ipv4_ping_ttl_asym;;& 677 ipv4_traceroute|traceroute) ipv4_traceroute;;& 678 ipv4_traceroute_asym|traceroute) ipv4_traceroute_asym;;& 679 ipv4_ping_frag|ping) ipv4_ping_frag;;& 680 ipv4_ping_local|ping) ipv4_ping_local;;& 681 ipv4_tcp_local) ipv4_tcp_local;;& 682 ipv4_udp_local) ipv4_udp_local;;& 683 684 ipv6_ping_ttl|ping) ipv6_ping_ttl;;& 685 ipv6_ping_ttl_asym|ping) ipv6_ping_ttl_asym;;& 686 ipv6_traceroute|traceroute) ipv6_traceroute;;& 687 ipv6_traceroute_asym|traceroute) ipv6_traceroute_asym;;& 688 ipv6_ping_frag|ping) ipv6_ping_frag;;& 689 ipv6_ping_local|ping) ipv6_ping_local;;& 690 ipv6_tcp_local) ipv6_tcp_local;;& 691 ipv6_udp_local) ipv6_udp_local;;& 692 693 # setup namespaces and config, but do not run any tests 694 setup_sym|setup) setup_sym; exit 0;; 695 setup_asym) setup_asym; exit 0;; 696 697 help) echo "Test names: $TESTS"; exit 0;; 698 esac 699done 700 701cleanup 702 703printf "\nTests passed: %3d\n" ${nsuccess} 704printf "Tests failed: %3d\n" ${nfail} 705 706exit $ret 707