1#!/bin/bash 2# SPDX-License-Identifier: GPL-2.0 3# 4# Setup/topology: 5# 6# NS1 NS2 NS3 7# veth1 <---> veth2 veth3 <---> veth4 (the top route) 8# veth5 <---> veth6 veth7 <---> veth8 (the bottom route) 9# 10# each vethN gets IPv[4|6]_N address 11# 12# IPv*_SRC = IPv*_1 13# IPv*_DST = IPv*_4 14# 15# all tests test pings from IPv*_SRC to IPv*_DST 16# 17# by default, routes are configured to allow packets to go 18# IP*_1 <=> IP*_2 <=> IP*_3 <=> IP*_4 (the top route) 19# 20# a GRE device is installed in NS3 with IPv*_GRE, and 21# NS1/NS2 are configured to route packets to IPv*_GRE via IP*_8 22# (the bottom route) 23# 24# Tests: 25# 26# 1. routes NS2->IPv*_DST are brought down, so the only way a ping 27# from IP*_SRC to IP*_DST can work is via IPv*_GRE 28# 29# 2a. in an egress test, a bpf LWT_XMIT program is installed on veth1 30# that encaps the packets with an IP/GRE header to route to IPv*_GRE 31# 32# ping: SRC->[encap at veth1:egress]->GRE:decap->DST 33# ping replies go DST->SRC directly 34# 35# 2b. in an ingress test, a bpf LWT_IN program is installed on veth2 36# that encaps the packets with an IP/GRE header to route to IPv*_GRE 37# 38# ping: SRC->[encap at veth2:ingress]->GRE:decap->DST 39# ping replies go DST->SRC directly 40 41if [[ $EUID -ne 0 ]]; then 42 echo "This script must be run as root" 43 echo "FAIL" 44 exit 1 45fi 46 47readonly NS1="ns1-$(mktemp -u XXXXXX)" 48readonly NS2="ns2-$(mktemp -u XXXXXX)" 49readonly NS3="ns3-$(mktemp -u XXXXXX)" 50 51readonly IPv4_1="172.16.1.100" 52readonly IPv4_2="172.16.2.100" 53readonly IPv4_3="172.16.3.100" 54readonly IPv4_4="172.16.4.100" 55readonly IPv4_5="172.16.5.100" 56readonly IPv4_6="172.16.6.100" 57readonly IPv4_7="172.16.7.100" 58readonly IPv4_8="172.16.8.100" 59readonly IPv4_GRE="172.16.16.100" 60 61readonly IPv4_SRC=$IPv4_1 62readonly IPv4_DST=$IPv4_4 63 64readonly IPv6_1="fb01::1" 65readonly IPv6_2="fb02::1" 66readonly IPv6_3="fb03::1" 67readonly IPv6_4="fb04::1" 68readonly IPv6_5="fb05::1" 69readonly IPv6_6="fb06::1" 70readonly IPv6_7="fb07::1" 71readonly IPv6_8="fb08::1" 72readonly IPv6_GRE="fb10::1" 73 74readonly IPv6_SRC=$IPv6_1 75readonly IPv6_DST=$IPv6_4 76 77TEST_STATUS=0 78TESTS_SUCCEEDED=0 79TESTS_FAILED=0 80 81TMPFILE="" 82 83process_test_results() 84{ 85 if [[ "${TEST_STATUS}" -eq 0 ]] ; then 86 echo "PASS" 87 TESTS_SUCCEEDED=$((TESTS_SUCCEEDED+1)) 88 else 89 echo "FAIL" 90 TESTS_FAILED=$((TESTS_FAILED+1)) 91 fi 92} 93 94print_test_summary_and_exit() 95{ 96 echo "passed tests: ${TESTS_SUCCEEDED}" 97 echo "failed tests: ${TESTS_FAILED}" 98 if [ "${TESTS_FAILED}" -eq "0" ] ; then 99 exit 0 100 else 101 exit 1 102 fi 103} 104 105setup() 106{ 107 set -e # exit on error 108 TEST_STATUS=0 109 110 # create devices and namespaces 111 ip netns add "${NS1}" 112 ip netns add "${NS2}" 113 ip netns add "${NS3}" 114 115 ip link add veth1 type veth peer name veth2 116 ip link add veth3 type veth peer name veth4 117 ip link add veth5 type veth peer name veth6 118 ip link add veth7 type veth peer name veth8 119 120 ip netns exec ${NS2} sysctl -wq net.ipv4.ip_forward=1 121 ip netns exec ${NS2} sysctl -wq net.ipv6.conf.all.forwarding=1 122 123 ip link set veth1 netns ${NS1} 124 ip link set veth2 netns ${NS2} 125 ip link set veth3 netns ${NS2} 126 ip link set veth4 netns ${NS3} 127 ip link set veth5 netns ${NS1} 128 ip link set veth6 netns ${NS2} 129 ip link set veth7 netns ${NS2} 130 ip link set veth8 netns ${NS3} 131 132 if [ ! -z "${VRF}" ] ; then 133 ip -netns ${NS1} link add red type vrf table 1001 134 ip -netns ${NS1} link set red up 135 ip -netns ${NS1} route add table 1001 unreachable default metric 8192 136 ip -netns ${NS1} -6 route add table 1001 unreachable default metric 8192 137 ip -netns ${NS1} link set veth1 vrf red 138 ip -netns ${NS1} link set veth5 vrf red 139 140 ip -netns ${NS2} link add red type vrf table 1001 141 ip -netns ${NS2} link set red up 142 ip -netns ${NS2} route add table 1001 unreachable default metric 8192 143 ip -netns ${NS2} -6 route add table 1001 unreachable default metric 8192 144 ip -netns ${NS2} link set veth2 vrf red 145 ip -netns ${NS2} link set veth3 vrf red 146 ip -netns ${NS2} link set veth6 vrf red 147 ip -netns ${NS2} link set veth7 vrf red 148 fi 149 150 # configure addesses: the top route (1-2-3-4) 151 ip -netns ${NS1} addr add ${IPv4_1}/24 dev veth1 152 ip -netns ${NS2} addr add ${IPv4_2}/24 dev veth2 153 ip -netns ${NS2} addr add ${IPv4_3}/24 dev veth3 154 ip -netns ${NS3} addr add ${IPv4_4}/24 dev veth4 155 ip -netns ${NS1} -6 addr add ${IPv6_1}/128 nodad dev veth1 156 ip -netns ${NS2} -6 addr add ${IPv6_2}/128 nodad dev veth2 157 ip -netns ${NS2} -6 addr add ${IPv6_3}/128 nodad dev veth3 158 ip -netns ${NS3} -6 addr add ${IPv6_4}/128 nodad dev veth4 159 160 # configure addresses: the bottom route (5-6-7-8) 161 ip -netns ${NS1} addr add ${IPv4_5}/24 dev veth5 162 ip -netns ${NS2} addr add ${IPv4_6}/24 dev veth6 163 ip -netns ${NS2} addr add ${IPv4_7}/24 dev veth7 164 ip -netns ${NS3} addr add ${IPv4_8}/24 dev veth8 165 ip -netns ${NS1} -6 addr add ${IPv6_5}/128 nodad dev veth5 166 ip -netns ${NS2} -6 addr add ${IPv6_6}/128 nodad dev veth6 167 ip -netns ${NS2} -6 addr add ${IPv6_7}/128 nodad dev veth7 168 ip -netns ${NS3} -6 addr add ${IPv6_8}/128 nodad dev veth8 169 170 ip -netns ${NS1} link set dev veth1 up 171 ip -netns ${NS2} link set dev veth2 up 172 ip -netns ${NS2} link set dev veth3 up 173 ip -netns ${NS3} link set dev veth4 up 174 ip -netns ${NS1} link set dev veth5 up 175 ip -netns ${NS2} link set dev veth6 up 176 ip -netns ${NS2} link set dev veth7 up 177 ip -netns ${NS3} link set dev veth8 up 178 179 # configure routes: IP*_SRC -> veth1/IP*_2 (= top route) default; 180 # the bottom route to specific bottom addresses 181 182 # NS1 183 # top route 184 ip -netns ${NS1} route add ${IPv4_2}/32 dev veth1 ${VRF} 185 ip -netns ${NS1} route add default dev veth1 via ${IPv4_2} ${VRF} # go top by default 186 ip -netns ${NS1} -6 route add ${IPv6_2}/128 dev veth1 ${VRF} 187 ip -netns ${NS1} -6 route add default dev veth1 via ${IPv6_2} ${VRF} # go top by default 188 # bottom route 189 ip -netns ${NS1} route add ${IPv4_6}/32 dev veth5 ${VRF} 190 ip -netns ${NS1} route add ${IPv4_7}/32 dev veth5 via ${IPv4_6} ${VRF} 191 ip -netns ${NS1} route add ${IPv4_8}/32 dev veth5 via ${IPv4_6} ${VRF} 192 ip -netns ${NS1} -6 route add ${IPv6_6}/128 dev veth5 ${VRF} 193 ip -netns ${NS1} -6 route add ${IPv6_7}/128 dev veth5 via ${IPv6_6} ${VRF} 194 ip -netns ${NS1} -6 route add ${IPv6_8}/128 dev veth5 via ${IPv6_6} ${VRF} 195 196 # NS2 197 # top route 198 ip -netns ${NS2} route add ${IPv4_1}/32 dev veth2 ${VRF} 199 ip -netns ${NS2} route add ${IPv4_4}/32 dev veth3 ${VRF} 200 ip -netns ${NS2} -6 route add ${IPv6_1}/128 dev veth2 ${VRF} 201 ip -netns ${NS2} -6 route add ${IPv6_4}/128 dev veth3 ${VRF} 202 # bottom route 203 ip -netns ${NS2} route add ${IPv4_5}/32 dev veth6 ${VRF} 204 ip -netns ${NS2} route add ${IPv4_8}/32 dev veth7 ${VRF} 205 ip -netns ${NS2} -6 route add ${IPv6_5}/128 dev veth6 ${VRF} 206 ip -netns ${NS2} -6 route add ${IPv6_8}/128 dev veth7 ${VRF} 207 208 # NS3 209 # top route 210 ip -netns ${NS3} route add ${IPv4_3}/32 dev veth4 211 ip -netns ${NS3} route add ${IPv4_1}/32 dev veth4 via ${IPv4_3} 212 ip -netns ${NS3} route add ${IPv4_2}/32 dev veth4 via ${IPv4_3} 213 ip -netns ${NS3} -6 route add ${IPv6_3}/128 dev veth4 214 ip -netns ${NS3} -6 route add ${IPv6_1}/128 dev veth4 via ${IPv6_3} 215 ip -netns ${NS3} -6 route add ${IPv6_2}/128 dev veth4 via ${IPv6_3} 216 # bottom route 217 ip -netns ${NS3} route add ${IPv4_7}/32 dev veth8 218 ip -netns ${NS3} route add ${IPv4_5}/32 dev veth8 via ${IPv4_7} 219 ip -netns ${NS3} route add ${IPv4_6}/32 dev veth8 via ${IPv4_7} 220 ip -netns ${NS3} -6 route add ${IPv6_7}/128 dev veth8 221 ip -netns ${NS3} -6 route add ${IPv6_5}/128 dev veth8 via ${IPv6_7} 222 ip -netns ${NS3} -6 route add ${IPv6_6}/128 dev veth8 via ${IPv6_7} 223 224 # configure IPv4 GRE device in NS3, and a route to it via the "bottom" route 225 ip -netns ${NS3} tunnel add gre_dev mode gre remote ${IPv4_1} local ${IPv4_GRE} ttl 255 226 ip -netns ${NS3} link set gre_dev up 227 ip -netns ${NS3} addr add ${IPv4_GRE} dev gre_dev 228 ip -netns ${NS1} route add ${IPv4_GRE}/32 dev veth5 via ${IPv4_6} ${VRF} 229 ip -netns ${NS2} route add ${IPv4_GRE}/32 dev veth7 via ${IPv4_8} ${VRF} 230 231 232 # configure IPv6 GRE device in NS3, and a route to it via the "bottom" route 233 ip -netns ${NS3} -6 tunnel add name gre6_dev mode ip6gre remote ${IPv6_1} local ${IPv6_GRE} ttl 255 234 ip -netns ${NS3} link set gre6_dev up 235 ip -netns ${NS3} -6 addr add ${IPv6_GRE} nodad dev gre6_dev 236 ip -netns ${NS1} -6 route add ${IPv6_GRE}/128 dev veth5 via ${IPv6_6} ${VRF} 237 ip -netns ${NS2} -6 route add ${IPv6_GRE}/128 dev veth7 via ${IPv6_8} ${VRF} 238 239 # rp_filter gets confused by what these tests are doing, so disable it 240 ip netns exec ${NS1} sysctl -wq net.ipv4.conf.all.rp_filter=0 241 ip netns exec ${NS2} sysctl -wq net.ipv4.conf.all.rp_filter=0 242 ip netns exec ${NS3} sysctl -wq net.ipv4.conf.all.rp_filter=0 243 244 TMPFILE=$(mktemp /tmp/test_lwt_ip_encap.XXXXXX) 245 246 sleep 1 # reduce flakiness 247 set +e 248} 249 250cleanup() 251{ 252 if [ -f ${TMPFILE} ] ; then 253 rm ${TMPFILE} 254 fi 255 256 ip netns del ${NS1} 2> /dev/null 257 ip netns del ${NS2} 2> /dev/null 258 ip netns del ${NS3} 2> /dev/null 259} 260 261trap cleanup EXIT 262 263remove_routes_to_gredev() 264{ 265 ip -netns ${NS1} route del ${IPv4_GRE} dev veth5 ${VRF} 266 ip -netns ${NS2} route del ${IPv4_GRE} dev veth7 ${VRF} 267 ip -netns ${NS1} -6 route del ${IPv6_GRE}/128 dev veth5 ${VRF} 268 ip -netns ${NS2} -6 route del ${IPv6_GRE}/128 dev veth7 ${VRF} 269} 270 271add_unreachable_routes_to_gredev() 272{ 273 ip -netns ${NS1} route add unreachable ${IPv4_GRE}/32 ${VRF} 274 ip -netns ${NS2} route add unreachable ${IPv4_GRE}/32 ${VRF} 275 ip -netns ${NS1} -6 route add unreachable ${IPv6_GRE}/128 ${VRF} 276 ip -netns ${NS2} -6 route add unreachable ${IPv6_GRE}/128 ${VRF} 277} 278 279test_ping() 280{ 281 local readonly PROTO=$1 282 local readonly EXPECTED=$2 283 local RET=0 284 285 if [ "${PROTO}" == "IPv4" ] ; then 286 ip netns exec ${NS1} ping -c 1 -W 1 -I veth1 ${IPv4_DST} 2>&1 > /dev/null 287 RET=$? 288 elif [ "${PROTO}" == "IPv6" ] ; then 289 ip netns exec ${NS1} ping6 -c 1 -W 6 -I veth1 ${IPv6_DST} 2>&1 > /dev/null 290 RET=$? 291 else 292 echo " test_ping: unknown PROTO: ${PROTO}" 293 TEST_STATUS=1 294 fi 295 296 if [ "0" != "${RET}" ]; then 297 RET=1 298 fi 299 300 if [ "${EXPECTED}" != "${RET}" ] ; then 301 echo " test_ping failed: expected: ${EXPECTED}; got ${RET}" 302 TEST_STATUS=1 303 fi 304} 305 306test_gso() 307{ 308 local readonly PROTO=$1 309 local readonly PKT_SZ=5000 310 local IP_DST="" 311 : > ${TMPFILE} # trim the capture file 312 313 # check that nc is present 314 command -v nc >/dev/null 2>&1 || \ 315 { echo >&2 "nc is not available: skipping TSO tests"; return; } 316 317 # listen on port 9000, capture TCP into $TMPFILE 318 if [ "${PROTO}" == "IPv4" ] ; then 319 IP_DST=${IPv4_DST} 320 ip netns exec ${NS3} bash -c \ 321 "nc -4 -l -p 9000 > ${TMPFILE} &" 322 elif [ "${PROTO}" == "IPv6" ] ; then 323 IP_DST=${IPv6_DST} 324 ip netns exec ${NS3} bash -c \ 325 "nc -6 -l -p 9000 > ${TMPFILE} &" 326 RET=$? 327 else 328 echo " test_gso: unknown PROTO: ${PROTO}" 329 TEST_STATUS=1 330 fi 331 sleep 1 # let nc start listening 332 333 # send a packet larger than MTU 334 ip netns exec ${NS1} bash -c \ 335 "dd if=/dev/zero bs=$PKT_SZ count=1 > /dev/tcp/${IP_DST}/9000 2>/dev/null" 336 sleep 2 # let the packet get delivered 337 338 # verify we received all expected bytes 339 SZ=$(stat -c %s ${TMPFILE}) 340 if [ "$SZ" != "$PKT_SZ" ] ; then 341 echo " test_gso failed: ${PROTO}" 342 TEST_STATUS=1 343 fi 344} 345 346test_egress() 347{ 348 local readonly ENCAP=$1 349 echo "starting egress ${ENCAP} encap test ${VRF}" 350 setup 351 352 # by default, pings work 353 test_ping IPv4 0 354 test_ping IPv6 0 355 356 # remove NS2->DST routes, ping fails 357 ip -netns ${NS2} route del ${IPv4_DST}/32 dev veth3 ${VRF} 358 ip -netns ${NS2} -6 route del ${IPv6_DST}/128 dev veth3 ${VRF} 359 test_ping IPv4 1 360 test_ping IPv6 1 361 362 # install replacement routes (LWT/eBPF), pings succeed 363 if [ "${ENCAP}" == "IPv4" ] ; then 364 ip -netns ${NS1} route add ${IPv4_DST} encap bpf xmit obj \ 365 test_lwt_ip_encap.o sec encap_gre dev veth1 ${VRF} 366 ip -netns ${NS1} -6 route add ${IPv6_DST} encap bpf xmit obj \ 367 test_lwt_ip_encap.o sec encap_gre dev veth1 ${VRF} 368 elif [ "${ENCAP}" == "IPv6" ] ; then 369 ip -netns ${NS1} route add ${IPv4_DST} encap bpf xmit obj \ 370 test_lwt_ip_encap.o sec encap_gre6 dev veth1 ${VRF} 371 ip -netns ${NS1} -6 route add ${IPv6_DST} encap bpf xmit obj \ 372 test_lwt_ip_encap.o sec encap_gre6 dev veth1 ${VRF} 373 else 374 echo " unknown encap ${ENCAP}" 375 TEST_STATUS=1 376 fi 377 test_ping IPv4 0 378 test_ping IPv6 0 379 380 # skip GSO tests with VRF: VRF routing needs properly assigned 381 # source IP/device, which is easy to do with ping and hard with dd/nc. 382 if [ -z "${VRF}" ] ; then 383 test_gso IPv4 384 test_gso IPv6 385 fi 386 387 # a negative test: remove routes to GRE devices: ping fails 388 remove_routes_to_gredev 389 test_ping IPv4 1 390 test_ping IPv6 1 391 392 # another negative test 393 add_unreachable_routes_to_gredev 394 test_ping IPv4 1 395 test_ping IPv6 1 396 397 cleanup 398 process_test_results 399} 400 401test_ingress() 402{ 403 local readonly ENCAP=$1 404 echo "starting ingress ${ENCAP} encap test ${VRF}" 405 setup 406 407 # need to wait a bit for IPv6 to autoconf, otherwise 408 # ping6 sometimes fails with "unable to bind to address" 409 410 # by default, pings work 411 test_ping IPv4 0 412 test_ping IPv6 0 413 414 # remove NS2->DST routes, pings fail 415 ip -netns ${NS2} route del ${IPv4_DST}/32 dev veth3 ${VRF} 416 ip -netns ${NS2} -6 route del ${IPv6_DST}/128 dev veth3 ${VRF} 417 test_ping IPv4 1 418 test_ping IPv6 1 419 420 # install replacement routes (LWT/eBPF), pings succeed 421 if [ "${ENCAP}" == "IPv4" ] ; then 422 ip -netns ${NS2} route add ${IPv4_DST} encap bpf in obj \ 423 test_lwt_ip_encap.o sec encap_gre dev veth2 ${VRF} 424 ip -netns ${NS2} -6 route add ${IPv6_DST} encap bpf in obj \ 425 test_lwt_ip_encap.o sec encap_gre dev veth2 ${VRF} 426 elif [ "${ENCAP}" == "IPv6" ] ; then 427 ip -netns ${NS2} route add ${IPv4_DST} encap bpf in obj \ 428 test_lwt_ip_encap.o sec encap_gre6 dev veth2 ${VRF} 429 ip -netns ${NS2} -6 route add ${IPv6_DST} encap bpf in obj \ 430 test_lwt_ip_encap.o sec encap_gre6 dev veth2 ${VRF} 431 else 432 echo "FAIL: unknown encap ${ENCAP}" 433 TEST_STATUS=1 434 fi 435 test_ping IPv4 0 436 test_ping IPv6 0 437 438 # a negative test: remove routes to GRE devices: ping fails 439 remove_routes_to_gredev 440 test_ping IPv4 1 441 test_ping IPv6 1 442 443 # another negative test 444 add_unreachable_routes_to_gredev 445 test_ping IPv4 1 446 test_ping IPv6 1 447 448 cleanup 449 process_test_results 450} 451 452VRF="" 453test_egress IPv4 454test_egress IPv6 455test_ingress IPv4 456test_ingress IPv6 457 458VRF="vrf red" 459test_egress IPv4 460test_egress IPv6 461test_ingress IPv4 462test_ingress IPv6 463 464print_test_summary_and_exit 465