| #!/bin/bash |
| # SPDX-License-Identifier: GPL-2.0 |
| # |
| # author: Andrea Mayer <andrea.mayer@uniroma2.it> |
| |
| # This test evaluates the SRv6 encap "lookup" attribute. After encapsulation |
| # the router looks up the route for the first SID, that is the outer IPv6 |
| # destination of the encapsulated packet. The attribute selects the FIB table |
| # used for this post-encap SID route lookup. |
| # |
| # Two routers (rt-1, rt-2) provide L3 VPN services over an IPv6 underlay |
| # (fd00::/64). Each router uses a separate VRF per tenant, with default |
| # blackhole routes (IPv4 and IPv6) to prevent traffic from leaking out of |
| # the VRF. Tenant traffic is encapsulated, then decapsulated with an |
| # End.DT46. Each router proxies both NDP and ARP. |
| # |
| # The routes that match the first SIDs are installed in a dedicated underlay |
| # table (500) rather than the main table (254). The encap routes use |
| # "lookup 500" to select this table for the post-encap SID route lookup. |
| # |
| # Without the "lookup" attribute, the route for the first SID cannot be found: |
| # - on the input path (forwarded traffic), the lookup stays in the VRF |
| # and hits the blackhole; |
| # - on the output path (locally originated traffic), the lookup falls |
| # through to the main table, with no route to the first SID. |
| # |
| # |
| # Legend (specific per-tenant addresses are in the instantiation tables below): |
| # X = tenant id and VRF table id; two tenants: 100 and 200 |
| # ("tX" means tenant X, e.g. t100, t200) |
| # a, b = the two host ids of the tenant |
| # HA, HB = addresses of host a, host b |
| # RLO1, RLO2 = rlo-X router loopback address on rt-1, rt-2 (tenant gateway |
| # for the output path, dual-stack) |
| # vrf-X = per-tenant VRF on each router (table X) |
| # |
| # Constants (same for every tenant): |
| # underlay = table 500; post-encap SID route lookup (via "lookup 500") |
| # localsid = table 90; holds the decap SIDs (End.DT46) |
| # fd00::/64 = underlay link between rt-1 and rt-2 |
| # veth-tX = cafe::254/10.0.0.254 (tenant gateway on veth, both routers) |
| # |
| # |
| # +-------------------+ +-------------------+ |
| # | | | | |
| # | hs-tX-a netns | | hs-tX-b netns | |
| # | | | | |
| # | +-------------+ | | +-------------+ | |
| # | | veth0 | | | | veth0 | | |
| # | | HA | | | | HB | | |
| # | +-------------+ | | +-------------+ | |
| # | . | | . | |
| # +-------------------+ +-------------------+ |
| # . . |
| # . . |
| # +-----------------------------------+ +-----------------------------------+ |
| # | . | | . | |
| # | +---------------+ | | +---------------+ | |
| # | | veth-tX | +----------+ | | +----------+ | veth-tX | | |
| # | | ::254/.254 | | localsid | | | | localsid | | ::254/.254 | | |
| # | +-------+-------+ +----------+ | | +----------+ +-------+-------+ | |
| # | | +----------+ | | +----------+ | | |
| # | +----+----+ | underlay | | | | underlay | +----+----+ | |
| # | | vrf-X | +----------+ | | +----------+ | vrf-X | | |
| # | +----+----+ | | +----+----+ | |
| # | | | | | | |
| # | +-----+----+ +------------+ | | +------------+ +----+-----+ | |
| # | | rlo-X | | veth0 | | | | veth0 | | rlo-X | | |
| # | | RLO1 | | fd00::1/64 |..|...|..| fd00::2/64 | | RLO2 | | |
| # | +----------+ +------------+ | | +------------+ +----------+ | |
| # | rt-1 netns | | rt-2 netns | |
| # +-----------------------------------+ +-----------------------------------+ |
| # |
| # |
| # Per-tenant instantiation: |
| # +-----+------+-------------------+-------------------+ |
| # | X | a, b | HA | HB | |
| # +-----+------+-------------------+-------------------+ |
| # | 100 | 1, 2 | cafe::1, 10.0.0.1 | cafe::2, 10.0.0.2 | |
| # | 200 | 3, 4 | cafe::3, 10.0.0.3 | cafe::4, 10.0.0.4 | |
| # +-----+------+-------------------+-------------------+ |
| # |
| # Router loopback (rlo-X) addresses, per tenant: |
| # +-----+-----------------------+-----------------------+ |
| # | X | RLO1 (rt-1) | RLO2 (rt-2) | |
| # +-----+-----------------------+-----------------------+ |
| # | 100 | cafe::101, 10.0.0.101 | cafe::102, 10.0.0.102 | |
| # | 200 | cafe::201, 10.0.0.201 | cafe::202, 10.0.0.202 | |
| # +-----+-----------------------+-----------------------+ |
| # |
| # |
| # Network configuration |
| # ===================== |
| # |
| # rt-1: localsid table (table 90) |
| # +--------+--------------------+----------------------------------+ |
| # | tenant | SID | Action | |
| # +--------+--------------------+----------------------------------+ |
| # | 100 | fc00:2:1:100::0d46 | apply SRv6 End.DT46 vrftable 100 | |
| # | 200 | fc00:2:1:200::0d46 | apply SRv6 End.DT46 vrftable 200 | |
| # +--------+--------------------+----------------------------------+ |
| # |
| # rt-1: underlay table (table 500) - post-encap SID route lookup |
| # +--------+--------------------+-------------------------------+ |
| # | tenant | SID | Action | |
| # +--------+--------------------+-------------------------------+ |
| # | 100 | fc00:1:2:100::0d46 | forward via fd00::2 dev veth0 | |
| # | 200 | fc00:1:2:200::0d46 | forward via fd00::2 dev veth0 | |
| # +--------+--------------------+-------------------------------+ |
| # |
| # rt-1: VRF tables (per tenant: vrf-X = table X) |
| # +--------+------------+------------------------------------------+ |
| # | tenant | dst | encap action | |
| # +--------+------------+------------------------------------------+ |
| # | 100 | cafe::2 | encap segs fc00:1:2:100::0d46 lookup 500 | |
| # | | 10.0.0.2 | | |
| # | | cafe::102 | | |
| # | | 10.0.0.102 | | |
| # | 200 | cafe::4 | encap segs fc00:1:2:200::0d46 lookup 500 | |
| # | | 10.0.0.4 | | |
| # | | cafe::202 | | |
| # | | 10.0.0.202 | | |
| # +--------+------------+------------------------------------------+ |
| # |
| # |
| # rt-2: localsid table (table 90) |
| # +--------+--------------------+----------------------------------+ |
| # | tenant | SID | Action | |
| # +--------+--------------------+----------------------------------+ |
| # | 100 | fc00:1:2:100::0d46 | apply SRv6 End.DT46 vrftable 100 | |
| # | 200 | fc00:1:2:200::0d46 | apply SRv6 End.DT46 vrftable 200 | |
| # +--------+--------------------+----------------------------------+ |
| # |
| # rt-2: underlay table (table 500) - post-encap SID route lookup |
| # +--------+--------------------+-------------------------------+ |
| # | tenant | SID | Action | |
| # +--------+--------------------+-------------------------------+ |
| # | 100 | fc00:2:1:100::0d46 | forward via fd00::1 dev veth0 | |
| # | 200 | fc00:2:1:200::0d46 | forward via fd00::1 dev veth0 | |
| # +--------+--------------------+-------------------------------+ |
| # |
| # rt-2: VRF tables (per tenant: vrf-X = table X) |
| # +--------+------------+------------------------------------------+ |
| # | tenant | dst | encap action | |
| # +--------+------------+------------------------------------------+ |
| # | 100 | cafe::1 | encap segs fc00:2:1:100::0d46 lookup 500 | |
| # | | 10.0.0.1 | | |
| # | | cafe::101 | | |
| # | | 10.0.0.101 | | |
| # | 200 | cafe::3 | encap segs fc00:2:1:200::0d46 lookup 500 | |
| # | | 10.0.0.3 | | |
| # | | cafe::201 | | |
| # | | 10.0.0.201 | | |
| # +--------+------------+------------------------------------------+ |
| # Within a tenant, a single SID reaches the adjacent router (its loopback) |
| # and the remote host connected to it, in both IPv4 and IPv6. |
| # |
| # For both rt-1 and rt-2, each VRF also has the connected host prefix (cafe::/64 |
| # or 10.0.0.0/24) and a default blackhole (IPv4 and IPv6). |
| # |
| # |
| # Locally originated traffic (output path) |
| # ======================================== |
| # |
| # The configuration above covers forwarded traffic, where packets arrive from |
| # a host and are encapsulated by the router. To also test router-originated |
| # traffic, each router pings the other router's loopback address through |
| # the VPN. |
| # |
| # Example (tenant 100), rt-1 pings cafe::102 (rt-2's loopback): |
| # 1. rt-1 looks up cafe::102 in vrf-100 and encapsulates it (SID |
| # fc00:1:2:100::0d46), then "lookup 500" finds the route for the SID in the |
| # underlay table (next hop fd00::2) and forwards it; |
| # 2. rt-2 decapsulates it (localsid, End.DT46) and delivers it locally |
| # (cafe::102 is on the rlo-100 interface); |
| # 3. rt-2 replies with destination cafe::101 (rt-1's loopback). rt-2 looks up |
| # cafe::101 in vrf-100 and encapsulates it back to rt-1 (again via "lookup |
| # 500"). rt-1 decapsulates it and delivers it. |
| |
| # shellcheck source=lib.sh |
| source lib.sh |
| |
| readonly LOCALSID_TABLE_ID=90 |
| readonly UNDERLAY_TABLE_ID=500 |
| readonly IPv6_RT_NETWORK=fd00 |
| readonly IPv6_HS_NETWORK=cafe |
| readonly IPv4_HS_NETWORK=10.0.0 |
| readonly VPN_LOCATOR_SERVICE=fc00 |
| readonly DT46_FUNC=0d46 |
| readonly DUMMY_DEVNAME=dum0 |
| readonly IPv6_TESTS_ADDR=2001:db8::1 |
| readonly TESTS_TABLE_ID=54321 |
| PING_TIMEOUT_SEC=4 |
| |
| SETUP_ERR=1 |
| |
| ret=${ksft_skip} |
| nsuccess=0 |
| nfail=0 |
| |
| PAUSE_ON_FAIL=${PAUSE_ON_FAIL:=no} |
| |
| 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 -r 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 "################################################################################" |
| } |
| |
| get_rtname() |
| { |
| local rtid="$1" |
| |
| echo "rt_${rtid}" |
| } |
| |
| get_rt_nsname() |
| { |
| local rtid="$1" |
| local varname |
| |
| varname="$(get_rtname "${rtid}")" |
| echo "${!varname}" |
| } |
| |
| get_hsname() |
| { |
| local tid="$1" |
| local hsid="$2" |
| |
| echo "hs_t${tid}_${hsid}" |
| } |
| |
| get_hs_nsname() |
| { |
| local tid="$1" |
| local hsid="$2" |
| local varname |
| |
| varname="$(get_hsname "${tid}" "${hsid}")" |
| echo "${!varname}" |
| } |
| |
| cleanup() |
| { |
| ip link del veth-rt-1 2>/dev/null || true |
| ip link del veth-rt-2 2>/dev/null || true |
| |
| cleanup_all_ns |
| |
| # 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}" |
| } |
| |
| # Host id of the router loopback (rlo) for a (router, tenant) pair. |
| # E.g. rt-1/tenant 100 -> 101, rt-2/tenant 200 -> 202. |
| get_rlo_hostid() |
| { |
| local rtid="$1" |
| local tid="$2" |
| |
| echo "$((tid + rtid))" |
| } |
| |
| build_vpn_sid() |
| { |
| local rtsrc="$1" |
| local rtdst="$2" |
| local tid="$3" |
| |
| echo "${VPN_LOCATOR_SERVICE}:${rtsrc}:${rtdst}:${tid}::${DT46_FUNC}" |
| } |
| |
| # Install a dual-stack (IPv6 and IPv4) encap route in a VRF on the given |
| # router. |
| # args: |
| # $1 - router id |
| # $2 - host part of the IPv6 destination |
| # $3 - host part of the IPv4 destination |
| # $4 - SRv6 SID used as the encap destination |
| # $5 - tenant id |
| # $6 - if "true", add the "lookup" attribute to the encap route |
| __set_encap_route() |
| { |
| local rt="$1" |
| local dst6="$2" |
| local dst4="$3" |
| local sid="$4" |
| local tid="$5" |
| local use_lookup="$6" |
| local lookup='' |
| local rtname |
| |
| rtname="$(get_rt_nsname "${rt}")" |
| |
| if [ "${use_lookup}" = "true" ]; then |
| lookup="lookup ${UNDERLAY_TABLE_ID}" |
| fi |
| |
| # shellcheck disable=SC2086 |
| ip -netns "${rtname}" -6 route replace \ |
| "${IPv6_HS_NETWORK}::${dst6}/128" vrf "vrf-${tid}" \ |
| encap seg6 mode encap segs "${sid}" ${lookup} dev veth0 |
| |
| # shellcheck disable=SC2086 |
| ip -netns "${rtname}" -4 route replace \ |
| "${IPv4_HS_NETWORK}.${dst4}/32" vrf "vrf-${tid}" \ |
| encap seg6 mode encap segs "${sid}" ${lookup} dev veth0 |
| } |
| |
| # Install the dual-stack encap route for a tenant host on rt, with the |
| # "lookup" attribute so the first SID is looked up in the underlay table. |
| # args: |
| # $1 - router id where the encap route is installed |
| # $2 - host destination id (host part of cafe::<id>/128 and 10.0.0.<id>/32) |
| # $3 - SRv6 SID used as the encap destination |
| # $4 - tenant id |
| set_host_encap_route() |
| { |
| local rt="$1" |
| local hsdst="$2" |
| local sid="$3" |
| local tid="$4" |
| |
| __set_encap_route "${rt}" "${hsdst}" "${hsdst}" "${sid}" "${tid}" true |
| } |
| |
| set_host_encap_route_nolookup() |
| { |
| local rt="$1" |
| local hsdst="$2" |
| local sid="$3" |
| local tid="$4" |
| |
| __set_encap_route "${rt}" "${hsdst}" "${hsdst}" "${sid}" "${tid}" false |
| } |
| |
| # Install the dual-stack encap route on rtsrc toward rtdst's rlo loopback |
| # (RLO1 or RLO2, see header), with the "lookup" attribute so the first |
| # SID is looked up in the underlay table. |
| # args: |
| # $1 - router id where the encap route is installed |
| # $2 - router id whose loopback address is the route destination |
| # $3 - SRv6 SID used as the encap destination |
| # $4 - tenant id |
| set_gw_encap_route() |
| { |
| local rtsrc="$1" |
| local rtdst="$2" |
| local sid="$3" |
| local tid="$4" |
| local dst |
| |
| dst="$(get_rlo_hostid "${rtdst}" "${tid}")" |
| |
| __set_encap_route "${rtsrc}" "${dst}" "${dst}" "${sid}" "${tid}" true |
| } |
| |
| set_gw_encap_route_nolookup() |
| { |
| local rtsrc="$1" |
| local rtdst="$2" |
| local sid="$3" |
| local tid="$4" |
| local dst |
| |
| dst="$(get_rlo_hostid "${rtdst}" "${tid}")" |
| |
| __set_encap_route "${rtsrc}" "${dst}" "${dst}" "${sid}" "${tid}" false |
| } |
| |
| # Setup the basic networking for a router |
| setup_rt_networking() |
| { |
| local id="$1" |
| local nsname |
| |
| nsname="$(get_rt_nsname "${id}")" |
| |
| ip link set "veth-rt-${id}" netns "${nsname}" |
| ip -netns "${nsname}" link set "veth-rt-${id}" name veth0 |
| |
| 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 "${nsname}" addr add "${IPv6_RT_NETWORK}::${id}/64" dev veth0 nodad |
| ip -netns "${nsname}" link set veth0 up |
| |
| ip netns exec "${nsname}" sysctl -wq net.ipv4.ip_forward=1 |
| ip netns exec "${nsname}" sysctl -wq net.ipv6.conf.all.forwarding=1 |
| } |
| |
| # Setup a host namespace and attach it to its gateway |
| setup_hs() |
| { |
| local hid="$1" |
| local rid="$2" |
| local tid="$3" |
| local rtveth="veth-t${tid}" |
| local hsname |
| local rtname |
| |
| hsname="$(get_hs_nsname "${tid}" "${hid}")" |
| rtname="$(get_rt_nsname "${rid}")" |
| |
| 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 "${rtveth}" |
| ip -netns "${hsname}" link set "${rtveth}" netns "${rtname}" |
| |
| ip -netns "${hsname}" addr add \ |
| "${IPv6_HS_NETWORK}::${hid}/64" dev veth0 nodad |
| ip -netns "${hsname}" addr add \ |
| "${IPv4_HS_NETWORK}.${hid}/24" dev veth0 |
| |
| ip -netns "${hsname}" link set veth0 up |
| } |
| |
| # Setup the per-tenant VRF on a router (gateway, loopback, blackhole) |
| setup_rt() |
| { |
| local rid="$1" |
| local tid="$2" |
| local rtveth="veth-t${tid}" |
| local rlo_dev="rlo-${tid}" |
| local rtname |
| local gw_addr_v6 |
| local gw_addr_v4 |
| |
| rtname="$(get_rt_nsname "${rid}")" |
| |
| gw_addr_v6="${IPv6_HS_NETWORK}::$(get_rlo_hostid "${rid}" "${tid}")" |
| gw_addr_v4="${IPv4_HS_NETWORK}.$(get_rlo_hostid "${rid}" "${tid}")" |
| |
| ip -netns "${rtname}" link add "vrf-${tid}" type vrf table "${tid}" |
| ip -netns "${rtname}" link set "vrf-${tid}" up |
| |
| ip -netns "${rtname}" link set "${rtveth}" master "vrf-${tid}" |
| |
| ip -netns "${rtname}" addr add \ |
| "${IPv6_HS_NETWORK}::254/64" dev "${rtveth}" nodad |
| ip -netns "${rtname}" addr add \ |
| "${IPv4_HS_NETWORK}.254/24" dev "${rtveth}" |
| |
| ip -netns "${rtname}" link set "${rtveth}" up |
| |
| ip netns exec "${rtname}" \ |
| sysctl -wq "net.ipv6.conf.${rtveth}.proxy_ndp=1" |
| ip netns exec "${rtname}" \ |
| sysctl -wq "net.ipv4.conf.${rtveth}.proxy_arp=1" |
| |
| ip netns exec "${rtname}" sh -c "echo 1 > /proc/sys/net/vrf/strict_mode" |
| |
| # router loopback interface for locally originated traffic |
| ip -netns "${rtname}" link add "${rlo_dev}" type dummy |
| ip -netns "${rtname}" link set "${rlo_dev}" master "vrf-${tid}" |
| |
| ip -netns "${rtname}" addr add "${gw_addr_v6}/128" \ |
| dev "${rlo_dev}" nodad |
| ip -netns "${rtname}" addr add "${gw_addr_v4}/32" \ |
| dev "${rlo_dev}" |
| |
| ip -netns "${rtname}" link set "${rlo_dev}" up |
| |
| # default blackhole routes in the VRF: any traffic that does not match |
| # a specific route is dropped. Without the "lookup" attribute on the |
| # encap route, the route for the first SID cannot be found from within |
| # the VRF. |
| ip -netns "${rtname}" -6 route add blackhole default metric 4278198272 \ |
| vrf "vrf-${tid}" |
| ip -netns "${rtname}" -4 route add blackhole default metric 4278198272 \ |
| vrf "vrf-${tid}" |
| } |
| |
| # Configure a one-way VPN path towards hsdst (on rtdst) for tenant tid. |
| # The encap side is set up on rtsrc and the decap side on rtdst. |
| # args: |
| # $1 - router id where the encap side is set up |
| # $2 - host id of the destination host |
| # $3 - router id of the destination router (connected to the destination host) |
| # $4 - tenant id |
| setup_vpn_config() |
| { |
| local rtsrc="$1" |
| local hsdst="$2" |
| local rtdst="$3" |
| local tid="$4" |
| local rtveth="veth-t${tid}" |
| local rtsrc_name |
| local rtdst_name |
| local vpn_sid |
| |
| rtsrc_name="$(get_rt_nsname "${rtsrc}")" |
| rtdst_name="$(get_rt_nsname "${rtdst}")" |
| vpn_sid="$(build_vpn_sid "${rtsrc}" "${rtdst}" "${tid}")" |
| |
| ip -netns "${rtsrc_name}" -6 neigh add proxy \ |
| "${IPv6_HS_NETWORK}::${hsdst}" dev "${rtveth}" |
| set_host_encap_route "${rtsrc}" "${hsdst}" "${vpn_sid}" "${tid}" |
| |
| ip -netns "${rtsrc_name}" -6 route add "${vpn_sid}/128" \ |
| table "${UNDERLAY_TABLE_ID}" \ |
| via "fd00::${rtdst}" dev veth0 |
| |
| # set the decap route for decapsulating packets arriving from rtsrc |
| # and destined to hsdst |
| ip -netns "${rtdst_name}" -6 route add "${vpn_sid}/128" \ |
| table "${LOCALSID_TABLE_ID}" \ |
| encap seg6local action End.DT46 \ |
| vrftable "${tid}" dev "vrf-${tid}" |
| |
| # all SIDs for VPNs start with a common locator which is fc00::/16. |
| # Routes for handling the SRv6 End.DT* behavior instances are grouped |
| # together in the 'localsid' table. |
| # |
| # NOTE: added only once |
| if ! ip -netns "${rtdst_name}" -6 rule show | \ |
| grep -q "to ${VPN_LOCATOR_SERVICE}::/16 lookup ${LOCALSID_TABLE_ID}"; then |
| ip -netns "${rtdst_name}" -6 rule add \ |
| to "${VPN_LOCATOR_SERVICE}::/16" \ |
| lookup "${LOCALSID_TABLE_ID}" prio 999 |
| fi |
| } |
| |
| # Configure rtsrc to reach rtdst's loopback address through the VPN. |
| # args: |
| # $1 - router id where the encap route is installed |
| # $2 - router id whose loopback is the destination |
| # $3 - tenant id |
| setup_vpn_gw_encap() |
| { |
| local rtsrc="$1" |
| local rtdst="$2" |
| local tid="$3" |
| local sid |
| |
| sid="$(build_vpn_sid "${rtsrc}" "${rtdst}" "${tid}")" |
| |
| set_gw_encap_route "${rtsrc}" "${rtdst}" "${sid}" "${tid}" |
| } |
| |
| setup() |
| { |
| ip link add veth-rt-1 type veth peer name veth-rt-2 |
| setup_ns rt_1 rt_2 |
| setup_rt_networking 1 |
| setup_rt_networking 2 |
| |
| # setup two hosts for the tenant 100. |
| # - host hs-t100-1 is directly connected to the router rt-1; |
| # - host hs-t100-2 is directly connected to the router rt-2. |
| setup_ns hs_t100_1 hs_t100_2 |
| setup_hs 1 1 100 |
| setup_hs 2 2 100 |
| |
| # setup two hosts for the tenant 200. |
| # - host hs-t200-3 is directly connected to the router rt-1; |
| # - host hs-t200-4 is directly connected to the router rt-2. |
| setup_ns hs_t200_3 hs_t200_4 |
| setup_hs 3 1 200 |
| setup_hs 4 2 200 |
| |
| # configure each router for each tenant: VRF, blackhole routes, |
| # router loopback interface |
| setup_rt 1 100 |
| setup_rt 2 100 |
| setup_rt 1 200 |
| setup_rt 2 200 |
| |
| # setup the L3 VPN which connects the host hs-t100-1 and host hs-t100-2 |
| # within the same tenant 100. |
| setup_vpn_config 1 2 2 100 |
| setup_vpn_config 2 1 1 100 |
| |
| # setup the L3 VPN which connects the host hs-t200-3 and host hs-t200-4 |
| # within the same tenant 200. |
| setup_vpn_config 1 4 2 200 |
| setup_vpn_config 2 3 1 200 |
| |
| # allow each router to reach the other's loopback through the VPN |
| setup_vpn_gw_encap 2 1 100 |
| setup_vpn_gw_encap 1 2 100 |
| setup_vpn_gw_encap 2 1 200 |
| setup_vpn_gw_encap 1 2 200 |
| |
| # testing environment was set up successfully |
| SETUP_ERR=0 |
| } |
| |
| check_rt_connectivity() |
| { |
| local rtsrc="$1" |
| local rtdst="$2" |
| local nsname |
| |
| nsname="$(get_rt_nsname "${rtsrc}")" |
| |
| ip netns exec "${nsname}" ping -c 1 -W 1 "${IPv6_RT_NETWORK}::${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 tid="$3" |
| local nsname |
| |
| nsname="$(get_hs_nsname "${tid}" "${hssrc}")" |
| |
| ip netns exec "${nsname}" ping -c 1 -W "${PING_TIMEOUT_SEC}" \ |
| "${IPv6_HS_NETWORK}::${hsdst}" >/dev/null 2>&1 |
| } |
| |
| check_hs_ipv4_connectivity() |
| { |
| local hssrc="$1" |
| local hsdst="$2" |
| local tid="$3" |
| local nsname |
| |
| nsname="$(get_hs_nsname "${tid}" "${hssrc}")" |
| |
| ip netns exec "${nsname}" ping -c 1 -W "${PING_TIMEOUT_SEC}" \ |
| "${IPv4_HS_NETWORK}.${hsdst}" >/dev/null 2>&1 |
| } |
| |
| check_and_log_hs_connectivity() |
| { |
| local hssrc="$1" |
| local hsdst="$2" |
| local tid="$3" |
| |
| check_hs_ipv6_connectivity "${hssrc}" "${hsdst}" "${tid}" |
| log_test $? 0 "IPv6 connectivity: hs-t${tid}-${hssrc} -> hs-t${tid}-${hsdst} (tenant ${tid})" |
| |
| check_hs_ipv4_connectivity "${hssrc}" "${hsdst}" "${tid}" |
| log_test $? 0 "IPv4 connectivity: hs-t${tid}-${hssrc} -> hs-t${tid}-${hsdst} (tenant ${tid})" |
| } |
| |
| check_and_log_hs_isolation() |
| { |
| local hssrc="$1" |
| local tidsrc="$2" |
| local hsdst="$3" |
| local tiddst="$4" |
| |
| check_hs_ipv6_connectivity "${hssrc}" "${hsdst}" "${tidsrc}" |
| log_test $? 1 "IPv6 isolation: hs-t${tidsrc}-${hssrc} -X-> hs-t${tiddst}-${hsdst}" |
| |
| check_hs_ipv4_connectivity "${hssrc}" "${hsdst}" "${tidsrc}" |
| log_test $? 1 "IPv4 isolation: hs-t${tidsrc}-${hssrc} -X-> hs-t${tiddst}-${hsdst}" |
| } |
| |
| check_and_log_hs2gw_connectivity() |
| { |
| local hssrc="$1" |
| local tid="$2" |
| |
| check_hs_ipv6_connectivity "${hssrc}" 254 "${tid}" |
| log_test $? 0 "IPv6 connectivity: hs-t${tid}-${hssrc} -> gw (tenant ${tid})" |
| |
| check_hs_ipv4_connectivity "${hssrc}" 254 "${tid}" |
| log_test $? 0 "IPv4 connectivity: hs-t${tid}-${hssrc} -> gw (tenant ${tid})" |
| } |
| |
| router_tests() |
| { |
| log_section "IPv6 routers connectivity test" |
| |
| check_and_log_rt_connectivity 1 2 |
| check_and_log_rt_connectivity 2 1 |
| } |
| |
| host2gateway_tests() |
| { |
| log_section "Connectivity test among hosts and gateway" |
| |
| check_and_log_hs2gw_connectivity 1 100 |
| check_and_log_hs2gw_connectivity 2 100 |
| |
| check_and_log_hs2gw_connectivity 3 200 |
| check_and_log_hs2gw_connectivity 4 200 |
| } |
| |
| host_vpn_tests() |
| { |
| log_section "SRv6 VPN connectivity test among hosts in the same tenant" |
| |
| check_and_log_hs_connectivity 1 2 100 |
| check_and_log_hs_connectivity 2 1 100 |
| |
| check_and_log_hs_connectivity 3 4 200 |
| check_and_log_hs_connectivity 4 3 200 |
| } |
| |
| host_vpn_isolation_tests() |
| { |
| local l1="1 2" |
| local l2="3 4" |
| local t1=100 |
| local t2=200 |
| local i |
| local j |
| local tmp |
| |
| log_section "SRv6 VPN isolation test among hosts in different tenants" |
| |
| for _ in 0 1; do |
| for i in ${l1}; do |
| for j in ${l2}; do |
| check_and_log_hs_isolation "${i}" "${t1}" "${j}" "${t2}" |
| done |
| done |
| |
| # let us test the reverse path |
| tmp="${l1}"; l1="${l2}"; l2="${tmp}" |
| tmp=${t1}; t1=${t2}; t2=${tmp} |
| done |
| } |
| |
| __test_nolookup() |
| { |
| local hssrc="$1" |
| local hsdst="$2" |
| local rtsrc="$3" |
| local rtdst="$4" |
| local tid="$5" |
| local vpn_sid |
| |
| vpn_sid="$(build_vpn_sid "${rtsrc}" "${rtdst}" "${tid}")" |
| |
| # replace encap route(s) without "lookup" attribute |
| set_host_encap_route_nolookup "${rtsrc}" "${hsdst}" "${vpn_sid}" "${tid}" |
| |
| check_hs_ipv6_connectivity "${hssrc}" "${hsdst}" "${tid}" |
| log_test $? 1 "IPv6 w/o lookup: hs-t${tid}-${hssrc} -X-> hs-t${tid}-${hsdst} (tenant ${tid})" |
| |
| check_hs_ipv4_connectivity "${hssrc}" "${hsdst}" "${tid}" |
| log_test $? 1 "IPv4 w/o lookup: hs-t${tid}-${hssrc} -X-> hs-t${tid}-${hsdst} (tenant ${tid})" |
| |
| # restore encap route(s) with "lookup" for subsequent tests |
| set_host_encap_route "${rtsrc}" "${hsdst}" "${vpn_sid}" "${tid}" |
| } |
| |
| host_vpn_nolookup_tests() |
| { |
| log_section "SRv6 VPN connectivity test among hosts w/o lookup" |
| |
| __test_nolookup 1 2 1 2 100 |
| __test_nolookup 2 1 2 1 100 |
| |
| __test_nolookup 3 4 1 2 200 |
| __test_nolookup 4 3 2 1 200 |
| } |
| |
| check_gw_ipv6_connectivity() |
| { |
| local rtsrc="$1" |
| local rtdst="$2" |
| local tidsrc="$3" |
| local tiddst="$4" |
| local rtname |
| local src_v6 |
| local dst_v6 |
| |
| rtname="$(get_rt_nsname "${rtsrc}")" |
| src_v6="${IPv6_HS_NETWORK}::$(get_rlo_hostid "${rtsrc}" "${tidsrc}")" |
| dst_v6="${IPv6_HS_NETWORK}::$(get_rlo_hostid "${rtdst}" "${tiddst}")" |
| |
| ip netns exec "${rtname}" ip vrf exec "vrf-${tidsrc}" \ |
| ping -c 1 -W "${PING_TIMEOUT_SEC}" \ |
| -I "${src_v6}" "${dst_v6}" >/dev/null 2>&1 |
| } |
| |
| check_gw_ipv4_connectivity() |
| { |
| local rtsrc="$1" |
| local rtdst="$2" |
| local tidsrc="$3" |
| local tiddst="$4" |
| local rtname |
| local src_v4 |
| local dst_v4 |
| |
| rtname="$(get_rt_nsname "${rtsrc}")" |
| src_v4="${IPv4_HS_NETWORK}.$(get_rlo_hostid "${rtsrc}" "${tidsrc}")" |
| dst_v4="${IPv4_HS_NETWORK}.$(get_rlo_hostid "${rtdst}" "${tiddst}")" |
| |
| ip netns exec "${rtname}" ip vrf exec "vrf-${tidsrc}" \ |
| ping -c 1 -W "${PING_TIMEOUT_SEC}" \ |
| -I "${src_v4}" "${dst_v4}" >/dev/null 2>&1 |
| } |
| |
| check_and_log_gw_connectivity() |
| { |
| local rtsrc="$1" |
| local rtdst="$2" |
| local tid="$3" |
| |
| check_gw_ipv6_connectivity "${rtsrc}" "${rtdst}" "${tid}" "${tid}" |
| log_test $? 0 "IPv6 connectivity: rt-${rtsrc} -> rt-${rtdst} (tenant ${tid})" |
| |
| check_gw_ipv4_connectivity "${rtsrc}" "${rtdst}" "${tid}" "${tid}" |
| log_test $? 0 "IPv4 connectivity: rt-${rtsrc} -> rt-${rtdst} (tenant ${tid})" |
| } |
| |
| check_and_log_gw_isolation() |
| { |
| local rtsrc="$1" |
| local rtdst="$2" |
| local tidsrc="$3" |
| local tiddst="$4" |
| |
| check_gw_ipv6_connectivity "${rtsrc}" "${rtdst}" "${tidsrc}" "${tiddst}" |
| log_test $? 1 "IPv6 isolation: rt-${rtsrc} -X-> rt-${rtdst} (tenants ${tidsrc}/${tiddst})" |
| |
| check_gw_ipv4_connectivity "${rtsrc}" "${rtdst}" "${tidsrc}" "${tiddst}" |
| log_test $? 1 "IPv4 isolation: rt-${rtsrc} -X-> rt-${rtdst} (tenants ${tidsrc}/${tiddst})" |
| } |
| |
| gw_vpn_isolation_tests() |
| { |
| log_section "SRv6 VPN isolation test among routers in different tenants" |
| |
| check_and_log_gw_isolation 1 2 100 200 |
| check_and_log_gw_isolation 2 1 100 200 |
| |
| check_and_log_gw_isolation 1 2 200 100 |
| check_and_log_gw_isolation 2 1 200 100 |
| } |
| |
| gw_vpn_tests() |
| { |
| log_section "SRv6 VPN connectivity test among routers in the same tenant" |
| |
| check_and_log_gw_connectivity 1 2 100 |
| check_and_log_gw_connectivity 2 1 100 |
| |
| check_and_log_gw_connectivity 1 2 200 |
| check_and_log_gw_connectivity 2 1 200 |
| } |
| |
| __test_gw_nolookup() |
| { |
| local rtsrc="$1" |
| local rtdst="$2" |
| local tid="$3" |
| local sid |
| |
| sid="$(build_vpn_sid "${rtsrc}" "${rtdst}" "${tid}")" |
| |
| # replace gw encap route without "lookup" attribute |
| set_gw_encap_route_nolookup "${rtsrc}" "${rtdst}" "${sid}" "${tid}" |
| |
| check_gw_ipv6_connectivity "${rtsrc}" "${rtdst}" "${tid}" "${tid}" |
| log_test $? 1 "IPv6 w/o lookup: rt-${rtsrc} -X-> rt-${rtdst} (tenant ${tid})" |
| |
| check_gw_ipv4_connectivity "${rtsrc}" "${rtdst}" "${tid}" "${tid}" |
| log_test $? 1 "IPv4 w/o lookup: rt-${rtsrc} -X-> rt-${rtdst} (tenant ${tid})" |
| |
| # restore gw encap route with "lookup" for subsequent tests |
| set_gw_encap_route "${rtsrc}" "${rtdst}" "${sid}" "${tid}" |
| } |
| |
| gw_vpn_nolookup_tests() |
| { |
| log_section "SRv6 VPN connectivity test among routers w/o lookup" |
| |
| __test_gw_nolookup 1 2 100 |
| __test_gw_nolookup 2 1 100 |
| |
| __test_gw_nolookup 1 2 200 |
| __test_gw_nolookup 2 1 200 |
| } |
| |
| 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 |
| } |
| |
| test_vrf_or_ksft_skip() |
| { |
| modprobe vrf &>/dev/null || true |
| if [ ! -e /proc/sys/net/vrf/strict_mode ]; then |
| echo "SKIP: vrf sysctl does not exist" |
| exit "${ksft_skip}" |
| fi |
| } |
| |
| test_dummy_dev_or_ksft_skip() |
| { |
| local test_netns |
| |
| setup_ns test_netns |
| |
| modprobe dummy &>/dev/null || true |
| if ! ip -netns "${test_netns}" link add "${DUMMY_DEVNAME}" \ |
| type dummy; then |
| cleanup_ns "${test_netns}" |
| echo "SKIP: dummy dev not supported" |
| exit "${ksft_skip}" |
| fi |
| |
| cleanup_ns "${test_netns}" |
| } |
| |
| test_encap_lookup_supp_or_ksft_skip() |
| { |
| local nsname |
| |
| setup_ns nsname |
| |
| ip -netns "${nsname}" link add "${DUMMY_DEVNAME}" type dummy |
| ip -netns "${nsname}" link set "${DUMMY_DEVNAME}" up |
| |
| if ! ip -netns "${nsname}" -6 route add "${IPv6_TESTS_ADDR}/128" \ |
| encap seg6 mode encap segs fc00::1 \ |
| lookup "${TESTS_TABLE_ID}" \ |
| dev "${DUMMY_DEVNAME}" 2>/dev/null; then |
| cleanup_ns "${nsname}" |
| echo "SKIP: seg6 encap lookup attribute not supported" |
| exit "${ksft_skip}" |
| fi |
| |
| # An old kernel with a recent iproute2 accepts the route but |
| # silently ignores the lookup attribute. Dump the route and check |
| # the attribute is really there, otherwise the test falsely passes. |
| if ! ip -netns "${nsname}" -6 route show "${IPv6_TESTS_ADDR}/128" | \ |
| grep -q "lookup ${TESTS_TABLE_ID}"; then |
| cleanup_ns "${nsname}" |
| echo "SKIP: seg6 encap lookup attribute not supported" |
| exit "${ksft_skip}" |
| fi |
| |
| cleanup_ns "${nsname}" |
| } |
| |
| 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_dummy_dev_or_ksft_skip |
| test_vrf_or_ksft_skip |
| test_encap_lookup_supp_or_ksft_skip |
| |
| set -e |
| trap cleanup EXIT |
| |
| setup |
| set +e |
| |
| router_tests |
| host2gateway_tests |
| host_vpn_tests |
| host_vpn_isolation_tests |
| host_vpn_nolookup_tests |
| gw_vpn_tests |
| gw_vpn_isolation_tests |
| gw_vpn_nolookup_tests |
| |
| print_log_test_results |