Back to home page

OSCL-LXR

 
 

    


0001 #!/bin/bash
0002 # SPDX-License-Identifier: GPL-2.0
0003 #
0004 # Copyright (c) 2019 David Ahern <dsahern@gmail.com>. All rights reserved.
0005 # Copyright (c) 2020 Michael Jeanson <mjeanson@efficios.com>. All rights reserved.
0006 #
0007 # Requires CONFIG_NET_VRF, CONFIG_VETH, CONFIG_BRIDGE and CONFIG_NET_NS.
0008 #
0009 #
0010 # Symmetric routing topology
0011 #
0012 #                     blue         red
0013 # +----+              .253 +----+ .253              +----+
0014 # | h1 |-------------------| r1 |-------------------| h2 |
0015 # +----+ .1                +----+                .2 +----+
0016 #         172.16.1/24                  172.16.2/24
0017 #    2001:db8:16:1/64                  2001:db8:16:2/64
0018 #
0019 #
0020 # Route from h1 to h2 and back goes through r1, incoming vrf blue has a route
0021 # to the outgoing vrf red for the n2 network and red has a route back to n1.
0022 # The red VRF interface has a MTU of 1400.
0023 #
0024 # The first test sends a ping with a ttl of 1 from h1 to h2 and parses the
0025 # output of the command to check that a ttl expired error is received.
0026 #
0027 # The second test runs traceroute from h1 to h2 and parses the output to check
0028 # for a hop on r1.
0029 #
0030 # The third test sends a ping with a packet size of 1450 from h1 to h2 and
0031 # parses the output of the command to check that a fragmentation error is
0032 # received.
0033 #
0034 #
0035 # Asymmetric routing topology
0036 #
0037 # This topology represents a customer setup where the issue with icmp errors
0038 # and VRF route leaking was initialy reported. The MTU test isn't done here
0039 # because of the lack of a return route in the red VRF.
0040 #
0041 #                     blue         red
0042 #                     .253 +----+ .253
0043 #                     +----| r1 |----+
0044 #                     |    +----+    |
0045 # +----+              |              |              +----+
0046 # | h1 |--------------+              +--------------| h2 |
0047 # +----+ .1           |              |           .2 +----+
0048 #         172.16.1/24 |    +----+    | 172.16.2/24
0049 #    2001:db8:16:1/64 +----| r2 |----+ 2001:db8:16:2/64
0050 #                     .254 +----+ .254
0051 #
0052 #
0053 # Route from h1 to h2 goes through r1, incoming vrf blue has a route to the
0054 # outgoing vrf red for the n2 network but red doesn't have a route back to n1.
0055 # Route from h2 to h1 goes through r2.
0056 #
0057 # The objective is to check that the incoming vrf routing table is selected
0058 # to send an ICMP error back to the source when the ttl of a packet reaches 1
0059 # while it is forwarded between different vrfs.
0060 
0061 VERBOSE=0
0062 PAUSE_ON_FAIL=no
0063 DEFAULT_TTYPE=sym
0064 
0065 H1_N1=172.16.1.0/24
0066 H1_N1_6=2001:db8:16:1::/64
0067 
0068 H1_N1_IP=172.16.1.1
0069 R1_N1_IP=172.16.1.253
0070 R2_N1_IP=172.16.1.254
0071 
0072 H1_N1_IP6=2001:db8:16:1::1
0073 R1_N1_IP6=2001:db8:16:1::253
0074 R2_N1_IP6=2001:db8:16:1::254
0075 
0076 H2_N2=172.16.2.0/24
0077 H2_N2_6=2001:db8:16:2::/64
0078 
0079 H2_N2_IP=172.16.2.2
0080 R1_N2_IP=172.16.2.253
0081 R2_N2_IP=172.16.2.254
0082 
0083 H2_N2_IP6=2001:db8:16:2::2
0084 R1_N2_IP6=2001:db8:16:2::253
0085 R2_N2_IP6=2001:db8:16:2::254
0086 
0087 ################################################################################
0088 # helpers
0089 
0090 log_section()
0091 {
0092         echo
0093         echo "###########################################################################"
0094         echo "$*"
0095         echo "###########################################################################"
0096         echo
0097 }
0098 
0099 log_test()
0100 {
0101         local rc=$1
0102         local expected=$2
0103         local msg="$3"
0104 
0105         if [ "${rc}" -eq "${expected}" ]; then
0106                 printf "TEST: %-60s  [ OK ]\n" "${msg}"
0107                 nsuccess=$((nsuccess+1))
0108         else
0109                 ret=1
0110                 nfail=$((nfail+1))
0111                 printf "TEST: %-60s  [FAIL]\n" "${msg}"
0112                 if [ "${PAUSE_ON_FAIL}" = "yes" ]; then
0113                         echo
0114                         echo "hit enter to continue, 'q' to quit"
0115                         read -r a
0116                         [ "$a" = "q" ] && exit 1
0117                 fi
0118         fi
0119 }
0120 
0121 run_cmd()
0122 {
0123         local cmd="$*"
0124         local out
0125         local rc
0126 
0127         if [ "$VERBOSE" = "1" ]; then
0128                 echo "COMMAND: $cmd"
0129         fi
0130 
0131         # shellcheck disable=SC2086
0132         out=$(eval $cmd 2>&1)
0133         rc=$?
0134         if [ "$VERBOSE" = "1" ] && [ -n "$out" ]; then
0135                 echo "$out"
0136         fi
0137 
0138         [ "$VERBOSE" = "1" ] && echo
0139 
0140         return $rc
0141 }
0142 
0143 run_cmd_grep()
0144 {
0145         local grep_pattern="$1"
0146         shift
0147         local cmd="$*"
0148         local out
0149         local rc
0150 
0151         if [ "$VERBOSE" = "1" ]; then
0152                 echo "COMMAND: $cmd"
0153         fi
0154 
0155         # shellcheck disable=SC2086
0156         out=$(eval $cmd 2>&1)
0157         if [ "$VERBOSE" = "1" ] && [ -n "$out" ]; then
0158                 echo "$out"
0159         fi
0160 
0161         echo "$out" | grep -q "$grep_pattern"
0162         rc=$?
0163 
0164         [ "$VERBOSE" = "1" ] && echo
0165 
0166         return $rc
0167 }
0168 
0169 ################################################################################
0170 # setup and teardown
0171 
0172 cleanup()
0173 {
0174         local ns
0175 
0176         for ns in h1 h2 r1 r2; do
0177                 ip netns del $ns 2>/dev/null
0178         done
0179 }
0180 
0181 setup_vrf()
0182 {
0183         local ns=$1
0184 
0185         ip -netns "${ns}" rule del pref 0
0186         ip -netns "${ns}" rule add pref 32765 from all lookup local
0187         ip -netns "${ns}" -6 rule del pref 0
0188         ip -netns "${ns}" -6 rule add pref 32765 from all lookup local
0189 }
0190 
0191 create_vrf()
0192 {
0193         local ns=$1
0194         local vrf=$2
0195         local table=$3
0196 
0197         ip -netns "${ns}" link add "${vrf}" type vrf table "${table}"
0198         ip -netns "${ns}" link set "${vrf}" up
0199         ip -netns "${ns}" route add vrf "${vrf}" unreachable default metric 8192
0200         ip -netns "${ns}" -6 route add vrf "${vrf}" unreachable default metric 8192
0201 
0202         ip -netns "${ns}" addr add 127.0.0.1/8 dev "${vrf}"
0203         ip -netns "${ns}" -6 addr add ::1 dev "${vrf}" nodad
0204 }
0205 
0206 setup_sym()
0207 {
0208         local ns
0209 
0210         # make sure we are starting with a clean slate
0211         cleanup
0212 
0213         #
0214         # create nodes as namespaces
0215         #
0216         for ns in h1 h2 r1; do
0217                 ip netns add $ns
0218                 ip -netns $ns link set lo up
0219 
0220                 case "${ns}" in
0221                 h[12]) ip netns exec $ns sysctl -q -w net.ipv6.conf.all.forwarding=0
0222                        ip netns exec $ns sysctl -q -w net.ipv6.conf.all.keep_addr_on_down=1
0223                         ;;
0224                 r1)    ip netns exec $ns sysctl -q -w net.ipv4.ip_forward=1
0225                        ip netns exec $ns sysctl -q -w net.ipv6.conf.all.forwarding=1
0226                 esac
0227         done
0228 
0229         #
0230         # create interconnects
0231         #
0232         ip -netns h1 link add eth0 type veth peer name r1h1
0233         ip -netns h1 link set r1h1 netns r1 name eth0 up
0234 
0235         ip -netns h2 link add eth0 type veth peer name r1h2
0236         ip -netns h2 link set r1h2 netns r1 name eth1 up
0237 
0238         #
0239         # h1
0240         #
0241         ip -netns h1 addr add dev eth0 ${H1_N1_IP}/24
0242         ip -netns h1 -6 addr add dev eth0 ${H1_N1_IP6}/64 nodad
0243         ip -netns h1 link set eth0 up
0244 
0245         # h1 to h2 via r1
0246         ip -netns h1    route add ${H2_N2} via ${R1_N1_IP} dev eth0
0247         ip -netns h1 -6 route add ${H2_N2_6} via "${R1_N1_IP6}" dev eth0
0248 
0249         #
0250         # h2
0251         #
0252         ip -netns h2 addr add dev eth0 ${H2_N2_IP}/24
0253         ip -netns h2 -6 addr add dev eth0 ${H2_N2_IP6}/64 nodad
0254         ip -netns h2 link set eth0 up
0255 
0256         # h2 to h1 via r1
0257         ip -netns h2 route add default via ${R1_N2_IP} dev eth0
0258         ip -netns h2 -6 route add default via ${R1_N2_IP6} dev eth0
0259 
0260         #
0261         # r1
0262         #
0263         setup_vrf r1
0264         create_vrf r1 blue 1101
0265         create_vrf r1 red 1102
0266         ip -netns r1 link set mtu 1400 dev eth1
0267         ip -netns r1 link set eth0 vrf blue up
0268         ip -netns r1 link set eth1 vrf red up
0269         ip -netns r1 addr add dev eth0 ${R1_N1_IP}/24
0270         ip -netns r1 -6 addr add dev eth0 ${R1_N1_IP6}/64 nodad
0271         ip -netns r1 addr add dev eth1 ${R1_N2_IP}/24
0272         ip -netns r1 -6 addr add dev eth1 ${R1_N2_IP6}/64 nodad
0273 
0274         # Route leak from blue to red
0275         ip -netns r1 route add vrf blue ${H2_N2} dev red
0276         ip -netns r1 -6 route add vrf blue ${H2_N2_6} dev red
0277 
0278         # Route leak from red to blue
0279         ip -netns r1 route add vrf red ${H1_N1} dev blue
0280         ip -netns r1 -6 route add vrf red ${H1_N1_6} dev blue
0281 
0282 
0283         # Wait for ip config to settle
0284         sleep 2
0285 }
0286 
0287 setup_asym()
0288 {
0289         local ns
0290 
0291         # make sure we are starting with a clean slate
0292         cleanup
0293 
0294         #
0295         # create nodes as namespaces
0296         #
0297         for ns in h1 h2 r1 r2; do
0298                 ip netns add $ns
0299                 ip -netns $ns link set lo up
0300 
0301                 case "${ns}" in
0302                 h[12]) ip netns exec $ns sysctl -q -w net.ipv6.conf.all.forwarding=0
0303                        ip netns exec $ns sysctl -q -w net.ipv6.conf.all.keep_addr_on_down=1
0304                         ;;
0305                 r[12]) ip netns exec $ns sysctl -q -w net.ipv4.ip_forward=1
0306                        ip netns exec $ns sysctl -q -w net.ipv6.conf.all.forwarding=1
0307                 esac
0308         done
0309 
0310         #
0311         # create interconnects
0312         #
0313         ip -netns h1 link add eth0 type veth peer name r1h1
0314         ip -netns h1 link set r1h1 netns r1 name eth0 up
0315 
0316         ip -netns h1 link add eth1 type veth peer name r2h1
0317         ip -netns h1 link set r2h1 netns r2 name eth0 up
0318 
0319         ip -netns h2 link add eth0 type veth peer name r1h2
0320         ip -netns h2 link set r1h2 netns r1 name eth1 up
0321 
0322         ip -netns h2 link add eth1 type veth peer name r2h2
0323         ip -netns h2 link set r2h2 netns r2 name eth1 up
0324 
0325         #
0326         # h1
0327         #
0328         ip -netns h1 link add br0 type bridge
0329         ip -netns h1 link set br0 up
0330         ip -netns h1 addr add dev br0 ${H1_N1_IP}/24
0331         ip -netns h1 -6 addr add dev br0 ${H1_N1_IP6}/64 nodad
0332         ip -netns h1 link set eth0 master br0 up
0333         ip -netns h1 link set eth1 master br0 up
0334 
0335         # h1 to h2 via r1
0336         ip -netns h1    route add ${H2_N2} via ${R1_N1_IP} dev br0
0337         ip -netns h1 -6 route add ${H2_N2_6} via "${R1_N1_IP6}" dev br0
0338 
0339         #
0340         # h2
0341         #
0342         ip -netns h2 link add br0 type bridge
0343         ip -netns h2 link set br0 up
0344         ip -netns h2 addr add dev br0 ${H2_N2_IP}/24
0345         ip -netns h2 -6 addr add dev br0 ${H2_N2_IP6}/64 nodad
0346         ip -netns h2 link set eth0 master br0 up
0347         ip -netns h2 link set eth1 master br0 up
0348 
0349         # h2 to h1 via r2
0350         ip -netns h2 route add default via ${R2_N2_IP} dev br0
0351         ip -netns h2 -6 route add default via ${R2_N2_IP6} dev br0
0352 
0353         #
0354         # r1
0355         #
0356         setup_vrf r1
0357         create_vrf r1 blue 1101
0358         create_vrf r1 red 1102
0359         ip -netns r1 link set mtu 1400 dev eth1
0360         ip -netns r1 link set eth0 vrf blue up
0361         ip -netns r1 link set eth1 vrf red up
0362         ip -netns r1 addr add dev eth0 ${R1_N1_IP}/24
0363         ip -netns r1 -6 addr add dev eth0 ${R1_N1_IP6}/64 nodad
0364         ip -netns r1 addr add dev eth1 ${R1_N2_IP}/24
0365         ip -netns r1 -6 addr add dev eth1 ${R1_N2_IP6}/64 nodad
0366 
0367         # Route leak from blue to red
0368         ip -netns r1 route add vrf blue ${H2_N2} dev red
0369         ip -netns r1 -6 route add vrf blue ${H2_N2_6} dev red
0370 
0371         # No route leak from red to blue
0372 
0373         #
0374         # r2
0375         #
0376         ip -netns r2 addr add dev eth0 ${R2_N1_IP}/24
0377         ip -netns r2 -6 addr add dev eth0 ${R2_N1_IP6}/64 nodad
0378         ip -netns r2 addr add dev eth1 ${R2_N2_IP}/24
0379         ip -netns r2 -6 addr add dev eth1 ${R2_N2_IP6}/64 nodad
0380 
0381         # Wait for ip config to settle
0382         sleep 2
0383 }
0384 
0385 check_connectivity()
0386 {
0387         ip netns exec h1 ping -c1 -w1 ${H2_N2_IP} >/dev/null 2>&1
0388         log_test $? 0 "Basic IPv4 connectivity"
0389         return $?
0390 }
0391 
0392 check_connectivity6()
0393 {
0394         ip netns exec h1 "${ping6}" -c1 -w1 ${H2_N2_IP6} >/dev/null 2>&1
0395         log_test $? 0 "Basic IPv6 connectivity"
0396         return $?
0397 }
0398 
0399 check_traceroute()
0400 {
0401         if [ ! -x "$(command -v traceroute)" ]; then
0402                 echo "SKIP: Could not run IPV4 test without traceroute"
0403                 return 1
0404         fi
0405 }
0406 
0407 check_traceroute6()
0408 {
0409         if [ ! -x "$(command -v traceroute6)" ]; then
0410                 echo "SKIP: Could not run IPV6 test without traceroute6"
0411                 return 1
0412         fi
0413 }
0414 
0415 ipv4_traceroute()
0416 {
0417         local ttype="$1"
0418 
0419         [ "x$ttype" = "x" ] && ttype="$DEFAULT_TTYPE"
0420 
0421         log_section "IPv4 ($ttype route): VRF ICMP error route lookup traceroute"
0422 
0423         check_traceroute || return
0424 
0425         setup_"$ttype"
0426 
0427         check_connectivity || return
0428 
0429         run_cmd_grep "${R1_N1_IP}" ip netns exec h1 traceroute ${H2_N2_IP}
0430         log_test $? 0 "Traceroute reports a hop on r1"
0431 }
0432 
0433 ipv4_traceroute_asym()
0434 {
0435         ipv4_traceroute asym
0436 }
0437 
0438 ipv6_traceroute()
0439 {
0440         local ttype="$1"
0441 
0442         [ "x$ttype" = "x" ] && ttype="$DEFAULT_TTYPE"
0443 
0444         log_section "IPv6 ($ttype route): VRF ICMP error route lookup traceroute"
0445 
0446         check_traceroute6 || return
0447 
0448         setup_"$ttype"
0449 
0450         check_connectivity6 || return
0451 
0452         run_cmd_grep "${R1_N1_IP6}" ip netns exec h1 traceroute6 ${H2_N2_IP6}
0453         log_test $? 0 "Traceroute6 reports a hop on r1"
0454 }
0455 
0456 ipv6_traceroute_asym()
0457 {
0458         ipv6_traceroute asym
0459 }
0460 
0461 ipv4_ping_ttl()
0462 {
0463         local ttype="$1"
0464 
0465         [ "x$ttype" = "x" ] && ttype="$DEFAULT_TTYPE"
0466 
0467         log_section "IPv4 ($ttype route): VRF ICMP ttl error route lookup ping"
0468 
0469         setup_"$ttype"
0470 
0471         check_connectivity || return
0472 
0473         run_cmd_grep "Time to live exceeded" ip netns exec h1 ping -t1 -c1 -W2 ${H2_N2_IP}
0474         log_test $? 0 "Ping received ICMP ttl exceeded"
0475 }
0476 
0477 ipv4_ping_ttl_asym()
0478 {
0479         ipv4_ping_ttl asym
0480 }
0481 
0482 ipv4_ping_frag()
0483 {
0484         local ttype="$1"
0485 
0486         [ "x$ttype" = "x" ] && ttype="$DEFAULT_TTYPE"
0487 
0488         log_section "IPv4 ($ttype route): VRF ICMP fragmentation error route lookup ping"
0489 
0490         setup_"$ttype"
0491 
0492         check_connectivity || return
0493 
0494         run_cmd_grep "Frag needed" ip netns exec h1 ping -s 1450 -Mdo -c1 -W2 ${H2_N2_IP}
0495         log_test $? 0 "Ping received ICMP Frag needed"
0496 }
0497 
0498 ipv4_ping_frag_asym()
0499 {
0500         ipv4_ping_frag asym
0501 }
0502 
0503 ipv6_ping_ttl()
0504 {
0505         local ttype="$1"
0506 
0507         [ "x$ttype" = "x" ] && ttype="$DEFAULT_TTYPE"
0508 
0509         log_section "IPv6 ($ttype route): VRF ICMP ttl error route lookup ping"
0510 
0511         setup_"$ttype"
0512 
0513         check_connectivity6 || return
0514 
0515         run_cmd_grep "Time exceeded: Hop limit" ip netns exec h1 "${ping6}" -t1 -c1 -W2 ${H2_N2_IP6}
0516         log_test $? 0 "Ping received ICMP Hop limit"
0517 }
0518 
0519 ipv6_ping_ttl_asym()
0520 {
0521         ipv6_ping_ttl asym
0522 }
0523 
0524 ipv6_ping_frag()
0525 {
0526         local ttype="$1"
0527 
0528         [ "x$ttype" = "x" ] && ttype="$DEFAULT_TTYPE"
0529 
0530         log_section "IPv6 ($ttype route): VRF ICMP fragmentation error route lookup ping"
0531 
0532         setup_"$ttype"
0533 
0534         check_connectivity6 || return
0535 
0536         run_cmd_grep "Packet too big" ip netns exec h1 "${ping6}" -s 1450 -Mdo -c1 -W2 ${H2_N2_IP6}
0537         log_test $? 0 "Ping received ICMP Packet too big"
0538 }
0539 
0540 ipv6_ping_frag_asym()
0541 {
0542         ipv6_ping_frag asym
0543 }
0544 
0545 ################################################################################
0546 # usage
0547 
0548 usage()
0549 {
0550         cat <<EOF
0551 usage: ${0##*/} OPTS
0552 
0553         -4          Run IPv4 tests only
0554         -6          Run IPv6 tests only
0555         -t TEST     Run only TEST
0556         -p          Pause on fail
0557         -v          verbose mode (show commands and output)
0558 EOF
0559 }
0560 
0561 ################################################################################
0562 # main
0563 
0564 # Some systems don't have a ping6 binary anymore
0565 command -v ping6 > /dev/null 2>&1 && ping6=$(command -v ping6) || ping6=$(command -v ping)
0566 
0567 TESTS_IPV4="ipv4_ping_ttl ipv4_traceroute ipv4_ping_frag ipv4_ping_ttl_asym ipv4_traceroute_asym"
0568 TESTS_IPV6="ipv6_ping_ttl ipv6_traceroute ipv6_ping_frag ipv6_ping_ttl_asym ipv6_traceroute_asym"
0569 
0570 ret=0
0571 nsuccess=0
0572 nfail=0
0573 
0574 while getopts :46t:pvh o
0575 do
0576         case $o in
0577                 4) TESTS=ipv4;;
0578                 6) TESTS=ipv6;;
0579                 t) TESTS=$OPTARG;;
0580                 p) PAUSE_ON_FAIL=yes;;
0581                 v) VERBOSE=1;;
0582                 h) usage; exit 0;;
0583                 *) usage; exit 1;;
0584         esac
0585 done
0586 
0587 #
0588 # show user test config
0589 #
0590 if [ -z "$TESTS" ]; then
0591         TESTS="$TESTS_IPV4 $TESTS_IPV6"
0592 elif [ "$TESTS" = "ipv4" ]; then
0593         TESTS="$TESTS_IPV4"
0594 elif [ "$TESTS" = "ipv6" ]; then
0595         TESTS="$TESTS_IPV6"
0596 fi
0597 
0598 for t in $TESTS
0599 do
0600         case $t in
0601         ipv4_ping_ttl|ping)              ipv4_ping_ttl;;&
0602         ipv4_ping_ttl_asym|ping)         ipv4_ping_ttl_asym;;&
0603         ipv4_traceroute|traceroute)      ipv4_traceroute;;&
0604         ipv4_traceroute_asym|traceroute) ipv4_traceroute_asym;;&
0605         ipv4_ping_frag|ping)             ipv4_ping_frag;;&
0606 
0607         ipv6_ping_ttl|ping)              ipv6_ping_ttl;;&
0608         ipv6_ping_ttl_asym|ping)         ipv6_ping_ttl_asym;;&
0609         ipv6_traceroute|traceroute)      ipv6_traceroute;;&
0610         ipv6_traceroute_asym|traceroute) ipv6_traceroute_asym;;&
0611         ipv6_ping_frag|ping)             ipv6_ping_frag;;&
0612 
0613         # setup namespaces and config, but do not run any tests
0614         setup_sym|setup)                 setup_sym; exit 0;;
0615         setup_asym)                      setup_asym; exit 0;;
0616 
0617         help)                       echo "Test names: $TESTS"; exit 0;;
0618         esac
0619 done
0620 
0621 cleanup
0622 
0623 printf "\nTests passed: %3d\n" ${nsuccess}
0624 printf "Tests failed: %3d\n"   ${nfail}
0625 
0626 exit $ret