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