xref: /linux/tools/testing/selftests/net/srv6_hl2encap_red_l2vpn_test.sh (revision c532de5a67a70f8533d495f8f2aaa9a0491c3ad0)
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 SRv6 H.L2Encaps.Red behavior.
7#
8# Below is depicted the IPv6 network of an operator which offers L2 VPN
9# services to hosts, enabling them to communicate with each other.
10# In this example, hosts hs-1 and hs-2 are connected through an L2 VPN service.
11# Currently, the SRv6 subsystem in Linux allows hosts hs-1 and hs-2 to exchange
12# full L2 frames as long as they carry IPv4/IPv6.
13#
14# Routers rt-1,rt-2,rt-3 and rt-4 implement L2 VPN services
15# leveraging the SRv6 architecture. The key components for such VPNs are:
16#
17#   i) The SRv6 H.L2Encaps.Red behavior applies SRv6 Policies on traffic
18#      received by connected hosts, initiating the VPN tunnel. Such a behavior
19#      is an optimization of the SRv6 H.L2Encap aiming to reduce the
20#      length of the SID List carried in the pushed SRH. Specifically, the
21#      H.L2Encaps.Red removes the first SID contained in the SID List (i.e. SRv6
22#      Policy) by storing it into the IPv6 Destination Address. When a SRv6
23#      Policy is made of only one SID, the SRv6 H.L2Encaps.Red behavior omits
24#      the SRH at all and pushes that SID directly into the IPv6 DA;
25#
26#  ii) The SRv6 End behavior advances the active SID in the SID List
27#      carried by the SRH;
28#
29# iii) The SRv6 End.DX2 behavior is used for removing the SRv6 Policy
30#      and, thus, it terminates the VPN tunnel. The decapsulated L2 frame is
31#      sent over the interface connected with the destination host.
32#
33#               cafe::1                      cafe::2
34#              10.0.0.1                     10.0.0.2
35#             +--------+                   +--------+
36#             |        |                   |        |
37#             |  hs-1  |                   |  hs-2  |
38#             |        |                   |        |
39#             +---+----+                   +--- +---+
40#    cafe::/64    |                             |      cafe::/64
41#  10.0.0.0/24    |                             |    10.0.0.0/24
42#             +---+----+                   +----+---+
43#             |        |  fcf0:0:1:2::/64  |        |
44#             |  rt-1  +-------------------+  rt-2  |
45#             |        |                   |        |
46#             +---+----+                   +----+---+
47#                 |      .               .      |
48#                 |  fcf0:0:1:3::/64   .        |
49#                 |          .       .          |
50#                 |            .   .            |
51# fcf0:0:1:4::/64 |              .              | fcf0:0:2:3::/64
52#                 |            .   .            |
53#                 |          .       .          |
54#                 |  fcf0:0:2:4::/64   .        |
55#                 |      .               .      |
56#             +---+----+                   +----+---+
57#             |        |                   |        |
58#             |  rt-4  +-------------------+  rt-3  |
59#             |        |  fcf0:0:3:4::/64  |        |
60#             +---+----+                   +----+---+
61#
62#
63# Every fcf0:0:x:y::/64 network interconnects the SRv6 routers rt-x with rt-y
64# in the IPv6 operator network.
65#
66# Local SID table
67# ===============
68#
69# Each SRv6 router is configured with a Local SID table in which SIDs are
70# stored. Considering the given SRv6 router rt-x, at least two SIDs are
71# configured in the Local SID table:
72#
73#   Local SID table for SRv6 router rt-x
74#   +----------------------------------------------------------+
75#   |fcff:x::e is associated with the SRv6 End behavior        |
76#   |fcff:x::d2 is associated with the SRv6 End.DX2 behavior   |
77#   +----------------------------------------------------------+
78#
79# The fcff::/16 prefix is reserved by the operator for implementing SRv6 VPN
80# services. Reachability of SIDs is ensured by proper configuration of the IPv6
81# operator's network and SRv6 routers.
82#
83# SRv6 Policies
84# =============
85#
86# An SRv6 ingress router applies SRv6 policies to the traffic received from a
87# connected host. SRv6 policy enforcement consists of encapsulating the
88# received traffic into a new IPv6 packet with a given SID List contained in
89# the SRH.
90#
91# L2 VPN between hs-1 and hs-2
92# ----------------------------
93#
94# Hosts hs-1 and hs-2 are connected using a dedicated L2 VPN.
95# Specifically, packets generated from hs-1 and directed towards hs-2 are
96# handled by rt-1 which applies the following SRv6 Policies:
97#
98#   i.a) L2 traffic, SID List=fcff:2::d2
99#
100# Policy (i.a) steers tunneled L2 traffic through SRv6 router rt-2.
101# The H.L2Encaps.Red omits the presence of SRH at all, since the SID List
102# consists of only one SID (fcff:2::d2) that can be stored directly in the IPv6
103# DA.
104#
105# On the reverse path (i.e. from hs-2 to hs-1), rt-2 applies the following
106# policies:
107#
108#   i.b) L2 traffic, SID List=fcff:4::e,fcff:3::e,fcff:1::d2
109#
110# Policy (i.b) steers tunneled L2 traffic through the SRv6 routers
111# rt-4,rt-3,rt2. The H.L2Encaps.Red reduces the SID List in the SRH by removing
112# the first SID (fcff:4::e) and pushing it into the IPv6 DA.
113#
114# In summary:
115#  hs-1->hs-2 |IPv6 DA=fcff:2::d2|eth|...|                              (i.a)
116#  hs-2->hs-1 |IPv6 DA=fcff:4::e|SRH SIDs=fcff:3::e,fcff:1::d2|eth|...| (i.b)
117#
118
119# Kselftest framework requirement - SKIP code is 4.
120readonly ksft_skip=4
121
122readonly RDMSUFF="$(mktemp -u XXXXXXXX)"
123readonly DUMMY_DEVNAME="dum0"
124readonly RT2HS_DEVNAME="veth-hs"
125readonly HS_VETH_NAME="veth0"
126readonly LOCALSID_TABLE_ID=90
127readonly IPv6_RT_NETWORK=fcf0:0
128readonly IPv6_HS_NETWORK=cafe
129readonly IPv4_HS_NETWORK=10.0.0
130readonly VPN_LOCATOR_SERVICE=fcff
131readonly MAC_PREFIX=00:00:00:c0:01
132readonly END_FUNC=000e
133readonly DX2_FUNC=00d2
134
135PING_TIMEOUT_SEC=4
136PAUSE_ON_FAIL=${PAUSE_ON_FAIL:=no}
137
138# IDs of routers and hosts are initialized during the setup of the testing
139# network
140ROUTERS=''
141HOSTS=''
142
143SETUP_ERR=1
144
145ret=${ksft_skip}
146nsuccess=0
147nfail=0
148
149log_test()
150{
151	local rc="$1"
152	local expected="$2"
153	local msg="$3"
154
155	if [ "${rc}" -eq "${expected}" ]; then
156		nsuccess=$((nsuccess+1))
157		printf "\n    TEST: %-60s  [ OK ]\n" "${msg}"
158	else
159		ret=1
160		nfail=$((nfail+1))
161		printf "\n    TEST: %-60s  [FAIL]\n" "${msg}"
162		if [ "${PAUSE_ON_FAIL}" = "yes" ]; then
163			echo
164			echo "hit enter to continue, 'q' to quit"
165			read a
166			[ "$a" = "q" ] && exit 1
167		fi
168	fi
169}
170
171print_log_test_results()
172{
173	printf "\nTests passed: %3d\n" "${nsuccess}"
174	printf "Tests failed: %3d\n"   "${nfail}"
175
176	# when a test fails, the value of 'ret' is set to 1 (error code).
177	# Conversely, when all tests are passed successfully, the 'ret' value
178	# is set to 0 (success code).
179	if [ "${ret}" -ne 1 ]; then
180		ret=0
181	fi
182}
183
184log_section()
185{
186	echo
187	echo "################################################################################"
188	echo "TEST SECTION: $*"
189	echo "################################################################################"
190}
191
192test_command_or_ksft_skip()
193{
194	local cmd="$1"
195
196	if [ ! -x "$(command -v "${cmd}")" ]; then
197		echo "SKIP: Could not run test without \"${cmd}\" tool";
198		exit "${ksft_skip}"
199	fi
200}
201
202get_nodename()
203{
204	local name="$1"
205
206	echo "${name}-${RDMSUFF}"
207}
208
209get_rtname()
210{
211	local rtid="$1"
212
213	get_nodename "rt-${rtid}"
214}
215
216get_hsname()
217{
218	local hsid="$1"
219
220	get_nodename "hs-${hsid}"
221}
222
223__create_namespace()
224{
225	local name="$1"
226
227	ip netns add "${name}"
228}
229
230create_router()
231{
232	local rtid="$1"
233	local nsname
234
235	nsname="$(get_rtname "${rtid}")"
236
237	__create_namespace "${nsname}"
238}
239
240create_host()
241{
242	local hsid="$1"
243	local nsname
244
245	nsname="$(get_hsname "${hsid}")"
246
247	__create_namespace "${nsname}"
248}
249
250cleanup()
251{
252	local nsname
253	local i
254
255	# destroy routers
256	for i in ${ROUTERS}; do
257		nsname="$(get_rtname "${i}")"
258
259		ip netns del "${nsname}" &>/dev/null || true
260	done
261
262	# destroy hosts
263	for i in ${HOSTS}; do
264		nsname="$(get_hsname "${i}")"
265
266		ip netns del "${nsname}" &>/dev/null || true
267	done
268
269	# check whether the setup phase was completed successfully or not. In
270	# case of an error during the setup phase of the testing environment,
271	# the selftest is considered as "skipped".
272	if [ "${SETUP_ERR}" -ne 0 ]; then
273		echo "SKIP: Setting up the testing environment failed"
274		exit "${ksft_skip}"
275	fi
276
277	exit "${ret}"
278}
279
280add_link_rt_pairs()
281{
282	local rt="$1"
283	local rt_neighs="$2"
284	local neigh
285	local nsname
286	local neigh_nsname
287
288	nsname="$(get_rtname "${rt}")"
289
290	for neigh in ${rt_neighs}; do
291		neigh_nsname="$(get_rtname "${neigh}")"
292
293		ip link add "veth-rt-${rt}-${neigh}" netns "${nsname}" \
294			type veth peer name "veth-rt-${neigh}-${rt}" \
295			netns "${neigh_nsname}"
296	done
297}
298
299get_network_prefix()
300{
301	local rt="$1"
302	local neigh="$2"
303	local p="${rt}"
304	local q="${neigh}"
305
306	if [ "${p}" -gt "${q}" ]; then
307		p="${q}"; q="${rt}"
308	fi
309
310	echo "${IPv6_RT_NETWORK}:${p}:${q}"
311}
312
313# Setup the basic networking for the routers
314setup_rt_networking()
315{
316	local rt="$1"
317	local rt_neighs="$2"
318	local nsname
319	local net_prefix
320	local devname
321	local neigh
322
323	nsname="$(get_rtname "${rt}")"
324
325	for neigh in ${rt_neighs}; do
326		devname="veth-rt-${rt}-${neigh}"
327
328		net_prefix="$(get_network_prefix "${rt}" "${neigh}")"
329
330		ip -netns "${nsname}" addr \
331			add "${net_prefix}::${rt}/64" dev "${devname}" nodad
332
333		ip -netns "${nsname}" link set "${devname}" up
334	done
335
336	ip -netns "${nsname}" link add "${DUMMY_DEVNAME}" type dummy
337
338	ip -netns "${nsname}" link set "${DUMMY_DEVNAME}" up
339	ip -netns "${nsname}" link set lo up
340
341	ip netns exec "${nsname}" sysctl -wq net.ipv6.conf.all.accept_dad=0
342	ip netns exec "${nsname}" sysctl -wq net.ipv6.conf.default.accept_dad=0
343	ip netns exec "${nsname}" sysctl -wq net.ipv6.conf.all.forwarding=1
344
345	ip netns exec "${nsname}" sysctl -wq net.ipv4.conf.all.rp_filter=0
346	ip netns exec "${nsname}" sysctl -wq net.ipv4.conf.default.rp_filter=0
347	ip netns exec "${nsname}" sysctl -wq net.ipv4.ip_forward=1
348}
349
350# Setup local SIDs for an SRv6 router
351setup_rt_local_sids()
352{
353	local rt="$1"
354	local rt_neighs="$2"
355	local net_prefix
356	local devname
357	local nsname
358	local neigh
359
360	nsname="$(get_rtname "${rt}")"
361
362	for neigh in ${rt_neighs}; do
363		devname="veth-rt-${rt}-${neigh}"
364
365		net_prefix="$(get_network_prefix "${rt}" "${neigh}")"
366
367		# set underlay network routes for SIDs reachability
368		ip -netns "${nsname}" -6 route \
369			add "${VPN_LOCATOR_SERVICE}:${neigh}::/32" \
370			table "${LOCALSID_TABLE_ID}" \
371			via "${net_prefix}::${neigh}" dev "${devname}"
372	done
373
374	# Local End behavior (note that dev "${DUMMY_DEVNAME}" is a dummy
375	# interface)
376	ip -netns "${nsname}" -6 route \
377		add "${VPN_LOCATOR_SERVICE}:${rt}::${END_FUNC}" \
378		table "${LOCALSID_TABLE_ID}" \
379		encap seg6local action End dev "${DUMMY_DEVNAME}"
380
381	# all SIDs for VPNs start with a common locator. Routes and SRv6
382	# Endpoint behaviors instaces are grouped together in the 'localsid'
383	# table.
384	ip -netns "${nsname}" -6 rule add \
385		to "${VPN_LOCATOR_SERVICE}::/16" \
386		lookup "${LOCALSID_TABLE_ID}" prio 999
387}
388
389# build and install the SRv6 policy into the ingress SRv6 router.
390# args:
391#  $1 - destination host (i.e. cafe::x host)
392#  $2 - SRv6 router configured for enforcing the SRv6 Policy
393#  $3 - SRv6 routers configured for steering traffic (End behaviors)
394#  $4 - SRv6 router configured for removing the SRv6 Policy (router connected
395#       to the destination host)
396#  $5 - encap mode (full or red)
397#  $6 - traffic type (IPv6 or IPv4)
398__setup_rt_policy()
399{
400	local dst="$1"
401	local encap_rt="$2"
402	local end_rts="$3"
403	local dec_rt="$4"
404	local mode="$5"
405	local traffic="$6"
406	local nsname
407	local policy=''
408	local n
409
410	nsname="$(get_rtname "${encap_rt}")"
411
412	for n in ${end_rts}; do
413		policy="${policy}${VPN_LOCATOR_SERVICE}:${n}::${END_FUNC},"
414	done
415
416	policy="${policy}${VPN_LOCATOR_SERVICE}:${dec_rt}::${DX2_FUNC}"
417
418	# add SRv6 policy to incoming traffic sent by connected hosts
419	if [ "${traffic}" -eq 6 ]; then
420		ip -netns "${nsname}" -6 route \
421			add "${IPv6_HS_NETWORK}::${dst}" \
422			encap seg6 mode "${mode}" segs "${policy}" \
423			dev dum0
424	else
425		ip -netns "${nsname}" -4 route \
426			add "${IPv4_HS_NETWORK}.${dst}" \
427			encap seg6 mode "${mode}" segs "${policy}" \
428			dev dum0
429	fi
430}
431
432# see __setup_rt_policy
433setup_rt_policy_ipv6()
434{
435	__setup_rt_policy "$1" "$2" "$3" "$4" "$5" 6
436}
437
438#see __setup_rt_policy
439setup_rt_policy_ipv4()
440{
441	__setup_rt_policy "$1" "$2" "$3" "$4" "$5" 4
442}
443
444setup_decap()
445{
446	local rt="$1"
447	local nsname
448
449	nsname="$(get_rtname "${rt}")"
450
451	# Local End.DX2 behavior
452	ip -netns "${nsname}" -6 route \
453		add "${VPN_LOCATOR_SERVICE}:${rt}::${DX2_FUNC}" \
454		table "${LOCALSID_TABLE_ID}" \
455		encap seg6local action End.DX2 oif "${RT2HS_DEVNAME}" \
456		dev "${RT2HS_DEVNAME}"
457}
458
459setup_hs()
460{
461	local hs="$1"
462	local rt="$2"
463	local hsname
464	local rtname
465
466	hsname="$(get_hsname "${hs}")"
467	rtname="$(get_rtname "${rt}")"
468
469	ip netns exec "${hsname}" sysctl -wq net.ipv6.conf.all.accept_dad=0
470	ip netns exec "${hsname}" sysctl -wq net.ipv6.conf.default.accept_dad=0
471
472	ip -netns "${hsname}" link add "${HS_VETH_NAME}" type veth \
473		peer name "${RT2HS_DEVNAME}" netns "${rtname}"
474
475	ip -netns "${hsname}" addr add "${IPv6_HS_NETWORK}::${hs}/64" \
476		dev "${HS_VETH_NAME}" nodad
477	ip -netns "${hsname}" addr add "${IPv4_HS_NETWORK}.${hs}/24" \
478		dev "${HS_VETH_NAME}"
479
480	ip -netns "${hsname}" link set "${HS_VETH_NAME}" up
481	ip -netns "${hsname}" link set lo up
482
483	ip -netns "${rtname}" addr add "${IPv6_HS_NETWORK}::254/64" \
484		dev "${RT2HS_DEVNAME}" nodad
485	ip -netns "${rtname}" addr \
486		add "${IPv4_HS_NETWORK}.254/24" dev "${RT2HS_DEVNAME}"
487
488	ip -netns "${rtname}" link set "${RT2HS_DEVNAME}" up
489
490	# disable the rp_filter otherwise the kernel gets confused about how
491	# to route decap ipv4 packets.
492	ip netns exec "${rtname}" \
493		sysctl -wq net.ipv4.conf."${RT2HS_DEVNAME}".rp_filter=0
494}
495
496# set an auto-generated mac address
497# args:
498#  $1 - name of the node (e.g.: hs-1, rt-3, etc)
499#  $2 - id of the node (e.g.: 1 for hs-1, 3 for rt-3, etc)
500#  $3 - host part of the IPv6 network address
501#  $4 - name of the network interface to which the generated mac address must
502#       be set.
503set_mac_address()
504{
505	local nodename="$1"
506	local nodeid="$2"
507	local host="$3"
508	local ifname="$4"
509	local nsname
510
511	nsname=$(get_nodename "${nodename}")
512
513	ip -netns "${nsname}" link set dev "${ifname}" down
514
515	ip -netns "${nsname}" link set address "${MAC_PREFIX}:${nodeid}" \
516		dev "${ifname}"
517
518	# the IPv6 address must be set once again after the MAC address has
519	# been changed.
520	ip -netns "${nsname}" addr add "${IPv6_HS_NETWORK}::${host}/64" \
521		dev "${ifname}" nodad
522
523	ip -netns "${nsname}" link set dev "${ifname}" up
524}
525
526set_host_l2peer()
527{
528	local hssrc="$1"
529	local hsdst="$2"
530	local ipprefix="$3"
531	local proto="$4"
532	local hssrc_name
533	local ipaddr
534
535	hssrc_name="$(get_hsname "${hssrc}")"
536
537	if [ "${proto}" -eq 6 ]; then
538		ipaddr="${ipprefix}::${hsdst}"
539	else
540		ipaddr="${ipprefix}.${hsdst}"
541	fi
542
543	ip -netns "${hssrc_name}" route add "${ipaddr}" dev "${HS_VETH_NAME}"
544
545	ip -netns "${hssrc_name}" neigh \
546		add "${ipaddr}" lladdr "${MAC_PREFIX}:${hsdst}" \
547		dev "${HS_VETH_NAME}"
548}
549
550# setup an SRv6 L2 VPN between host hs-x and hs-y (currently, the SRv6
551# subsystem only supports L2 frames whose layer-3 is IPv4/IPv6).
552# args:
553#  $1 - source host
554#  $2 - SRv6 routers configured for steering tunneled traffic
555#  $3 - destination host
556setup_l2vpn()
557{
558	local hssrc="$1"
559	local end_rts="$2"
560	local hsdst="$3"
561	local rtsrc="${hssrc}"
562	local rtdst="${hsdst}"
563
564	# set fixed mac for source node and the neigh MAC address
565	set_mac_address "hs-${hssrc}" "${hssrc}" "${hssrc}" "${HS_VETH_NAME}"
566	set_host_l2peer "${hssrc}" "${hsdst}" "${IPv6_HS_NETWORK}" 6
567	set_host_l2peer "${hssrc}" "${hsdst}" "${IPv4_HS_NETWORK}" 4
568
569	# we have to set the mac address of the veth-host (on ingress router)
570	# to the mac address of the remote peer (L2 VPN destination host).
571	# Otherwise, traffic coming from the source host is dropped at the
572	# ingress router.
573	set_mac_address "rt-${rtsrc}" "${hsdst}" 254 "${RT2HS_DEVNAME}"
574
575	# set the SRv6 Policies at the ingress router
576	setup_rt_policy_ipv6 "${hsdst}" "${rtsrc}" "${end_rts}" "${rtdst}" \
577		l2encap.red 6
578	setup_rt_policy_ipv4 "${hsdst}" "${rtsrc}" "${end_rts}" "${rtdst}" \
579		l2encap.red 4
580
581	# set the decap behavior
582	setup_decap "${rtsrc}"
583}
584
585setup()
586{
587	local i
588
589	# create routers
590	ROUTERS="1 2 3 4"; readonly ROUTERS
591	for i in ${ROUTERS}; do
592		create_router "${i}"
593	done
594
595	# create hosts
596	HOSTS="1 2"; readonly HOSTS
597	for i in ${HOSTS}; do
598		create_host "${i}"
599	done
600
601	# set up the links for connecting routers
602	add_link_rt_pairs 1 "2 3 4"
603	add_link_rt_pairs 2 "3 4"
604	add_link_rt_pairs 3 "4"
605
606	# set up the basic connectivity of routers and routes required for
607	# reachability of SIDs.
608	setup_rt_networking 1 "2 3 4"
609	setup_rt_networking 2 "1 3 4"
610	setup_rt_networking 3 "1 2 4"
611	setup_rt_networking 4 "1 2 3"
612
613	# set up the hosts connected to routers
614	setup_hs 1 1
615	setup_hs 2 2
616
617	# set up default SRv6 Endpoints (i.e. SRv6 End and SRv6 End.DX2)
618	setup_rt_local_sids 1 "2 3 4"
619	setup_rt_local_sids 2 "1 3 4"
620	setup_rt_local_sids 3 "1 2 4"
621	setup_rt_local_sids 4 "1 2 3"
622
623	# create a L2 VPN between hs-1 and hs-2.
624	# NB: currently, H.L2Encap* enables tunneling of L2 frames whose
625	# layer-3 is IPv4/IPv6.
626	#
627	# the network path between hs-1 and hs-2 traverses several routers
628	# depending on the direction of traffic.
629	#
630	# Direction hs-1 -> hs-2 (H.L2Encaps.Red)
631	# - rt-2 (SRv6 End.DX2 behavior)
632	#
633	# Direction hs-2 -> hs-1 (H.L2Encaps.Red)
634	#  - rt-4,rt-3 (SRv6 End behaviors)
635	#  - rt-1 (SRv6 End.DX2 behavior)
636	setup_l2vpn 1 "" 2
637	setup_l2vpn 2 "4 3" 1
638
639	# testing environment was set up successfully
640	SETUP_ERR=0
641}
642
643check_rt_connectivity()
644{
645	local rtsrc="$1"
646	local rtdst="$2"
647	local prefix
648	local rtsrc_nsname
649
650	rtsrc_nsname="$(get_rtname "${rtsrc}")"
651
652	prefix="$(get_network_prefix "${rtsrc}" "${rtdst}")"
653
654	ip netns exec "${rtsrc_nsname}" ping -c 1 -W "${PING_TIMEOUT_SEC}" \
655		"${prefix}::${rtdst}" >/dev/null 2>&1
656}
657
658check_and_log_rt_connectivity()
659{
660	local rtsrc="$1"
661	local rtdst="$2"
662
663	check_rt_connectivity "${rtsrc}" "${rtdst}"
664	log_test $? 0 "Routers connectivity: rt-${rtsrc} -> rt-${rtdst}"
665}
666
667check_hs_ipv6_connectivity()
668{
669	local hssrc="$1"
670	local hsdst="$2"
671	local hssrc_nsname
672
673	hssrc_nsname="$(get_hsname "${hssrc}")"
674
675	ip netns exec "${hssrc_nsname}" ping -c 1 -W "${PING_TIMEOUT_SEC}" \
676		"${IPv6_HS_NETWORK}::${hsdst}" >/dev/null 2>&1
677}
678
679check_hs_ipv4_connectivity()
680{
681	local hssrc="$1"
682	local hsdst="$2"
683	local hssrc_nsname
684
685	hssrc_nsname="$(get_hsname "${hssrc}")"
686
687	ip netns exec "${hssrc_nsname}" ping -c 1 -W "${PING_TIMEOUT_SEC}" \
688		"${IPv4_HS_NETWORK}.${hsdst}" >/dev/null 2>&1
689}
690
691check_and_log_hs2gw_connectivity()
692{
693	local hssrc="$1"
694
695	check_hs_ipv6_connectivity "${hssrc}" 254
696	log_test $? 0 "IPv6 Hosts connectivity: hs-${hssrc} -> gw"
697
698	check_hs_ipv4_connectivity "${hssrc}" 254
699	log_test $? 0 "IPv4 Hosts connectivity: hs-${hssrc} -> gw"
700}
701
702check_and_log_hs_ipv6_connectivity()
703{
704	local hssrc="$1"
705	local hsdst="$2"
706
707	check_hs_ipv6_connectivity "${hssrc}" "${hsdst}"
708	log_test $? 0 "IPv6 Hosts connectivity: hs-${hssrc} -> hs-${hsdst}"
709}
710
711check_and_log_hs_ipv4_connectivity()
712{
713	local hssrc="$1"
714	local hsdst="$2"
715
716	check_hs_ipv4_connectivity "${hssrc}" "${hsdst}"
717	log_test $? 0 "IPv4 Hosts connectivity: hs-${hssrc} -> hs-${hsdst}"
718}
719
720check_and_log_hs_connectivity()
721{
722	local hssrc="$1"
723	local hsdst="$2"
724
725	check_and_log_hs_ipv4_connectivity "${hssrc}" "${hsdst}"
726	check_and_log_hs_ipv6_connectivity "${hssrc}" "${hsdst}"
727}
728
729router_tests()
730{
731	local i
732	local j
733
734	log_section "IPv6 routers connectivity test"
735
736	for i in ${ROUTERS}; do
737		for j in ${ROUTERS}; do
738			if [ "${i}" -eq "${j}" ]; then
739				continue
740			fi
741
742			check_and_log_rt_connectivity "${i}" "${j}"
743		done
744	done
745}
746
747host2gateway_tests()
748{
749	local hs
750
751	log_section "IPv4/IPv6 connectivity test among hosts and gateways"
752
753	for hs in ${HOSTS}; do
754		check_and_log_hs2gw_connectivity "${hs}"
755	done
756}
757
758host_vpn_tests()
759{
760	log_section "SRv6 L2 VPN connectivity test hosts (h1 <-> h2)"
761
762	check_and_log_hs_connectivity 1 2
763	check_and_log_hs_connectivity 2 1
764}
765
766test_dummy_dev_or_ksft_skip()
767{
768	local test_netns
769
770	test_netns="dummy-$(mktemp -u XXXXXXXX)"
771
772	if ! ip netns add "${test_netns}"; then
773		echo "SKIP: Cannot set up netns for testing dummy dev support"
774		exit "${ksft_skip}"
775	fi
776
777	modprobe dummy &>/dev/null || true
778	if ! ip -netns "${test_netns}" link \
779		add "${DUMMY_DEVNAME}" type dummy; then
780		echo "SKIP: dummy dev not supported"
781
782		ip netns del "${test_netns}"
783		exit "${ksft_skip}"
784	fi
785
786	ip netns del "${test_netns}"
787}
788
789test_iproute2_supp_or_ksft_skip()
790{
791	if ! ip route help 2>&1 | grep -qo "l2encap.red"; then
792		echo "SKIP: Missing SRv6 l2encap.red support in iproute2"
793		exit "${ksft_skip}"
794	fi
795}
796
797if [ "$(id -u)" -ne 0 ]; then
798	echo "SKIP: Need root privileges"
799	exit "${ksft_skip}"
800fi
801
802# required programs to carry out this selftest
803test_command_or_ksft_skip ip
804test_command_or_ksft_skip ping
805test_command_or_ksft_skip sysctl
806test_command_or_ksft_skip grep
807
808test_iproute2_supp_or_ksft_skip
809test_dummy_dev_or_ksft_skip
810
811set -e
812trap cleanup EXIT
813
814setup
815set +e
816
817router_tests
818host2gateway_tests
819host_vpn_tests
820
821print_log_test_results
822