2 # SPDX-License-Identifier: GPL-2.0
4 # author: Andrea Mayer <andrea.mayer@uniroma2.it>
6 # This script is designed for testing the SRv6 H.L2Encaps.Red behavior.
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.
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:
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;
26 # ii) The SRv6 End behavior advances the active SID in the SID List
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.
35 # +--------+ +--------+
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 |
46 # +---+----+ +----+---+
48 # | fcf0:0:1:3::/64 . |
51 # fcf0:0:1:4::/64 | . | fcf0:0:2:3::/64
54 # | fcf0:0:2:4::/64 . |
56 # +---+----+ +----+---+
58 # | rt-4 +-------------------+ rt-3 |
59 # | | fcf0:0:3:4::/64 | |
60 # +---+----+ +----+---+
63 # Every fcf0:0:x:y::/64 network interconnects the SRv6 routers rt-x with rt-y
64 # in the IPv6 operator network.
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:
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 # +----------------------------------------------------------+
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.
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
91 # L2 VPN between hs-1 and hs-2
92 # ----------------------------
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:
98 # i.a) L2 traffic, SID List=fcff:2::d2
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
105 # On the reverse path (i.e. from hs-2 to hs-1), rt-2 applies the following
108 # i.b) L2 traffic, SID List=fcff:4::e,fcff:3::e,fcff:1::d2
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.
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)
119 # Kselftest framework requirement - SKIP code is 4.
122 readonly RDMSUFF
="$(mktemp -u XXXXXXXX)"
123 readonly DUMMY_DEVNAME
="dum0"
124 readonly RT2HS_DEVNAME
="veth-hs"
125 readonly HS_VETH_NAME
="veth0"
126 readonly LOCALSID_TABLE_ID
=90
127 readonly IPv6_RT_NETWORK
=fcf0
:0
128 readonly IPv6_HS_NETWORK
=cafe
129 readonly IPv4_HS_NETWORK
=10.0.0
130 readonly VPN_LOCATOR_SERVICE
=fcff
131 readonly MAC_PREFIX
=00:00:00:c0
:01
132 readonly END_FUNC
=000e
133 readonly DX2_FUNC
=00d2
136 PAUSE_ON_FAIL
=${PAUSE_ON_FAIL:=no}
138 # IDs of routers and hosts are initialized during the setup of the testing
155 if [ "${rc}" -eq "${expected}" ]; then
156 nsuccess
=$
((nsuccess
+1))
157 printf "\n TEST: %-60s [ OK ]\n" "${msg}"
161 printf "\n TEST: %-60s [FAIL]\n" "${msg}"
162 if [ "${PAUSE_ON_FAIL}" = "yes" ]; then
164 echo "hit enter to continue, 'q' to quit"
166 [ "$a" = "q" ] && exit 1
171 print_log_test_results
()
173 printf "\nTests passed: %3d\n" "${nsuccess}"
174 printf "Tests failed: %3d\n" "${nfail}"
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
187 echo "################################################################################"
188 echo "TEST SECTION: $*"
189 echo "################################################################################"
192 test_command_or_ksft_skip
()
196 if [ ! -x "$(command -v "${cmd}")" ]; then
197 echo "SKIP: Could not run test without \"${cmd}\" tool";
206 echo "${name}-${RDMSUFF}"
213 get_nodename
"rt-${rtid}"
220 get_nodename
"hs-${hsid}"
227 ip netns add
"${name}"
235 nsname
="$(get_rtname "${rtid}")"
237 __create_namespace
"${nsname}"
245 nsname
="$(get_hsname "${hsid}")"
247 __create_namespace
"${nsname}"
256 for i
in ${ROUTERS}; do
257 nsname
="$(get_rtname "${i}")"
259 ip netns del
"${nsname}" &>/dev
/null || true
263 for i
in ${HOSTS}; do
264 nsname
="$(get_hsname "${i}")"
266 ip netns del
"${nsname}" &>/dev
/null || true
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"
288 nsname
="$(get_rtname "${rt}")"
290 for neigh
in ${rt_neighs}; do
291 neigh_nsname
="$(get_rtname "${neigh}")"
293 ip link add
"veth-rt-${rt}-${neigh}" netns "${nsname}" \
294 type veth peer name
"veth-rt-${neigh}-${rt}" \
295 netns
"${neigh_nsname}"
306 if [ "${p}" -gt "${q}" ]; then
310 echo "${IPv6_RT_NETWORK}:${p}:${q}"
313 # Setup the basic networking for the routers
314 setup_rt_networking
()
323 nsname
="$(get_rtname "${rt}")"
325 for neigh
in ${rt_neighs}; do
326 devname
="veth-rt-${rt}-${neigh}"
328 net_prefix
="$(get_network_prefix "${rt}" "${neigh}")"
330 ip
-netns "${nsname}" addr \
331 add
"${net_prefix}::${rt}/64" dev "${devname}" nodad
333 ip
-netns "${nsname}" link
set "${devname}" up
336 ip
-netns "${nsname}" link add
"${DUMMY_DEVNAME}" type dummy
338 ip
-netns "${nsname}" link
set "${DUMMY_DEVNAME}" up
339 ip
-netns "${nsname}" link
set lo up
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
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
350 # Setup local SIDs for an SRv6 router
351 setup_rt_local_sids
()
360 nsname
="$(get_rtname "${rt}")"
362 for neigh
in ${rt_neighs}; do
363 devname
="veth-rt-${rt}-${neigh}"
365 net_prefix
="$(get_network_prefix "${rt}" "${neigh}")"
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}"
374 # Local End behavior (note that dev "${DUMMY_DEVNAME}" is a dummy
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}"
381 # all SIDs for VPNs start with a common locator. Routes and SRv6
382 # Endpoint behaviors instaces are grouped together in the 'localsid'
384 ip
-netns "${nsname}" -6 rule add \
385 to
"${VPN_LOCATOR_SERVICE}::/16" \
386 lookup
"${LOCALSID_TABLE_ID}" prio
999
389 # build and install the SRv6 policy into the ingress SRv6 router.
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)
410 nsname
="$(get_rtname "${encap_rt}")"
412 for n
in ${end_rts}; do
413 policy
="${policy}${VPN_LOCATOR_SERVICE}:${n}::${END_FUNC},"
416 policy
="${policy}${VPN_LOCATOR_SERVICE}:${dec_rt}::${DX2_FUNC}"
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}" \
425 ip
-netns "${nsname}" -4 route \
426 add
"${IPv4_HS_NETWORK}.${dst}" \
427 encap seg6 mode
"${mode}" segs
"${policy}" \
432 # see __setup_rt_policy
433 setup_rt_policy_ipv6
()
435 __setup_rt_policy
"$1" "$2" "$3" "$4" "$5" 6
438 #see __setup_rt_policy
439 setup_rt_policy_ipv4
()
441 __setup_rt_policy
"$1" "$2" "$3" "$4" "$5" 4
449 nsname
="$(get_rtname "${rt}")"
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}"
466 hsname
="$(get_hsname "${hs}")"
467 rtname
="$(get_rtname "${rt}")"
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
472 ip
-netns "${hsname}" link add
"${HS_VETH_NAME}" type veth \
473 peer name
"${RT2HS_DEVNAME}" netns
"${rtname}"
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}"
480 ip
-netns "${hsname}" link
set "${HS_VETH_NAME}" up
481 ip
-netns "${hsname}" link
set lo up
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}"
488 ip
-netns "${rtname}" link
set "${RT2HS_DEVNAME}" up
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
496 # set an auto-generated mac address
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
511 nsname
=$
(get_nodename
"${nodename}")
513 ip
-netns "${nsname}" link
set dev
"${ifname}" down
515 ip
-netns "${nsname}" link set address "${MAC_PREFIX}:${nodeid}" \
518 # the IPv6 address must be set once again after the MAC address has
520 ip
-netns "${nsname}" addr add "${IPv6_HS_NETWORK}::${host}/64" \
521 dev
"${ifname}" nodad
523 ip
-netns "${nsname}" link
set dev
"${ifname}" up
535 hssrc_name
="$(get_hsname "${hssrc}")"
537 if [ "${proto}" -eq 6 ]; then
538 ipaddr
="${ipprefix}::${hsdst}"
540 ipaddr
="${ipprefix}.${hsdst}"
543 ip
-netns "${hssrc_name}" route add "${ipaddr}" dev "${HS_VETH_NAME}"
545 ip
-netns "${hssrc_name}" neigh \
546 add
"${ipaddr}" lladdr "${MAC_PREFIX}:${hsdst}" \
547 dev
"${HS_VETH_NAME}"
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).
554 # $2 - SRv6 routers configured for steering tunneled traffic
555 # $3 - destination host
561 local rtsrc
="${hssrc}"
562 local rtdst
="${hsdst}"
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
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
573 set_mac_address
"rt-${rtsrc}" "${hsdst}" 254 "${RT2HS_DEVNAME}"
575 # set the SRv6 Policies at the ingress router
576 setup_rt_policy_ipv6
"${hsdst}" "${rtsrc}" "${end_rts}" "${rtdst}" \
578 setup_rt_policy_ipv4
"${hsdst}" "${rtsrc}" "${end_rts}" "${rtdst}" \
581 # set the decap behavior
582 setup_decap
"${rtsrc}"
590 ROUTERS
="1 2 3 4"; readonly ROUTERS
591 for i
in ${ROUTERS}; do
596 HOSTS
="1 2"; readonly HOSTS
597 for i
in ${HOSTS}; do
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"
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"
613 # set up the hosts connected to routers
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"
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.
627 # the network path between hs-1 and hs-2 traverses several routers
628 # depending on the direction of traffic.
630 # Direction hs-1 -> hs-2 (H.L2Encaps.Red)
631 # - rt-2 (SRv6 End.DX2 behavior)
633 # Direction hs-2 -> hs-1 (H.L2Encaps.Red)
634 # - rt-4,rt-3 (SRv6 End behaviors)
635 # - rt-1 (SRv6 End.DX2 behavior)
637 setup_l2vpn
2 "4 3" 1
639 # testing environment was set up successfully
643 check_rt_connectivity
()
650 rtsrc_nsname
="$(get_rtname "${rtsrc}")"
652 prefix
="$(get_network_prefix "${rtsrc}" "${rtdst}")"
654 ip netns
exec "${rtsrc_nsname}" ping -c 1 -W "${PING_TIMEOUT_SEC}" \
655 "${prefix}::${rtdst}" >/dev
/null
2>&1
658 check_and_log_rt_connectivity
()
663 check_rt_connectivity
"${rtsrc}" "${rtdst}"
664 log_test $?
0 "Routers connectivity: rt-${rtsrc} -> rt-${rtdst}"
667 check_hs_ipv6_connectivity
()
673 hssrc_nsname
="$(get_hsname "${hssrc}")"
675 ip netns
exec "${hssrc_nsname}" ping -c 1 -W "${PING_TIMEOUT_SEC}" \
676 "${IPv6_HS_NETWORK}::${hsdst}" >/dev
/null
2>&1
679 check_hs_ipv4_connectivity
()
685 hssrc_nsname
="$(get_hsname "${hssrc}")"
687 ip netns
exec "${hssrc_nsname}" ping -c 1 -W "${PING_TIMEOUT_SEC}" \
688 "${IPv4_HS_NETWORK}.${hsdst}" >/dev
/null
2>&1
691 check_and_log_hs2gw_connectivity
()
695 check_hs_ipv6_connectivity
"${hssrc}" 254
696 log_test $?
0 "IPv6 Hosts connectivity: hs-${hssrc} -> gw"
698 check_hs_ipv4_connectivity
"${hssrc}" 254
699 log_test $?
0 "IPv4 Hosts connectivity: hs-${hssrc} -> gw"
702 check_and_log_hs_ipv6_connectivity
()
707 check_hs_ipv6_connectivity
"${hssrc}" "${hsdst}"
708 log_test $?
0 "IPv6 Hosts connectivity: hs-${hssrc} -> hs-${hsdst}"
711 check_and_log_hs_ipv4_connectivity
()
716 check_hs_ipv4_connectivity
"${hssrc}" "${hsdst}"
717 log_test $?
0 "IPv4 Hosts connectivity: hs-${hssrc} -> hs-${hsdst}"
720 check_and_log_hs_connectivity
()
725 check_and_log_hs_ipv4_connectivity
"${hssrc}" "${hsdst}"
726 check_and_log_hs_ipv6_connectivity
"${hssrc}" "${hsdst}"
734 log_section
"IPv6 routers connectivity test"
736 for i
in ${ROUTERS}; do
737 for j
in ${ROUTERS}; do
738 if [ "${i}" -eq "${j}" ]; then
742 check_and_log_rt_connectivity
"${i}" "${j}"
751 log_section
"IPv4/IPv6 connectivity test among hosts and gateways"
753 for hs
in ${HOSTS}; do
754 check_and_log_hs2gw_connectivity
"${hs}"
760 log_section
"SRv6 L2 VPN connectivity test hosts (h1 <-> h2)"
762 check_and_log_hs_connectivity
1 2
763 check_and_log_hs_connectivity
2 1
766 test_dummy_dev_or_ksft_skip
()
770 test_netns
="dummy-$(mktemp -u XXXXXXXX)"
772 if ! ip netns add
"${test_netns}"; then
773 echo "SKIP: Cannot set up netns for testing dummy dev support"
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"
782 ip netns del
"${test_netns}"
786 ip netns del
"${test_netns}"
789 test_iproute2_supp_or_ksft_skip
()
791 if ! ip route
help 2>&1 |
grep -qo "l2encap.red"; then
792 echo "SKIP: Missing SRv6 l2encap.red support in iproute2"
797 if [ "$(id -u)" -ne 0 ]; then
798 echo "SKIP: Need root privileges"
802 # required programs to carry out this selftest
803 test_command_or_ksft_skip ip
804 test_command_or_ksft_skip
ping
805 test_command_or_ksft_skip sysctl
806 test_command_or_ksft_skip
grep
808 test_iproute2_supp_or_ksft_skip
809 test_dummy_dev_or_ksft_skip
821 print_log_test_results