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