|
| 1 | +#!/bin/bash |
| 2 | +# SPDX-License-Identifier: GPL-2.0 |
| 3 | +# |
| 4 | +# author: Jianguo Wu <[email protected]> |
| 5 | +# |
| 6 | +# Mostly copied from tools/testing/selftests/net/srv6_end_dt6_l3vpn_test.sh. |
| 7 | +# |
| 8 | +# This script is designed for testing the support of netfilter hooks for |
| 9 | +# SRv6 End.DX4 behavior. |
| 10 | +# |
| 11 | +# Hereafter a network diagram is shown, where one tenants (named 100) offer |
| 12 | +# IPv6 L3 VPN services allowing hosts to communicate with each other across |
| 13 | +# an IPv6 network. |
| 14 | +# |
| 15 | +# Routers rt-1 and rt-2 implement IPv6 L3 VPN services leveraging the SRv6 |
| 16 | +# architecture. The key components for such VPNs are: a) SRv6 Encap behavior, |
| 17 | +# b) SRv6 End.DX4 behavior. |
| 18 | +# |
| 19 | +# To explain how an IPv6 L3 VPN based on SRv6 works, let us briefly consider an |
| 20 | +# example where, within the same domain of tenant 100, the host hs-1 pings |
| 21 | +# the host hs-2. |
| 22 | +# |
| 23 | +# First of all, L2 reachability of the host hs-2 is taken into account by |
| 24 | +# the router rt-1 which acts as an arp proxy. |
| 25 | +# |
| 26 | +# When the host hs-1 sends an IPv6 packet destined to hs-2, the router rt-1 |
| 27 | +# receives the packet on the internal veth-t100 interface, rt-1 contains the |
| 28 | +# SRv6 Encap route for encapsulating the IPv6 packet in a IPv6 plus the Segment |
| 29 | +# Routing Header (SRH) packet. This packet is sent through the (IPv6) core |
| 30 | +# network up to the router rt-2 that receives it on veth0 interface. |
| 31 | +# |
| 32 | +# The rt-2 router uses the 'localsid' routing table to process incoming |
| 33 | +# IPv6+SRH packets which belong to the VPN of the tenant 100. For each of these |
| 34 | +# packets, the SRv6 End.DX4 behavior removes the outer IPv6+SRH headers and |
| 35 | +# routs the packet to the specified nexthop. Afterwards, the packet is sent to |
| 36 | +# the host hs-2 through the veth-t100 interface. |
| 37 | +# |
| 38 | +# The ping response follows the same processing but this time the role of rt-1 |
| 39 | +# and rt-2 are swapped. |
| 40 | +# |
| 41 | +# And when net.netfilter.nf_hooks_lwtunnel is set to 1 in rt-1 or rt-2, and a |
| 42 | +# rpfilter iptables rule is added, SRv6 packets will go through netfilter PREROUTING |
| 43 | +# hooks. |
| 44 | +# |
| 45 | +# |
| 46 | +# +-------------------+ +-------------------+ |
| 47 | +# | | | | |
| 48 | +# | hs-1 netns | | hs-2 netns | |
| 49 | +# | | | | |
| 50 | +# | +-------------+ | | +-------------+ | |
| 51 | +# | | veth0 | | | | veth0 | | |
| 52 | +# | | cafe::1/64 | | | | cafe::2/64 | | |
| 53 | +# | +-------------+ | | +-------------+ | |
| 54 | +# | . | | . | |
| 55 | +# +-------------------+ +-------------------+ |
| 56 | +# . . |
| 57 | +# . . |
| 58 | +# . . |
| 59 | +# +-----------------------------------+ +-----------------------------------+ |
| 60 | +# | . | | . | |
| 61 | +# | +---------------+ | | +---------------- | |
| 62 | +# | | veth-t100 | | | | veth-t100 | | |
| 63 | +# | | cafe::11/64 | +----------+ | | +----------+ | cafe::22/64 | | |
| 64 | +# | +-------+-------+ | route | | | | route | +-------+-------- | |
| 65 | +# | | table | | | | table | | |
| 66 | +# | +----------+ | | +----------+ | |
| 67 | +# | +--------------+ | | +--------------+ | |
| 68 | +# | | veth0 | | | | veth0 | | |
| 69 | +# | | 2001:11::1/64 |.|...|.| 2001:11::2/64 | | |
| 70 | +# | +--------------+ | | +--------------+ | |
| 71 | +# | | | | |
| 72 | +# | rt-1 netns | | rt-2 netns | |
| 73 | +# | | | | |
| 74 | +# +-----------------------------------+ +-----------------------------------+ |
| 75 | +# |
| 76 | +# ~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 77 | +# | Network configuration | |
| 78 | +# ~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 79 | +# |
| 80 | +# rt-1: localsid table |
| 81 | +# +----------------------------------------------------------------+ |
| 82 | +# |SID |Action | |
| 83 | +# +----------------------------------------------------------------+ |
| 84 | +# |fc00:21:100::6004|apply SRv6 End.DX6 nh6 cafe::1 dev veth-t100 | |
| 85 | +# +----------------------------------------------------------------+ |
| 86 | +# |
| 87 | +# rt-1: route table |
| 88 | +# +---------------------------------------------------+ |
| 89 | +# |host |Action | |
| 90 | +# +---------------------------------------------------+ |
| 91 | +# |cafe::2 |apply seg6 encap segs fc00:12:100::6004| |
| 92 | +# +---------------------------------------------------+ |
| 93 | +# |cafe::/64 |forward to dev veth_t100 | |
| 94 | +# +---------------------------------------------------+ |
| 95 | +# |
| 96 | +# |
| 97 | +# rt-2: localsid table |
| 98 | +# +---------------------------------------------------------------+ |
| 99 | +# |SID |Action | |
| 100 | +# +---------------------------------------------------------------+ |
| 101 | +# |fc00:12:100::6004|apply SRv6 End.DX6 nh6 cafe::2 dev veth-t100 | |
| 102 | +# +---------------------------------------------------------------+ |
| 103 | +# |
| 104 | +# rt-2: route table |
| 105 | +# +---------------------------------------------------+ |
| 106 | +# |host |Action | |
| 107 | +# +---------------------------------------------------+ |
| 108 | +# |cafe::1 |apply seg6 encap segs fc00:21:100::6004| |
| 109 | +# +---------------------------------------------------+ |
| 110 | +# |cafe::/64 |forward to dev veth_t100 | |
| 111 | +# +---------------------------------------------------+ |
| 112 | +# |
| 113 | + |
| 114 | +# Kselftest framework requirement - SKIP code is 4. |
| 115 | +ksft_skip=4 |
| 116 | + |
| 117 | +readonly IPv6_RT_NETWORK=2001:11 |
| 118 | +readonly IPv6_HS_NETWORK=cafe |
| 119 | +readonly SID_LOCATOR=fc00 |
| 120 | + |
| 121 | +PING_TIMEOUT_SEC=4 |
| 122 | + |
| 123 | +ret=0 |
| 124 | + |
| 125 | +PAUSE_ON_FAIL=${PAUSE_ON_FAIL:=no} |
| 126 | + |
| 127 | +log_test() |
| 128 | +{ |
| 129 | + local rc=$1 |
| 130 | + local expected=$2 |
| 131 | + local msg="$3" |
| 132 | + |
| 133 | + if [ ${rc} -eq ${expected} ]; then |
| 134 | + nsuccess=$((nsuccess+1)) |
| 135 | + printf "\n TEST: %-60s [ OK ]\n" "${msg}" |
| 136 | + else |
| 137 | + ret=1 |
| 138 | + nfail=$((nfail+1)) |
| 139 | + printf "\n TEST: %-60s [FAIL]\n" "${msg}" |
| 140 | + if [ "${PAUSE_ON_FAIL}" = "yes" ]; then |
| 141 | + echo |
| 142 | + echo "hit enter to continue, 'q' to quit" |
| 143 | + read a |
| 144 | + [ "$a" = "q" ] && exit 1 |
| 145 | + fi |
| 146 | + fi |
| 147 | +} |
| 148 | + |
| 149 | +print_log_test_results() |
| 150 | +{ |
| 151 | + if [ "$TESTS" != "none" ]; then |
| 152 | + printf "\nTests passed: %3d\n" ${nsuccess} |
| 153 | + printf "Tests failed: %3d\n" ${nfail} |
| 154 | + fi |
| 155 | +} |
| 156 | + |
| 157 | +log_section() |
| 158 | +{ |
| 159 | + echo |
| 160 | + echo "################################################################################" |
| 161 | + echo "TEST SECTION: $*" |
| 162 | + echo "################################################################################" |
| 163 | +} |
| 164 | + |
| 165 | +cleanup() |
| 166 | +{ |
| 167 | + ip link del veth-rt-1 2>/dev/null || true |
| 168 | + ip link del veth-rt-2 2>/dev/null || true |
| 169 | + |
| 170 | + # destroy routers rt-* and hosts hs-* |
| 171 | + for ns in $(ip netns show | grep -E 'rt-*|hs-*'); do |
| 172 | + ip netns del ${ns} || true |
| 173 | + done |
| 174 | +} |
| 175 | + |
| 176 | +# Setup the basic networking for the routers |
| 177 | +setup_rt_networking() |
| 178 | +{ |
| 179 | + local rt=$1 |
| 180 | + local nsname=rt-${rt} |
| 181 | + |
| 182 | + ip netns add ${nsname} |
| 183 | + |
| 184 | + ip netns exec ${nsname} sysctl -wq net.ipv6.conf.all.accept_dad=0 |
| 185 | + ip netns exec ${nsname} sysctl -wq net.ipv6.conf.default.accept_dad=0 |
| 186 | + |
| 187 | + ip link set veth-rt-${rt} netns ${nsname} |
| 188 | + ip -netns ${nsname} link set veth-rt-${rt} name veth0 |
| 189 | + |
| 190 | + ip -netns ${nsname} addr add ${IPv6_RT_NETWORK}::${rt}/64 dev veth0 nodad |
| 191 | + ip -netns ${nsname} link set veth0 up |
| 192 | + ip -netns ${nsname} link set lo up |
| 193 | + |
| 194 | + ip netns exec ${nsname} sysctl -wq net.ipv6.conf.all.forwarding=1 |
| 195 | +} |
| 196 | + |
| 197 | +setup_rt_netfilter() |
| 198 | +{ |
| 199 | + local rt=$1 |
| 200 | + local nsname=rt-${rt} |
| 201 | + |
| 202 | + ip netns exec ${nsname} sysctl -wq net.netfilter.nf_hooks_lwtunnel=1 |
| 203 | + ip netns exec ${nsname} ip6tables -t raw -A PREROUTING -m rpfilter --invert -j DROP |
| 204 | +} |
| 205 | + |
| 206 | +setup_hs() |
| 207 | +{ |
| 208 | + local hs=$1 |
| 209 | + local rt=$2 |
| 210 | + local tid=$3 |
| 211 | + local hsname=hs-${hs} |
| 212 | + local rtname=rt-${rt} |
| 213 | + local rtveth=veth-t${tid} |
| 214 | + |
| 215 | + # set the networking for the host |
| 216 | + ip netns add ${hsname} |
| 217 | + |
| 218 | + ip -netns ${hsname} link add veth0 type veth peer name ${rtveth} |
| 219 | + ip -netns ${hsname} link set ${rtveth} netns ${rtname} |
| 220 | + ip -netns ${hsname} addr add ${IPv6_HS_NETWORK}::${hs}/64 dev veth0 nodad |
| 221 | + ip -netns ${hsname} link set veth0 up |
| 222 | + ip -netns ${hsname} link set lo up |
| 223 | + |
| 224 | + ip -netns ${rtname} addr add ${IPv6_HS_NETWORK}::${rt}${hs}/64 dev ${rtveth} |
| 225 | + ip -netns ${rtname} link set ${rtveth} up |
| 226 | + |
| 227 | + ip netns exec ${rtname} sysctl -wq net.ipv6.conf.all.accept_dad=0 |
| 228 | + ip netns exec ${rtname} sysctl -wq net.ipv6.conf.default.accept_dad=0 |
| 229 | + |
| 230 | + ip netns exec ${rtname} sysctl -wq net.ipv6.conf.${rtveth}.proxy_ndp=1 |
| 231 | +} |
| 232 | + |
| 233 | +setup_vpn_config() |
| 234 | +{ |
| 235 | + local hssrc=$1 |
| 236 | + local rtsrc=$2 |
| 237 | + local hsdst=$3 |
| 238 | + local rtdst=$4 |
| 239 | + local tid=$5 |
| 240 | + |
| 241 | + local hssrc_name=hs-t${tid}-${hssrc} |
| 242 | + local hsdst_name=hs-t${tid}-${hsdst} |
| 243 | + local rtsrc_name=rt-${rtsrc} |
| 244 | + local rtdst_name=rt-${rtdst} |
| 245 | + local rtveth=veth-t${tid} |
| 246 | + local vpn_sid=${SID_LOCATOR}:${hssrc}${hsdst}:${tid}::6004 |
| 247 | + |
| 248 | + ip -netns ${rtsrc_name} -6 neigh add proxy ${IPv6_HS_NETWORK}::${hsdst} dev ${rtveth} |
| 249 | + |
| 250 | + # set the encap route for encapsulating packets which arrive from the |
| 251 | + # host hssrc and destined to the access router rtsrc. |
| 252 | + ip -netns ${rtsrc_name} -6 route add ${IPv6_HS_NETWORK}::${hsdst}/128 \ |
| 253 | + encap seg6 mode encap segs ${vpn_sid} dev veth0 |
| 254 | + ip -netns ${rtsrc_name} -6 route add ${vpn_sid}/128 \ |
| 255 | + via 2001:11::${rtdst} dev veth0 |
| 256 | + |
| 257 | + # set the decap route for decapsulating packets which arrive from |
| 258 | + # the rtdst router and destined to the hsdst host. |
| 259 | + ip -netns ${rtdst_name} -6 route add ${vpn_sid}/128 \ |
| 260 | + encap seg6local action End.DX6 nh6 ${IPv6_HS_NETWORK}::${hsdst} dev veth-t${tid} |
| 261 | +} |
| 262 | + |
| 263 | +setup() |
| 264 | +{ |
| 265 | + ip link add veth-rt-1 type veth peer name veth-rt-2 |
| 266 | + # setup the networking for router rt-1 and router rt-2 |
| 267 | + setup_rt_networking 1 |
| 268 | + setup_rt_networking 2 |
| 269 | + |
| 270 | + # setup two hosts for the tenant 100. |
| 271 | + # - host hs-1 is directly connected to the router rt-1; |
| 272 | + # - host hs-2 is directly connected to the router rt-2. |
| 273 | + setup_hs 1 1 100 |
| 274 | + setup_hs 2 2 100 |
| 275 | + |
| 276 | + # setup the IPv4 L3 VPN which connects the host hs-1 and host hs-2. |
| 277 | + setup_vpn_config 1 1 2 2 100 #args: src_host src_router dst_host dst_router tenant |
| 278 | + setup_vpn_config 2 2 1 1 100 |
| 279 | +} |
| 280 | + |
| 281 | +check_hs_connectivity() |
| 282 | +{ |
| 283 | + local hssrc=$1 |
| 284 | + local hsdst=$2 |
| 285 | + local tid=$3 |
| 286 | + |
| 287 | + ip netns exec hs-${hssrc} ping -6 -c 1 -W ${PING_TIMEOUT_SEC} \ |
| 288 | + ${IPv6_HS_NETWORK}::${hsdst} >/dev/null 2>&1 |
| 289 | +} |
| 290 | + |
| 291 | +check_and_log_hs_connectivity() |
| 292 | +{ |
| 293 | + local hssrc=$1 |
| 294 | + local hsdst=$2 |
| 295 | + local tid=$3 |
| 296 | + |
| 297 | + check_hs_connectivity ${hssrc} ${hsdst} ${tid} |
| 298 | + log_test $? 0 "Hosts connectivity: hs-${hssrc} -> hs-${hsdst} (tenant ${tid})" |
| 299 | +} |
| 300 | + |
| 301 | +host_tests() |
| 302 | +{ |
| 303 | + log_section "SRv6 VPN connectivity test among hosts in the same tenant" |
| 304 | + |
| 305 | + check_and_log_hs_connectivity 1 2 100 |
| 306 | + check_and_log_hs_connectivity 2 1 100 |
| 307 | +} |
| 308 | + |
| 309 | +router_netfilter_tests() |
| 310 | +{ |
| 311 | + log_section "SRv6 VPN connectivity test with netfilter enabled in routers" |
| 312 | + setup_rt_netfilter 1 |
| 313 | + setup_rt_netfilter 2 |
| 314 | + |
| 315 | + check_and_log_hs_connectivity 1 2 100 |
| 316 | + check_and_log_hs_connectivity 2 1 100 |
| 317 | +} |
| 318 | + |
| 319 | +if [ "$(id -u)" -ne 0 ];then |
| 320 | + echo "SKIP: Need root privileges" |
| 321 | + exit $ksft_skip |
| 322 | +fi |
| 323 | + |
| 324 | +if [ ! -x "$(command -v ip)" ]; then |
| 325 | + echo "SKIP: Could not run test without ip tool" |
| 326 | + exit $ksft_skip |
| 327 | +fi |
| 328 | + |
| 329 | +cleanup &>/dev/null |
| 330 | + |
| 331 | +setup |
| 332 | + |
| 333 | +host_tests |
| 334 | +router_netfilter_tests |
| 335 | + |
| 336 | +print_log_test_results |
| 337 | + |
| 338 | +cleanup &>/dev/null |
| 339 | + |
| 340 | +exit ${ret} |
0 commit comments