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 # configure addesses: the top route (1-2-3-4) 133 ip -netns ${NS1} addr add ${IPv4_1}/24 dev veth1 134 ip -netns ${NS2} addr add ${IPv4_2}/24 dev veth2 135 ip -netns ${NS2} addr add ${IPv4_3}/24 dev veth3 136 ip -netns ${NS3} addr add ${IPv4_4}/24 dev veth4 137 ip -netns ${NS1} -6 addr add ${IPv6_1}/128 nodad dev veth1 138 ip -netns ${NS2} -6 addr add ${IPv6_2}/128 nodad dev veth2 139 ip -netns ${NS2} -6 addr add ${IPv6_3}/128 nodad dev veth3 140 ip -netns ${NS3} -6 addr add ${IPv6_4}/128 nodad dev veth4 141 142 # configure addresses: the bottom route (5-6-7-8) 143 ip -netns ${NS1} addr add ${IPv4_5}/24 dev veth5 144 ip -netns ${NS2} addr add ${IPv4_6}/24 dev veth6 145 ip -netns ${NS2} addr add ${IPv4_7}/24 dev veth7 146 ip -netns ${NS3} addr add ${IPv4_8}/24 dev veth8 147 ip -netns ${NS1} -6 addr add ${IPv6_5}/128 nodad dev veth5 148 ip -netns ${NS2} -6 addr add ${IPv6_6}/128 nodad dev veth6 149 ip -netns ${NS2} -6 addr add ${IPv6_7}/128 nodad dev veth7 150 ip -netns ${NS3} -6 addr add ${IPv6_8}/128 nodad dev veth8 151 152 ip -netns ${NS1} link set dev veth1 up 153 ip -netns ${NS2} link set dev veth2 up 154 ip -netns ${NS2} link set dev veth3 up 155 ip -netns ${NS3} link set dev veth4 up 156 ip -netns ${NS1} link set dev veth5 up 157 ip -netns ${NS2} link set dev veth6 up 158 ip -netns ${NS2} link set dev veth7 up 159 ip -netns ${NS3} link set dev veth8 up 160 161 # configure routes: IP*_SRC -> veth1/IP*_2 (= top route) default; 162 # the bottom route to specific bottom addresses 163 164 # NS1 165 # top route 166 ip -netns ${NS1} route add ${IPv4_2}/32 dev veth1 167 ip -netns ${NS1} route add default dev veth1 via ${IPv4_2} # go top by default 168 ip -netns ${NS1} -6 route add ${IPv6_2}/128 dev veth1 169 ip -netns ${NS1} -6 route add default dev veth1 via ${IPv6_2} # go top by default 170 # bottom route 171 ip -netns ${NS1} route add ${IPv4_6}/32 dev veth5 172 ip -netns ${NS1} route add ${IPv4_7}/32 dev veth5 via ${IPv4_6} 173 ip -netns ${NS1} route add ${IPv4_8}/32 dev veth5 via ${IPv4_6} 174 ip -netns ${NS1} -6 route add ${IPv6_6}/128 dev veth5 175 ip -netns ${NS1} -6 route add ${IPv6_7}/128 dev veth5 via ${IPv6_6} 176 ip -netns ${NS1} -6 route add ${IPv6_8}/128 dev veth5 via ${IPv6_6} 177 178 # NS2 179 # top route 180 ip -netns ${NS2} route add ${IPv4_1}/32 dev veth2 181 ip -netns ${NS2} route add ${IPv4_4}/32 dev veth3 182 ip -netns ${NS2} -6 route add ${IPv6_1}/128 dev veth2 183 ip -netns ${NS2} -6 route add ${IPv6_4}/128 dev veth3 184 # bottom route 185 ip -netns ${NS2} route add ${IPv4_5}/32 dev veth6 186 ip -netns ${NS2} route add ${IPv4_8}/32 dev veth7 187 ip -netns ${NS2} -6 route add ${IPv6_5}/128 dev veth6 188 ip -netns ${NS2} -6 route add ${IPv6_8}/128 dev veth7 189 190 # NS3 191 # top route 192 ip -netns ${NS3} route add ${IPv4_3}/32 dev veth4 193 ip -netns ${NS3} route add ${IPv4_1}/32 dev veth4 via ${IPv4_3} 194 ip -netns ${NS3} route add ${IPv4_2}/32 dev veth4 via ${IPv4_3} 195 ip -netns ${NS3} -6 route add ${IPv6_3}/128 dev veth4 196 ip -netns ${NS3} -6 route add ${IPv6_1}/128 dev veth4 via ${IPv6_3} 197 ip -netns ${NS3} -6 route add ${IPv6_2}/128 dev veth4 via ${IPv6_3} 198 # bottom route 199 ip -netns ${NS3} route add ${IPv4_7}/32 dev veth8 200 ip -netns ${NS3} route add ${IPv4_5}/32 dev veth8 via ${IPv4_7} 201 ip -netns ${NS3} route add ${IPv4_6}/32 dev veth8 via ${IPv4_7} 202 ip -netns ${NS3} -6 route add ${IPv6_7}/128 dev veth8 203 ip -netns ${NS3} -6 route add ${IPv6_5}/128 dev veth8 via ${IPv6_7} 204 ip -netns ${NS3} -6 route add ${IPv6_6}/128 dev veth8 via ${IPv6_7} 205 206 # configure IPv4 GRE device in NS3, and a route to it via the "bottom" route 207 ip -netns ${NS3} tunnel add gre_dev mode gre remote ${IPv4_1} local ${IPv4_GRE} ttl 255 208 ip -netns ${NS3} link set gre_dev up 209 ip -netns ${NS3} addr add ${IPv4_GRE} dev gre_dev 210 ip -netns ${NS1} route add ${IPv4_GRE}/32 dev veth5 via ${IPv4_6} 211 ip -netns ${NS2} route add ${IPv4_GRE}/32 dev veth7 via ${IPv4_8} 212 213 214 # configure IPv6 GRE device in NS3, and a route to it via the "bottom" route 215 ip -netns ${NS3} -6 tunnel add name gre6_dev mode ip6gre remote ${IPv6_1} local ${IPv6_GRE} ttl 255 216 ip -netns ${NS3} link set gre6_dev up 217 ip -netns ${NS3} -6 addr add ${IPv6_GRE} nodad dev gre6_dev 218 ip -netns ${NS1} -6 route add ${IPv6_GRE}/128 dev veth5 via ${IPv6_6} 219 ip -netns ${NS2} -6 route add ${IPv6_GRE}/128 dev veth7 via ${IPv6_8} 220 221 # rp_filter gets confused by what these tests are doing, so disable it 222 ip netns exec ${NS1} sysctl -wq net.ipv4.conf.all.rp_filter=0 223 ip netns exec ${NS2} sysctl -wq net.ipv4.conf.all.rp_filter=0 224 ip netns exec ${NS3} sysctl -wq net.ipv4.conf.all.rp_filter=0 225 226 TMPFILE=$(mktemp /tmp/test_lwt_ip_encap.XXXXXX) 227 228 sleep 1 # reduce flakiness 229 set +e 230} 231 232cleanup() 233{ 234 if [ -f ${TMPFILE} ] ; then 235 rm ${TMPFILE} 236 fi 237 238 ip netns del ${NS1} 2> /dev/null 239 ip netns del ${NS2} 2> /dev/null 240 ip netns del ${NS3} 2> /dev/null 241} 242 243trap cleanup EXIT 244 245remove_routes_to_gredev() 246{ 247 ip -netns ${NS1} route del ${IPv4_GRE} dev veth5 248 ip -netns ${NS2} route del ${IPv4_GRE} dev veth7 249 ip -netns ${NS1} -6 route del ${IPv6_GRE}/128 dev veth5 250 ip -netns ${NS2} -6 route del ${IPv6_GRE}/128 dev veth7 251} 252 253add_unreachable_routes_to_gredev() 254{ 255 ip -netns ${NS1} route add unreachable ${IPv4_GRE}/32 256 ip -netns ${NS2} route add unreachable ${IPv4_GRE}/32 257 ip -netns ${NS1} -6 route add unreachable ${IPv6_GRE}/128 258 ip -netns ${NS2} -6 route add unreachable ${IPv6_GRE}/128 259} 260 261test_ping() 262{ 263 local readonly PROTO=$1 264 local readonly EXPECTED=$2 265 local RET=0 266 267 if [ "${PROTO}" == "IPv4" ] ; then 268 ip netns exec ${NS1} ping -c 1 -W 1 -I ${IPv4_SRC} ${IPv4_DST} 2>&1 > /dev/null 269 RET=$? 270 elif [ "${PROTO}" == "IPv6" ] ; then 271 ip netns exec ${NS1} ping6 -c 1 -W 6 -I ${IPv6_SRC} ${IPv6_DST} 2>&1 > /dev/null 272 RET=$? 273 else 274 echo " test_ping: unknown PROTO: ${PROTO}" 275 TEST_STATUS=1 276 fi 277 278 if [ "0" != "${RET}" ]; then 279 RET=1 280 fi 281 282 if [ "${EXPECTED}" != "${RET}" ] ; then 283 echo " test_ping failed: expected: ${EXPECTED}; got ${RET}" 284 TEST_STATUS=1 285 fi 286} 287 288test_gso() 289{ 290 local readonly PROTO=$1 291 local readonly PKT_SZ=5000 292 local IP_DST="" 293 : > ${TMPFILE} # trim the capture file 294 295 # check that nc is present 296 command -v nc >/dev/null 2>&1 || \ 297 { echo >&2 "nc is not available: skipping TSO tests"; return; } 298 299 # listen on IPv*_DST, capture TCP into $TMPFILE 300 if [ "${PROTO}" == "IPv4" ] ; then 301 IP_DST=${IPv4_DST} 302 ip netns exec ${NS3} bash -c \ 303 "nc -4 -l -s ${IPv4_DST} -p 9000 > ${TMPFILE} &" 304 elif [ "${PROTO}" == "IPv6" ] ; then 305 IP_DST=${IPv6_DST} 306 ip netns exec ${NS3} bash -c \ 307 "nc -6 -l -s ${IPv6_DST} -p 9000 > ${TMPFILE} &" 308 RET=$? 309 else 310 echo " test_gso: unknown PROTO: ${PROTO}" 311 TEST_STATUS=1 312 fi 313 sleep 1 # let nc start listening 314 315 # send a packet larger than MTU 316 ip netns exec ${NS1} bash -c \ 317 "dd if=/dev/zero bs=$PKT_SZ count=1 > /dev/tcp/${IP_DST}/9000 2>/dev/null" 318 sleep 2 # let the packet get delivered 319 320 # verify we received all expected bytes 321 SZ=$(stat -c %s ${TMPFILE}) 322 if [ "$SZ" != "$PKT_SZ" ] ; then 323 echo " test_gso failed: ${PROTO}" 324 TEST_STATUS=1 325 fi 326} 327 328test_egress() 329{ 330 local readonly ENCAP=$1 331 echo "starting egress ${ENCAP} encap test" 332 setup 333 334 # by default, pings work 335 test_ping IPv4 0 336 test_ping IPv6 0 337 338 # remove NS2->DST routes, ping fails 339 ip -netns ${NS2} route del ${IPv4_DST}/32 dev veth3 340 ip -netns ${NS2} -6 route del ${IPv6_DST}/128 dev veth3 341 test_ping IPv4 1 342 test_ping IPv6 1 343 344 # install replacement routes (LWT/eBPF), pings succeed 345 if [ "${ENCAP}" == "IPv4" ] ; then 346 ip -netns ${NS1} route add ${IPv4_DST} encap bpf xmit obj test_lwt_ip_encap.o sec encap_gre dev veth1 347 ip -netns ${NS1} -6 route add ${IPv6_DST} encap bpf xmit obj test_lwt_ip_encap.o sec encap_gre dev veth1 348 elif [ "${ENCAP}" == "IPv6" ] ; then 349 ip -netns ${NS1} route add ${IPv4_DST} encap bpf xmit obj test_lwt_ip_encap.o sec encap_gre6 dev veth1 350 ip -netns ${NS1} -6 route add ${IPv6_DST} encap bpf xmit obj test_lwt_ip_encap.o sec encap_gre6 dev veth1 351 else 352 echo " unknown encap ${ENCAP}" 353 TEST_STATUS=1 354 fi 355 test_ping IPv4 0 356 test_ping IPv6 0 357 test_gso IPv4 358 test_gso IPv6 359 360 # a negative test: remove routes to GRE devices: ping fails 361 remove_routes_to_gredev 362 test_ping IPv4 1 363 test_ping IPv6 1 364 365 # another negative test 366 add_unreachable_routes_to_gredev 367 test_ping IPv4 1 368 test_ping IPv6 1 369 370 cleanup 371 process_test_results 372} 373 374test_ingress() 375{ 376 local readonly ENCAP=$1 377 echo "starting ingress ${ENCAP} encap test" 378 setup 379 380 # need to wait a bit for IPv6 to autoconf, otherwise 381 # ping6 sometimes fails with "unable to bind to address" 382 383 # by default, pings work 384 test_ping IPv4 0 385 test_ping IPv6 0 386 387 # remove NS2->DST routes, pings fail 388 ip -netns ${NS2} route del ${IPv4_DST}/32 dev veth3 389 ip -netns ${NS2} -6 route del ${IPv6_DST}/128 dev veth3 390 test_ping IPv4 1 391 test_ping IPv6 1 392 393 # install replacement routes (LWT/eBPF), pings succeed 394 if [ "${ENCAP}" == "IPv4" ] ; then 395 ip -netns ${NS2} route add ${IPv4_DST} encap bpf in obj test_lwt_ip_encap.o sec encap_gre dev veth2 396 ip -netns ${NS2} -6 route add ${IPv6_DST} encap bpf in obj test_lwt_ip_encap.o sec encap_gre dev veth2 397 elif [ "${ENCAP}" == "IPv6" ] ; then 398 ip -netns ${NS2} route add ${IPv4_DST} encap bpf in obj test_lwt_ip_encap.o sec encap_gre6 dev veth2 399 ip -netns ${NS2} -6 route add ${IPv6_DST} encap bpf in obj test_lwt_ip_encap.o sec encap_gre6 dev veth2 400 else 401 echo "FAIL: unknown encap ${ENCAP}" 402 TEST_STATUS=1 403 fi 404 test_ping IPv4 0 405 test_ping IPv6 0 406 407 # a negative test: remove routes to GRE devices: ping fails 408 remove_routes_to_gredev 409 test_ping IPv4 1 410 test_ping IPv6 1 411 412 # another negative test 413 add_unreachable_routes_to_gredev 414 test_ping IPv4 1 415 test_ping IPv6 1 416 417 cleanup 418 process_test_results 419} 420 421test_egress IPv4 422test_egress IPv6 423test_ingress IPv4 424test_ingress IPv6 425 426print_test_summary_and_exit 427