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