File Index
Files and directories
Sign in
FILE

srv6_end_next_csid_l3vpn_test.sh

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/net/srv6_end_next_csid_l3vpn_test.sh BASH
   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
 248source lib.sh
 249
 250readonly DUMMY_DEVNAME="dum0"
 251readonly VRF_TID=100
 252readonly VRF_DEVNAME="vrf-${VRF_TID}"
 253readonly RT2HS_DEVNAME="veth-t${VRF_TID}"
 254readonly LOCALSID_TABLE_ID=90
 255readonly IPv6_RT_NETWORK=fcf0:0
 256readonly IPv6_HS_NETWORK=cafe
 257readonly IPv4_HS_NETWORK=10.0.0
 258readonly VPN_LOCATOR_SERVICE=fcff
 259readonly DT46_FUNC=0d46
 260readonly HEADEND_ENCAP="encap.red"
 261
 262# do not add ':' as separator
 263readonly LCBLOCK_ADDR=fcbb0000
 264readonly LCBLOCK_BLEN=32
 265# do not add ':' as separator
 266readonly LCNODEFUNC_FMT="0%d00"
 267readonly LCNODEFUNC_BLEN=16
 268
 269readonly LCBLOCK_NODEFUNC_BLEN=$((LCBLOCK_BLEN + LCNODEFUNC_BLEN))
 270
 271readonly CSID_CNTR_PREFIX="dead:beaf::/32"
 272# ID of the router used for testing the C-SID container cfgs
 273readonly CSID_CNTR_RT_ID_TEST=1
 274# Routing table used for testing the C-SID container cfgs
 275readonly CSID_CNTR_RT_TABLE=91
 276
 277# C-SID container configurations to be tested
 278#
 279# An entry of the array is defined as "a,b,c" where:
 280# - 'a' and 'b' elements represent respectively the Locator-Block length
 281#   (lblen) in bits and the Locator-Node Function length (nflen) in bits.
 282#   'a' and 'b' can be set to default values using the placeholder "d" which
 283#   indicates the default kernel values (32 for lblen and 16 for nflen);
 284#   otherwise, any numeric value is accepted;
 285# - 'c' indicates whether the C-SID configuration provided by the values 'a'
 286#   and 'b' should be considered valid ("y") or invalid ("n").
 287declare -ra CSID_CONTAINER_CFGS=(
 288	"d,d,y"
 289	"d,16,y"
 290	"16,d,y"
 291	"16,32,y"
 292	"32,16,y"
 293	"48,8,y"
 294	"8,48,y"
 295	"d,0,n"
 296	"0,d,n"
 297	"32,0,n"
 298	"0,32,n"
 299	"17,d,n"
 300	"d,17,n"
 301	"120,16,n"
 302	"16,120,n"
 303	"0,128,n"
 304	"128,0,n"
 305	"130,0,n"
 306	"0,130,n"
 307	"0,0,n"
 308)
 309
 310PING_TIMEOUT_SEC=4
 311PAUSE_ON_FAIL=${PAUSE_ON_FAIL:=no}
 312
 313# IDs of routers and hosts are initialized during the setup of the testing
 314# network
 315ROUTERS=''
 316HOSTS=''
 317
 318SETUP_ERR=1
 319
 320ret=${ksft_skip}
 321nsuccess=0
 322nfail=0
 323
 324log_test()
 325{
 326	local rc="$1"
 327	local expected="$2"
 328	local msg="$3"
 329
 330	if [ "${rc}" -eq "${expected}" ]; then
 331		nsuccess=$((nsuccess+1))
 332		printf "\n    TEST: %-60s  [ OK ]\n" "${msg}"
 333	else
 334		ret=1
 335		nfail=$((nfail+1))
 336		printf "\n    TEST: %-60s  [FAIL]\n" "${msg}"
 337		if [ "${PAUSE_ON_FAIL}" = "yes" ]; then
 338			echo
 339			echo "hit enter to continue, 'q' to quit"
 340			read a
 341			[ "$a" = "q" ] && exit 1
 342		fi
 343	fi
 344}
 345
 346print_log_test_results()
 347{
 348	printf "\nTests passed: %3d\n" "${nsuccess}"
 349	printf "Tests failed: %3d\n"   "${nfail}"
 350
 351	# when a test fails, the value of 'ret' is set to 1 (error code).
 352	# Conversely, when all tests are passed successfully, the 'ret' value
 353	# is set to 0 (success code).
 354	if [ "${ret}" -ne 1 ]; then
 355		ret=0
 356	fi
 357}
 358
 359log_section()
 360{
 361	echo
 362	echo "################################################################################"
 363	echo "TEST SECTION: $*"
 364	echo "################################################################################"
 365}
 366
 367test_command_or_ksft_skip()
 368{
 369	local cmd="$1"
 370
 371	if [ ! -x "$(command -v "${cmd}")" ]; then
 372		echo "SKIP: Could not run test without \"${cmd}\" tool";
 373		exit "${ksft_skip}"
 374	fi
 375}
 376
 377get_rtname()
 378{
 379	local rtid="$1"
 380
 381	echo "rt_${rtid}"
 382}
 383
 384get_hsname()
 385{
 386	local hsid="$1"
 387
 388	echo "hs_${hsid}"
 389}
 390
 391create_router()
 392{
 393	local rtid="$1"
 394	local nsname
 395
 396	nsname="$(get_rtname "${rtid}")"
 397	setup_ns "${nsname}"
 398}
 399
 400create_host()
 401{
 402	local hsid="$1"
 403	local nsname
 404
 405	nsname="$(get_hsname "${hsid}")"
 406	setup_ns "${nsname}"
 407}
 408
 409cleanup()
 410{
 411	cleanup_all_ns
 412
 413	# check whether the setup phase was completed successfully or not. In
 414	# case of an error during the setup phase of the testing environment,
 415	# the selftest is considered as "skipped".
 416	if [ "${SETUP_ERR}" -ne 0 ]; then
 417		echo "SKIP: Setting up the testing environment failed"
 418		exit "${ksft_skip}"
 419	fi
 420
 421	exit "${ret}"
 422}
 423
 424add_link_rt_pairs()
 425{
 426	local rt="$1"
 427	local rt_neighs="$2"
 428	local neigh
 429	local nsname
 430	local neigh_nsname
 431
 432	eval nsname=\${$(get_rtname "${rt}")}
 433
 434	for neigh in ${rt_neighs}; do
 435		eval neigh_nsname=\${$(get_rtname "${neigh}")}
 436
 437		ip link add "veth-rt-${rt}-${neigh}" netns "${nsname}" \
 438			type veth peer name "veth-rt-${neigh}-${rt}" \
 439			netns "${neigh_nsname}"
 440	done
 441}
 442
 443get_network_prefix()
 444{
 445	local rt="$1"
 446	local neigh="$2"
 447	local p="${rt}"
 448	local q="${neigh}"
 449
 450	if [ "${p}" -gt "${q}" ]; then
 451		p="${q}"; q="${rt}"
 452	fi
 453
 454	echo "${IPv6_RT_NETWORK}:${p}:${q}"
 455}
 456
 457# Setup the basic networking for the routers
 458setup_rt_networking()
 459{
 460	local rt="$1"
 461	local rt_neighs="$2"
 462	local nsname
 463	local net_prefix
 464	local devname
 465	local neigh
 466
 467	eval nsname=\${$(get_rtname "${rt}")}
 468
 469	for neigh in ${rt_neighs}; do
 470		devname="veth-rt-${rt}-${neigh}"
 471
 472		net_prefix="$(get_network_prefix "${rt}" "${neigh}")"
 473
 474		ip -netns "${nsname}" addr \
 475			add "${net_prefix}::${rt}/64" dev "${devname}" nodad
 476
 477		ip -netns "${nsname}" link set "${devname}" up
 478	done
 479
 480        ip -netns "${nsname}" link add "${DUMMY_DEVNAME}" type dummy
 481
 482        ip -netns "${nsname}" link set "${DUMMY_DEVNAME}" up
 483	ip -netns "${nsname}" link set lo up
 484
 485	ip netns exec "${nsname}" sysctl -wq net.ipv6.conf.all.accept_dad=0
 486	ip netns exec "${nsname}" sysctl -wq net.ipv6.conf.default.accept_dad=0
 487	ip netns exec "${nsname}" sysctl -wq net.ipv6.conf.all.forwarding=1
 488	ip netns exec "${nsname}" sysctl -wq net.ipv4.ip_forward=1
 489}
 490
 491# build an ipv6 prefix/address based on the input string
 492# Note that the input string does not contain ':' and '::' which are considered
 493# to be implicit.
 494# e.g.:
 495#  - input:  fbcc00000400300
 496#  - output: fbcc:0000:0400:0300:0000:0000:0000:0000
 497#                                ^^^^^^^^^^^^^^^^^^^
 498#                              fill the address with 0s
 499build_ipv6_addr()
 500{
 501	local addr="$1"
 502	local out=""
 503	local strlen="${#addr}"
 504	local padn
 505	local i
 506
 507	# add ":" every 4 digits (16 bits)
 508	for (( i = 0; i < strlen; i++ )); do
 509		if (( i > 0 && i < 32 && (i % 4) == 0 )); then
 510			out="${out}:"
 511		fi
 512
 513		out="${out}${addr:$i:1}"
 514	done
 515
 516	# fill the remaining bits of the address with 0s
 517	padn=$((32 - strlen))
 518	for (( i = padn; i > 0; i-- )); do
 519		if (( i > 0 && i < 32 && (i % 4) == 0 )); then
 520			out="${out}:"
 521		fi
 522
 523		out="${out}0"
 524	done
 525
 526	printf "${out}"
 527}
 528
 529build_csid()
 530{
 531	local nodeid="$1"
 532
 533	printf "${LCNODEFUNC_FMT}" "${nodeid}"
 534}
 535
 536build_lcnode_func_prefix()
 537{
 538	local nodeid="$1"
 539	local lcnodefunc
 540	local prefix
 541	local out
 542
 543	lcnodefunc="$(build_csid "${nodeid}")"
 544	prefix="$(build_ipv6_addr "${LCBLOCK_ADDR}${lcnodefunc}")"
 545
 546	out="${prefix}/${LCBLOCK_NODEFUNC_BLEN}"
 547
 548	echo "${out}"
 549}
 550
 551# Setup local SIDs for an SRv6 router
 552setup_rt_local_sids()
 553{
 554	local rt="$1"
 555	local rt_neighs="$2"
 556	local net_prefix
 557	local devname
 558	local nsname
 559	local neigh
 560	local lcnode_func_prefix
 561	local lcblock_prefix
 562
 563	eval nsname=\${$(get_rtname "${rt}")}
 564
 565	for neigh in ${rt_neighs}; do
 566		devname="veth-rt-${rt}-${neigh}"
 567
 568		net_prefix="$(get_network_prefix "${rt}" "${neigh}")"
 569
 570		# set underlay network routes for SIDs reachability
 571		ip -netns "${nsname}" -6 route \
 572			add "${VPN_LOCATOR_SERVICE}:${neigh}::/32" \
 573			table "${LOCALSID_TABLE_ID}" \
 574			via "${net_prefix}::${neigh}" dev "${devname}"
 575
 576		# set the underlay network for C-SIDs reachability
 577		lcnode_func_prefix="$(build_lcnode_func_prefix "${neigh}")"
 578
 579		ip -netns "${nsname}" -6 route \
 580			add "${lcnode_func_prefix}" \
 581			table "${LOCALSID_TABLE_ID}" \
 582			via "${net_prefix}::${neigh}" dev "${devname}"
 583	done
 584
 585	lcnode_func_prefix="$(build_lcnode_func_prefix "${rt}")"
 586
 587	# enabled NEXT-C-SID SRv6 End behavior (note that "dev" is the dummy
 588	# dum0 device chosen for the sake of simplicity).
 589	ip -netns "${nsname}" -6 route \
 590		add "${lcnode_func_prefix}" \
 591		table "${LOCALSID_TABLE_ID}" \
 592		encap seg6local action End flavors next-csid \
 593		lblen "${LCBLOCK_BLEN}" nflen "${LCNODEFUNC_BLEN}" \
 594		dev "${DUMMY_DEVNAME}"
 595
 596	# all SIDs for VPNs start with a common locator. Routes and SRv6
 597	# Endpoint behavior instances are grouped together in the 'localsid'
 598	# table.
 599	ip -netns "${nsname}" -6 rule \
 600		add to "${VPN_LOCATOR_SERVICE}::/16" \
 601		lookup "${LOCALSID_TABLE_ID}" prio 999
 602
 603	# common locator block for NEXT-C-SIDS compression mechanism.
 604	lcblock_prefix="$(build_ipv6_addr "${LCBLOCK_ADDR}")"
 605	ip -netns "${nsname}" -6 rule \
 606		add to "${lcblock_prefix}/${LCBLOCK_BLEN}" \
 607		lookup "${LOCALSID_TABLE_ID}" prio 999
 608}
 609
 610# build and install the SRv6 policy into the ingress SRv6 router as well as the
 611# decap SID in the egress one.
 612# args:
 613#  $1 - src host (evaluate automatically the ingress router)
 614#  $2 - dst host (evaluate automatically the egress router)
 615#  $3 - SRv6 routers configured for steering traffic (End behaviors)
 616#  $4 - single SID or double SID
 617#  $5 - traffic type (IPv6 or IPv4)
 618__setup_l3vpn()
 619{
 620	local src="$1"
 621	local dst="$2"
 622	local end_rts="$3"
 623	local mode="$4"
 624	local traffic="$5"
 625	local nsname
 626	local policy
 627	local container
 628	local decapsid
 629	local lcnfunc
 630	local dt
 631	local n
 632	local rtsrc_nsname
 633	local rtdst_nsname
 634
 635	eval rtsrc_nsname=\${$(get_rtname "${src}")}
 636	eval rtdst_nsname=\${$(get_rtname "${dst}")}
 637
 638	container="${LCBLOCK_ADDR}"
 639
 640	# build first SID (C-SID container)
 641	for n in ${end_rts}; do
 642		lcnfunc="$(build_csid "${n}")"
 643
 644		container="${container}${lcnfunc}"
 645	done
 646
 647	if [ "${mode}" -eq 1 ]; then
 648		# single SID policy
 649		dt="$(build_csid "${dst}")${DT46_FUNC}"
 650		container="${container}${dt}"
 651		# build the full ipv6 address for the container
 652		policy="$(build_ipv6_addr "${container}")"
 653
 654		# build the decap SID used in the decap node
 655		container="${LCBLOCK_ADDR}${dt}"
 656		decapsid="$(build_ipv6_addr "${container}")"
 657	else
 658		# double SID policy
 659		decapsid="${VPN_LOCATOR_SERVICE}:${dst}::${DT46_FUNC}"
 660
 661		policy="$(build_ipv6_addr "${container}"),${decapsid}"
 662	fi
 663
 664	# apply encap policy
 665	if [ "${traffic}" -eq 6 ]; then
 666		ip -netns "${rtsrc_nsname}" -6 route \
 667			add "${IPv6_HS_NETWORK}::${dst}" vrf "${VRF_DEVNAME}" \
 668			encap seg6 mode "${HEADEND_ENCAP}" segs "${policy}" \
 669			dev "${VRF_DEVNAME}"
 670
 671		ip -netns "${rtsrc_nsname}" -6 neigh \
 672			add proxy "${IPv6_HS_NETWORK}::${dst}" \
 673			dev "${RT2HS_DEVNAME}"
 674	else
 675		# "dev" must be different from the one where the packet is
 676		# received, otherwise the proxy arp does not work.
 677		ip -netns "${rtsrc_nsname}" -4 route \
 678			add "${IPv4_HS_NETWORK}.${dst}" vrf "${VRF_DEVNAME}" \
 679			encap seg6 mode "${HEADEND_ENCAP}" segs "${policy}" \
 680			dev "${VRF_DEVNAME}"
 681	fi
 682
 683	# apply decap
 684	# Local End.DT46 behavior (decap)
 685	ip -netns "${rtdst_nsname}" -6 route \
 686		add "${decapsid}" \
 687		table "${LOCALSID_TABLE_ID}" \
 688		encap seg6local action End.DT46 vrftable "${VRF_TID}" \
 689		dev "${VRF_DEVNAME}"
 690}
 691
 692# see __setup_l3vpn()
 693setup_ipv4_vpn_2sids()
 694{
 695	__setup_l3vpn "$1" "$2" "$3" 2 4
 696}
 697
 698# see __setup_l3vpn()
 699setup_ipv6_vpn_1sid()
 700{
 701	__setup_l3vpn "$1" "$2" "$3" 1 6
 702}
 703
 704setup_hs()
 705{
 706	local hs="$1"
 707	local rt="$2"
 708	local hsname
 709	local rtname
 710
 711	eval hsname=\${$(get_hsname "${hs}")}
 712	eval rtname=\${$(get_rtname "${rt}")}
 713
 714	ip netns exec "${hsname}" sysctl -wq net.ipv6.conf.all.accept_dad=0
 715	ip netns exec "${hsname}" sysctl -wq net.ipv6.conf.default.accept_dad=0
 716
 717	ip -netns "${hsname}" link add veth0 type veth \
 718		peer name "${RT2HS_DEVNAME}" netns "${rtname}"
 719
 720	ip -netns "${hsname}" addr \
 721		add "${IPv6_HS_NETWORK}::${hs}/64" dev veth0 nodad
 722	ip -netns "${hsname}" addr add "${IPv4_HS_NETWORK}.${hs}/24" dev veth0
 723
 724	ip -netns "${hsname}" link set veth0 up
 725	ip -netns "${hsname}" link set lo up
 726
 727	# configure the VRF on the router which is directly connected to the
 728	# source host.
 729	ip -netns "${rtname}" link \
 730		add "${VRF_DEVNAME}" type vrf table "${VRF_TID}"
 731	ip -netns "${rtname}" link set "${VRF_DEVNAME}" up
 732
 733	# enslave the veth interface connecting the router with the host to the
 734	# VRF in the access router
 735	ip -netns "${rtname}" link \
 736		set "${RT2HS_DEVNAME}" master "${VRF_DEVNAME}"
 737
 738	# set default routes to unreachable for both ipv6 and ipv4
 739	ip -netns "${rtname}" -6 route \
 740		add unreachable default metric 4278198272 \
 741		vrf "${VRF_DEVNAME}"
 742	ip -netns "${rtname}" -4 route \
 743		add unreachable default metric 4278198272 \
 744		vrf "${VRF_DEVNAME}"
 745
 746	ip -netns "${rtname}" addr \
 747		add "${IPv6_HS_NETWORK}::254/64" dev "${RT2HS_DEVNAME}" nodad
 748	ip -netns "${rtname}" addr \
 749		add "${IPv4_HS_NETWORK}.254/24" dev "${RT2HS_DEVNAME}"
 750
 751	ip -netns "${rtname}" link set "${RT2HS_DEVNAME}" up
 752
 753	ip netns exec "${rtname}" \
 754		sysctl -wq net.ipv6.conf."${RT2HS_DEVNAME}".proxy_ndp=1
 755	ip netns exec "${rtname}" \
 756		sysctl -wq net.ipv4.conf."${RT2HS_DEVNAME}".proxy_arp=1
 757
 758	ip netns exec "${rtname}" sh -c "echo 1 > /proc/sys/net/vrf/strict_mode"
 759}
 760
 761setup()
 762{
 763	local i
 764
 765	# create routers
 766	ROUTERS="1 2 3 4"; readonly ROUTERS
 767	for i in ${ROUTERS}; do
 768		create_router "${i}"
 769	done
 770
 771	# create hosts
 772	HOSTS="1 2"; readonly HOSTS
 773	for i in ${HOSTS}; do
 774		create_host "${i}"
 775	done
 776
 777	# set up the links for connecting routers
 778	add_link_rt_pairs 1 "2 3 4"
 779	add_link_rt_pairs 2 "3 4"
 780	add_link_rt_pairs 3 "4"
 781
 782	# set up the basic connectivity of routers and routes required for
 783	# reachability of SIDs.
 784	setup_rt_networking 1 "2 3 4"
 785	setup_rt_networking 2 "1 3 4"
 786	setup_rt_networking 3 "1 2 4"
 787	setup_rt_networking 4 "1 2 3"
 788
 789	# set up the hosts connected to routers
 790	setup_hs 1 1
 791	setup_hs 2 2
 792
 793	# set up default SRv6 Endpoints (i.e. SRv6 End and SRv6 End.DT46)
 794	setup_rt_local_sids 1 "2 3 4"
 795	setup_rt_local_sids 2 "1 3 4"
 796	setup_rt_local_sids 3 "1 2 4"
 797	setup_rt_local_sids 4 "1 2 3"
 798
 799	# set up SRv6 Policies
 800
 801	# create an IPv6 VPN between hosts hs-1 and hs-2.
 802	#
 803	# Direction hs-1 -> hs-2
 804	# - rt-1 encap (H.Encaps.Red)
 805	# - rt-4 SRv6 End behavior (NEXT-C-SID flavor)
 806	# - rt-3 SRv6 End behavior (NEXT-C-SID flavor)
 807	# - rt-2 SRv6 End.DT46 behavior
 808	setup_ipv6_vpn_1sid 1 2 "4 3"
 809
 810	# Direction hs2 -> hs-1
 811	# - rt-2 encap (H.Encaps.Red)
 812	# - rt-3 SRv6 End behavior (NEXT-C-SID flavor)
 813	# - rt-4 SRv6 End behavior (NEXT-C-SID flavor)
 814	# - rt-1 SRv6 End.DT46 behavior
 815	setup_ipv6_vpn_1sid 2 1 "3 4"
 816
 817	# create an IPv4 VPN between hosts hs-1 and hs-2
 818	#
 819	# Direction hs-1 -> hs-2
 820	# - rt-1 encap (H.Encaps.Red)
 821	# - rt-3 SRv6 End behavior (NEXT-C-SID flavor)
 822	# - rt-2 SRv6 End.DT46 behavior
 823	setup_ipv4_vpn_2sids 1 2 "3"
 824
 825	# Direction hs-2 -> hs-1
 826	# - rt-2 encap (H.Encaps.Red)
 827	# - rt-3 SRv6 End behavior (NEXT-C-SID flavor)
 828	# - rt-1 SRv6 End.DT46 behavior
 829	setup_ipv4_vpn_2sids 2 1 "3"
 830
 831	# testing environment was set up successfully
 832	SETUP_ERR=0
 833}
 834
 835check_rt_connectivity()
 836{
 837	local rtsrc="$1"
 838	local rtdst="$2"
 839	local prefix
 840	local rtsrc_nsname
 841
 842	eval rtsrc_nsname=\${$(get_rtname "${rtsrc}")}
 843
 844	prefix="$(get_network_prefix "${rtsrc}" "${rtdst}")"
 845
 846	ip netns exec "${rtsrc_nsname}" ping -c 1 -W "${PING_TIMEOUT_SEC}" \
 847		"${prefix}::${rtdst}" >/dev/null 2>&1
 848}
 849
 850check_and_log_rt_connectivity()
 851{
 852	local rtsrc="$1"
 853	local rtdst="$2"
 854
 855	check_rt_connectivity "${rtsrc}" "${rtdst}"
 856	log_test $? 0 "Routers connectivity: rt-${rtsrc} -> rt-${rtdst}"
 857}
 858
 859check_hs_ipv6_connectivity()
 860{
 861	local hssrc="$1"
 862	local hsdst="$2"
 863	local hssrc_nsname
 864
 865	eval hssrc_nsname=\${$(get_hsname "${hssrc}")}
 866
 867	ip netns exec "${hssrc_nsname}" ping -c 1 -W "${PING_TIMEOUT_SEC}" \
 868		"${IPv6_HS_NETWORK}::${hsdst}" >/dev/null 2>&1
 869}
 870
 871check_hs_ipv4_connectivity()
 872{
 873	local hssrc="$1"
 874	local hsdst="$2"
 875	local hssrc_nsname
 876
 877	eval hssrc_nsname=\${$(get_hsname "${hssrc}")}
 878
 879	ip netns exec "${hssrc_nsname}" ping -c 1 -W "${PING_TIMEOUT_SEC}" \
 880		"${IPv4_HS_NETWORK}.${hsdst}" >/dev/null 2>&1
 881}
 882
 883check_and_log_hs2gw_connectivity()
 884{
 885	local hssrc="$1"
 886
 887	check_hs_ipv6_connectivity "${hssrc}" 254
 888	log_test $? 0 "IPv6 Hosts connectivity: hs-${hssrc} -> gw"
 889
 890	check_hs_ipv4_connectivity "${hssrc}" 254
 891	log_test $? 0 "IPv4 Hosts connectivity: hs-${hssrc} -> gw"
 892}
 893
 894check_and_log_hs_ipv6_connectivity()
 895{
 896	local hssrc="$1"
 897	local hsdst="$2"
 898
 899	check_hs_ipv6_connectivity "${hssrc}" "${hsdst}"
 900	log_test $? 0 "IPv6 Hosts connectivity: hs-${hssrc} -> hs-${hsdst}"
 901}
 902
 903check_and_log_hs_ipv4_connectivity()
 904{
 905	local hssrc="$1"
 906	local hsdst="$2"
 907
 908	check_hs_ipv4_connectivity "${hssrc}" "${hsdst}"
 909	log_test $? 0 "IPv4 Hosts connectivity: hs-${hssrc} -> hs-${hsdst}"
 910}
 911
 912router_tests()
 913{
 914	local i
 915	local j
 916
 917	log_section "IPv6 routers connectivity test"
 918
 919	for i in ${ROUTERS}; do
 920		for j in ${ROUTERS}; do
 921			if [ "${i}" -eq "${j}" ]; then
 922				continue
 923			fi
 924
 925			check_and_log_rt_connectivity "${i}" "${j}"
 926		done
 927	done
 928}
 929
 930host2gateway_tests()
 931{
 932	local hs
 933
 934	log_section "IPv4/IPv6 connectivity test among hosts and gateways"
 935
 936	for hs in ${HOSTS}; do
 937		check_and_log_hs2gw_connectivity "${hs}"
 938	done
 939}
 940
 941host_vpn_tests()
 942{
 943	log_section "SRv6 VPN connectivity test hosts (h1 <-> h2, IPv6)"
 944
 945	check_and_log_hs_ipv6_connectivity 1 2
 946	check_and_log_hs_ipv6_connectivity 2 1
 947
 948	log_section "SRv6 VPN connectivity test hosts (h1 <-> h2, IPv4)"
 949
 950	check_and_log_hs_ipv4_connectivity 1 2
 951	check_and_log_hs_ipv4_connectivity 2 1
 952}
 953
 954__nextcsid_end_behavior_test()
 955{
 956	local nsname="$1"
 957	local cmd="$2"
 958	local blen="$3"
 959	local flen="$4"
 960	local layout=""
 961
 962	if [ "${blen}" != "d" ]; then
 963		layout="${layout} lblen ${blen}"
 964	fi
 965
 966	if [ "${flen}" != "d" ]; then
 967		layout="${layout} nflen ${flen}"
 968	fi
 969
 970	ip -netns "${nsname}" -6 route \
 971		"${cmd}" "${CSID_CNTR_PREFIX}" \
 972		table "${CSID_CNTR_RT_TABLE}" \
 973		encap seg6local action End flavors next-csid ${layout} \
 974		dev "${DUMMY_DEVNAME}" &>/dev/null
 975
 976	return "$?"
 977}
 978
 979rt_x_nextcsid_end_behavior_test()
 980{
 981	local rt="$1"
 982	local blen="$2"
 983	local flen="$3"
 984	local nsname
 985	local ret
 986
 987	eval nsname=\${$(get_rtname "${rt}")}
 988
 989	__nextcsid_end_behavior_test "${nsname}" "add" "${blen}" "${flen}"
 990	ret="$?"
 991	__nextcsid_end_behavior_test "${nsname}" "del" "${blen}" "${flen}"
 992
 993	return "${ret}"
 994}
 995
 996__parse_csid_container_cfg()
 997{
 998	local cfg="$1"
 999	local index="$2"
1000	local out
1001
1002	echo "${cfg}" | cut -d',' -f"${index}"
1003}
1004
1005csid_container_cfg_tests()
1006{
1007	local valid
1008	local blen
1009	local flen
1010	local cfg
1011	local ret
1012
1013	log_section "C-SID Container config tests (legend: d='kernel default')"
1014
1015	for cfg in "${CSID_CONTAINER_CFGS[@]}"; do
1016		blen="$(__parse_csid_container_cfg "${cfg}" 1)"
1017		flen="$(__parse_csid_container_cfg "${cfg}" 2)"
1018		valid="$(__parse_csid_container_cfg "${cfg}" 3)"
1019
1020		rt_x_nextcsid_end_behavior_test \
1021			"${CSID_CNTR_RT_ID_TEST}" \
1022			"${blen}" \
1023			"${flen}"
1024		ret="$?"
1025
1026		if [ "${valid}" == "y" ]; then
1027			log_test "${ret}" 0 \
1028				"Accept valid C-SID container cfg (lblen=${blen}, nflen=${flen})"
1029		else
1030			log_test "${ret}" 2 \
1031				"Reject invalid C-SID container cfg (lblen=${blen}, nflen=${flen})"
1032		fi
1033	done
1034}
1035
1036test_iproute2_supp_or_ksft_skip()
1037{
1038	if ! ip route help 2>&1 | grep -qo "next-csid"; then
1039		echo "SKIP: Missing SRv6 NEXT-C-SID flavor support in iproute2"
1040		exit "${ksft_skip}"
1041	fi
1042}
1043
1044test_dummy_dev_or_ksft_skip()
1045{
1046        local test_netns
1047
1048        test_netns="dummy-$(mktemp -u XXXXXXXX)"
1049
1050        if ! ip netns add "${test_netns}"; then
1051                echo "SKIP: Cannot set up netns for testing dummy dev support"
1052                exit "${ksft_skip}"
1053        fi
1054
1055        modprobe dummy &>/dev/null || true
1056        if ! ip -netns "${test_netns}" link \
1057                add "${DUMMY_DEVNAME}" type dummy; then
1058                echo "SKIP: dummy dev not supported"
1059
1060                ip netns del "${test_netns}"
1061                exit "${ksft_skip}"
1062        fi
1063
1064        ip netns del "${test_netns}"
1065}
1066
1067test_vrf_or_ksft_skip()
1068{
1069	modprobe vrf &>/dev/null || true
1070	if [ ! -e /proc/sys/net/vrf/strict_mode ]; then
1071		echo "SKIP: vrf sysctl does not exist"
1072		exit "${ksft_skip}"
1073	fi
1074}
1075
1076if [ "$(id -u)" -ne 0 ]; then
1077	echo "SKIP: Need root privileges"
1078	exit "${ksft_skip}"
1079fi
1080
1081# required programs to carry out this selftest
1082test_command_or_ksft_skip ip
1083test_command_or_ksft_skip ping
1084test_command_or_ksft_skip sysctl
1085test_command_or_ksft_skip grep
1086test_command_or_ksft_skip cut
1087
1088test_iproute2_supp_or_ksft_skip
1089test_dummy_dev_or_ksft_skip
1090test_vrf_or_ksft_skip
1091
1092set -e
1093trap cleanup EXIT
1094
1095setup
1096set +e
1097
1098csid_container_cfg_tests
1099
1100router_tests
1101host2gateway_tests
1102host_vpn_tests
1103
1104print_log_test_results
~/ Read-only file preview.