Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests: seg6: add selftest for NEXT-C-SID flavor in SRv6 End behavior

This selftest is designed for testing the support of NEXT-C-SID flavor
for SRv6 End behavior. It instantiates a virtual network composed of
several nodes: hosts and SRv6 routers. Each node is realized using a
network namespace that is properly interconnected to others through veth
pairs.
The test considers SRv6 routers implementing IPv4/IPv6 L3 VPNs leveraged
by hosts for communicating with each other. Such routers i) apply
different SRv6 Policies to the traffic received from connected hosts,
considering the IPv4 or IPv6 protocols; ii) use the NEXT-C-SID
compression mechanism for encoding several SRv6 segments within a single
128-bit SID address, referred to as a Compressed SID (C-SID) container.

The NEXT-C-SID is provided as a "flavor" of the SRv6 End behavior,
enabling it to properly process the C-SID containers. The correct
execution of the enabled NEXT-C-SID SRv6 End behavior is verified
through reachability tests carried out between hosts belonging to the
same VPN.

Signed-off-by: Andrea Mayer <andrea.mayer@uniroma2.it>
Acked-by: David Ahern <dsahern@kernel.org>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>

authored by

Andrea Mayer and committed by
Paolo Abeni
19d6356a 848f3c0d

+1146
+1
tools/testing/selftests/net/Makefile
··· 37 37 TEST_PROGS += srv6_end_dt6_l3vpn_test.sh 38 38 TEST_PROGS += srv6_hencap_red_l3vpn_test.sh 39 39 TEST_PROGS += srv6_hl2encap_red_l2vpn_test.sh 40 + TEST_PROGS += srv6_end_next_csid_l3vpn_test.sh 40 41 TEST_PROGS += vrf_strict_mode_test.sh 41 42 TEST_PROGS += arp_ndisc_evict_nocarrier.sh 42 43 TEST_PROGS += ndisc_unsolicited_na_test.sh
+1145
tools/testing/selftests/net/srv6_end_next_csid_l3vpn_test.sh
··· 1 + #!/bin/bash 2 + # SPDX-License-Identifier: GPL-2.0 3 + # 4 + # author: Andrea Mayer <andrea.mayer@uniroma2.it> 5 + # 6 + # This script is designed for testing the support of NEXT-C-SID flavor for SRv6 7 + # End behavior. 8 + # A basic knowledge of SRv6 architecture [1] and of the compressed SID approach 9 + # [2] is assumed for the reader. 10 + # 11 + # The network topology used in the selftest is depicted hereafter, composed by 12 + # two hosts and four routers. Hosts hs-1 and hs-2 are connected through an 13 + # IPv4/IPv6 L3 VPN service, offered by routers rt-1, rt-2, rt-3 and rt-4 using 14 + # the NEXT-C-SID flavor. The key components for such VPNs are: 15 + # 16 + # i) The SRv6 H.Encaps/H.Encaps.Red behaviors [1] apply SRv6 Policies on 17 + # traffic received by connected hosts, initiating the VPN tunnel; 18 + # 19 + # ii) The SRv6 End behavior [1] advances the active SID in the SID List 20 + # carried by the SRH; 21 + # 22 + # iii) The NEXT-C-SID mechanism [2] offers the possibility of encoding several 23 + # SRv6 segments within a single 128-bit SID address, referred to as a 24 + # Compressed SID (C-SID) container. In this way, the length of the SID 25 + # List can be drastically reduced. 26 + # The NEXT-C-SID is provided as a "flavor" of the SRv6 End behavior 27 + # which advances the current C-SID (i.e. the Locator-Node Function defined 28 + # in [2]) with the next one carried in the Argument, if available. 29 + # When no more C-SIDs are available in the Argument, the SRv6 End behavior 30 + # will apply the End function selecting the next SID in the SID List. 31 + # 32 + # iv) The SRv6 End.DT46 behavior [1] is used for removing the SRv6 Policy and, 33 + # thus, it terminates the VPN tunnel. Such a behavior is capable of 34 + # handling, at the same time, both tunneled IPv4 and IPv6 traffic. 35 + # 36 + # [1] https://datatracker.ietf.org/doc/html/rfc8986 37 + # [2] https://datatracker.ietf.org/doc/html/draft-ietf-spring-srv6-srh-compression 38 + # 39 + # 40 + # cafe::1 cafe::2 41 + # 10.0.0.1 10.0.0.2 42 + # +--------+ +--------+ 43 + # | | | | 44 + # | hs-1 | | hs-2 | 45 + # | | | | 46 + # +---+----+ +----+---+ 47 + # cafe::/64 | | cafe::/64 48 + # 10.0.0.0/24 | | 10.0.0.0/24 49 + # +---+----+ +----+---+ 50 + # | | fcf0:0:1:2::/64 | | 51 + # | rt-1 +-------------------+ rt-2 | 52 + # | | | | 53 + # +---+----+ +----+---+ 54 + # | . . | 55 + # | fcf0:0:1:3::/64 . | 56 + # | . . | 57 + # | . . | 58 + # fcf0:0:1:4::/64 | . | fcf0:0:2:3::/64 59 + # | . . | 60 + # | . . | 61 + # | fcf0:0:2:4::/64 . | 62 + # | . . | 63 + # +---+----+ +----+---+ 64 + # | | | | 65 + # | rt-4 +-------------------+ rt-3 | 66 + # | | fcf0:0:3:4::/64 | | 67 + # +---+----+ +----+---+ 68 + # 69 + # Every fcf0:0:x:y::/64 network interconnects the SRv6 routers rt-x with rt-y in 70 + # the selftest network. 71 + # 72 + # Local SID/C-SID table 73 + # ===================== 74 + # 75 + # Each SRv6 router is configured with a Local SID/C-SID table in which 76 + # SIDs/C-SIDs are stored. Considering an SRv6 router rt-x, SIDs/C-SIDs are 77 + # configured in the Local SID/C-SIDs table as follows: 78 + # 79 + # Local SID/C-SID table for SRv6 router rt-x 80 + # +-----------------------------------------------------------+ 81 + # |fcff:x::d46 is associated with the non-compressed SRv6 | 82 + # | End.DT46 behavior | 83 + # +-----------------------------------------------------------+ 84 + # |fcbb:0:0x00::/48 is associated with the NEXT-C-SID flavor | 85 + # | of SRv6 End behavior | 86 + # +-----------------------------------------------------------+ 87 + # |fcbb:0:0x00:d46::/64 is associated with the SRv6 End.DT46 | 88 + # | behavior when NEXT-C-SID compression is turned on | 89 + # +-----------------------------------------------------------+ 90 + # 91 + # The fcff::/16 prefix is reserved for implementing SRv6 services with regular 92 + # (non compressed) SIDs. Reachability of SIDs is ensured by proper configuration 93 + # of the IPv6 routing tables in the routers. 94 + # Similarly, the fcbb:0::/32 prefix is reserved for implementing SRv6 VPN 95 + # services leveraging the NEXT-C-SID compression mechanism. Indeed, the 96 + # fcbb:0::/32 is used for encoding the Locator-Block while the Locator-Node 97 + # Function is encoded with 16 bits. 98 + # 99 + # Incoming traffic classification and application of SRv6 Policies 100 + # ================================================================ 101 + # 102 + # An SRv6 ingress router applies different SRv6 Policies to the traffic received 103 + # from a connected host, considering the IPv4 or IPv6 destination address. 104 + # SRv6 policy enforcement consists of encapsulating the received traffic into a 105 + # new IPv6 packet with a given SID List contained in the SRH. 106 + # When the SID List contains only one SID, the SRH could be omitted completely 107 + # and that SID is stored directly in the IPv6 Destination Address (DA) (this is 108 + # called "reduced" encapsulation). 109 + # 110 + # Test cases for NEXT-C-SID 111 + # ========================= 112 + # 113 + # We consider two test cases for NEXT-C-SID: i) single SID and ii) double SID. 114 + # 115 + # In the single SID test case we have a number of segments that are all 116 + # contained in a single Compressed SID (C-SID) container. Therefore the 117 + # resulting SID List has only one SID. Using the reduced encapsulation format 118 + # this will result in a packet with no SRH. 119 + # 120 + # In the double SID test case we have one segment carried in a Compressed SID 121 + # (C-SID) container, followed by a regular (non compressed) SID. The resulting 122 + # SID List has two segments and it is possible to test the advance to the next 123 + # SID when all the C-SIDs in a C-SID container have been processed. Using the 124 + # reduced encapsulation format this will result in a packet with an SRH 125 + # containing 1 segment. 126 + # 127 + # For the single SID test case, we use the IPv4 addresses of hs-1 and hs-2, for 128 + # the double SID test case, we use their IPv6 addresses. This is only done to 129 + # simplify the test setup and avoid adding other hosts or multiple addresses on 130 + # the same interface of a host. 131 + # 132 + # Traffic from hs-1 to hs-2 133 + # ------------------------- 134 + # 135 + # Packets generated from hs-1 and directed towards hs-2 are handled by rt-1 136 + # which applies the SRv6 Policies as follows: 137 + # 138 + # i) IPv6 DA=cafe::2, H.Encaps.Red with SID List=fcbb:0:0400:0300:0200:d46:: 139 + # ii) IPv4 DA=10.0.0.2, H.Encaps.Red with SID List=fcbb:0:0300::,fcff:2::d46 140 + # 141 + # ### i) single SID 142 + # 143 + # The router rt-1 is configured to enforce the given Policy through the SRv6 144 + # H.Encaps.Red behavior which avoids the presence of the SRH at all, since it 145 + # pushes the single SID directly in the IPv6 DA. Such a SID encodes a whole 146 + # C-SID container carrying several C-SIDs (e.g. 0400, 0300, etc). 147 + # 148 + # As the packet reaches the router rt-4, the enabled NEXT-C-SID SRv6 End 149 + # behavior (associated with fcbb:0:0400::/48) is triggered. This behavior 150 + # analyzes the IPv6 DA and checks whether the Argument of the C-SID container 151 + # is zero or not. In this case, the Argument is *NOT* zero and the IPv6 DA is 152 + # updated as follows: 153 + # 154 + # +---------------------------------------------------------------+ 155 + # | Before applying the rt-4 enabled NEXT-C-SID SRv6 End behavior | 156 + # +---------------------------------------------------------------+ 157 + # | +---------- Argument | 158 + # | vvvvvvvvvvvvvvvv | 159 + # | IPv6 DA fcbb:0:0400:0300:0200:d46:: | 160 + # | ^^^^ <-- shifting | 161 + # | | | 162 + # | Locator-Node Function | 163 + # +---------------------------------------------------------------+ 164 + # | After applying the rt-4 enabled NEXT-C-SID SRv6 End behavior | 165 + # +---------------------------------------------------------------+ 166 + # | +---------- Argument | 167 + # | vvvvvvvvvvvv | 168 + # | IPv6 DA fcbb:0:0300:0200:d46:: | 169 + # | ^^^^ | 170 + # | | | 171 + # | Locator-Node Function | 172 + # +---------------------------------------------------------------+ 173 + # 174 + # After having applied the enabled NEXT-C-SID SRv6 End behavior, the packet is 175 + # sent to the next node, i.e. rt-3. 176 + # 177 + # The enabled NEXT-C-SID SRv6 End behavior on rt-3 is executed as the packet is 178 + # received. This behavior processes the packet and updates the IPv6 DA with 179 + # fcbb:0:0200:d46::, since the Argument is *NOT* zero. Then, the packet is sent 180 + # to the router rt-2. 181 + # 182 + # The router rt-2 is configured for decapsulating the inner IPv6 packet and, 183 + # for this reason, it applies the SRv6 End.DT46 behavior on the received 184 + # packet. It is worth noting that the SRv6 End.DT46 behavior does not require 185 + # the presence of the SRH: it is fully capable to operate properly on 186 + # IPv4/IPv6-in-IPv6 encapsulations. 187 + # At the end of the decap operation, the packet is sent to the 188 + # host hs-2. 189 + # 190 + # ### ii) double SID 191 + # 192 + # The router rt-1 is configured to enforce the given Policy through the SRv6 193 + # H.Encaps.Red. As a result, the first SID fcbb:0:0300:: is stored into the 194 + # IPv6 DA, while the SRH pushed into the packet is made of only one SID, i.e. 195 + # fcff:2::d46. Hence, the packet sent by hs-1 to hs-2 is encapsulated in an 196 + # outer IPv6 header plus the SRH. 197 + # 198 + # As the packet reaches the node rt-3, the router applies the enabled NEXT-C-SID 199 + # SRv6 End behavior. 200 + # 201 + # +---------------------------------------------------------------+ 202 + # | Before applying the rt-3 enabled NEXT-C-SID SRv6 End behavior | 203 + # +---------------------------------------------------------------+ 204 + # | +---------- Argument | 205 + # | vvvv (Argument is all filled with zeros) | 206 + # | IPv6 DA fcbb:0:0300:: | 207 + # | ^^^^ | 208 + # | | | 209 + # | Locator-Node Function | 210 + # +---------------------------------------------------------------+ 211 + # | After applying the rt-3 enabled NEXT-C-SID SRv6 End behavior | 212 + # +---------------------------------------------------------------+ 213 + # | | 214 + # | IPv6 DA fcff:2::d46 | 215 + # | ^^^^^^^^^^^ | 216 + # | | | 217 + # | SID copied from the SID List contained in the SRH | 218 + # +---------------------------------------------------------------+ 219 + # 220 + # Since the Argument of the C-SID container is zero, the behavior can not 221 + # update the Locator-Node function with the next C-SID carried in the Argument 222 + # itself. Thus, the enabled NEXT-C-SID SRv6 End behavior operates as the 223 + # traditional End behavior: it updates the IPv6 DA by copying the next 224 + # available SID in the SID List carried by the SRH. After that, the packet is 225 + # sent to the node rt-2. 226 + # 227 + # Once the packet is received by rt-2, the router decapsulates the inner IPv6 228 + # packet using the SRv6 End.DT46 behavior (associated with the SID fcff:2::d46) 229 + # and sends it to the host hs-2. 230 + # 231 + # Traffic from hs-2 to hs-1 232 + # ------------------------- 233 + # 234 + # Packets generated from hs-2 and directed towards hs-1 are handled by rt-2 235 + # which applies the SRv6 Policies as follows: 236 + # 237 + # i) IPv6 DA=cafe::1, SID List=fcbb:0:0300:0400:0100:d46:: 238 + # ii) IPv4 DA=10.0.0.1, SID List=fcbb:0:0300::,fcff:1::d46 239 + # 240 + # For simplicity, such SRv6 Policies were chosen so that, in both use cases (i) 241 + # and (ii), the network paths crossed by traffic from hs-2 to hs-1 are the same 242 + # as those taken by traffic from hs-1 to hs-2. 243 + # In this way, traffic from hs-2 to hs-1 is processed similarly to traffic from 244 + # hs-1 to hs-2. So, the traffic processing scheme turns out to be the same as 245 + # that adopted in the use cases already examined (of course, it is necessary to 246 + # consider the different SIDs/C-SIDs). 247 + 248 + # Kselftest framework requirement - SKIP code is 4. 249 + readonly ksft_skip=4 250 + 251 + readonly RDMSUFF="$(mktemp -u XXXXXXXX)" 252 + readonly DUMMY_DEVNAME="dum0" 253 + readonly VRF_TID=100 254 + readonly VRF_DEVNAME="vrf-${VRF_TID}" 255 + readonly RT2HS_DEVNAME="veth-t${VRF_TID}" 256 + readonly LOCALSID_TABLE_ID=90 257 + readonly IPv6_RT_NETWORK=fcf0:0 258 + readonly IPv6_HS_NETWORK=cafe 259 + readonly IPv4_HS_NETWORK=10.0.0 260 + readonly VPN_LOCATOR_SERVICE=fcff 261 + readonly DT46_FUNC=0d46 262 + readonly HEADEND_ENCAP="encap.red" 263 + 264 + # do not add ':' as separator 265 + readonly LCBLOCK_ADDR=fcbb0000 266 + readonly LCBLOCK_BLEN=32 267 + # do not add ':' as separator 268 + readonly LCNODEFUNC_FMT="0%d00" 269 + readonly LCNODEFUNC_BLEN=16 270 + 271 + readonly LCBLOCK_NODEFUNC_BLEN=$((LCBLOCK_BLEN + LCNODEFUNC_BLEN)) 272 + 273 + readonly CSID_CNTR_PREFIX="dead:beaf::/32" 274 + # ID of the router used for testing the C-SID container cfgs 275 + readonly CSID_CNTR_RT_ID_TEST=1 276 + # Routing table used for testing the C-SID container cfgs 277 + readonly CSID_CNTR_RT_TABLE=91 278 + 279 + # C-SID container configurations to be tested 280 + # 281 + # An entry of the array is defined as "a,b,c" where: 282 + # - 'a' and 'b' elements represent respectively the Locator-Block length 283 + # (lblen) in bits and the Locator-Node Function length (nflen) in bits. 284 + # 'a' and 'b' can be set to default values using the placeholder "d" which 285 + # indicates the default kernel values (32 for lblen and 16 for nflen); 286 + # otherwise, any numeric value is accepted; 287 + # - 'c' indicates whether the C-SID configuration provided by the values 'a' 288 + # and 'b' should be considered valid ("y") or invalid ("n"). 289 + declare -ra CSID_CONTAINER_CFGS=( 290 + "d,d,y" 291 + "d,16,y" 292 + "16,d,y" 293 + "16,32,y" 294 + "32,16,y" 295 + "48,8,y" 296 + "8,48,y" 297 + "d,0,n" 298 + "0,d,n" 299 + "32,0,n" 300 + "0,32,n" 301 + "17,d,n" 302 + "d,17,n" 303 + "120,16,n" 304 + "16,120,n" 305 + "0,128,n" 306 + "128,0,n" 307 + "130,0,n" 308 + "0,130,n" 309 + "0,0,n" 310 + ) 311 + 312 + PING_TIMEOUT_SEC=4 313 + PAUSE_ON_FAIL=${PAUSE_ON_FAIL:=no} 314 + 315 + # IDs of routers and hosts are initialized during the setup of the testing 316 + # network 317 + ROUTERS='' 318 + HOSTS='' 319 + 320 + SETUP_ERR=1 321 + 322 + ret=${ksft_skip} 323 + nsuccess=0 324 + nfail=0 325 + 326 + log_test() 327 + { 328 + local rc="$1" 329 + local expected="$2" 330 + local msg="$3" 331 + 332 + if [ "${rc}" -eq "${expected}" ]; then 333 + nsuccess=$((nsuccess+1)) 334 + printf "\n TEST: %-60s [ OK ]\n" "${msg}" 335 + else 336 + ret=1 337 + nfail=$((nfail+1)) 338 + printf "\n TEST: %-60s [FAIL]\n" "${msg}" 339 + if [ "${PAUSE_ON_FAIL}" = "yes" ]; then 340 + echo 341 + echo "hit enter to continue, 'q' to quit" 342 + read a 343 + [ "$a" = "q" ] && exit 1 344 + fi 345 + fi 346 + } 347 + 348 + print_log_test_results() 349 + { 350 + printf "\nTests passed: %3d\n" "${nsuccess}" 351 + printf "Tests failed: %3d\n" "${nfail}" 352 + 353 + # when a test fails, the value of 'ret' is set to 1 (error code). 354 + # Conversely, when all tests are passed successfully, the 'ret' value 355 + # is set to 0 (success code). 356 + if [ "${ret}" -ne 1 ]; then 357 + ret=0 358 + fi 359 + } 360 + 361 + log_section() 362 + { 363 + echo 364 + echo "################################################################################" 365 + echo "TEST SECTION: $*" 366 + echo "################################################################################" 367 + } 368 + 369 + test_command_or_ksft_skip() 370 + { 371 + local cmd="$1" 372 + 373 + if [ ! -x "$(command -v "${cmd}")" ]; then 374 + echo "SKIP: Could not run test without \"${cmd}\" tool"; 375 + exit "${ksft_skip}" 376 + fi 377 + } 378 + 379 + get_nodename() 380 + { 381 + local name="$1" 382 + 383 + echo "${name}-${RDMSUFF}" 384 + } 385 + 386 + get_rtname() 387 + { 388 + local rtid="$1" 389 + 390 + get_nodename "rt-${rtid}" 391 + } 392 + 393 + get_hsname() 394 + { 395 + local hsid="$1" 396 + 397 + get_nodename "hs-${hsid}" 398 + } 399 + 400 + __create_namespace() 401 + { 402 + local name="$1" 403 + 404 + ip netns add "${name}" 405 + } 406 + 407 + create_router() 408 + { 409 + local rtid="$1" 410 + local nsname 411 + 412 + nsname="$(get_rtname "${rtid}")" 413 + 414 + __create_namespace "${nsname}" 415 + } 416 + 417 + create_host() 418 + { 419 + local hsid="$1" 420 + local nsname 421 + 422 + nsname="$(get_hsname "${hsid}")" 423 + 424 + __create_namespace "${nsname}" 425 + } 426 + 427 + cleanup() 428 + { 429 + local nsname 430 + local i 431 + 432 + # destroy routers 433 + for i in ${ROUTERS}; do 434 + nsname="$(get_rtname "${i}")" 435 + 436 + ip netns del "${nsname}" &>/dev/null || true 437 + done 438 + 439 + # destroy hosts 440 + for i in ${HOSTS}; do 441 + nsname="$(get_hsname "${i}")" 442 + 443 + ip netns del "${nsname}" &>/dev/null || true 444 + done 445 + 446 + # check whether the setup phase was completed successfully or not. In 447 + # case of an error during the setup phase of the testing environment, 448 + # the selftest is considered as "skipped". 449 + if [ "${SETUP_ERR}" -ne 0 ]; then 450 + echo "SKIP: Setting up the testing environment failed" 451 + exit "${ksft_skip}" 452 + fi 453 + 454 + exit "${ret}" 455 + } 456 + 457 + add_link_rt_pairs() 458 + { 459 + local rt="$1" 460 + local rt_neighs="$2" 461 + local neigh 462 + local nsname 463 + local neigh_nsname 464 + 465 + nsname="$(get_rtname "${rt}")" 466 + 467 + for neigh in ${rt_neighs}; do 468 + neigh_nsname="$(get_rtname "${neigh}")" 469 + 470 + ip link add "veth-rt-${rt}-${neigh}" netns "${nsname}" \ 471 + type veth peer name "veth-rt-${neigh}-${rt}" \ 472 + netns "${neigh_nsname}" 473 + done 474 + } 475 + 476 + get_network_prefix() 477 + { 478 + local rt="$1" 479 + local neigh="$2" 480 + local p="${rt}" 481 + local q="${neigh}" 482 + 483 + if [ "${p}" -gt "${q}" ]; then 484 + p="${q}"; q="${rt}" 485 + fi 486 + 487 + echo "${IPv6_RT_NETWORK}:${p}:${q}" 488 + } 489 + 490 + # Setup the basic networking for the routers 491 + setup_rt_networking() 492 + { 493 + local rt="$1" 494 + local rt_neighs="$2" 495 + local nsname 496 + local net_prefix 497 + local devname 498 + local neigh 499 + 500 + nsname="$(get_rtname "${rt}")" 501 + 502 + for neigh in ${rt_neighs}; do 503 + devname="veth-rt-${rt}-${neigh}" 504 + 505 + net_prefix="$(get_network_prefix "${rt}" "${neigh}")" 506 + 507 + ip -netns "${nsname}" addr \ 508 + add "${net_prefix}::${rt}/64" dev "${devname}" nodad 509 + 510 + ip -netns "${nsname}" link set "${devname}" up 511 + done 512 + 513 + ip -netns "${nsname}" link add "${DUMMY_DEVNAME}" type dummy 514 + 515 + ip -netns "${nsname}" link set "${DUMMY_DEVNAME}" up 516 + ip -netns "${nsname}" link set lo up 517 + 518 + ip netns exec "${nsname}" sysctl -wq net.ipv6.conf.all.accept_dad=0 519 + ip netns exec "${nsname}" sysctl -wq net.ipv6.conf.default.accept_dad=0 520 + ip netns exec "${nsname}" sysctl -wq net.ipv6.conf.all.forwarding=1 521 + 522 + ip netns exec "${nsname}" sysctl -wq net.ipv4.conf.all.rp_filter=0 523 + ip netns exec "${nsname}" sysctl -wq net.ipv4.conf.default.rp_filter=0 524 + ip netns exec "${nsname}" sysctl -wq net.ipv4.ip_forward=1 525 + } 526 + 527 + # build an ipv6 prefix/address based on the input string 528 + # Note that the input string does not contain ':' and '::' which are considered 529 + # to be implicit. 530 + # e.g.: 531 + # - input: fbcc00000400300 532 + # - output: fbcc:0000:0400:0300:0000:0000:0000:0000 533 + # ^^^^^^^^^^^^^^^^^^^ 534 + # fill the address with 0s 535 + build_ipv6_addr() 536 + { 537 + local addr="$1" 538 + local out="" 539 + local strlen="${#addr}" 540 + local padn 541 + local i 542 + 543 + # add ":" every 4 digits (16 bits) 544 + for (( i = 0; i < strlen; i++ )); do 545 + if (( i > 0 && i < 32 && (i % 4) == 0 )); then 546 + out="${out}:" 547 + fi 548 + 549 + out="${out}${addr:$i:1}" 550 + done 551 + 552 + # fill the remaining bits of the address with 0s 553 + padn=$((32 - strlen)) 554 + for (( i = padn; i > 0; i-- )); do 555 + if (( i > 0 && i < 32 && (i % 4) == 0 )); then 556 + out="${out}:" 557 + fi 558 + 559 + out="${out}0" 560 + done 561 + 562 + printf "${out}" 563 + } 564 + 565 + build_csid() 566 + { 567 + local nodeid="$1" 568 + 569 + printf "${LCNODEFUNC_FMT}" "${nodeid}" 570 + } 571 + 572 + build_lcnode_func_prefix() 573 + { 574 + local nodeid="$1" 575 + local lcnodefunc 576 + local prefix 577 + local out 578 + 579 + lcnodefunc="$(build_csid "${nodeid}")" 580 + prefix="$(build_ipv6_addr "${LCBLOCK_ADDR}${lcnodefunc}")" 581 + 582 + out="${prefix}/${LCBLOCK_NODEFUNC_BLEN}" 583 + 584 + echo "${out}" 585 + } 586 + 587 + # Setup local SIDs for an SRv6 router 588 + setup_rt_local_sids() 589 + { 590 + local rt="$1" 591 + local rt_neighs="$2" 592 + local net_prefix 593 + local devname 594 + local nsname 595 + local neigh 596 + local lcnode_func_prefix 597 + local lcblock_prefix 598 + 599 + nsname="$(get_rtname "${rt}")" 600 + 601 + for neigh in ${rt_neighs}; do 602 + devname="veth-rt-${rt}-${neigh}" 603 + 604 + net_prefix="$(get_network_prefix "${rt}" "${neigh}")" 605 + 606 + # set underlay network routes for SIDs reachability 607 + ip -netns "${nsname}" -6 route \ 608 + add "${VPN_LOCATOR_SERVICE}:${neigh}::/32" \ 609 + table "${LOCALSID_TABLE_ID}" \ 610 + via "${net_prefix}::${neigh}" dev "${devname}" 611 + 612 + # set the underlay network for C-SIDs reachability 613 + lcnode_func_prefix="$(build_lcnode_func_prefix "${neigh}")" 614 + 615 + ip -netns "${nsname}" -6 route \ 616 + add "${lcnode_func_prefix}" \ 617 + table "${LOCALSID_TABLE_ID}" \ 618 + via "${net_prefix}::${neigh}" dev "${devname}" 619 + done 620 + 621 + lcnode_func_prefix="$(build_lcnode_func_prefix "${rt}")" 622 + 623 + # enabled NEXT-C-SID SRv6 End behavior (note that "dev" is the dummy 624 + # dum0 device chosen for the sake of simplicity). 625 + ip -netns "${nsname}" -6 route \ 626 + add "${lcnode_func_prefix}" \ 627 + table "${LOCALSID_TABLE_ID}" \ 628 + encap seg6local action End flavors next-csid \ 629 + lblen "${LCBLOCK_BLEN}" nflen "${LCNODEFUNC_BLEN}" \ 630 + dev "${DUMMY_DEVNAME}" 631 + 632 + # all SIDs for VPNs start with a common locator. Routes and SRv6 633 + # Endpoint behavior instaces are grouped together in the 'localsid' 634 + # table. 635 + ip -netns "${nsname}" -6 rule \ 636 + add to "${VPN_LOCATOR_SERVICE}::/16" \ 637 + lookup "${LOCALSID_TABLE_ID}" prio 999 638 + 639 + # common locator block for NEXT-C-SIDS compression mechanism. 640 + lcblock_prefix="$(build_ipv6_addr "${LCBLOCK_ADDR}")" 641 + ip -netns "${nsname}" -6 rule \ 642 + add to "${lcblock_prefix}/${LCBLOCK_BLEN}" \ 643 + lookup "${LOCALSID_TABLE_ID}" prio 999 644 + } 645 + 646 + # build and install the SRv6 policy into the ingress SRv6 router as well as the 647 + # decap SID in the egress one. 648 + # args: 649 + # $1 - src host (evaluate automatically the ingress router) 650 + # $2 - dst host (evaluate automatically the egress router) 651 + # $3 - SRv6 routers configured for steering traffic (End behaviors) 652 + # $4 - single SID or double SID 653 + # $5 - traffic type (IPv6 or IPv4) 654 + __setup_l3vpn() 655 + { 656 + local src="$1" 657 + local dst="$2" 658 + local end_rts="$3" 659 + local mode="$4" 660 + local traffic="$5" 661 + local nsname 662 + local policy 663 + local container 664 + local decapsid 665 + local lcnfunc 666 + local dt 667 + local n 668 + local rtsrc_nsname 669 + local rtdst_nsname 670 + 671 + rtsrc_nsname="$(get_rtname "${src}")" 672 + rtdst_nsname="$(get_rtname "${dst}")" 673 + 674 + container="${LCBLOCK_ADDR}" 675 + 676 + # build first SID (C-SID container) 677 + for n in ${end_rts}; do 678 + lcnfunc="$(build_csid "${n}")" 679 + 680 + container="${container}${lcnfunc}" 681 + done 682 + 683 + if [ "${mode}" -eq 1 ]; then 684 + # single SID policy 685 + dt="$(build_csid "${dst}")${DT46_FUNC}" 686 + container="${container}${dt}" 687 + # build the full ipv6 address for the container 688 + policy="$(build_ipv6_addr "${container}")" 689 + 690 + # build the decap SID used in the decap node 691 + container="${LCBLOCK_ADDR}${dt}" 692 + decapsid="$(build_ipv6_addr "${container}")" 693 + else 694 + # double SID policy 695 + decapsid="${VPN_LOCATOR_SERVICE}:${dst}::${DT46_FUNC}" 696 + 697 + policy="$(build_ipv6_addr "${container}"),${decapsid}" 698 + fi 699 + 700 + # apply encap policy 701 + if [ "${traffic}" -eq 6 ]; then 702 + ip -netns "${rtsrc_nsname}" -6 route \ 703 + add "${IPv6_HS_NETWORK}::${dst}" vrf "${VRF_DEVNAME}" \ 704 + encap seg6 mode "${HEADEND_ENCAP}" segs "${policy}" \ 705 + dev "${VRF_DEVNAME}" 706 + 707 + ip -netns "${rtsrc_nsname}" -6 neigh \ 708 + add proxy "${IPv6_HS_NETWORK}::${dst}" \ 709 + dev "${RT2HS_DEVNAME}" 710 + else 711 + # "dev" must be different from the one where the packet is 712 + # received, otherwise the proxy arp does not work. 713 + ip -netns "${rtsrc_nsname}" -4 route \ 714 + add "${IPv4_HS_NETWORK}.${dst}" vrf "${VRF_DEVNAME}" \ 715 + encap seg6 mode "${HEADEND_ENCAP}" segs "${policy}" \ 716 + dev "${VRF_DEVNAME}" 717 + fi 718 + 719 + # apply decap 720 + # Local End.DT46 behavior (decap) 721 + ip -netns "${rtdst_nsname}" -6 route \ 722 + add "${decapsid}" \ 723 + table "${LOCALSID_TABLE_ID}" \ 724 + encap seg6local action End.DT46 vrftable "${VRF_TID}" \ 725 + dev "${VRF_DEVNAME}" 726 + } 727 + 728 + # see __setup_l3vpn() 729 + setup_ipv4_vpn_2sids() 730 + { 731 + __setup_l3vpn "$1" "$2" "$3" 2 4 732 + } 733 + 734 + # see __setup_l3vpn() 735 + setup_ipv6_vpn_1sid() 736 + { 737 + __setup_l3vpn "$1" "$2" "$3" 1 6 738 + } 739 + 740 + setup_hs() 741 + { 742 + local hs="$1" 743 + local rt="$2" 744 + local hsname 745 + local rtname 746 + 747 + hsname="$(get_hsname "${hs}")" 748 + rtname="$(get_rtname "${rt}")" 749 + 750 + ip netns exec "${hsname}" sysctl -wq net.ipv6.conf.all.accept_dad=0 751 + ip netns exec "${hsname}" sysctl -wq net.ipv6.conf.default.accept_dad=0 752 + 753 + ip -netns "${hsname}" link add veth0 type veth \ 754 + peer name "${RT2HS_DEVNAME}" netns "${rtname}" 755 + 756 + ip -netns "${hsname}" addr \ 757 + add "${IPv6_HS_NETWORK}::${hs}/64" dev veth0 nodad 758 + ip -netns "${hsname}" addr add "${IPv4_HS_NETWORK}.${hs}/24" dev veth0 759 + 760 + ip -netns "${hsname}" link set veth0 up 761 + ip -netns "${hsname}" link set lo up 762 + 763 + # configure the VRF on the router which is directly connected to the 764 + # source host. 765 + ip -netns "${rtname}" link \ 766 + add "${VRF_DEVNAME}" type vrf table "${VRF_TID}" 767 + ip -netns "${rtname}" link set "${VRF_DEVNAME}" up 768 + 769 + # enslave the veth interface connecting the router with the host to the 770 + # VRF in the access router 771 + ip -netns "${rtname}" link \ 772 + set "${RT2HS_DEVNAME}" master "${VRF_DEVNAME}" 773 + 774 + # set default routes to unreachable for both ipv6 and ipv4 775 + ip -netns "${rtname}" -6 route \ 776 + add unreachable default metric 4278198272 \ 777 + vrf "${VRF_DEVNAME}" 778 + ip -netns "${rtname}" -4 route \ 779 + add unreachable default metric 4278198272 \ 780 + vrf "${VRF_DEVNAME}" 781 + 782 + ip -netns "${rtname}" addr \ 783 + add "${IPv6_HS_NETWORK}::254/64" dev "${RT2HS_DEVNAME}" nodad 784 + ip -netns "${rtname}" addr \ 785 + add "${IPv4_HS_NETWORK}.254/24" dev "${RT2HS_DEVNAME}" 786 + 787 + ip -netns "${rtname}" link set "${RT2HS_DEVNAME}" up 788 + 789 + ip netns exec "${rtname}" \ 790 + sysctl -wq net.ipv6.conf."${RT2HS_DEVNAME}".proxy_ndp=1 791 + ip netns exec "${rtname}" \ 792 + sysctl -wq net.ipv4.conf."${RT2HS_DEVNAME}".proxy_arp=1 793 + 794 + # disable the rp_filter otherwise the kernel gets confused about how 795 + # to route decap ipv4 packets. 796 + ip netns exec "${rtname}" \ 797 + sysctl -wq net.ipv4.conf."${RT2HS_DEVNAME}".rp_filter=0 798 + 799 + ip netns exec "${rtname}" sh -c "echo 1 > /proc/sys/net/vrf/strict_mode" 800 + } 801 + 802 + setup() 803 + { 804 + local i 805 + 806 + # create routers 807 + ROUTERS="1 2 3 4"; readonly ROUTERS 808 + for i in ${ROUTERS}; do 809 + create_router "${i}" 810 + done 811 + 812 + # create hosts 813 + HOSTS="1 2"; readonly HOSTS 814 + for i in ${HOSTS}; do 815 + create_host "${i}" 816 + done 817 + 818 + # set up the links for connecting routers 819 + add_link_rt_pairs 1 "2 3 4" 820 + add_link_rt_pairs 2 "3 4" 821 + add_link_rt_pairs 3 "4" 822 + 823 + # set up the basic connectivity of routers and routes required for 824 + # reachability of SIDs. 825 + setup_rt_networking 1 "2 3 4" 826 + setup_rt_networking 2 "1 3 4" 827 + setup_rt_networking 3 "1 2 4" 828 + setup_rt_networking 4 "1 2 3" 829 + 830 + # set up the hosts connected to routers 831 + setup_hs 1 1 832 + setup_hs 2 2 833 + 834 + # set up default SRv6 Endpoints (i.e. SRv6 End and SRv6 End.DT46) 835 + setup_rt_local_sids 1 "2 3 4" 836 + setup_rt_local_sids 2 "1 3 4" 837 + setup_rt_local_sids 3 "1 2 4" 838 + setup_rt_local_sids 4 "1 2 3" 839 + 840 + # set up SRv6 Policies 841 + 842 + # create an IPv6 VPN between hosts hs-1 and hs-2. 843 + # 844 + # Direction hs-1 -> hs-2 845 + # - rt-1 encap (H.Encaps.Red) 846 + # - rt-4 SRv6 End behavior (NEXT-C-SID flavor) 847 + # - rt-3 SRv6 End behavior (NEXT-C-SID flavor) 848 + # - rt-2 SRv6 End.DT46 behavior 849 + setup_ipv6_vpn_1sid 1 2 "4 3" 850 + 851 + # Direction hs2 -> hs-1 852 + # - rt-2 encap (H.Encaps.Red) 853 + # - rt-3 SRv6 End behavior (NEXT-C-SID flavor) 854 + # - rt-4 SRv6 End behavior (NEXT-C-SID flavor) 855 + # - rt-1 SRv6 End.DT46 behavior 856 + setup_ipv6_vpn_1sid 2 1 "3 4" 857 + 858 + # create an IPv4 VPN between hosts hs-1 and hs-2 859 + # 860 + # Direction hs-1 -> hs-2 861 + # - rt-1 encap (H.Encaps.Red) 862 + # - rt-3 SRv6 End behavior (NEXT-C-SID flavor) 863 + # - rt-2 SRv6 End.DT46 behavior 864 + setup_ipv4_vpn_2sids 1 2 "3" 865 + 866 + # Direction hs-2 -> hs-1 867 + # - rt-2 encap (H.Encaps.Red) 868 + # - rt-3 SRv6 End behavior (NEXT-C-SID flavor) 869 + # - rt-1 SRv6 End.DT46 behavior 870 + setup_ipv4_vpn_2sids 2 1 "3" 871 + 872 + # testing environment was set up successfully 873 + SETUP_ERR=0 874 + } 875 + 876 + check_rt_connectivity() 877 + { 878 + local rtsrc="$1" 879 + local rtdst="$2" 880 + local prefix 881 + local rtsrc_nsname 882 + 883 + rtsrc_nsname="$(get_rtname "${rtsrc}")" 884 + 885 + prefix="$(get_network_prefix "${rtsrc}" "${rtdst}")" 886 + 887 + ip netns exec "${rtsrc_nsname}" ping -c 1 -W "${PING_TIMEOUT_SEC}" \ 888 + "${prefix}::${rtdst}" >/dev/null 2>&1 889 + } 890 + 891 + check_and_log_rt_connectivity() 892 + { 893 + local rtsrc="$1" 894 + local rtdst="$2" 895 + 896 + check_rt_connectivity "${rtsrc}" "${rtdst}" 897 + log_test $? 0 "Routers connectivity: rt-${rtsrc} -> rt-${rtdst}" 898 + } 899 + 900 + check_hs_ipv6_connectivity() 901 + { 902 + local hssrc="$1" 903 + local hsdst="$2" 904 + local hssrc_nsname 905 + 906 + hssrc_nsname="$(get_hsname "${hssrc}")" 907 + 908 + ip netns exec "${hssrc_nsname}" ping -c 1 -W "${PING_TIMEOUT_SEC}" \ 909 + "${IPv6_HS_NETWORK}::${hsdst}" >/dev/null 2>&1 910 + } 911 + 912 + check_hs_ipv4_connectivity() 913 + { 914 + local hssrc="$1" 915 + local hsdst="$2" 916 + local hssrc_nsname 917 + 918 + hssrc_nsname="$(get_hsname "${hssrc}")" 919 + 920 + ip netns exec "${hssrc_nsname}" ping -c 1 -W "${PING_TIMEOUT_SEC}" \ 921 + "${IPv4_HS_NETWORK}.${hsdst}" >/dev/null 2>&1 922 + } 923 + 924 + check_and_log_hs2gw_connectivity() 925 + { 926 + local hssrc="$1" 927 + 928 + check_hs_ipv6_connectivity "${hssrc}" 254 929 + log_test $? 0 "IPv6 Hosts connectivity: hs-${hssrc} -> gw" 930 + 931 + check_hs_ipv4_connectivity "${hssrc}" 254 932 + log_test $? 0 "IPv4 Hosts connectivity: hs-${hssrc} -> gw" 933 + } 934 + 935 + check_and_log_hs_ipv6_connectivity() 936 + { 937 + local hssrc="$1" 938 + local hsdst="$2" 939 + 940 + check_hs_ipv6_connectivity "${hssrc}" "${hsdst}" 941 + log_test $? 0 "IPv6 Hosts connectivity: hs-${hssrc} -> hs-${hsdst}" 942 + } 943 + 944 + check_and_log_hs_ipv4_connectivity() 945 + { 946 + local hssrc="$1" 947 + local hsdst="$2" 948 + 949 + check_hs_ipv4_connectivity "${hssrc}" "${hsdst}" 950 + log_test $? 0 "IPv4 Hosts connectivity: hs-${hssrc} -> hs-${hsdst}" 951 + } 952 + 953 + router_tests() 954 + { 955 + local i 956 + local j 957 + 958 + log_section "IPv6 routers connectivity test" 959 + 960 + for i in ${ROUTERS}; do 961 + for j in ${ROUTERS}; do 962 + if [ "${i}" -eq "${j}" ]; then 963 + continue 964 + fi 965 + 966 + check_and_log_rt_connectivity "${i}" "${j}" 967 + done 968 + done 969 + } 970 + 971 + host2gateway_tests() 972 + { 973 + local hs 974 + 975 + log_section "IPv4/IPv6 connectivity test among hosts and gateways" 976 + 977 + for hs in ${HOSTS}; do 978 + check_and_log_hs2gw_connectivity "${hs}" 979 + done 980 + } 981 + 982 + host_vpn_tests() 983 + { 984 + log_section "SRv6 VPN connectivity test hosts (h1 <-> h2, IPv6)" 985 + 986 + check_and_log_hs_ipv6_connectivity 1 2 987 + check_and_log_hs_ipv6_connectivity 2 1 988 + 989 + log_section "SRv6 VPN connectivity test hosts (h1 <-> h2, IPv4)" 990 + 991 + check_and_log_hs_ipv4_connectivity 1 2 992 + check_and_log_hs_ipv4_connectivity 2 1 993 + } 994 + 995 + __nextcsid_end_behavior_test() 996 + { 997 + local nsname="$1" 998 + local cmd="$2" 999 + local blen="$3" 1000 + local flen="$4" 1001 + local layout="" 1002 + 1003 + if [ "${blen}" != "d" ]; then 1004 + layout="${layout} lblen ${blen}" 1005 + fi 1006 + 1007 + if [ "${flen}" != "d" ]; then 1008 + layout="${layout} nflen ${flen}" 1009 + fi 1010 + 1011 + ip -netns "${nsname}" -6 route \ 1012 + "${cmd}" "${CSID_CNTR_PREFIX}" \ 1013 + table "${CSID_CNTR_RT_TABLE}" \ 1014 + encap seg6local action End flavors next-csid ${layout} \ 1015 + dev "${DUMMY_DEVNAME}" &>/dev/null 1016 + 1017 + return "$?" 1018 + } 1019 + 1020 + rt_x_nextcsid_end_behavior_test() 1021 + { 1022 + local rt="$1" 1023 + local blen="$2" 1024 + local flen="$3" 1025 + local nsname 1026 + local ret 1027 + 1028 + nsname="$(get_rtname "${rt}")" 1029 + 1030 + __nextcsid_end_behavior_test "${nsname}" "add" "${blen}" "${flen}" 1031 + ret="$?" 1032 + __nextcsid_end_behavior_test "${nsname}" "del" "${blen}" "${flen}" 1033 + 1034 + return "${ret}" 1035 + } 1036 + 1037 + __parse_csid_container_cfg() 1038 + { 1039 + local cfg="$1" 1040 + local index="$2" 1041 + local out 1042 + 1043 + echo "${cfg}" | cut -d',' -f"${index}" 1044 + } 1045 + 1046 + csid_container_cfg_tests() 1047 + { 1048 + local valid 1049 + local blen 1050 + local flen 1051 + local cfg 1052 + local ret 1053 + 1054 + log_section "C-SID Container config tests (legend: d='kernel default')" 1055 + 1056 + for cfg in "${CSID_CONTAINER_CFGS[@]}"; do 1057 + blen="$(__parse_csid_container_cfg "${cfg}" 1)" 1058 + flen="$(__parse_csid_container_cfg "${cfg}" 2)" 1059 + valid="$(__parse_csid_container_cfg "${cfg}" 3)" 1060 + 1061 + rt_x_nextcsid_end_behavior_test \ 1062 + "${CSID_CNTR_RT_ID_TEST}" \ 1063 + "${blen}" \ 1064 + "${flen}" 1065 + ret="$?" 1066 + 1067 + if [ "${valid}" == "y" ]; then 1068 + log_test "${ret}" 0 \ 1069 + "Accept valid C-SID container cfg (lblen=${blen}, nflen=${flen})" 1070 + else 1071 + log_test "${ret}" 2 \ 1072 + "Reject invalid C-SID container cfg (lblen=${blen}, nflen=${flen})" 1073 + fi 1074 + done 1075 + } 1076 + 1077 + test_iproute2_supp_or_ksft_skip() 1078 + { 1079 + if ! ip route help 2>&1 | grep -qo "next-csid"; then 1080 + echo "SKIP: Missing SRv6 NEXT-C-SID flavor support in iproute2" 1081 + exit "${ksft_skip}" 1082 + fi 1083 + } 1084 + 1085 + test_dummy_dev_or_ksft_skip() 1086 + { 1087 + local test_netns 1088 + 1089 + test_netns="dummy-$(mktemp -u XXXXXXXX)" 1090 + 1091 + if ! ip netns add "${test_netns}"; then 1092 + echo "SKIP: Cannot set up netns for testing dummy dev support" 1093 + exit "${ksft_skip}" 1094 + fi 1095 + 1096 + modprobe dummy &>/dev/null || true 1097 + if ! ip -netns "${test_netns}" link \ 1098 + add "${DUMMY_DEVNAME}" type dummy; then 1099 + echo "SKIP: dummy dev not supported" 1100 + 1101 + ip netns del "${test_netns}" 1102 + exit "${ksft_skip}" 1103 + fi 1104 + 1105 + ip netns del "${test_netns}" 1106 + } 1107 + 1108 + test_vrf_or_ksft_skip() 1109 + { 1110 + modprobe vrf &>/dev/null || true 1111 + if [ ! -e /proc/sys/net/vrf/strict_mode ]; then 1112 + echo "SKIP: vrf sysctl does not exist" 1113 + exit "${ksft_skip}" 1114 + fi 1115 + } 1116 + 1117 + if [ "$(id -u)" -ne 0 ]; then 1118 + echo "SKIP: Need root privileges" 1119 + exit "${ksft_skip}" 1120 + fi 1121 + 1122 + # required programs to carry out this selftest 1123 + test_command_or_ksft_skip ip 1124 + test_command_or_ksft_skip ping 1125 + test_command_or_ksft_skip sysctl 1126 + test_command_or_ksft_skip grep 1127 + test_command_or_ksft_skip cut 1128 + 1129 + test_iproute2_supp_or_ksft_skip 1130 + test_dummy_dev_or_ksft_skip 1131 + test_vrf_or_ksft_skip 1132 + 1133 + set -e 1134 + trap cleanup EXIT 1135 + 1136 + setup 1137 + set +e 1138 + 1139 + csid_container_cfg_tests 1140 + 1141 + router_tests 1142 + host2gateway_tests 1143 + host_vpn_tests 1144 + 1145 + print_log_test_results