477 lines · bash
1#!/bin/bash2# SPDX-License-Identifier: GPL-2.03#4# Setup/topology:5#6# NS1 NS2 NS37# veth1 <---> veth2 veth3 <---> veth4 (the top route)8# veth5 <---> veth6 veth7 <---> veth8 (the bottom route)9#10# each vethN gets IPv[4|6]_N address11#12# IPv*_SRC = IPv*_113# IPv*_DST = IPv*_414#15# all tests test pings from IPv*_SRC to IPv*_DST16#17# by default, routes are configured to allow packets to go18# IP*_1 <=> IP*_2 <=> IP*_3 <=> IP*_4 (the top route)19#20# a GRE device is installed in NS3 with IPv*_GRE, and21# NS1/NS2 are configured to route packets to IPv*_GRE via IP*_822# (the bottom route)23#24# Tests:25#26# 1. routes NS2->IPv*_DST are brought down, so the only way a ping27# from IP*_SRC to IP*_DST can work is via IPv*_GRE28#29# 2a. in an egress test, a bpf LWT_XMIT program is installed on veth130# that encaps the packets with an IP/GRE header to route to IPv*_GRE31#32# ping: SRC->[encap at veth1:egress]->GRE:decap->DST33# ping replies go DST->SRC directly34#35# 2b. in an ingress test, a bpf LWT_IN program is installed on veth236# that encaps the packets with an IP/GRE header to route to IPv*_GRE37#38# ping: SRC->[encap at veth2:ingress]->GRE:decap->DST39# ping replies go DST->SRC directly40 41BPF_FILE="test_lwt_ip_encap.bpf.o"42if [[ $EUID -ne 0 ]]; then43 echo "This script must be run as root"44 echo "FAIL"45 exit 146fi47 48readonly NS1="ns1-$(mktemp -u XXXXXX)"49readonly NS2="ns2-$(mktemp -u XXXXXX)"50readonly NS3="ns3-$(mktemp -u XXXXXX)"51 52readonly IPv4_1="172.16.1.100"53readonly IPv4_2="172.16.2.100"54readonly IPv4_3="172.16.3.100"55readonly IPv4_4="172.16.4.100"56readonly IPv4_5="172.16.5.100"57readonly IPv4_6="172.16.6.100"58readonly IPv4_7="172.16.7.100"59readonly IPv4_8="172.16.8.100"60readonly IPv4_GRE="172.16.16.100"61 62readonly IPv4_SRC=$IPv4_163readonly IPv4_DST=$IPv4_464 65readonly IPv6_1="fb01::1"66readonly IPv6_2="fb02::1"67readonly IPv6_3="fb03::1"68readonly IPv6_4="fb04::1"69readonly IPv6_5="fb05::1"70readonly IPv6_6="fb06::1"71readonly IPv6_7="fb07::1"72readonly IPv6_8="fb08::1"73readonly IPv6_GRE="fb10::1"74 75readonly IPv6_SRC=$IPv6_176readonly IPv6_DST=$IPv6_477 78TEST_STATUS=079TESTS_SUCCEEDED=080TESTS_FAILED=081 82TMPFILE=""83 84process_test_results()85{86 if [[ "${TEST_STATUS}" -eq 0 ]] ; then87 echo "PASS"88 TESTS_SUCCEEDED=$((TESTS_SUCCEEDED+1))89 else90 echo "FAIL"91 TESTS_FAILED=$((TESTS_FAILED+1))92 fi93}94 95print_test_summary_and_exit()96{97 echo "passed tests: ${TESTS_SUCCEEDED}"98 echo "failed tests: ${TESTS_FAILED}"99 if [ "${TESTS_FAILED}" -eq "0" ] ; then100 exit 0101 else102 exit 1103 fi104}105 106setup()107{108 set -e # exit on error109 TEST_STATUS=0110 111 # create devices and namespaces112 ip netns add "${NS1}"113 ip netns add "${NS2}"114 ip netns add "${NS3}"115 116 # rp_filter gets confused by what these tests are doing, so disable it117 ip netns exec ${NS1} sysctl -wq net.ipv4.conf.all.rp_filter=0118 ip netns exec ${NS2} sysctl -wq net.ipv4.conf.all.rp_filter=0119 ip netns exec ${NS3} sysctl -wq net.ipv4.conf.all.rp_filter=0120 ip netns exec ${NS1} sysctl -wq net.ipv4.conf.default.rp_filter=0121 ip netns exec ${NS2} sysctl -wq net.ipv4.conf.default.rp_filter=0122 ip netns exec ${NS3} sysctl -wq net.ipv4.conf.default.rp_filter=0123 124 # disable IPv6 DAD because it sometimes takes too long and fails tests125 ip netns exec ${NS1} sysctl -wq net.ipv6.conf.all.accept_dad=0126 ip netns exec ${NS2} sysctl -wq net.ipv6.conf.all.accept_dad=0127 ip netns exec ${NS3} sysctl -wq net.ipv6.conf.all.accept_dad=0128 ip netns exec ${NS1} sysctl -wq net.ipv6.conf.default.accept_dad=0129 ip netns exec ${NS2} sysctl -wq net.ipv6.conf.default.accept_dad=0130 ip netns exec ${NS3} sysctl -wq net.ipv6.conf.default.accept_dad=0131 132 ip link add veth1 type veth peer name veth2133 ip link add veth3 type veth peer name veth4134 ip link add veth5 type veth peer name veth6135 ip link add veth7 type veth peer name veth8136 137 ip netns exec ${NS2} sysctl -wq net.ipv4.ip_forward=1138 ip netns exec ${NS2} sysctl -wq net.ipv6.conf.all.forwarding=1139 140 ip link set veth1 netns ${NS1}141 ip link set veth2 netns ${NS2}142 ip link set veth3 netns ${NS2}143 ip link set veth4 netns ${NS3}144 ip link set veth5 netns ${NS1}145 ip link set veth6 netns ${NS2}146 ip link set veth7 netns ${NS2}147 ip link set veth8 netns ${NS3}148 149 if [ ! -z "${VRF}" ] ; then150 ip -netns ${NS1} link add red type vrf table 1001151 ip -netns ${NS1} link set red up152 ip -netns ${NS1} route add table 1001 unreachable default metric 8192153 ip -netns ${NS1} -6 route add table 1001 unreachable default metric 8192154 ip -netns ${NS1} link set veth1 vrf red155 ip -netns ${NS1} link set veth5 vrf red156 157 ip -netns ${NS2} link add red type vrf table 1001158 ip -netns ${NS2} link set red up159 ip -netns ${NS2} route add table 1001 unreachable default metric 8192160 ip -netns ${NS2} -6 route add table 1001 unreachable default metric 8192161 ip -netns ${NS2} link set veth2 vrf red162 ip -netns ${NS2} link set veth3 vrf red163 ip -netns ${NS2} link set veth6 vrf red164 ip -netns ${NS2} link set veth7 vrf red165 fi166 167 # configure addesses: the top route (1-2-3-4)168 ip -netns ${NS1} addr add ${IPv4_1}/24 dev veth1169 ip -netns ${NS2} addr add ${IPv4_2}/24 dev veth2170 ip -netns ${NS2} addr add ${IPv4_3}/24 dev veth3171 ip -netns ${NS3} addr add ${IPv4_4}/24 dev veth4172 ip -netns ${NS1} -6 addr add ${IPv6_1}/128 nodad dev veth1173 ip -netns ${NS2} -6 addr add ${IPv6_2}/128 nodad dev veth2174 ip -netns ${NS2} -6 addr add ${IPv6_3}/128 nodad dev veth3175 ip -netns ${NS3} -6 addr add ${IPv6_4}/128 nodad dev veth4176 177 # configure addresses: the bottom route (5-6-7-8)178 ip -netns ${NS1} addr add ${IPv4_5}/24 dev veth5179 ip -netns ${NS2} addr add ${IPv4_6}/24 dev veth6180 ip -netns ${NS2} addr add ${IPv4_7}/24 dev veth7181 ip -netns ${NS3} addr add ${IPv4_8}/24 dev veth8182 ip -netns ${NS1} -6 addr add ${IPv6_5}/128 nodad dev veth5183 ip -netns ${NS2} -6 addr add ${IPv6_6}/128 nodad dev veth6184 ip -netns ${NS2} -6 addr add ${IPv6_7}/128 nodad dev veth7185 ip -netns ${NS3} -6 addr add ${IPv6_8}/128 nodad dev veth8186 187 ip -netns ${NS1} link set dev veth1 up188 ip -netns ${NS2} link set dev veth2 up189 ip -netns ${NS2} link set dev veth3 up190 ip -netns ${NS3} link set dev veth4 up191 ip -netns ${NS1} link set dev veth5 up192 ip -netns ${NS2} link set dev veth6 up193 ip -netns ${NS2} link set dev veth7 up194 ip -netns ${NS3} link set dev veth8 up195 196 # configure routes: IP*_SRC -> veth1/IP*_2 (= top route) default;197 # the bottom route to specific bottom addresses198 199 # NS1200 # top route201 ip -netns ${NS1} route add ${IPv4_2}/32 dev veth1 ${VRF}202 ip -netns ${NS1} route add default dev veth1 via ${IPv4_2} ${VRF} # go top by default203 ip -netns ${NS1} -6 route add ${IPv6_2}/128 dev veth1 ${VRF}204 ip -netns ${NS1} -6 route add default dev veth1 via ${IPv6_2} ${VRF} # go top by default205 # bottom route206 ip -netns ${NS1} route add ${IPv4_6}/32 dev veth5 ${VRF}207 ip -netns ${NS1} route add ${IPv4_7}/32 dev veth5 via ${IPv4_6} ${VRF}208 ip -netns ${NS1} route add ${IPv4_8}/32 dev veth5 via ${IPv4_6} ${VRF}209 ip -netns ${NS1} -6 route add ${IPv6_6}/128 dev veth5 ${VRF}210 ip -netns ${NS1} -6 route add ${IPv6_7}/128 dev veth5 via ${IPv6_6} ${VRF}211 ip -netns ${NS1} -6 route add ${IPv6_8}/128 dev veth5 via ${IPv6_6} ${VRF}212 213 # NS2214 # top route215 ip -netns ${NS2} route add ${IPv4_1}/32 dev veth2 ${VRF}216 ip -netns ${NS2} route add ${IPv4_4}/32 dev veth3 ${VRF}217 ip -netns ${NS2} -6 route add ${IPv6_1}/128 dev veth2 ${VRF}218 ip -netns ${NS2} -6 route add ${IPv6_4}/128 dev veth3 ${VRF}219 # bottom route220 ip -netns ${NS2} route add ${IPv4_5}/32 dev veth6 ${VRF}221 ip -netns ${NS2} route add ${IPv4_8}/32 dev veth7 ${VRF}222 ip -netns ${NS2} -6 route add ${IPv6_5}/128 dev veth6 ${VRF}223 ip -netns ${NS2} -6 route add ${IPv6_8}/128 dev veth7 ${VRF}224 225 # NS3226 # top route227 ip -netns ${NS3} route add ${IPv4_3}/32 dev veth4228 ip -netns ${NS3} route add ${IPv4_1}/32 dev veth4 via ${IPv4_3}229 ip -netns ${NS3} route add ${IPv4_2}/32 dev veth4 via ${IPv4_3}230 ip -netns ${NS3} -6 route add ${IPv6_3}/128 dev veth4231 ip -netns ${NS3} -6 route add ${IPv6_1}/128 dev veth4 via ${IPv6_3}232 ip -netns ${NS3} -6 route add ${IPv6_2}/128 dev veth4 via ${IPv6_3}233 # bottom route234 ip -netns ${NS3} route add ${IPv4_7}/32 dev veth8235 ip -netns ${NS3} route add ${IPv4_5}/32 dev veth8 via ${IPv4_7}236 ip -netns ${NS3} route add ${IPv4_6}/32 dev veth8 via ${IPv4_7}237 ip -netns ${NS3} -6 route add ${IPv6_7}/128 dev veth8238 ip -netns ${NS3} -6 route add ${IPv6_5}/128 dev veth8 via ${IPv6_7}239 ip -netns ${NS3} -6 route add ${IPv6_6}/128 dev veth8 via ${IPv6_7}240 241 # configure IPv4 GRE device in NS3, and a route to it via the "bottom" route242 ip -netns ${NS3} tunnel add gre_dev mode gre remote ${IPv4_1} local ${IPv4_GRE} ttl 255243 ip -netns ${NS3} link set gre_dev up244 ip -netns ${NS3} addr add ${IPv4_GRE} dev gre_dev245 ip -netns ${NS1} route add ${IPv4_GRE}/32 dev veth5 via ${IPv4_6} ${VRF}246 ip -netns ${NS2} route add ${IPv4_GRE}/32 dev veth7 via ${IPv4_8} ${VRF}247 248 249 # configure IPv6 GRE device in NS3, and a route to it via the "bottom" route250 ip -netns ${NS3} -6 tunnel add name gre6_dev mode ip6gre remote ${IPv6_1} local ${IPv6_GRE} ttl 255251 ip -netns ${NS3} link set gre6_dev up252 ip -netns ${NS3} -6 addr add ${IPv6_GRE} nodad dev gre6_dev253 ip -netns ${NS1} -6 route add ${IPv6_GRE}/128 dev veth5 via ${IPv6_6} ${VRF}254 ip -netns ${NS2} -6 route add ${IPv6_GRE}/128 dev veth7 via ${IPv6_8} ${VRF}255 256 TMPFILE=$(mktemp /tmp/test_lwt_ip_encap.XXXXXX)257 258 sleep 1 # reduce flakiness259 set +e260}261 262cleanup()263{264 if [ -f ${TMPFILE} ] ; then265 rm ${TMPFILE}266 fi267 268 ip netns del ${NS1} 2> /dev/null269 ip netns del ${NS2} 2> /dev/null270 ip netns del ${NS3} 2> /dev/null271}272 273trap cleanup EXIT274 275remove_routes_to_gredev()276{277 ip -netns ${NS1} route del ${IPv4_GRE} dev veth5 ${VRF}278 ip -netns ${NS2} route del ${IPv4_GRE} dev veth7 ${VRF}279 ip -netns ${NS1} -6 route del ${IPv6_GRE}/128 dev veth5 ${VRF}280 ip -netns ${NS2} -6 route del ${IPv6_GRE}/128 dev veth7 ${VRF}281}282 283add_unreachable_routes_to_gredev()284{285 ip -netns ${NS1} route add unreachable ${IPv4_GRE}/32 ${VRF}286 ip -netns ${NS2} route add unreachable ${IPv4_GRE}/32 ${VRF}287 ip -netns ${NS1} -6 route add unreachable ${IPv6_GRE}/128 ${VRF}288 ip -netns ${NS2} -6 route add unreachable ${IPv6_GRE}/128 ${VRF}289}290 291test_ping()292{293 local readonly PROTO=$1294 local readonly EXPECTED=$2295 local RET=0296 297 if [ "${PROTO}" == "IPv4" ] ; then298 ip netns exec ${NS1} ping -c 1 -W 1 -I veth1 ${IPv4_DST} 2>&1 > /dev/null299 RET=$?300 elif [ "${PROTO}" == "IPv6" ] ; then301 ip netns exec ${NS1} ping6 -c 1 -W 1 -I veth1 ${IPv6_DST} 2>&1 > /dev/null302 RET=$?303 else304 echo " test_ping: unknown PROTO: ${PROTO}"305 TEST_STATUS=1306 fi307 308 if [ "0" != "${RET}" ]; then309 RET=1310 fi311 312 if [ "${EXPECTED}" != "${RET}" ] ; then313 echo " test_ping failed: expected: ${EXPECTED}; got ${RET}"314 TEST_STATUS=1315 fi316}317 318test_gso()319{320 local readonly PROTO=$1321 local readonly PKT_SZ=5000322 local IP_DST=""323 : > ${TMPFILE} # trim the capture file324 325 # check that nc is present326 command -v nc >/dev/null 2>&1 || \327 { echo >&2 "nc is not available: skipping TSO tests"; return; }328 329 # listen on port 9000, capture TCP into $TMPFILE330 if [ "${PROTO}" == "IPv4" ] ; then331 IP_DST=${IPv4_DST}332 ip netns exec ${NS3} bash -c \333 "nc -4 -l -p 9000 > ${TMPFILE} &"334 elif [ "${PROTO}" == "IPv6" ] ; then335 IP_DST=${IPv6_DST}336 ip netns exec ${NS3} bash -c \337 "nc -6 -l -p 9000 > ${TMPFILE} &"338 RET=$?339 else340 echo " test_gso: unknown PROTO: ${PROTO}"341 TEST_STATUS=1342 fi343 sleep 1 # let nc start listening344 345 # send a packet larger than MTU346 ip netns exec ${NS1} bash -c \347 "dd if=/dev/zero bs=$PKT_SZ count=1 > /dev/tcp/${IP_DST}/9000 2>/dev/null"348 sleep 2 # let the packet get delivered349 350 # verify we received all expected bytes351 SZ=$(stat -c %s ${TMPFILE})352 if [ "$SZ" != "$PKT_SZ" ] ; then353 echo " test_gso failed: ${PROTO}"354 TEST_STATUS=1355 fi356}357 358test_egress()359{360 local readonly ENCAP=$1361 echo "starting egress ${ENCAP} encap test ${VRF}"362 setup363 364 # by default, pings work365 test_ping IPv4 0366 test_ping IPv6 0367 368 # remove NS2->DST routes, ping fails369 ip -netns ${NS2} route del ${IPv4_DST}/32 dev veth3 ${VRF}370 ip -netns ${NS2} -6 route del ${IPv6_DST}/128 dev veth3 ${VRF}371 test_ping IPv4 1372 test_ping IPv6 1373 374 # install replacement routes (LWT/eBPF), pings succeed375 if [ "${ENCAP}" == "IPv4" ] ; then376 ip -netns ${NS1} route add ${IPv4_DST} encap bpf xmit obj \377 ${BPF_FILE} sec encap_gre dev veth1 ${VRF}378 ip -netns ${NS1} -6 route add ${IPv6_DST} encap bpf xmit obj \379 ${BPF_FILE} sec encap_gre dev veth1 ${VRF}380 elif [ "${ENCAP}" == "IPv6" ] ; then381 ip -netns ${NS1} route add ${IPv4_DST} encap bpf xmit obj \382 ${BPF_FILE} sec encap_gre6 dev veth1 ${VRF}383 ip -netns ${NS1} -6 route add ${IPv6_DST} encap bpf xmit obj \384 ${BPF_FILE} sec encap_gre6 dev veth1 ${VRF}385 else386 echo " unknown encap ${ENCAP}"387 TEST_STATUS=1388 fi389 test_ping IPv4 0390 test_ping IPv6 0391 392 # skip GSO tests with VRF: VRF routing needs properly assigned393 # source IP/device, which is easy to do with ping and hard with dd/nc.394 if [ -z "${VRF}" ] ; then395 test_gso IPv4396 test_gso IPv6397 fi398 399 # a negative test: remove routes to GRE devices: ping fails400 remove_routes_to_gredev401 test_ping IPv4 1402 test_ping IPv6 1403 404 # another negative test405 add_unreachable_routes_to_gredev406 test_ping IPv4 1407 test_ping IPv6 1408 409 cleanup410 process_test_results411}412 413test_ingress()414{415 local readonly ENCAP=$1416 echo "starting ingress ${ENCAP} encap test ${VRF}"417 setup418 419 # need to wait a bit for IPv6 to autoconf, otherwise420 # ping6 sometimes fails with "unable to bind to address"421 422 # by default, pings work423 test_ping IPv4 0424 test_ping IPv6 0425 426 # remove NS2->DST routes, pings fail427 ip -netns ${NS2} route del ${IPv4_DST}/32 dev veth3 ${VRF}428 ip -netns ${NS2} -6 route del ${IPv6_DST}/128 dev veth3 ${VRF}429 test_ping IPv4 1430 test_ping IPv6 1431 432 # install replacement routes (LWT/eBPF), pings succeed433 if [ "${ENCAP}" == "IPv4" ] ; then434 ip -netns ${NS2} route add ${IPv4_DST} encap bpf in obj \435 ${BPF_FILE} sec encap_gre dev veth2 ${VRF}436 ip -netns ${NS2} -6 route add ${IPv6_DST} encap bpf in obj \437 ${BPF_FILE} sec encap_gre dev veth2 ${VRF}438 elif [ "${ENCAP}" == "IPv6" ] ; then439 ip -netns ${NS2} route add ${IPv4_DST} encap bpf in obj \440 ${BPF_FILE} sec encap_gre6 dev veth2 ${VRF}441 ip -netns ${NS2} -6 route add ${IPv6_DST} encap bpf in obj \442 ${BPF_FILE} sec encap_gre6 dev veth2 ${VRF}443 else444 echo "FAIL: unknown encap ${ENCAP}"445 TEST_STATUS=1446 fi447 test_ping IPv4 0448 test_ping IPv6 0449 450 # a negative test: remove routes to GRE devices: ping fails451 remove_routes_to_gredev452 test_ping IPv4 1453 test_ping IPv6 1454 455 # another negative test456 add_unreachable_routes_to_gredev457 test_ping IPv4 1458 test_ping IPv6 1459 460 cleanup461 process_test_results462}463 464VRF=""465test_egress IPv4466test_egress IPv6467test_ingress IPv4468test_ingress IPv6469 470VRF="vrf red"471test_egress IPv4472test_egress IPv6473test_ingress IPv4474test_ingress IPv6475 476print_test_summary_and_exit477