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 62VERBOSE=0 63PAUSE_ON_FAIL=no 64DEFAULT_TTYPE=sym 65 66H1_N1=172.16.1.0/24 67H1_N1_6=2001:db8:16:1::/64 68 69H1_N1_IP=172.16.1.1 70R1_N1_IP=172.16.1.253 71R2_N1_IP=172.16.1.254 72 73H1_N1_IP6=2001:db8:16:1::1 74R1_N1_IP6=2001:db8:16:1::253 75R2_N1_IP6=2001:db8:16:1::254 76 77H2_N2=172.16.2.0/24 78H2_N2_6=2001:db8:16:2::/64 79 80H2_N2_IP=172.16.2.2 81R1_N2_IP=172.16.2.253 82R2_N2_IP=172.16.2.254 83 84H2_N2_IP6=2001:db8:16:2::2 85R1_N2_IP6=2001:db8:16:2::253 86R2_N2_IP6=2001:db8:16:2::254 87 88################################################################################ 89# helpers 90 91log_section() 92{ 93 echo 94 echo "###########################################################################" 95 echo "$*" 96 echo "###########################################################################" 97 echo 98} 99 100log_test() 101{ 102 local rc=$1 103 local expected=$2 104 local msg="$3" 105 106 if [ "${rc}" -eq "${expected}" ]; then 107 printf "TEST: %-60s [ OK ]\n" "${msg}" 108 nsuccess=$((nsuccess+1)) 109 else 110 ret=1 111 nfail=$((nfail+1)) 112 printf "TEST: %-60s [FAIL]\n" "${msg}" 113 if [ "${PAUSE_ON_FAIL}" = "yes" ]; then 114 echo 115 echo "hit enter to continue, 'q' to quit" 116 read -r a 117 [ "$a" = "q" ] && exit 1 118 fi 119 fi 120} 121 122run_cmd() 123{ 124 local cmd="$*" 125 local out 126 local rc 127 128 if [ "$VERBOSE" = "1" ]; then 129 echo "COMMAND: $cmd" 130 fi 131 132 # shellcheck disable=SC2086 133 out=$(eval $cmd 2>&1) 134 rc=$? 135 if [ "$VERBOSE" = "1" ] && [ -n "$out" ]; then 136 echo "$out" 137 fi 138 139 [ "$VERBOSE" = "1" ] && echo 140 141 return $rc 142} 143 144run_cmd_grep() 145{ 146 local grep_pattern="$1" 147 shift 148 local cmd="$*" 149 local out 150 local rc 151 152 if [ "$VERBOSE" = "1" ]; then 153 echo "COMMAND: $cmd" 154 fi 155 156 # shellcheck disable=SC2086 157 out=$(eval $cmd 2>&1) 158 if [ "$VERBOSE" = "1" ] && [ -n "$out" ]; then 159 echo "$out" 160 fi 161 162 echo "$out" | grep -q "$grep_pattern" 163 rc=$? 164 165 [ "$VERBOSE" = "1" ] && echo 166 167 return $rc 168} 169 170################################################################################ 171# setup and teardown 172 173cleanup() 174{ 175 cleanup_ns $h1 $h2 $r1 $r2 176} 177 178setup_vrf() 179{ 180 local ns=$1 181 182 ip -netns "${ns}" rule del pref 0 183 ip -netns "${ns}" rule add pref 32765 from all lookup local 184 ip -netns "${ns}" -6 rule del pref 0 185 ip -netns "${ns}" -6 rule add pref 32765 from all lookup local 186} 187 188create_vrf() 189{ 190 local ns=$1 191 local vrf=$2 192 local table=$3 193 194 ip -netns "${ns}" link add "${vrf}" type vrf table "${table}" 195 ip -netns "${ns}" link set "${vrf}" up 196 ip -netns "${ns}" route add vrf "${vrf}" unreachable default metric 8192 197 ip -netns "${ns}" -6 route add vrf "${vrf}" unreachable default metric 8192 198 199 ip -netns "${ns}" addr add 127.0.0.1/8 dev "${vrf}" 200 ip -netns "${ns}" -6 addr add ::1 dev "${vrf}" nodad 201} 202 203setup_sym() 204{ 205 local ns 206 207 # make sure we are starting with a clean slate 208 cleanup 209 210 # 211 # create nodes as namespaces 212 setup_ns h1 h2 r1 213 for ns in $h1 $h2 $r1; do 214 if echo $ns | grep -q h[12]-; then 215 ip netns exec $ns sysctl -q -w net.ipv6.conf.all.forwarding=0 216 ip netns exec $ns sysctl -q -w net.ipv6.conf.all.keep_addr_on_down=1 217 else 218 ip netns exec $ns sysctl -q -w net.ipv4.ip_forward=1 219 ip netns exec $ns sysctl -q -w net.ipv6.conf.all.forwarding=1 220 fi 221 done 222 223 # 224 # create interconnects 225 # 226 ip -netns $h1 link add eth0 type veth peer name r1h1 227 ip -netns $h1 link set r1h1 netns $r1 name eth0 up 228 229 ip -netns $h2 link add eth0 type veth peer name r1h2 230 ip -netns $h2 link set r1h2 netns $r1 name eth1 up 231 232 # 233 # h1 234 # 235 ip -netns $h1 addr add dev eth0 ${H1_N1_IP}/24 236 ip -netns $h1 -6 addr add dev eth0 ${H1_N1_IP6}/64 nodad 237 ip -netns $h1 link set eth0 up 238 239 # h1 to h2 via r1 240 ip -netns $h1 route add ${H2_N2} via ${R1_N1_IP} dev eth0 241 ip -netns $h1 -6 route add ${H2_N2_6} via "${R1_N1_IP6}" dev eth0 242 243 # 244 # h2 245 # 246 ip -netns $h2 addr add dev eth0 ${H2_N2_IP}/24 247 ip -netns $h2 -6 addr add dev eth0 ${H2_N2_IP6}/64 nodad 248 ip -netns $h2 link set eth0 up 249 250 # h2 to h1 via r1 251 ip -netns $h2 route add default via ${R1_N2_IP} dev eth0 252 ip -netns $h2 -6 route add default via ${R1_N2_IP6} dev eth0 253 254 # 255 # r1 256 # 257 setup_vrf $r1 258 create_vrf $r1 blue 1101 259 create_vrf $r1 red 1102 260 ip -netns $r1 link set mtu 1400 dev eth1 261 ip -netns $r1 link set eth0 vrf blue up 262 ip -netns $r1 link set eth1 vrf red up 263 ip -netns $r1 addr add dev eth0 ${R1_N1_IP}/24 264 ip -netns $r1 -6 addr add dev eth0 ${R1_N1_IP6}/64 nodad 265 ip -netns $r1 addr add dev eth1 ${R1_N2_IP}/24 266 ip -netns $r1 -6 addr add dev eth1 ${R1_N2_IP6}/64 nodad 267 268 # Route leak from blue to red 269 ip -netns $r1 route add vrf blue ${H2_N2} dev red 270 ip -netns $r1 -6 route add vrf blue ${H2_N2_6} dev red 271 272 # Route leak from red to blue 273 ip -netns $r1 route add vrf red ${H1_N1} dev blue 274 ip -netns $r1 -6 route add vrf red ${H1_N1_6} dev blue 275 276 277 # Wait for ip config to settle 278 sleep 2 279} 280 281setup_asym() 282{ 283 local ns 284 285 # make sure we are starting with a clean slate 286 cleanup 287 288 # 289 # create nodes as namespaces 290 setup_ns h1 h2 r1 r2 291 for ns in $h1 $h2 $r1 $r2; do 292 if echo $ns | grep -q h[12]-; then 293 ip netns exec $ns sysctl -q -w net.ipv6.conf.all.forwarding=0 294 ip netns exec $ns sysctl -q -w net.ipv6.conf.all.keep_addr_on_down=1 295 else 296 ip netns exec $ns sysctl -q -w net.ipv4.ip_forward=1 297 ip netns exec $ns sysctl -q -w net.ipv6.conf.all.forwarding=1 298 fi 299 done 300 301 # 302 # create interconnects 303 # 304 ip -netns $h1 link add eth0 type veth peer name r1h1 305 ip -netns $h1 link set r1h1 netns $r1 name eth0 up 306 307 ip -netns $h1 link add eth1 type veth peer name r2h1 308 ip -netns $h1 link set r2h1 netns $r2 name eth0 up 309 310 ip -netns $h2 link add eth0 type veth peer name r1h2 311 ip -netns $h2 link set r1h2 netns $r1 name eth1 up 312 313 ip -netns $h2 link add eth1 type veth peer name r2h2 314 ip -netns $h2 link set r2h2 netns $r2 name eth1 up 315 316 # 317 # h1 318 # 319 ip -netns $h1 link add br0 type bridge 320 ip -netns $h1 link set br0 up 321 ip -netns $h1 addr add dev br0 ${H1_N1_IP}/24 322 ip -netns $h1 -6 addr add dev br0 ${H1_N1_IP6}/64 nodad 323 ip -netns $h1 link set eth0 master br0 up 324 ip -netns $h1 link set eth1 master br0 up 325 326 # h1 to h2 via r1 327 ip -netns $h1 route add ${H2_N2} via ${R1_N1_IP} dev br0 328 ip -netns $h1 -6 route add ${H2_N2_6} via "${R1_N1_IP6}" dev br0 329 330 # 331 # h2 332 # 333 ip -netns $h2 link add br0 type bridge 334 ip -netns $h2 link set br0 up 335 ip -netns $h2 addr add dev br0 ${H2_N2_IP}/24 336 ip -netns $h2 -6 addr add dev br0 ${H2_N2_IP6}/64 nodad 337 ip -netns $h2 link set eth0 master br0 up 338 ip -netns $h2 link set eth1 master br0 up 339 340 # h2 to h1 via r2 341 ip -netns $h2 route add default via ${R2_N2_IP} dev br0 342 ip -netns $h2 -6 route add default via ${R2_N2_IP6} dev br0 343 344 # 345 # r1 346 # 347 setup_vrf $r1 348 create_vrf $r1 blue 1101 349 create_vrf $r1 red 1102 350 ip -netns $r1 link set mtu 1400 dev eth1 351 ip -netns $r1 link set eth0 vrf blue up 352 ip -netns $r1 link set eth1 vrf red up 353 ip -netns $r1 addr add dev eth0 ${R1_N1_IP}/24 354 ip -netns $r1 -6 addr add dev eth0 ${R1_N1_IP6}/64 nodad 355 ip -netns $r1 addr add dev eth1 ${R1_N2_IP}/24 356 ip -netns $r1 -6 addr add dev eth1 ${R1_N2_IP6}/64 nodad 357 358 # Route leak from blue to red 359 ip -netns $r1 route add vrf blue ${H2_N2} dev red 360 ip -netns $r1 -6 route add vrf blue ${H2_N2_6} dev red 361 362 # No route leak from red to blue 363 364 # 365 # r2 366 # 367 ip -netns $r2 addr add dev eth0 ${R2_N1_IP}/24 368 ip -netns $r2 -6 addr add dev eth0 ${R2_N1_IP6}/64 nodad 369 ip -netns $r2 addr add dev eth1 ${R2_N2_IP}/24 370 ip -netns $r2 -6 addr add dev eth1 ${R2_N2_IP6}/64 nodad 371 372 # Wait for ip config to settle 373 sleep 2 374} 375 376check_connectivity() 377{ 378 ip netns exec $h1 ping -c1 -w1 ${H2_N2_IP} >/dev/null 2>&1 379 log_test $? 0 "Basic IPv4 connectivity" 380 return $? 381} 382 383check_connectivity6() 384{ 385 ip netns exec $h1 "${ping6}" -c1 -w1 ${H2_N2_IP6} >/dev/null 2>&1 386 log_test $? 0 "Basic IPv6 connectivity" 387 return $? 388} 389 390check_traceroute() 391{ 392 if [ ! -x "$(command -v traceroute)" ]; then 393 echo "SKIP: Could not run IPV4 test without traceroute" 394 return 1 395 fi 396} 397 398check_traceroute6() 399{ 400 if [ ! -x "$(command -v traceroute6)" ]; then 401 echo "SKIP: Could not run IPV6 test without traceroute6" 402 return 1 403 fi 404} 405 406ipv4_traceroute() 407{ 408 local ttype="$1" 409 410 [ "x$ttype" = "x" ] && ttype="$DEFAULT_TTYPE" 411 412 log_section "IPv4 ($ttype route): VRF ICMP error route lookup traceroute" 413 414 check_traceroute || return 415 416 setup_"$ttype" 417 418 check_connectivity || return 419 420 run_cmd_grep "${R1_N1_IP}" ip netns exec $h1 traceroute ${H2_N2_IP} 421 log_test $? 0 "Traceroute reports a hop on r1" 422} 423 424ipv4_traceroute_asym() 425{ 426 ipv4_traceroute asym 427} 428 429ipv6_traceroute() 430{ 431 local ttype="$1" 432 433 [ "x$ttype" = "x" ] && ttype="$DEFAULT_TTYPE" 434 435 log_section "IPv6 ($ttype route): VRF ICMP error route lookup traceroute" 436 437 check_traceroute6 || return 438 439 setup_"$ttype" 440 441 check_connectivity6 || return 442 443 run_cmd_grep "${R1_N1_IP6}" ip netns exec $h1 traceroute6 ${H2_N2_IP6} 444 log_test $? 0 "Traceroute6 reports a hop on r1" 445} 446 447ipv6_traceroute_asym() 448{ 449 ipv6_traceroute asym 450} 451 452ipv4_ping_ttl() 453{ 454 local ttype="$1" 455 456 [ "x$ttype" = "x" ] && ttype="$DEFAULT_TTYPE" 457 458 log_section "IPv4 ($ttype route): VRF ICMP ttl error route lookup ping" 459 460 setup_"$ttype" 461 462 check_connectivity || return 463 464 run_cmd_grep "Time to live exceeded" ip netns exec $h1 ping -t1 -c1 -W2 ${H2_N2_IP} 465 log_test $? 0 "Ping received ICMP ttl exceeded" 466} 467 468ipv4_ping_ttl_asym() 469{ 470 ipv4_ping_ttl asym 471} 472 473ipv4_ping_frag() 474{ 475 local ttype="$1" 476 477 [ "x$ttype" = "x" ] && ttype="$DEFAULT_TTYPE" 478 479 log_section "IPv4 ($ttype route): VRF ICMP fragmentation error route lookup ping" 480 481 setup_"$ttype" 482 483 check_connectivity || return 484 485 run_cmd_grep "Frag needed" ip netns exec $h1 ping -s 1450 -Mdo -c1 -W2 ${H2_N2_IP} 486 log_test $? 0 "Ping received ICMP Frag needed" 487} 488 489ipv4_ping_frag_asym() 490{ 491 ipv4_ping_frag asym 492} 493 494ipv6_ping_ttl() 495{ 496 local ttype="$1" 497 498 [ "x$ttype" = "x" ] && ttype="$DEFAULT_TTYPE" 499 500 log_section "IPv6 ($ttype route): VRF ICMP ttl error route lookup ping" 501 502 setup_"$ttype" 503 504 check_connectivity6 || return 505 506 run_cmd_grep "Time exceeded: Hop limit" ip netns exec $h1 "${ping6}" -t1 -c1 -W2 ${H2_N2_IP6} 507 log_test $? 0 "Ping received ICMP Hop limit" 508} 509 510ipv6_ping_ttl_asym() 511{ 512 ipv6_ping_ttl asym 513} 514 515ipv6_ping_frag() 516{ 517 local ttype="$1" 518 519 [ "x$ttype" = "x" ] && ttype="$DEFAULT_TTYPE" 520 521 log_section "IPv6 ($ttype route): VRF ICMP fragmentation error route lookup ping" 522 523 setup_"$ttype" 524 525 check_connectivity6 || return 526 527 run_cmd_grep "Packet too big" ip netns exec $h1 "${ping6}" -s 1450 -Mdo -c1 -W2 ${H2_N2_IP6} 528 log_test $? 0 "Ping received ICMP Packet too big" 529} 530 531ipv6_ping_frag_asym() 532{ 533 ipv6_ping_frag asym 534} 535 536################################################################################ 537# usage 538 539usage() 540{ 541 cat <<EOF 542usage: ${0##*/} OPTS 543 544 -4 Run IPv4 tests only 545 -6 Run IPv6 tests only 546 -t TEST Run only TEST 547 -p Pause on fail 548 -v verbose mode (show commands and output) 549EOF 550} 551 552################################################################################ 553# main 554 555# Some systems don't have a ping6 binary anymore 556command -v ping6 > /dev/null 2>&1 && ping6=$(command -v ping6) || ping6=$(command -v ping) 557 558TESTS_IPV4="ipv4_ping_ttl ipv4_traceroute ipv4_ping_frag ipv4_ping_ttl_asym ipv4_traceroute_asym" 559TESTS_IPV6="ipv6_ping_ttl ipv6_traceroute ipv6_ping_ttl_asym ipv6_traceroute_asym" 560 561ret=0 562nsuccess=0 563nfail=0 564 565while getopts :46t:pvh o 566do 567 case $o in 568 4) TESTS=ipv4;; 569 6) TESTS=ipv6;; 570 t) TESTS=$OPTARG;; 571 p) PAUSE_ON_FAIL=yes;; 572 v) VERBOSE=1;; 573 h) usage; exit 0;; 574 *) usage; exit 1;; 575 esac 576done 577 578# 579# show user test config 580# 581if [ -z "$TESTS" ]; then 582 TESTS="$TESTS_IPV4 $TESTS_IPV6" 583elif [ "$TESTS" = "ipv4" ]; then 584 TESTS="$TESTS_IPV4" 585elif [ "$TESTS" = "ipv6" ]; then 586 TESTS="$TESTS_IPV6" 587fi 588 589for t in $TESTS 590do 591 case $t in 592 ipv4_ping_ttl|ping) ipv4_ping_ttl;;& 593 ipv4_ping_ttl_asym|ping) ipv4_ping_ttl_asym;;& 594 ipv4_traceroute|traceroute) ipv4_traceroute;;& 595 ipv4_traceroute_asym|traceroute) ipv4_traceroute_asym;;& 596 ipv4_ping_frag|ping) ipv4_ping_frag;;& 597 598 ipv6_ping_ttl|ping) ipv6_ping_ttl;;& 599 ipv6_ping_ttl_asym|ping) ipv6_ping_ttl_asym;;& 600 ipv6_traceroute|traceroute) ipv6_traceroute;;& 601 ipv6_traceroute_asym|traceroute) ipv6_traceroute_asym;;& 602 ipv6_ping_frag|ping) ipv6_ping_frag;;& 603 604 # setup namespaces and config, but do not run any tests 605 setup_sym|setup) setup_sym; exit 0;; 606 setup_asym) setup_asym; exit 0;; 607 608 help) echo "Test names: $TESTS"; exit 0;; 609 esac 610done 611 612cleanup 613 614printf "\nTests passed: %3d\n" ${nsuccess} 615printf "Tests failed: %3d\n" ${nfail} 616 617exit $ret 618