selftests: Setup for functional tests for fib and socket lookups
authorDavid Ahern <dsahern@gmail.com>
Thu, 1 Aug 2019 18:56:35 +0000 (11:56 -0700)
committerDavid S. Miller <davem@davemloft.net>
Sat, 3 Aug 2019 17:42:04 +0000 (10:42 -0700)
Initial commit for functional test suite for fib and socket lookups.
This commit contains the namespace setup, networking config, test options
and other basic infrastructure.

Signed-off-by: David Ahern <dsahern@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
tools/testing/selftests/net/Makefile
tools/testing/selftests/net/fcnal-test.sh [new file with mode: 0755]

index ba9ee36c9e9462a97c6993cc8eef5e2223a423d9..70f2d665617013e9e22143a0ef831e0b65e32f29 100644 (file)
@@ -10,7 +10,7 @@ TEST_PROGS += fib_tests.sh fib-onlink-tests.sh pmtu.sh udpgso.sh ip_defrag.sh
 TEST_PROGS += udpgso_bench.sh fib_rule_tests.sh msg_zerocopy.sh psock_snd.sh
 TEST_PROGS += udpgro_bench.sh udpgro.sh test_vxlan_under_vrf.sh reuseport_addr_any.sh
 TEST_PROGS += test_vxlan_fdb_changelink.sh so_txtime.sh ipv6_flowlabel.sh
-TEST_PROGS += tcp_fastopen_backup_key.sh
+TEST_PROGS += tcp_fastopen_backup_key.sh fcnal-test.sh
 TEST_PROGS_EXTENDED := in_netns.sh
 TEST_GEN_FILES =  socket nettest
 TEST_GEN_FILES += psock_fanout psock_tpacket msg_zerocopy reuseport_addr_any
