870 lines
24 KiB
Bash
870 lines
24 KiB
Bash
|
#!/bin/bash
|
||
|
# SPDX-License-Identifier: GPL-2.0
|
||
|
#
|
||
|
# author: Andrea Mayer <andrea.mayer@uniroma2.it>
|
||
|
# author: Paolo Lungaroni <paolo.lungaroni@uniroma2.it>
|
||
|
#
|
||
|
# This script is designed to test the support for "flavors" in the SRv6 End
|
||
|
# behavior.
|
||
|
#
|
||
|
# Flavors defined in RFC8986 [1] represent additional operations that can modify
|
||
|
# or extend the existing SRv6 End, End.X and End.T behaviors. For the sake of
|
||
|
# convenience, we report the list of flavors described in [1] hereafter:
|
||
|
# - Penultimate Segment Pop (PSP);
|
||
|
# - Ultimate Segment Pop (USP);
|
||
|
# - Ultimate Segment Decapsulation (USD).
|
||
|
#
|
||
|
# The End, End.X, and End.T behaviors can support these flavors either
|
||
|
# individually or in combinations.
|
||
|
# Currently in this selftest we consider only the PSP flavor for the SRv6 End
|
||
|
# behavior. However, it is possible to extend the script as soon as other
|
||
|
# flavors will be supported in the kernel.
|
||
|
#
|
||
|
# The purpose of the PSP flavor consists in instructing the penultimate node
|
||
|
# listed in the SRv6 policy to remove (i.e. pop) the outermost SRH from the IPv6
|
||
|
# header.
|
||
|
# A PSP enabled SRv6 End behavior instance processes the SRH by:
|
||
|
# - decrementing the Segment Left (SL) value from 1 to 0;
|
||
|
# - copying the last SID from the SID List into the IPv6 Destination Address
|
||
|
# (DA);
|
||
|
# - removing the SRH from the extension headers following the IPv6 header.
|
||
|
#
|
||
|
# Once the SRH is removed, the IPv6 packet is forwarded to the destination using
|
||
|
# the IPv6 DA updated during the PSP operation (i.e. the IPv6 DA corresponding
|
||
|
# to the last SID carried by the removed SRH).
|
||
|
#
|
||
|
# Although the PSP flavor can be set for any SRv6 End behavior instance on any
|
||
|
# SR node, it will be active only on such behaviors bound to a penultimate SID
|
||
|
# for a given SRv6 policy.
|
||
|
# SL=2 SL=1 SL=0
|
||
|
# | | |
|
||
|
# For example, given the SRv6 policy (SID List := <X, Y, Z>):
|
||
|
# - a PSP enabled SRv6 End behavior bound to SID Y will apply the PSP operation
|
||
|
# as Segment Left (SL) is 1, corresponding to the Penultimate Segment of the
|
||
|
# SID List;
|
||
|
# - a PSP enabled SRv6 End behavior bound to SID X will *NOT* apply the PSP
|
||
|
# operation as the Segment Left is 2. This behavior instance will apply the
|
||
|
# "standard" End packet processing, ignoring the configured PSP flavor at
|
||
|
# all.
|
||
|
#
|
||
|
# [1] RFC8986: https://datatracker.ietf.org/doc/html/rfc8986
|
||
|
#
|
||
|
# Network topology
|
||
|
# ================
|
||
|
#
|
||
|
# The network topology used in this selftest is depicted hereafter, composed by
|
||
|
# two hosts (hs-1, hs-2) and four routers (rt-1, rt-2, rt-3, rt-4).
|
||
|
# Hosts hs-1 and hs-2 are connected to routers rt-1 and rt-2, respectively,
|
||
|
# allowing them to communicate with each other.
|
||
|
# Traffic exchanged between hs-1 and hs-2 can follow different network paths.
|
||
|
# The network operator, through specific SRv6 Policies can steer traffic to one
|
||
|
# path rather than another. In this selftest this is implemented as follows:
|
||
|
#
|
||
|
# i) The SRv6 H.Insert behavior applies SRv6 Policies on traffic received by
|
||
|
# connected hosts. It pushes the Segment Routing Header (SRH) after the
|
||
|
# IPv6 header. The SRH contains the SID List (i.e. SRv6 Policy) needed for
|
||
|
# steering traffic across the segments/waypoints specified in that list;
|
||
|
#
|
||
|
# ii) The SRv6 End behavior advances the active SID in the SID List carried by
|
||
|
# the SRH;
|
||
|
#
|
||
|
# iii) The PSP enabled SRv6 End behavior is used to remove the SRH when such
|
||
|
# behavior is configured on a node bound to the Penultimate Segment carried
|
||
|
# by the SID List.
|
||
|
#
|
||
|
# cafe::1 cafe::2
|
||
|
# +--------+ +--------+
|
||
|
# | | | |
|
||
|
# | hs-1 | | hs-2 |
|
||
|
# | | | |
|
||
|
# +---+----+ +--- +---+
|
||
|
# cafe::/64 | | cafe::/64
|
||
|
# | |
|
||
|
# +---+----+ +----+---+
|
||
|
# | | fcf0:0:1:2::/64 | |
|
||
|
# | rt-1 +-------------------+ rt-2 |
|
||
|
# | | | |
|
||
|
# +---+----+ +----+---+
|
||
|
# | . . |
|
||
|
# | fcf0:0:1:3::/64 . |
|
||
|
# | . . |
|
||
|
# | . . |
|
||
|
# fcf0:0:1:4::/64 | . | fcf0:0:2:3::/64
|
||
|
# | . . |
|
||
|
# | . . |
|
||
|
# | fcf0:0:2:4::/64 . |
|
||
|
# | . . |
|
||
|
# +---+----+ +----+---+
|
||
|
# | | | |
|
||
|
# | rt-4 +-------------------+ rt-3 |
|
||
|
# | | fcf0:0:3:4::/64 | |
|
||
|
# +---+----+ +----+---+
|
||
|
#
|
||
|
# Every fcf0:0:x:y::/64 network interconnects the SRv6 routers rt-x with rt-y in
|
||
|
# the IPv6 operator network.
|
||
|
#
|
||
|
#
|
||
|
# Local SID table
|
||
|
# ===============
|
||
|
#
|
||
|
# Each SRv6 router is configured with a Local SID table in which SIDs are
|
||
|
# stored. Considering the given SRv6 router rt-x, at least two SIDs are
|
||
|
# configured in the Local SID table:
|
||
|
#
|
||
|
# Local SID table for SRv6 router rt-x
|
||
|
# +---------------------------------------------------------------------+
|
||
|
# |fcff:x::e is associated with the SRv6 End behavior |
|
||
|
# |fcff:x::ef1 is associated with the SRv6 End behavior with PSP flavor |
|
||
|
# +---------------------------------------------------------------------+
|
||
|
#
|
||
|
# The fcff::/16 prefix is reserved by the operator for the SIDs. Reachability of
|
||
|
# SIDs is ensured by proper configuration of the IPv6 operator's network and
|
||
|
# SRv6 routers.
|
||
|
#
|
||
|
#
|
||
|
# SRv6 Policies
|
||
|
# =============
|
||
|
#
|
||
|
# An SRv6 ingress router applies different SRv6 Policies to the traffic received
|
||
|
# from connected hosts on the basis of the destination addresses.
|
||
|
# In case of SRv6 H.Insert behavior, the SRv6 Policy enforcement consists of
|
||
|
# pushing the SRH (carrying a given SID List) after the existing IPv6 header.
|
||
|
# Note that in the inserting mode, there is no encapsulation at all.
|
||
|
#
|
||
|
# Before applying an SRv6 Policy using the SRv6 H.Insert behavior
|
||
|
# +------+---------+
|
||
|
# | IPv6 | Payload |
|
||
|
# +------+---------+
|
||
|
#
|
||
|
# After applying an SRv6 Policy using the SRv6 H.Insert behavior
|
||
|
# +------+-----+---------+
|
||
|
# | IPv6 | SRH | Payload |
|
||
|
# +------+-----+---------+
|
||
|
#
|
||
|
# Traffic from hs-1 to hs-2
|
||
|
# -------------------------
|
||
|
#
|
||
|
# Packets generated from hs-1 and directed towards hs-2 are
|
||
|
# handled by rt-1 which applies the following SRv6 Policy:
|
||
|
#
|
||
|
# i.a) IPv6 traffic, SID List=fcff:3::e,fcff:4::ef1,fcff:2::ef1,cafe::2
|
||
|
#
|
||
|
# Router rt-1 is configured to enforce the Policy (i.a) through the SRv6
|
||
|
# H.Insert behavior which pushes the SRH after the existing IPv6 header. This
|
||
|
# Policy steers the traffic from hs-1 across rt-3, rt-4, rt-2 and finally to the
|
||
|
# destination hs-2.
|
||
|
#
|
||
|
# As the packet reaches the router rt-3, the SRv6 End behavior bound to SID
|
||
|
# fcff:3::e is triggered. The behavior updates the Segment Left (from SL=3 to
|
||
|
# SL=2) in the SRH, the IPv6 DA with fcff:4::ef1 and forwards the packet to the
|
||
|
# next router on the path, i.e. rt-4.
|
||
|
#
|
||
|
# When router rt-4 receives the packet, the PSP enabled SRv6 End behavior bound
|
||
|
# to SID fcff:4::ef1 is executed. Since the SL=2, the PSP operation is *NOT*
|
||
|
# kicked in and the behavior applies the default End processing: the Segment
|
||
|
# Left is decreased (from SL=2 to SL=1), the IPv6 DA is updated with the SID
|
||
|
# fcff:2::ef1 and the packet is forwarded to router rt-2.
|
||
|
#
|
||
|
# The PSP enabled SRv6 End behavior on rt-2 is associated with SID fcff:2::ef1
|
||
|
# and is executed as the packet is received. Because SL=1, the behavior applies
|
||
|
# the PSP processing on the packet as follows: i) SL is decreased, i.e. from
|
||
|
# SL=1 to SL=0; ii) last SID (cafe::2) is copied into the IPv6 DA; iii) the
|
||
|
# outermost SRH is removed from the extension headers following the IPv6 header.
|
||
|
# Once the PSP processing is completed, the packet is forwarded to the host hs-2
|
||
|
# (destination).
|
||
|
#
|
||
|
# Traffic from hs-2 to hs-1
|
||
|
# -------------------------
|
||
|
#
|
||
|
# Packets generated from hs-2 and directed to hs-1 are handled by rt-2 which
|
||
|
# applies the following SRv6 Policy:
|
||
|
#
|
||
|
# i.b) IPv6 traffic, SID List=fcff:1::ef1,cafe::1
|
||
|
#
|
||
|
# Router rt-2 is configured to enforce the Policy (i.b) through the SRv6
|
||
|
# H.Insert behavior which pushes the SRH after the existing IPv6 header. This
|
||
|
# Policy steers the traffic from hs-2 across rt-1 and finally to the
|
||
|
# destination hs-1
|
||
|
#
|
||
|
#
|
||
|
# When the router rt-1 receives the packet, the PSP enabled SRv6 End behavior
|
||
|
# associated with the SID fcff:1::ef1 is triggered. Since the SL=1,
|
||
|
# the PSP operation takes place: i) the SL is decremented; ii) the IPv6 DA is
|
||
|
# set with the last SID; iii) the SRH is removed from the extension headers
|
||
|
# after the IPv6 header. At this point, the packet with IPv6 DA=cafe::1 is sent
|
||
|
# to the destination, i.e. hs-1.
|
||
|
|
||
|
# Kselftest framework requirement - SKIP code is 4.
|
||
|
readonly ksft_skip=4
|
||
|
|
||
|
readonly RDMSUFF="$(mktemp -u XXXXXXXX)"
|
||
|
readonly DUMMY_DEVNAME="dum0"
|
||
|
readonly RT2HS_DEVNAME="veth1"
|
||
|
readonly LOCALSID_TABLE_ID=90
|
||
|
readonly IPv6_RT_NETWORK=fcf0:0
|
||
|
readonly IPv6_HS_NETWORK=cafe
|
||
|
readonly IPv6_TESTS_ADDR=2001:db8::1
|
||
|
readonly LOCATOR_SERVICE=fcff
|
||
|
readonly END_FUNC=000e
|
||
|
readonly END_PSP_FUNC=0ef1
|
||
|
|
||
|
PING_TIMEOUT_SEC=4
|
||
|
PAUSE_ON_FAIL=${PAUSE_ON_FAIL:=no}
|
||
|
|
||
|
# IDs of routers and hosts are initialized during the setup of the testing
|
||
|
# network
|
||
|
ROUTERS=''
|
||
|
HOSTS=''
|
||
|
|
||
|
SETUP_ERR=1
|
||
|
|
||
|
ret=${ksft_skip}
|
||
|
nsuccess=0
|
||
|
nfail=0
|
||
|
|
||
|
log_test()
|
||
|
{
|
||
|
local rc="$1"
|
||
|
local expected="$2"
|
||
|
local msg="$3"
|
||
|
|
||
|
if [ "${rc}" -eq "${expected}" ]; then
|
||
|
nsuccess=$((nsuccess+1))
|
||
|
printf "\n TEST: %-60s [ OK ]\n" "${msg}"
|
||
|
else
|
||
|
ret=1
|
||
|
nfail=$((nfail+1))
|
||
|
printf "\n TEST: %-60s [FAIL]\n" "${msg}"
|
||
|
if [ "${PAUSE_ON_FAIL}" = "yes" ]; then
|
||
|
echo
|
||
|
echo "hit enter to continue, 'q' to quit"
|
||
|
read a
|
||
|
[ "$a" = "q" ] && exit 1
|
||
|
fi
|
||
|
fi
|
||
|
}
|
||
|
|
||
|
print_log_test_results()
|
||
|
{
|
||
|
printf "\nTests passed: %3d\n" "${nsuccess}"
|
||
|
printf "Tests failed: %3d\n" "${nfail}"
|
||
|
|
||
|
# when a test fails, the value of 'ret' is set to 1 (error code).
|
||
|
# Conversely, when all tests are passed successfully, the 'ret' value
|
||
|
# is set to 0 (success code).
|
||
|
if [ "${ret}" -ne 1 ]; then
|
||
|
ret=0
|
||
|
fi
|
||
|
}
|
||
|
|
||
|
log_section()
|
||
|
{
|
||
|
echo
|
||
|
echo "################################################################################"
|
||
|
echo "TEST SECTION: $*"
|
||
|
echo "################################################################################"
|
||
|
}
|
||
|
|
||
|
test_command_or_ksft_skip()
|
||
|
{
|
||
|
local cmd="$1"
|
||
|
|
||
|
if [ ! -x "$(command -v "${cmd}")" ]; then
|
||
|
echo "SKIP: Could not run test without \"${cmd}\" tool";
|
||
|
exit "${ksft_skip}"
|
||
|
fi
|
||
|
}
|
||
|
|
||
|
get_nodename()
|
||
|
{
|
||
|
local name="$1"
|
||
|
|
||
|
echo "${name}-${RDMSUFF}"
|
||
|
}
|
||
|
|
||
|
get_rtname()
|
||
|
{
|
||
|
local rtid="$1"
|
||
|
|
||
|
get_nodename "rt-${rtid}"
|
||
|
}
|
||
|
|
||
|
get_hsname()
|
||
|
{
|
||
|
local hsid="$1"
|
||
|
|
||
|
get_nodename "hs-${hsid}"
|
||
|
}
|
||
|
|
||
|
__create_namespace()
|
||
|
{
|
||
|
local name="$1"
|
||
|
|
||
|
ip netns add "${name}"
|
||
|
}
|
||
|
|
||
|
create_router()
|
||
|
{
|
||
|
local rtid="$1"
|
||
|
local nsname
|
||
|
|
||
|
nsname="$(get_rtname "${rtid}")"
|
||
|
|
||
|
__create_namespace "${nsname}"
|
||
|
}
|
||
|
|
||
|
create_host()
|
||
|
{
|
||
|
local hsid="$1"
|
||
|
local nsname
|
||
|
|
||
|
nsname="$(get_hsname "${hsid}")"
|
||
|
|
||
|
__create_namespace "${nsname}"
|
||
|
}
|
||
|
|
||
|
cleanup()
|
||
|
{
|
||
|
local nsname
|
||
|
local i
|
||
|
|
||
|
# destroy routers
|
||
|
for i in ${ROUTERS}; do
|
||
|
nsname="$(get_rtname "${i}")"
|
||
|
|
||
|
ip netns del "${nsname}" &>/dev/null || true
|
||
|
done
|
||
|
|
||
|
# destroy hosts
|
||
|
for i in ${HOSTS}; do
|
||
|
nsname="$(get_hsname "${i}")"
|
||
|
|
||
|
ip netns del "${nsname}" &>/dev/null || true
|
||
|
done
|
||
|
|
||
|
# check whether the setup phase was completed successfully or not. In
|
||
|
# case of an error during the setup phase of the testing environment,
|
||
|
# the selftest is considered as "skipped".
|
||
|
if [ "${SETUP_ERR}" -ne 0 ]; then
|
||
|
echo "SKIP: Setting up the testing environment failed"
|
||
|
exit "${ksft_skip}"
|
||
|
fi
|
||
|
|
||
|
exit "${ret}"
|
||
|
}
|
||
|
|
||
|
add_link_rt_pairs()
|
||
|
{
|
||
|
local rt="$1"
|
||
|
local rt_neighs="$2"
|
||
|
local neigh
|
||
|
local nsname
|
||
|
local neigh_nsname
|
||
|
|
||
|
nsname="$(get_rtname "${rt}")"
|
||
|
|
||
|
for neigh in ${rt_neighs}; do
|
||
|
neigh_nsname="$(get_rtname "${neigh}")"
|
||
|
|
||
|
ip link add "veth-rt-${rt}-${neigh}" netns "${nsname}" \
|
||
|
type veth peer name "veth-rt-${neigh}-${rt}" \
|
||
|
netns "${neigh_nsname}"
|
||
|
done
|
||
|
}
|
||
|
|
||
|
get_network_prefix()
|
||
|
{
|
||
|
local rt="$1"
|
||
|
local neigh="$2"
|
||
|
local p="${rt}"
|
||
|
local q="${neigh}"
|
||
|
|
||
|
if [ "${p}" -gt "${q}" ]; then
|
||
|
p="${q}"; q="${rt}"
|
||
|
fi
|
||
|
|
||
|
echo "${IPv6_RT_NETWORK}:${p}:${q}"
|
||
|
}
|
||
|
|
||
|
# Given the description of a router <id:op> as an input, the function returns
|
||
|
# the <id> token which represents the ID of the router.
|
||
|
# i.e. input: "12:psp"
|
||
|
# output: "12"
|
||
|
__get_srv6_rtcfg_id()
|
||
|
{
|
||
|
local element="$1"
|
||
|
|
||
|
echo "${element}" | cut -d':' -f1
|
||
|
}
|
||
|
|
||
|
# Given the description of a router <id:op> as an input, the function returns
|
||
|
# the <op> token which represents the operation (e.g. End behavior with or
|
||
|
# withouth flavors) configured for the node.
|
||
|
|
||
|
# Note that when the operation represents an End behavior with a list of
|
||
|
# flavors, the output is the ordered version of that list.
|
||
|
# i.e. input: "5:usp,psp,usd"
|
||
|
# output: "psp,usd,usp"
|
||
|
__get_srv6_rtcfg_op()
|
||
|
{
|
||
|
local element="$1"
|
||
|
|
||
|
# return the lexicographically ordered flavors
|
||
|
echo "${element}" | cut -d':' -f2 | sed 's/,/\n/g' | sort | \
|
||
|
xargs | sed 's/ /,/g'
|
||
|
}
|
||
|
|
||
|
# Setup the basic networking for the routers
|
||
|
setup_rt_networking()
|
||
|
{
|
||
|
local rt="$1"
|
||
|
local rt_neighs="$2"
|
||
|
local nsname
|
||
|
local net_prefix
|
||
|
local devname
|
||
|
local neigh
|
||
|
|
||
|
nsname="$(get_rtname "${rt}")"
|
||
|
|
||
|
for neigh in ${rt_neighs}; do
|
||
|
devname="veth-rt-${rt}-${neigh}"
|
||
|
|
||
|
net_prefix="$(get_network_prefix "${rt}" "${neigh}")"
|
||
|
|
||
|
ip -netns "${nsname}" addr \
|
||
|
add "${net_prefix}::${rt}/64" dev "${devname}" nodad
|
||
|
|
||
|
ip -netns "${nsname}" link set "${devname}" up
|
||
|
done
|
||
|
|
||
|
ip -netns "${nsname}" link set lo up
|
||
|
|
||
|
ip -netns "${nsname}" link add ${DUMMY_DEVNAME} type dummy
|
||
|
ip -netns "${nsname}" link set ${DUMMY_DEVNAME} up
|
||
|
|
||
|
ip netns exec "${nsname}" sysctl -wq net.ipv6.conf.all.accept_dad=0
|
||
|
ip netns exec "${nsname}" sysctl -wq net.ipv6.conf.default.accept_dad=0
|
||
|
ip netns exec "${nsname}" sysctl -wq net.ipv6.conf.all.forwarding=1
|
||
|
}
|
||
|
|
||
|
# Setup local SIDs for an SRv6 router
|
||
|
setup_rt_local_sids()
|
||
|
{
|
||
|
local rt="$1"
|
||
|
local rt_neighs="$2"
|
||
|
local net_prefix
|
||
|
local devname
|
||
|
local nsname
|
||
|
local neigh
|
||
|
|
||
|
nsname="$(get_rtname "${rt}")"
|
||
|
|
||
|
for neigh in ${rt_neighs}; do
|
||
|
devname="veth-rt-${rt}-${neigh}"
|
||
|
|
||
|
net_prefix="$(get_network_prefix "${rt}" "${neigh}")"
|
||
|
|
||
|
# set underlay network routes for SIDs reachability
|
||
|
ip -netns "${nsname}" -6 route \
|
||
|
add "${LOCATOR_SERVICE}:${neigh}::/32" \
|
||
|
table "${LOCALSID_TABLE_ID}" \
|
||
|
via "${net_prefix}::${neigh}" dev "${devname}"
|
||
|
done
|
||
|
|
||
|
# Local End behavior (note that "dev" is a dummy interface chosen for
|
||
|
# the sake of simplicity).
|
||
|
ip -netns "${nsname}" -6 route \
|
||
|
add "${LOCATOR_SERVICE}:${rt}::${END_FUNC}" \
|
||
|
table "${LOCALSID_TABLE_ID}" \
|
||
|
encap seg6local action End dev "${DUMMY_DEVNAME}"
|
||
|
|
||
|
|
||
|
# all SIDs start with a common locator. Routes and SRv6 Endpoint
|
||
|
# behavior instaces are grouped together in the 'localsid' table.
|
||
|
ip -netns "${nsname}" -6 rule \
|
||
|
add to "${LOCATOR_SERVICE}::/16" \
|
||
|
lookup "${LOCALSID_TABLE_ID}" prio 999
|
||
|
|
||
|
# set default routes to unreachable
|
||
|
ip -netns "${nsname}" -6 route \
|
||
|
add unreachable default metric 4278198272 \
|
||
|
dev "${DUMMY_DEVNAME}"
|
||
|
}
|
||
|
|
||
|
# This helper function builds and installs the SID List (i.e. SRv6 Policy)
|
||
|
# to be applied on incoming packets at the ingress node. Moreover, it
|
||
|
# configures the SRv6 nodes specified in the SID List to process the traffic
|
||
|
# according to the operations required by the Policy itself.
|
||
|
# args:
|
||
|
# $1 - destination host (i.e. cafe::x host)
|
||
|
# $2 - SRv6 router configured for enforcing the SRv6 Policy
|
||
|
# $3 - compact way to represent a list of SRv6 routers with their operations
|
||
|
# (i.e. behaviors) that each of them needs to perform. Every <nodeid:op>
|
||
|
# element constructs a SID that is associated with the behavior <op> on
|
||
|
# the <nodeid> node. The list of such elements forms an SRv6 Policy.
|
||
|
__setup_rt_policy()
|
||
|
{
|
||
|
local dst="$1"
|
||
|
local encap_rt="$2"
|
||
|
local policy_rts="$3"
|
||
|
local behavior_cfg
|
||
|
local in_nsname
|
||
|
local rt_nsname
|
||
|
local policy=''
|
||
|
local function
|
||
|
local fullsid
|
||
|
local op_type
|
||
|
local node
|
||
|
local n
|
||
|
|
||
|
in_nsname="$(get_rtname "${encap_rt}")"
|
||
|
|
||
|
for n in ${policy_rts}; do
|
||
|
node="$(__get_srv6_rtcfg_id "${n}")"
|
||
|
op_type="$(__get_srv6_rtcfg_op "${n}")"
|
||
|
rt_nsname="$(get_rtname "${node}")"
|
||
|
|
||
|
case "${op_type}" in
|
||
|
"noflv")
|
||
|
policy="${policy}${LOCATOR_SERVICE}:${node}::${END_FUNC},"
|
||
|
function="${END_FUNC}"
|
||
|
behavior_cfg="End"
|
||
|
;;
|
||
|
|
||
|
"psp")
|
||
|
policy="${policy}${LOCATOR_SERVICE}:${node}::${END_PSP_FUNC},"
|
||
|
function="${END_PSP_FUNC}"
|
||
|
behavior_cfg="End flavors psp"
|
||
|
;;
|
||
|
|
||
|
*)
|
||
|
break
|
||
|
;;
|
||
|
esac
|
||
|
|
||
|
fullsid="${LOCATOR_SERVICE}:${node}::${function}"
|
||
|
|
||
|
# add SRv6 Endpoint behavior to the selected router
|
||
|
if ! ip -netns "${rt_nsname}" -6 route get "${fullsid}" \
|
||
|
&>/dev/null; then
|
||
|
ip -netns "${rt_nsname}" -6 route \
|
||
|
add "${fullsid}" \
|
||
|
table "${LOCALSID_TABLE_ID}" \
|
||
|
encap seg6local action ${behavior_cfg} \
|
||
|
dev "${DUMMY_DEVNAME}"
|
||
|
fi
|
||
|
done
|
||
|
|
||
|
# we need to remove the trailing comma to avoid inserting an empty
|
||
|
# address (::0) in the SID List.
|
||
|
policy="${policy%,}"
|
||
|
|
||
|
# add SRv6 policy to incoming traffic sent by connected hosts
|
||
|
ip -netns "${in_nsname}" -6 route \
|
||
|
add "${IPv6_HS_NETWORK}::${dst}" \
|
||
|
encap seg6 mode inline segs "${policy}" \
|
||
|
dev "${DUMMY_DEVNAME}"
|
||
|
|
||
|
ip -netns "${in_nsname}" -6 neigh \
|
||
|
add proxy "${IPv6_HS_NETWORK}::${dst}" \
|
||
|
dev "${RT2HS_DEVNAME}"
|
||
|
}
|
||
|
|
||
|
# see __setup_rt_policy
|
||
|
setup_rt_policy_ipv6()
|
||
|
{
|
||
|
__setup_rt_policy "$1" "$2" "$3"
|
||
|
}
|
||
|
|
||
|
setup_hs()
|
||
|
{
|
||
|
local hs="$1"
|
||
|
local rt="$2"
|
||
|
local hsname
|
||
|
local rtname
|
||
|
|
||
|
hsname="$(get_hsname "${hs}")"
|
||
|
rtname="$(get_rtname "${rt}")"
|
||
|
|
||
|
ip netns exec "${hsname}" sysctl -wq net.ipv6.conf.all.accept_dad=0
|
||
|
ip netns exec "${hsname}" sysctl -wq net.ipv6.conf.default.accept_dad=0
|
||
|
|
||
|
ip -netns "${hsname}" link add veth0 type veth \
|
||
|
peer name "${RT2HS_DEVNAME}" netns "${rtname}"
|
||
|
|
||
|
ip -netns "${hsname}" addr \
|
||
|
add "${IPv6_HS_NETWORK}::${hs}/64" dev veth0 nodad
|
||
|
|
||
|
ip -netns "${hsname}" link set veth0 up
|
||
|
ip -netns "${hsname}" link set lo up
|
||
|
|
||
|
ip -netns "${rtname}" addr \
|
||
|
add "${IPv6_HS_NETWORK}::254/64" dev "${RT2HS_DEVNAME}" nodad
|
||
|
|
||
|
ip -netns "${rtname}" link set "${RT2HS_DEVNAME}" up
|
||
|
|
||
|
ip netns exec "${rtname}" \
|
||
|
sysctl -wq net.ipv6.conf."${RT2HS_DEVNAME}".proxy_ndp=1
|
||
|
}
|
||
|
|
||
|
setup()
|
||
|
{
|
||
|
local i
|
||
|
|
||
|
# create routers
|
||
|
ROUTERS="1 2 3 4"; readonly ROUTERS
|
||
|
for i in ${ROUTERS}; do
|
||
|
create_router "${i}"
|
||
|
done
|
||
|
|
||
|
# create hosts
|
||
|
HOSTS="1 2"; readonly HOSTS
|
||
|
for i in ${HOSTS}; do
|
||
|
create_host "${i}"
|
||
|
done
|
||
|
|
||
|
# set up the links for connecting routers
|
||
|
add_link_rt_pairs 1 "2 3 4"
|
||
|
add_link_rt_pairs 2 "3 4"
|
||
|
add_link_rt_pairs 3 "4"
|
||
|
|
||
|
# set up the basic connectivity of routers and routes required for
|
||
|
# reachability of SIDs.
|
||
|
setup_rt_networking 1 "2 3 4"
|
||
|
setup_rt_networking 2 "1 3 4"
|
||
|
setup_rt_networking 3 "1 2 4"
|
||
|
setup_rt_networking 4 "1 2 3"
|
||
|
|
||
|
# set up the hosts connected to routers
|
||
|
setup_hs 1 1
|
||
|
setup_hs 2 2
|
||
|
|
||
|
# set up default SRv6 Endpoints (i.e. SRv6 End behavior)
|
||
|
setup_rt_local_sids 1 "2 3 4"
|
||
|
setup_rt_local_sids 2 "1 3 4"
|
||
|
setup_rt_local_sids 3 "1 2 4"
|
||
|
setup_rt_local_sids 4 "1 2 3"
|
||
|
|
||
|
# set up SRv6 policies
|
||
|
# create a connection between hosts hs-1 and hs-2.
|
||
|
# The path between hs-1 and hs-2 traverses SRv6 aware routers.
|
||
|
# For each direction two path are chosen:
|
||
|
#
|
||
|
# Direction hs-1 -> hs-2 (PSP flavor)
|
||
|
# - rt-1 (SRv6 H.Insert policy)
|
||
|
# - rt-3 (SRv6 End behavior)
|
||
|
# - rt-4 (SRv6 End flavor PSP with SL>1, acting as End behavior)
|
||
|
# - rt-2 (SRv6 End flavor PSP with SL=1)
|
||
|
#
|
||
|
# Direction hs-2 -> hs-1 (PSP flavor)
|
||
|
# - rt-2 (SRv6 H.Insert policy)
|
||
|
# - rt-1 (SRv6 End flavor PSP with SL=1)
|
||
|
setup_rt_policy_ipv6 2 1 "3:noflv 4:psp 2:psp"
|
||
|
setup_rt_policy_ipv6 1 2 "1:psp"
|
||
|
|
||
|
# testing environment was set up successfully
|
||
|
SETUP_ERR=0
|
||
|
}
|
||
|
|
||
|
check_rt_connectivity()
|
||
|
{
|
||
|
local rtsrc="$1"
|
||
|
local rtdst="$2"
|
||
|
local prefix
|
||
|
local rtsrc_nsname
|
||
|
|
||
|
rtsrc_nsname="$(get_rtname "${rtsrc}")"
|
||
|
|
||
|
prefix="$(get_network_prefix "${rtsrc}" "${rtdst}")"
|
||
|
|
||
|
ip netns exec "${rtsrc_nsname}" ping -c 1 -W "${PING_TIMEOUT_SEC}" \
|
||
|
"${prefix}::${rtdst}" >/dev/null 2>&1
|
||
|
}
|
||
|
|
||
|
check_and_log_rt_connectivity()
|
||
|
{
|
||
|
local rtsrc="$1"
|
||
|
local rtdst="$2"
|
||
|
|
||
|
check_rt_connectivity "${rtsrc}" "${rtdst}"
|
||
|
log_test $? 0 "Routers connectivity: rt-${rtsrc} -> rt-${rtdst}"
|
||
|
}
|
||
|
|
||
|
check_hs_ipv6_connectivity()
|
||
|
{
|
||
|
local hssrc="$1"
|
||
|
local hsdst="$2"
|
||
|
local hssrc_nsname
|
||
|
|
||
|
hssrc_nsname="$(get_hsname "${hssrc}")"
|
||
|
|
||
|
ip netns exec "${hssrc_nsname}" ping -c 1 -W "${PING_TIMEOUT_SEC}" \
|
||
|
"${IPv6_HS_NETWORK}::${hsdst}" >/dev/null 2>&1
|
||
|
}
|
||
|
|
||
|
check_and_log_hs2gw_connectivity()
|
||
|
{
|
||
|
local hssrc="$1"
|
||
|
|
||
|
check_hs_ipv6_connectivity "${hssrc}" 254
|
||
|
log_test $? 0 "IPv6 Hosts connectivity: hs-${hssrc} -> gw"
|
||
|
}
|
||
|
|
||
|
check_and_log_hs_ipv6_connectivity()
|
||
|
{
|
||
|
local hssrc="$1"
|
||
|
local hsdst="$2"
|
||
|
|
||
|
check_hs_ipv6_connectivity "${hssrc}" "${hsdst}"
|
||
|
log_test $? 0 "IPv6 Hosts connectivity: hs-${hssrc} -> hs-${hsdst}"
|
||
|
}
|
||
|
|
||
|
check_and_log_hs_connectivity()
|
||
|
{
|
||
|
local hssrc="$1"
|
||
|
local hsdst="$2"
|
||
|
|
||
|
check_and_log_hs_ipv6_connectivity "${hssrc}" "${hsdst}"
|
||
|
}
|
||
|
|
||
|
router_tests()
|
||
|
{
|
||
|
local i
|
||
|
local j
|
||
|
|
||
|
log_section "IPv6 routers connectivity test"
|
||
|
|
||
|
for i in ${ROUTERS}; do
|
||
|
for j in ${ROUTERS}; do
|
||
|
if [ "${i}" -eq "${j}" ]; then
|
||
|
continue
|
||
|
fi
|
||
|
|
||
|
check_and_log_rt_connectivity "${i}" "${j}"
|
||
|
done
|
||
|
done
|
||
|
}
|
||
|
|
||
|
host2gateway_tests()
|
||
|
{
|
||
|
local hs
|
||
|
|
||
|
log_section "IPv6 connectivity test among hosts and gateways"
|
||
|
|
||
|
for hs in ${HOSTS}; do
|
||
|
check_and_log_hs2gw_connectivity "${hs}"
|
||
|
done
|
||
|
}
|
||
|
|
||
|
host_srv6_end_flv_psp_tests()
|
||
|
{
|
||
|
log_section "SRv6 connectivity test hosts (h1 <-> h2, PSP flavor)"
|
||
|
|
||
|
check_and_log_hs_connectivity 1 2
|
||
|
check_and_log_hs_connectivity 2 1
|
||
|
}
|
||
|
|
||
|
test_iproute2_supp_or_ksft_skip()
|
||
|
{
|
||
|
local flavor="$1"
|
||
|
|
||
|
if ! ip route help 2>&1 | grep -qo "${flavor}"; then
|
||
|
echo "SKIP: Missing SRv6 ${flavor} flavor support in iproute2"
|
||
|
exit "${ksft_skip}"
|
||
|
fi
|
||
|
}
|
||
|
|
||
|
test_kernel_supp_or_ksft_skip()
|
||
|
{
|
||
|
local flavor="$1"
|
||
|
local test_netns
|
||
|
|
||
|
test_netns="kflv-$(mktemp -u XXXXXXXX)"
|
||
|
|
||
|
if ! ip netns add "${test_netns}"; then
|
||
|
echo "SKIP: Cannot set up netns to test kernel support for flavors"
|
||
|
exit "${ksft_skip}"
|
||
|
fi
|
||
|
|
||
|
if ! ip -netns "${test_netns}" link \
|
||
|
add "${DUMMY_DEVNAME}" type dummy; then
|
||
|
echo "SKIP: Cannot set up dummy dev to test kernel support for flavors"
|
||
|
|
||
|
ip netns del "${test_netns}"
|
||
|
exit "${ksft_skip}"
|
||
|
fi
|
||
|
|
||
|
if ! ip -netns "${test_netns}" link \
|
||
|
set "${DUMMY_DEVNAME}" up; then
|
||
|
echo "SKIP: Cannot activate dummy dev to test kernel support for flavors"
|
||
|
|
||
|
ip netns del "${test_netns}"
|
||
|
exit "${ksft_skip}"
|
||
|
fi
|
||
|
|
||
|
if ! ip -netns "${test_netns}" -6 route \
|
||
|
add "${IPv6_TESTS_ADDR}" encap seg6local \
|
||
|
action End flavors "${flavor}" dev "${DUMMY_DEVNAME}"; then
|
||
|
echo "SKIP: ${flavor} flavor not supported in kernel"
|
||
|
|
||
|
ip netns del "${test_netns}"
|
||
|
exit "${ksft_skip}"
|
||
|
fi
|
||
|
|
||
|
ip netns del "${test_netns}"
|
||
|
}
|
||
|
|
||
|
test_dummy_dev_or_ksft_skip()
|
||
|
{
|
||
|
local test_netns
|
||
|
|
||
|
test_netns="dummy-$(mktemp -u XXXXXXXX)"
|
||
|
|
||
|
if ! ip netns add "${test_netns}"; then
|
||
|
echo "SKIP: Cannot set up netns for testing dummy dev support"
|
||
|
exit "${ksft_skip}"
|
||
|
fi
|
||
|
|
||
|
modprobe dummy &>/dev/null || true
|
||
|
if ! ip -netns "${test_netns}" link \
|
||
|
add "${DUMMY_DEVNAME}" type dummy; then
|
||
|
echo "SKIP: dummy dev not supported"
|
||
|
|
||
|
ip netns del "${test_netns}"
|
||
|
exit "${ksft_skip}"
|
||
|
fi
|
||
|
|
||
|
ip netns del "${test_netns}"
|
||
|
}
|
||
|
|
||
|
if [ "$(id -u)" -ne 0 ]; then
|
||
|
echo "SKIP: Need root privileges"
|
||
|
exit "${ksft_skip}"
|
||
|
fi
|
||
|
|
||
|
# required programs to carry out this selftest
|
||
|
test_command_or_ksft_skip ip
|
||
|
test_command_or_ksft_skip ping
|
||
|
test_command_or_ksft_skip sysctl
|
||
|
test_command_or_ksft_skip grep
|
||
|
test_command_or_ksft_skip cut
|
||
|
test_command_or_ksft_skip sed
|
||
|
test_command_or_ksft_skip sort
|
||
|
test_command_or_ksft_skip xargs
|
||
|
|
||
|
test_dummy_dev_or_ksft_skip
|
||
|
test_iproute2_supp_or_ksft_skip psp
|
||
|
test_kernel_supp_or_ksft_skip psp
|
||
|
|
||
|
set -e
|
||
|
trap cleanup EXIT
|
||
|
|
||
|
setup
|
||
|
set +e
|
||
|
|
||
|
router_tests
|
||
|
host2gateway_tests
|
||
|
host_srv6_end_flv_psp_tests
|
||
|
|
||
|
print_log_test_results
|