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. 249readonly ksft_skip=4 250 251readonly RDMSUFF="$(mktemp -u XXXXXXXX)" 252readonly DUMMY_DEVNAME="dum0" 253readonly VRF_TID=100 254readonly VRF_DEVNAME="vrf-${VRF_TID}" 255readonly RT2HS_DEVNAME="veth-t${VRF_TID}" 256readonly LOCALSID_TABLE_ID=90 257readonly IPv6_RT_NETWORK=fcf0:0 258readonly IPv6_HS_NETWORK=cafe 259readonly IPv4_HS_NETWORK=10.0.0 260readonly VPN_LOCATOR_SERVICE=fcff 261readonly DT46_FUNC=0d46 262readonly HEADEND_ENCAP="encap.red" 263 264# do not add ':' as separator 265readonly LCBLOCK_ADDR=fcbb0000 266readonly LCBLOCK_BLEN=32 267# do not add ':' as separator 268readonly LCNODEFUNC_FMT="0%d00" 269readonly LCNODEFUNC_BLEN=16 270 271readonly LCBLOCK_NODEFUNC_BLEN=$((LCBLOCK_BLEN + LCNODEFUNC_BLEN)) 272 273readonly CSID_CNTR_PREFIX="dead:beaf::/32" 274# ID of the router used for testing the C-SID container cfgs 275readonly CSID_CNTR_RT_ID_TEST=1 276# Routing table used for testing the C-SID container cfgs 277readonly 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"). 289declare -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 312PING_TIMEOUT_SEC=4 313PAUSE_ON_FAIL=${PAUSE_ON_FAIL:=no} 314 315# IDs of routers and hosts are initialized during the setup of the testing 316# network 317ROUTERS='' 318HOSTS='' 319 320SETUP_ERR=1 321 322ret=${ksft_skip} 323nsuccess=0 324nfail=0 325 326log_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 348print_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 361log_section() 362{ 363 echo 364 echo "################################################################################" 365 echo "TEST SECTION: $*" 366 echo "################################################################################" 367} 368 369test_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 379get_nodename() 380{ 381 local name="$1" 382 383 echo "${name}-${RDMSUFF}" 384} 385 386get_rtname() 387{ 388 local rtid="$1" 389 390 get_nodename "rt-${rtid}" 391} 392 393get_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 407create_router() 408{ 409 local rtid="$1" 410 local nsname 411 412 nsname="$(get_rtname "${rtid}")" 413 414 __create_namespace "${nsname}" 415} 416 417create_host() 418{ 419 local hsid="$1" 420 local nsname 421 422 nsname="$(get_hsname "${hsid}")" 423 424 __create_namespace "${nsname}" 425} 426 427cleanup() 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 457add_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 476get_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 491setup_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 535build_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 565build_csid() 566{ 567 local nodeid="$1" 568 569 printf "${LCNODEFUNC_FMT}" "${nodeid}" 570} 571 572build_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 588setup_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() 729setup_ipv4_vpn_2sids() 730{ 731 __setup_l3vpn "$1" "$2" "$3" 2 4 732} 733 734# see __setup_l3vpn() 735setup_ipv6_vpn_1sid() 736{ 737 __setup_l3vpn "$1" "$2" "$3" 1 6 738} 739 740setup_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 802setup() 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 876check_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 891check_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 900check_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 912check_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 924check_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 935check_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 944check_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 953router_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 971host2gateway_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 982host_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 1020rt_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 1046csid_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 1077test_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 1085test_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 1108test_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 1117if [ "$(id -u)" -ne 0 ]; then 1118 echo "SKIP: Need root privileges" 1119 exit "${ksft_skip}" 1120fi 1121 1122# required programs to carry out this selftest 1123test_command_or_ksft_skip ip 1124test_command_or_ksft_skip ping 1125test_command_or_ksft_skip sysctl 1126test_command_or_ksft_skip grep 1127test_command_or_ksft_skip cut 1128 1129test_iproute2_supp_or_ksft_skip 1130test_dummy_dev_or_ksft_skip 1131test_vrf_or_ksft_skip 1132 1133set -e 1134trap cleanup EXIT 1135 1136setup 1137set +e 1138 1139csid_container_cfg_tests 1140 1141router_tests 1142host2gateway_tests 1143host_vpn_tests 1144 1145print_log_test_results 1146