diff --git a/tools/testing/selftests/net/fcnal-test.sh b/tools/testing/selftests/net/fcnal-test.sh
new file mode 100755 (executable)
index 0000000..22cfbd2
--- /dev/null
@@ -0,0 +1,520 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (c) 2019 David Ahern <dsahern@gmail.com>. All rights reserved.
+#
+# IPv4 and IPv6 functional tests focusing on VRF and routing lookups
+# for various permutations:
+#   1. icmp, tcp, udp and netfilter
+#   2. client, server, no-server
+#   3. global address on interface
+#   4. global address on 'lo'
+#   5. remote and local traffic
+#   6. VRF and non-VRF permutations
+#
+# Setup:
+#                     ns-A     |     ns-B
+# No VRF case:
+#    [ lo ]         [ eth1 ]---|---[ eth1 ]      [ lo ]
+#                                                remote address
+# VRF case:
+#         [ red ]---[ eth1 ]---|---[ eth1 ]      [ lo ]
+#
+# ns-A:
+#     eth1: 172.16.1.1/24, 2001:db8:1::1/64
+#       lo: 127.0.0.1/8, ::1/128
+#           172.16.2.1/32, 2001:db8:2::1/128
+#      red: 127.0.0.1/8, ::1/128
+#           172.16.3.1/32, 2001:db8:3::1/128
+#
+# ns-B:
+#     eth1: 172.16.1.2/24, 2001:db8:1::2/64
+#      lo2: 127.0.0.1/8, ::1/128
+#           172.16.2.2/32, 2001:db8:2::2/128
+#
+# server / client nomenclature relative to ns-A
+
+VERBOSE=0
+
+NSA_DEV=eth1
+NSB_DEV=eth1
+VRF=red
+VRF_TABLE=1101
+
+# IPv4 config
+NSA_IP=172.16.1.1
+NSB_IP=172.16.1.2
+VRF_IP=172.16.3.1
+
+# IPv6 config
+NSA_IP6=2001:db8:1::1
+NSB_IP6=2001:db8:1::2
+VRF_IP6=2001:db8:3::1
+
+NSA_LO_IP=172.16.2.1
+NSB_LO_IP=172.16.2.2
+NSA_LO_IP6=2001:db8:2::1
+NSB_LO_IP6=2001:db8:2::2
+
+MCAST=ff02::1
+# set after namespace create
+NSA_LINKIP6=
+NSB_LINKIP6=
+
+NSA=ns-A
+NSB=ns-B
+
+NSA_CMD="ip netns exec ${NSA}"
+NSB_CMD="ip netns exec ${NSB}"
+
+which ping6 > /dev/null 2>&1 && ping6=$(which ping6) || ping6=$(which ping)
+
+################################################################################
+# utilities
+
+log_test()
+{
+       local rc=$1
+       local expected=$2
+       local msg="$3"
+
+       [ "${VERBOSE}" = "1" ] && echo
+
+       if [ ${rc} -eq ${expected} ]; then
+               nsuccess=$((nsuccess+1))
+               printf "TEST: %-70s  [ OK ]\n" "${msg}"
+       else
+               nfail=$((nfail+1))
+               printf "TEST: %-70s  [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
+
+       if [ "${PAUSE}" = "yes" ]; then
+               echo
+               echo "hit enter to continue, 'q' to quit"
+               read a
+               [ "$a" = "q" ] && exit 1
+       fi
+
+       kill_procs
+}
+
+log_test_addr()
+{
+       local addr=$1
+       local rc=$2
+       local expected=$3
+       local msg="$4"
+       local astr
+
+       astr=$(addr2str ${addr})
+       log_test $rc $expected "$msg - ${astr}"
+}
+
+log_section()
+{
+       echo
+       echo "###########################################################################"
+       echo "$*"
+       echo "###########################################################################"
+       echo
+}
+
+log_subsection()
+{
+       echo
+       echo "#################################################################"
+       echo "$*"
+       echo
+}
+
+log_start()
+{
+       # make sure we have no test instances running
+       kill_procs
+
+       if [ "${VERBOSE}" = "1" ]; then
+               echo
+               echo "#######################################################"
+       fi
+}
+
+log_debug()
+{
+       if [ "${VERBOSE}" = "1" ]; then
+               echo
+               echo "$*"
+               echo
+       fi
+}
+
+show_hint()
+{
+       if [ "${VERBOSE}" = "1" ]; then
+               echo "HINT: $*"
+               echo
+       fi
+}
+
+kill_procs()
+{
+       killall nettest ping ping6 >/dev/null 2>&1
+       sleep 1
+}
+
+do_run_cmd()
+{
+       local cmd="$*"
+       local out
+
+       if [ "$VERBOSE" = "1" ]; then
+               echo "COMMAND: ${cmd}"
+       fi
+
+       out=$($cmd 2>&1)
+       rc=$?
+       if [ "$VERBOSE" = "1" -a -n "$out" ]; then
+               echo "$out"
+       fi
+
+       return $rc
+}
+
+run_cmd()
+{
+       do_run_cmd ${NSA_CMD} $*
+}
+
+run_cmd_nsb()
+{
+       do_run_cmd ${NSB_CMD} $*
+}
+
+setup_cmd()
+{
+       local cmd="$*"
+       local rc
+
+       run_cmd ${cmd}
+       rc=$?
+       if [ $rc -ne 0 ]; then
+               # show user the command if not done so already
+               if [ "$VERBOSE" = "0" ]; then
+                       echo "setup command: $cmd"
+               fi
+               echo "failed. stopping tests"
+               if [ "${PAUSE_ON_FAIL}" = "yes" ]; then
+                       echo
+                       echo "hit enter to continue"
+                       read a
+               fi
+               exit $rc
+       fi
+}
+
+setup_cmd_nsb()
+{
+       local cmd="$*"
+       local rc
+
+       run_cmd_nsb ${cmd}
+       rc=$?
+       if [ $rc -ne 0 ]; then
+               # show user the command if not done so already
+               if [ "$VERBOSE" = "0" ]; then
+                       echo "setup command: $cmd"
+               fi
+               echo "failed. stopping tests"
+               if [ "${PAUSE_ON_FAIL}" = "yes" ]; then
+                       echo
+                       echo "hit enter to continue"
+                       read a
+               fi
+               exit $rc
+       fi
+}
+
+# set sysctl values in NS-A
+set_sysctl()
+{
+       echo "SYSCTL: $*"
+       echo
+       run_cmd sysctl -q -w $*
+}
+
+################################################################################
+# Setup for tests
+
+addr2str()
+{
+       case "$1" in
+       127.0.0.1) echo "loopback";;
+       ::1) echo "IPv6 loopback";;
+
+       ${NSA_IP})      echo "ns-A IP";;
+       ${NSA_IP6})     echo "ns-A IPv6";;
+       ${NSA_LO_IP})   echo "ns-A loopback IP";;
+       ${NSA_LO_IP6})  echo "ns-A loopback IPv6";;
+       ${NSA_LINKIP6}|${NSA_LINKIP6}%*) echo "ns-A IPv6 LLA";;
+
+       ${NSB_IP})      echo "ns-B IP";;
+       ${NSB_IP6})     echo "ns-B IPv6";;
+       ${NSB_LO_IP})   echo "ns-B loopback IP";;
+       ${NSB_LO_IP6})  echo "ns-B loopback IPv6";;
+       ${NSB_LINKIP6}|${NSB_LINKIP6}%*) echo "ns-B IPv6 LLA";;
+
+       ${VRF_IP})      echo "VRF IP";;
+       ${VRF_IP6})     echo "VRF IPv6";;
+
+       ${MCAST}%*)     echo "multicast IP";;
+
+       *) echo "unknown";;
+       esac
+}
+
+get_linklocal()
+{
+       local ns=$1
+       local dev=$2
+       local addr
+
+       addr=$(ip -netns ${ns} -6 -br addr show dev ${dev} | \
+       awk '{
+               for (i = 3; i <= NF; ++i) {
+                       if ($i ~ /^fe80/)
+                               print $i
+               }
+       }'
+       )
+       addr=${addr/\/*}
+
+       [ -z "$addr" ] && return 1
+
+       echo $addr
+
+       return 0
+}
+
+################################################################################
+# create namespaces and vrf
+
+create_vrf()
+{
+       local ns=$1
+       local vrf=$2
+       local table=$3
+       local addr=$4
+       local addr6=$5
+
+       ip -netns ${ns} link add ${vrf} type vrf table ${table}
+       ip -netns ${ns} link set ${vrf} up
+       ip -netns ${ns} route add vrf ${vrf} unreachable default metric 8192
+       ip -netns ${ns} -6 route add vrf ${vrf} unreachable default metric 8192
+
+       ip -netns ${ns} addr add 127.0.0.1/8 dev ${vrf}
+       ip -netns ${ns} -6 addr add ::1 dev ${vrf} nodad
+       if [ "${addr}" != "-" ]; then
+               ip -netns ${ns} addr add dev ${vrf} ${addr}
+       fi
+       if [ "${addr6}" != "-" ]; then
+               ip -netns ${ns} -6 addr add dev ${vrf} ${addr6}
+       fi
+
+       ip -netns ${ns} ru del pref 0
+       ip -netns ${ns} ru add pref 32765 from all lookup local
+       ip -netns ${ns} -6 ru del pref 0
+       ip -netns ${ns} -6 ru add pref 32765 from all lookup local
+}
+
+create_ns()
+{
+       local ns=$1
+       local addr=$2
+       local addr6=$3
+
+       ip netns add ${ns}
+
+       ip -netns ${ns} link set lo up
+       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
+}
+
+# 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
+
+       ip -netns ${ns1} li add ${ns1_dev} type veth peer name tmp
+       ip -netns ${ns1} li set ${ns1_dev} up
+       ip -netns ${ns1} li set tmp netns ${ns2} name ${ns2_dev}
+       ip -netns ${ns2} li set ${ns2_dev} up
+
+       if [ "${ns1_addr}" != "-" ]; then
+               ip -netns ${ns1} addr add dev ${ns1_dev} ${ns1_addr}
+               ip -netns ${ns2} addr add dev ${ns2_dev} ${ns2_addr}
+       fi
+
+       if [ "${ns1_addr6}" != "-" ]; then
+               ip -netns ${ns1} addr add dev ${ns1_dev} ${ns1_addr6}
+               ip -netns ${ns2} addr add dev ${ns2_dev} ${ns2_addr6}
+       fi
+}
+
+cleanup()
+{
+       # explicit cleanups to check those code paths
+       ip netns | grep -q ${NSA}
+       if [ $? -eq 0 ]; then
+               ip -netns ${NSA} link delete ${VRF}
+               ip -netns ${NSA} ro flush table ${VRF_TABLE}
+
+               ip -netns ${NSA} addr flush dev ${NSA_DEV}
+               ip -netns ${NSA} -6 addr flush dev ${NSA_DEV}
+               ip -netns ${NSA} link set dev ${NSA_DEV} down
+               ip -netns ${NSA} link del dev ${NSA_DEV}
+
+               ip netns del ${NSA}
+       fi
+
+       ip netns del ${NSB}
+}
+
+setup()
+{
+       local with_vrf=${1}
+
+       # make sure we are starting with a clean slate
+       kill_procs
+       cleanup 2>/dev/null
+
+       log_debug "Configuring network namespaces"
+       set -e
+
+       create_ns ${NSA} ${NSA_LO_IP}/32 ${NSA_LO_IP6}/128
+       create_ns ${NSB} ${NSB_LO_IP}/32 ${NSB_LO_IP6}/128
+       connect_ns ${NSA} ${NSA_DEV} ${NSA_IP}/24 ${NSA_IP6}/64 \
+                  ${NSB} ${NSB_DEV} ${NSB_IP}/24 ${NSB_IP6}/64
+
+       NSA_LINKIP6=$(get_linklocal ${NSA} ${NSA_DEV})
+       NSB_LINKIP6=$(get_linklocal ${NSB} ${NSB_DEV})
+
+       # tell ns-A how to get to remote addresses of ns-B
+       if [ "${with_vrf}" = "yes" ]; then
+               create_vrf ${NSA} ${VRF} ${VRF_TABLE} ${VRF_IP} ${VRF_IP6}
+
+               ip -netns ${NSA} link set dev ${NSA_DEV} vrf ${VRF}
+               ip -netns ${NSA} ro add vrf ${VRF} ${NSB_LO_IP}/32 via ${NSB_IP} dev ${NSA_DEV}
+               ip -netns ${NSA} -6 ro add vrf ${VRF} ${NSB_LO_IP6}/128 via ${NSB_IP6} dev ${NSA_DEV}
+
+               ip -netns ${NSB} ro add ${VRF_IP}/32 via ${NSA_IP} dev ${NSB_DEV}
+               ip -netns ${NSB} -6 ro add ${VRF_IP6}/128 via ${NSA_IP6} dev ${NSB_DEV}
+       else
+               ip -netns ${NSA} ro add ${NSB_LO_IP}/32 via ${NSB_IP} dev ${NSA_DEV}
+               ip -netns ${NSA} ro add ${NSB_LO_IP6}/128 via ${NSB_IP6} dev ${NSA_DEV}
+       fi
+
+
+       # tell ns-B how to get to remote addresses of ns-A
+       ip -netns ${NSB} ro add ${NSA_LO_IP}/32 via ${NSA_IP} dev ${NSB_DEV}
+       ip -netns ${NSB} ro add ${NSA_LO_IP6}/128 via ${NSA_IP6} dev ${NSB_DEV}
+
+       set +e
+
+       sleep 1
+}
+
+################################################################################
+# usage
+
+usage()
+{
+       cat <<EOF
+usage: ${0##*/} OPTS
+
+       -4          IPv4 tests only
+       -6          IPv6 tests only
+       -t <test>   Test name/set to run
+       -p          Pause on fail
+       -P          Pause after each test
+       -v          Be verbose
+EOF
+}
+
+################################################################################
+# main
+
+TESTS_IPV4=""
+TESTS_IPV6=""
+PAUSE_ON_FAIL=no
+PAUSE=no
+
+while getopts :46t:pPvh o
+do
+       case $o in
+               4) TESTS=ipv4;;
+               6) TESTS=ipv6;;
+               t) TESTS=$OPTARG;;
+               p) PAUSE_ON_FAIL=yes;;
+               P) PAUSE=yes;;
+               v) VERBOSE=1;;
+               h) usage; exit 0;;
+               *) usage; exit 1;;
+       esac
+done
+
+# make sure we don't pause twice
+[ "${PAUSE}" = "yes" ] && PAUSE_ON_FAIL=no
+
+#
+# show user test config
+#
+if [ -z "$TESTS" ]; then
+       TESTS="$TESTS_IPV4 $TESTS_IPV6 $TESTS_OTHER"
+elif [ "$TESTS" = "ipv4" ]; then
+       TESTS="$TESTS_IPV4"
+elif [ "$TESTS" = "ipv6" ]; then
+       TESTS="$TESTS_IPV6"
+fi
+
+declare -i nfail=0
+declare -i nsuccess=0
+
+for t in $TESTS
+do
+       case $t in
+       # setup namespaces and config, but do not run any tests
+       setup)           setup; exit 0;;
+       vrf_setup)       setup "yes"; exit 0;;
+
+       help)            echo "Test names: $TESTS"; exit 0;;
+       esac
+done
+
+cleanup 2>/dev/null
+
+printf "\nTests passed: %3d\n" ${nsuccess}
+printf "Tests failed: %3d\n"   ${nfail}