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