#!/bin/bash # SPDX-License-Identifier: GPL-2.0 # # Various combinations of VRF with xfrms and qdisc. source lib.sh PAUSE_ON_FAIL=no VERBOSE=0 ret=0 HOST1_4=192.168.1.1 HOST2_4=192.168.1.2 HOST1_6=2001:db8:1::1 HOST2_6=2001:db8:1::2 XFRM1_4=10.0.1.1 XFRM2_4=10.0.1.2 XFRM1_6=fc00:1000::1 XFRM2_6=fc00:1000::2 IF_ID=123 VRF=red TABLE=300 AUTH_1=0xd94fcfea65fddf21dc6e0d24a0253508 AUTH_2=0xdc6e0d24a0253508d94fcfea65fddf21 ENC_1=0xfc46c20f8048be9725930ff3fb07ac2a91f0347dffeacf62 ENC_2=0x3fb07ac2a91f0347dffeacf62fc46c20f8048be9725930ff SPI_1=0x02122b77 SPI_2=0x2b770212 which ping6 > /dev/null 2>&1 && ping6=$(which ping6) || ping6=$(which ping) ################################################################################ # log_test() { local rc=$1 local expected=$2 local msg="$3" if [ ${rc} -eq ${expected} ]; then printf "TEST: %-60s [ OK ]\n" "${msg}" nsuccess=$((nsuccess+1)) else ret=1 nfail=$((nfail+1)) printf "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 } run_cmd_host1() { local cmd="$*" local out local rc if [ "$VERBOSE" = "1" ]; then printf " COMMAND: $cmd\n" fi out=$(eval ip netns exec $host1 $cmd 2>&1) rc=$? if [ "$VERBOSE" = "1" ]; then if [ -n "$out" ]; then echo echo " $out" fi echo fi return $rc } ################################################################################ # create namespaces for hosts and sws create_vrf() { local ns=$1 local vrf=$2 local table=$3 if [ -n "${ns}" ]; then ns="-netns ${ns}" fi ip ${ns} link add ${vrf} type vrf table ${table} ip ${ns} link set ${vrf} up ip ${ns} route add vrf ${vrf} unreachable default metric 8192 ip ${ns} -6 route add vrf ${vrf} unreachable default metric 8192 ip ${ns} addr add 127.0.0.1/8 dev ${vrf} ip ${ns} -6 addr add ::1 dev ${vrf} nodad ip ${ns} ru del pref 0 ip ${ns} ru add pref 32765 from all lookup local ip ${ns} -6 ru del pref 0 ip ${ns} -6 ru add pref 32765 from all lookup local } create_ns() { local ns=$1 local addr=$2 local addr6=$3 [ -z "${addr}" ] && addr="-" [ -z "${addr6}" ] && addr6="-" if [ "${addr}" != "-" ]; then ip -netns ${ns} addr add dev lo ${addr} fi if [ "${addr6}" != "-" ]; then ip -netns ${ns} -6 addr add dev lo ${addr6} fi ip -netns ${ns} ro add unreachable default metric 8192 ip -netns ${ns} -6 ro add unreachable default metric 8192 ip netns exec ${ns} sysctl -qw net.ipv4.ip_forward=1 ip netns exec ${ns} sysctl -qw net.ipv6.conf.all.keep_addr_on_down=1 ip netns exec ${ns} sysctl -qw net.ipv6.conf.all.forwarding=1 ip netns exec ${ns} sysctl -qw net.ipv6.conf.default.forwarding=1 ip netns exec ${ns} sysctl -qw net.ipv6.conf.default.accept_dad=0 } # create veth pair to connect namespaces and apply addresses. connect_ns() { local ns1=$1 local ns1_dev=$2 local ns1_addr=$3 local ns1_addr6=$4 local ns2=$5 local ns2_dev=$6 local ns2_addr=$7 local ns2_addr6=$8 local ns1arg local ns2arg if [ -n "${ns1}" ]; then ns1arg="-netns ${ns1}" fi if [ -n "${ns2}" ]; then ns2arg="-netns ${ns2}" fi ip ${ns1arg} li add ${ns1_dev} type veth peer name tmp ip ${ns1arg} li set ${ns1_dev} up ip ${ns1arg} li set tmp netns ${ns2} name ${ns2_dev} ip ${ns2arg} li set ${ns2_dev} up if [ "${ns1_addr}" != "-" ]; then ip ${ns1arg} addr add dev ${ns1_dev} ${ns1_addr} ip ${ns2arg} addr add dev ${ns2_dev} ${ns2_addr} fi if [ "${ns1_addr6}" != "-" ]; then ip ${ns1arg} addr add dev ${ns1_dev} ${ns1_addr6} nodad ip ${ns2arg} addr add dev ${ns2_dev} ${ns2_addr6} nodad fi } ################################################################################ cleanup() { cleanup_ns $host1 $host2 } setup() { setup_ns host1 host2 create_ns "$host1" create_ns "$host2" connect_ns "$host1" eth0 ${HOST1_4}/24 ${HOST1_6}/64 \ "$host2" eth0 ${HOST2_4}/24 ${HOST2_6}/64 create_vrf "$host1" ${VRF} ${TABLE} ip -netns $host1 link set dev eth0 master ${VRF} } cleanup_xfrm() { for ns in $host1 $host2 do for x in state policy do ip -netns ${ns} xfrm ${x} flush ip -6 -netns ${ns} xfrm ${x} flush done done } setup_xfrm() { local h1_4=$1 local h2_4=$2 local h1_6=$3 local h2_6=$4 local devarg="$5" # # policy # # host1 - IPv4 out ip -netns $host1 xfrm policy add \ src ${h1_4} dst ${h2_4} ${devarg} dir out \ tmpl src ${HOST1_4} dst ${HOST2_4} proto esp mode tunnel # host2 - IPv4 in ip -netns $host2 xfrm policy add \ src ${h1_4} dst ${h2_4} dir in \ tmpl src ${HOST1_4} dst ${HOST2_4} proto esp mode tunnel # host1 - IPv4 in ip -netns $host1 xfrm policy add \ src ${h2_4} dst ${h1_4} ${devarg} dir in \ tmpl src ${HOST2_4} dst ${HOST1_4} proto esp mode tunnel # host2 - IPv4 out ip -netns $host2 xfrm policy add \ src ${h2_4} dst ${h1_4} dir out \ tmpl src ${HOST2_4} dst ${HOST1_4} proto esp mode tunnel # host1 - IPv6 out ip -6 -netns $host1 xfrm policy add \ src ${h1_6} dst ${h2_6} ${devarg} dir out \ tmpl src ${HOST1_6} dst ${HOST2_6} proto esp mode tunnel # host2 - IPv6 in ip -6 -netns $host2 xfrm policy add \ src ${h1_6} dst ${h2_6} dir in \ tmpl src ${HOST1_6} dst ${HOST2_6} proto esp mode tunnel # host1 - IPv6 in ip -6 -netns $host1 xfrm policy add \ src ${h2_6} dst ${h1_6} ${devarg} dir in \ tmpl src ${HOST2_6} dst ${HOST1_6} proto esp mode tunnel # host2 - IPv6 out ip -6 -netns $host2 xfrm policy add \ src ${h2_6} dst ${h1_6} dir out \ tmpl src ${HOST2_6} dst ${HOST1_6} proto esp mode tunnel # # state # ip -netns $host1 xfrm state add src ${HOST1_4} dst ${HOST2_4} \ proto esp spi ${SPI_1} reqid 0 mode tunnel \ replay-window 4 replay-oseq 0x4 \ auth-trunc 'hmac(sha1)' ${AUTH_1} 96 \ enc 'cbc(aes)' ${ENC_1} \ sel src ${h1_4} dst ${h2_4} ${devarg} ip -netns $host2 xfrm state add src ${HOST1_4} dst ${HOST2_4} \ proto esp spi ${SPI_1} reqid 0 mode tunnel \ replay-window 4 replay-oseq 0x4 \ auth-trunc 'hmac(sha1)' ${AUTH_1} 96 \ enc 'cbc(aes)' ${ENC_1} \ sel src ${h1_4} dst ${h2_4} ip -netns $host1 xfrm state add src ${HOST2_4} dst ${HOST1_4} \ proto esp spi ${SPI_2} reqid 0 mode tunnel \ replay-window 4 replay-oseq 0x4 \ auth-trunc 'hmac(sha1)' ${AUTH_2} 96 \ enc 'cbc(aes)' ${ENC_2} \ sel src ${h2_4} dst ${h1_4} ${devarg} ip -netns $host2 xfrm state add src ${HOST2_4} dst ${HOST1_4} \ proto esp spi ${SPI_2} reqid 0 mode tunnel \ replay-window 4 replay-oseq 0x4 \ auth-trunc 'hmac(sha1)' ${AUTH_2} 96 \ enc 'cbc(aes)' ${ENC_2} \ sel src ${h2_4} dst ${h1_4} ip -6 -netns $host1 xfrm state add src ${HOST1_6} dst ${HOST2_6} \ proto esp spi ${SPI_1} reqid 0 mode tunnel \ replay-window 4 replay-oseq 0x4 \ auth-trunc 'hmac(sha1)' ${AUTH_1} 96 \ enc 'cbc(aes)' ${ENC_1} \ sel src ${h1_6} dst ${h2_6} ${devarg} ip -6 -netns $host2 xfrm state add src ${HOST1_6} dst ${HOST2_6} \ proto esp spi ${SPI_1} reqid 0 mode tunnel \ replay-window 4 replay-oseq 0x4 \ auth-trunc 'hmac(sha1)' ${AUTH_1} 96 \ enc 'cbc(aes)' ${ENC_1} \ sel src ${h1_6} dst ${h2_6} ip -6 -netns $host1 xfrm state add src ${HOST2_6} dst ${HOST1_6} \ proto esp spi ${SPI_2} reqid 0 mode tunnel \ replay-window 4 replay-oseq 0x4 \ auth-trunc 'hmac(sha1)' ${AUTH_2} 96 \ enc 'cbc(aes)' ${ENC_2} \ sel src ${h2_6} dst ${h1_6} ${devarg} ip -6 -netns $host2 xfrm state add src ${HOST2_6} dst ${HOST1_6} \ proto esp spi ${SPI_2} reqid 0 mode tunnel \ replay-window 4 replay-oseq 0x4 \ auth-trunc 'hmac(sha1)' ${AUTH_2} 96 \ enc 'cbc(aes)' ${ENC_2} \ sel src ${h2_6} dst ${h1_6} } cleanup_xfrm_dev() { ip -netns $host1 li del xfrm0 ip -netns $host2 addr del ${XFRM2_4}/24 dev eth0 ip -netns $host2 addr del ${XFRM2_6}/64 dev eth0 } setup_xfrm_dev() { local vrfarg="vrf ${VRF}" ip -netns $host1 li add type xfrm dev eth0 if_id ${IF_ID} ip -netns $host1 li set xfrm0 ${vrfarg} up ip -netns $host1 addr add ${XFRM1_4}/24 dev xfrm0 ip -netns $host1 addr add ${XFRM1_6}/64 dev xfrm0 ip -netns $host2 addr add ${XFRM2_4}/24 dev eth0 ip -netns $host2 addr add ${XFRM2_6}/64 dev eth0 setup_xfrm ${XFRM1_4} ${XFRM2_4} ${XFRM1_6} ${XFRM2_6} "if_id ${IF_ID}" } run_tests() { cleanup_xfrm # no IPsec run_cmd_host1 ip vrf exec ${VRF} ping -c1 -w1 ${HOST2_4} log_test $? 0 "IPv4 no xfrm policy" run_cmd_host1 ip vrf exec ${VRF} ${ping6} -c1 -w1 ${HOST2_6} log_test $? 0 "IPv6 no xfrm policy" # xfrm without VRF in sel setup_xfrm ${HOST1_4} ${HOST2_4} ${HOST1_6} ${HOST2_6} run_cmd_host1 ip vrf exec ${VRF} ping -c1 -w1 ${HOST2_4} log_test $? 0 "IPv4 xfrm policy based on address" run_cmd_host1 ip vrf exec ${VRF} ${ping6} -c1 -w1 ${HOST2_6} log_test $? 0 "IPv6 xfrm policy based on address" cleanup_xfrm # xfrm with VRF in sel # Known failure: ipv4 resets the flow oif after the lookup. Fix is # not straightforward. # setup_xfrm ${HOST1_4} ${HOST2_4} ${HOST1_6} ${HOST2_6} "dev ${VRF}" # run_cmd_host1 ip vrf exec ${VRF} ping -c1 -w1 ${HOST2_4} # log_test $? 0 "IPv4 xfrm policy with VRF in selector" run_cmd_host1 ip vrf exec ${VRF} ${ping6} -c1 -w1 ${HOST2_6} log_test $? 0 "IPv6 xfrm policy with VRF in selector" cleanup_xfrm # xfrm with enslaved device in sel # Known failures: combined with the above, __xfrm{4,6}_selector_match # needs to consider both l3mdev and enslaved device index. # setup_xfrm ${HOST1_4} ${HOST2_4} ${HOST1_6} ${HOST2_6} "dev eth0" # run_cmd_host1 ip vrf exec ${VRF} ping -c1 -w1 ${HOST2_4} # log_test $? 0 "IPv4 xfrm policy with enslaved device in selector" # run_cmd_host1 ip vrf exec ${VRF} ${ping6} -c1 -w1 ${HOST2_6} # log_test $? 0 "IPv6 xfrm policy with enslaved device in selector" # cleanup_xfrm # xfrm device setup_xfrm_dev run_cmd_host1 ip vrf exec ${VRF} ping -c1 -w1 ${XFRM2_4} log_test $? 0 "IPv4 xfrm policy with xfrm device" run_cmd_host1 ip vrf exec ${VRF} ${ping6} -c1 -w1 ${XFRM2_6} log_test $? 0 "IPv6 xfrm policy with xfrm device" cleanup_xfrm_dev } ################################################################################ # usage usage() { cat </dev/null setup echo echo "No qdisc on VRF device" run_tests run_cmd_host1 tc qdisc add dev ${VRF} root netem delay 100ms echo echo "netem qdisc on VRF device" run_tests printf "\nTests passed: %3d\n" ${nsuccess} printf "Tests failed: %3d\n" ${nfail} exit $ret