xref: /linux/tools/testing/selftests/net/srv6_encap_lookup_l3vpn_test.sh (revision 91ec2035134982b98fab0609a9fd8480e8217dc1)
1#!/bin/bash
2# SPDX-License-Identifier: GPL-2.0
3#
4# author: Andrea Mayer <andrea.mayer@uniroma2.it>
5
6# This test evaluates the SRv6 encap "lookup" attribute. After encapsulation
7# the router looks up the route for the first SID, that is the outer IPv6
8# destination of the encapsulated packet. The attribute selects the FIB table
9# used for this post-encap SID route lookup.
10#
11# Two routers (rt-1, rt-2) provide L3 VPN services over an IPv6 underlay
12# (fd00::/64). Each router uses a separate VRF per tenant, with default
13# blackhole routes (IPv4 and IPv6) to prevent traffic from leaking out of
14# the VRF. Tenant traffic is encapsulated, then decapsulated with an
15# End.DT46. Each router proxies both NDP and ARP.
16#
17# The routes that match the first SIDs are installed in a dedicated underlay
18# table (500) rather than the main table (254). The encap routes use
19# "lookup 500" to select this table for the post-encap SID route lookup.
20#
21# Without the "lookup" attribute, the route for the first SID cannot be found:
22#  - on the input path (forwarded traffic), the lookup stays in the VRF
23#    and hits the blackhole;
24#  - on the output path (locally originated traffic), the lookup falls
25#    through to the main table, with no route to the first SID.
26#
27#
28# Legend (specific per-tenant addresses are in the instantiation tables below):
29#   X          = tenant id and VRF table id; two tenants: 100 and 200
30#                ("tX" means tenant X, e.g. t100, t200)
31#   a, b       = the two host ids of the tenant
32#   HA, HB     = addresses of host a, host b
33#   RLO1, RLO2 = rlo-X router loopback address on rt-1, rt-2 (tenant gateway
34#                for the output path, dual-stack)
35#   vrf-X      = per-tenant VRF on each router (table X)
36#
37# Constants (same for every tenant):
38#   underlay   = table 500; post-encap SID route lookup (via "lookup 500")
39#   localsid   = table 90; holds the decap SIDs (End.DT46)
40#   fd00::/64  = underlay link between rt-1 and rt-2
41#   veth-tX    = cafe::254/10.0.0.254 (tenant gateway on veth, both routers)
42#
43#
44# +-------------------+                                   +-------------------+
45# |                   |                                   |                   |
46# |   hs-tX-a netns   |                                   |   hs-tX-b netns   |
47# |                   |                                   |                   |
48# |  +-------------+  |                                   |  +-------------+  |
49# |  |    veth0    |  |                                   |  |    veth0    |  |
50# |  |     HA      |  |                                   |  |     HB      |  |
51# |  +-------------+  |                                   |  +-------------+  |
52# |        .          |                                   |         .         |
53# +-------------------+                                   +-------------------+
54#          .                                                        .
55#          .                                                        .
56# +-----------------------------------+   +-----------------------------------+
57# |        .                          |   |                         .         |
58# | +---------------+                 |   |                 +---------------+ |
59# | |   veth-tX     |   +----------+  |   |  +----------+   |   veth-tX     | |
60# | |  ::254/.254   |   | localsid |  |   |  | localsid |   |  ::254/.254   | |
61# | +-------+-------+   +----------+  |   |  +----------+   +-------+-------+ |
62# |         |           +----------+  |   |  +----------+           |         |
63# |    +----+----+      | underlay |  |   |  | underlay |      +----+----+    |
64# |    | vrf-X   |      +----------+  |   |  +----------+      | vrf-X   |    |
65# |    +----+----+                    |   |                    +----+----+    |
66# |         |                         |   |                         |         |
67# |   +-----+----+    +------------+  |   |  +------------+    +----+-----+   |
68# |   |  rlo-X   |    |   veth0    |  |   |  |   veth0    |    |  rlo-X   |   |
69# |   |   RLO1   |    | fd00::1/64 |..|...|..| fd00::2/64 |    |   RLO2   |   |
70# |   +----------+    +------------+  |   |  +------------+    +----------+   |
71# |              rt-1 netns           |   |          rt-2 netns               |
72# +-----------------------------------+   +-----------------------------------+
73#
74#
75# Per-tenant instantiation:
76# +-----+------+-------------------+-------------------+
77# |  X  | a, b | HA                | HB                |
78# +-----+------+-------------------+-------------------+
79# | 100 | 1, 2 | cafe::1, 10.0.0.1 | cafe::2, 10.0.0.2 |
80# | 200 | 3, 4 | cafe::3, 10.0.0.3 | cafe::4, 10.0.0.4 |
81# +-----+------+-------------------+-------------------+
82#
83# Router loopback (rlo-X) addresses, per tenant:
84# +-----+-----------------------+-----------------------+
85# |  X  | RLO1 (rt-1)           | RLO2 (rt-2)           |
86# +-----+-----------------------+-----------------------+
87# | 100 | cafe::101, 10.0.0.101 | cafe::102, 10.0.0.102 |
88# | 200 | cafe::201, 10.0.0.201 | cafe::202, 10.0.0.202 |
89# +-----+-----------------------+-----------------------+
90#
91#
92# Network configuration
93# =====================
94#
95# rt-1: localsid table (table 90)
96# +--------+--------------------+----------------------------------+
97# | tenant | SID                | Action                           |
98# +--------+--------------------+----------------------------------+
99# |  100   | fc00:2:1:100::0d46 | apply SRv6 End.DT46 vrftable 100 |
100# |  200   | fc00:2:1:200::0d46 | apply SRv6 End.DT46 vrftable 200 |
101# +--------+--------------------+----------------------------------+
102#
103# rt-1: underlay table (table 500) - post-encap SID route lookup
104# +--------+--------------------+-------------------------------+
105# | tenant | SID                | Action                        |
106# +--------+--------------------+-------------------------------+
107# |  100   | fc00:1:2:100::0d46 | forward via fd00::2 dev veth0 |
108# |  200   | fc00:1:2:200::0d46 | forward via fd00::2 dev veth0 |
109# +--------+--------------------+-------------------------------+
110#
111# rt-1: VRF tables (per tenant: vrf-X = table X)
112# +--------+------------+------------------------------------------+
113# | tenant | dst        | encap action                             |
114# +--------+------------+------------------------------------------+
115# | 100    | cafe::2    | encap segs fc00:1:2:100::0d46 lookup 500 |
116# |        | 10.0.0.2   |                                          |
117# |        | cafe::102  |                                          |
118# |        | 10.0.0.102 |                                          |
119# | 200    | cafe::4    | encap segs fc00:1:2:200::0d46 lookup 500 |
120# |        | 10.0.0.4   |                                          |
121# |        | cafe::202  |                                          |
122# |        | 10.0.0.202 |                                          |
123# +--------+------------+------------------------------------------+
124#
125#
126# rt-2: localsid table (table 90)
127# +--------+--------------------+----------------------------------+
128# | tenant | SID                | Action                           |
129# +--------+--------------------+----------------------------------+
130# |  100   | fc00:1:2:100::0d46 | apply SRv6 End.DT46 vrftable 100 |
131# |  200   | fc00:1:2:200::0d46 | apply SRv6 End.DT46 vrftable 200 |
132# +--------+--------------------+----------------------------------+
133#
134# rt-2: underlay table (table 500) - post-encap SID route lookup
135# +--------+--------------------+-------------------------------+
136# | tenant | SID                | Action                        |
137# +--------+--------------------+-------------------------------+
138# |  100   | fc00:2:1:100::0d46 | forward via fd00::1 dev veth0 |
139# |  200   | fc00:2:1:200::0d46 | forward via fd00::1 dev veth0 |
140# +--------+--------------------+-------------------------------+
141#
142# rt-2: VRF tables (per tenant: vrf-X = table X)
143# +--------+------------+------------------------------------------+
144# | tenant | dst        | encap action                             |
145# +--------+------------+------------------------------------------+
146# | 100    | cafe::1    | encap segs fc00:2:1:100::0d46 lookup 500 |
147# |        | 10.0.0.1   |                                          |
148# |        | cafe::101  |                                          |
149# |        | 10.0.0.101 |                                          |
150# | 200    | cafe::3    | encap segs fc00:2:1:200::0d46 lookup 500 |
151# |        | 10.0.0.3   |                                          |
152# |        | cafe::201  |                                          |
153# |        | 10.0.0.201 |                                          |
154# +--------+------------+------------------------------------------+
155# Within a tenant, a single SID reaches the adjacent router (its loopback)
156# and the remote host connected to it, in both IPv4 and IPv6.
157#
158# For both rt-1 and rt-2, each VRF also has the connected host prefix (cafe::/64
159# or 10.0.0.0/24) and a default blackhole (IPv4 and IPv6).
160#
161#
162# Locally originated traffic (output path)
163# ========================================
164#
165# The configuration above covers forwarded traffic, where packets arrive from
166# a host and are encapsulated by the router. To also test router-originated
167# traffic, each router pings the other router's loopback address through
168# the VPN.
169#
170# Example (tenant 100), rt-1 pings cafe::102 (rt-2's loopback):
171#   1. rt-1 looks up cafe::102 in vrf-100 and encapsulates it (SID
172#      fc00:1:2:100::0d46), then "lookup 500" finds the route for the SID in the
173#      underlay table (next hop fd00::2) and forwards it;
174#   2. rt-2 decapsulates it (localsid, End.DT46) and delivers it locally
175#      (cafe::102 is on the rlo-100 interface);
176#   3. rt-2 replies with destination cafe::101 (rt-1's loopback). rt-2 looks up
177#      cafe::101 in vrf-100 and encapsulates it back to rt-1 (again via "lookup
178#      500"). rt-1 decapsulates it and delivers it.
179
180# shellcheck source=lib.sh
181source lib.sh
182
183readonly LOCALSID_TABLE_ID=90
184readonly UNDERLAY_TABLE_ID=500
185readonly IPv6_RT_NETWORK=fd00
186readonly IPv6_HS_NETWORK=cafe
187readonly IPv4_HS_NETWORK=10.0.0
188readonly VPN_LOCATOR_SERVICE=fc00
189readonly DT46_FUNC=0d46
190readonly DUMMY_DEVNAME=dum0
191readonly IPv6_TESTS_ADDR=2001:db8::1
192readonly TESTS_TABLE_ID=54321
193PING_TIMEOUT_SEC=4
194
195SETUP_ERR=1
196
197ret=${ksft_skip}
198nsuccess=0
199nfail=0
200
201PAUSE_ON_FAIL=${PAUSE_ON_FAIL:=no}
202
203log_test()
204{
205	local rc="$1"
206	local expected="$2"
207	local msg="$3"
208
209	if [ "${rc}" -eq "${expected}" ]; then
210		nsuccess=$((nsuccess+1))
211		printf "\n    TEST: %-60s  [ OK ]\n" "${msg}"
212	else
213		ret=1
214		nfail=$((nfail+1))
215		printf "\n    TEST: %-60s  [FAIL]\n" "${msg}"
216		if [ "${PAUSE_ON_FAIL}" = "yes" ]; then
217			echo
218			echo "hit enter to continue, 'q' to quit"
219			read -r a
220			[ "$a" = "q" ] && exit 1
221		fi
222	fi
223}
224
225print_log_test_results()
226{
227	printf "\nTests passed: %3d\n" "${nsuccess}"
228	printf "Tests failed: %3d\n"   "${nfail}"
229
230	# when a test fails, the value of 'ret' is set to 1 (error code).
231	# Conversely, when all tests are passed successfully, the 'ret' value
232	# is set to 0 (success code).
233	if [ "${ret}" -ne 1 ]; then
234		ret=0
235	fi
236}
237
238log_section()
239{
240	echo
241	echo "################################################################################"
242	echo "TEST SECTION: $*"
243	echo "################################################################################"
244}
245
246get_rtname()
247{
248	local rtid="$1"
249
250	echo "rt_${rtid}"
251}
252
253get_rt_nsname()
254{
255	local rtid="$1"
256	local varname
257
258	varname="$(get_rtname "${rtid}")"
259	echo "${!varname}"
260}
261
262get_hsname()
263{
264	local tid="$1"
265	local hsid="$2"
266
267	echo "hs_t${tid}_${hsid}"
268}
269
270get_hs_nsname()
271{
272	local tid="$1"
273	local hsid="$2"
274	local varname
275
276	varname="$(get_hsname "${tid}" "${hsid}")"
277	echo "${!varname}"
278}
279
280cleanup()
281{
282	ip link del veth-rt-1 2>/dev/null || true
283	ip link del veth-rt-2 2>/dev/null || true
284
285	cleanup_all_ns
286
287	# check whether the setup phase was completed successfully or not. In
288	# case of an error during the setup phase of the testing environment,
289	# the selftest is considered as "skipped".
290	if [ "${SETUP_ERR}" -ne 0 ]; then
291		echo "SKIP: Setting up the testing environment failed"
292		exit "${ksft_skip}"
293	fi
294
295	exit "${ret}"
296}
297
298# Host id of the router loopback (rlo) for a (router, tenant) pair.
299# E.g. rt-1/tenant 100 -> 101, rt-2/tenant 200 -> 202.
300get_rlo_hostid()
301{
302	local rtid="$1"
303	local tid="$2"
304
305	echo "$((tid + rtid))"
306}
307
308build_vpn_sid()
309{
310	local rtsrc="$1"
311	local rtdst="$2"
312	local tid="$3"
313
314	echo "${VPN_LOCATOR_SERVICE}:${rtsrc}:${rtdst}:${tid}::${DT46_FUNC}"
315}
316
317# Install a dual-stack (IPv6 and IPv4) encap route in a VRF on the given
318# router.
319# args:
320#  $1 - router id
321#  $2 - host part of the IPv6 destination
322#  $3 - host part of the IPv4 destination
323#  $4 - SRv6 SID used as the encap destination
324#  $5 - tenant id
325#  $6 - if "true", add the "lookup" attribute to the encap route
326__set_encap_route()
327{
328	local rt="$1"
329	local dst6="$2"
330	local dst4="$3"
331	local sid="$4"
332	local tid="$5"
333	local use_lookup="$6"
334	local lookup=''
335	local rtname
336
337	rtname="$(get_rt_nsname "${rt}")"
338
339	if [ "${use_lookup}" = "true" ]; then
340		lookup="lookup ${UNDERLAY_TABLE_ID}"
341	fi
342
343	# shellcheck disable=SC2086
344	ip -netns "${rtname}" -6 route replace \
345		"${IPv6_HS_NETWORK}::${dst6}/128" vrf "vrf-${tid}" \
346		encap seg6 mode encap segs "${sid}" ${lookup} dev veth0
347
348	# shellcheck disable=SC2086
349	ip -netns "${rtname}" -4 route replace \
350		"${IPv4_HS_NETWORK}.${dst4}/32" vrf "vrf-${tid}" \
351		encap seg6 mode encap segs "${sid}" ${lookup} dev veth0
352}
353
354# Install the dual-stack encap route for a tenant host on rt, with the
355# "lookup" attribute so the first SID is looked up in the underlay table.
356# args:
357#  $1 - router id where the encap route is installed
358#  $2 - host destination id (host part of cafe::<id>/128 and 10.0.0.<id>/32)
359#  $3 - SRv6 SID used as the encap destination
360#  $4 - tenant id
361set_host_encap_route()
362{
363	local rt="$1"
364	local hsdst="$2"
365	local sid="$3"
366	local tid="$4"
367
368	__set_encap_route "${rt}" "${hsdst}" "${hsdst}" "${sid}" "${tid}" true
369}
370
371set_host_encap_route_nolookup()
372{
373	local rt="$1"
374	local hsdst="$2"
375	local sid="$3"
376	local tid="$4"
377
378	__set_encap_route "${rt}" "${hsdst}" "${hsdst}" "${sid}" "${tid}" false
379}
380
381# Install the dual-stack encap route on rtsrc toward rtdst's rlo loopback
382# (RLO1 or RLO2, see header), with the "lookup" attribute so the first
383# SID is looked up in the underlay table.
384# args:
385#  $1 - router id where the encap route is installed
386#  $2 - router id whose loopback address is the route destination
387#  $3 - SRv6 SID used as the encap destination
388#  $4 - tenant id
389set_gw_encap_route()
390{
391	local rtsrc="$1"
392	local rtdst="$2"
393	local sid="$3"
394	local tid="$4"
395	local dst
396
397	dst="$(get_rlo_hostid "${rtdst}" "${tid}")"
398
399	__set_encap_route "${rtsrc}" "${dst}" "${dst}" "${sid}" "${tid}" true
400}
401
402set_gw_encap_route_nolookup()
403{
404	local rtsrc="$1"
405	local rtdst="$2"
406	local sid="$3"
407	local tid="$4"
408	local dst
409
410	dst="$(get_rlo_hostid "${rtdst}" "${tid}")"
411
412	__set_encap_route "${rtsrc}" "${dst}" "${dst}" "${sid}" "${tid}" false
413}
414
415# Setup the basic networking for a router
416setup_rt_networking()
417{
418	local id="$1"
419	local nsname
420
421	nsname="$(get_rt_nsname "${id}")"
422
423	ip link set "veth-rt-${id}" netns "${nsname}"
424	ip -netns "${nsname}" link set "veth-rt-${id}" name veth0
425
426	ip netns exec "${nsname}" sysctl -wq net.ipv6.conf.all.accept_dad=0
427	ip netns exec "${nsname}" sysctl -wq net.ipv6.conf.default.accept_dad=0
428
429	ip -netns "${nsname}" addr add "${IPv6_RT_NETWORK}::${id}/64" dev veth0 nodad
430	ip -netns "${nsname}" link set veth0 up
431
432	ip netns exec "${nsname}" sysctl -wq net.ipv4.ip_forward=1
433	ip netns exec "${nsname}" sysctl -wq net.ipv6.conf.all.forwarding=1
434}
435
436# Setup a host namespace and attach it to its gateway
437setup_hs()
438{
439	local hid="$1"
440	local rid="$2"
441	local tid="$3"
442	local rtveth="veth-t${tid}"
443	local hsname
444	local rtname
445
446	hsname="$(get_hs_nsname "${tid}" "${hid}")"
447	rtname="$(get_rt_nsname "${rid}")"
448
449	ip netns exec "${hsname}" sysctl -wq net.ipv6.conf.all.accept_dad=0
450	ip netns exec "${hsname}" sysctl -wq net.ipv6.conf.default.accept_dad=0
451
452	ip -netns "${hsname}" link add veth0 type veth peer name "${rtveth}"
453	ip -netns "${hsname}" link set "${rtveth}" netns "${rtname}"
454
455	ip -netns "${hsname}" addr add \
456		"${IPv6_HS_NETWORK}::${hid}/64" dev veth0 nodad
457	ip -netns "${hsname}" addr add \
458		"${IPv4_HS_NETWORK}.${hid}/24" dev veth0
459
460	ip -netns "${hsname}" link set veth0 up
461}
462
463# Setup the per-tenant VRF on a router (gateway, loopback, blackhole)
464setup_rt()
465{
466	local rid="$1"
467	local tid="$2"
468	local rtveth="veth-t${tid}"
469	local rlo_dev="rlo-${tid}"
470	local rtname
471	local gw_addr_v6
472	local gw_addr_v4
473
474	rtname="$(get_rt_nsname "${rid}")"
475
476	gw_addr_v6="${IPv6_HS_NETWORK}::$(get_rlo_hostid "${rid}" "${tid}")"
477	gw_addr_v4="${IPv4_HS_NETWORK}.$(get_rlo_hostid "${rid}" "${tid}")"
478
479	ip -netns "${rtname}" link add "vrf-${tid}" type vrf table "${tid}"
480	ip -netns "${rtname}" link set "vrf-${tid}" up
481
482	ip -netns "${rtname}" link set "${rtveth}" master "vrf-${tid}"
483
484	ip -netns "${rtname}" addr add \
485		"${IPv6_HS_NETWORK}::254/64" dev "${rtveth}" nodad
486	ip -netns "${rtname}" addr add \
487		"${IPv4_HS_NETWORK}.254/24" dev "${rtveth}"
488
489	ip -netns "${rtname}" link set "${rtveth}" up
490
491	ip netns exec "${rtname}" \
492		sysctl -wq "net.ipv6.conf.${rtveth}.proxy_ndp=1"
493	ip netns exec "${rtname}" \
494		sysctl -wq "net.ipv4.conf.${rtveth}.proxy_arp=1"
495
496	ip netns exec "${rtname}" sh -c "echo 1 > /proc/sys/net/vrf/strict_mode"
497
498	# router loopback interface for locally originated traffic
499	ip -netns "${rtname}" link add "${rlo_dev}" type dummy
500	ip -netns "${rtname}" link set "${rlo_dev}" master "vrf-${tid}"
501
502	ip -netns "${rtname}" addr add "${gw_addr_v6}/128" \
503		dev "${rlo_dev}" nodad
504	ip -netns "${rtname}" addr add "${gw_addr_v4}/32" \
505		dev "${rlo_dev}"
506
507	ip -netns "${rtname}" link set "${rlo_dev}" up
508
509	# default blackhole routes in the VRF: any traffic that does not match
510	# a specific route is dropped. Without the "lookup" attribute on the
511	# encap route, the route for the first SID cannot be found from within
512	# the VRF.
513	ip -netns "${rtname}" -6 route add blackhole default metric 4278198272 \
514		vrf "vrf-${tid}"
515	ip -netns "${rtname}" -4 route add blackhole default metric 4278198272 \
516		vrf "vrf-${tid}"
517}
518
519# Configure a one-way VPN path towards hsdst (on rtdst) for tenant tid.
520# The encap side is set up on rtsrc and the decap side on rtdst.
521# args:
522#  $1 - router id where the encap side is set up
523#  $2 - host id of the destination host
524#  $3 - router id of the destination router (connected to the destination host)
525#  $4 - tenant id
526setup_vpn_config()
527{
528	local rtsrc="$1"
529	local hsdst="$2"
530	local rtdst="$3"
531	local tid="$4"
532	local rtveth="veth-t${tid}"
533	local rtsrc_name
534	local rtdst_name
535	local vpn_sid
536
537	rtsrc_name="$(get_rt_nsname "${rtsrc}")"
538	rtdst_name="$(get_rt_nsname "${rtdst}")"
539	vpn_sid="$(build_vpn_sid "${rtsrc}" "${rtdst}" "${tid}")"
540
541	ip -netns "${rtsrc_name}" -6 neigh add proxy \
542		"${IPv6_HS_NETWORK}::${hsdst}" dev "${rtveth}"
543	set_host_encap_route "${rtsrc}" "${hsdst}" "${vpn_sid}" "${tid}"
544
545	ip -netns "${rtsrc_name}" -6 route add "${vpn_sid}/128" \
546		table "${UNDERLAY_TABLE_ID}" \
547		via "fd00::${rtdst}" dev veth0
548
549	# set the decap route for decapsulating packets arriving from rtsrc
550	# and destined to hsdst
551	ip -netns "${rtdst_name}" -6 route add "${vpn_sid}/128" \
552		table "${LOCALSID_TABLE_ID}" \
553		encap seg6local action End.DT46 \
554		vrftable "${tid}" dev "vrf-${tid}"
555
556	# all SIDs for VPNs start with a common locator which is fc00::/16.
557	# Routes for handling the SRv6 End.DT* behavior instances are grouped
558	# together in the 'localsid' table.
559	#
560	# NOTE: added only once
561	if ! ip -netns "${rtdst_name}" -6 rule show | \
562	    grep -q "to ${VPN_LOCATOR_SERVICE}::/16 lookup ${LOCALSID_TABLE_ID}"; then
563		ip -netns "${rtdst_name}" -6 rule add \
564			to "${VPN_LOCATOR_SERVICE}::/16" \
565			lookup "${LOCALSID_TABLE_ID}" prio 999
566	fi
567}
568
569# Configure rtsrc to reach rtdst's loopback address through the VPN.
570# args:
571#  $1 - router id where the encap route is installed
572#  $2 - router id whose loopback is the destination
573#  $3 - tenant id
574setup_vpn_gw_encap()
575{
576	local rtsrc="$1"
577	local rtdst="$2"
578	local tid="$3"
579	local sid
580
581	sid="$(build_vpn_sid "${rtsrc}" "${rtdst}" "${tid}")"
582
583	set_gw_encap_route "${rtsrc}" "${rtdst}" "${sid}" "${tid}"
584}
585
586setup()
587{
588	ip link add veth-rt-1 type veth peer name veth-rt-2
589	setup_ns rt_1 rt_2
590	setup_rt_networking 1
591	setup_rt_networking 2
592
593	# setup two hosts for the tenant 100.
594	#  - host hs-t100-1 is directly connected to the router rt-1;
595	#  - host hs-t100-2 is directly connected to the router rt-2.
596	setup_ns hs_t100_1 hs_t100_2
597	setup_hs 1 1 100
598	setup_hs 2 2 100
599
600	# setup two hosts for the tenant 200.
601	#  - host hs-t200-3 is directly connected to the router rt-1;
602	#  - host hs-t200-4 is directly connected to the router rt-2.
603	setup_ns hs_t200_3 hs_t200_4
604	setup_hs 3 1 200
605	setup_hs 4 2 200
606
607	# configure each router for each tenant: VRF, blackhole routes,
608	# router loopback interface
609	setup_rt 1 100
610	setup_rt 2 100
611	setup_rt 1 200
612	setup_rt 2 200
613
614	# setup the L3 VPN which connects the host hs-t100-1 and host hs-t100-2
615	# within the same tenant 100.
616	setup_vpn_config 1 2 2 100
617	setup_vpn_config 2 1 1 100
618
619	# setup the L3 VPN which connects the host hs-t200-3 and host hs-t200-4
620	# within the same tenant 200.
621	setup_vpn_config 1 4 2 200
622	setup_vpn_config 2 3 1 200
623
624	# allow each router to reach the other's loopback through the VPN
625	setup_vpn_gw_encap 2 1 100
626	setup_vpn_gw_encap 1 2 100
627	setup_vpn_gw_encap 2 1 200
628	setup_vpn_gw_encap 1 2 200
629
630	# testing environment was set up successfully
631	SETUP_ERR=0
632}
633
634check_rt_connectivity()
635{
636	local rtsrc="$1"
637	local rtdst="$2"
638	local nsname
639
640	nsname="$(get_rt_nsname "${rtsrc}")"
641
642	ip netns exec "${nsname}" ping -c 1 -W 1 "${IPv6_RT_NETWORK}::${rtdst}" \
643		>/dev/null 2>&1
644}
645
646check_and_log_rt_connectivity()
647{
648	local rtsrc="$1"
649	local rtdst="$2"
650
651	check_rt_connectivity "${rtsrc}" "${rtdst}"
652	log_test $? 0 "Routers connectivity: rt-${rtsrc} -> rt-${rtdst}"
653}
654
655check_hs_ipv6_connectivity()
656{
657	local hssrc="$1"
658	local hsdst="$2"
659	local tid="$3"
660	local nsname
661
662	nsname="$(get_hs_nsname "${tid}" "${hssrc}")"
663
664	ip netns exec "${nsname}" ping -c 1 -W "${PING_TIMEOUT_SEC}" \
665		"${IPv6_HS_NETWORK}::${hsdst}" >/dev/null 2>&1
666}
667
668check_hs_ipv4_connectivity()
669{
670	local hssrc="$1"
671	local hsdst="$2"
672	local tid="$3"
673	local nsname
674
675	nsname="$(get_hs_nsname "${tid}" "${hssrc}")"
676
677	ip netns exec "${nsname}" ping -c 1 -W "${PING_TIMEOUT_SEC}" \
678		"${IPv4_HS_NETWORK}.${hsdst}" >/dev/null 2>&1
679}
680
681check_and_log_hs_connectivity()
682{
683	local hssrc="$1"
684	local hsdst="$2"
685	local tid="$3"
686
687	check_hs_ipv6_connectivity "${hssrc}" "${hsdst}" "${tid}"
688	log_test $? 0 "IPv6 connectivity: hs-t${tid}-${hssrc} -> hs-t${tid}-${hsdst} (tenant ${tid})"
689
690	check_hs_ipv4_connectivity "${hssrc}" "${hsdst}" "${tid}"
691	log_test $? 0 "IPv4 connectivity: hs-t${tid}-${hssrc} -> hs-t${tid}-${hsdst} (tenant ${tid})"
692}
693
694check_and_log_hs_isolation()
695{
696	local hssrc="$1"
697	local tidsrc="$2"
698	local hsdst="$3"
699	local tiddst="$4"
700
701	check_hs_ipv6_connectivity "${hssrc}" "${hsdst}" "${tidsrc}"
702	log_test $? 1 "IPv6 isolation: hs-t${tidsrc}-${hssrc} -X-> hs-t${tiddst}-${hsdst}"
703
704	check_hs_ipv4_connectivity "${hssrc}" "${hsdst}" "${tidsrc}"
705	log_test $? 1 "IPv4 isolation: hs-t${tidsrc}-${hssrc} -X-> hs-t${tiddst}-${hsdst}"
706}
707
708check_and_log_hs2gw_connectivity()
709{
710	local hssrc="$1"
711	local tid="$2"
712
713	check_hs_ipv6_connectivity "${hssrc}" 254 "${tid}"
714	log_test $? 0 "IPv6 connectivity: hs-t${tid}-${hssrc} -> gw (tenant ${tid})"
715
716	check_hs_ipv4_connectivity "${hssrc}" 254 "${tid}"
717	log_test $? 0 "IPv4 connectivity: hs-t${tid}-${hssrc} -> gw (tenant ${tid})"
718}
719
720router_tests()
721{
722	log_section "IPv6 routers connectivity test"
723
724	check_and_log_rt_connectivity 1 2
725	check_and_log_rt_connectivity 2 1
726}
727
728host2gateway_tests()
729{
730	log_section "Connectivity test among hosts and gateway"
731
732	check_and_log_hs2gw_connectivity 1 100
733	check_and_log_hs2gw_connectivity 2 100
734
735	check_and_log_hs2gw_connectivity 3 200
736	check_and_log_hs2gw_connectivity 4 200
737}
738
739host_vpn_tests()
740{
741	log_section "SRv6 VPN connectivity test among hosts in the same tenant"
742
743	check_and_log_hs_connectivity 1 2 100
744	check_and_log_hs_connectivity 2 1 100
745
746	check_and_log_hs_connectivity 3 4 200
747	check_and_log_hs_connectivity 4 3 200
748}
749
750host_vpn_isolation_tests()
751{
752	local l1="1 2"
753	local l2="3 4"
754	local t1=100
755	local t2=200
756	local i
757	local j
758	local tmp
759
760	log_section "SRv6 VPN isolation test among hosts in different tenants"
761
762	for _ in 0 1; do
763		for i in ${l1}; do
764			for j in ${l2}; do
765				check_and_log_hs_isolation "${i}" "${t1}" "${j}" "${t2}"
766			done
767		done
768
769		# let us test the reverse path
770		tmp="${l1}"; l1="${l2}"; l2="${tmp}"
771		tmp=${t1}; t1=${t2}; t2=${tmp}
772	done
773}
774
775__test_nolookup()
776{
777	local hssrc="$1"
778	local hsdst="$2"
779	local rtsrc="$3"
780	local rtdst="$4"
781	local tid="$5"
782	local vpn_sid
783
784	vpn_sid="$(build_vpn_sid "${rtsrc}" "${rtdst}" "${tid}")"
785
786	# replace encap route(s) without "lookup" attribute
787	set_host_encap_route_nolookup "${rtsrc}" "${hsdst}" "${vpn_sid}" "${tid}"
788
789	check_hs_ipv6_connectivity "${hssrc}" "${hsdst}" "${tid}"
790	log_test $? 1 "IPv6 w/o lookup: hs-t${tid}-${hssrc} -X-> hs-t${tid}-${hsdst} (tenant ${tid})"
791
792	check_hs_ipv4_connectivity "${hssrc}" "${hsdst}" "${tid}"
793	log_test $? 1 "IPv4 w/o lookup: hs-t${tid}-${hssrc} -X-> hs-t${tid}-${hsdst} (tenant ${tid})"
794
795	# restore encap route(s) with "lookup" for subsequent tests
796	set_host_encap_route "${rtsrc}" "${hsdst}" "${vpn_sid}" "${tid}"
797}
798
799host_vpn_nolookup_tests()
800{
801	log_section "SRv6 VPN connectivity test among hosts w/o lookup"
802
803	__test_nolookup 1 2 1 2 100
804	__test_nolookup 2 1 2 1 100
805
806	__test_nolookup 3 4 1 2 200
807	__test_nolookup 4 3 2 1 200
808}
809
810check_gw_ipv6_connectivity()
811{
812	local rtsrc="$1"
813	local rtdst="$2"
814	local tidsrc="$3"
815	local tiddst="$4"
816	local rtname
817	local src_v6
818	local dst_v6
819
820	rtname="$(get_rt_nsname "${rtsrc}")"
821	src_v6="${IPv6_HS_NETWORK}::$(get_rlo_hostid "${rtsrc}" "${tidsrc}")"
822	dst_v6="${IPv6_HS_NETWORK}::$(get_rlo_hostid "${rtdst}" "${tiddst}")"
823
824	ip netns exec "${rtname}" ip vrf exec "vrf-${tidsrc}" \
825		ping -c 1 -W "${PING_TIMEOUT_SEC}" \
826		-I "${src_v6}" "${dst_v6}" >/dev/null 2>&1
827}
828
829check_gw_ipv4_connectivity()
830{
831	local rtsrc="$1"
832	local rtdst="$2"
833	local tidsrc="$3"
834	local tiddst="$4"
835	local rtname
836	local src_v4
837	local dst_v4
838
839	rtname="$(get_rt_nsname "${rtsrc}")"
840	src_v4="${IPv4_HS_NETWORK}.$(get_rlo_hostid "${rtsrc}" "${tidsrc}")"
841	dst_v4="${IPv4_HS_NETWORK}.$(get_rlo_hostid "${rtdst}" "${tiddst}")"
842
843	ip netns exec "${rtname}" ip vrf exec "vrf-${tidsrc}" \
844		ping -c 1 -W "${PING_TIMEOUT_SEC}" \
845		-I "${src_v4}" "${dst_v4}" >/dev/null 2>&1
846}
847
848check_and_log_gw_connectivity()
849{
850	local rtsrc="$1"
851	local rtdst="$2"
852	local tid="$3"
853
854	check_gw_ipv6_connectivity "${rtsrc}" "${rtdst}" "${tid}" "${tid}"
855	log_test $? 0 "IPv6 connectivity: rt-${rtsrc} -> rt-${rtdst} (tenant ${tid})"
856
857	check_gw_ipv4_connectivity "${rtsrc}" "${rtdst}" "${tid}" "${tid}"
858	log_test $? 0 "IPv4 connectivity: rt-${rtsrc} -> rt-${rtdst} (tenant ${tid})"
859}
860
861check_and_log_gw_isolation()
862{
863	local rtsrc="$1"
864	local rtdst="$2"
865	local tidsrc="$3"
866	local tiddst="$4"
867
868	check_gw_ipv6_connectivity "${rtsrc}" "${rtdst}" "${tidsrc}" "${tiddst}"
869	log_test $? 1 "IPv6 isolation: rt-${rtsrc} -X-> rt-${rtdst} (tenants ${tidsrc}/${tiddst})"
870
871	check_gw_ipv4_connectivity "${rtsrc}" "${rtdst}" "${tidsrc}" "${tiddst}"
872	log_test $? 1 "IPv4 isolation: rt-${rtsrc} -X-> rt-${rtdst} (tenants ${tidsrc}/${tiddst})"
873}
874
875gw_vpn_isolation_tests()
876{
877	log_section "SRv6 VPN isolation test among routers in different tenants"
878
879	check_and_log_gw_isolation 1 2 100 200
880	check_and_log_gw_isolation 2 1 100 200
881
882	check_and_log_gw_isolation 1 2 200 100
883	check_and_log_gw_isolation 2 1 200 100
884}
885
886gw_vpn_tests()
887{
888	log_section "SRv6 VPN connectivity test among routers in the same tenant"
889
890	check_and_log_gw_connectivity 1 2 100
891	check_and_log_gw_connectivity 2 1 100
892
893	check_and_log_gw_connectivity 1 2 200
894	check_and_log_gw_connectivity 2 1 200
895}
896
897__test_gw_nolookup()
898{
899	local rtsrc="$1"
900	local rtdst="$2"
901	local tid="$3"
902	local sid
903
904	sid="$(build_vpn_sid "${rtsrc}" "${rtdst}" "${tid}")"
905
906	# replace gw encap route without "lookup" attribute
907	set_gw_encap_route_nolookup "${rtsrc}" "${rtdst}" "${sid}" "${tid}"
908
909	check_gw_ipv6_connectivity "${rtsrc}" "${rtdst}" "${tid}" "${tid}"
910	log_test $? 1 "IPv6 w/o lookup: rt-${rtsrc} -X-> rt-${rtdst} (tenant ${tid})"
911
912	check_gw_ipv4_connectivity "${rtsrc}" "${rtdst}" "${tid}" "${tid}"
913	log_test $? 1 "IPv4 w/o lookup: rt-${rtsrc} -X-> rt-${rtdst} (tenant ${tid})"
914
915	# restore gw encap route with "lookup" for subsequent tests
916	set_gw_encap_route "${rtsrc}" "${rtdst}" "${sid}" "${tid}"
917}
918
919gw_vpn_nolookup_tests()
920{
921	log_section "SRv6 VPN connectivity test among routers w/o lookup"
922
923	__test_gw_nolookup 1 2 100
924	__test_gw_nolookup 2 1 100
925
926	__test_gw_nolookup 1 2 200
927	__test_gw_nolookup 2 1 200
928}
929
930test_command_or_ksft_skip()
931{
932	local cmd="$1"
933
934	if [ ! -x "$(command -v "${cmd}")" ]; then
935		echo "SKIP: Could not run test without \"${cmd}\" tool"
936		exit "${ksft_skip}"
937	fi
938}
939
940test_vrf_or_ksft_skip()
941{
942	modprobe vrf &>/dev/null || true
943	if [ ! -e /proc/sys/net/vrf/strict_mode ]; then
944		echo "SKIP: vrf sysctl does not exist"
945		exit "${ksft_skip}"
946	fi
947}
948
949test_dummy_dev_or_ksft_skip()
950{
951	local test_netns
952
953	setup_ns test_netns
954
955	modprobe dummy &>/dev/null || true
956	if ! ip -netns "${test_netns}" link add "${DUMMY_DEVNAME}" \
957			type dummy; then
958		cleanup_ns "${test_netns}"
959		echo "SKIP: dummy dev not supported"
960		exit "${ksft_skip}"
961	fi
962
963	cleanup_ns "${test_netns}"
964}
965
966test_encap_lookup_supp_or_ksft_skip()
967{
968	local nsname
969
970	setup_ns nsname
971
972	ip -netns "${nsname}" link add "${DUMMY_DEVNAME}" type dummy
973	ip -netns "${nsname}" link set "${DUMMY_DEVNAME}" up
974
975	if ! ip -netns "${nsname}" -6 route add "${IPv6_TESTS_ADDR}/128" \
976			encap seg6 mode encap segs fc00::1 \
977			lookup "${TESTS_TABLE_ID}" \
978			dev "${DUMMY_DEVNAME}" 2>/dev/null; then
979		cleanup_ns "${nsname}"
980		echo "SKIP: seg6 encap lookup attribute not supported"
981		exit "${ksft_skip}"
982	fi
983
984	# An old kernel with a recent iproute2 accepts the route but
985	# silently ignores the lookup attribute. Dump the route and check
986	# the attribute is really there, otherwise the test falsely passes.
987	if ! ip -netns "${nsname}" -6 route show "${IPv6_TESTS_ADDR}/128" | \
988			grep -q "lookup ${TESTS_TABLE_ID}"; then
989		cleanup_ns "${nsname}"
990		echo "SKIP: seg6 encap lookup attribute not supported"
991		exit "${ksft_skip}"
992	fi
993
994	cleanup_ns "${nsname}"
995}
996
997if [ "$(id -u)" -ne 0 ]; then
998	echo "SKIP: Need root privileges"
999	exit "${ksft_skip}"
1000fi
1001
1002# required programs to carry out this selftest
1003test_command_or_ksft_skip ip
1004test_command_or_ksft_skip ping
1005test_command_or_ksft_skip sysctl
1006test_command_or_ksft_skip grep
1007
1008test_dummy_dev_or_ksft_skip
1009test_vrf_or_ksft_skip
1010test_encap_lookup_supp_or_ksft_skip
1011
1012set -e
1013trap cleanup EXIT
1014
1015setup
1016set +e
1017
1018router_tests
1019host2gateway_tests
1020host_vpn_tests
1021host_vpn_isolation_tests
1022host_vpn_nolookup_tests
1023gw_vpn_tests
1024gw_vpn_isolation_tests
1025gw_vpn_nolookup_tests
1026
1027print_log_test_results
1028