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
1105