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