1#!/bin/bash 2# SPDX-License-Identifier: GPL-2.0 3# 4# Run traceroute/traceroute6 tests 5# 6 7source lib.sh 8VERBOSE=0 9PAUSE_ON_FAIL=no 10 11################################################################################ 12# 13run_cmd() 14{ 15 local ns 16 local cmd 17 local out 18 local rc 19 20 ns="$1" 21 shift 22 cmd="$*" 23 24 if [ "$VERBOSE" = "1" ]; then 25 printf " COMMAND: $cmd\n" 26 fi 27 28 out=$(eval ip netns exec ${ns} ${cmd} 2>&1) 29 rc=$? 30 if [ "$VERBOSE" = "1" -a -n "$out" ]; then 31 echo " $out" 32 fi 33 34 [ "$VERBOSE" = "1" ] && echo 35 36 return $rc 37} 38 39################################################################################ 40# create namespaces and interconnects 41 42create_ns() 43{ 44 local ns=$1 45 local addr=$2 46 local addr6=$3 47 48 [ -z "${addr}" ] && addr="-" 49 [ -z "${addr6}" ] && addr6="-" 50 51 if [ "${addr}" != "-" ]; then 52 ip netns exec ${ns} ip addr add dev lo ${addr} 53 fi 54 if [ "${addr6}" != "-" ]; then 55 ip netns exec ${ns} ip -6 addr add dev lo ${addr6} 56 fi 57 58 ip netns exec ${ns} ip ro add unreachable default metric 8192 59 ip netns exec ${ns} ip -6 ro add unreachable default metric 8192 60 61 ip netns exec ${ns} sysctl -qw net.ipv4.ip_forward=1 62 ip netns exec ${ns} sysctl -qw net.ipv6.conf.all.keep_addr_on_down=1 63 ip netns exec ${ns} sysctl -qw net.ipv6.conf.all.forwarding=1 64 ip netns exec ${ns} sysctl -qw net.ipv6.conf.default.forwarding=1 65 ip netns exec ${ns} sysctl -qw net.ipv6.conf.default.accept_dad=0 66} 67 68# create veth pair to connect namespaces and apply addresses. 69connect_ns() 70{ 71 local ns1=$1 72 local ns1_dev=$2 73 local ns1_addr=$3 74 local ns1_addr6=$4 75 local ns2=$5 76 local ns2_dev=$6 77 local ns2_addr=$7 78 local ns2_addr6=$8 79 80 ip netns exec ${ns1} ip li add ${ns1_dev} type veth peer name tmp 81 ip netns exec ${ns1} ip li set ${ns1_dev} up 82 ip netns exec ${ns1} ip li set tmp netns ${ns2} name ${ns2_dev} 83 ip netns exec ${ns2} ip li set ${ns2_dev} up 84 85 if [ "${ns1_addr}" != "-" ]; then 86 ip netns exec ${ns1} ip addr add dev ${ns1_dev} ${ns1_addr} 87 fi 88 89 if [ "${ns2_addr}" != "-" ]; then 90 ip netns exec ${ns2} ip addr add dev ${ns2_dev} ${ns2_addr} 91 fi 92 93 if [ "${ns1_addr6}" != "-" ]; then 94 ip netns exec ${ns1} ip addr add dev ${ns1_dev} ${ns1_addr6} 95 fi 96 97 if [ "${ns2_addr6}" != "-" ]; then 98 ip netns exec ${ns2} ip addr add dev ${ns2_dev} ${ns2_addr6} 99 fi 100} 101 102################################################################################ 103# traceroute6 test 104# 105# Verify that in this scenario 106# 107# ------------------------ N2 108# | | 109# ------ ------ N3 ---- 110# | R1 | | R2 |------|H2| 111# ------ ------ ---- 112# | | 113# ------------------------ N1 114# | 115# ---- 116# |H1| 117# ---- 118# 119# where H1's default route goes through R1 and R1's default route goes 120# through R2 over N2, traceroute6 from H1 to H2 reports R2's address 121# on N2 and not N1. 122# 123# Addresses are assigned as follows: 124# 125# N1: 2000:101::/64 126# N2: 2000:102::/64 127# N3: 2000:103::/64 128# 129# R1's host part of address: 1 130# R2's host part of address: 2 131# H1's host part of address: 3 132# H2's host part of address: 4 133# 134# For example: 135# the IPv6 address of R1's interface on N2 is 2000:102::1/64 136 137cleanup_traceroute6() 138{ 139 cleanup_ns $h1 $h2 $r1 $r2 140} 141 142setup_traceroute6() 143{ 144 brdev=br0 145 146 # start clean 147 cleanup_traceroute6 148 149 set -e 150 setup_ns h1 h2 r1 r2 151 create_ns $h1 152 create_ns $h2 153 create_ns $r1 154 create_ns $r2 155 156 # Setup N3 157 connect_ns $r2 eth3 - 2000:103::2/64 $h2 eth3 - 2000:103::4/64 158 ip netns exec $h2 ip route add default via 2000:103::2 159 160 # Setup N2 161 connect_ns $r1 eth2 - 2000:102::1/64 $r2 eth2 - 2000:102::2/64 162 ip netns exec $r1 ip route add default via 2000:102::2 163 164 # Setup N1. host-1 and router-2 connect to a bridge in router-1. 165 ip netns exec $r1 ip link add name ${brdev} type bridge 166 ip netns exec $r1 ip link set ${brdev} up 167 ip netns exec $r1 ip addr add 2000:101::1/64 dev ${brdev} 168 169 connect_ns $h1 eth0 - 2000:101::3/64 $r1 eth0 - - 170 ip netns exec $r1 ip link set dev eth0 master ${brdev} 171 ip netns exec $h1 ip route add default via 2000:101::1 172 173 connect_ns $r2 eth1 - 2000:101::2/64 $r1 eth1 - - 174 ip netns exec $r1 ip link set dev eth1 master ${brdev} 175 176 # Prime the network 177 ip netns exec $h1 ping6 -c5 2000:103::4 >/dev/null 2>&1 178 179 set +e 180} 181 182run_traceroute6() 183{ 184 setup_traceroute6 185 186 RET=0 187 188 # traceroute6 host-2 from host-1 (expects 2000:102::2) 189 run_cmd $h1 "traceroute6 2000:103::4 | grep -q 2000:102::2" 190 check_err $? "traceroute6 did not return 2000:102::2" 191 log_test "IPv6 traceroute" 192 193 cleanup_traceroute6 194} 195 196################################################################################ 197# traceroute6 with VRF test 198# 199# Verify that in this scenario 200# 201# ------------------------ N2 202# | | 203# ------ ------ N3 ---- 204# | R1 | | R2 |------|H2| 205# ------ ------ ---- 206# | | 207# ------------------------ N1 208# | 209# ---- 210# |H1| 211# ---- 212# 213# Where H1's default route goes through R1 and R1's default route goes through 214# R2 over N2, traceroute6 from H1 to H2 reports R2's address on N2 and not N1. 215# The interfaces connecting R2 to the different subnets are membmer in a VRF 216# and the intention is to check that traceroute6 does not report the VRF's 217# address. 218# 219# Addresses are assigned as follows: 220# 221# N1: 2000:101::/64 222# N2: 2000:102::/64 223# N3: 2000:103::/64 224# 225# R1's host part of address: 1 226# R2's host part of address: 2 227# H1's host part of address: 3 228# H2's host part of address: 4 229# 230# For example: 231# the IPv6 address of R1's interface on N2 is 2000:102::1/64 232 233cleanup_traceroute6_vrf() 234{ 235 cleanup_all_ns 236} 237 238setup_traceroute6_vrf() 239{ 240 # Start clean 241 cleanup_traceroute6_vrf 242 243 setup_ns h1 h2 r1 r2 244 create_ns "$h1" 245 create_ns "$h2" 246 create_ns "$r1" 247 create_ns "$r2" 248 249 ip -n "$r2" link add name vrf100 up type vrf table 100 250 ip -n "$r2" addr add 2001:db8:100::1/64 dev vrf100 251 252 # Setup N3 253 connect_ns "$r2" eth3 - 2000:103::2/64 "$h2" eth3 - 2000:103::4/64 254 255 ip -n "$r2" link set dev eth3 master vrf100 256 257 ip -n "$h2" route add default via 2000:103::2 258 259 # Setup N2 260 connect_ns "$r1" eth2 - 2000:102::1/64 "$r2" eth2 - 2000:102::2/64 261 262 ip -n "$r1" route add default via 2000:102::2 263 264 ip -n "$r2" link set dev eth2 master vrf100 265 266 # Setup N1. host-1 and router-2 connect to a bridge in router-1. 267 ip -n "$r1" link add name br100 up type bridge 268 ip -n "$r1" addr add 2000:101::1/64 dev br100 269 270 connect_ns "$h1" eth0 - 2000:101::3/64 "$r1" eth0 - - 271 272 ip -n "$h1" route add default via 2000:101::1 273 274 ip -n "$r1" link set dev eth0 master br100 275 276 connect_ns "$r2" eth1 - 2000:101::2/64 "$r1" eth1 - - 277 278 ip -n "$r2" link set dev eth1 master vrf100 279 280 ip -n "$r1" link set dev eth1 master br100 281 282 # Prime the network 283 ip netns exec "$h1" ping6 -c5 2000:103::4 >/dev/null 2>&1 284} 285 286run_traceroute6_vrf() 287{ 288 setup_traceroute6_vrf 289 290 RET=0 291 292 # traceroute6 host-2 from host-1 (expects 2000:102::2) 293 run_cmd "$h1" "traceroute6 2000:103::4 | grep 2000:102::2" 294 check_err $? "traceroute6 did not return 2000:102::2" 295 log_test "IPv6 traceroute with VRF" 296 297 cleanup_traceroute6_vrf 298} 299 300################################################################################ 301# traceroute test 302# 303# Verify that traceroute from H1 to H2 shows 1.0.3.1 and 1.0.1.1 when 304# traceroute uses 1.0.3.3 and 1.0.1.3 as the source IP, respectively. 305# 306# 1.0.3.3/24 1.0.3.1/24 307# ---- 1.0.1.3/24 1.0.1.1/24 ---- 1.0.2.1/24 1.0.2.4/24 ---- 308# |H1|--------------------------|R1|--------------------------|H2| 309# ---- N1 ---- N2 ---- 310# 311# where net.ipv4.icmp_errors_use_inbound_ifaddr is set on R1 and 1.0.3.1/24 and 312# 1.0.1.1/24 are R1's primary addresses on N1. The kernel is expected to prefer 313# a source address that is on the same subnet as the destination IP of the ICMP 314# error message. 315 316cleanup_traceroute() 317{ 318 cleanup_ns $h1 $h2 $router 319} 320 321setup_traceroute() 322{ 323 # start clean 324 cleanup_traceroute 325 326 set -e 327 setup_ns h1 h2 router 328 create_ns $h1 329 create_ns $h2 330 create_ns $router 331 332 connect_ns $h1 eth0 1.0.1.3/24 - \ 333 $router eth1 1.0.3.1/24 - 334 ip -n "$h1" addr add 1.0.3.3/24 dev eth0 335 ip netns exec $h1 ip route add default via 1.0.1.1 336 337 ip netns exec $router ip addr add 1.0.1.1/24 dev eth1 338 ip netns exec $router sysctl -qw \ 339 net.ipv4.icmp_errors_use_inbound_ifaddr=1 340 341 connect_ns $h2 eth0 1.0.2.4/24 - \ 342 $router eth2 1.0.2.1/24 - 343 ip netns exec $h2 ip route add default via 1.0.2.1 344 345 # Prime the network 346 ip netns exec $h1 ping -c5 1.0.2.4 >/dev/null 2>&1 347 348 set +e 349} 350 351run_traceroute() 352{ 353 setup_traceroute 354 355 RET=0 356 357 # traceroute host-2 from host-1. Expect a source IP that is on the same 358 # subnet as destination IP of the ICMP error message. 359 run_cmd "$h1" "traceroute -s 1.0.1.3 1.0.2.4 | grep -q 1.0.1.1" 360 check_err $? "traceroute did not return 1.0.1.1" 361 run_cmd "$h1" "traceroute -s 1.0.3.3 1.0.2.4 | grep -q 1.0.3.1" 362 check_err $? "traceroute did not return 1.0.3.1" 363 log_test "IPv4 traceroute" 364 365 cleanup_traceroute 366} 367 368################################################################################ 369# traceroute with VRF test 370# 371# Verify that traceroute from H1 to H2 shows 1.0.3.1 and 1.0.1.1 when 372# traceroute uses 1.0.3.3 and 1.0.1.3 as the source IP, respectively. The 373# intention is to check that the kernel does not choose an IP assigned to the 374# VRF device, but rather an address from the VRF port (eth1) that received the 375# packet that generates the ICMP error message. 376# 377# 1.0.4.1/24 (vrf100) 378# 1.0.3.3/24 1.0.3.1/24 379# ---- 1.0.1.3/24 1.0.1.1/24 ---- 1.0.2.1/24 1.0.2.4/24 ---- 380# |H1|--------------------------|R1|--------------------------|H2| 381# ---- N1 ---- N2 ---- 382 383cleanup_traceroute_vrf() 384{ 385 cleanup_all_ns 386} 387 388setup_traceroute_vrf() 389{ 390 # Start clean 391 cleanup_traceroute_vrf 392 393 setup_ns h1 h2 router 394 create_ns "$h1" 395 create_ns "$h2" 396 create_ns "$router" 397 398 ip -n "$router" link add name vrf100 up type vrf table 100 399 ip -n "$router" addr add 1.0.4.1/24 dev vrf100 400 401 connect_ns "$h1" eth0 1.0.1.3/24 - \ 402 "$router" eth1 1.0.1.1/24 - 403 404 ip -n "$h1" addr add 1.0.3.3/24 dev eth0 405 ip -n "$h1" route add default via 1.0.1.1 406 407 ip -n "$router" link set dev eth1 master vrf100 408 ip -n "$router" addr add 1.0.3.1/24 dev eth1 409 ip netns exec "$router" sysctl -qw \ 410 net.ipv4.icmp_errors_use_inbound_ifaddr=1 411 412 connect_ns "$h2" eth0 1.0.2.4/24 - \ 413 "$router" eth2 1.0.2.1/24 - 414 415 ip -n "$h2" route add default via 1.0.2.1 416 417 ip -n "$router" link set dev eth2 master vrf100 418 419 # Prime the network 420 ip netns exec "$h1" ping -c5 1.0.2.4 >/dev/null 2>&1 421} 422 423run_traceroute_vrf() 424{ 425 setup_traceroute_vrf 426 427 RET=0 428 429 # traceroute host-2 from host-1. Expect a source IP that is on the same 430 # subnet as destination IP of the ICMP error message. 431 run_cmd "$h1" "traceroute -s 1.0.1.3 1.0.2.4 | grep 1.0.1.1" 432 check_err $? "traceroute did not return 1.0.1.1" 433 run_cmd "$h1" "traceroute -s 1.0.3.3 1.0.2.4 | grep 1.0.3.1" 434 check_err $? "traceroute did not return 1.0.3.1" 435 log_test "IPv4 traceroute with VRF" 436 437 cleanup_traceroute_vrf 438} 439 440################################################################################ 441# Run tests 442 443run_tests() 444{ 445 run_traceroute6 446 run_traceroute6_vrf 447 run_traceroute 448 run_traceroute_vrf 449} 450 451################################################################################ 452# main 453 454while getopts :pv o 455do 456 case $o in 457 p) PAUSE_ON_FAIL=yes;; 458 v) VERBOSE=$(($VERBOSE + 1));; 459 *) exit 1;; 460 esac 461done 462 463require_command traceroute6 464require_command traceroute 465 466run_tests 467 468exit "${EXIT_STATUS}" 469