summaryrefslogtreecommitdiffstats
path: root/tools/testing/selftests/net
diff options
context:
space:
mode:
Diffstat (limited to 'tools/testing/selftests/net')
-rw-r--r--tools/testing/selftests/net/.gitignore2
-rw-r--r--tools/testing/selftests/net/Makefile6
-rwxr-xr-xtools/testing/selftests/net/altnames.sh75
-rwxr-xr-xtools/testing/selftests/net/fcnal-test.sh3890
-rwxr-xr-xtools/testing/selftests/net/fib_nexthop_multiprefix.sh6
-rwxr-xr-xtools/testing/selftests/net/fib_nexthops.sh38
-rwxr-xr-xtools/testing/selftests/net/fib_tests.sh92
-rw-r--r--tools/testing/selftests/net/fin_ack_lat.c151
-rwxr-xr-xtools/testing/selftests/net/fin_ack_lat.sh35
-rw-r--r--tools/testing/selftests/net/forwarding/devlink_lib.sh244
-rwxr-xr-xtools/testing/selftests/net/forwarding/ethtool.sh318
-rwxr-xr-xtools/testing/selftests/net/forwarding/ethtool_lib.sh69
-rw-r--r--tools/testing/selftests/net/forwarding/fib_offload_lib.sh873
-rw-r--r--tools/testing/selftests/net/forwarding/lib.sh139
-rwxr-xr-xtools/testing/selftests/net/forwarding/loopback.sh8
-rwxr-xr-xtools/testing/selftests/net/forwarding/router.sh189
-rwxr-xr-xtools/testing/selftests/net/forwarding/router_bridge_vlan.sh2
-rwxr-xr-xtools/testing/selftests/net/forwarding/sch_ets.sh44
-rw-r--r--tools/testing/selftests/net/forwarding/sch_ets_core.sh300
-rw-r--r--tools/testing/selftests/net/forwarding/sch_ets_tests.sh227
-rw-r--r--tools/testing/selftests/net/forwarding/sch_tbf_core.sh233
-rwxr-xr-xtools/testing/selftests/net/forwarding/sch_tbf_ets.sh6
-rw-r--r--tools/testing/selftests/net/forwarding/sch_tbf_etsprio.sh39
-rwxr-xr-xtools/testing/selftests/net/forwarding/sch_tbf_prio.sh6
-rwxr-xr-xtools/testing/selftests/net/forwarding/sch_tbf_root.sh33
-rw-r--r--tools/testing/selftests/net/forwarding/tc_common.sh53
-rwxr-xr-xtools/testing/selftests/net/l2tp.sh382
-rw-r--r--tools/testing/selftests/net/mptcp/.gitignore2
-rw-r--r--tools/testing/selftests/net/mptcp/Makefile13
-rw-r--r--tools/testing/selftests/net/mptcp/config4
-rw-r--r--tools/testing/selftests/net/mptcp/mptcp_connect.c841
-rwxr-xr-xtools/testing/selftests/net/mptcp/mptcp_connect.sh595
-rw-r--r--tools/testing/selftests/net/mptcp/settings1
-rw-r--r--tools/testing/selftests/net/nettest.c1813
-rwxr-xr-xtools/testing/selftests/net/pmtu.sh11
-rw-r--r--tools/testing/selftests/net/reuseport_dualstack.c3
-rw-r--r--tools/testing/selftests/net/so_txtime.c88
-rwxr-xr-xtools/testing/selftests/net/so_txtime.sh9
-rw-r--r--tools/testing/selftests/net/tcp_mmap.c73
-rw-r--r--tools/testing/selftests/net/tls.c148
-rwxr-xr-xtools/testing/selftests/net/traceroute.sh322
-rw-r--r--tools/testing/selftests/net/udpgso.c19
-rw-r--r--tools/testing/selftests/net/udpgso_bench_tx.c3
-rwxr-xr-xtools/testing/selftests/net/xfrm_policy.sh7
44 files changed, 11319 insertions, 93 deletions
diff --git a/tools/testing/selftests/net/.gitignore b/tools/testing/selftests/net/.gitignore
index c7cced739c34..ecc52d4c034d 100644
--- a/tools/testing/selftests/net/.gitignore
+++ b/tools/testing/selftests/net/.gitignore
@@ -21,3 +21,5 @@ ipv6_flowlabel
ipv6_flowlabel_mgr
so_txtime
tcp_fastopen_backup_key
+nettest
+fin_ack_lat
diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile
index 1b24e36b4047..b5694196430a 100644
--- a/tools/testing/selftests/net/Makefile
+++ b/tools/testing/selftests/net/Makefile
@@ -10,14 +10,16 @@ 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 l2tp.sh traceroute.sh
+TEST_PROGS += fin_ack_lat.sh
TEST_PROGS_EXTENDED := in_netns.sh
-TEST_GEN_FILES = socket
+TEST_GEN_FILES = socket nettest
TEST_GEN_FILES += psock_fanout psock_tpacket msg_zerocopy reuseport_addr_any
TEST_GEN_FILES += tcp_mmap tcp_inq psock_snd txring_overwrite
TEST_GEN_FILES += udpgso udpgso_bench_tx udpgso_bench_rx ip_defrag
TEST_GEN_FILES += so_txtime ipv6_flowlabel ipv6_flowlabel_mgr
TEST_GEN_FILES += tcp_fastopen_backup_key
+TEST_GEN_FILES += fin_ack_lat
TEST_GEN_PROGS = reuseport_bpf reuseport_bpf_cpu reuseport_bpf_numa
TEST_GEN_PROGS += reuseport_dualstack reuseaddr_conflict tls
diff --git a/tools/testing/selftests/net/altnames.sh b/tools/testing/selftests/net/altnames.sh
new file mode 100755
index 000000000000..4254ddc3f70b
--- /dev/null
+++ b/tools/testing/selftests/net/altnames.sh
@@ -0,0 +1,75 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+lib_dir=$(dirname $0)/forwarding
+
+ALL_TESTS="altnames_test"
+NUM_NETIFS=0
+source $lib_dir/lib.sh
+
+DUMMY_DEV=dummytest
+SHORT_NAME=shortname
+LONG_NAME=someveryveryveryveryveryverylongname
+
+altnames_test()
+{
+ RET=0
+ local output
+ local name
+
+ ip link property add $DUMMY_DEV altname $SHORT_NAME
+ check_err $? "Failed to add short alternative name"
+
+ output=$(ip -j -p link show $SHORT_NAME)
+ check_err $? "Failed to do link show with short alternative name"
+
+ name=$(echo $output | jq -e -r ".[0].altnames[0]")
+ check_err $? "Failed to get short alternative name from link show JSON"
+
+ [ "$name" == "$SHORT_NAME" ]
+ check_err $? "Got unexpected short alternative name from link show JSON"
+
+ ip -j -p link show $DUMMY_DEV &>/dev/null
+ check_err $? "Failed to do link show with original name"
+
+ ip link property add $DUMMY_DEV altname $LONG_NAME
+ check_err $? "Failed to add long alternative name"
+
+ output=$(ip -j -p link show $LONG_NAME)
+ check_err $? "Failed to do link show with long alternative name"
+
+ name=$(echo $output | jq -e -r ".[0].altnames[1]")
+ check_err $? "Failed to get long alternative name from link show JSON"
+
+ [ "$name" == "$LONG_NAME" ]
+ check_err $? "Got unexpected long alternative name from link show JSON"
+
+ ip link property del $DUMMY_DEV altname $SHORT_NAME
+ check_err $? "Failed to add short alternative name"
+
+ ip -j -p link show $SHORT_NAME &>/dev/null
+ check_fail $? "Unexpected success while trying to do link show with deleted short alternative name"
+
+ # long name is left there on purpose to be removed alongside the device
+
+ log_test "altnames test"
+}
+
+setup_prepare()
+{
+ ip link add name $DUMMY_DEV type dummy
+}
+
+cleanup()
+{
+ pre_cleanup
+ ip link del name $DUMMY_DEV
+}
+
+trap cleanup EXIT
+
+setup_prepare
+
+tests_run
+
+exit $EXIT_STATUS
diff --git a/tools/testing/selftests/net/fcnal-test.sh b/tools/testing/selftests/net/fcnal-test.sh
new file mode 100755
index 000000000000..fb5c55dd6df8
--- /dev/null
+++ b/tools/testing/selftests/net/fcnal-test.sh
@@ -0,0 +1,3890 @@
+#!/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
+#
+# ns-A to ns-C connection - only for VRF and same config
+# as ns-A to ns-B
+#
+# server / client nomenclature relative to ns-A
+
+VERBOSE=0
+
+NSA_DEV=eth1
+NSA_DEV2=eth2
+NSB_DEV=eth1
+NSC_DEV=eth2
+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
+NS_NET=172.16.1.0/24
+
+# IPv6 config
+NSA_IP6=2001:db8:1::1
+NSB_IP6=2001:db8:1::2
+VRF_IP6=2001:db8:3::1
+NS_NET6=2001:db8:1::/120
+
+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
+
+MD5_PW=abc123
+MD5_WRONG_PW=abc1234
+
+MCAST=ff02::1
+# set after namespace create
+NSA_LINKIP6=
+NSB_LINKIP6=
+
+NSA=ns-A
+NSB=ns-B
+NSC=ns-C
+
+NSA_CMD="ip netns exec ${NSA}"
+NSB_CMD="ip netns exec ${NSB}"
+NSC_CMD="ip netns exec ${NSC}"
+
+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} $*
+}
+
+run_cmd_nsc()
+{
+ do_run_cmd ${NSC_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}
+ ip netns del ${NSC} >/dev/null 2>&1
+}
+
+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}
+
+ # some VRF tests use ns-C which has the same config as
+ # ns-B but for a device NOT in the VRF
+ create_ns ${NSC} "-" "-"
+ connect_ns ${NSA} ${NSA_DEV2} ${NSA_IP}/24 ${NSA_IP6}/64 \
+ ${NSC} ${NSC_DEV} ${NSB_IP}/24 ${NSB_IP6}/64
+ 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
+}
+
+################################################################################
+# IPv4
+
+ipv4_ping_novrf()
+{
+ local a
+
+ #
+ # out
+ #
+ for a in ${NSB_IP} ${NSB_LO_IP}
+ do
+ log_start
+ run_cmd ping -c1 -w1 ${a}
+ log_test_addr ${a} $? 0 "ping out"
+
+ log_start
+ run_cmd ping -c1 -w1 -I ${NSA_DEV} ${a}
+ log_test_addr ${a} $? 0 "ping out, device bind"
+
+ log_start
+ run_cmd ping -c1 -w1 -I ${NSA_LO_IP} ${a}
+ log_test_addr ${a} $? 0 "ping out, address bind"
+ done
+
+ #
+ # in
+ #
+ for a in ${NSA_IP} ${NSA_LO_IP}
+ do
+ log_start
+ run_cmd_nsb ping -c1 -w1 ${a}
+ log_test_addr ${a} $? 0 "ping in"
+ done
+
+ #
+ # local traffic
+ #
+ for a in ${NSA_IP} ${NSA_LO_IP} 127.0.0.1
+ do
+ log_start
+ run_cmd ping -c1 -w1 ${a}
+ log_test_addr ${a} $? 0 "ping local"
+ done
+
+ #
+ # local traffic, socket bound to device
+ #
+ # address on device
+ a=${NSA_IP}
+ log_start
+ run_cmd ping -c1 -w1 -I ${NSA_DEV} ${a}
+ log_test_addr ${a} $? 0 "ping local, device bind"
+
+ # loopback addresses not reachable from device bind
+ # fails in a really weird way though because ipv4 special cases
+ # route lookups with oif set.
+ for a in ${NSA_LO_IP} 127.0.0.1
+ do
+ log_start
+ show_hint "Fails since address on loopback device is out of device scope"
+ run_cmd ping -c1 -w1 -I ${NSA_DEV} ${a}
+ log_test_addr ${a} $? 1 "ping local, device bind"
+ done
+
+ #
+ # ip rule blocks reachability to remote address
+ #
+ log_start
+ setup_cmd ip rule add pref 32765 from all lookup local
+ setup_cmd ip rule del pref 0 from all lookup local
+ setup_cmd ip rule add pref 50 to ${NSB_LO_IP} prohibit
+ setup_cmd ip rule add pref 51 from ${NSB_IP} prohibit
+
+ a=${NSB_LO_IP}
+ run_cmd ping -c1 -w1 ${a}
+ log_test_addr ${a} $? 2 "ping out, blocked by rule"
+
+ # NOTE: ipv4 actually allows the lookup to fail and yet still create
+ # a viable rtable if the oif (e.g., bind to device) is set, so this
+ # case succeeds despite the rule
+ # run_cmd ping -c1 -w1 -I ${NSA_DEV} ${a}
+
+ a=${NSA_LO_IP}
+ log_start
+ show_hint "Response generates ICMP (or arp request is ignored) due to ip rule"
+ run_cmd_nsb ping -c1 -w1 ${a}
+ log_test_addr ${a} $? 1 "ping in, blocked by rule"
+
+ [ "$VERBOSE" = "1" ] && echo
+ setup_cmd ip rule del pref 32765 from all lookup local
+ setup_cmd ip rule add pref 0 from all lookup local
+ setup_cmd ip rule del pref 50 to ${NSB_LO_IP} prohibit
+ setup_cmd ip rule del pref 51 from ${NSB_IP} prohibit
+
+ #
+ # route blocks reachability to remote address
+ #
+ log_start
+ setup_cmd ip route replace unreachable ${NSB_LO_IP}
+ setup_cmd ip route replace unreachable ${NSB_IP}
+
+ a=${NSB_LO_IP}
+ run_cmd ping -c1 -w1 ${a}
+ log_test_addr ${a} $? 2 "ping out, blocked by route"
+
+ # NOTE: ipv4 actually allows the lookup to fail and yet still create
+ # a viable rtable if the oif (e.g., bind to device) is set, so this
+ # case succeeds despite not having a route for the address
+ # run_cmd ping -c1 -w1 -I ${NSA_DEV} ${a}
+
+ a=${NSA_LO_IP}
+ log_start
+ show_hint "Response is dropped (or arp request is ignored) due to ip route"
+ run_cmd_nsb ping -c1 -w1 ${a}
+ log_test_addr ${a} $? 1 "ping in, blocked by route"
+
+ #
+ # remove 'remote' routes; fallback to default
+ #
+ log_start
+ setup_cmd ip ro del ${NSB_LO_IP}
+
+ a=${NSB_LO_IP}
+ run_cmd ping -c1 -w1 ${a}
+ log_test_addr ${a} $? 2 "ping out, unreachable default route"
+
+ # NOTE: ipv4 actually allows the lookup to fail and yet still create
+ # a viable rtable if the oif (e.g., bind to device) is set, so this
+ # case succeeds despite not having a route for the address
+ # run_cmd ping -c1 -w1 -I ${NSA_DEV} ${a}
+}
+
+ipv4_ping_vrf()
+{
+ local a
+
+ # should default on; does not exist on older kernels
+ set_sysctl net.ipv4.raw_l3mdev_accept=1 2>/dev/null
+
+ #
+ # out
+ #
+ for a in ${NSB_IP} ${NSB_LO_IP}
+ do
+ log_start
+ run_cmd ping -c1 -w1 -I ${VRF} ${a}
+ log_test_addr ${a} $? 0 "ping out, VRF bind"
+
+ log_start
+ run_cmd ping -c1 -w1 -I ${NSA_DEV} ${a}
+ log_test_addr ${a} $? 0 "ping out, device bind"
+
+ log_start
+ run_cmd ip vrf exec ${VRF} ping -c1 -w1 -I ${NSA_IP} ${a}
+ log_test_addr ${a} $? 0 "ping out, vrf device + dev address bind"
+
+ log_start
+ run_cmd ip vrf exec ${VRF} ping -c1 -w1 -I ${VRF_IP} ${a}
+ log_test_addr ${a} $? 0 "ping out, vrf device + vrf address bind"
+ done
+
+ #
+ # in
+ #
+ for a in ${NSA_IP} ${VRF_IP}
+ do
+ log_start
+ run_cmd_nsb ping -c1 -w1 ${a}
+ log_test_addr ${a} $? 0 "ping in"
+ done
+
+ #
+ # local traffic, local address
+ #
+ for a in ${NSA_IP} ${VRF_IP} 127.0.0.1
+ do
+ log_start
+ show_hint "Source address should be ${a}"
+ run_cmd ping -c1 -w1 -I ${VRF} ${a}
+ log_test_addr ${a} $? 0 "ping local, VRF bind"
+ done
+
+ #
+ # local traffic, socket bound to device
+ #
+ # address on device
+ a=${NSA_IP}
+ log_start
+ run_cmd ping -c1 -w1 -I ${NSA_DEV} ${a}
+ log_test_addr ${a} $? 0 "ping local, device bind"
+
+ # vrf device is out of scope
+ for a in ${VRF_IP} 127.0.0.1
+ do
+ log_start
+ show_hint "Fails since address on vrf device is out of device scope"
+ run_cmd ping -c1 -w1 -I ${NSA_DEV} ${a}
+ log_test_addr ${a} $? 1 "ping local, device bind"
+ done
+
+ #
+ # ip rule blocks address
+ #
+ log_start
+ setup_cmd ip rule add pref 50 to ${NSB_LO_IP} prohibit
+ setup_cmd ip rule add pref 51 from ${NSB_IP} prohibit
+
+ a=${NSB_LO_IP}
+ run_cmd ping -c1 -w1 -I ${VRF} ${a}
+ log_test_addr ${a} $? 2 "ping out, vrf bind, blocked by rule"
+
+ log_start
+ run_cmd ping -c1 -w1 -I ${NSA_DEV} ${a}
+ log_test_addr ${a} $? 2 "ping out, device bind, blocked by rule"
+
+ a=${NSA_LO_IP}
+ log_start
+ show_hint "Response lost due to ip rule"
+ run_cmd_nsb ping -c1 -w1 ${a}
+ log_test_addr ${a} $? 1 "ping in, blocked by rule"
+
+ [ "$VERBOSE" = "1" ] && echo
+ setup_cmd ip rule del pref 50 to ${NSB_LO_IP} prohibit
+ setup_cmd ip rule del pref 51 from ${NSB_IP} prohibit
+
+ #
+ # remove 'remote' routes; fallback to default
+ #
+ log_start
+ setup_cmd ip ro del vrf ${VRF} ${NSB_LO_IP}
+
+ a=${NSB_LO_IP}
+ run_cmd ping -c1 -w1 -I ${VRF} ${a}
+ log_test_addr ${a} $? 2 "ping out, vrf bind, unreachable route"
+
+ log_start
+ run_cmd ping -c1 -w1 -I ${NSA_DEV} ${a}
+ log_test_addr ${a} $? 2 "ping out, device bind, unreachable route"
+
+ a=${NSA_LO_IP}
+ log_start
+ show_hint "Response lost by unreachable route"
+ run_cmd_nsb ping -c1 -w1 ${a}
+ log_test_addr ${a} $? 1 "ping in, unreachable route"
+}
+
+ipv4_ping()
+{
+ log_section "IPv4 ping"
+
+ log_subsection "No VRF"
+ setup
+ set_sysctl net.ipv4.raw_l3mdev_accept=0 2>/dev/null
+ ipv4_ping_novrf
+ setup
+ set_sysctl net.ipv4.raw_l3mdev_accept=1 2>/dev/null
+ ipv4_ping_novrf
+
+ log_subsection "With VRF"
+ setup "yes"
+ ipv4_ping_vrf
+}
+
+################################################################################
+# IPv4 TCP
+
+#
+# MD5 tests without VRF
+#
+ipv4_tcp_md5_novrf()
+{
+ #
+ # single address
+ #
+
+ # basic use case
+ log_start
+ run_cmd nettest -s -M ${MD5_PW} -r ${NSB_IP} &
+ sleep 1
+ run_cmd_nsb nettest -r ${NSA_IP} -M ${MD5_PW}
+ log_test $? 0 "MD5: Single address config"
+
+ # client sends MD5, server not configured
+ log_start
+ show_hint "Should timeout due to MD5 mismatch"
+ run_cmd nettest -s &
+ sleep 1
+ run_cmd_nsb nettest -r ${NSA_IP} -M ${MD5_PW}
+ log_test $? 2 "MD5: Server no config, client uses password"
+
+ # wrong password
+ log_start
+ show_hint "Should timeout since client uses wrong password"
+ run_cmd nettest -s -M ${MD5_PW} -r ${NSB_IP} &
+ sleep 1
+ run_cmd_nsb nettest -r ${NSA_IP} -M ${MD5_WRONG_PW}
+ log_test $? 2 "MD5: Client uses wrong password"
+
+ # client from different address
+ log_start
+ show_hint "Should timeout due to MD5 mismatch"
+ run_cmd nettest -s -M ${MD5_PW} -r ${NSB_LO_IP} &
+ sleep 1
+ run_cmd_nsb nettest -r ${NSA_IP} -M ${MD5_PW}
+ log_test $? 2 "MD5: Client address does not match address configured with password"
+
+ #
+ # MD5 extension - prefix length
+ #
+
+ # client in prefix
+ log_start
+ run_cmd nettest -s -M ${MD5_PW} -m ${NS_NET} &
+ sleep 1
+ run_cmd_nsb nettest -r ${NSA_IP} -M ${MD5_PW}
+ log_test $? 0 "MD5: Prefix config"
+
+ # client in prefix, wrong password
+ log_start
+ show_hint "Should timeout since client uses wrong password"
+ run_cmd nettest -s -M ${MD5_PW} -m ${NS_NET} &
+ sleep 1
+ run_cmd_nsb nettest -r ${NSA_IP} -M ${MD5_WRONG_PW}
+ log_test $? 2 "MD5: Prefix config, client uses wrong password"
+
+ # client outside of prefix
+ log_start
+ show_hint "Should timeout due to MD5 mismatch"
+ run_cmd nettest -s -M ${MD5_PW} -m ${NS_NET} &
+ sleep 1
+ run_cmd_nsb nettest -l ${NSB_LO_IP} -r ${NSA_IP} -M ${MD5_PW}
+ log_test $? 2 "MD5: Prefix config, client address not in configured prefix"
+}
+
+#
+# MD5 tests with VRF
+#
+ipv4_tcp_md5()
+{
+ #
+ # single address
+ #
+
+ # basic use case
+ log_start
+ run_cmd nettest -s -d ${VRF} -M ${MD5_PW} -r ${NSB_IP} &
+ sleep 1
+ run_cmd_nsb nettest -r ${NSA_IP} -M ${MD5_PW}
+ log_test $? 0 "MD5: VRF: Single address config"
+
+ # client sends MD5, server not configured
+ log_start
+ show_hint "Should timeout since server does not have MD5 auth"
+ run_cmd nettest -s -d ${VRF} &
+ sleep 1
+ run_cmd_nsb nettest -r ${NSA_IP} -M ${MD5_PW}
+ log_test $? 2 "MD5: VRF: Server no config, client uses password"
+
+ # wrong password
+ log_start
+ show_hint "Should timeout since client uses wrong password"
+ run_cmd nettest -s -d ${VRF} -M ${MD5_PW} -r ${NSB_IP} &
+ sleep 1
+ run_cmd_nsb nettest -r ${NSA_IP} -M ${MD5_WRONG_PW}
+ log_test $? 2 "MD5: VRF: Client uses wrong password"
+
+ # client from different address
+ log_start
+ show_hint "Should timeout since server config differs from client"
+ run_cmd nettest -s -d ${VRF} -M ${MD5_PW} -r ${NSB_LO_IP} &
+ sleep 1
+ run_cmd_nsb nettest -r ${NSA_IP} -M ${MD5_PW}
+ log_test $? 2 "MD5: VRF: Client address does not match address configured with password"
+
+ #
+ # MD5 extension - prefix length
+ #
+
+ # client in prefix
+ log_start
+ run_cmd nettest -s -d ${VRF} -M ${MD5_PW} -m ${NS_NET} &
+ sleep 1
+ run_cmd_nsb nettest -r ${NSA_IP} -M ${MD5_PW}
+ log_test $? 0 "MD5: VRF: Prefix config"
+
+ # client in prefix, wrong password
+ log_start
+ show_hint "Should timeout since client uses wrong password"
+ run_cmd nettest -s -d ${VRF} -M ${MD5_PW} -m ${NS_NET} &
+ sleep 1
+ run_cmd_nsb nettest -r ${NSA_IP} -M ${MD5_WRONG_PW}
+ log_test $? 2 "MD5: VRF: Prefix config, client uses wrong password"
+
+ # client outside of prefix
+ log_start
+ show_hint "Should timeout since client address is outside of prefix"
+ run_cmd nettest -s -d ${VRF} -M ${MD5_PW} -m ${NS_NET} &
+ sleep 1
+ run_cmd_nsb nettest -l ${NSB_LO_IP} -r ${NSA_IP} -M ${MD5_PW}
+ log_test $? 2 "MD5: VRF: Prefix config, client address not in configured prefix"
+
+ #
+ # duplicate config between default VRF and a VRF
+ #
+
+ log_start
+ run_cmd nettest -s -d ${VRF} -M ${MD5_PW} -r ${NSB_IP} &
+ run_cmd nettest -s -M ${MD5_WRONG_PW} -r ${NSB_IP} &
+ sleep 1
+ run_cmd_nsb nettest -r ${NSA_IP} -M ${MD5_PW}
+ log_test $? 0 "MD5: VRF: Single address config in default VRF and VRF, conn in VRF"
+
+ log_start
+ run_cmd nettest -s -d ${VRF} -M ${MD5_PW} -r ${NSB_IP} &
+ run_cmd nettest -s -M ${MD5_WRONG_PW} -r ${NSB_IP} &
+ sleep 1
+ run_cmd_nsc nettest -r ${NSA_IP} -M ${MD5_WRONG_PW}
+ log_test $? 0 "MD5: VRF: Single address config in default VRF and VRF, conn in default VRF"
+
+ log_start
+ show_hint "Should timeout since client in default VRF uses VRF password"
+ run_cmd nettest -s -d ${VRF} -M ${MD5_PW} -r ${NSB_IP} &
+ run_cmd nettest -s -M ${MD5_WRONG_PW} -r ${NSB_IP} &
+ sleep 1
+ run_cmd_nsc nettest -r ${NSA_IP} -M ${MD5_PW}
+ log_test $? 2 "MD5: VRF: Single address config in default VRF and VRF, conn in default VRF with VRF pw"
+
+ log_start
+ show_hint "Should timeout since client in VRF uses default VRF password"
+ run_cmd nettest -s -d ${VRF} -M ${MD5_PW} -r ${NSB_IP} &
+ run_cmd nettest -s -M ${MD5_WRONG_PW} -r ${NSB_IP} &
+ sleep 1
+ run_cmd_nsb nettest -r ${NSA_IP} -M ${MD5_WRONG_PW}
+ log_test $? 2 "MD5: VRF: Single address config in default VRF and VRF, conn in VRF with default VRF pw"
+
+ log_start
+ run_cmd nettest -s -d ${VRF} -M ${MD5_PW} -m ${NS_NET} &
+ run_cmd nettest -s -M ${MD5_WRONG_PW} -m ${NS_NET} &
+ sleep 1
+ run_cmd_nsb nettest -r ${NSA_IP} -M ${MD5_PW}
+ log_test $? 0 "MD5: VRF: Prefix config in default VRF and VRF, conn in VRF"
+
+ log_start
+ run_cmd nettest -s -d ${VRF} -M ${MD5_PW} -m ${NS_NET} &
+ run_cmd nettest -s -M ${MD5_WRONG_PW} -m ${NS_NET} &
+ sleep 1
+ run_cmd_nsc nettest -r ${NSA_IP} -M ${MD5_WRONG_PW}
+ log_test $? 0 "MD5: VRF: Prefix config in default VRF and VRF, conn in default VRF"
+
+ log_start
+ show_hint "Should timeout since client in default VRF uses VRF password"
+ run_cmd nettest -s -d ${VRF} -M ${MD5_PW} -m ${NS_NET} &
+ run_cmd nettest -s -M ${MD5_WRONG_PW} -m ${NS_NET} &
+ sleep 1
+ run_cmd_nsc nettest -r ${NSA_IP} -M ${MD5_PW}
+ log_test $? 2 "MD5: VRF: Prefix config in default VRF and VRF, conn in default VRF with VRF pw"
+
+ log_start
+ show_hint "Should timeout since client in VRF uses default VRF password"
+ run_cmd nettest -s -d ${VRF} -M ${MD5_PW} -m ${NS_NET} &
+ run_cmd nettest -s -M ${MD5_WRONG_PW} -m ${NS_NET} &
+ sleep 1
+ run_cmd_nsb nettest -r ${NSA_IP} -M ${MD5_WRONG_PW}
+ log_test $? 2 "MD5: VRF: Prefix config in default VRF and VRF, conn in VRF with default VRF pw"
+
+ #
+ # negative tests
+ #
+ log_start
+ run_cmd nettest -s -d ${NSA_DEV} -M ${MD5_PW} -r ${NSB_IP}
+ log_test $? 1 "MD5: VRF: Device must be a VRF - single address"
+
+ log_start
+ run_cmd nettest -s -d ${NSA_DEV} -M ${MD5_PW} -m ${NS_NET}
+ log_test $? 1 "MD5: VRF: Device must be a VRF - prefix"
+
+}
+
+ipv4_tcp_novrf()
+{
+ local a
+
+ #
+ # server tests
+ #
+ for a in ${NSA_IP} ${NSA_LO_IP}
+ do
+ log_start
+ run_cmd nettest -s &
+ sleep 1
+ run_cmd_nsb nettest -r ${a}
+ log_test_addr ${a} $? 0 "Global server"
+ done
+
+ a=${NSA_IP}
+ log_start
+ run_cmd nettest -s -d ${NSA_DEV} &
+ sleep 1
+ run_cmd_nsb nettest -r ${a}
+ log_test_addr ${a} $? 0 "Device server"
+
+ # verify TCP reset sent and received
+ for a in ${NSA_IP} ${NSA_LO_IP}
+ do
+ log_start
+ show_hint "Should fail 'Connection refused' since there is no server"
+ run_cmd_nsb nettest -r ${a}
+ log_test_addr ${a} $? 1 "No server"
+ done
+
+ #
+ # client
+ #
+ for a in ${NSB_IP} ${NSB_LO_IP}
+ do
+ log_start
+ run_cmd_nsb nettest -s &
+ sleep 1
+ run_cmd nettest -r ${a} -0 ${NSA_IP}
+ log_test_addr ${a} $? 0 "Client"
+
+ log_start
+ run_cmd_nsb nettest -s &
+ sleep 1
+ run_cmd nettest -r ${a} -d ${NSA_DEV}
+ log_test_addr ${a} $? 0 "Client, device bind"
+
+ log_start
+ show_hint "Should fail 'Connection refused'"
+ run_cmd nettest -r ${a}
+ log_test_addr ${a} $? 1 "No server, unbound client"
+
+ log_start
+ show_hint "Should fail 'Connection refused'"
+ run_cmd nettest -r ${a} -d ${NSA_DEV}
+ log_test_addr ${a} $? 1 "No server, device client"
+ done
+
+ #
+ # local address tests
+ #
+ for a in ${NSA_IP} ${NSA_LO_IP} 127.0.0.1
+ do
+ log_start
+ run_cmd nettest -s &
+ sleep 1
+ run_cmd nettest -r ${a} -0 ${a} -1 ${a}
+ log_test_addr ${a} $? 0 "Global server, local connection"
+ done
+
+ a=${NSA_IP}
+ log_start
+ run_cmd nettest -s -d ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -r ${a} -0 ${a}
+ log_test_addr ${a} $? 0 "Device server, unbound client, local connection"
+
+ for a in ${NSA_LO_IP} 127.0.0.1
+ do
+ log_start
+ show_hint "Should fail 'Connection refused' since addresses on loopback are out of device scope"
+ run_cmd nettest -s -d ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -r ${a}
+ log_test_addr ${a} $? 1 "Device server, unbound client, local connection"
+ done
+
+ a=${NSA_IP}
+ log_start
+ run_cmd nettest -s &
+ sleep 1
+ run_cmd nettest -r ${a} -0 ${a} -d ${NSA_DEV}
+ log_test_addr ${a} $? 0 "Global server, device client, local connection"
+
+ for a in ${NSA_LO_IP} 127.0.0.1
+ do
+ log_start
+ show_hint "Should fail 'No route to host' since addresses on loopback are out of device scope"
+ run_cmd nettest -s &
+ sleep 1
+ run_cmd nettest -r ${a} -d ${NSA_DEV}
+ log_test_addr ${a} $? 1 "Global server, device client, local connection"
+ done
+
+ a=${NSA_IP}
+ log_start
+ run_cmd nettest -s -d ${NSA_DEV} -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -d ${NSA_DEV} -r ${a} -0 ${a}
+ log_test_addr ${a} $? 0 "Device server, device client, local connection"
+
+ log_start
+ show_hint "Should fail 'Connection refused'"
+ run_cmd nettest -d ${NSA_DEV} -r ${a}
+ log_test_addr ${a} $? 1 "No server, device client, local conn"
+
+ ipv4_tcp_md5_novrf
+}
+
+ipv4_tcp_vrf()
+{
+ local a
+
+ # disable global server
+ log_subsection "Global server disabled"
+
+ set_sysctl net.ipv4.tcp_l3mdev_accept=0
+
+ #
+ # server tests
+ #
+ for a in ${NSA_IP} ${VRF_IP}
+ do
+ log_start
+ show_hint "Should fail 'Connection refused' since global server with VRF is disabled"
+ run_cmd nettest -s &
+ sleep 1
+ run_cmd_nsb nettest -r ${a}
+ log_test_addr ${a} $? 1 "Global server"
+
+ log_start
+ run_cmd nettest -s -d ${VRF} -2 ${VRF} &
+ sleep 1
+ run_cmd_nsb nettest -r ${a}
+ log_test_addr ${a} $? 0 "VRF server"
+
+ log_start
+ run_cmd nettest -s -d ${NSA_DEV} -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd_nsb nettest -r ${a}
+ log_test_addr ${a} $? 0 "Device server"
+
+ # verify TCP reset received
+ log_start
+ show_hint "Should fail 'Connection refused' since there is no server"
+ run_cmd_nsb nettest -r ${a}
+ log_test_addr ${a} $? 1 "No server"
+ done
+
+ # local address tests
+ # (${VRF_IP} and 127.0.0.1 both timeout)
+ a=${NSA_IP}
+ log_start
+ show_hint "Should fail 'Connection refused' since global server with VRF is disabled"
+ run_cmd nettest -s &
+ sleep 1
+ run_cmd nettest -r ${a} -d ${NSA_DEV}
+ log_test_addr ${a} $? 1 "Global server, local connection"
+
+ # run MD5 tests
+ ipv4_tcp_md5
+
+ #
+ # enable VRF global server
+ #
+ log_subsection "VRF Global server enabled"
+ set_sysctl net.ipv4.tcp_l3mdev_accept=1
+
+ for a in ${NSA_IP} ${VRF_IP}
+ do
+ log_start
+ show_hint "client socket should be bound to VRF"
+ run_cmd nettest -s -2 ${VRF} &
+ sleep 1
+ run_cmd_nsb nettest -r ${a}
+ log_test_addr ${a} $? 0 "Global server"
+
+ log_start
+ show_hint "client socket should be bound to VRF"
+ run_cmd nettest -s -d ${VRF} -2 ${VRF} &
+ sleep 1
+ run_cmd_nsb nettest -r ${a}
+ log_test_addr ${a} $? 0 "VRF server"
+
+ # verify TCP reset received
+ log_start
+ show_hint "Should fail 'Connection refused'"
+ run_cmd_nsb nettest -r ${a}
+ log_test_addr ${a} $? 1 "No server"
+ done
+
+ a=${NSA_IP}
+ log_start
+ show_hint "client socket should be bound to device"
+ run_cmd nettest -s -d ${NSA_DEV} -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd_nsb nettest -r ${a}
+ log_test_addr ${a} $? 0 "Device server"
+
+ # local address tests
+ for a in ${NSA_IP} ${VRF_IP}
+ do
+ log_start
+ show_hint "Should fail 'Connection refused' since client is not bound to VRF"
+ run_cmd nettest -s -d ${VRF} &
+ sleep 1
+ run_cmd nettest -r ${a}
+ log_test_addr ${a} $? 1 "Global server, local connection"
+ done
+
+ #
+ # client
+ #
+ for a in ${NSB_IP} ${NSB_LO_IP}
+ do
+ log_start
+ run_cmd_nsb nettest -s &
+ sleep 1
+ run_cmd nettest -r ${a} -d ${VRF}
+ log_test_addr ${a} $? 0 "Client, VRF bind"
+
+ log_start
+ run_cmd_nsb nettest -s &
+ sleep 1
+ run_cmd nettest -r ${a} -d ${NSA_DEV}
+ log_test_addr ${a} $? 0 "Client, device bind"
+
+ log_start
+ show_hint "Should fail 'Connection refused'"
+ run_cmd nettest -r ${a} -d ${VRF}
+ log_test_addr ${a} $? 1 "No server, VRF client"
+
+ log_start
+ show_hint "Should fail 'Connection refused'"
+ run_cmd nettest -r ${a} -d ${NSA_DEV}
+ log_test_addr ${a} $? 1 "No server, device client"
+ done
+
+ for a in ${NSA_IP} ${VRF_IP} 127.0.0.1
+ do
+ log_start
+ run_cmd nettest -s -d ${VRF} -2 ${VRF} &
+ sleep 1
+ run_cmd nettest -r ${a} -d ${VRF} -0 ${a}
+ log_test_addr ${a} $? 0 "VRF server, VRF client, local connection"
+ done
+
+ a=${NSA_IP}
+ log_start
+ run_cmd nettest -s -d ${VRF} -2 ${VRF} &
+ sleep 1
+ run_cmd nettest -r ${a} -d ${NSA_DEV} -0 ${a}
+ log_test_addr ${a} $? 0 "VRF server, device client, local connection"
+
+ log_start
+ show_hint "Should fail 'No route to host' since client is out of VRF scope"
+ run_cmd nettest -s -d ${VRF} &
+ sleep 1
+ run_cmd nettest -r ${a}
+ log_test_addr ${a} $? 1 "VRF server, unbound client, local connection"
+
+ log_start
+ run_cmd nettest -s -d ${NSA_DEV} -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -r ${a} -d ${VRF} -0 ${a}
+ log_test_addr ${a} $? 0 "Device server, VRF client, local connection"
+
+ log_start
+ run_cmd nettest -s -d ${NSA_DEV} -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -r ${a} -d ${NSA_DEV} -0 ${a}
+ log_test_addr ${a} $? 0 "Device server, device client, local connection"
+}
+
+ipv4_tcp()
+{
+ log_section "IPv4/TCP"
+ log_subsection "No VRF"
+ setup
+
+ # tcp_l3mdev_accept should have no affect without VRF;
+ # run tests with it enabled and disabled to verify
+ log_subsection "tcp_l3mdev_accept disabled"
+ set_sysctl net.ipv4.tcp_l3mdev_accept=0
+ ipv4_tcp_novrf
+ log_subsection "tcp_l3mdev_accept enabled"
+ set_sysctl net.ipv4.tcp_l3mdev_accept=1
+ ipv4_tcp_novrf
+
+ log_subsection "With VRF"
+ setup "yes"
+ ipv4_tcp_vrf
+}
+
+################################################################################
+# IPv4 UDP
+
+ipv4_udp_novrf()
+{
+ local a
+
+ #
+ # server tests
+ #
+ for a in ${NSA_IP} ${NSA_LO_IP}
+ do
+ log_start
+ run_cmd nettest -D -s -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd_nsb nettest -D -r ${a}
+ log_test_addr ${a} $? 0 "Global server"
+
+ log_start
+ show_hint "Should fail 'Connection refused' since there is no server"
+ run_cmd_nsb nettest -D -r ${a}
+ log_test_addr ${a} $? 1 "No server"
+ done
+
+ a=${NSA_IP}
+ log_start
+ run_cmd nettest -D -d ${NSA_DEV} -s -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd_nsb nettest -D -r ${a}
+ log_test_addr ${a} $? 0 "Device server"
+
+ #
+ # client
+ #
+ for a in ${NSB_IP} ${NSB_LO_IP}
+ do
+ log_start
+ run_cmd_nsb nettest -D -s &
+ sleep 1
+ run_cmd nettest -D -r ${a} -0 ${NSA_IP}
+ log_test_addr ${a} $? 0 "Client"
+
+ log_start
+ run_cmd_nsb nettest -D -s &
+ sleep 1
+ run_cmd nettest -D -r ${a} -d ${NSA_DEV} -0 ${NSA_IP}
+ log_test_addr ${a} $? 0 "Client, device bind"
+
+ log_start
+ run_cmd_nsb nettest -D -s &
+ sleep 1
+ run_cmd nettest -D -r ${a} -d ${NSA_DEV} -C -0 ${NSA_IP}
+ log_test_addr ${a} $? 0 "Client, device send via cmsg"
+
+ log_start
+ run_cmd_nsb nettest -D -s &
+ sleep 1
+ run_cmd nettest -D -r ${a} -d ${NSA_DEV} -S -0 ${NSA_IP}
+ log_test_addr ${a} $? 0 "Client, device bind via IP_UNICAST_IF"
+
+ log_start
+ show_hint "Should fail 'Connection refused'"
+ run_cmd nettest -D -r ${a}
+ log_test_addr ${a} $? 1 "No server, unbound client"
+
+ log_start
+ show_hint "Should fail 'Connection refused'"
+ run_cmd nettest -D -r ${a} -d ${NSA_DEV}
+ log_test_addr ${a} $? 1 "No server, device client"
+ done
+
+ #
+ # local address tests
+ #
+ for a in ${NSA_IP} ${NSA_LO_IP} 127.0.0.1
+ do
+ log_start
+ run_cmd nettest -D -s &
+ sleep 1
+ run_cmd nettest -D -r ${a} -0 ${a} -1 ${a}
+ log_test_addr ${a} $? 0 "Global server, local connection"
+ done
+
+ a=${NSA_IP}
+ log_start
+ run_cmd nettest -s -D -d ${NSA_DEV} -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -D -r ${a}
+ log_test_addr ${a} $? 0 "Device server, unbound client, local connection"
+
+ for a in ${NSA_LO_IP} 127.0.0.1
+ do
+ log_start
+ show_hint "Should fail 'Connection refused' since address is out of device scope"
+ run_cmd nettest -s -D -d ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -D -r ${a}
+ log_test_addr ${a} $? 1 "Device server, unbound client, local connection"
+ done
+
+ a=${NSA_IP}
+ log_start
+ run_cmd nettest -s -D &
+ sleep 1
+ run_cmd nettest -D -d ${NSA_DEV} -r ${a}
+ log_test_addr ${a} $? 0 "Global server, device client, local connection"
+
+ log_start
+ run_cmd nettest -s -D &
+ sleep 1
+ run_cmd nettest -D -d ${NSA_DEV} -C -r ${a}
+ log_test_addr ${a} $? 0 "Global server, device send via cmsg, local connection"
+
+ log_start
+ run_cmd nettest -s -D &
+ sleep 1
+ run_cmd nettest -D -d ${NSA_DEV} -S -r ${a}
+ log_test_addr ${a} $? 0 "Global server, device client via IP_UNICAST_IF, local connection"
+
+ # IPv4 with device bind has really weird behavior - it overrides the
+ # fib lookup, generates an rtable and tries to send the packet. This
+ # causes failures for local traffic at different places
+ for a in ${NSA_LO_IP} 127.0.0.1
+ do
+ log_start
+ show_hint "Should fail since addresses on loopback are out of device scope"
+ run_cmd nettest -D -s &
+ sleep 1
+ run_cmd nettest -D -r ${a} -d ${NSA_DEV}
+ log_test_addr ${a} $? 2 "Global server, device client, local connection"
+
+ log_start
+ show_hint "Should fail since addresses on loopback are out of device scope"
+ run_cmd nettest -D -s &
+ sleep 1
+ run_cmd nettest -D -r ${a} -d ${NSA_DEV} -C
+ log_test_addr ${a} $? 1 "Global server, device send via cmsg, local connection"
+
+ log_start
+ show_hint "Should fail since addresses on loopback are out of device scope"
+ run_cmd nettest -D -s &
+ sleep 1
+ run_cmd nettest -D -r ${a} -d ${NSA_DEV} -S
+ log_test_addr ${a} $? 1 "Global server, device client via IP_UNICAST_IF, local connection"
+ done
+
+ a=${NSA_IP}
+ log_start
+ run_cmd nettest -D -s -d ${NSA_DEV} -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -D -d ${NSA_DEV} -r ${a} -0 ${a}
+ log_test_addr ${a} $? 0 "Device server, device client, local conn"
+
+ log_start
+ run_cmd nettest -D -d ${NSA_DEV} -r ${a}
+ log_test_addr ${a} $? 2 "No server, device client, local conn"
+}
+
+ipv4_udp_vrf()
+{
+ local a
+
+ # disable global server
+ log_subsection "Global server disabled"
+ set_sysctl net.ipv4.udp_l3mdev_accept=0
+
+ #
+ # server tests
+ #
+ for a in ${NSA_IP} ${VRF_IP}
+ do
+ log_start
+ show_hint "Fails because ingress is in a VRF and global server is disabled"
+ run_cmd nettest -D -s &
+ sleep 1
+ run_cmd_nsb nettest -D -r ${a}
+ log_test_addr ${a} $? 1 "Global server"
+
+ log_start
+ run_cmd nettest -D -d ${VRF} -s -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd_nsb nettest -D -r ${a}
+ log_test_addr ${a} $? 0 "VRF server"
+
+ log_start
+ run_cmd nettest -D -d ${NSA_DEV} -s -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd_nsb nettest -D -r ${a}
+ log_test_addr ${a} $? 0 "Enslaved device server"
+
+ log_start
+ show_hint "Should fail 'Connection refused' since there is no server"
+ run_cmd_nsb nettest -D -r ${a}
+ log_test_addr ${a} $? 1 "No server"
+
+ log_start
+ show_hint "Should fail 'Connection refused' since global server is out of scope"
+ run_cmd nettest -D -s &
+ sleep 1
+ run_cmd nettest -D -d ${VRF} -r ${a}
+ log_test_addr ${a} $? 1 "Global server, VRF client, local connection"
+ done
+
+ a=${NSA_IP}
+ log_start
+ run_cmd nettest -s -D -d ${VRF} -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -D -d ${VRF} -r ${a}
+ log_test_addr ${a} $? 0 "VRF server, VRF client, local conn"
+
+ log_start
+ run_cmd nettest -s -D -d ${VRF} -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -D -d ${NSA_DEV} -r ${a}
+ log_test_addr ${a} $? 0 "VRF server, enslaved device client, local connection"
+
+ a=${NSA_IP}
+ log_start
+ run_cmd nettest -s -D -d ${NSA_DEV} -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -D -d ${VRF} -r ${a}
+ log_test_addr ${a} $? 0 "Enslaved device server, VRF client, local conn"
+
+ log_start
+ run_cmd nettest -s -D -d ${NSA_DEV} -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -D -d ${NSA_DEV} -r ${a}
+ log_test_addr ${a} $? 0 "Enslaved device server, device client, local conn"
+
+ # enable global server
+ log_subsection "Global server enabled"
+ set_sysctl net.ipv4.udp_l3mdev_accept=1
+
+ #
+ # server tests
+ #
+ for a in ${NSA_IP} ${VRF_IP}
+ do
+ log_start
+ run_cmd nettest -D -s -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd_nsb nettest -D -r ${a}
+ log_test_addr ${a} $? 0 "Global server"
+
+ log_start
+ run_cmd nettest -D -d ${VRF} -s -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd_nsb nettest -D -r ${a}
+ log_test_addr ${a} $? 0 "VRF server"
+
+ log_start
+ run_cmd nettest -D -d ${NSA_DEV} -s -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd_nsb nettest -D -r ${a}
+ log_test_addr ${a} $? 0 "Enslaved device server"
+
+ log_start
+ show_hint "Should fail 'Connection refused'"
+ run_cmd_nsb nettest -D -r ${a}
+ log_test_addr ${a} $? 1 "No server"
+ done
+
+ #
+ # client tests
+ #
+ log_start
+ run_cmd_nsb nettest -D -s &
+ sleep 1
+ run_cmd nettest -d ${VRF} -D -r ${NSB_IP} -1 ${NSA_IP}
+ log_test $? 0 "VRF client"
+
+ log_start
+ run_cmd_nsb nettest -D -s &
+ sleep 1
+ run_cmd nettest -d ${NSA_DEV} -D -r ${NSB_IP} -1 ${NSA_IP}
+ log_test $? 0 "Enslaved device client"
+
+ # negative test - should fail
+ log_start
+ show_hint "Should fail 'Connection refused'"
+ run_cmd nettest -D -d ${VRF} -r ${NSB_IP}
+ log_test $? 1 "No server, VRF client"
+
+ log_start
+ show_hint "Should fail 'Connection refused'"
+ run_cmd nettest -D -d ${NSA_DEV} -r ${NSB_IP}
+ log_test $? 1 "No server, enslaved device client"
+
+ #
+ # local address tests
+ #
+ a=${NSA_IP}
+ log_start
+ run_cmd nettest -D -s -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -D -d ${VRF} -r ${a}
+ log_test_addr ${a} $? 0 "Global server, VRF client, local conn"
+
+ log_start
+ run_cmd nettest -s -D -d ${VRF} -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -D -d ${VRF} -r ${a}
+ log_test_addr ${a} $? 0 "VRF server, VRF client, local conn"
+
+ log_start
+ run_cmd nettest -s -D -d ${VRF} -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -D -d ${NSA_DEV} -r ${a}
+ log_test_addr ${a} $? 0 "VRF server, device client, local conn"
+
+ log_start
+ run_cmd nettest -s -D -d ${NSA_DEV} -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -D -d ${VRF} -r ${a}
+ log_test_addr ${a} $? 0 "Enslaved device server, VRF client, local conn"
+
+ log_start
+ run_cmd nettest -s -D -d ${NSA_DEV} -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -D -d ${NSA_DEV} -r ${a}
+ log_test_addr ${a} $? 0 "Enslaved device server, device client, local conn"
+
+ for a in ${VRF_IP} 127.0.0.1
+ do
+ log_start
+ run_cmd nettest -D -s -2 ${VRF} &
+ sleep 1
+ run_cmd nettest -D -d ${VRF} -r ${a}
+ log_test_addr ${a} $? 0 "Global server, VRF client, local conn"
+ done
+
+ for a in ${VRF_IP} 127.0.0.1
+ do
+ log_start
+ run_cmd nettest -s -D -d ${VRF} -2 ${VRF} &
+ sleep 1
+ run_cmd nettest -D -d ${VRF} -r ${a}
+ log_test_addr ${a} $? 0 "VRF server, VRF client, local conn"
+ done
+
+ # negative test - should fail
+ # verifies ECONNREFUSED
+ for a in ${NSA_IP} ${VRF_IP} 127.0.0.1
+ do
+ log_start
+ show_hint "Should fail 'Connection refused'"
+ run_cmd nettest -D -d ${VRF} -r ${a}
+ log_test_addr ${a} $? 1 "No server, VRF client, local conn"
+ done
+}
+
+ipv4_udp()
+{
+ log_section "IPv4/UDP"
+ log_subsection "No VRF"
+
+ setup
+
+ # udp_l3mdev_accept should have no affect without VRF;
+ # run tests with it enabled and disabled to verify
+ log_subsection "udp_l3mdev_accept disabled"
+ set_sysctl net.ipv4.udp_l3mdev_accept=0
+ ipv4_udp_novrf
+ log_subsection "udp_l3mdev_accept enabled"
+ set_sysctl net.ipv4.udp_l3mdev_accept=1
+ ipv4_udp_novrf
+
+ log_subsection "With VRF"
+ setup "yes"
+ ipv4_udp_vrf
+}
+
+################################################################################
+# IPv4 address bind
+#
+# verifies ability or inability to bind to an address / device
+
+ipv4_addr_bind_novrf()
+{
+ #
+ # raw socket
+ #
+ for a in ${NSA_IP} ${NSA_LO_IP}
+ do
+ log_start
+ run_cmd nettest -s -R -P icmp -l ${a} -b
+ log_test_addr ${a} $? 0 "Raw socket bind to local address"
+
+ log_start
+ run_cmd nettest -s -R -P icmp -l ${a} -d ${NSA_DEV} -b
+ log_test_addr ${a} $? 0 "Raw socket bind to local address after device bind"
+ done
+
+ #
+ # tcp sockets
+ #
+ a=${NSA_IP}
+ log_start
+ run_cmd nettest -l ${a} -r ${NSB_IP} -t1 -b
+ log_test_addr ${a} $? 0 "TCP socket bind to local address"
+
+ log_start
+ run_cmd nettest -l ${a} -r ${NSB_IP} -d ${NSA_DEV} -t1 -b
+ log_test_addr ${a} $? 0 "TCP socket bind to local address after device bind"
+
+ # Sadly, the kernel allows binding a socket to a device and then
+ # binding to an address not on the device. The only restriction
+ # is that the address is valid in the L3 domain. So this test
+ # passes when it really should not
+ #a=${NSA_LO_IP}
+ #log_start
+ #show_hint "Should fail with 'Cannot assign requested address'"
+ #run_cmd nettest -s -l ${a} -d ${NSA_DEV} -t1 -b
+ #log_test_addr ${a} $? 1 "TCP socket bind to out of scope local address"
+}
+
+ipv4_addr_bind_vrf()
+{
+ #
+ # raw socket
+ #
+ for a in ${NSA_IP} ${VRF_IP}
+ do
+ log_start
+ run_cmd nettest -s -R -P icmp -l ${a} -b
+ log_test_addr ${a} $? 0 "Raw socket bind to local address"
+
+ log_start
+ run_cmd nettest -s -R -P icmp -l ${a} -d ${NSA_DEV} -b
+ log_test_addr ${a} $? 0 "Raw socket bind to local address after device bind"
+ log_start
+ run_cmd nettest -s -R -P icmp -l ${a} -d ${VRF} -b
+ log_test_addr ${a} $? 0 "Raw socket bind to local address after VRF bind"
+ done
+
+ a=${NSA_LO_IP}
+ log_start
+ show_hint "Address on loopback is out of VRF scope"
+ run_cmd nettest -s -R -P icmp -l ${a} -d ${VRF} -b
+ log_test_addr ${a} $? 1 "Raw socket bind to out of scope address after VRF bind"
+
+ #
+ # tcp sockets
+ #
+ for a in ${NSA_IP} ${VRF_IP}
+ do
+ log_start
+ run_cmd nettest -s -l ${a} -d ${VRF} -t1 -b
+ log_test_addr ${a} $? 0 "TCP socket bind to local address"
+
+ log_start
+ run_cmd nettest -s -l ${a} -d ${NSA_DEV} -t1 -b
+ log_test_addr ${a} $? 0 "TCP socket bind to local address after device bind"
+ done
+
+ a=${NSA_LO_IP}
+ log_start
+ show_hint "Address on loopback out of scope for VRF"
+ run_cmd nettest -s -l ${a} -d ${VRF} -t1 -b
+ log_test_addr ${a} $? 1 "TCP socket bind to invalid local address for VRF"
+
+ log_start
+ show_hint "Address on loopback out of scope for device in VRF"
+ run_cmd nettest -s -l ${a} -d ${NSA_DEV} -t1 -b
+ log_test_addr ${a} $? 1 "TCP socket bind to invalid local address for device bind"
+}
+
+ipv4_addr_bind()
+{
+ log_section "IPv4 address binds"
+
+ log_subsection "No VRF"
+ setup
+ ipv4_addr_bind_novrf
+
+ log_subsection "With VRF"
+ setup "yes"
+ ipv4_addr_bind_vrf
+}
+
+################################################################################
+# IPv4 runtime tests
+
+ipv4_rt()
+{
+ local desc="$1"
+ local varg="$2"
+ local with_vrf="yes"
+ local a
+
+ #
+ # server tests
+ #
+ for a in ${NSA_IP} ${VRF_IP}
+ do
+ log_start
+ run_cmd nettest ${varg} -s &
+ sleep 1
+ run_cmd_nsb nettest ${varg} -r ${a} &
+ sleep 3
+ run_cmd ip link del ${VRF}
+ sleep 1
+ log_test_addr ${a} 0 0 "${desc}, global server"
+
+ setup ${with_vrf}
+ done
+
+ for a in ${NSA_IP} ${VRF_IP}
+ do
+ log_start
+ run_cmd nettest ${varg} -s -d ${VRF} &
+ sleep 1
+ run_cmd_nsb nettest ${varg} -r ${a} &
+ sleep 3
+ run_cmd ip link del ${VRF}
+ sleep 1
+ log_test_addr ${a} 0 0 "${desc}, VRF server"
+
+ setup ${with_vrf}
+ done
+
+ a=${NSA_IP}
+ log_start
+ run_cmd nettest ${varg} -s -d ${NSA_DEV} &
+ sleep 1
+ run_cmd_nsb nettest ${varg} -r ${a} &
+ sleep 3
+ run_cmd ip link del ${VRF}
+ sleep 1
+ log_test_addr ${a} 0 0 "${desc}, enslaved device server"
+
+ setup ${with_vrf}
+
+ #
+ # client test
+ #
+ log_start
+ run_cmd_nsb nettest ${varg} -s &
+ sleep 1
+ run_cmd nettest ${varg} -d ${VRF} -r ${NSB_IP} &
+ sleep 3
+ run_cmd ip link del ${VRF}
+ sleep 1
+ log_test_addr ${a} 0 0 "${desc}, VRF client"
+
+ setup ${with_vrf}
+
+ log_start
+ run_cmd_nsb nettest ${varg} -s &
+ sleep 1
+ run_cmd nettest ${varg} -d ${NSA_DEV} -r ${NSB_IP} &
+ sleep 3
+ run_cmd ip link del ${VRF}
+ sleep 1
+ log_test_addr ${a} 0 0 "${desc}, enslaved device client"
+
+ setup ${with_vrf}
+
+ #
+ # local address tests
+ #
+ for a in ${NSA_IP} ${VRF_IP}
+ do
+ log_start
+ run_cmd nettest ${varg} -s &
+ sleep 1
+ run_cmd nettest ${varg} -d ${VRF} -r ${a} &
+ sleep 3
+ run_cmd ip link del ${VRF}
+ sleep 1
+ log_test_addr ${a} 0 0 "${desc}, global server, VRF client, local"
+
+ setup ${with_vrf}
+ done
+
+ for a in ${NSA_IP} ${VRF_IP}
+ do
+ log_start
+ run_cmd nettest ${varg} -d ${VRF} -s &
+ sleep 1
+ run_cmd nettest ${varg} -d ${VRF} -r ${a} &
+ sleep 3
+ run_cmd ip link del ${VRF}
+ sleep 1
+ log_test_addr ${a} 0 0 "${desc}, VRF server and client, local"
+
+ setup ${with_vrf}
+ done
+
+ a=${NSA_IP}
+ log_start
+ run_cmd nettest ${varg} -s &
+ sleep 1
+ run_cmd nettest ${varg} -d ${NSA_DEV} -r ${a} &
+ sleep 3
+ run_cmd ip link del ${VRF}
+ sleep 1
+ log_test_addr ${a} 0 0 "${desc}, global server, enslaved device client, local"
+
+ setup ${with_vrf}
+
+ log_start
+ run_cmd nettest ${varg} -d ${VRF} -s &
+ sleep 1
+ run_cmd nettest ${varg} -d ${NSA_DEV} -r ${a} &
+ sleep 3
+ run_cmd ip link del ${VRF}
+ sleep 1
+ log_test_addr ${a} 0 0 "${desc}, VRF server, enslaved device client, local"
+
+ setup ${with_vrf}
+
+ log_start
+ run_cmd nettest ${varg} -d ${NSA_DEV} -s &
+ sleep 1
+ run_cmd nettest ${varg} -d ${NSA_DEV} -r ${a} &
+ sleep 3
+ run_cmd ip link del ${VRF}
+ sleep 1
+ log_test_addr ${a} 0 0 "${desc}, enslaved device server and client, local"
+}
+
+ipv4_ping_rt()
+{
+ local with_vrf="yes"
+ local a
+
+ for a in ${NSA_IP} ${VRF_IP}
+ do
+ log_start
+ run_cmd_nsb ping -f ${a} &
+ sleep 3
+ run_cmd ip link del ${VRF}
+ sleep 1
+ log_test_addr ${a} 0 0 "Device delete with active traffic - ping in"
+
+ setup ${with_vrf}
+ done
+
+ a=${NSB_IP}
+ log_start
+ run_cmd ping -f -I ${VRF} ${a} &
+ sleep 3
+ run_cmd ip link del ${VRF}
+ sleep 1
+ log_test_addr ${a} 0 0 "Device delete with active traffic - ping out"
+}
+
+ipv4_runtime()
+{
+ log_section "Run time tests - ipv4"
+
+ setup "yes"
+ ipv4_ping_rt
+
+ setup "yes"
+ ipv4_rt "TCP active socket" "-n -1"
+
+ setup "yes"
+ ipv4_rt "TCP passive socket" "-i"
+}
+
+################################################################################
+# IPv6
+
+ipv6_ping_novrf()
+{
+ local a
+
+ # should not have an impact, but make a known state
+ set_sysctl net.ipv4.raw_l3mdev_accept=0 2>/dev/null
+
+ #
+ # out
+ #
+ for a in ${NSB_IP6} ${NSB_LO_IP6} ${NSB_LINKIP6}%${NSA_DEV} ${MCAST}%${NSA_DEV}
+ do
+ log_start
+ run_cmd ${ping6} -c1 -w1 ${a}
+ log_test_addr ${a} $? 0 "ping out"
+ done
+
+ for a in ${NSB_IP6} ${NSB_LO_IP6}
+ do
+ log_start
+ run_cmd ${ping6} -c1 -w1 -I ${NSA_DEV} ${a}
+ log_test_addr ${a} $? 0 "ping out, device bind"
+
+ log_start
+ run_cmd ${ping6} -c1 -w1 -I ${NSA_LO_IP6} ${a}
+ log_test_addr ${a} $? 0 "ping out, loopback address bind"
+ done
+
+ #
+ # in
+ #
+ for a in ${NSA_IP6} ${NSA_LO_IP6} ${NSA_LINKIP6}%${NSB_DEV} ${MCAST}%${NSB_DEV}
+ do
+ log_start
+ run_cmd_nsb ${ping6} -c1 -w1 ${a}
+ log_test_addr ${a} $? 0 "ping in"
+ done
+
+ #
+ # local traffic, local address
+ #
+ for a in ${NSA_IP6} ${NSA_LO_IP6} ::1 ${NSA_LINKIP6}%${NSA_DEV} ${MCAST}%${NSA_DEV}
+ do
+ log_start
+ run_cmd ${ping6} -c1 -w1 ${a}
+ log_test_addr ${a} $? 0 "ping local, no bind"
+ done
+
+ for a in ${NSA_IP6} ${NSA_LINKIP6}%${NSA_DEV} ${MCAST}%${NSA_DEV}
+ do
+ log_start
+ run_cmd ${ping6} -c1 -w1 -I ${NSA_DEV} ${a}
+ log_test_addr ${a} $? 0 "ping local, device bind"
+ done
+
+ for a in ${NSA_LO_IP6} ::1
+ do
+ log_start
+ show_hint "Fails since address on loopback is out of device scope"
+ run_cmd ${ping6} -c1 -w1 -I ${NSA_DEV} ${a}
+ log_test_addr ${a} $? 2 "ping local, device bind"
+ done
+
+ #
+ # ip rule blocks address
+ #
+ log_start
+ setup_cmd ip -6 rule add pref 32765 from all lookup local
+ setup_cmd ip -6 rule del pref 0 from all lookup local
+ setup_cmd ip -6 rule add pref 50 to ${NSB_LO_IP6} prohibit
+ setup_cmd ip -6 rule add pref 51 from ${NSB_IP6} prohibit
+
+ a=${NSB_LO_IP6}
+ run_cmd ${ping6} -c1 -w1 ${a}
+ log_test_addr ${a} $? 2 "ping out, blocked by rule"
+
+ log_start
+ run_cmd ${ping6} -c1 -w1 -I ${NSA_DEV} ${a}
+ log_test_addr ${a} $? 2 "ping out, device bind, blocked by rule"
+
+ a=${NSA_LO_IP6}
+ log_start
+ show_hint "Response lost due to ip rule"
+ run_cmd_nsb ${ping6} -c1 -w1 ${a}
+ log_test_addr ${a} $? 1 "ping in, blocked by rule"
+
+ setup_cmd ip -6 rule add pref 0 from all lookup local
+ setup_cmd ip -6 rule del pref 32765 from all lookup local
+ setup_cmd ip -6 rule del pref 50 to ${NSB_LO_IP6} prohibit
+ setup_cmd ip -6 rule del pref 51 from ${NSB_IP6} prohibit
+
+ #
+ # route blocks reachability to remote address
+ #
+ log_start
+ setup_cmd ip -6 route del ${NSB_LO_IP6}
+ setup_cmd ip -6 route add unreachable ${NSB_LO_IP6} metric 10
+ setup_cmd ip -6 route add unreachable ${NSB_IP6} metric 10
+
+ a=${NSB_LO_IP6}
+ run_cmd ${ping6} -c1 -w1 ${a}
+ log_test_addr ${a} $? 2 "ping out, blocked by route"
+
+ log_start
+ run_cmd ${ping6} -c1 -w1 -I ${NSA_DEV} ${a}
+ log_test_addr ${a} $? 2 "ping out, device bind, blocked by route"
+
+ a=${NSA_LO_IP6}
+ log_start
+ show_hint "Response lost due to ip route"
+ run_cmd_nsb ${ping6} -c1 -w1 ${a}
+ log_test_addr ${a} $? 1 "ping in, blocked by route"
+
+
+ #
+ # remove 'remote' routes; fallback to default
+ #
+ log_start
+ setup_cmd ip -6 ro del unreachable ${NSB_LO_IP6}
+ setup_cmd ip -6 ro del unreachable ${NSB_IP6}
+
+ a=${NSB_LO_IP6}
+ run_cmd ${ping6} -c1 -w1 ${a}
+ log_test_addr ${a} $? 2 "ping out, unreachable route"
+
+ log_start
+ run_cmd ${ping6} -c1 -w1 -I ${NSA_DEV} ${a}
+ log_test_addr ${a} $? 2 "ping out, device bind, unreachable route"
+}
+
+ipv6_ping_vrf()
+{
+ local a
+
+ # should default on; does not exist on older kernels
+ set_sysctl net.ipv4.raw_l3mdev_accept=1 2>/dev/null
+
+ #
+ # out
+ #
+ for a in ${NSB_IP6} ${NSB_LO_IP6}
+ do
+ log_start
+ run_cmd ${ping6} -c1 -w1 -I ${VRF} ${a}
+ log_test_addr ${a} $? 0 "ping out, VRF bind"
+ done
+
+ for a in ${NSB_LINKIP6}%${VRF} ${MCAST}%${VRF}
+ do
+ log_start
+ show_hint "Fails since VRF device does not support linklocal or multicast"
+ run_cmd ${ping6} -c1 -w1 ${a}
+ log_test_addr ${a} $? 2 "ping out, VRF bind"
+ done
+
+ for a in ${NSB_IP6} ${NSB_LO_IP6} ${NSB_LINKIP6}%${NSA_DEV} ${MCAST}%${NSA_DEV}
+ do
+ log_start
+ run_cmd ${ping6} -c1 -w1 -I ${NSA_DEV} ${a}
+ log_test_addr ${a} $? 0 "ping out, device bind"
+ done
+
+ for a in ${NSB_IP6} ${NSB_LO_IP6} ${NSB_LINKIP6}%${NSA_DEV}
+ do
+ log_start
+ run_cmd ip vrf exec ${VRF} ${ping6} -c1 -w1 -I ${VRF_IP6} ${a}
+ log_test_addr ${a} $? 0 "ping out, vrf device+address bind"
+ done
+
+ #
+ # in
+ #
+ for a in ${NSA_IP6} ${VRF_IP6} ${NSA_LINKIP6}%${NSB_DEV} ${MCAST}%${NSB_DEV}
+ do
+ log_start
+ run_cmd_nsb ${ping6} -c1 -w1 ${a}
+ log_test_addr ${a} $? 0 "ping in"
+ done
+
+ a=${NSA_LO_IP6}
+ log_start
+ show_hint "Fails since loopback address is out of VRF scope"
+ run_cmd_nsb ${ping6} -c1 -w1 ${a}
+ log_test_addr ${a} $? 1 "ping in"
+
+ #
+ # local traffic, local address
+ #
+ for a in ${NSA_IP6} ${VRF_IP6} ::1
+ do
+ log_start
+ show_hint "Source address should be ${a}"
+ run_cmd ${ping6} -c1 -w1 -I ${VRF} ${a}
+ log_test_addr ${a} $? 0 "ping local, VRF bind"
+ done
+
+ for a in ${NSA_IP6} ${NSA_LINKIP6}%${NSA_DEV} ${MCAST}%${NSA_DEV}
+ do
+ log_start
+ run_cmd ${ping6} -c1 -w1 -I ${NSA_DEV} ${a}
+ log_test_addr ${a} $? 0 "ping local, device bind"
+ done
+
+ # LLA to GUA - remove ipv6 global addresses from ns-B
+ setup_cmd_nsb ip -6 addr del ${NSB_IP6}/64 dev ${NSB_DEV}
+ setup_cmd_nsb ip -6 addr del ${NSB_LO_IP6}/128 dev lo
+ setup_cmd_nsb ip -6 ro add ${NSA_IP6}/128 via ${NSA_LINKIP6} dev ${NSB_DEV}
+
+ for a in ${NSA_IP6} ${VRF_IP6}
+ do
+ log_start
+ run_cmd_nsb ${ping6} -c1 -w1 ${NSA_IP6}
+ log_test_addr ${a} $? 0 "ping in, LLA to GUA"
+ done
+
+ setup_cmd_nsb ip -6 ro del ${NSA_IP6}/128 via ${NSA_LINKIP6} dev ${NSB_DEV}
+ setup_cmd_nsb ip -6 addr add ${NSB_IP6}/64 dev ${NSB_DEV}
+ setup_cmd_nsb ip -6 addr add ${NSB_LO_IP6}/128 dev lo
+
+ #
+ # ip rule blocks address
+ #
+ log_start
+ setup_cmd ip -6 rule add pref 50 to ${NSB_LO_IP6} prohibit
+ setup_cmd ip -6 rule add pref 51 from ${NSB_IP6} prohibit
+
+ a=${NSB_LO_IP6}
+ run_cmd ${ping6} -c1 -w1 ${a}
+ log_test_addr ${a} $? 2 "ping out, blocked by rule"
+
+ log_start
+ run_cmd ${ping6} -c1 -w1 -I ${NSA_DEV} ${a}
+ log_test_addr ${a} $? 2 "ping out, device bind, blocked by rule"
+
+ a=${NSA_LO_IP6}
+ log_start
+ show_hint "Response lost due to ip rule"
+ run_cmd_nsb ${ping6} -c1 -w1 ${a}
+ log_test_addr ${a} $? 1 "ping in, blocked by rule"
+
+ log_start
+ setup_cmd ip -6 rule del pref 50 to ${NSB_LO_IP6} prohibit
+ setup_cmd ip -6 rule del pref 51 from ${NSB_IP6} prohibit
+
+ #
+ # remove 'remote' routes; fallback to default
+ #
+ log_start
+ setup_cmd ip -6 ro del ${NSB_LO_IP6} vrf ${VRF}
+
+ a=${NSB_LO_IP6}
+ run_cmd ${ping6} -c1 -w1 ${a}
+ log_test_addr ${a} $? 2 "ping out, unreachable route"
+
+ log_start
+ run_cmd ${ping6} -c1 -w1 -I ${NSA_DEV} ${a}
+ log_test_addr ${a} $? 2 "ping out, device bind, unreachable route"
+
+ ip -netns ${NSB} -6 ro del ${NSA_LO_IP6}
+ a=${NSA_LO_IP6}
+ log_start
+ run_cmd_nsb ${ping6} -c1 -w1 ${a}
+ log_test_addr ${a} $? 2 "ping in, unreachable route"
+}
+
+ipv6_ping()
+{
+ log_section "IPv6 ping"
+
+ log_subsection "No VRF"
+ setup
+ ipv6_ping_novrf
+
+ log_subsection "With VRF"
+ setup "yes"
+ ipv6_ping_vrf
+}
+
+################################################################################
+# IPv6 TCP
+
+#
+# MD5 tests without VRF
+#
+ipv6_tcp_md5_novrf()
+{
+ #
+ # single address
+ #
+
+ # basic use case
+ log_start
+ run_cmd nettest -6 -s -M ${MD5_PW} -r ${NSB_IP6} &
+ sleep 1
+ run_cmd_nsb nettest -6 -r ${NSA_IP6} -M ${MD5_PW}
+ log_test $? 0 "MD5: Single address config"
+
+ # client sends MD5, server not configured
+ log_start
+ show_hint "Should timeout due to MD5 mismatch"
+ run_cmd nettest -6 -s &
+ sleep 1
+ run_cmd_nsb nettest -6 -r ${NSA_IP6} -M ${MD5_PW}
+ log_test $? 2 "MD5: Server no config, client uses password"
+
+ # wrong password
+ log_start
+ show_hint "Should timeout since client uses wrong password"
+ run_cmd nettest -6 -s -M ${MD5_PW} -r ${NSB_IP6} &
+ sleep 1
+ run_cmd_nsb nettest -6 -r ${NSA_IP6} -M ${MD5_WRONG_PW}
+ log_test $? 2 "MD5: Client uses wrong password"
+
+ # client from different address
+ log_start
+ show_hint "Should timeout due to MD5 mismatch"
+ run_cmd nettest -6 -s -M ${MD5_PW} -r ${NSB_LO_IP6} &
+ sleep 1
+ run_cmd_nsb nettest -6 -r ${NSA_IP6} -M ${MD5_PW}
+ log_test $? 2 "MD5: Client address does not match address configured with password"
+
+ #
+ # MD5 extension - prefix length
+ #
+
+ # client in prefix
+ log_start
+ run_cmd nettest -6 -s -M ${MD5_PW} -m ${NS_NET6} &
+ sleep 1
+ run_cmd_nsb nettest -6 -r ${NSA_IP6} -M ${MD5_PW}
+ log_test $? 0 "MD5: Prefix config"
+
+ # client in prefix, wrong password
+ log_start
+ show_hint "Should timeout since client uses wrong password"
+ run_cmd nettest -6 -s -M ${MD5_PW} -m ${NS_NET6} &
+ sleep 1
+ run_cmd_nsb nettest -6 -r ${NSA_IP6} -M ${MD5_WRONG_PW}
+ log_test $? 2 "MD5: Prefix config, client uses wrong password"
+
+ # client outside of prefix
+ log_start
+ show_hint "Should timeout due to MD5 mismatch"
+ run_cmd nettest -6 -s -M ${MD5_PW} -m ${NS_NET6} &
+ sleep 1
+ run_cmd_nsb nettest -6 -l ${NSB_LO_IP6} -r ${NSA_IP6} -M ${MD5_PW}
+ log_test $? 2 "MD5: Prefix config, client address not in configured prefix"
+}
+
+#
+# MD5 tests with VRF
+#
+ipv6_tcp_md5()
+{
+ #
+ # single address
+ #
+
+ # basic use case
+ log_start
+ run_cmd nettest -6 -s -d ${VRF} -M ${MD5_PW} -r ${NSB_IP6} &
+ sleep 1
+ run_cmd_nsb nettest -6 -r ${NSA_IP6} -M ${MD5_PW}
+ log_test $? 0 "MD5: VRF: Single address config"
+
+ # client sends MD5, server not configured
+ log_start
+ show_hint "Should timeout since server does not have MD5 auth"
+ run_cmd nettest -6 -s -d ${VRF} &
+ sleep 1
+ run_cmd_nsb nettest -6 -r ${NSA_IP6} -M ${MD5_PW}
+ log_test $? 2 "MD5: VRF: Server no config, client uses password"
+
+ # wrong password
+ log_start
+ show_hint "Should timeout since client uses wrong password"
+ run_cmd nettest -6 -s -d ${VRF} -M ${MD5_PW} -r ${NSB_IP6} &
+ sleep 1
+ run_cmd_nsb nettest -6 -r ${NSA_IP6} -M ${MD5_WRONG_PW}
+ log_test $? 2 "MD5: VRF: Client uses wrong password"
+
+ # client from different address
+ log_start
+ show_hint "Should timeout since server config differs from client"
+ run_cmd nettest -6 -s -d ${VRF} -M ${MD5_PW} -r ${NSB_LO_IP6} &
+ sleep 1
+ run_cmd_nsb nettest -6 -r ${NSA_IP6} -M ${MD5_PW}
+ log_test $? 2 "MD5: VRF: Client address does not match address configured with password"
+
+ #
+ # MD5 extension - prefix length
+ #
+
+ # client in prefix
+ log_start
+ run_cmd nettest -6 -s -d ${VRF} -M ${MD5_PW} -m ${NS_NET6} &
+ sleep 1
+ run_cmd_nsb nettest -6 -r ${NSA_IP6} -M ${MD5_PW}
+ log_test $? 0 "MD5: VRF: Prefix config"
+
+ # client in prefix, wrong password
+ log_start
+ show_hint "Should timeout since client uses wrong password"
+ run_cmd nettest -6 -s -d ${VRF} -M ${MD5_PW} -m ${NS_NET6} &
+ sleep 1
+ run_cmd_nsb nettest -6 -r ${NSA_IP6} -M ${MD5_WRONG_PW}
+ log_test $? 2 "MD5: VRF: Prefix config, client uses wrong password"
+
+ # client outside of prefix
+ log_start
+ show_hint "Should timeout since client address is outside of prefix"
+ run_cmd nettest -6 -s -d ${VRF} -M ${MD5_PW} -m ${NS_NET6} &
+ sleep 1
+ run_cmd_nsb nettest -6 -l ${NSB_LO_IP6} -r ${NSA_IP6} -M ${MD5_PW}
+ log_test $? 2 "MD5: VRF: Prefix config, client address not in configured prefix"
+
+ #
+ # duplicate config between default VRF and a VRF
+ #
+
+ log_start
+ run_cmd nettest -6 -s -d ${VRF} -M ${MD5_PW} -r ${NSB_IP6} &
+ run_cmd nettest -6 -s -M ${MD5_WRONG_PW} -r ${NSB_IP6} &
+ sleep 1
+ run_cmd_nsb nettest -6 -r ${NSA_IP6} -M ${MD5_PW}
+ log_test $? 0 "MD5: VRF: Single address config in default VRF and VRF, conn in VRF"
+
+ log_start
+ run_cmd nettest -6 -s -d ${VRF} -M ${MD5_PW} -r ${NSB_IP6} &
+ run_cmd nettest -6 -s -M ${MD5_WRONG_PW} -r ${NSB_IP6} &
+ sleep 1
+ run_cmd_nsc nettest -6 -r ${NSA_IP6} -M ${MD5_WRONG_PW}
+ log_test $? 0 "MD5: VRF: Single address config in default VRF and VRF, conn in default VRF"
+
+ log_start
+ show_hint "Should timeout since client in default VRF uses VRF password"
+ run_cmd nettest -6 -s -d ${VRF} -M ${MD5_PW} -r ${NSB_IP6} &
+ run_cmd nettest -6 -s -M ${MD5_WRONG_PW} -r ${NSB_IP6} &
+ sleep 1
+ run_cmd_nsc nettest -6 -r ${NSA_IP6} -M ${MD5_PW}
+ log_test $? 2 "MD5: VRF: Single address config in default VRF and VRF, conn in default VRF with VRF pw"
+
+ log_start
+ show_hint "Should timeout since client in VRF uses default VRF password"
+ run_cmd nettest -6 -s -d ${VRF} -M ${MD5_PW} -r ${NSB_IP6} &
+ run_cmd nettest -6 -s -M ${MD5_WRONG_PW} -r ${NSB_IP6} &
+ sleep 1
+ run_cmd_nsb nettest -6 -r ${NSA_IP6} -M ${MD5_WRONG_PW}
+ log_test $? 2 "MD5: VRF: Single address config in default VRF and VRF, conn in VRF with default VRF pw"
+
+ log_start
+ run_cmd nettest -6 -s -d ${VRF} -M ${MD5_PW} -m ${NS_NET6} &
+ run_cmd nettest -6 -s -M ${MD5_WRONG_PW} -m ${NS_NET6} &
+ sleep 1
+ run_cmd_nsb nettest -6 -r ${NSA_IP6} -M ${MD5_PW}
+ log_test $? 0 "MD5: VRF: Prefix config in default VRF and VRF, conn in VRF"
+
+ log_start
+ run_cmd nettest -6 -s -d ${VRF} -M ${MD5_PW} -m ${NS_NET6} &
+ run_cmd nettest -6 -s -M ${MD5_WRONG_PW} -m ${NS_NET6} &
+ sleep 1
+ run_cmd_nsc nettest -6 -r ${NSA_IP6} -M ${MD5_WRONG_PW}
+ log_test $? 0 "MD5: VRF: Prefix config in default VRF and VRF, conn in default VRF"
+
+ log_start
+ show_hint "Should timeout since client in default VRF uses VRF password"
+ run_cmd nettest -6 -s -d ${VRF} -M ${MD5_PW} -m ${NS_NET6} &
+ run_cmd nettest -6 -s -M ${MD5_WRONG_PW} -m ${NS_NET6} &
+ sleep 1
+ run_cmd_nsc nettest -6 -r ${NSA_IP6} -M ${MD5_PW}
+ log_test $? 2 "MD5: VRF: Prefix config in default VRF and VRF, conn in default VRF with VRF pw"
+
+ log_start
+ show_hint "Should timeout since client in VRF uses default VRF password"
+ run_cmd nettest -6 -s -d ${VRF} -M ${MD5_PW} -m ${NS_NET6} &
+ run_cmd nettest -6 -s -M ${MD5_WRONG_PW} -m ${NS_NET6} &
+ sleep 1
+ run_cmd_nsb nettest -6 -r ${NSA_IP6} -M ${MD5_WRONG_PW}
+ log_test $? 2 "MD5: VRF: Prefix config in default VRF and VRF, conn in VRF with default VRF pw"
+
+ #
+ # negative tests
+ #
+ log_start
+ run_cmd nettest -6 -s -d ${NSA_DEV} -M ${MD5_PW} -r ${NSB_IP6}
+ log_test $? 1 "MD5: VRF: Device must be a VRF - single address"
+
+ log_start
+ run_cmd nettest -6 -s -d ${NSA_DEV} -M ${MD5_PW} -m ${NS_NET6}
+ log_test $? 1 "MD5: VRF: Device must be a VRF - prefix"
+
+}
+
+ipv6_tcp_novrf()
+{
+ local a
+
+ #
+ # server tests
+ #
+ for a in ${NSA_IP6} ${NSA_LO_IP6} ${NSA_LINKIP6}%${NSB_DEV}
+ do
+ log_start
+ run_cmd nettest -6 -s &
+ sleep 1
+ run_cmd_nsb nettest -6 -r ${a}
+ log_test_addr ${a} $? 0 "Global server"
+ done
+
+ # verify TCP reset received
+ for a in ${NSA_IP6} ${NSA_LO_IP6} ${NSA_LINKIP6}%${NSB_DEV}
+ do
+ log_start
+ show_hint "Should fail 'Connection refused'"
+ run_cmd_nsb nettest -6 -r ${a}
+ log_test_addr ${a} $? 1 "No server"
+ done
+
+ #
+ # client
+ #
+ for a in ${NSB_IP6} ${NSB_LO_IP6} ${NSB_LINKIP6}%${NSA_DEV}
+ do
+ log_start
+ run_cmd_nsb nettest -6 -s &
+ sleep 1
+ run_cmd nettest -6 -r ${a}
+ log_test_addr ${a} $? 0 "Client"
+ done
+
+ for a in ${NSB_IP6} ${NSB_LO_IP6} ${NSB_LINKIP6}%${NSA_DEV}
+ do
+ log_start
+ run_cmd_nsb nettest -6 -s &
+ sleep 1
+ run_cmd nettest -6 -r ${a} -d ${NSA_DEV}
+ log_test_addr ${a} $? 0 "Client, device bind"
+ done
+
+ for a in ${NSB_IP6} ${NSB_LO_IP6} ${NSB_LINKIP6}%${NSA_DEV}
+ do
+ log_start
+ show_hint "Should fail 'Connection refused'"
+ run_cmd nettest -6 -r ${a} -d ${NSA_DEV}
+ log_test_addr ${a} $? 1 "No server, device client"
+ done
+
+ #
+ # local address tests
+ #
+ for a in ${NSA_IP6} ${NSA_LO_IP6} ::1
+ do
+ log_start
+ run_cmd nettest -6 -s &
+ sleep 1
+ run_cmd nettest -6 -r ${a}
+ log_test_addr ${a} $? 0 "Global server, local connection"
+ done
+
+ a=${NSA_IP6}
+ log_start
+ run_cmd nettest -6 -s -d ${NSA_DEV} -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -6 -r ${a} -0 ${a}
+ log_test_addr ${a} $? 0 "Device server, unbound client, local connection"
+
+ for a in ${NSA_LO_IP6} ::1
+ do
+ log_start
+ show_hint "Should fail 'Connection refused' since addresses on loopback are out of device scope"
+ run_cmd nettest -6 -s -d ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -6 -r ${a}
+ log_test_addr ${a} $? 1 "Device server, unbound client, local connection"
+ done
+
+ a=${NSA_IP6}
+ log_start
+ run_cmd nettest -6 -s &
+ sleep 1
+ run_cmd nettest -6 -r ${a} -d ${NSA_DEV} -0 ${a}
+ log_test_addr ${a} $? 0 "Global server, device client, local connection"
+
+ for a in ${NSA_LO_IP6} ::1
+ do
+ log_start
+ show_hint "Should fail 'Connection refused' since addresses on loopback are out of device scope"
+ run_cmd nettest -6 -s &
+ sleep 1
+ run_cmd nettest -6 -r ${a} -d ${NSA_DEV}
+ log_test_addr ${a} $? 1 "Global server, device client, local connection"
+ done
+
+ for a in ${NSA_IP6} ${NSA_LINKIP6}
+ do
+ log_start
+ run_cmd nettest -6 -s -d ${NSA_DEV} -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -6 -d ${NSA_DEV} -r ${a}
+ log_test_addr ${a} $? 0 "Device server, device client, local conn"
+ done
+
+ for a in ${NSA_IP6} ${NSA_LINKIP6}
+ do
+ log_start
+ show_hint "Should fail 'Connection refused'"
+ run_cmd nettest -6 -d ${NSA_DEV} -r ${a}
+ log_test_addr ${a} $? 1 "No server, device client, local conn"
+ done
+
+ ipv6_tcp_md5_novrf
+}
+
+ipv6_tcp_vrf()
+{
+ local a
+
+ # disable global server
+ log_subsection "Global server disabled"
+
+ set_sysctl net.ipv4.tcp_l3mdev_accept=0
+
+ #
+ # server tests
+ #
+ for a in ${NSA_IP6} ${VRF_IP6} ${NSA_LINKIP6}%${NSB_DEV}
+ do
+ log_start
+ show_hint "Should fail 'Connection refused' since global server with VRF is disabled"
+ run_cmd nettest -6 -s &
+ sleep 1
+ run_cmd_nsb nettest -6 -r ${a}
+ log_test_addr ${a} $? 1 "Global server"
+ done
+
+ for a in ${NSA_IP6} ${VRF_IP6}
+ do
+ log_start
+ run_cmd nettest -6 -s -d ${VRF} -2 ${VRF} &
+ sleep 1
+ run_cmd_nsb nettest -6 -r ${a}
+ log_test_addr ${a} $? 0 "VRF server"
+ done
+
+ # link local is always bound to ingress device
+ a=${NSA_LINKIP6}%${NSB_DEV}
+ log_start
+ run_cmd nettest -6 -s -d ${VRF} -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd_nsb nettest -6 -r ${a}
+ log_test_addr ${a} $? 0 "VRF server"
+
+ for a in ${NSA_IP6} ${VRF_IP6} ${NSA_LINKIP6}%${NSB_DEV}
+ do
+ log_start
+ run_cmd nettest -6 -s -d ${NSA_DEV} -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd_nsb nettest -6 -r ${a}
+ log_test_addr ${a} $? 0 "Device server"
+ done
+
+ # verify TCP reset received
+ for a in ${NSA_IP6} ${VRF_IP6} ${NSA_LINKIP6}%${NSB_DEV}
+ do
+ log_start
+ show_hint "Should fail 'Connection refused'"
+ run_cmd_nsb nettest -6 -r ${a}
+ log_test_addr ${a} $? 1 "No server"
+ done
+
+ # local address tests
+ a=${NSA_IP6}
+ log_start
+ show_hint "Should fail 'Connection refused' since global server with VRF is disabled"
+ run_cmd nettest -6 -s &
+ sleep 1
+ run_cmd nettest -6 -r ${a} -d ${NSA_DEV}
+ log_test_addr ${a} $? 1 "Global server, local connection"
+
+ # run MD5 tests
+ ipv6_tcp_md5
+
+ #
+ # enable VRF global server
+ #
+ log_subsection "VRF Global server enabled"
+ set_sysctl net.ipv4.tcp_l3mdev_accept=1
+
+ for a in ${NSA_IP6} ${VRF_IP6}
+ do
+ log_start
+ run_cmd nettest -6 -s -2 ${VRF} &
+ sleep 1
+ run_cmd_nsb nettest -6 -r ${a}
+ log_test_addr ${a} $? 0 "Global server"
+ done
+
+ for a in ${NSA_IP6} ${VRF_IP6}
+ do
+ log_start
+ run_cmd nettest -6 -s -d ${VRF} -2 ${VRF} &
+ sleep 1
+ run_cmd_nsb nettest -6 -r ${a}
+ log_test_addr ${a} $? 0 "VRF server"
+ done
+
+ # For LLA, child socket is bound to device
+ a=${NSA_LINKIP6}%${NSB_DEV}
+ log_start
+ run_cmd nettest -6 -s -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd_nsb nettest -6 -r ${a}
+ log_test_addr ${a} $? 0 "Global server"
+
+ log_start
+ run_cmd nettest -6 -s -d ${VRF} -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd_nsb nettest -6 -r ${a}
+ log_test_addr ${a} $? 0 "VRF server"
+
+ for a in ${NSA_IP6} ${NSA_LINKIP6}%${NSB_DEV}
+ do
+ log_start
+ run_cmd nettest -6 -s -d ${NSA_DEV} -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd_nsb nettest -6 -r ${a}
+ log_test_addr ${a} $? 0 "Device server"
+ done
+
+ # verify TCP reset received
+ for a in ${NSA_IP6} ${VRF_IP6} ${NSA_LINKIP6}%${NSB_DEV}
+ do
+ log_start
+ show_hint "Should fail 'Connection refused'"
+ run_cmd_nsb nettest -6 -r ${a}
+ log_test_addr ${a} $? 1 "No server"
+ done
+
+ # local address tests
+ for a in ${NSA_IP6} ${VRF_IP6}
+ do
+ log_start
+ show_hint "Fails 'Connection refused' since client is not in VRF"
+ run_cmd nettest -6 -s -d ${VRF} &
+ sleep 1
+ run_cmd nettest -6 -r ${a}
+ log_test_addr ${a} $? 1 "Global server, local connection"
+ done
+
+
+ #
+ # client
+ #
+ for a in ${NSB_IP6} ${NSB_LO_IP6}
+ do
+ log_start
+ run_cmd_nsb nettest -6 -s &
+ sleep 1
+ run_cmd nettest -6 -r ${a} -d ${VRF}
+ log_test_addr ${a} $? 0 "Client, VRF bind"
+ done
+
+ a=${NSB_LINKIP6}
+ log_start
+ show_hint "Fails since VRF device does not allow linklocal addresses"
+ run_cmd_nsb nettest -6 -s &
+ sleep 1
+ run_cmd nettest -6 -r ${a} -d ${VRF}
+ log_test_addr ${a} $? 1 "Client, VRF bind"
+
+ for a in ${NSB_IP6} ${NSB_LO_IP6} ${NSB_LINKIP6}
+ do
+ log_start
+ run_cmd_nsb nettest -6 -s &
+ sleep 1
+ run_cmd nettest -6 -r ${a} -d ${NSA_DEV}
+ log_test_addr ${a} $? 0 "Client, device bind"
+ done
+
+ for a in ${NSB_IP6} ${NSB_LO_IP6}
+ do
+ log_start
+ show_hint "Should fail 'Connection refused'"
+ run_cmd nettest -6 -r ${a} -d ${VRF}
+ log_test_addr ${a} $? 1 "No server, VRF client"
+ done
+
+ for a in ${NSB_IP6} ${NSB_LO_IP6} ${NSB_LINKIP6}
+ do
+ log_start
+ show_hint "Should fail 'Connection refused'"
+ run_cmd nettest -6 -r ${a} -d ${NSA_DEV}
+ log_test_addr ${a} $? 1 "No server, device client"
+ done
+
+ for a in ${NSA_IP6} ${VRF_IP6} ::1
+ do
+ log_start
+ run_cmd nettest -6 -s -d ${VRF} -2 ${VRF} &
+ sleep 1
+ run_cmd nettest -6 -r ${a} -d ${VRF} -0 ${a}
+ log_test_addr ${a} $? 0 "VRF server, VRF client, local connection"
+ done
+
+ a=${NSA_IP6}
+ log_start
+ run_cmd nettest -6 -s -d ${VRF} -2 ${VRF} &
+ sleep 1
+ run_cmd nettest -6 -r ${a} -d ${NSA_DEV} -0 ${a}
+ log_test_addr ${a} $? 0 "VRF server, device client, local connection"
+
+ a=${NSA_IP6}
+ log_start
+ show_hint "Should fail since unbound client is out of VRF scope"
+ run_cmd nettest -6 -s -d ${VRF} &
+ sleep 1
+ run_cmd nettest -6 -r ${a}
+ log_test_addr ${a} $? 1 "VRF server, unbound client, local connection"
+
+ log_start
+ run_cmd nettest -6 -s -d ${NSA_DEV} -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -6 -r ${a} -d ${VRF} -0 ${a}
+ log_test_addr ${a} $? 0 "Device server, VRF client, local connection"
+
+ for a in ${NSA_IP6} ${NSA_LINKIP6}
+ do
+ log_start
+ run_cmd nettest -6 -s -d ${NSA_DEV} -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -6 -r ${a} -d ${NSA_DEV} -0 ${a}
+ log_test_addr ${a} $? 0 "Device server, device client, local connection"
+ done
+}
+
+ipv6_tcp()
+{
+ log_section "IPv6/TCP"
+ log_subsection "No VRF"
+ setup
+
+ # tcp_l3mdev_accept should have no affect without VRF;
+ # run tests with it enabled and disabled to verify
+ log_subsection "tcp_l3mdev_accept disabled"
+ set_sysctl net.ipv4.tcp_l3mdev_accept=0
+ ipv6_tcp_novrf
+ log_subsection "tcp_l3mdev_accept enabled"
+ set_sysctl net.ipv4.tcp_l3mdev_accept=1
+ ipv6_tcp_novrf
+
+ log_subsection "With VRF"
+ setup "yes"
+ ipv6_tcp_vrf
+}
+
+################################################################################
+# IPv6 UDP
+
+ipv6_udp_novrf()
+{
+ local a
+
+ #
+ # server tests
+ #
+ for a in ${NSA_IP6} ${NSA_LINKIP6}%${NSB_DEV}
+ do
+ log_start
+ run_cmd nettest -6 -D -s -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd_nsb nettest -6 -D -r ${a}
+ log_test_addr ${a} $? 0 "Global server"
+
+ log_start
+ run_cmd nettest -6 -D -d ${NSA_DEV} -s -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd_nsb nettest -6 -D -r ${a}
+ log_test_addr ${a} $? 0 "Device server"
+ done
+
+ a=${NSA_LO_IP6}
+ log_start
+ run_cmd nettest -6 -D -s -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd_nsb nettest -6 -D -r ${a}
+ log_test_addr ${a} $? 0 "Global server"
+
+ # should fail since loopback address is out of scope for a device
+ # bound server, but it does not - hence this is more documenting
+ # behavior.
+ #log_start
+ #show_hint "Should fail since loopback address is out of scope"
+ #run_cmd nettest -6 -D -d ${NSA_DEV} -s -2 ${NSA_DEV} &
+ #sleep 1
+ #run_cmd_nsb nettest -6 -D -r ${a}
+ #log_test_addr ${a} $? 1 "Device server"
+
+ # negative test - should fail
+ for a in ${NSA_IP6} ${NSA_LO_IP6} ${NSA_LINKIP6}%${NSB_DEV}
+ do
+ log_start
+ show_hint "Should fail 'Connection refused' since there is no server"
+ run_cmd_nsb nettest -6 -D -r ${a}
+ log_test_addr ${a} $? 1 "No server"
+ done
+
+ #
+ # client
+ #
+ for a in ${NSB_IP6} ${NSB_LO_IP6} ${NSB_LINKIP6}%${NSA_DEV}
+ do
+ log_start
+ run_cmd_nsb nettest -6 -D -s &
+ sleep 1
+ run_cmd nettest -6 -D -r ${a} -0 ${NSA_IP6}
+ log_test_addr ${a} $? 0 "Client"
+
+ log_start
+ run_cmd_nsb nettest -6 -D -s &
+ sleep 1
+ run_cmd nettest -6 -D -r ${a} -d ${NSA_DEV} -0 ${NSA_IP6}
+ log_test_addr ${a} $? 0 "Client, device bind"
+
+ log_start
+ run_cmd_nsb nettest -6 -D -s &
+ sleep 1
+ run_cmd nettest -6 -D -r ${a} -d ${NSA_DEV} -C -0 ${NSA_IP6}
+ log_test_addr ${a} $? 0 "Client, device send via cmsg"
+
+ log_start
+ run_cmd_nsb nettest -6 -D -s &
+ sleep 1
+ run_cmd nettest -6 -D -r ${a} -d ${NSA_DEV} -S -0 ${NSA_IP6}
+ log_test_addr ${a} $? 0 "Client, device bind via IPV6_UNICAST_IF"
+
+ log_start
+ show_hint "Should fail 'Connection refused'"
+ run_cmd nettest -6 -D -r ${a}
+ log_test_addr ${a} $? 1 "No server, unbound client"
+
+ log_start
+ show_hint "Should fail 'Connection refused'"
+ run_cmd nettest -6 -D -r ${a} -d ${NSA_DEV}
+ log_test_addr ${a} $? 1 "No server, device client"
+ done
+
+ #
+ # local address tests
+ #
+ for a in ${NSA_IP6} ${NSA_LO_IP6} ::1
+ do
+ log_start
+ run_cmd nettest -6 -D -s &
+ sleep 1
+ run_cmd nettest -6 -D -r ${a} -0 ${a} -1 ${a}
+ log_test_addr ${a} $? 0 "Global server, local connection"
+ done
+
+ a=${NSA_IP6}
+ log_start
+ run_cmd nettest -6 -s -D -d ${NSA_DEV} -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -6 -D -r ${a}
+ log_test_addr ${a} $? 0 "Device server, unbound client, local connection"
+
+ for a in ${NSA_LO_IP6} ::1
+ do
+ log_start
+ show_hint "Should fail 'Connection refused' since address is out of device scope"
+ run_cmd nettest -6 -s -D -d ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -6 -D -r ${a}
+ log_test_addr ${a} $? 1 "Device server, local connection"
+ done
+
+ a=${NSA_IP6}
+ log_start
+ run_cmd nettest -6 -s -D &
+ sleep 1
+ run_cmd nettest -6 -D -d ${NSA_DEV} -r ${a}
+ log_test_addr ${a} $? 0 "Global server, device client, local connection"
+
+ log_start
+ run_cmd nettest -6 -s -D &
+ sleep 1
+ run_cmd nettest -6 -D -d ${NSA_DEV} -C -r ${a}
+ log_test_addr ${a} $? 0 "Global server, device send via cmsg, local connection"
+
+ log_start
+ run_cmd nettest -6 -s -D &
+ sleep 1
+ run_cmd nettest -6 -D -d ${NSA_DEV} -S -r ${a}
+ log_test_addr ${a} $? 0 "Global server, device client via IPV6_UNICAST_IF, local connection"
+
+ for a in ${NSA_LO_IP6} ::1
+ do
+ log_start
+ show_hint "Should fail 'No route to host' since addresses on loopback are out of device scope"
+ run_cmd nettest -6 -D -s &
+ sleep 1
+ run_cmd nettest -6 -D -r ${a} -d ${NSA_DEV}
+ log_test_addr ${a} $? 1 "Global server, device client, local connection"
+
+ log_start
+ show_hint "Should fail 'No route to host' since addresses on loopback are out of device scope"
+ run_cmd nettest -6 -D -s &
+ sleep 1
+ run_cmd nettest -6 -D -r ${a} -d ${NSA_DEV} -C
+ log_test_addr ${a} $? 1 "Global server, device send via cmsg, local connection"
+
+ log_start
+ show_hint "Should fail 'No route to host' since addresses on loopback are out of device scope"
+ run_cmd nettest -6 -D -s &
+ sleep 1
+ run_cmd nettest -6 -D -r ${a} -d ${NSA_DEV} -S
+ log_test_addr ${a} $? 1 "Global server, device client via IP_UNICAST_IF, local connection"
+ done
+
+ a=${NSA_IP6}
+ log_start
+ run_cmd nettest -6 -D -s -d ${NSA_DEV} -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -6 -D -d ${NSA_DEV} -r ${a} -0 ${a}
+ log_test_addr ${a} $? 0 "Device server, device client, local conn"
+
+ log_start
+ show_hint "Should fail 'Connection refused'"
+ run_cmd nettest -6 -D -d ${NSA_DEV} -r ${a}
+ log_test_addr ${a} $? 1 "No server, device client, local conn"
+
+ # LLA to GUA
+ run_cmd_nsb ip -6 addr del ${NSB_IP6}/64 dev ${NSB_DEV}
+ run_cmd_nsb ip -6 ro add ${NSA_IP6}/128 dev ${NSB_DEV}
+ log_start
+ run_cmd nettest -6 -s -D &
+ sleep 1
+ run_cmd_nsb nettest -6 -D -r ${NSA_IP6}
+ log_test $? 0 "UDP in - LLA to GUA"
+
+ run_cmd_nsb ip -6 ro del ${NSA_IP6}/128 dev ${NSB_DEV}
+ run_cmd_nsb ip -6 addr add ${NSB_IP6}/64 dev ${NSB_DEV} nodad
+}
+
+ipv6_udp_vrf()
+{
+ local a
+
+ # disable global server
+ log_subsection "Global server disabled"
+ set_sysctl net.ipv4.udp_l3mdev_accept=0
+
+ #
+ # server tests
+ #
+ for a in ${NSA_IP6} ${VRF_IP6}
+ do
+ log_start
+ show_hint "Should fail 'Connection refused' since global server is disabled"
+ run_cmd nettest -6 -D -s &
+ sleep 1
+ run_cmd_nsb nettest -6 -D -r ${a}
+ log_test_addr ${a} $? 1 "Global server"
+ done
+
+ for a in ${NSA_IP6} ${VRF_IP6}
+ do
+ log_start
+ run_cmd nettest -6 -D -d ${VRF} -s -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd_nsb nettest -6 -D -r ${a}
+ log_test_addr ${a} $? 0 "VRF server"
+ done
+
+ for a in ${NSA_IP6} ${VRF_IP6}
+ do
+ log_start
+ run_cmd nettest -6 -D -d ${NSA_DEV} -s -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd_nsb nettest -6 -D -r ${a}
+ log_test_addr ${a} $? 0 "Enslaved device server"
+ done
+
+ # negative test - should fail
+ for a in ${NSA_IP6} ${VRF_IP6}
+ do
+ log_start
+ show_hint "Should fail 'Connection refused' since there is no server"
+ run_cmd_nsb nettest -6 -D -r ${a}
+ log_test_addr ${a} $? 1 "No server"
+ done
+
+ #
+ # local address tests
+ #
+ for a in ${NSA_IP6} ${VRF_IP6}
+ do
+ log_start
+ show_hint "Should fail 'Connection refused' since global server is disabled"
+ run_cmd nettest -6 -D -s &
+ sleep 1
+ run_cmd nettest -6 -D -d ${VRF} -r ${a}
+ log_test_addr ${a} $? 1 "Global server, VRF client, local conn"
+ done
+
+ for a in ${NSA_IP6} ${VRF_IP6}
+ do
+ log_start
+ run_cmd nettest -6 -D -d ${VRF} -s &
+ sleep 1
+ run_cmd nettest -6 -D -d ${VRF} -r ${a}
+ log_test_addr ${a} $? 0 "VRF server, VRF client, local conn"
+ done
+
+ a=${NSA_IP6}
+ log_start
+ show_hint "Should fail 'Connection refused' since global server is disabled"
+ run_cmd nettest -6 -D -s &
+ sleep 1
+ run_cmd nettest -6 -D -d ${NSA_DEV} -r ${a}
+ log_test_addr ${a} $? 1 "Global server, device client, local conn"
+
+ log_start
+ run_cmd nettest -6 -D -d ${VRF} -s -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -6 -D -d ${NSA_DEV} -r ${a}
+ log_test_addr ${a} $? 0 "VRF server, device client, local conn"
+
+ log_start
+ run_cmd nettest -6 -D -d ${NSA_DEV} -s -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -6 -D -d ${VRF} -r ${a}
+ log_test_addr ${a} $? 0 "Enslaved device server, VRF client, local conn"
+
+ log_start
+ run_cmd nettest -6 -D -d ${NSA_DEV} -s -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -6 -D -d ${NSA_DEV} -r ${a}
+ log_test_addr ${a} $? 0 "Enslaved device server, device client, local conn"
+
+ # disable global server
+ log_subsection "Global server enabled"
+ set_sysctl net.ipv4.udp_l3mdev_accept=1
+
+ #
+ # server tests
+ #
+ for a in ${NSA_IP6} ${VRF_IP6}
+ do
+ log_start
+ run_cmd nettest -6 -D -s -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd_nsb nettest -6 -D -r ${a}
+ log_test_addr ${a} $? 0 "Global server"
+ done
+
+ for a in ${NSA_IP6} ${VRF_IP6}
+ do
+ log_start
+ run_cmd nettest -6 -D -d ${VRF} -s -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd_nsb nettest -6 -D -r ${a}
+ log_test_addr ${a} $? 0 "VRF server"
+ done
+
+ for a in ${NSA_IP6} ${VRF_IP6}
+ do
+ log_start
+ run_cmd nettest -6 -D -d ${NSA_DEV} -s -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd_nsb nettest -6 -D -r ${a}
+ log_test_addr ${a} $? 0 "Enslaved device server"
+ done
+
+ # negative test - should fail
+ for a in ${NSA_IP6} ${VRF_IP6}
+ do
+ log_start
+ run_cmd_nsb nettest -6 -D -r ${a}
+ log_test_addr ${a} $? 1 "No server"
+ done
+
+ #
+ # client tests
+ #
+ log_start
+ run_cmd_nsb nettest -6 -D -s &
+ sleep 1
+ run_cmd nettest -6 -D -d ${VRF} -r ${NSB_IP6}
+ log_test $? 0 "VRF client"
+
+ # negative test - should fail
+ log_start
+ run_cmd nettest -6 -D -d ${VRF} -r ${NSB_IP6}
+ log_test $? 1 "No server, VRF client"
+
+ log_start
+ run_cmd_nsb nettest -6 -D -s &
+ sleep 1
+ run_cmd nettest -6 -D -d ${NSA_DEV} -r ${NSB_IP6}
+ log_test $? 0 "Enslaved device client"
+
+ # negative test - should fail
+ log_start
+ run_cmd nettest -6 -D -d ${NSA_DEV} -r ${NSB_IP6}
+ log_test $? 1 "No server, enslaved device client"
+
+ #
+ # local address tests
+ #
+ a=${NSA_IP6}
+ log_start
+ run_cmd nettest -6 -D -s -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -6 -D -d ${VRF} -r ${a}
+ log_test_addr ${a} $? 0 "Global server, VRF client, local conn"
+
+ #log_start
+ run_cmd nettest -6 -D -d ${VRF} -s -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -6 -D -d ${VRF} -r ${a}
+ log_test_addr ${a} $? 0 "VRF server, VRF client, local conn"
+
+
+ a=${VRF_IP6}
+ log_start
+ run_cmd nettest -6 -D -s -2 ${VRF} &
+ sleep 1
+ run_cmd nettest -6 -D -d ${VRF} -r ${a}
+ log_test_addr ${a} $? 0 "Global server, VRF client, local conn"
+
+ log_start
+ run_cmd nettest -6 -D -d ${VRF} -s -2 ${VRF} &
+ sleep 1
+ run_cmd nettest -6 -D -d ${VRF} -r ${a}
+ log_test_addr ${a} $? 0 "VRF server, VRF client, local conn"
+
+ # negative test - should fail
+ for a in ${NSA_IP6} ${VRF_IP6}
+ do
+ log_start
+ run_cmd nettest -6 -D -d ${VRF} -r ${a}
+ log_test_addr ${a} $? 1 "No server, VRF client, local conn"
+ done
+
+ # device to global IP
+ a=${NSA_IP6}
+ log_start
+ run_cmd nettest -6 -D -s -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -6 -D -d ${NSA_DEV} -r ${a}
+ log_test_addr ${a} $? 0 "Global server, device client, local conn"
+
+ log_start
+ run_cmd nettest -6 -D -d ${VRF} -s -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -6 -D -d ${NSA_DEV} -r ${a}
+ log_test_addr ${a} $? 0 "VRF server, device client, local conn"
+
+ log_start
+ run_cmd nettest -6 -D -d ${NSA_DEV} -s -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -6 -D -d ${VRF} -r ${a}
+ log_test_addr ${a} $? 0 "Device server, VRF client, local conn"
+
+ log_start
+ run_cmd nettest -6 -D -d ${NSA_DEV} -s -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -6 -D -d ${NSA_DEV} -r ${a}
+ log_test_addr ${a} $? 0 "Device server, device client, local conn"
+
+ log_start
+ run_cmd nettest -6 -D -d ${NSA_DEV} -r ${a}
+ log_test_addr ${a} $? 1 "No server, device client, local conn"
+
+
+ # link local addresses
+ log_start
+ run_cmd nettest -6 -D -s &
+ sleep 1
+ run_cmd_nsb nettest -6 -D -d ${NSB_DEV} -r ${NSA_LINKIP6}
+ log_test $? 0 "Global server, linklocal IP"
+
+ log_start
+ run_cmd_nsb nettest -6 -D -d ${NSB_DEV} -r ${NSA_LINKIP6}
+ log_test $? 1 "No server, linklocal IP"
+
+
+ log_start
+ run_cmd_nsb nettest -6 -D -s &
+ sleep 1
+ run_cmd nettest -6 -D -d ${NSA_DEV} -r ${NSB_LINKIP6}
+ log_test $? 0 "Enslaved device client, linklocal IP"
+
+ log_start
+ run_cmd nettest -6 -D -d ${NSA_DEV} -r ${NSB_LINKIP6}
+ log_test $? 1 "No server, device client, peer linklocal IP"
+
+
+ log_start
+ run_cmd nettest -6 -D -s &
+ sleep 1
+ run_cmd nettest -6 -D -d ${NSA_DEV} -r ${NSA_LINKIP6}
+ log_test $? 0 "Enslaved device client, local conn - linklocal IP"
+
+ log_start
+ run_cmd nettest -6 -D -d ${NSA_DEV} -r ${NSA_LINKIP6}
+ log_test $? 1 "No server, device client, local conn - linklocal IP"
+
+ # LLA to GUA
+ run_cmd_nsb ip -6 addr del ${NSB_IP6}/64 dev ${NSB_DEV}
+ run_cmd_nsb ip -6 ro add ${NSA_IP6}/128 dev ${NSB_DEV}
+ log_start
+ run_cmd nettest -6 -s -D &
+ sleep 1
+ run_cmd_nsb nettest -6 -D -r ${NSA_IP6}
+ log_test $? 0 "UDP in - LLA to GUA"
+
+ run_cmd_nsb ip -6 ro del ${NSA_IP6}/128 dev ${NSB_DEV}
+ run_cmd_nsb ip -6 addr add ${NSB_IP6}/64 dev ${NSB_DEV} nodad
+}
+
+ipv6_udp()
+{
+ # should not matter, but set to known state
+ set_sysctl net.ipv4.udp_early_demux=1
+
+ log_section "IPv6/UDP"
+ log_subsection "No VRF"
+ setup
+
+ # udp_l3mdev_accept should have no affect without VRF;
+ # run tests with it enabled and disabled to verify
+ log_subsection "udp_l3mdev_accept disabled"
+ set_sysctl net.ipv4.udp_l3mdev_accept=0
+ ipv6_udp_novrf
+ log_subsection "udp_l3mdev_accept enabled"
+ set_sysctl net.ipv4.udp_l3mdev_accept=1
+ ipv6_udp_novrf
+
+ log_subsection "With VRF"
+ setup "yes"
+ ipv6_udp_vrf
+}
+
+################################################################################
+# IPv6 address bind
+
+ipv6_addr_bind_novrf()
+{
+ #
+ # raw socket
+ #
+ for a in ${NSA_IP6} ${NSA_LO_IP6}
+ do
+ log_start
+ run_cmd nettest -6 -s -R -P ipv6-icmp -l ${a} -b
+ log_test_addr ${a} $? 0 "Raw socket bind to local address"
+
+ log_start
+ run_cmd nettest -6 -s -R -P ipv6-icmp -l ${a} -d ${NSA_DEV} -b
+ log_test_addr ${a} $? 0 "Raw socket bind to local address after device bind"
+ done
+
+ #
+ # tcp sockets
+ #
+ a=${NSA_IP6}
+ log_start
+ run_cmd nettest -6 -s -l ${a} -t1 -b
+ log_test_addr ${a} $? 0 "TCP socket bind to local address"
+
+ log_start
+ run_cmd nettest -6 -s -l ${a} -d ${NSA_DEV} -t1 -b
+ log_test_addr ${a} $? 0 "TCP socket bind to local address after device bind"
+
+ a=${NSA_LO_IP6}
+ log_start
+ show_hint "Should fail with 'Cannot assign requested address'"
+ run_cmd nettest -6 -s -l ${a} -d ${NSA_DEV} -t1 -b
+ log_test_addr ${a} $? 1 "TCP socket bind to out of scope local address"
+}
+
+ipv6_addr_bind_vrf()
+{
+ #
+ # raw socket
+ #
+ for a in ${NSA_IP6} ${VRF_IP6}
+ do
+ log_start
+ run_cmd nettest -6 -s -R -P ipv6-icmp -l ${a} -d ${VRF} -b
+ log_test_addr ${a} $? 0 "Raw socket bind to local address after vrf bind"
+
+ log_start
+ run_cmd nettest -6 -s -R -P ipv6-icmp -l ${a} -d ${NSA_DEV} -b
+ log_test_addr ${a} $? 0 "Raw socket bind to local address after device bind"
+ done
+
+ a=${NSA_LO_IP6}
+ log_start
+ show_hint "Address on loopback is out of VRF scope"
+ run_cmd nettest -6 -s -R -P ipv6-icmp -l ${a} -d ${VRF} -b
+ log_test_addr ${a} $? 1 "Raw socket bind to invalid local address after vrf bind"
+
+ #
+ # tcp sockets
+ #
+ # address on enslaved device is valid for the VRF or device in a VRF
+ for a in ${NSA_IP6} ${VRF_IP6}
+ do
+ log_start
+ run_cmd nettest -6 -s -l ${a} -d ${VRF} -t1 -b
+ log_test_addr ${a} $? 0 "TCP socket bind to local address with VRF bind"
+ done
+
+ a=${NSA_IP6}
+ log_start
+ run_cmd nettest -6 -s -l ${a} -d ${NSA_DEV} -t1 -b
+ log_test_addr ${a} $? 0 "TCP socket bind to local address with device bind"
+
+ a=${VRF_IP6}
+ log_start
+ run_cmd nettest -6 -s -l ${a} -d ${NSA_DEV} -t1 -b
+ log_test_addr ${a} $? 1 "TCP socket bind to VRF address with device bind"
+
+ a=${NSA_LO_IP6}
+ log_start
+ show_hint "Address on loopback out of scope for VRF"
+ run_cmd nettest -6 -s -l ${a} -d ${VRF} -t1 -b
+ log_test_addr ${a} $? 1 "TCP socket bind to invalid local address for VRF"
+
+ log_start
+ show_hint "Address on loopback out of scope for device in VRF"
+ run_cmd nettest -6 -s -l ${a} -d ${NSA_DEV} -t1 -b
+ log_test_addr ${a} $? 1 "TCP socket bind to invalid local address for device bind"
+
+}
+
+ipv6_addr_bind()
+{
+ log_section "IPv6 address binds"
+
+ log_subsection "No VRF"
+ setup
+ ipv6_addr_bind_novrf
+
+ log_subsection "With VRF"
+ setup "yes"
+ ipv6_addr_bind_vrf
+}
+
+################################################################################
+# IPv6 runtime tests
+
+ipv6_rt()
+{
+ local desc="$1"
+ local varg="-6 $2"
+ local with_vrf="yes"
+ local a
+
+ #
+ # server tests
+ #
+ for a in ${NSA_IP6} ${VRF_IP6}
+ do
+ log_start
+ run_cmd nettest ${varg} -s &
+ sleep 1
+ run_cmd_nsb nettest ${varg} -r ${a} &
+ sleep 3
+ run_cmd ip link del ${VRF}
+ sleep 1
+ log_test_addr ${a} 0 0 "${desc}, global server"
+
+ setup ${with_vrf}
+ done
+
+ for a in ${NSA_IP6} ${VRF_IP6}
+ do
+ log_start
+ run_cmd nettest ${varg} -d ${VRF} -s &
+ sleep 1
+ run_cmd_nsb nettest ${varg} -r ${a} &
+ sleep 3
+ run_cmd ip link del ${VRF}
+ sleep 1
+ log_test_addr ${a} 0 0 "${desc}, VRF server"
+
+ setup ${with_vrf}
+ done
+
+ for a in ${NSA_IP6} ${VRF_IP6}
+ do
+ log_start
+ run_cmd nettest ${varg} -d ${NSA_DEV} -s &
+ sleep 1
+ run_cmd_nsb nettest ${varg} -r ${a} &
+ sleep 3
+ run_cmd ip link del ${VRF}
+ sleep 1
+ log_test_addr ${a} 0 0 "${desc}, enslaved device server"
+
+ setup ${with_vrf}
+ done
+
+ #
+ # client test
+ #
+ log_start
+ run_cmd_nsb nettest ${varg} -s &
+ sleep 1
+ run_cmd nettest ${varg} -d ${VRF} -r ${NSB_IP6} &
+ sleep 3
+ run_cmd ip link del ${VRF}
+ sleep 1
+ log_test 0 0 "${desc}, VRF client"
+
+ setup ${with_vrf}
+
+ log_start
+ run_cmd_nsb nettest ${varg} -s &
+ sleep 1
+ run_cmd nettest ${varg} -d ${NSA_DEV} -r ${NSB_IP6} &
+ sleep 3
+ run_cmd ip link del ${VRF}
+ sleep 1
+ log_test 0 0 "${desc}, enslaved device client"
+
+ setup ${with_vrf}
+
+
+ #
+ # local address tests
+ #
+ for a in ${NSA_IP6} ${VRF_IP6}
+ do
+ log_start
+ run_cmd nettest ${varg} -s &
+ sleep 1
+ run_cmd nettest ${varg} -d ${VRF} -r ${a} &
+ sleep 3
+ run_cmd ip link del ${VRF}
+ sleep 1
+ log_test_addr ${a} 0 0 "${desc}, global server, VRF client"
+
+ setup ${with_vrf}
+ done
+
+ for a in ${NSA_IP6} ${VRF_IP6}
+ do
+ log_start
+ run_cmd nettest ${varg} -d ${VRF} -s &
+ sleep 1
+ run_cmd nettest ${varg} -d ${VRF} -r ${a} &
+ sleep 3
+ run_cmd ip link del ${VRF}
+ sleep 1
+ log_test_addr ${a} 0 0 "${desc}, VRF server and client"
+
+ setup ${with_vrf}
+ done
+
+ a=${NSA_IP6}
+ log_start
+ run_cmd nettest ${varg} -s &
+ sleep 1
+ run_cmd nettest ${varg} -d ${NSA_DEV} -r ${a} &
+ sleep 3
+ run_cmd ip link del ${VRF}
+ sleep 1
+ log_test_addr ${a} 0 0 "${desc}, global server, device client"
+
+ setup ${with_vrf}
+
+ log_start
+ run_cmd nettest ${varg} -d ${VRF} -s &
+ sleep 1
+ run_cmd nettest ${varg} -d ${NSA_DEV} -r ${a} &
+ sleep 3
+ run_cmd ip link del ${VRF}
+ sleep 1
+ log_test_addr ${a} 0 0 "${desc}, VRF server, device client"
+
+ setup ${with_vrf}
+
+ log_start
+ run_cmd nettest ${varg} -d ${NSA_DEV} -s &
+ sleep 1
+ run_cmd nettest ${varg} -d ${NSA_DEV} -r ${a} &
+ sleep 3
+ run_cmd ip link del ${VRF}
+ sleep 1
+ log_test_addr ${a} 0 0 "${desc}, device server, device client"
+}
+
+ipv6_ping_rt()
+{
+ local with_vrf="yes"
+ local a
+
+ a=${NSA_IP6}
+ log_start
+ run_cmd_nsb ${ping6} -f ${a} &
+ sleep 3
+ run_cmd ip link del ${VRF}
+ sleep 1
+ log_test_addr ${a} 0 0 "Device delete with active traffic - ping in"
+
+ setup ${with_vrf}
+
+ log_start
+ run_cmd ${ping6} -f ${NSB_IP6} -I ${VRF} &
+ sleep 1
+ run_cmd ip link del ${VRF}
+ sleep 1
+ log_test_addr ${a} 0 0 "Device delete with active traffic - ping out"
+}
+
+ipv6_runtime()
+{
+ log_section "Run time tests - ipv6"
+
+ setup "yes"
+ ipv6_ping_rt
+
+ setup "yes"
+ ipv6_rt "TCP active socket" "-n -1"
+
+ setup "yes"
+ ipv6_rt "TCP passive socket" "-i"
+
+ setup "yes"
+ ipv6_rt "UDP active socket" "-D -n -1"
+}
+
+################################################################################
+# netfilter blocking connections
+
+netfilter_tcp_reset()
+{
+ local a
+
+ for a in ${NSA_IP} ${VRF_IP}
+ do
+ log_start
+ run_cmd nettest -s &
+ sleep 1
+ run_cmd_nsb nettest -r ${a}
+ log_test_addr ${a} $? 1 "Global server, reject with TCP-reset on Rx"
+ done
+}
+
+netfilter_icmp()
+{
+ local stype="$1"
+ local arg
+ local a
+
+ [ "${stype}" = "UDP" ] && arg="-D"
+
+ for a in ${NSA_IP} ${VRF_IP}
+ do
+ log_start
+ run_cmd nettest ${arg} -s &
+ sleep 1
+ run_cmd_nsb nettest ${arg} -r ${a}
+ log_test_addr ${a} $? 1 "Global ${stype} server, Rx reject icmp-port-unreach"
+ done
+}
+
+ipv4_netfilter()
+{
+ log_section "IPv4 Netfilter"
+ log_subsection "TCP reset"
+
+ setup "yes"
+ run_cmd iptables -A INPUT -p tcp --dport 12345 -j REJECT --reject-with tcp-reset
+
+ netfilter_tcp_reset
+
+ log_start
+ log_subsection "ICMP unreachable"
+
+ log_start
+ run_cmd iptables -F
+ run_cmd iptables -A INPUT -p tcp --dport 12345 -j REJECT --reject-with icmp-port-unreachable
+ run_cmd iptables -A INPUT -p udp --dport 12345 -j REJECT --reject-with icmp-port-unreachable
+
+ netfilter_icmp "TCP"
+ netfilter_icmp "UDP"
+
+ log_start
+ iptables -F
+}
+
+netfilter_tcp6_reset()
+{
+ local a
+
+ for a in ${NSA_IP6} ${VRF_IP6}
+ do
+ log_start
+ run_cmd nettest -6 -s &
+ sleep 1
+ run_cmd_nsb nettest -6 -r ${a}
+ log_test_addr ${a} $? 1 "Global server, reject with TCP-reset on Rx"
+ done
+}
+
+netfilter_icmp6()
+{
+ local stype="$1"
+ local arg
+ local a
+
+ [ "${stype}" = "UDP" ] && arg="$arg -D"
+
+ for a in ${NSA_IP6} ${VRF_IP6}
+ do
+ log_start
+ run_cmd nettest -6 -s ${arg} &
+ sleep 1
+ run_cmd_nsb nettest -6 ${arg} -r ${a}
+ log_test_addr ${a} $? 1 "Global ${stype} server, Rx reject icmp-port-unreach"
+ done
+}
+
+ipv6_netfilter()
+{
+ log_section "IPv6 Netfilter"
+ log_subsection "TCP reset"
+
+ setup "yes"
+ run_cmd ip6tables -A INPUT -p tcp --dport 12345 -j REJECT --reject-with tcp-reset
+
+ netfilter_tcp6_reset
+
+ log_subsection "ICMP unreachable"
+
+ log_start
+ run_cmd ip6tables -F
+ run_cmd ip6tables -A INPUT -p tcp --dport 12345 -j REJECT --reject-with icmp6-port-unreachable
+ run_cmd ip6tables -A INPUT -p udp --dport 12345 -j REJECT --reject-with icmp6-port-unreachable
+
+ netfilter_icmp6 "TCP"
+ netfilter_icmp6 "UDP"
+
+ log_start
+ ip6tables -F
+}
+
+################################################################################
+# specific use cases
+
+# VRF only.
+# ns-A device enslaved to bridge. Verify traffic with and without
+# br_netfilter module loaded. Repeat with SVI on bridge.
+use_case_br()
+{
+ setup "yes"
+
+ setup_cmd ip link set ${NSA_DEV} down
+ setup_cmd ip addr del dev ${NSA_DEV} ${NSA_IP}/24
+ setup_cmd ip -6 addr del dev ${NSA_DEV} ${NSA_IP6}/64
+
+ setup_cmd ip link add br0 type bridge
+ setup_cmd ip addr add dev br0 ${NSA_IP}/24
+ setup_cmd ip -6 addr add dev br0 ${NSA_IP6}/64 nodad
+
+ setup_cmd ip li set ${NSA_DEV} master br0
+ setup_cmd ip li set ${NSA_DEV} up
+ setup_cmd ip li set br0 up
+ setup_cmd ip li set br0 vrf ${VRF}
+
+ rmmod br_netfilter 2>/dev/null
+ sleep 5 # DAD
+
+ run_cmd ip neigh flush all
+ run_cmd ping -c1 -w1 -I br0 ${NSB_IP}
+ log_test $? 0 "Bridge into VRF - IPv4 ping out"
+
+ run_cmd ip neigh flush all
+ run_cmd ${ping6} -c1 -w1 -I br0 ${NSB_IP6}
+ log_test $? 0 "Bridge into VRF - IPv6 ping out"
+
+ run_cmd ip neigh flush all
+ run_cmd_nsb ping -c1 -w1 ${NSA_IP}
+ log_test $? 0 "Bridge into VRF - IPv4 ping in"
+
+ run_cmd ip neigh flush all
+ run_cmd_nsb ${ping6} -c1 -w1 ${NSA_IP6}
+ log_test $? 0 "Bridge into VRF - IPv6 ping in"
+
+ modprobe br_netfilter
+ if [ $? -eq 0 ]; then
+ run_cmd ip neigh flush all
+ run_cmd ping -c1 -w1 -I br0 ${NSB_IP}
+ log_test $? 0 "Bridge into VRF with br_netfilter - IPv4 ping out"
+
+ run_cmd ip neigh flush all
+ run_cmd ${ping6} -c1 -w1 -I br0 ${NSB_IP6}
+ log_test $? 0 "Bridge into VRF with br_netfilter - IPv6 ping out"
+
+ run_cmd ip neigh flush all
+ run_cmd_nsb ping -c1 -w1 ${NSA_IP}
+ log_test $? 0 "Bridge into VRF with br_netfilter - IPv4 ping in"
+
+ run_cmd ip neigh flush all
+ run_cmd_nsb ${ping6} -c1 -w1 ${NSA_IP6}
+ log_test $? 0 "Bridge into VRF with br_netfilter - IPv6 ping in"
+ fi
+
+ setup_cmd ip li set br0 nomaster
+ setup_cmd ip li add br0.100 link br0 type vlan id 100
+ setup_cmd ip li set br0.100 vrf ${VRF} up
+ setup_cmd ip addr add dev br0.100 172.16.101.1/24
+ setup_cmd ip -6 addr add dev br0.100 2001:db8:101::1/64 nodad
+
+ setup_cmd_nsb ip li add vlan100 link ${NSB_DEV} type vlan id 100
+ setup_cmd_nsb ip addr add dev vlan100 172.16.101.2/24
+ setup_cmd_nsb ip -6 addr add dev vlan100 2001:db8:101::2/64 nodad
+ setup_cmd_nsb ip li set vlan100 up
+ sleep 1
+
+ rmmod br_netfilter 2>/dev/null
+
+ run_cmd ip neigh flush all
+ run_cmd ping -c1 -w1 -I br0.100 172.16.101.2
+ log_test $? 0 "Bridge vlan into VRF - IPv4 ping out"
+
+ run_cmd ip neigh flush all
+ run_cmd ${ping6} -c1 -w1 -I br0.100 2001:db8:101::2
+ log_test $? 0 "Bridge vlan into VRF - IPv6 ping out"
+
+ run_cmd ip neigh flush all
+ run_cmd_nsb ping -c1 -w1 172.16.101.1
+ log_test $? 0 "Bridge vlan into VRF - IPv4 ping in"
+
+ run_cmd ip neigh flush all
+ run_cmd_nsb ${ping6} -c1 -w1 2001:db8:101::1
+ log_test $? 0 "Bridge vlan into VRF - IPv6 ping in"
+
+ modprobe br_netfilter
+ if [ $? -eq 0 ]; then
+ run_cmd ip neigh flush all
+ run_cmd ping -c1 -w1 -I br0.100 172.16.101.2
+ log_test $? 0 "Bridge vlan into VRF with br_netfilter - IPv4 ping out"
+
+ run_cmd ip neigh flush all
+ run_cmd ${ping6} -c1 -w1 -I br0.100 2001:db8:101::2
+ log_test $? 0 "Bridge vlan into VRF with br_netfilter - IPv6 ping out"
+
+ run_cmd ip neigh flush all
+ run_cmd_nsb ping -c1 -w1 172.16.101.1
+ log_test $? 0 "Bridge vlan into VRF - IPv4 ping in"
+
+ run_cmd ip neigh flush all
+ run_cmd_nsb ${ping6} -c1 -w1 2001:db8:101::1
+ log_test $? 0 "Bridge vlan into VRF - IPv6 ping in"
+ fi
+
+ setup_cmd ip li del br0 2>/dev/null
+ setup_cmd_nsb ip li del vlan100 2>/dev/null
+}
+
+use_cases()
+{
+ log_section "Use cases"
+ use_case_br
+}
+
+################################################################################
+# 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="ipv4_ping ipv4_tcp ipv4_udp ipv4_addr_bind ipv4_runtime ipv4_netfilter"
+TESTS_IPV6="ipv6_ping ipv6_tcp ipv6_udp ipv6_addr_bind ipv6_runtime ipv6_netfilter"
+TESTS_OTHER="use_cases"
+
+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
+
+which nettest >/dev/null
+if [ $? -ne 0 ]; then
+ echo "'nettest' command not found; skipping tests"
+ exit 0
+fi
+
+declare -i nfail=0
+declare -i nsuccess=0
+
+for t in $TESTS
+do
+ case $t in
+ ipv4_ping|ping) ipv4_ping;;
+ ipv4_tcp|tcp) ipv4_tcp;;
+ ipv4_udp|udp) ipv4_udp;;
+ ipv4_bind|bind) ipv4_addr_bind;;
+ ipv4_runtime) ipv4_runtime;;
+ ipv4_netfilter) ipv4_netfilter;;
+
+ ipv6_ping|ping6) ipv6_ping;;
+ ipv6_tcp|tcp6) ipv6_tcp;;
+ ipv6_udp|udp6) ipv6_udp;;
+ ipv6_bind|bind6) ipv6_addr_bind;;
+ ipv6_runtime) ipv6_runtime;;
+ ipv6_netfilter) ipv6_netfilter;;
+
+ use_cases) use_cases;;
+
+ # 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}
diff --git a/tools/testing/selftests/net/fib_nexthop_multiprefix.sh b/tools/testing/selftests/net/fib_nexthop_multiprefix.sh
index e6828732843e..9dc35a16e415 100755
--- a/tools/testing/selftests/net/fib_nexthop_multiprefix.sh
+++ b/tools/testing/selftests/net/fib_nexthop_multiprefix.sh
@@ -15,6 +15,8 @@
PAUSE_ON_FAIL=no
VERBOSE=0
+which ping6 > /dev/null 2>&1 && ping6=$(which ping6) || ping6=$(which ping)
+
################################################################################
# helpers
@@ -200,7 +202,7 @@ validate_v6_exception()
local rc
if [ ${ping_sz} != "0" ]; then
- run_cmd ip netns exec h0 ping6 -s ${ping_sz} -c5 -w5 ${dst}
+ run_cmd ip netns exec h0 ${ping6} -s ${ping_sz} -c5 -w5 ${dst}
fi
if [ "$VERBOSE" = "1" ]; then
@@ -243,7 +245,7 @@ do
run_cmd taskset -c ${c} ip netns exec h0 ping -c1 -w1 172.16.10${i}.1
[ $? -ne 0 ] && printf "\nERROR: ping to h${i} failed\n" && ret=1
- run_cmd taskset -c ${c} ip netns exec h0 ping6 -c1 -w1 2001:db8:10${i}::1
+ run_cmd taskset -c ${c} ip netns exec h0 ${ping6} -c1 -w1 2001:db8:10${i}::1
[ $? -ne 0 ] && printf "\nERROR: ping6 to h${i} failed\n" && ret=1
[ $ret -ne 0 ] && break
diff --git a/tools/testing/selftests/net/fib_nexthops.sh b/tools/testing/selftests/net/fib_nexthops.sh
index c5c93d5fb3ad..796670ebc65b 100755
--- a/tools/testing/selftests/net/fib_nexthops.sh
+++ b/tools/testing/selftests/net/fib_nexthops.sh
@@ -212,6 +212,8 @@ check_output()
printf " ${out}\n"
printf " Expected:\n"
printf " ${expected}\n\n"
+ else
+ echo " WARNING: Unexpected route entry"
fi
fi
@@ -274,7 +276,7 @@ ipv6_fcnal()
run_cmd "$IP nexthop get id 52"
log_test $? 0 "Get nexthop by id"
- check_nexthop "id 52" "id 52 via 2001:db8:91::2 dev veth1"
+ check_nexthop "id 52" "id 52 via 2001:db8:91::2 dev veth1 scope link"
run_cmd "$IP nexthop del id 52"
log_test $? 0 "Delete nexthop by id"
@@ -479,12 +481,12 @@ ipv6_fcnal_runtime()
run_cmd "$IP -6 nexthop add id 85 dev veth1"
run_cmd "$IP ro replace 2001:db8:101::1/128 nhid 85"
log_test $? 0 "IPv6 route with device only nexthop"
- check_route6 "2001:db8:101::1" "2001:db8:101::1 nhid 85 dev veth1"
+ check_route6 "2001:db8:101::1" "2001:db8:101::1 nhid 85 dev veth1 metric 1024 pref medium"
run_cmd "$IP nexthop add id 123 group 81/85"
run_cmd "$IP ro replace 2001:db8:101::1/128 nhid 123"
log_test $? 0 "IPv6 multipath route with nexthop mix - dev only + gw"
- check_route6 "2001:db8:101::1" "2001:db8:101::1 nhid 85 nexthop via 2001:db8:91::2 dev veth1 nexthop dev veth1"
+ check_route6 "2001:db8:101::1" "2001:db8:101::1 nhid 123 metric 1024 nexthop via 2001:db8:91::2 dev veth1 weight 1 nexthop dev veth1 weight 1 pref medium"
#
# IPv6 route with v4 nexthop - not allowed
@@ -538,7 +540,7 @@ ipv4_fcnal()
run_cmd "$IP nexthop get id 12"
log_test $? 0 "Get nexthop by id"
- check_nexthop "id 12" "id 12 via 172.16.1.2 src 172.16.1.1 dev veth1 scope link"
+ check_nexthop "id 12" "id 12 via 172.16.1.2 dev veth1 scope link"
run_cmd "$IP nexthop del id 12"
log_test $? 0 "Delete nexthop by id"
@@ -685,7 +687,7 @@ ipv4_withv6_fcnal()
set +e
run_cmd "$IP ro add 172.16.101.1/32 nhid 11"
log_test $? 0 "IPv6 nexthop with IPv4 route"
- check_route "172.16.101.1" "172.16.101.1 nhid 11 via ${lladdr} dev veth1"
+ check_route "172.16.101.1" "172.16.101.1 nhid 11 via inet6 ${lladdr} dev veth1"
set -e
run_cmd "$IP nexthop add id 12 via 172.16.1.2 dev veth1"
@@ -694,11 +696,11 @@ ipv4_withv6_fcnal()
run_cmd "$IP ro replace 172.16.101.1/32 nhid 101"
log_test $? 0 "IPv6 nexthop with IPv4 route"
- check_route "172.16.101.1" "172.16.101.1 nhid 101 nexthop via ${lladdr} dev veth1 weight 1 nexthop via 172.16.1.2 dev veth1 weight 1"
+ check_route "172.16.101.1" "172.16.101.1 nhid 101 nexthop via inet6 ${lladdr} dev veth1 weight 1 nexthop via 172.16.1.2 dev veth1 weight 1"
run_cmd "$IP ro replace 172.16.101.1/32 via inet6 ${lladdr} dev veth1"
log_test $? 0 "IPv4 route with IPv6 gateway"
- check_route "172.16.101.1" "172.16.101.1 via ${lladdr} dev veth1"
+ check_route "172.16.101.1" "172.16.101.1 via inet6 ${lladdr} dev veth1"
run_cmd "$IP ro replace 172.16.101.1/32 via inet6 2001:db8:50::1 dev veth1"
log_test $? 2 "IPv4 route with invalid IPv6 gateway"
@@ -785,10 +787,10 @@ ipv4_fcnal_runtime()
log_test $? 0 "IPv4 route with device only nexthop"
check_route "172.16.101.1" "172.16.101.1 nhid 85 dev veth1"
- run_cmd "$IP nexthop add id 122 group 21/85"
- run_cmd "$IP ro replace 172.16.101.1/32 nhid 122"
+ run_cmd "$IP nexthop add id 123 group 21/85"
+ run_cmd "$IP ro replace 172.16.101.1/32 nhid 123"
log_test $? 0 "IPv4 multipath route with nexthop mix - dev only + gw"
- check_route "172.16.101.1" "172.16.101.1 nhid 85 nexthop via 172.16.1.2 dev veth1 nexthop dev veth1"
+ check_route "172.16.101.1" "172.16.101.1 nhid 123 nexthop via 172.16.1.2 dev veth1 weight 1 nexthop dev veth1 weight 1"
#
# IPv4 with IPv6
@@ -820,7 +822,7 @@ ipv4_fcnal_runtime()
run_cmd "$IP ro replace 172.16.101.1/32 nhid 101"
log_test $? 0 "IPv4 route with mixed v4-v6 multipath route"
- check_route "172.16.101.1" "172.16.101.1 nhid 101 nexthop via ${lladdr} dev veth1 weight 1 nexthop via 172.16.1.2 dev veth1 weight 1"
+ check_route "172.16.101.1" "172.16.101.1 nhid 101 nexthop via inet6 ${lladdr} dev veth1 weight 1 nexthop via 172.16.1.2 dev veth1 weight 1"
run_cmd "ip netns exec me ping -c1 -w1 172.16.101.1"
log_test $? 0 "IPv6 nexthop with IPv4 route"
@@ -938,6 +940,20 @@ basic()
run_cmd "$IP nexthop add id 104 group 1 dev veth1"
log_test $? 2 "Nexthop group and device"
+ # Tests to ensure that flushing works as expected.
+ run_cmd "$IP nexthop add id 105 blackhole proto 99"
+ run_cmd "$IP nexthop add id 106 blackhole proto 100"
+ run_cmd "$IP nexthop add id 107 blackhole proto 99"
+ run_cmd "$IP nexthop flush proto 99"
+ check_nexthop "id 105" ""
+ check_nexthop "id 106" "id 106 blackhole proto 100"
+ check_nexthop "id 107" ""
+ run_cmd "$IP nexthop flush proto 100"
+ check_nexthop "id 106" ""
+
+ run_cmd "$IP nexthop flush proto 100"
+ log_test $? 0 "Test proto flush"
+
run_cmd "$IP nexthop add id 104 group 1 blackhole"
log_test $? 2 "Nexthop group and blackhole"
diff --git a/tools/testing/selftests/net/fib_tests.sh b/tools/testing/selftests/net/fib_tests.sh
index 4465fc2dae14..6dd403103800 100755
--- a/tools/testing/selftests/net/fib_tests.sh
+++ b/tools/testing/selftests/net/fib_tests.sh
@@ -9,7 +9,7 @@ ret=0
ksft_skip=4
# all tests in this script. Can be overridden with -t option
-TESTS="unregister down carrier nexthop ipv6_rt ipv4_rt ipv6_addr_metric ipv4_addr_metric ipv6_route_metrics ipv4_route_metrics ipv4_route_v6_gw rp_filter"
+TESTS="unregister down carrier nexthop suppress ipv6_rt ipv4_rt ipv6_addr_metric ipv4_addr_metric ipv6_route_metrics ipv4_route_metrics ipv4_route_v6_gw rp_filter ipv4_del_addr"
VERBOSE=0
PAUSE_ON_FAIL=no
@@ -17,6 +17,8 @@ PAUSE=no
IP="ip -netns ns1"
NS_EXEC="ip netns exec ns1"
+which ping6 > /dev/null 2>&1 && ping6=$(which ping6) || ping6=$(which ping)
+
log_test()
{
local rc=$1
@@ -614,6 +616,20 @@ fib_nexthop_test()
cleanup
}
+fib_suppress_test()
+{
+ $IP link add dummy1 type dummy
+ $IP link set dummy1 up
+ $IP -6 route add default dev dummy1
+ $IP -6 rule add table main suppress_prefixlength 0
+ ping -f -c 1000 -W 1 1234::1 || true
+ $IP -6 rule del table main suppress_prefixlength 0
+ $IP link del dummy1
+
+ # If we got here without crashing, we're good.
+ return 0
+}
+
################################################################################
# Tests on route add and replace
@@ -1086,7 +1102,7 @@ ipv6_route_metrics_test()
log_test $rc 0 "Multipath route with mtu metric"
$IP -6 ro add 2001:db8:104::/64 via 2001:db8:101::2 mtu 1300
- run_cmd "ip netns exec ns1 ping6 -w1 -c1 -s 1500 2001:db8:104::1"
+ run_cmd "ip netns exec ns1 ${ping6} -w1 -c1 -s 1500 2001:db8:104::1"
log_test $? 0 "Using route with mtu metric"
run_cmd "$IP -6 ro add 2001:db8:114::/64 via 2001:db8:101::2 congctl lock foo"
@@ -1422,6 +1438,27 @@ ipv4_addr_metric_test()
fi
log_test $rc 0 "Prefix route with metric on link up"
+ # explicitly check for metric changes on edge scenarios
+ run_cmd "$IP addr flush dev dummy2"
+ run_cmd "$IP addr add dev dummy2 172.16.104.0/24 metric 259"
+ run_cmd "$IP addr change dev dummy2 172.16.104.0/24 metric 260"
+ rc=$?
+ if [ $rc -eq 0 ]; then
+ check_route "172.16.104.0/24 dev dummy2 proto kernel scope link src 172.16.104.0 metric 260"
+ rc=$?
+ fi
+ log_test $rc 0 "Modify metric of .0/24 address"
+
+ run_cmd "$IP addr flush dev dummy2"
+ run_cmd "$IP addr add dev dummy2 172.16.104.1/32 peer 172.16.104.2 metric 260"
+ run_cmd "$IP addr change dev dummy2 172.16.104.1/32 peer 172.16.104.2 metric 261"
+ rc=$?
+ if [ $rc -eq 0 ]; then
+ check_route "172.16.104.2 dev dummy2 proto kernel scope link src 172.16.104.1 metric 261"
+ rc=$?
+ fi
+ log_test $rc 0 "Modify metric of address with peer route"
+
$IP li del dummy1
$IP li del dummy2
cleanup
@@ -1463,6 +1500,55 @@ ipv4_route_metrics_test()
route_cleanup
}
+ipv4_del_addr_test()
+{
+ echo
+ echo "IPv4 delete address route tests"
+
+ setup
+
+ set -e
+ $IP li add dummy1 type dummy
+ $IP li set dummy1 up
+ $IP li add dummy2 type dummy
+ $IP li set dummy2 up
+ $IP li add red type vrf table 1111
+ $IP li set red up
+ $IP ro add vrf red unreachable default
+ $IP li set dummy2 vrf red
+
+ $IP addr add dev dummy1 172.16.104.1/24
+ $IP addr add dev dummy1 172.16.104.11/24
+ $IP addr add dev dummy2 172.16.104.1/24
+ $IP addr add dev dummy2 172.16.104.11/24
+ $IP route add 172.16.105.0/24 via 172.16.104.2 src 172.16.104.11
+ $IP route add vrf red 172.16.105.0/24 via 172.16.104.2 src 172.16.104.11
+ set +e
+
+ # removing address from device in vrf should only remove route from vrf table
+ $IP addr del dev dummy2 172.16.104.11/24
+ $IP ro ls vrf red | grep -q 172.16.105.0/24
+ log_test $? 1 "Route removed from VRF when source address deleted"
+
+ $IP ro ls | grep -q 172.16.105.0/24
+ log_test $? 0 "Route in default VRF not removed"
+
+ $IP addr add dev dummy2 172.16.104.11/24
+ $IP route add vrf red 172.16.105.0/24 via 172.16.104.2 src 172.16.104.11
+
+ $IP addr del dev dummy1 172.16.104.11/24
+ $IP ro ls | grep -q 172.16.105.0/24
+ log_test $? 1 "Route removed in default VRF when source address deleted"
+
+ $IP ro ls vrf red | grep -q 172.16.105.0/24
+ log_test $? 0 "Route in VRF is not removed by address delete"
+
+ $IP li del dummy1
+ $IP li del dummy2
+ cleanup
+}
+
+
ipv4_route_v6_gw_test()
{
local rc
@@ -1591,10 +1677,12 @@ do
fib_carrier_test|carrier) fib_carrier_test;;
fib_rp_filter_test|rp_filter) fib_rp_filter_test;;
fib_nexthop_test|nexthop) fib_nexthop_test;;
+ fib_suppress_test|suppress) fib_suppress_test;;
ipv6_route_test|ipv6_rt) ipv6_route_test;;
ipv4_route_test|ipv4_rt) ipv4_route_test;;
ipv6_addr_metric) ipv6_addr_metric_test;;
ipv4_addr_metric) ipv4_addr_metric_test;;
+ ipv4_del_addr) ipv4_del_addr_test;;
ipv6_route_metrics) ipv6_route_metrics_test;;
ipv4_route_metrics) ipv4_route_metrics_test;;
ipv4_route_v6_gw) ipv4_route_v6_gw_test;;
diff --git a/tools/testing/selftests/net/fin_ack_lat.c b/tools/testing/selftests/net/fin_ack_lat.c
new file mode 100644
index 000000000000..70187494b57a
--- /dev/null
+++ b/tools/testing/selftests/net/fin_ack_lat.c
@@ -0,0 +1,151 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <arpa/inet.h>
+#include <errno.h>
+#include <error.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <unistd.h>
+
+static int child_pid;
+
+static unsigned long timediff(struct timeval s, struct timeval e)
+{
+ unsigned long s_us, e_us;
+
+ s_us = s.tv_sec * 1000000 + s.tv_usec;
+ e_us = e.tv_sec * 1000000 + e.tv_usec;
+ if (s_us > e_us)
+ return 0;
+ return e_us - s_us;
+}
+
+static void client(int port)
+{
+ int sock = 0;
+ struct sockaddr_in addr, laddr;
+ socklen_t len = sizeof(laddr);
+ struct linger sl;
+ int flag = 1;
+ int buffer;
+ struct timeval start, end;
+ unsigned long lat, sum_lat = 0, nr_lat = 0;
+
+ while (1) {
+ gettimeofday(&start, NULL);
+
+ sock = socket(AF_INET, SOCK_STREAM, 0);
+ if (sock < 0)
+ error(-1, errno, "socket creation");
+
+ sl.l_onoff = 1;
+ sl.l_linger = 0;
+ if (setsockopt(sock, SOL_SOCKET, SO_LINGER, &sl, sizeof(sl)))
+ error(-1, errno, "setsockopt(linger)");
+
+ if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
+ &flag, sizeof(flag)))
+ error(-1, errno, "setsockopt(nodelay)");
+
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+
+ if (inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr) <= 0)
+ error(-1, errno, "inet_pton");
+
+ if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0)
+ error(-1, errno, "connect");
+
+ send(sock, &buffer, sizeof(buffer), 0);
+ if (read(sock, &buffer, sizeof(buffer)) == -1)
+ error(-1, errno, "waiting read");
+
+ gettimeofday(&end, NULL);
+ lat = timediff(start, end);
+ sum_lat += lat;
+ nr_lat++;
+ if (lat < 100000)
+ goto close;
+
+ if (getsockname(sock, (struct sockaddr *)&laddr, &len) == -1)
+ error(-1, errno, "getsockname");
+ printf("port: %d, lat: %lu, avg: %lu, nr: %lu\n",
+ ntohs(laddr.sin_port), lat,
+ sum_lat / nr_lat, nr_lat);
+close:
+ fflush(stdout);
+ close(sock);
+ }
+}
+
+static void server(int sock, struct sockaddr_in address)
+{
+ int accepted;
+ int addrlen = sizeof(address);
+ int buffer;
+
+ while (1) {
+ accepted = accept(sock, (struct sockaddr *)&address,
+ (socklen_t *)&addrlen);
+ if (accepted < 0)
+ error(-1, errno, "accept");
+
+ if (read(accepted, &buffer, sizeof(buffer)) == -1)
+ error(-1, errno, "read");
+ close(accepted);
+ }
+}
+
+static void sig_handler(int signum)
+{
+ kill(SIGTERM, child_pid);
+ exit(0);
+}
+
+int main(int argc, char const *argv[])
+{
+ int sock;
+ int opt = 1;
+ struct sockaddr_in address;
+ struct sockaddr_in laddr;
+ socklen_t len = sizeof(laddr);
+
+ if (signal(SIGTERM, sig_handler) == SIG_ERR)
+ error(-1, errno, "signal");
+
+ sock = socket(AF_INET, SOCK_STREAM, 0);
+ if (sock < 0)
+ error(-1, errno, "socket");
+
+ if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT,
+ &opt, sizeof(opt)) == -1)
+ error(-1, errno, "setsockopt");
+
+ address.sin_family = AF_INET;
+ address.sin_addr.s_addr = INADDR_ANY;
+ /* dynamically allocate unused port */
+ address.sin_port = 0;
+
+ if (bind(sock, (struct sockaddr *)&address, sizeof(address)) < 0)
+ error(-1, errno, "bind");
+
+ if (listen(sock, 3) < 0)
+ error(-1, errno, "listen");
+
+ if (getsockname(sock, (struct sockaddr *)&laddr, &len) == -1)
+ error(-1, errno, "getsockname");
+
+ fprintf(stderr, "server port: %d\n", ntohs(laddr.sin_port));
+ child_pid = fork();
+ if (!child_pid)
+ client(ntohs(laddr.sin_port));
+ else
+ server(sock, laddr);
+
+ return 0;
+}
diff --git a/tools/testing/selftests/net/fin_ack_lat.sh b/tools/testing/selftests/net/fin_ack_lat.sh
new file mode 100755
index 000000000000..a3ff6e0b2c7a
--- /dev/null
+++ b/tools/testing/selftests/net/fin_ack_lat.sh
@@ -0,0 +1,35 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Test latency spikes caused by FIN/ACK handling race.
+
+set +x
+set -e
+
+tmpfile=$(mktemp /tmp/fin_ack_latency.XXXX.log)
+
+cleanup() {
+ kill $(pidof fin_ack_lat)
+ rm -f $tmpfile
+}
+
+trap cleanup EXIT
+
+do_test() {
+ RUNTIME=$1
+
+ ./fin_ack_lat | tee $tmpfile &
+ PID=$!
+
+ sleep $RUNTIME
+ NR_SPIKES=$(wc -l $tmpfile | awk '{print $1}')
+ if [ $NR_SPIKES -gt 0 ]
+ then
+ echo "FAIL: $NR_SPIKES spikes detected"
+ return 1
+ fi
+ return 0
+}
+
+do_test "30"
+echo "test done"
diff --git a/tools/testing/selftests/net/forwarding/devlink_lib.sh b/tools/testing/selftests/net/forwarding/devlink_lib.sh
index 8553a67a2322..40b076983239 100644
--- a/tools/testing/selftests/net/forwarding/devlink_lib.sh
+++ b/tools/testing/selftests/net/forwarding/devlink_lib.sh
@@ -4,19 +4,21 @@
##############################################################################
# Defines
-DEVLINK_DEV=$(devlink port show "${NETIFS[p1]}" -j \
- | jq -r '.port | keys[]' | cut -d/ -f-2)
-if [ -z "$DEVLINK_DEV" ]; then
- echo "SKIP: ${NETIFS[p1]} has no devlink device registered for it"
- exit 1
-fi
-if [[ "$(echo $DEVLINK_DEV | grep -c pci)" -eq 0 ]]; then
- echo "SKIP: devlink device's bus is not PCI"
- exit 1
-fi
+if [[ ! -v DEVLINK_DEV ]]; then
+ DEVLINK_DEV=$(devlink port show "${NETIFS[p1]}" -j \
+ | jq -r '.port | keys[]' | cut -d/ -f-2)
+ if [ -z "$DEVLINK_DEV" ]; then
+ echo "SKIP: ${NETIFS[p1]} has no devlink device registered for it"
+ exit 1
+ fi
+ if [[ "$(echo $DEVLINK_DEV | grep -c pci)" -eq 0 ]]; then
+ echo "SKIP: devlink device's bus is not PCI"
+ exit 1
+ fi
-DEVLINK_VIDDID=$(lspci -s $(echo $DEVLINK_DEV | cut -d"/" -f2) \
- -n | cut -d" " -f3)
+ DEVLINK_VIDDID=$(lspci -s $(echo $DEVLINK_DEV | cut -d"/" -f2) \
+ -n | cut -d" " -f3)
+fi
##############################################################################
# Sanity checks
@@ -27,6 +29,12 @@ if [ $? -ne 0 ]; then
exit 1
fi
+devlink help 2>&1 | grep trap &> /dev/null
+if [ $? -ne 0 ]; then
+ echo "SKIP: iproute2 too old, missing devlink trap support"
+ exit 1
+fi
+
##############################################################################
# Devlink helpers
@@ -190,3 +198,215 @@ devlink_tc_bind_pool_th_restore()
devlink sb tc bind set $port tc $tc type $dir \
pool ${orig[0]} th ${orig[1]}
}
+
+devlink_traps_num_get()
+{
+ devlink -j trap | jq '.[]["'$DEVLINK_DEV'"] | length'
+}
+
+devlink_traps_get()
+{
+ devlink -j trap | jq -r '.[]["'$DEVLINK_DEV'"][].name'
+}
+
+devlink_trap_type_get()
+{
+ local trap_name=$1; shift
+
+ devlink -j trap show $DEVLINK_DEV trap $trap_name \
+ | jq -r '.[][][].type'
+}
+
+devlink_trap_action_set()
+{
+ local trap_name=$1; shift
+ local action=$1; shift
+
+ # Pipe output to /dev/null to avoid expected warnings.
+ devlink trap set $DEVLINK_DEV trap $trap_name \
+ action $action &> /dev/null
+}
+
+devlink_trap_action_get()
+{
+ local trap_name=$1; shift
+
+ devlink -j trap show $DEVLINK_DEV trap $trap_name \
+ | jq -r '.[][][].action'
+}
+
+devlink_trap_group_get()
+{
+ devlink -j trap show $DEVLINK_DEV trap $trap_name \
+ | jq -r '.[][][].group'
+}
+
+devlink_trap_metadata_test()
+{
+ local trap_name=$1; shift
+ local metadata=$1; shift
+
+ devlink -jv trap show $DEVLINK_DEV trap $trap_name \
+ | jq -e '.[][][].metadata | contains(["'$metadata'"])' \
+ &> /dev/null
+}
+
+devlink_trap_rx_packets_get()
+{
+ local trap_name=$1; shift
+
+ devlink -js trap show $DEVLINK_DEV trap $trap_name \
+ | jq '.[][][]["stats"]["rx"]["packets"]'
+}
+
+devlink_trap_rx_bytes_get()
+{
+ local trap_name=$1; shift
+
+ devlink -js trap show $DEVLINK_DEV trap $trap_name \
+ | jq '.[][][]["stats"]["rx"]["bytes"]'
+}
+
+devlink_trap_stats_idle_test()
+{
+ local trap_name=$1; shift
+ local t0_packets t0_bytes
+ local t1_packets t1_bytes
+
+ t0_packets=$(devlink_trap_rx_packets_get $trap_name)
+ t0_bytes=$(devlink_trap_rx_bytes_get $trap_name)
+
+ sleep 1
+
+ t1_packets=$(devlink_trap_rx_packets_get $trap_name)
+ t1_bytes=$(devlink_trap_rx_bytes_get $trap_name)
+
+ if [[ $t0_packets -eq $t1_packets && $t0_bytes -eq $t1_bytes ]]; then
+ return 0
+ else
+ return 1
+ fi
+}
+
+devlink_traps_enable_all()
+{
+ local trap_name
+
+ for trap_name in $(devlink_traps_get); do
+ devlink_trap_action_set $trap_name "trap"
+ done
+}
+
+devlink_traps_disable_all()
+{
+ for trap_name in $(devlink_traps_get); do
+ devlink_trap_action_set $trap_name "drop"
+ done
+}
+
+devlink_trap_groups_get()
+{
+ devlink -j trap group | jq -r '.[]["'$DEVLINK_DEV'"][].name'
+}
+
+devlink_trap_group_action_set()
+{
+ local group_name=$1; shift
+ local action=$1; shift
+
+ # Pipe output to /dev/null to avoid expected warnings.
+ devlink trap group set $DEVLINK_DEV group $group_name action $action \
+ &> /dev/null
+}
+
+devlink_trap_group_rx_packets_get()
+{
+ local group_name=$1; shift
+
+ devlink -js trap group show $DEVLINK_DEV group $group_name \
+ | jq '.[][][]["stats"]["rx"]["packets"]'
+}
+
+devlink_trap_group_rx_bytes_get()
+{
+ local group_name=$1; shift
+
+ devlink -js trap group show $DEVLINK_DEV group $group_name \
+ | jq '.[][][]["stats"]["rx"]["bytes"]'
+}
+
+devlink_trap_group_stats_idle_test()
+{
+ local group_name=$1; shift
+ local t0_packets t0_bytes
+ local t1_packets t1_bytes
+
+ t0_packets=$(devlink_trap_group_rx_packets_get $group_name)
+ t0_bytes=$(devlink_trap_group_rx_bytes_get $group_name)
+
+ sleep 1
+
+ t1_packets=$(devlink_trap_group_rx_packets_get $group_name)
+ t1_bytes=$(devlink_trap_group_rx_bytes_get $group_name)
+
+ if [[ $t0_packets -eq $t1_packets && $t0_bytes -eq $t1_bytes ]]; then
+ return 0
+ else
+ return 1
+ fi
+}
+
+devlink_trap_exception_test()
+{
+ local trap_name=$1; shift
+ local group_name=$1; shift
+
+ devlink_trap_stats_idle_test $trap_name
+ check_fail $? "Trap stats idle when packets should have been trapped"
+
+ devlink_trap_group_stats_idle_test $group_name
+ check_fail $? "Trap group idle when packets should have been trapped"
+}
+
+devlink_trap_drop_test()
+{
+ local trap_name=$1; shift
+ local group_name=$1; shift
+ local dev=$1; shift
+
+ # This is the common part of all the tests. It checks that stats are
+ # initially idle, then non-idle after changing the trap action and
+ # finally idle again. It also makes sure the packets are dropped and
+ # never forwarded.
+ devlink_trap_stats_idle_test $trap_name
+ check_err $? "Trap stats not idle with initial drop action"
+ devlink_trap_group_stats_idle_test $group_name
+ check_err $? "Trap group stats not idle with initial drop action"
+
+
+ devlink_trap_action_set $trap_name "trap"
+ devlink_trap_stats_idle_test $trap_name
+ check_fail $? "Trap stats idle after setting action to trap"
+ devlink_trap_group_stats_idle_test $group_name
+ check_fail $? "Trap group stats idle after setting action to trap"
+
+ devlink_trap_action_set $trap_name "drop"
+
+ devlink_trap_stats_idle_test $trap_name
+ check_err $? "Trap stats not idle after setting action to drop"
+ devlink_trap_group_stats_idle_test $group_name
+ check_err $? "Trap group stats not idle after setting action to drop"
+
+ tc_check_packets "dev $dev egress" 101 0
+ check_err $? "Packets were not dropped"
+}
+
+devlink_trap_drop_cleanup()
+{
+ local mz_pid=$1; shift
+ local dev=$1; shift
+ local proto=$1; shift
+
+ kill $mz_pid && wait $mz_pid &> /dev/null
+ tc filter del dev $dev egress protocol $proto pref 1 handle 101 flower
+}
diff --git a/tools/testing/selftests/net/forwarding/ethtool.sh b/tools/testing/selftests/net/forwarding/ethtool.sh
new file mode 100755
index 000000000000..eb8e2a23bbb4
--- /dev/null
+++ b/tools/testing/selftests/net/forwarding/ethtool.sh
@@ -0,0 +1,318 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+ALL_TESTS="
+ same_speeds_autoneg_off
+ different_speeds_autoneg_off
+ combination_of_neg_on_and_off
+ advertise_subset_of_speeds
+ check_highest_speed_is_chosen
+ different_speeds_autoneg_on
+"
+NUM_NETIFS=2
+source lib.sh
+source ethtool_lib.sh
+
+h1_create()
+{
+ simple_if_init $h1 192.0.2.1/24
+}
+
+h1_destroy()
+{
+ simple_if_fini $h1 192.0.2.1/24
+}
+
+h2_create()
+{
+ simple_if_init $h2 192.0.2.2/24
+}
+
+h2_destroy()
+{
+ simple_if_fini $h2 192.0.2.2/24
+}
+
+setup_prepare()
+{
+ h1=${NETIFS[p1]}
+ h2=${NETIFS[p2]}
+
+ h1_create
+ h2_create
+}
+
+cleanup()
+{
+ pre_cleanup
+
+ h2_destroy
+ h1_destroy
+}
+
+different_speeds_get()
+{
+ local dev1=$1; shift
+ local dev2=$1; shift
+ local with_mode=$1; shift
+ local adver=$1; shift
+
+ local -a speeds_arr
+
+ speeds_arr=($(common_speeds_get $dev1 $dev2 $with_mode $adver))
+ if [[ ${#speeds_arr[@]} < 2 ]]; then
+ check_err 1 "cannot check different speeds. There are not enough speeds"
+ fi
+
+ echo ${speeds_arr[0]} ${speeds_arr[1]}
+}
+
+same_speeds_autoneg_off()
+{
+ # Check that when each of the reported speeds is forced, the links come
+ # up and are operational.
+ local -a speeds_arr=($(common_speeds_get $h1 $h2 0 0))
+
+ for speed in "${speeds_arr[@]}"; do
+ RET=0
+ ethtool_set $h1 speed $speed autoneg off
+ ethtool_set $h2 speed $speed autoneg off
+
+ setup_wait_dev_with_timeout $h1
+ setup_wait_dev_with_timeout $h2
+ ping_do $h1 192.0.2.2
+ check_err $? "speed $speed autoneg off"
+ log_test "force of same speed autoneg off"
+ log_info "speed = $speed"
+ done
+
+ ethtool -s $h2 autoneg on
+ ethtool -s $h1 autoneg on
+}
+
+different_speeds_autoneg_off()
+{
+ # Test that when we force different speeds, links are not up and ping
+ # fails.
+ RET=0
+
+ local -a speeds_arr=($(different_speeds_get $h1 $h2 0 0))
+ local speed1=${speeds_arr[0]}
+ local speed2=${speeds_arr[1]}
+
+ ethtool_set $h1 speed $speed1 autoneg off
+ ethtool_set $h2 speed $speed2 autoneg off
+
+ setup_wait_dev_with_timeout $h1
+ setup_wait_dev_with_timeout $h2
+ ping_do $h1 192.0.2.2
+ check_fail $? "ping with different speeds"
+
+ log_test "force of different speeds autoneg off"
+
+ ethtool -s $h2 autoneg on
+ ethtool -s $h1 autoneg on
+}
+
+combination_of_neg_on_and_off()
+{
+ # Test that when one device is forced to a speed supported by both
+ # endpoints and the other device is configured to autoneg on, the links
+ # are up and ping passes.
+ local -a speeds_arr=($(common_speeds_get $h1 $h2 0 1))
+
+ for speed in "${speeds_arr[@]}"; do
+ RET=0
+ ethtool_set $h1 speed $speed autoneg off
+
+ setup_wait_dev_with_timeout $h1
+ setup_wait_dev_with_timeout $h2
+ ping_do $h1 192.0.2.2
+ check_err $? "h1-speed=$speed autoneg off, h2 autoneg on"
+ log_test "one side with autoneg off and another with autoneg on"
+ log_info "force speed = $speed"
+ done
+
+ ethtool -s $h1 autoneg on
+}
+
+hex_speed_value_get()
+{
+ local speed=$1; shift
+
+ local shift_size=${speed_values[$speed]}
+ speed=$((0x1 << $"shift_size"))
+ printf "%#x" "$speed"
+}
+
+subset_of_common_speeds_get()
+{
+ local dev1=$1; shift
+ local dev2=$1; shift
+ local adver=$1; shift
+
+ local -a speeds_arr=($(common_speeds_get $dev1 $dev2 0 $adver))
+ local speed_to_advertise=0
+ local speed_to_remove=${speeds_arr[0]}
+ speed_to_remove+='base'
+
+ local -a speeds_mode_arr=($(common_speeds_get $dev1 $dev2 1 $adver))
+
+ for speed in ${speeds_mode_arr[@]}; do
+ if [[ $speed != $speed_to_remove* ]]; then
+ speed=$(hex_speed_value_get $speed)
+ speed_to_advertise=$(($speed_to_advertise | \
+ $speed))
+ fi
+
+ done
+
+ # Convert to hex.
+ printf "%#x" "$speed_to_advertise"
+}
+
+speed_to_advertise_get()
+{
+ # The function returns the hex number that is composed by OR-ing all
+ # the modes corresponding to the provided speed.
+ local speed_without_mode=$1; shift
+ local supported_speeds=("$@"); shift
+ local speed_to_advertise=0
+
+ speed_without_mode+='base'
+
+ for speed in ${supported_speeds[@]}; do
+ if [[ $speed == $speed_without_mode* ]]; then
+ speed=$(hex_speed_value_get $speed)
+ speed_to_advertise=$(($speed_to_advertise | \
+ $speed))
+ fi
+
+ done
+
+ # Convert to hex.
+ printf "%#x" "$speed_to_advertise"
+}
+
+advertise_subset_of_speeds()
+{
+ # Test that when one device advertises a subset of speeds and another
+ # advertises a specific speed (but all modes of this speed), the links
+ # are up and ping passes.
+ RET=0
+
+ local speed_1_to_advertise=$(subset_of_common_speeds_get $h1 $h2 1)
+ ethtool_set $h1 advertise $speed_1_to_advertise
+
+ if [ $RET != 0 ]; then
+ log_test "advertise subset of speeds"
+ return
+ fi
+
+ local -a speeds_arr_without_mode=($(common_speeds_get $h1 $h2 0 1))
+ # Check only speeds that h1 advertised. Remove the first speed.
+ unset speeds_arr_without_mode[0]
+ local -a speeds_arr_with_mode=($(common_speeds_get $h1 $h2 1 1))
+
+ for speed_value in ${speeds_arr_without_mode[@]}; do
+ RET=0
+ local speed_2_to_advertise=$(speed_to_advertise_get $speed_value \
+ "${speeds_arr_with_mode[@]}")
+ ethtool_set $h2 advertise $speed_2_to_advertise
+
+ setup_wait_dev_with_timeout $h1
+ setup_wait_dev_with_timeout $h2
+ ping_do $h1 192.0.2.2
+ check_err $? "h1=$speed_1_to_advertise, h2=$speed_2_to_advertise ($speed_value)"
+
+ log_test "advertise subset of speeds"
+ log_info "h1=$speed_1_to_advertise, h2=$speed_2_to_advertise"
+ done
+
+ ethtool -s $h2 autoneg on
+ ethtool -s $h1 autoneg on
+}
+
+check_highest_speed_is_chosen()
+{
+ # Test that when one device advertises a subset of speeds, the other
+ # chooses the highest speed. This test checks configuration without
+ # traffic.
+ RET=0
+
+ local max_speed
+ local chosen_speed
+ local speed_to_advertise=$(subset_of_common_speeds_get $h1 $h2 1)
+
+ ethtool_set $h1 advertise $speed_to_advertise
+
+ if [ $RET != 0 ]; then
+ log_test "check highest speed"
+ return
+ fi
+
+ local -a speeds_arr=($(common_speeds_get $h1 $h2 0 1))
+ # Remove the first speed, h1 does not advertise this speed.
+ unset speeds_arr[0]
+
+ max_speed=${speeds_arr[0]}
+ for current in ${speeds_arr[@]}; do
+ if [[ $current -gt $max_speed ]]; then
+ max_speed=$current
+ fi
+ done
+
+ setup_wait_dev_with_timeout $h1
+ setup_wait_dev_with_timeout $h2
+ chosen_speed=$(ethtool $h1 | grep 'Speed:')
+ chosen_speed=${chosen_speed%"Mb/s"*}
+ chosen_speed=${chosen_speed#*"Speed: "}
+ ((chosen_speed == max_speed))
+ check_err $? "h1 advertise $speed_to_advertise, h2 sync to speed $chosen_speed"
+
+ log_test "check highest speed"
+
+ ethtool -s $h2 autoneg on
+ ethtool -s $h1 autoneg on
+}
+
+different_speeds_autoneg_on()
+{
+ # Test that when we configure links to advertise different speeds,
+ # links are not up and ping fails.
+ RET=0
+
+ local -a speeds=($(different_speeds_get $h1 $h2 1 1))
+ local speed1=${speeds[0]}
+ local speed2=${speeds[1]}
+
+ speed1=$(hex_speed_value_get $speed1)
+ speed2=$(hex_speed_value_get $speed2)
+
+ ethtool_set $h1 advertise $speed1
+ ethtool_set $h2 advertise $speed2
+
+ if (($RET)); then
+ setup_wait_dev_with_timeout $h1
+ setup_wait_dev_with_timeout $h2
+ ping_do $h1 192.0.2.2
+ check_fail $? "ping with different speeds autoneg on"
+ fi
+
+ log_test "advertise different speeds autoneg on"
+
+ ethtool -s $h2 autoneg on
+ ethtool -s $h1 autoneg on
+}
+
+trap cleanup EXIT
+
+setup_prepare
+setup_wait
+
+declare -gA speed_values
+eval "speed_values=($(speeds_arr_get))"
+
+tests_run
+
+exit $EXIT_STATUS
diff --git a/tools/testing/selftests/net/forwarding/ethtool_lib.sh b/tools/testing/selftests/net/forwarding/ethtool_lib.sh
new file mode 100755
index 000000000000..925d229a59d8
--- /dev/null
+++ b/tools/testing/selftests/net/forwarding/ethtool_lib.sh
@@ -0,0 +1,69 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+speeds_arr_get()
+{
+ cmd='/ETHTOOL_LINK_MODE_[^[:space:]]*_BIT[[:space:]]+=[[:space:]]+/ \
+ {sub(/,$/, "") \
+ sub(/ETHTOOL_LINK_MODE_/,"") \
+ sub(/_BIT/,"") \
+ sub(/_Full/,"/Full") \
+ sub(/_Half/,"/Half");\
+ print "["$1"]="$3}'
+
+ awk "${cmd}" /usr/include/linux/ethtool.h
+}
+
+ethtool_set()
+{
+ local cmd="$@"
+ local out=$(ethtool -s $cmd 2>&1 | wc -l)
+
+ check_err $out "error in configuration. $cmd"
+}
+
+dev_speeds_get()
+{
+ local dev=$1; shift
+ local with_mode=$1; shift
+ local adver=$1; shift
+ local speeds_str
+
+ if (($adver)); then
+ mode="Advertised link modes"
+ else
+ mode="Supported link modes"
+ fi
+
+ speeds_str=$(ethtool "$dev" | \
+ # Snip everything before the link modes section.
+ sed -n '/'"$mode"':/,$p' | \
+ # Quit processing the rest at the start of the next section.
+ # When checking, skip the header of this section (hence the 2,).
+ sed -n '2,${/^[\t][^ \t]/q};p' | \
+ # Drop the section header of the current section.
+ cut -d':' -f2)
+
+ local -a speeds_arr=($speeds_str)
+ if [[ $with_mode -eq 0 ]]; then
+ for ((i=0; i<${#speeds_arr[@]}; i++)); do
+ speeds_arr[$i]=${speeds_arr[$i]%base*}
+ done
+ fi
+ echo ${speeds_arr[@]}
+}
+
+common_speeds_get()
+{
+ dev1=$1; shift
+ dev2=$1; shift
+ with_mode=$1; shift
+ adver=$1; shift
+
+ local -a dev1_speeds=($(dev_speeds_get $dev1 $with_mode $adver))
+ local -a dev2_speeds=($(dev_speeds_get $dev2 $with_mode $adver))
+
+ comm -12 \
+ <(printf '%s\n' "${dev1_speeds[@]}" | sort -u) \
+ <(printf '%s\n' "${dev2_speeds[@]}" | sort -u)
+}
diff --git a/tools/testing/selftests/net/forwarding/fib_offload_lib.sh b/tools/testing/selftests/net/forwarding/fib_offload_lib.sh
new file mode 100644
index 000000000000..66496659bea7
--- /dev/null
+++ b/tools/testing/selftests/net/forwarding/fib_offload_lib.sh
@@ -0,0 +1,873 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Various helpers and tests to verify FIB offload.
+
+__fib_trap_check()
+{
+ local ns=$1; shift
+ local family=$1; shift
+ local route=$1; shift
+ local should_fail=$1; shift
+ local ret
+
+ ip -n $ns -j -p -$family route show $route \
+ | jq -e '.[]["flags"] | contains(["trap"])' &> /dev/null
+ ret=$?
+ if [[ $should_fail == "true" ]]; then
+ if [[ $ret -ne 0 ]]; then
+ return 0
+ else
+ return 1
+ fi
+ fi
+
+ return $ret
+}
+
+fib_trap_check()
+{
+ local ns=$1; shift
+ local family=$1; shift
+ local route=$1; shift
+ local should_fail=$1; shift
+
+ busywait 5000 __fib_trap_check $ns $family "$route" $should_fail
+}
+
+fib4_trap_check()
+{
+ local ns=$1; shift
+ local route=$1; shift
+ local should_fail=$1; shift
+
+ fib_trap_check $ns 4 "$route" $should_fail
+}
+
+fib6_trap_check()
+{
+ local ns=$1; shift
+ local route=$1; shift
+ local should_fail=$1; shift
+
+ fib_trap_check $ns 6 "$route" $should_fail
+}
+
+fib_ipv4_identical_routes_test()
+{
+ local ns=$1; shift
+ local i
+
+ RET=0
+
+ for i in $(seq 1 3); do
+ ip -n $ns link add name dummy$i type dummy
+ ip -n $ns link set dev dummy$i up
+ done
+
+ ip -n $ns route add 192.0.2.0/24 dev dummy1 tos 0 metric 1024
+ fib4_trap_check $ns "192.0.2.0/24 dev dummy1 tos 0 metric 1024" false
+ check_err $? "Route not in hardware when should"
+
+ ip -n $ns route append 192.0.2.0/24 dev dummy2 tos 0 metric 1024
+ fib4_trap_check $ns "192.0.2.0/24 dev dummy2 tos 0 metric 1024" true
+ check_err $? "Appended route in hardware when should not"
+
+ ip -n $ns route prepend 192.0.2.0/24 dev dummy3 tos 0 metric 1024
+ fib4_trap_check $ns "192.0.2.0/24 dev dummy3 tos 0 metric 1024" false
+ check_err $? "Prepended route not in hardware when should"
+
+ fib4_trap_check $ns "192.0.2.0/24 dev dummy1 tos 0 metric 1024" true
+ check_err $? "Route was not replaced in hardware by prepended one"
+
+ log_test "IPv4 identical routes"
+
+ for i in $(seq 1 3); do
+ ip -n $ns link del dev dummy$i
+ done
+}
+
+fib_ipv4_tos_test()
+{
+ local ns=$1; shift
+
+ RET=0
+
+ ip -n $ns link add name dummy1 type dummy
+ ip -n $ns link set dev dummy1 up
+
+ ip -n $ns route add 192.0.2.0/24 dev dummy1 tos 0 metric 1024
+ fib4_trap_check $ns "192.0.2.0/24 dev dummy1 tos 0 metric 1024" false
+ check_err $? "Route not in hardware when should"
+
+ ip -n $ns route add 192.0.2.0/24 dev dummy1 tos 2 metric 1024
+ fib4_trap_check $ns "192.0.2.0/24 dev dummy1 tos 2 metric 1024" false
+ check_err $? "Highest TOS route not in hardware when should"
+
+ fib4_trap_check $ns "192.0.2.0/24 dev dummy1 tos 0 metric 1024" true
+ check_err $? "Lowest TOS route still in hardware when should not"
+
+ ip -n $ns route add 192.0.2.0/24 dev dummy1 tos 1 metric 1024
+ fib4_trap_check $ns "192.0.2.0/24 dev dummy1 tos 1 metric 1024" true
+ check_err $? "Middle TOS route in hardware when should not"
+
+ log_test "IPv4 routes with TOS"
+
+ ip -n $ns link del dev dummy1
+}
+
+fib_ipv4_metric_test()
+{
+ local ns=$1; shift
+
+ RET=0
+
+ ip -n $ns link add name dummy1 type dummy
+ ip -n $ns link set dev dummy1 up
+
+ ip -n $ns route add 192.0.2.0/24 dev dummy1 metric 1024
+ fib4_trap_check $ns "192.0.2.0/24 dev dummy1 metric 1024" false
+ check_err $? "Route not in hardware when should"
+
+ ip -n $ns route add 192.0.2.0/24 dev dummy1 metric 1022
+ fib4_trap_check $ns "192.0.2.0/24 dev dummy1 metric 1022" false
+ check_err $? "Lowest metric route not in hardware when should"
+
+ fib4_trap_check $ns "192.0.2.0/24 dev dummy1 metric 1024" true
+ check_err $? "Highest metric route still in hardware when should not"
+
+ ip -n $ns route add 192.0.2.0/24 dev dummy1 metric 1023
+ fib4_trap_check $ns "192.0.2.0/24 dev dummy1 metric 1023" true
+ check_err $? "Middle metric route in hardware when should not"
+
+ log_test "IPv4 routes with metric"
+
+ ip -n $ns link del dev dummy1
+}
+
+fib_ipv4_replace_test()
+{
+ local ns=$1; shift
+ local i
+
+ RET=0
+
+ for i in $(seq 1 2); do
+ ip -n $ns link add name dummy$i type dummy
+ ip -n $ns link set dev dummy$i up
+ done
+
+ ip -n $ns route add 192.0.2.0/24 dev dummy1 metric 1024
+ fib4_trap_check $ns "192.0.2.0/24 dev dummy1 metric 1024" false
+ check_err $? "Route not in hardware when should"
+
+ ip -n $ns route replace 192.0.2.0/24 dev dummy2 metric 1024
+ fib4_trap_check $ns "192.0.2.0/24 dev dummy2 metric 1024" false
+ check_err $? "Replacement route not in hardware when should"
+
+ # Add a route with an higher metric and make sure that replacing it
+ # does not affect the lower metric one.
+ ip -n $ns route add 192.0.2.0/24 dev dummy1 metric 1025
+ ip -n $ns route replace 192.0.2.0/24 dev dummy2 metric 1025
+
+ fib4_trap_check $ns "192.0.2.0/24 dev dummy2 metric 1024" false
+ check_err $? "Lowest metric route not in hardware when should"
+ fib4_trap_check $ns "192.0.2.0/24 dev dummy2 metric 1025" true
+ check_err $? "Highest metric route in hardware when should not"
+
+ log_test "IPv4 route replace"
+
+ for i in $(seq 1 2); do
+ ip -n $ns link del dev dummy$i
+ done
+}
+
+fib_ipv4_delete_test()
+{
+ local ns=$1; shift
+ local metric
+
+ RET=0
+
+ ip -n $ns link add name dummy1 type dummy
+ ip -n $ns link set dev dummy1 up
+
+ # Insert multiple routes with the same prefix and length and varying
+ # metrics. Make sure that throughout delete operations the lowest
+ # metric route is the one in hardware.
+ for metric in $(seq 1024 1026); do
+ ip -n $ns route add 192.0.2.0/24 dev dummy1 metric $metric
+ done
+
+ fib4_trap_check $ns "192.0.2.0/24 dev dummy1 metric 1024" false
+ check_err $? "Route not in hardware when should"
+
+ ip -n $ns route del 192.0.2.0/24 dev dummy1 metric 1024
+ fib4_trap_check $ns "192.0.2.0/24 dev dummy1 metric 1025" false
+ check_err $? "Lowest metric route not in hardware when should"
+
+ ip -n $ns route del 192.0.2.0/24 dev dummy1 metric 1026
+ fib4_trap_check $ns "192.0.2.0/24 dev dummy1 metric 1025" false
+ check_err $? "Sole route not in hardware when should"
+
+ log_test "IPv4 route delete"
+
+ ip -n $ns link del dev dummy1
+}
+
+fib_ipv4_plen_test()
+{
+ local ns=$1; shift
+
+ RET=0
+
+ ip -n $ns link add name dummy1 type dummy
+ ip -n $ns link set dev dummy1 up
+
+ # Add two routes with the same key and different prefix length and
+ # make sure both are in hardware. It can be verfied that both are
+ # sharing the same leaf by checking the /proc/net/fib_trie
+ ip -n $ns route add 192.0.2.0/24 dev dummy1
+ ip -n $ns route add 192.0.2.0/25 dev dummy1
+
+ fib4_trap_check $ns "192.0.2.0/24 dev dummy1" false
+ check_err $? "/24 not in hardware when should"
+
+ fib4_trap_check $ns "192.0.2.0/25 dev dummy1" false
+ check_err $? "/25 not in hardware when should"
+
+ log_test "IPv4 routes with different prefix length"
+
+ ip -n $ns link del dev dummy1
+}
+
+fib_ipv4_replay_metric_test()
+{
+ local ns=$1; shift
+ local devlink_dev=$1; shift
+
+ RET=0
+
+ ip -n $ns link add name dummy1 type dummy
+ ip -n $ns link set dev dummy1 up
+
+ ip -n $ns route add 192.0.2.0/24 dev dummy1 metric 1024
+ ip -n $ns route add 192.0.2.0/24 dev dummy1 metric 1025
+
+ devlink -N $ns dev reload $devlink_dev
+
+ fib4_trap_check $ns "192.0.2.0/24 dev dummy1 metric 1024" false
+ check_err $? "Lowest metric route not in hardware when should"
+
+ fib4_trap_check $ns "192.0.2.0/24 dev dummy1 metric 1025" true
+ check_err $? "Highest metric route in hardware when should not"
+
+ log_test "IPv4 routes replay - metric"
+
+ ip -n $ns link del dev dummy1
+}
+
+fib_ipv4_replay_tos_test()
+{
+ local ns=$1; shift
+ local devlink_dev=$1; shift
+
+ RET=0
+
+ ip -n $ns link add name dummy1 type dummy
+ ip -n $ns link set dev dummy1 up
+
+ ip -n $ns route add 192.0.2.0/24 dev dummy1 tos 0
+ ip -n $ns route add 192.0.2.0/24 dev dummy1 tos 1
+
+ devlink -N $ns dev reload $devlink_dev
+
+ fib4_trap_check $ns "192.0.2.0/24 dev dummy1 tos 1" false
+ check_err $? "Highest TOS route not in hardware when should"
+
+ fib4_trap_check $ns "192.0.2.0/24 dev dummy1 tos 0" true
+ check_err $? "Lowest TOS route in hardware when should not"
+
+ log_test "IPv4 routes replay - TOS"
+
+ ip -n $ns link del dev dummy1
+}
+
+fib_ipv4_replay_plen_test()
+{
+ local ns=$1; shift
+ local devlink_dev=$1; shift
+
+ RET=0
+
+ ip -n $ns link add name dummy1 type dummy
+ ip -n $ns link set dev dummy1 up
+
+ ip -n $ns route add 192.0.2.0/24 dev dummy1
+ ip -n $ns route add 192.0.2.0/25 dev dummy1
+
+ devlink -N $ns dev reload $devlink_dev
+
+ fib4_trap_check $ns "192.0.2.0/24 dev dummy1" false
+ check_err $? "/24 not in hardware when should"
+
+ fib4_trap_check $ns "192.0.2.0/25 dev dummy1" false
+ check_err $? "/25 not in hardware when should"
+
+ log_test "IPv4 routes replay - prefix length"
+
+ ip -n $ns link del dev dummy1
+}
+
+fib_ipv4_flush_test()
+{
+ local ns=$1; shift
+ local metric
+
+ RET=0
+
+ ip -n $ns link add name dummy1 type dummy
+ ip -n $ns link set dev dummy1 up
+
+ # Exercise the routes flushing code paths by inserting various
+ # prefix routes on a netdev and then deleting it.
+ for metric in $(seq 1 20); do
+ ip -n $ns route add 192.0.2.0/24 dev dummy1 metric $metric
+ done
+
+ ip -n $ns link del dev dummy1
+
+ log_test "IPv4 routes flushing"
+}
+
+fib_ipv6_add_test()
+{
+ local ns=$1; shift
+
+ RET=0
+
+ for i in $(seq 1 2); do
+ ip -n $ns link add name dummy$i type dummy
+ ip -n $ns link set dev dummy$i up
+ done
+
+ ip -n $ns route add 2001:db8:1::/64 dev dummy1 metric 1024
+ fib6_trap_check $ns "2001:db8:1::/64 dev dummy1 metric 1024" false
+ check_err $? "Route not in hardware when should"
+
+ ip -n $ns route append 2001:db8:1::/64 dev dummy2 metric 1024
+ fib6_trap_check $ns "2001:db8:1::/64 dev dummy2 metric 1024" true
+ check_err $? "Route in hardware when should not"
+
+ fib6_trap_check $ns "2001:db8:1::/64 dev dummy1 metric 1024" false
+ check_err $? "Route not in hardware after appending route"
+
+ log_test "IPv6 single route add"
+
+ for i in $(seq 1 2); do
+ ip -n $ns link del dev dummy$i
+ done
+}
+
+fib_ipv6_metric_test()
+{
+ local ns=$1; shift
+
+ RET=0
+
+ ip -n $ns link add name dummy1 type dummy
+ ip -n $ns link set dev dummy1 up
+
+ ip -n $ns route add 2001:db8:1::/64 dev dummy1 metric 1024
+ fib6_trap_check $ns "2001:db8:1::/64 dev dummy1 metric 1024" false
+ check_err $? "Route not in hardware when should"
+
+ ip -n $ns route add 2001:db8:1::/64 dev dummy1 metric 1022
+ fib6_trap_check $ns "2001:db8:1::/64 dev dummy1 metric 1022" false
+ check_err $? "Lowest metric route not in hardware when should"
+
+ fib6_trap_check $ns "2001:db8:1::/64 dev dummy1 metric 1024" true
+ check_err $? "Highest metric route still in hardware when should not"
+
+ ip -n $ns route add 2001:db8:1::/64 dev dummy1 metric 1023
+ fib6_trap_check $ns "2001:db8:1::/64 dev dummy1 metric 1023" true
+ check_err $? "Middle metric route in hardware when should not"
+
+ log_test "IPv6 routes with metric"
+
+ ip -n $ns link del dev dummy1
+}
+
+fib_ipv6_append_single_test()
+{
+ local ns=$1; shift
+
+ # When an IPv6 multipath route is added without the 'nexthop' keyword,
+ # different code paths are taken compared to when the keyword is used.
+ # This test tries to verify the former.
+ RET=0
+
+ for i in $(seq 1 2); do
+ ip -n $ns link add name dummy$i type dummy
+ ip -n $ns link set dev dummy$i up
+ ip -n $ns address add 2001:db8:$i::1/64 dev dummy$i
+ done
+
+ ip -n $ns route add 2001:db8:10::/64 via 2001:db8:1::2 metric 1024
+ fib6_trap_check $ns "2001:db8:10::/64 metric 1024" false
+ check_err $? "Route not in hardware when should"
+
+ ip -n $ns route append 2001:db8:10::/64 via 2001:db8:2::2 metric 1024
+ fib6_trap_check $ns "2001:db8:10::/64 metric 1024" false
+ check_err $? "Route not in hardware after appending"
+
+ ip -n $ns route add 2001:db8:10::/64 via 2001:db8:1::2 metric 1025
+ fib6_trap_check $ns "2001:db8:10::/64 metric 1025" true
+ check_err $? "Route in hardware when should not"
+
+ ip -n $ns route append 2001:db8:10::/64 via 2001:db8:2::2 metric 1025
+ fib6_trap_check $ns "2001:db8:10::/64 metric 1025" true
+ check_err $? "Route in hardware when should not after appending"
+
+ fib6_trap_check $ns "2001:db8:10::/64 metric 1024" false
+ check_err $? "Lowest metric route not in hardware when should"
+
+ log_test "IPv6 append single route without 'nexthop' keyword"
+
+ for i in $(seq 1 2); do
+ ip -n $ns link del dev dummy$i
+ done
+}
+
+fib_ipv6_replace_single_test()
+{
+ local ns=$1; shift
+ local i
+
+ RET=0
+
+ for i in $(seq 1 2); do
+ ip -n $ns link add name dummy$i type dummy
+ ip -n $ns link set dev dummy$i up
+ done
+
+ ip -n $ns route add 2001:db8:1::/64 dev dummy1 metric 1024
+ fib6_trap_check $ns "2001:db8:1::/64 dev dummy1 metric 1024" false
+ check_err $? "Route not in hardware when should"
+
+ ip -n $ns route replace 2001:db8:1::/64 dev dummy2 metric 1024
+ fib6_trap_check $ns "2001:db8:1::/64 dev dummy2 metric 1024" false
+ check_err $? "Replacement route not in hardware when should"
+
+ # Add a route with an higher metric and make sure that replacing it
+ # does not affect the lower metric one.
+ ip -n $ns route add 2001:db8:1::/64 dev dummy1 metric 1025
+ ip -n $ns route replace 2001:db8:1::/64 dev dummy2 metric 1025
+
+ fib6_trap_check $ns "2001:db8:1::/64 dev dummy2 metric 1024" false
+ check_err $? "Lowest metric route not in hardware when should"
+ fib6_trap_check $ns "2001:db8:1::/64 dev dummy2 metric 1025" true
+ check_err $? "Highest metric route in hardware when should not"
+
+ log_test "IPv6 single route replace"
+
+ for i in $(seq 1 2); do
+ ip -n $ns link del dev dummy$i
+ done
+}
+
+fib_ipv6_metric_multipath_test()
+{
+ local ns=$1; shift
+
+ RET=0
+
+ for i in $(seq 1 2); do
+ ip -n $ns link add name dummy$i type dummy
+ ip -n $ns link set dev dummy$i up
+ ip -n $ns address add 2001:db8:$i::1/64 dev dummy$i
+ done
+
+ ip -n $ns route add 2001:db8:10::/64 metric 1024 \
+ nexthop via 2001:db8:1::2 dev dummy1 \
+ nexthop via 2001:db8:2::2 dev dummy2
+ fib6_trap_check $ns "2001:db8:10::/64 metric 1024" false
+ check_err $? "Route not in hardware when should"
+
+ ip -n $ns route add 2001:db8:10::/64 metric 1022 \
+ nexthop via 2001:db8:1::2 dev dummy1 \
+ nexthop via 2001:db8:2::2 dev dummy2
+ fib6_trap_check $ns "2001:db8:10::/64 metric 1022" false
+ check_err $? "Lowest metric route not in hardware when should"
+
+ ip -n $ns route add 2001:db8:10::/64 metric 1023 \
+ nexthop via 2001:db8:1::2 dev dummy1 \
+ nexthop via 2001:db8:2::2 dev dummy2
+ fib6_trap_check $ns "2001:db8:10::/64 metric 1024" true
+ check_err $? "Highest metric route still in hardware when should not"
+
+ fib6_trap_check $ns "2001:db8:10::/64 metric 1023" true
+ check_err $? "Middle metric route in hardware when should not"
+
+ log_test "IPv6 multipath routes with metric"
+
+ for i in $(seq 1 2); do
+ ip -n $ns link del dev dummy$i
+ done
+}
+
+fib_ipv6_append_multipath_test()
+{
+ local ns=$1; shift
+
+ RET=0
+
+ for i in $(seq 1 3); do
+ ip -n $ns link add name dummy$i type dummy
+ ip -n $ns link set dev dummy$i up
+ ip -n $ns address add 2001:db8:$i::1/64 dev dummy$i
+ done
+
+ ip -n $ns route add 2001:db8:10::/64 metric 1024 \
+ nexthop via 2001:db8:1::2 dev dummy1
+ fib6_trap_check $ns "2001:db8:10::/64 metric 1024" false
+ check_err $? "Route not in hardware when should"
+
+ ip -n $ns route append 2001:db8:10::/64 metric 1024 \
+ nexthop via 2001:db8:2::2 dev dummy2 \
+ nexthop via 2001:db8:3::2 dev dummy3
+ fib6_trap_check $ns "2001:db8:10::/64 metric 1024" false
+ check_err $? "Route not in hardware after appending"
+
+ ip -n $ns route add 2001:db8:10::/64 metric 1025 \
+ nexthop via 2001:db8:1::2 dev dummy1
+ fib6_trap_check $ns "2001:db8:10::/64 metric 1025" true
+ check_err $? "Route in hardware when should not"
+
+ ip -n $ns route append 2001:db8:10::/64 metric 1025 \
+ nexthop via 2001:db8:2::2 dev dummy2 \
+ nexthop via 2001:db8:3::2 dev dummy3
+ fib6_trap_check $ns "2001:db8:10::/64 metric 1025" true
+ check_err $? "Route in hardware when should not after appending"
+
+ fib6_trap_check $ns "2001:db8:10::/64 metric 1024" false
+ check_err $? "Lowest metric route not in hardware when should"
+
+ log_test "IPv6 append multipath route with 'nexthop' keyword"
+
+ for i in $(seq 1 3); do
+ ip -n $ns link del dev dummy$i
+ done
+}
+
+fib_ipv6_replace_multipath_test()
+{
+ local ns=$1; shift
+ local i
+
+ RET=0
+
+ for i in $(seq 1 3); do
+ ip -n $ns link add name dummy$i type dummy
+ ip -n $ns link set dev dummy$i up
+ ip -n $ns address add 2001:db8:$i::1/64 dev dummy$i
+ done
+
+ ip -n $ns route add 2001:db8:10::/64 metric 1024 \
+ nexthop via 2001:db8:1::2 dev dummy1 \
+ nexthop via 2001:db8:2::2 dev dummy2
+ fib6_trap_check $ns "2001:db8:10::/64 metric 1024" false
+ check_err $? "Route not in hardware when should"
+
+ ip -n $ns route replace 2001:db8:10::/64 metric 1024 \
+ nexthop via 2001:db8:1::2 dev dummy1 \
+ nexthop via 2001:db8:3::2 dev dummy3
+ fib6_trap_check $ns "2001:db8:10::/64 metric 1024" false
+ check_err $? "Replacement route not in hardware when should"
+
+ # Add a route with an higher metric and make sure that replacing it
+ # does not affect the lower metric one.
+ ip -n $ns route add 2001:db8:10::/64 metric 1025 \
+ nexthop via 2001:db8:1::2 dev dummy1 \
+ nexthop via 2001:db8:2::2 dev dummy2
+ ip -n $ns route replace 2001:db8:10::/64 metric 1025 \
+ nexthop via 2001:db8:1::2 dev dummy1 \
+ nexthop via 2001:db8:3::2 dev dummy3
+
+ fib6_trap_check $ns "2001:db8:10::/64 metric 1024" false
+ check_err $? "Lowest metric route not in hardware when should"
+ fib6_trap_check $ns "2001:db8:10::/64 metric 1025" true
+ check_err $? "Highest metric route in hardware when should not"
+
+ log_test "IPv6 multipath route replace"
+
+ for i in $(seq 1 3); do
+ ip -n $ns link del dev dummy$i
+ done
+}
+
+fib_ipv6_append_multipath_to_single_test()
+{
+ local ns=$1; shift
+
+ # Test that when the first route in the leaf is not a multipath route
+ # and we try to append a multipath route with the same metric to it, it
+ # is not notified.
+ RET=0
+
+ for i in $(seq 1 2); do
+ ip -n $ns link add name dummy$i type dummy
+ ip -n $ns link set dev dummy$i up
+ ip -n $ns address add 2001:db8:$i::1/64 dev dummy$i
+ done
+
+ ip -n $ns route add 2001:db8:10::/64 dev dummy1 metric 1024
+ fib6_trap_check $ns "2001:db8:10::/64 dev dummy1 metric 1024" false
+ check_err $? "Route not in hardware when should"
+
+ ip -n $ns route append 2001:db8:10::/64 metric 1024 \
+ nexthop via 2001:db8:2::2 dev dummy2
+ fib6_trap_check $ns "2001:db8:10::/64 dev dummy2 metric 1024" true
+ check_err $? "Route in hardware when should not"
+
+ fib6_trap_check $ns "2001:db8:10::/64 dev dummy1 metric 1024" false
+ check_err $? "Route not in hardware after append"
+
+ log_test "IPv6 append multipath route to non-multipath route"
+
+ for i in $(seq 1 2); do
+ ip -n $ns link del dev dummy$i
+ done
+}
+
+fib_ipv6_delete_single_test()
+{
+ local ns=$1; shift
+
+ # Test various deletion scenarios, where only a single route is
+ # deleted from the FIB node.
+ for i in $(seq 1 2); do
+ ip -n $ns link add name dummy$i type dummy
+ ip -n $ns link set dev dummy$i up
+ ip -n $ns address add 2001:db8:$i::1/64 dev dummy$i
+ done
+
+ # Test deletion of a single route when it is the only route in the FIB
+ # node.
+ RET=0
+
+ ip -n $ns route add 2001:db8:10::/64 dev dummy1 metric 1024
+ ip -n $ns route del 2001:db8:10::/64 dev dummy1 metric 1024
+
+ log_test "IPv6 delete sole single route"
+
+ # Test that deletion of last route does not affect the first one.
+ RET=0
+
+ ip -n $ns route add 2001:db8:10::/64 dev dummy1 metric 1024
+ ip -n $ns route add 2001:db8:10::/64 dev dummy1 metric 1025
+ ip -n $ns route del 2001:db8:10::/64 dev dummy1 metric 1025
+
+ fib6_trap_check $ns "2001:db8:10::/64 dev dummy1 metric 1024" false
+ check_err $? "Route not in hardware after deleting higher metric route"
+
+ log_test "IPv6 delete single route not in hardware"
+
+ ip -n $ns route del 2001:db8:10::/64 dev dummy1 metric 1024
+
+ # Test that first route is replaced by next single route in the FIB
+ # node.
+ RET=0
+
+ ip -n $ns route add 2001:db8:10::/64 dev dummy1 metric 1024
+ ip -n $ns route add 2001:db8:10::/64 dev dummy1 metric 1025
+ ip -n $ns route del 2001:db8:10::/64 dev dummy1 metric 1024
+
+ fib6_trap_check $ns "2001:db8:10::/64 dev dummy1 metric 1025" false
+ check_err $? "Route not in hardware after deleting lowest metric route"
+
+ log_test "IPv6 delete single route - replaced by single"
+
+ ip -n $ns route del 2001:db8:10::/64 dev dummy1 metric 1025
+
+ # Test that first route is replaced by next multipath route in the FIB
+ # node.
+ RET=0
+
+ ip -n $ns route add 2001:db8:10::/64 dev dummy1 metric 1024
+ ip -n $ns route add 2001:db8:10::/64 metric 1025 \
+ nexthop via 2001:db8:1::2 dev dummy1 \
+ nexthop via 2001:db8:2::2 dev dummy2
+ ip -n $ns route del 2001:db8:10::/64 dev dummy1 metric 1024
+
+ fib6_trap_check $ns "2001:db8:10::/64 metric 1025" false
+ check_err $? "Route not in hardware after deleting lowest metric route"
+
+ log_test "IPv6 delete single route - replaced by multipath"
+
+ ip -n $ns route del 2001:db8:10::/64 metric 1025
+
+ # Test deletion of a single nexthop from a multipath route.
+ ip -n $ns route add 2001:db8:10::/64 metric 1024 \
+ nexthop via 2001:db8:1::2 dev dummy1 \
+ nexthop via 2001:db8:2::2 dev dummy2
+ ip -n $ns route del 2001:db8:10::/64 metric 1024 \
+ nexthop via 2001:db8:1::2 dev dummy1
+
+ fib6_trap_check $ns "2001:db8:10::/64 metric 1024" false
+ check_err $? "Route not in hardware after deleting a single nexthop"
+
+ log_test "IPv6 delete single nexthop"
+
+ ip -n $ns route del 2001:db8:10::/64 metric 1024
+
+ for i in $(seq 1 2); do
+ ip -n $ns link del dev dummy$i
+ done
+}
+
+fib_ipv6_delete_multipath_test()
+{
+ local ns=$1; shift
+
+ # Test various deletion scenarios, where an entire multipath route is
+ # deleted from the FIB node.
+ for i in $(seq 1 2); do
+ ip -n $ns link add name dummy$i type dummy
+ ip -n $ns link set dev dummy$i up
+ ip -n $ns address add 2001:db8:$i::1/64 dev dummy$i
+ done
+
+ # Test deletion of a multipath route when it is the only route in the
+ # FIB node.
+ RET=0
+
+ ip -n $ns route add 2001:db8:10::/64 metric 1024 \
+ nexthop via 2001:db8:1::2 dev dummy1 \
+ nexthop via 2001:db8:2::2 dev dummy2
+ ip -n $ns route del 2001:db8:10::/64 metric 1024
+
+ log_test "IPv6 delete sole multipath route"
+
+ # Test that deletion of last route does not affect the first one.
+ RET=0
+
+ ip -n $ns route add 2001:db8:10::/64 metric 1024 \
+ nexthop via 2001:db8:1::2 dev dummy1 \
+ nexthop via 2001:db8:2::2 dev dummy2
+ ip -n $ns route add 2001:db8:10::/64 metric 1025 \
+ nexthop via 2001:db8:1::2 dev dummy1 \
+ nexthop via 2001:db8:2::2 dev dummy2
+ ip -n $ns route del 2001:db8:10::/64 metric 1025
+
+ fib6_trap_check $ns "2001:db8:10::/64 metric 1024" false
+ check_err $? "Route not in hardware after deleting higher metric route"
+
+ log_test "IPv6 delete multipath route not in hardware"
+
+ ip -n $ns route del 2001:db8:10::/64 metric 1024
+
+ # Test that first route is replaced by next single route in the FIB
+ # node.
+ RET=0
+
+ ip -n $ns route add 2001:db8:10::/64 metric 1024 \
+ nexthop via 2001:db8:1::2 dev dummy1 \
+ nexthop via 2001:db8:2::2 dev dummy2
+ ip -n $ns route add 2001:db8:10::/64 dev dummy1 metric 1025
+ ip -n $ns route del 2001:db8:10::/64 metric 1024
+
+ fib6_trap_check $ns "2001:db8:10::/64 dev dummy1 metric 1025" false
+ check_err $? "Route not in hardware after deleting lowest metric route"
+
+ log_test "IPv6 delete multipath route - replaced by single"
+
+ ip -n $ns route del 2001:db8:10::/64 dev dummy1 metric 1025
+
+ # Test that first route is replaced by next multipath route in the FIB
+ # node.
+ RET=0
+
+ ip -n $ns route add 2001:db8:10::/64 metric 1024 \
+ nexthop via 2001:db8:1::2 dev dummy1 \
+ nexthop via 2001:db8:2::2 dev dummy2
+ ip -n $ns route add 2001:db8:10::/64 metric 1025 \
+ nexthop via 2001:db8:1::2 dev dummy1 \
+ nexthop via 2001:db8:2::2 dev dummy2
+ ip -n $ns route del 2001:db8:10::/64 metric 1024
+
+ fib6_trap_check $ns "2001:db8:10::/64 metric 1025" false
+ check_err $? "Route not in hardware after deleting lowest metric route"
+
+ log_test "IPv6 delete multipath route - replaced by multipath"
+
+ ip -n $ns route del 2001:db8:10::/64 metric 1025
+
+ for i in $(seq 1 2); do
+ ip -n $ns link del dev dummy$i
+ done
+}
+
+fib_ipv6_replay_single_test()
+{
+ local ns=$1; shift
+ local devlink_dev=$1; shift
+
+ RET=0
+
+ for i in $(seq 1 2); do
+ ip -n $ns link add name dummy$i type dummy
+ ip -n $ns link set dev dummy$i up
+ done
+
+ ip -n $ns route add 2001:db8:1::/64 dev dummy1
+ ip -n $ns route append 2001:db8:1::/64 dev dummy2
+
+ devlink -N $ns dev reload $devlink_dev
+
+ fib6_trap_check $ns "2001:db8:1::/64 dev dummy1" false
+ check_err $? "First route not in hardware when should"
+
+ fib6_trap_check $ns "2001:db8:1::/64 dev dummy2" true
+ check_err $? "Second route in hardware when should not"
+
+ log_test "IPv6 routes replay - single route"
+
+ for i in $(seq 1 2); do
+ ip -n $ns link del dev dummy$i
+ done
+}
+
+fib_ipv6_replay_multipath_test()
+{
+ local ns=$1; shift
+ local devlink_dev=$1; shift
+
+ RET=0
+
+ for i in $(seq 1 2); do
+ ip -n $ns link add name dummy$i type dummy
+ ip -n $ns link set dev dummy$i up
+ ip -n $ns address add 2001:db8:$i::1/64 dev dummy$i
+ done
+
+ ip -n $ns route add 2001:db8:10::/64 metric 1024 \
+ nexthop via 2001:db8:1::2 dev dummy1 \
+ nexthop via 2001:db8:2::2 dev dummy2
+ ip -n $ns route add 2001:db8:10::/64 metric 1025 \
+ nexthop via 2001:db8:1::2 dev dummy1 \
+ nexthop via 2001:db8:2::2 dev dummy2
+
+ devlink -N $ns dev reload $devlink_dev
+
+ fib6_trap_check $ns "2001:db8:10::/64 metric 1024" false
+ check_err $? "First route not in hardware when should"
+
+ fib6_trap_check $ns "2001:db8:10::/64 metric 1025" true
+ check_err $? "Second route in hardware when should not"
+
+ log_test "IPv6 routes replay - multipath route"
+
+ for i in $(seq 1 2); do
+ ip -n $ns link del dev dummy$i
+ done
+}
diff --git a/tools/testing/selftests/net/forwarding/lib.sh b/tools/testing/selftests/net/forwarding/lib.sh
index 9385dc971269..2f5da414aaa7 100644
--- a/tools/testing/selftests/net/forwarding/lib.sh
+++ b/tools/testing/selftests/net/forwarding/lib.sh
@@ -18,6 +18,8 @@ NETIF_CREATE=${NETIF_CREATE:=yes}
MCD=${MCD:=smcrouted}
MC_CLI=${MC_CLI:=smcroutectl}
PING_TIMEOUT=${PING_TIMEOUT:=5}
+WAIT_TIMEOUT=${WAIT_TIMEOUT:=20}
+INTERFACE_TIMEOUT=${INTERFACE_TIMEOUT:=600}
relative_path="${BASH_SOURCE%/*}"
if [[ "$relative_path" == "${BASH_SOURCE}" ]]; then
@@ -223,33 +225,119 @@ log_info()
echo "INFO: $msg"
}
+busywait()
+{
+ local timeout=$1; shift
+
+ local start_time="$(date -u +%s%3N)"
+ while true
+ do
+ local out
+ out=$("$@")
+ local ret=$?
+ if ((!ret)); then
+ echo -n "$out"
+ return 0
+ fi
+
+ local current_time="$(date -u +%s%3N)"
+ if ((current_time - start_time > timeout)); then
+ echo -n "$out"
+ return 1
+ fi
+ done
+}
+
+until_counter_is()
+{
+ local value=$1; shift
+ local current=$("$@")
+
+ echo $((current))
+ ((current >= value))
+}
+
+busywait_for_counter()
+{
+ local timeout=$1; shift
+ local delta=$1; shift
+
+ local base=$("$@")
+ busywait "$timeout" until_counter_is $((base + delta)) "$@"
+}
+
setup_wait_dev()
{
local dev=$1; shift
+ local wait_time=${1:-$WAIT_TIME}; shift
+
+ setup_wait_dev_with_timeout "$dev" $INTERFACE_TIMEOUT $wait_time
+
+ if (($?)); then
+ check_err 1
+ log_test setup_wait_dev ": Interface $dev does not come up."
+ exit 1
+ fi
+}
+
+setup_wait_dev_with_timeout()
+{
+ local dev=$1; shift
+ local max_iterations=${1:-$WAIT_TIMEOUT}; shift
+ local wait_time=${1:-$WAIT_TIME}; shift
+ local i
- while true; do
+ for ((i = 1; i <= $max_iterations; ++i)); do
ip link show dev $dev up \
| grep 'state UP' &> /dev/null
if [[ $? -ne 0 ]]; then
sleep 1
else
- break
+ sleep $wait_time
+ return 0
fi
done
+
+ return 1
}
setup_wait()
{
local num_netifs=${1:-$NUM_NETIFS}
+ local i
for ((i = 1; i <= num_netifs; ++i)); do
- setup_wait_dev ${NETIFS[p$i]}
+ setup_wait_dev ${NETIFS[p$i]} 0
done
# Make sure links are ready.
sleep $WAIT_TIME
}
+cmd_jq()
+{
+ local cmd=$1
+ local jq_exp=$2
+ local jq_opts=$3
+ local ret
+ local output
+
+ output="$($cmd)"
+ # it the command fails, return error right away
+ ret=$?
+ if [[ $ret -ne 0 ]]; then
+ return $ret
+ fi
+ output=$(echo $output | jq -r $jq_opts "$jq_exp")
+ ret=$?
+ if [[ $ret -ne 0 ]]; then
+ return $ret
+ fi
+ echo $output
+ # return success only in case of non-empty output
+ [ ! -z "$output" ]
+}
+
lldpad_app_wait_set()
{
local dev=$1; shift
@@ -505,9 +593,10 @@ tc_rule_stats_get()
local dev=$1; shift
local pref=$1; shift
local dir=$1; shift
+ local selector=${1:-.packets}; shift
tc -j -s filter show dev $dev ${dir:-ingress} pref $pref \
- | jq '.[1].options.actions[].stats.packets'
+ | jq ".[1].options.actions[].stats$selector"
}
ethtool_stats_get()
@@ -518,6 +607,30 @@ ethtool_stats_get()
ethtool -S $dev | grep "^ *$stat:" | head -n 1 | cut -d: -f2
}
+humanize()
+{
+ local speed=$1; shift
+
+ for unit in bps Kbps Mbps Gbps; do
+ if (($(echo "$speed < 1024" | bc))); then
+ break
+ fi
+
+ speed=$(echo "scale=1; $speed / 1024" | bc)
+ done
+
+ echo "$speed${unit}"
+}
+
+rate()
+{
+ local t0=$1; shift
+ local t1=$1; shift
+ local interval=$1; shift
+
+ echo $((8 * (t1 - t0) / interval))
+}
+
mac_get()
{
local if_name=$1
@@ -1018,3 +1131,21 @@ flood_test()
flood_unicast_test $br_port $host1_if $host2_if
flood_multicast_test $br_port $host1_if $host2_if
}
+
+start_traffic()
+{
+ local h_in=$1; shift # Where the traffic egresses the host
+ local sip=$1; shift
+ local dip=$1; shift
+ local dmac=$1; shift
+
+ $MZ $h_in -p 8000 -A $sip -B $dip -c 0 \
+ -a own -b $dmac -t udp -q &
+ sleep 1
+}
+
+stop_traffic()
+{
+ # Suppress noise from killing mausezahn.
+ { kill %% && wait %%; } 2>/dev/null
+}
diff --git a/tools/testing/selftests/net/forwarding/loopback.sh b/tools/testing/selftests/net/forwarding/loopback.sh
index 6e4626ae71b0..8f4057310b5b 100755
--- a/tools/testing/selftests/net/forwarding/loopback.sh
+++ b/tools/testing/selftests/net/forwarding/loopback.sh
@@ -1,6 +1,9 @@
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
+# Kselftest framework requirement - SKIP code is 4.
+ksft_skip=4
+
ALL_TESTS="loopback_test"
NUM_NETIFS=2
source tc_common.sh
@@ -72,6 +75,11 @@ setup_prepare()
h1_create
h2_create
+
+ if ethtool -k $h1 | grep loopback | grep -q fixed; then
+ log_test "SKIP: dev $h1 does not support loopback feature"
+ exit $ksft_skip
+ fi
}
cleanup()
diff --git a/tools/testing/selftests/net/forwarding/router.sh b/tools/testing/selftests/net/forwarding/router.sh
index a75cb51cc5bd..057f91b05098 100755
--- a/tools/testing/selftests/net/forwarding/router.sh
+++ b/tools/testing/selftests/net/forwarding/router.sh
@@ -1,9 +1,23 @@
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
-ALL_TESTS="ping_ipv4 ping_ipv6"
+ALL_TESTS="
+ ping_ipv4
+ ping_ipv6
+ sip_in_class_e
+ mc_mac_mismatch
+ ipv4_sip_equal_dip
+ ipv6_sip_equal_dip
+ ipv4_dip_link_local
+"
+
NUM_NETIFS=4
source lib.sh
+source tc_common.sh
+
+require_command $MCD
+require_command $MC_CLI
+table_name=selftests
h1_create()
{
@@ -64,6 +78,8 @@ router_create()
ip link set dev $rp1 up
ip link set dev $rp2 up
+ tc qdisc add dev $rp2 clsact
+
ip address add 192.0.2.1/24 dev $rp1
ip address add 2001:db8:1::1/64 dev $rp1
@@ -79,10 +95,31 @@ router_destroy()
ip address del 2001:db8:1::1/64 dev $rp1
ip address del 192.0.2.1/24 dev $rp1
+ tc qdisc del dev $rp2 clsact
+
ip link set dev $rp2 down
ip link set dev $rp1 down
}
+start_mcd()
+{
+ SMCROUTEDIR="$(mktemp -d)"
+
+ for ((i = 1; i <= $NUM_NETIFS; ++i)); do
+ echo "phyint ${NETIFS[p$i]} enable" >> \
+ $SMCROUTEDIR/$table_name.conf
+ done
+
+ $MCD -N -I $table_name -f $SMCROUTEDIR/$table_name.conf \
+ -P $SMCROUTEDIR/$table_name.pid
+}
+
+kill_mcd()
+{
+ pkill $MCD
+ rm -rf $SMCROUTEDIR
+}
+
setup_prepare()
{
h1=${NETIFS[p1]}
@@ -91,6 +128,10 @@ setup_prepare()
rp2=${NETIFS[p3]}
h2=${NETIFS[p4]}
+ rp1mac=$(mac_get $rp1)
+
+ start_mcd
+
vrf_prepare
h1_create
@@ -113,6 +154,8 @@ cleanup()
h1_destroy
vrf_cleanup
+
+ kill_mcd
}
ping_ipv4()
@@ -125,6 +168,150 @@ ping_ipv6()
ping6_test $h1 2001:db8:2::2
}
+sip_in_class_e()
+{
+ RET=0
+
+ # Disable rpfilter to prevent packets to be dropped because of it.
+ sysctl_set net.ipv4.conf.all.rp_filter 0
+ sysctl_set net.ipv4.conf.$rp1.rp_filter 0
+
+ tc filter add dev $rp2 egress protocol ip pref 1 handle 101 \
+ flower src_ip 240.0.0.1 ip_proto udp action pass
+
+ $MZ $h1 -t udp "sp=54321,dp=12345" -c 5 -d 1msec \
+ -A 240.0.0.1 -b $rp1mac -B 198.51.100.2 -q
+
+ tc_check_packets "dev $rp2 egress" 101 5
+ check_err $? "Packets were dropped"
+
+ log_test "Source IP in class E"
+
+ tc filter del dev $rp2 egress protocol ip pref 1 handle 101 flower
+ sysctl_restore net.ipv4.conf.$rp1.rp_filter
+ sysctl_restore net.ipv4.conf.all.rp_filter
+}
+
+create_mcast_sg()
+{
+ local if_name=$1; shift
+ local s_addr=$1; shift
+ local mcast=$1; shift
+ local dest_ifs=${@}
+
+ $MC_CLI -I $table_name add $if_name $s_addr $mcast $dest_ifs
+}
+
+delete_mcast_sg()
+{
+ local if_name=$1; shift
+ local s_addr=$1; shift
+ local mcast=$1; shift
+ local dest_ifs=${@}
+
+ $MC_CLI -I $table_name remove $if_name $s_addr $mcast $dest_ifs
+}
+
+__mc_mac_mismatch()
+{
+ local desc=$1; shift
+ local proto=$1; shift
+ local sip=$1; shift
+ local dip=$1; shift
+ local flags=${1:-""}; shift
+ local dmac=01:02:03:04:05:06
+
+ RET=0
+
+ tc filter add dev $rp2 egress protocol $proto pref 1 handle 101 \
+ flower dst_ip $dip action pass
+
+ create_mcast_sg $rp1 $sip $dip $rp2
+
+ $MZ $flags $h1 -t udp "sp=54321,dp=12345" -c 5 -d 1msec -b $dmac \
+ -B $dip -q
+
+ tc_check_packets "dev $rp2 egress" 101 5
+ check_err $? "Packets were dropped"
+
+ log_test "Multicast MAC mismatch: $desc"
+
+ delete_mcast_sg $rp1 $sip $dip $rp2
+ tc filter del dev $rp2 egress protocol $proto pref 1 handle 101 flower
+}
+
+mc_mac_mismatch()
+{
+ __mc_mac_mismatch "IPv4" "ip" 192.0.2.2 225.1.2.3
+ __mc_mac_mismatch "IPv6" "ipv6" 2001:db8:1::2 ff0e::3 "-6"
+}
+
+ipv4_sip_equal_dip()
+{
+ RET=0
+
+ # Disable rpfilter to prevent packets to be dropped because of it.
+ sysctl_set net.ipv4.conf.all.rp_filter 0
+ sysctl_set net.ipv4.conf.$rp1.rp_filter 0
+
+ tc filter add dev $rp2 egress protocol ip pref 1 handle 101 \
+ flower src_ip 198.51.100.2 action pass
+
+ $MZ $h1 -t udp "sp=54321,dp=12345" -c 5 -d 1msec \
+ -A 198.51.100.2 -b $rp1mac -B 198.51.100.2 -q
+
+ tc_check_packets "dev $rp2 egress" 101 5
+ check_err $? "Packets were dropped"
+
+ log_test "Source IP is equal to destination IP: IPv4"
+
+ tc filter del dev $rp2 egress protocol ip pref 1 handle 101 flower
+ sysctl_restore net.ipv4.conf.$rp1.rp_filter
+ sysctl_restore net.ipv4.conf.all.rp_filter
+}
+
+ipv6_sip_equal_dip()
+{
+ RET=0
+
+ tc filter add dev $rp2 egress protocol ipv6 pref 1 handle 101 \
+ flower src_ip 2001:db8:2::2 action pass
+
+ $MZ -6 $h1 -t udp "sp=54321,dp=12345" -c 5 -d 1msec \
+ -A 2001:db8:2::2 -b $rp1mac -B 2001:db8:2::2 -q
+
+ tc_check_packets "dev $rp2 egress" 101 5
+ check_err $? "Packets were dropped"
+
+ log_test "Source IP is equal to destination IP: IPv6"
+
+ tc filter del dev $rp2 egress protocol ipv6 pref 1 handle 101 flower
+}
+
+ipv4_dip_link_local()
+{
+ local dip=169.254.1.1
+
+ RET=0
+
+ tc filter add dev $rp2 egress protocol ip pref 1 handle 101 \
+ flower dst_ip $dip action pass
+
+ ip neigh add 169.254.1.1 lladdr 00:11:22:33:44:55 dev $rp2
+ ip route add 169.254.1.0/24 dev $rp2
+
+ $MZ $h1 -t udp "sp=54321,dp=12345" -c 5 -d 1msec -b $rp1mac -B $dip -q
+
+ tc_check_packets "dev $rp2 egress" 101 5
+ check_err $? "Packets were dropped"
+
+ log_test "IPv4 destination IP is link-local"
+
+ ip route del 169.254.1.0/24 dev $rp2
+ ip neigh del 169.254.1.1 lladdr 00:11:22:33:44:55 dev $rp2
+ tc filter del dev $rp2 egress protocol ip pref 1 handle 101 flower
+}
+
trap cleanup EXIT
setup_prepare
diff --git a/tools/testing/selftests/net/forwarding/router_bridge_vlan.sh b/tools/testing/selftests/net/forwarding/router_bridge_vlan.sh
index fef88eb4b873..fa6a88c50750 100755
--- a/tools/testing/selftests/net/forwarding/router_bridge_vlan.sh
+++ b/tools/testing/selftests/net/forwarding/router_bridge_vlan.sh
@@ -36,7 +36,7 @@ h2_destroy()
{
ip -6 route del 2001:db8:1::/64 vrf v$h2
ip -4 route del 192.0.2.0/28 vrf v$h2
- simple_if_fini $h2 192.0.2.130/28
+ simple_if_fini $h2 192.0.2.130/28 2001:db8:2::2/64
}
router_create()
diff --git a/tools/testing/selftests/net/forwarding/sch_ets.sh b/tools/testing/selftests/net/forwarding/sch_ets.sh
new file mode 100755
index 000000000000..40e0ad1bc4f2
--- /dev/null
+++ b/tools/testing/selftests/net/forwarding/sch_ets.sh
@@ -0,0 +1,44 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+# A driver for the ETS selftest that implements testing in slowpath.
+lib_dir=.
+source sch_ets_core.sh
+
+ALL_TESTS="
+ ping_ipv4
+ priomap_mode
+ ets_test_strict
+ ets_test_mixed
+ ets_test_dwrr
+ classifier_mode
+ ets_test_strict
+ ets_test_mixed
+ ets_test_dwrr
+"
+
+switch_create()
+{
+ ets_switch_create
+
+ # Create a bottleneck so that the DWRR process can kick in.
+ tc qdisc add dev $swp2 root handle 1: tbf \
+ rate 1Gbit burst 1Mbit latency 100ms
+ PARENT="parent 1:"
+}
+
+switch_destroy()
+{
+ ets_switch_destroy
+ tc qdisc del dev $swp2 root
+}
+
+# Callback from sch_ets_tests.sh
+get_stats()
+{
+ local stream=$1; shift
+
+ link_stats_get $h2.1$stream rx bytes
+}
+
+ets_run
diff --git a/tools/testing/selftests/net/forwarding/sch_ets_core.sh b/tools/testing/selftests/net/forwarding/sch_ets_core.sh
new file mode 100644
index 000000000000..f906fcc66572
--- /dev/null
+++ b/tools/testing/selftests/net/forwarding/sch_ets_core.sh
@@ -0,0 +1,300 @@
+# SPDX-License-Identifier: GPL-2.0
+
+# This is a template for ETS Qdisc test.
+#
+# This test sends from H1 several traffic streams with 802.1p-tagged packets.
+# The tags are used at $swp1 to prioritize the traffic. Each stream is then
+# queued at a different ETS band according to the assigned priority. After
+# runnig for a while, counters at H2 are consulted to determine whether the
+# traffic scheduling was according to the ETS configuration.
+#
+# This template is supposed to be embedded by a test driver, which implements
+# statistics collection, any HW-specific stuff, and prominently configures the
+# system to assure that there is overcommitment at $swp2. That is necessary so
+# that the ETS traffic selection algorithm kicks in and has to schedule some
+# traffic at the expense of other.
+#
+# A driver for veth-based testing is in sch_ets.sh, an example of a driver for
+# an offloaded data path is in selftests/drivers/net/mlxsw/sch_ets.sh.
+#
+# +---------------------------------------------------------------------+
+# | H1 |
+# | + $h1.10 + $h1.11 + $h1.12 |
+# | | 192.0.2.1/28 | 192.0.2.17/28 | 192.0.2.33/28 |
+# | | egress-qos-map | egress-qos-map | egress-qos-map |
+# | | 0:0 | 0:1 | 0:2 |
+# | \____________________ | ____________________/ |
+# | \|/ |
+# | + $h1 |
+# +---------------------------|-----------------------------------------+
+# |
+# +---------------------------|-----------------------------------------+
+# | SW + $swp1 |
+# | | >1Gbps |
+# | ____________________/|\____________________ |
+# | / | \ |
+# | +--|----------------+ +--|----------------+ +--|----------------+ |
+# | | + $swp1.10 | | + $swp1.11 | | + $swp1.12 | |
+# | | ingress-qos-map| | ingress-qos-map| | ingress-qos-map| |
+# | | 0:0 1:1 2:2 | | 0:0 1:1 2:2 | | 0:0 1:1 2:2 | |
+# | | | | | | | |
+# | | BR10 | | BR11 | | BR12 | |
+# | | | | | | | |
+# | | + $swp2.10 | | + $swp2.11 | | + $swp2.12 | |
+# | +--|----------------+ +--|----------------+ +--|----------------+ |
+# | \____________________ | ____________________/ |
+# | \|/ |
+# | + $swp2 |
+# | | 1Gbps (ethtool or HTB qdisc) |
+# | | qdisc ets quanta $W0 $W1 $W2 |
+# | | priomap 0 1 2 |
+# +---------------------------|-----------------------------------------+
+# |
+# +---------------------------|-----------------------------------------+
+# | H2 + $h2 |
+# | ____________________/|\____________________ |
+# | / | \ |
+# | + $h2.10 + $h2.11 + $h2.12 |
+# | 192.0.2.2/28 192.0.2.18/28 192.0.2.34/28 |
+# +---------------------------------------------------------------------+
+
+NUM_NETIFS=4
+CHECK_TC=yes
+source $lib_dir/lib.sh
+source $lib_dir/sch_ets_tests.sh
+
+PARENT=root
+QDISC_DEV=
+
+sip()
+{
+ echo 192.0.2.$((16 * $1 + 1))
+}
+
+dip()
+{
+ echo 192.0.2.$((16 * $1 + 2))
+}
+
+# Callback from sch_ets_tests.sh
+ets_start_traffic()
+{
+ local dst_mac=$(mac_get $h2)
+ local i=$1; shift
+
+ start_traffic $h1.1$i $(sip $i) $(dip $i) $dst_mac
+}
+
+ETS_CHANGE_QDISC=
+
+priomap_mode()
+{
+ echo "Running in priomap mode"
+ ets_delete_qdisc
+ ETS_CHANGE_QDISC=ets_change_qdisc_priomap
+}
+
+classifier_mode()
+{
+ echo "Running in classifier mode"
+ ets_delete_qdisc
+ ETS_CHANGE_QDISC=ets_change_qdisc_classifier
+}
+
+ets_change_qdisc_priomap()
+{
+ local dev=$1; shift
+ local nstrict=$1; shift
+ local priomap=$1; shift
+ local quanta=("${@}")
+
+ local op=$(if [[ -n $QDISC_DEV ]]; then echo change; else echo add; fi)
+
+ tc qdisc $op dev $dev $PARENT handle 10: ets \
+ $(if ((nstrict)); then echo strict $nstrict; fi) \
+ $(if ((${#quanta[@]})); then echo quanta ${quanta[@]}; fi) \
+ priomap $priomap
+ QDISC_DEV=$dev
+}
+
+ets_change_qdisc_classifier()
+{
+ local dev=$1; shift
+ local nstrict=$1; shift
+ local priomap=$1; shift
+ local quanta=("${@}")
+
+ local op=$(if [[ -n $QDISC_DEV ]]; then echo change; else echo add; fi)
+
+ tc qdisc $op dev $dev $PARENT handle 10: ets \
+ $(if ((nstrict)); then echo strict $nstrict; fi) \
+ $(if ((${#quanta[@]})); then echo quanta ${quanta[@]}; fi)
+
+ if [[ $op == add ]]; then
+ local prio=0
+ local band
+
+ for band in $priomap; do
+ tc filter add dev $dev parent 10: basic \
+ match "meta(priority eq $prio)" \
+ flowid 10:$((band + 1))
+ ((prio++))
+ done
+ fi
+ QDISC_DEV=$dev
+}
+
+# Callback from sch_ets_tests.sh
+ets_change_qdisc()
+{
+ if [[ -z "$ETS_CHANGE_QDISC" ]]; then
+ exit 1
+ fi
+ $ETS_CHANGE_QDISC "$@"
+}
+
+ets_delete_qdisc()
+{
+ if [[ -n $QDISC_DEV ]]; then
+ tc qdisc del dev $QDISC_DEV $PARENT
+ QDISC_DEV=
+ fi
+}
+
+h1_create()
+{
+ local i;
+
+ simple_if_init $h1
+ mtu_set $h1 9900
+ for i in {0..2}; do
+ vlan_create $h1 1$i v$h1 $(sip $i)/28
+ ip link set dev $h1.1$i type vlan egress 0:$i
+ done
+}
+
+h1_destroy()
+{
+ local i
+
+ for i in {0..2}; do
+ vlan_destroy $h1 1$i
+ done
+ mtu_restore $h1
+ simple_if_fini $h1
+}
+
+h2_create()
+{
+ local i
+
+ simple_if_init $h2
+ mtu_set $h2 9900
+ for i in {0..2}; do
+ vlan_create $h2 1$i v$h2 $(dip $i)/28
+ done
+}
+
+h2_destroy()
+{
+ local i
+
+ for i in {0..2}; do
+ vlan_destroy $h2 1$i
+ done
+ mtu_restore $h2
+ simple_if_fini $h2
+}
+
+ets_switch_create()
+{
+ local i
+
+ ip link set dev $swp1 up
+ mtu_set $swp1 9900
+
+ ip link set dev $swp2 up
+ mtu_set $swp2 9900
+
+ for i in {0..2}; do
+ vlan_create $swp1 1$i
+ ip link set dev $swp1.1$i type vlan ingress 0:0 1:1 2:2
+
+ vlan_create $swp2 1$i
+
+ ip link add dev br1$i type bridge
+ ip link set dev $swp1.1$i master br1$i
+ ip link set dev $swp2.1$i master br1$i
+
+ ip link set dev br1$i up
+ ip link set dev $swp1.1$i up
+ ip link set dev $swp2.1$i up
+ done
+}
+
+ets_switch_destroy()
+{
+ local i
+
+ ets_delete_qdisc
+
+ for i in {0..2}; do
+ ip link del dev br1$i
+ vlan_destroy $swp2 1$i
+ vlan_destroy $swp1 1$i
+ done
+
+ mtu_restore $swp2
+ ip link set dev $swp2 down
+
+ mtu_restore $swp1
+ ip link set dev $swp1 down
+}
+
+setup_prepare()
+{
+ h1=${NETIFS[p1]}
+ swp1=${NETIFS[p2]}
+
+ swp2=${NETIFS[p3]}
+ h2=${NETIFS[p4]}
+
+ put=$swp2
+ hut=$h2
+
+ vrf_prepare
+
+ h1_create
+ h2_create
+ switch_create
+}
+
+cleanup()
+{
+ pre_cleanup
+
+ switch_destroy
+ h2_destroy
+ h1_destroy
+
+ vrf_cleanup
+}
+
+ping_ipv4()
+{
+ ping_test $h1.10 $(dip 0) " vlan 10"
+ ping_test $h1.11 $(dip 1) " vlan 11"
+ ping_test $h1.12 $(dip 2) " vlan 12"
+}
+
+ets_run()
+{
+ trap cleanup EXIT
+
+ setup_prepare
+ setup_wait
+
+ tests_run
+
+ exit $EXIT_STATUS
+}
diff --git a/tools/testing/selftests/net/forwarding/sch_ets_tests.sh b/tools/testing/selftests/net/forwarding/sch_ets_tests.sh
new file mode 100644
index 000000000000..3c3b204d47e8
--- /dev/null
+++ b/tools/testing/selftests/net/forwarding/sch_ets_tests.sh
@@ -0,0 +1,227 @@
+# SPDX-License-Identifier: GPL-2.0
+
+# Global interface:
+# $put -- port under test (e.g. $swp2)
+# get_stats($band) -- A function to collect stats for band
+# ets_start_traffic($band) -- Start traffic for this band
+# ets_change_qdisc($op, $dev, $nstrict, $quanta...) -- Add or change qdisc
+
+# WS describes the Qdisc configuration. It has one value per band (so the
+# number of array elements indicates the number of bands). If the value is
+# 0, it is a strict band, otherwise the it's a DRR band and the value is
+# that band's quantum.
+declare -a WS
+
+qdisc_describe()
+{
+ local nbands=${#WS[@]}
+ local nstrict=0
+ local i
+
+ for ((i = 0; i < nbands; i++)); do
+ if ((!${WS[$i]})); then
+ : $((nstrict++))
+ fi
+ done
+
+ echo -n "ets bands $nbands"
+ if ((nstrict)); then
+ echo -n " strict $nstrict"
+ fi
+ if ((nstrict < nbands)); then
+ echo -n " quanta"
+ for ((i = nstrict; i < nbands; i++)); do
+ echo -n " ${WS[$i]}"
+ done
+ fi
+}
+
+__strict_eval()
+{
+ local desc=$1; shift
+ local d=$1; shift
+ local total=$1; shift
+ local above=$1; shift
+
+ RET=0
+
+ if ((! total)); then
+ check_err 1 "No traffic observed"
+ log_test "$desc"
+ return
+ fi
+
+ local ratio=$(echo "scale=2; 100 * $d / $total" | bc -l)
+ if ((above)); then
+ test $(echo "$ratio > 95.0" | bc -l) -eq 1
+ check_err $? "Not enough traffic"
+ log_test "$desc"
+ log_info "Expected ratio >95% Measured ratio $ratio"
+ else
+ test $(echo "$ratio < 5" | bc -l) -eq 1
+ check_err $? "Too much traffic"
+ log_test "$desc"
+ log_info "Expected ratio <5% Measured ratio $ratio"
+ fi
+}
+
+strict_eval()
+{
+ __strict_eval "$@" 1
+}
+
+notraf_eval()
+{
+ __strict_eval "$@" 0
+}
+
+__ets_dwrr_test()
+{
+ local -a streams=("$@")
+
+ local low_stream=${streams[0]}
+ local seen_strict=0
+ local -a t0 t1 d
+ local stream
+ local total
+ local i
+
+ echo "Testing $(qdisc_describe), streams ${streams[@]}"
+
+ for stream in ${streams[@]}; do
+ ets_start_traffic $stream
+ done
+
+ sleep 10
+
+ t0=($(for stream in ${streams[@]}; do
+ get_stats $stream
+ done))
+
+ sleep 10
+
+ t1=($(for stream in ${streams[@]}; do
+ get_stats $stream
+ done))
+ d=($(for ((i = 0; i < ${#streams[@]}; i++)); do
+ echo $((${t1[$i]} - ${t0[$i]}))
+ done))
+ total=$(echo ${d[@]} | sed 's/ /+/g' | bc)
+
+ for ((i = 0; i < ${#streams[@]}; i++)); do
+ local stream=${streams[$i]}
+ if ((seen_strict)); then
+ notraf_eval "band $stream" ${d[$i]} $total
+ elif ((${WS[$stream]} == 0)); then
+ strict_eval "band $stream" ${d[$i]} $total
+ seen_strict=1
+ elif ((stream == low_stream)); then
+ # Low stream is used as DWRR evaluation reference.
+ continue
+ else
+ multipath_eval "bands $low_stream:$stream" \
+ ${WS[$low_stream]} ${WS[$stream]} \
+ ${d[0]} ${d[$i]}
+ fi
+ done
+
+ for stream in ${streams[@]}; do
+ stop_traffic
+ done
+}
+
+ets_dwrr_test_012()
+{
+ __ets_dwrr_test 0 1 2
+}
+
+ets_dwrr_test_01()
+{
+ __ets_dwrr_test 0 1
+}
+
+ets_dwrr_test_12()
+{
+ __ets_dwrr_test 1 2
+}
+
+ets_qdisc_setup()
+{
+ local dev=$1; shift
+ local nstrict=$1; shift
+ local -a quanta=("$@")
+
+ local ndwrr=${#quanta[@]}
+ local nbands=$((nstrict + ndwrr))
+ local nstreams=$(if ((nbands > 3)); then echo 3; else echo $nbands; fi)
+ local priomap=$(seq 0 $((nstreams - 1)))
+ local i
+
+ WS=($(
+ for ((i = 0; i < nstrict; i++)); do
+ echo 0
+ done
+ for ((i = 0; i < ndwrr; i++)); do
+ echo ${quanta[$i]}
+ done
+ ))
+
+ ets_change_qdisc $dev $nstrict "$priomap" ${quanta[@]}
+}
+
+ets_set_dwrr_uniform()
+{
+ ets_qdisc_setup $put 0 3300 3300 3300
+}
+
+ets_set_dwrr_varying()
+{
+ ets_qdisc_setup $put 0 5000 3500 1500
+}
+
+ets_set_strict()
+{
+ ets_qdisc_setup $put 3
+}
+
+ets_set_mixed()
+{
+ ets_qdisc_setup $put 1 5000 2500 1500
+}
+
+ets_change_quantum()
+{
+ tc class change dev $put classid 10:2 ets quantum 8000
+ WS[1]=8000
+}
+
+ets_set_dwrr_two_bands()
+{
+ ets_qdisc_setup $put 0 5000 2500
+}
+
+ets_test_strict()
+{
+ ets_set_strict
+ ets_dwrr_test_01
+ ets_dwrr_test_12
+}
+
+ets_test_mixed()
+{
+ ets_set_mixed
+ ets_dwrr_test_01
+ ets_dwrr_test_12
+}
+
+ets_test_dwrr()
+{
+ ets_set_dwrr_uniform
+ ets_dwrr_test_012
+ ets_set_dwrr_varying
+ ets_dwrr_test_012
+ ets_change_quantum
+ ets_dwrr_test_012
+ ets_set_dwrr_two_bands
+ ets_dwrr_test_01
+}
diff --git a/tools/testing/selftests/net/forwarding/sch_tbf_core.sh b/tools/testing/selftests/net/forwarding/sch_tbf_core.sh
new file mode 100644
index 000000000000..d1f26cb7cd73
--- /dev/null
+++ b/tools/testing/selftests/net/forwarding/sch_tbf_core.sh
@@ -0,0 +1,233 @@
+# SPDX-License-Identifier: GPL-2.0
+
+# This test sends a stream of traffic from H1 through a switch, to H2. On the
+# egress port from the switch ($swp2), a shaper is installed. The test verifies
+# that the rates on the port match the configured shaper.
+#
+# In order to test per-class shaping, $swp2 actually contains TBF under PRIO or
+# ETS, with two different configurations. Traffic is prioritized using 802.1p.
+#
+# +-------------------------------------------+
+# | H1 |
+# | + $h1.10 $h1.11 + |
+# | | 192.0.2.1/28 192.0.2.17/28 | |
+# | | | |
+# | \______________ _____________/ |
+# | \ / |
+# | + $h1 |
+# +---------------------|---------------------+
+# |
+# +---------------------|---------------------+
+# | SW + $swp1 |
+# | _______________/ \_______________ |
+# | / \ |
+# | +-|--------------+ +--------------|-+ |
+# | | + $swp1.10 | | $swp1.11 + | |
+# | | | | | |
+# | | BR10 | | BR11 | |
+# | | | | | |
+# | | + $swp2.10 | | $swp2.11 + | |
+# | +-|--------------+ +--------------|-+ |
+# | \_______________ ______________/ |
+# | \ / |
+# | + $swp2 |
+# +---------------------|---------------------+
+# |
+# +---------------------|---------------------+
+# | H2 + $h2 |
+# | ______________/ \______________ |
+# | / \ |
+# | | | |
+# | + $h2.10 $h2.11 + |
+# | 192.0.2.2/28 192.0.2.18/28 |
+# +-------------------------------------------+
+
+NUM_NETIFS=4
+CHECK_TC="yes"
+source $lib_dir/lib.sh
+
+ipaddr()
+{
+ local host=$1; shift
+ local vlan=$1; shift
+
+ echo 192.0.2.$((16 * (vlan - 10) + host))
+}
+
+host_create()
+{
+ local dev=$1; shift
+ local host=$1; shift
+
+ simple_if_init $dev
+ mtu_set $dev 10000
+
+ vlan_create $dev 10 v$dev $(ipaddr $host 10)/28
+ ip link set dev $dev.10 type vlan egress 0:0
+
+ vlan_create $dev 11 v$dev $(ipaddr $host 11)/28
+ ip link set dev $dev.11 type vlan egress 0:1
+}
+
+host_destroy()
+{
+ local dev=$1; shift
+
+ vlan_destroy $dev 11
+ vlan_destroy $dev 10
+ mtu_restore $dev
+ simple_if_fini $dev
+}
+
+h1_create()
+{
+ host_create $h1 1
+}
+
+h1_destroy()
+{
+ host_destroy $h1
+}
+
+h2_create()
+{
+ host_create $h2 2
+
+ tc qdisc add dev $h2 clsact
+ tc filter add dev $h2 ingress pref 1010 prot 802.1q \
+ flower $TCFLAGS vlan_id 10 action pass
+ tc filter add dev $h2 ingress pref 1011 prot 802.1q \
+ flower $TCFLAGS vlan_id 11 action pass
+}
+
+h2_destroy()
+{
+ tc qdisc del dev $h2 clsact
+ host_destroy $h2
+}
+
+switch_create()
+{
+ local intf
+ local vlan
+
+ ip link add dev br10 type bridge
+ ip link add dev br11 type bridge
+
+ for intf in $swp1 $swp2; do
+ ip link set dev $intf up
+ mtu_set $intf 10000
+
+ for vlan in 10 11; do
+ vlan_create $intf $vlan
+ ip link set dev $intf.$vlan master br$vlan
+ ip link set dev $intf.$vlan up
+ done
+ done
+
+ for vlan in 10 11; do
+ ip link set dev $swp1.$vlan type vlan ingress 0:0 1:1
+ done
+
+ ip link set dev br10 up
+ ip link set dev br11 up
+}
+
+switch_destroy()
+{
+ local intf
+ local vlan
+
+ # A test may have been interrupted mid-run, with Qdisc installed. Delete
+ # it here.
+ tc qdisc del dev $swp2 root 2>/dev/null
+
+ ip link set dev br11 down
+ ip link set dev br10 down
+
+ for intf in $swp2 $swp1; do
+ for vlan in 11 10; do
+ ip link set dev $intf.$vlan down
+ ip link set dev $intf.$vlan nomaster
+ vlan_destroy $intf $vlan
+ done
+
+ mtu_restore $intf
+ ip link set dev $intf down
+ done
+
+ ip link del dev br11
+ ip link del dev br10
+}
+
+setup_prepare()
+{
+ h1=${NETIFS[p1]}
+ swp1=${NETIFS[p2]}
+
+ swp2=${NETIFS[p3]}
+ h2=${NETIFS[p4]}
+
+ swp3=${NETIFS[p5]}
+ h3=${NETIFS[p6]}
+
+ swp4=${NETIFS[p7]}
+ swp5=${NETIFS[p8]}
+
+ h2_mac=$(mac_get $h2)
+
+ vrf_prepare
+
+ h1_create
+ h2_create
+ switch_create
+}
+
+cleanup()
+{
+ pre_cleanup
+
+ switch_destroy
+ h2_destroy
+ h1_destroy
+
+ vrf_cleanup
+}
+
+ping_ipv4()
+{
+ ping_test $h1.10 $(ipaddr 2 10) " vlan 10"
+ ping_test $h1.11 $(ipaddr 2 11) " vlan 11"
+}
+
+tbf_get_counter()
+{
+ local vlan=$1; shift
+
+ tc_rule_stats_get $h2 10$vlan ingress .bytes
+}
+
+do_tbf_test()
+{
+ local vlan=$1; shift
+ local mbit=$1; shift
+
+ start_traffic $h1.$vlan $(ipaddr 1 $vlan) $(ipaddr 2 $vlan) $h2_mac
+ sleep 5 # Wait for the burst to dwindle
+
+ local t2=$(busywait_for_counter 1000 +1 tbf_get_counter $vlan)
+ sleep 10
+ local t3=$(tbf_get_counter $vlan)
+ stop_traffic
+
+ RET=0
+
+ # Note: TBF uses 10^6 Mbits, not 2^20 ones.
+ local er=$((mbit * 1000 * 1000))
+ local nr=$(rate $t2 $t3 10)
+ local nr_pct=$((100 * (nr - er) / er))
+ ((-5 <= nr_pct && nr_pct <= 5))
+ check_err $? "Expected rate $(humanize $er), got $(humanize $nr), which is $nr_pct% off. Required accuracy is +-5%."
+
+ log_test "TC $((vlan - 10)): TBF rate ${mbit}Mbit"
+}
diff --git a/tools/testing/selftests/net/forwarding/sch_tbf_ets.sh b/tools/testing/selftests/net/forwarding/sch_tbf_ets.sh
new file mode 100755
index 000000000000..84fb6cab88e4
--- /dev/null
+++ b/tools/testing/selftests/net/forwarding/sch_tbf_ets.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+QDISC="ets strict"
+: ${lib_dir:=.}
+source $lib_dir/sch_tbf_etsprio.sh
diff --git a/tools/testing/selftests/net/forwarding/sch_tbf_etsprio.sh b/tools/testing/selftests/net/forwarding/sch_tbf_etsprio.sh
new file mode 100644
index 000000000000..8bd85da1905a
--- /dev/null
+++ b/tools/testing/selftests/net/forwarding/sch_tbf_etsprio.sh
@@ -0,0 +1,39 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+ALL_TESTS="
+ ping_ipv4
+ tbf_test
+"
+source $lib_dir/sch_tbf_core.sh
+
+tbf_test_one()
+{
+ local bs=$1; shift
+
+ tc qdisc replace dev $swp2 parent 10:3 handle 103: tbf \
+ rate 400Mbit burst $bs limit 1M
+ tc qdisc replace dev $swp2 parent 10:2 handle 102: tbf \
+ rate 800Mbit burst $bs limit 1M
+
+ do_tbf_test 10 400 $bs
+ do_tbf_test 11 800 $bs
+}
+
+tbf_test()
+{
+ # This test is used for both ETS and PRIO. Even though we only need two
+ # bands, PRIO demands a minimum of three.
+ tc qdisc add dev $swp2 root handle 10: $QDISC 3 priomap 2 1 0
+ tbf_test_one 128K
+ tc qdisc del dev $swp2 root
+}
+
+trap cleanup EXIT
+
+setup_prepare
+setup_wait
+
+tests_run
+
+exit $EXIT_STATUS
diff --git a/tools/testing/selftests/net/forwarding/sch_tbf_prio.sh b/tools/testing/selftests/net/forwarding/sch_tbf_prio.sh
new file mode 100755
index 000000000000..9c8cb1cb9ba4
--- /dev/null
+++ b/tools/testing/selftests/net/forwarding/sch_tbf_prio.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+QDISC="prio bands"
+: ${lib_dir:=.}
+source $lib_dir/sch_tbf_etsprio.sh
diff --git a/tools/testing/selftests/net/forwarding/sch_tbf_root.sh b/tools/testing/selftests/net/forwarding/sch_tbf_root.sh
new file mode 100755
index 000000000000..72aa21ba88c7
--- /dev/null
+++ b/tools/testing/selftests/net/forwarding/sch_tbf_root.sh
@@ -0,0 +1,33 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+ALL_TESTS="
+ ping_ipv4
+ tbf_test
+"
+: ${lib_dir:=.}
+source $lib_dir/sch_tbf_core.sh
+
+tbf_test_one()
+{
+ local bs=$1; shift
+
+ tc qdisc replace dev $swp2 root handle 108: tbf \
+ rate 400Mbit burst $bs limit 1M
+ do_tbf_test 10 400 $bs
+}
+
+tbf_test()
+{
+ tbf_test_one 128K
+ tc qdisc del dev $swp2 root
+}
+
+trap cleanup EXIT
+
+setup_prepare
+setup_wait
+
+tests_run
+
+exit $EXIT_STATUS
diff --git a/tools/testing/selftests/net/forwarding/tc_common.sh b/tools/testing/selftests/net/forwarding/tc_common.sh
index 9d3b64a2a264..64f652633585 100644
--- a/tools/testing/selftests/net/forwarding/tc_common.sh
+++ b/tools/testing/selftests/net/forwarding/tc_common.sh
@@ -3,23 +3,48 @@
CHECK_TC="yes"
+# Can be overridden by the configuration file. See lib.sh
+TC_HIT_TIMEOUT=${TC_HIT_TIMEOUT:=1000} # ms
+
+__tc_check_packets()
+{
+ local id=$1
+ local handle=$2
+ local count=$3
+ local operator=$4
+
+ start_time="$(date -u +%s%3N)"
+ while true
+ do
+ cmd_jq "tc -j -s filter show $id" \
+ ".[] | select(.options.handle == $handle) | \
+ select(.options.actions[0].stats.packets $operator $count)" \
+ &> /dev/null
+ ret=$?
+ if [[ $ret -eq 0 ]]; then
+ return $ret
+ fi
+ current_time="$(date -u +%s%3N)"
+ diff=$(expr $current_time - $start_time)
+ if [ "$diff" -gt "$TC_HIT_TIMEOUT" ]; then
+ return 1
+ fi
+ done
+}
+
tc_check_packets()
{
local id=$1
local handle=$2
local count=$3
- local ret
-
- output="$(tc -j -s filter show $id)"
- # workaround the jq bug which causes jq to return 0 in case input is ""
- ret=$?
- if [[ $ret -ne 0 ]]; then
- return $ret
- fi
- echo $output | \
- jq -e ".[] \
- | select(.options.handle == $handle) \
- | select(.options.actions[0].stats.packets == $count)" \
- &> /dev/null
- return $?
+
+ __tc_check_packets "$id" "$handle" "$count" "=="
+}
+
+tc_check_packets_hitting()
+{
+ local id=$1
+ local handle=$2
+
+ __tc_check_packets "$id" "$handle" 0 ">"
}
diff --git a/tools/testing/selftests/net/l2tp.sh b/tools/testing/selftests/net/l2tp.sh
new file mode 100755
index 000000000000..5782433886fc
--- /dev/null
+++ b/tools/testing/selftests/net/l2tp.sh
@@ -0,0 +1,382 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# L2TPv3 tunnel between 2 hosts
+#
+# host-1 | router | host-2
+# | |
+# lo l2tp | | l2tp lo
+# 172.16.101.1 172.16.1.1 | | 172.16.1.2 172.16.101.2
+# fc00:101::1 fc00:1::1 | | fc00:1::2 fc00:101::2
+# | |
+# eth0 | | eth0
+# 10.1.1.1 | | 10.1.2.1
+# 2001:db8:1::1 | | 2001:db8:2::1
+
+VERBOSE=0
+PAUSE_ON_FAIL=no
+
+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()
+{
+ local ns
+ local cmd
+ local out
+ local rc
+
+ ns="$1"
+ shift
+ cmd="$*"
+
+ if [ "$VERBOSE" = "1" ]; then
+ printf " COMMAND: $cmd\n"
+ fi
+
+ out=$(eval ip netns exec ${ns} ${cmd} 2>&1)
+ rc=$?
+ if [ "$VERBOSE" = "1" -a -n "$out" ]; then
+ echo " $out"
+ fi
+
+ [ "$VERBOSE" = "1" ] && echo
+
+ return $rc
+}
+
+################################################################################
+# create namespaces and interconnects
+
+create_ns()
+{
+ local ns=$1
+ local addr=$2
+ local addr6=$3
+
+ [ -z "${addr}" ] && addr="-"
+ [ -z "${addr6}" ] && addr6="-"
+
+ 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
+ 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
+
+ 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
+}
+
+################################################################################
+# test setup
+
+cleanup()
+{
+ local ns
+
+ for ns in host-1 host-2 router
+ do
+ ip netns del ${ns} 2>/dev/null
+ done
+}
+
+setup_l2tp_ipv4()
+{
+ #
+ # configure l2tpv3 tunnel on host-1
+ #
+ ip -netns host-1 l2tp add tunnel tunnel_id 1041 peer_tunnel_id 1042 \
+ encap ip local 10.1.1.1 remote 10.1.2.1
+ ip -netns host-1 l2tp add session name l2tp4 tunnel_id 1041 \
+ session_id 1041 peer_session_id 1042
+ ip -netns host-1 link set dev l2tp4 up
+ ip -netns host-1 addr add dev l2tp4 172.16.1.1 peer 172.16.1.2
+
+ #
+ # configure l2tpv3 tunnel on host-2
+ #
+ ip -netns host-2 l2tp add tunnel tunnel_id 1042 peer_tunnel_id 1041 \
+ encap ip local 10.1.2.1 remote 10.1.1.1
+ ip -netns host-2 l2tp add session name l2tp4 tunnel_id 1042 \
+ session_id 1042 peer_session_id 1041
+ ip -netns host-2 link set dev l2tp4 up
+ ip -netns host-2 addr add dev l2tp4 172.16.1.2 peer 172.16.1.1
+
+ #
+ # add routes to loopback addresses
+ #
+ ip -netns host-1 ro add 172.16.101.2/32 via 172.16.1.2
+ ip -netns host-2 ro add 172.16.101.1/32 via 172.16.1.1
+}
+
+setup_l2tp_ipv6()
+{
+ #
+ # configure l2tpv3 tunnel on host-1
+ #
+ ip -netns host-1 l2tp add tunnel tunnel_id 1061 peer_tunnel_id 1062 \
+ encap ip local 2001:db8:1::1 remote 2001:db8:2::1
+ ip -netns host-1 l2tp add session name l2tp6 tunnel_id 1061 \
+ session_id 1061 peer_session_id 1062
+ ip -netns host-1 link set dev l2tp6 up
+ ip -netns host-1 addr add dev l2tp6 fc00:1::1 peer fc00:1::2
+
+ #
+ # configure l2tpv3 tunnel on host-2
+ #
+ ip -netns host-2 l2tp add tunnel tunnel_id 1062 peer_tunnel_id 1061 \
+ encap ip local 2001:db8:2::1 remote 2001:db8:1::1
+ ip -netns host-2 l2tp add session name l2tp6 tunnel_id 1062 \
+ session_id 1062 peer_session_id 1061
+ ip -netns host-2 link set dev l2tp6 up
+ ip -netns host-2 addr add dev l2tp6 fc00:1::2 peer fc00:1::1
+
+ #
+ # add routes to loopback addresses
+ #
+ ip -netns host-1 -6 ro add fc00:101::2/128 via fc00:1::2
+ ip -netns host-2 -6 ro add fc00:101::1/128 via fc00:1::1
+}
+
+setup()
+{
+ # start clean
+ cleanup
+
+ set -e
+ create_ns host-1 172.16.101.1/32 fc00:101::1/128
+ create_ns host-2 172.16.101.2/32 fc00:101::2/128
+ create_ns router
+
+ connect_ns host-1 eth0 10.1.1.1/24 2001:db8:1::1/64 \
+ router eth1 10.1.1.2/24 2001:db8:1::2/64
+
+ connect_ns host-2 eth0 10.1.2.1/24 2001:db8:2::1/64 \
+ router eth2 10.1.2.2/24 2001:db8:2::2/64
+
+ ip -netns host-1 ro add 10.1.2.0/24 via 10.1.1.2
+ ip -netns host-1 -6 ro add 2001:db8:2::/64 via 2001:db8:1::2
+
+ ip -netns host-2 ro add 10.1.1.0/24 via 10.1.2.2
+ ip -netns host-2 -6 ro add 2001:db8:1::/64 via 2001:db8:2::2
+
+ setup_l2tp_ipv4
+ setup_l2tp_ipv6
+ set +e
+}
+
+setup_ipsec()
+{
+ #
+ # IPv4
+ #
+ run_cmd host-1 ip xfrm policy add \
+ src 10.1.1.1 dst 10.1.2.1 dir out \
+ tmpl proto esp mode transport
+
+ run_cmd host-1 ip xfrm policy add \
+ src 10.1.2.1 dst 10.1.1.1 dir in \
+ tmpl proto esp mode transport
+
+ run_cmd host-2 ip xfrm policy add \
+ src 10.1.1.1 dst 10.1.2.1 dir in \
+ tmpl proto esp mode transport
+
+ run_cmd host-2 ip xfrm policy add \
+ src 10.1.2.1 dst 10.1.1.1 dir out \
+ tmpl proto esp mode transport
+
+ ip -netns host-1 xfrm state add \
+ src 10.1.1.1 dst 10.1.2.1 \
+ spi 0x1000 proto esp aead 'rfc4106(gcm(aes))' \
+ 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f 128 mode transport
+
+ ip -netns host-1 xfrm state add \
+ src 10.1.2.1 dst 10.1.1.1 \
+ spi 0x1001 proto esp aead 'rfc4106(gcm(aes))' \
+ 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f 128 mode transport
+
+ ip -netns host-2 xfrm state add \
+ src 10.1.1.1 dst 10.1.2.1 \
+ spi 0x1000 proto esp aead 'rfc4106(gcm(aes))' \
+ 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f 128 mode transport
+
+ ip -netns host-2 xfrm state add \
+ src 10.1.2.1 dst 10.1.1.1 \
+ spi 0x1001 proto esp aead 'rfc4106(gcm(aes))' \
+ 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f 128 mode transport
+
+ #
+ # IPV6
+ #
+ run_cmd host-1 ip -6 xfrm policy add \
+ src 2001:db8:1::1 dst 2001:db8:2::1 dir out \
+ tmpl proto esp mode transport
+
+ run_cmd host-1 ip -6 xfrm policy add \
+ src 2001:db8:2::1 dst 2001:db8:1::1 dir in \
+ tmpl proto esp mode transport
+
+ run_cmd host-2 ip -6 xfrm policy add \
+ src 2001:db8:1::1 dst 2001:db8:2::1 dir in \
+ tmpl proto esp mode transport
+
+ run_cmd host-2 ip -6 xfrm policy add \
+ src 2001:db8:2::1 dst 2001:db8:1::1 dir out \
+ tmpl proto esp mode transport
+
+ ip -netns host-1 -6 xfrm state add \
+ src 2001:db8:1::1 dst 2001:db8:2::1 \
+ spi 0x1000 proto esp aead 'rfc4106(gcm(aes))' \
+ 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f 128 mode transport
+
+ ip -netns host-1 -6 xfrm state add \
+ src 2001:db8:2::1 dst 2001:db8:1::1 \
+ spi 0x1001 proto esp aead 'rfc4106(gcm(aes))' \
+ 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f 128 mode transport
+
+ ip -netns host-2 -6 xfrm state add \
+ src 2001:db8:1::1 dst 2001:db8:2::1 \
+ spi 0x1000 proto esp aead 'rfc4106(gcm(aes))' \
+ 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f 128 mode transport
+
+ ip -netns host-2 -6 xfrm state add \
+ src 2001:db8:2::1 dst 2001:db8:1::1 \
+ spi 0x1001 proto esp aead 'rfc4106(gcm(aes))' \
+ 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f 128 mode transport
+}
+
+teardown_ipsec()
+{
+ run_cmd host-1 ip xfrm state flush
+ run_cmd host-1 ip xfrm policy flush
+ run_cmd host-2 ip xfrm state flush
+ run_cmd host-2 ip xfrm policy flush
+}
+
+################################################################################
+# generate traffic through tunnel for various cases
+
+run_ping()
+{
+ local desc="$1"
+
+ run_cmd host-1 ping -c1 -w1 172.16.1.2
+ log_test $? 0 "IPv4 basic L2TP tunnel ${desc}"
+
+ run_cmd host-1 ping -c1 -w1 -I 172.16.101.1 172.16.101.2
+ log_test $? 0 "IPv4 route through L2TP tunnel ${desc}"
+
+ run_cmd host-1 ${ping6} -c1 -w1 fc00:1::2
+ log_test $? 0 "IPv6 basic L2TP tunnel ${desc}"
+
+ run_cmd host-1 ${ping6} -c1 -w1 -I fc00:101::1 fc00:101::2
+ log_test $? 0 "IPv6 route through L2TP tunnel ${desc}"
+}
+
+run_tests()
+{
+ local desc
+
+ setup
+ run_ping
+
+ setup_ipsec
+ run_ping "- with IPsec"
+ run_cmd host-1 ping -c1 -w1 172.16.1.2
+ log_test $? 0 "IPv4 basic L2TP tunnel ${desc}"
+
+ run_cmd host-1 ping -c1 -w1 -I 172.16.101.1 172.16.101.2
+ log_test $? 0 "IPv4 route through L2TP tunnel ${desc}"
+
+ run_cmd host-1 ${ping6} -c1 -w1 fc00:1::2
+ log_test $? 0 "IPv6 basic L2TP tunnel - with IPsec"
+
+ run_cmd host-1 ${ping6} -c1 -w1 -I fc00:101::1 fc00:101::2
+ log_test $? 0 "IPv6 route through L2TP tunnel - with IPsec"
+
+ teardown_ipsec
+ run_ping "- after IPsec teardown"
+}
+
+################################################################################
+# main
+
+declare -i nfail=0
+declare -i nsuccess=0
+
+while getopts :pv o
+do
+ case $o in
+ p) PAUSE_ON_FAIL=yes;;
+ v) VERBOSE=$(($VERBOSE + 1));;
+ *) exit 1;;
+ esac
+done
+
+run_tests
+cleanup
+
+printf "\nTests passed: %3d\n" ${nsuccess}
+printf "Tests failed: %3d\n" ${nfail}
diff --git a/tools/testing/selftests/net/mptcp/.gitignore b/tools/testing/selftests/net/mptcp/.gitignore
new file mode 100644
index 000000000000..d72f07642738
--- /dev/null
+++ b/tools/testing/selftests/net/mptcp/.gitignore
@@ -0,0 +1,2 @@
+mptcp_connect
+*.pcap
diff --git a/tools/testing/selftests/net/mptcp/Makefile b/tools/testing/selftests/net/mptcp/Makefile
new file mode 100644
index 000000000000..93de52016dde
--- /dev/null
+++ b/tools/testing/selftests/net/mptcp/Makefile
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0
+
+top_srcdir = ../../../../..
+
+CFLAGS = -Wall -Wl,--no-as-needed -O2 -g
+
+TEST_PROGS := mptcp_connect.sh
+
+TEST_GEN_FILES = mptcp_connect
+
+EXTRA_CLEAN := *.pcap
+
+include ../../lib.mk
diff --git a/tools/testing/selftests/net/mptcp/config b/tools/testing/selftests/net/mptcp/config
new file mode 100644
index 000000000000..2499824d9e1c
--- /dev/null
+++ b/tools/testing/selftests/net/mptcp/config
@@ -0,0 +1,4 @@
+CONFIG_MPTCP=y
+CONFIG_MPTCP_IPV6=y
+CONFIG_VETH=y
+CONFIG_NET_SCH_NETEM=m
diff --git a/tools/testing/selftests/net/mptcp/mptcp_connect.c b/tools/testing/selftests/net/mptcp/mptcp_connect.c
new file mode 100644
index 000000000000..99579c0223c1
--- /dev/null
+++ b/tools/testing/selftests/net/mptcp/mptcp_connect.c
@@ -0,0 +1,841 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#define _GNU_SOURCE
+
+#include <errno.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+
+#include <sys/poll.h>
+#include <sys/sendfile.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+
+#include <netdb.h>
+#include <netinet/in.h>
+
+#include <linux/tcp.h>
+
+extern int optind;
+
+#ifndef IPPROTO_MPTCP
+#define IPPROTO_MPTCP 262
+#endif
+#ifndef TCP_ULP
+#define TCP_ULP 31
+#endif
+
+static bool listen_mode;
+static int poll_timeout;
+
+enum cfg_mode {
+ CFG_MODE_POLL,
+ CFG_MODE_MMAP,
+ CFG_MODE_SENDFILE,
+};
+
+static enum cfg_mode cfg_mode = CFG_MODE_POLL;
+static const char *cfg_host;
+static const char *cfg_port = "12000";
+static int cfg_sock_proto = IPPROTO_MPTCP;
+static bool tcpulp_audit;
+static int pf = AF_INET;
+static int cfg_sndbuf;
+
+static void die_usage(void)
+{
+ fprintf(stderr, "Usage: mptcp_connect [-6] [-u] [-s MPTCP|TCP] [-p port] -m mode]"
+ "[ -l ] [ -t timeout ] connect_address\n");
+ exit(1);
+}
+
+static const char *getxinfo_strerr(int err)
+{
+ if (err == EAI_SYSTEM)
+ return strerror(errno);
+
+ return gai_strerror(err);
+}
+
+static void xgetnameinfo(const struct sockaddr *addr, socklen_t addrlen,
+ char *host, socklen_t hostlen,
+ char *serv, socklen_t servlen)
+{
+ int flags = NI_NUMERICHOST | NI_NUMERICSERV;
+ int err = getnameinfo(addr, addrlen, host, hostlen, serv, servlen,
+ flags);
+
+ if (err) {
+ const char *errstr = getxinfo_strerr(err);
+
+ fprintf(stderr, "Fatal: getnameinfo: %s\n", errstr);
+ exit(1);
+ }
+}
+
+static void xgetaddrinfo(const char *node, const char *service,
+ const struct addrinfo *hints,
+ struct addrinfo **res)
+{
+ int err = getaddrinfo(node, service, hints, res);
+
+ if (err) {
+ const char *errstr = getxinfo_strerr(err);
+
+ fprintf(stderr, "Fatal: getaddrinfo(%s:%s): %s\n",
+ node ? node : "", service ? service : "", errstr);
+ exit(1);
+ }
+}
+
+static void set_sndbuf(int fd, unsigned int size)
+{
+ int err;
+
+ err = setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size));
+ if (err) {
+ perror("set SO_SNDBUF");
+ exit(1);
+ }
+}
+
+static int sock_listen_mptcp(const char * const listenaddr,
+ const char * const port)
+{
+ int sock;
+ struct addrinfo hints = {
+ .ai_protocol = IPPROTO_TCP,
+ .ai_socktype = SOCK_STREAM,
+ .ai_flags = AI_PASSIVE | AI_NUMERICHOST
+ };
+
+ hints.ai_family = pf;
+
+ struct addrinfo *a, *addr;
+ int one = 1;
+
+ xgetaddrinfo(listenaddr, port, &hints, &addr);
+ hints.ai_family = pf;
+
+ for (a = addr; a; a = a->ai_next) {
+ sock = socket(a->ai_family, a->ai_socktype, cfg_sock_proto);
+ if (sock < 0)
+ continue;
+
+ if (-1 == setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one,
+ sizeof(one)))
+ perror("setsockopt");
+
+ if (bind(sock, a->ai_addr, a->ai_addrlen) == 0)
+ break; /* success */
+
+ perror("bind");
+ close(sock);
+ sock = -1;
+ }
+
+ freeaddrinfo(addr);
+
+ if (sock < 0) {
+ fprintf(stderr, "Could not create listen socket\n");
+ return sock;
+ }
+
+ if (listen(sock, 20)) {
+ perror("listen");
+ close(sock);
+ return -1;
+ }
+
+ return sock;
+}
+
+static bool sock_test_tcpulp(const char * const remoteaddr,
+ const char * const port)
+{
+ struct addrinfo hints = {
+ .ai_protocol = IPPROTO_TCP,
+ .ai_socktype = SOCK_STREAM,
+ };
+ struct addrinfo *a, *addr;
+ int sock = -1, ret = 0;
+ bool test_pass = false;
+
+ hints.ai_family = AF_INET;
+
+ xgetaddrinfo(remoteaddr, port, &hints, &addr);
+ for (a = addr; a; a = a->ai_next) {
+ sock = socket(a->ai_family, a->ai_socktype, IPPROTO_TCP);
+ if (sock < 0) {
+ perror("socket");
+ continue;
+ }
+ ret = setsockopt(sock, IPPROTO_TCP, TCP_ULP, "mptcp",
+ sizeof("mptcp"));
+ if (ret == -1 && errno == EOPNOTSUPP)
+ test_pass = true;
+ close(sock);
+
+ if (test_pass)
+ break;
+ if (!ret)
+ fprintf(stderr,
+ "setsockopt(TCP_ULP) returned 0\n");
+ else
+ perror("setsockopt(TCP_ULP)");
+ }
+ return test_pass;
+}
+
+static int sock_connect_mptcp(const char * const remoteaddr,
+ const char * const port, int proto)
+{
+ struct addrinfo hints = {
+ .ai_protocol = IPPROTO_TCP,
+ .ai_socktype = SOCK_STREAM,
+ };
+ struct addrinfo *a, *addr;
+ int sock = -1;
+
+ hints.ai_family = pf;
+
+ xgetaddrinfo(remoteaddr, port, &hints, &addr);
+ for (a = addr; a; a = a->ai_next) {
+ sock = socket(a->ai_family, a->ai_socktype, proto);
+ if (sock < 0) {
+ perror("socket");
+ continue;
+ }
+
+ if (connect(sock, a->ai_addr, a->ai_addrlen) == 0)
+ break; /* success */
+
+ perror("connect()");
+ close(sock);
+ sock = -1;
+ }
+
+ freeaddrinfo(addr);
+ return sock;
+}
+
+static size_t do_rnd_write(const int fd, char *buf, const size_t len)
+{
+ unsigned int do_w;
+ ssize_t bw;
+
+ do_w = rand() & 0xffff;
+ if (do_w == 0 || do_w > len)
+ do_w = len;
+
+ bw = write(fd, buf, do_w);
+ if (bw < 0)
+ perror("write");
+
+ return bw;
+}
+
+static size_t do_write(const int fd, char *buf, const size_t len)
+{
+ size_t offset = 0;
+
+ while (offset < len) {
+ size_t written;
+ ssize_t bw;
+
+ bw = write(fd, buf + offset, len - offset);
+ if (bw < 0) {
+ perror("write");
+ return 0;
+ }
+
+ written = (size_t)bw;
+ offset += written;
+ }
+
+ return offset;
+}
+
+static ssize_t do_rnd_read(const int fd, char *buf, const size_t len)
+{
+ size_t cap = rand();
+
+ cap &= 0xffff;
+
+ if (cap == 0)
+ cap = 1;
+ else if (cap > len)
+ cap = len;
+
+ return read(fd, buf, cap);
+}
+
+static void set_nonblock(int fd)
+{
+ int flags = fcntl(fd, F_GETFL);
+
+ if (flags == -1)
+ return;
+
+ fcntl(fd, F_SETFL, flags | O_NONBLOCK);
+}
+
+static int copyfd_io_poll(int infd, int peerfd, int outfd)
+{
+ struct pollfd fds = {
+ .fd = peerfd,
+ .events = POLLIN | POLLOUT,
+ };
+ unsigned int woff = 0, wlen = 0;
+ char wbuf[8192];
+
+ set_nonblock(peerfd);
+
+ for (;;) {
+ char rbuf[8192];
+ ssize_t len;
+
+ if (fds.events == 0)
+ break;
+
+ switch (poll(&fds, 1, poll_timeout)) {
+ case -1:
+ if (errno == EINTR)
+ continue;
+ perror("poll");
+ return 1;
+ case 0:
+ fprintf(stderr, "%s: poll timed out (events: "
+ "POLLIN %u, POLLOUT %u)\n", __func__,
+ fds.events & POLLIN, fds.events & POLLOUT);
+ return 2;
+ }
+
+ if (fds.revents & POLLIN) {
+ len = do_rnd_read(peerfd, rbuf, sizeof(rbuf));
+ if (len == 0) {
+ /* no more data to receive:
+ * peer has closed its write side
+ */
+ fds.events &= ~POLLIN;
+
+ if ((fds.events & POLLOUT) == 0)
+ /* and nothing more to send */
+ break;
+
+ /* Else, still have data to transmit */
+ } else if (len < 0) {
+ perror("read");
+ return 3;
+ }
+
+ do_write(outfd, rbuf, len);
+ }
+
+ if (fds.revents & POLLOUT) {
+ if (wlen == 0) {
+ woff = 0;
+ wlen = read(infd, wbuf, sizeof(wbuf));
+ }
+
+ if (wlen > 0) {
+ ssize_t bw;
+
+ bw = do_rnd_write(peerfd, wbuf + woff, wlen);
+ if (bw < 0)
+ return 111;
+
+ woff += bw;
+ wlen -= bw;
+ } else if (wlen == 0) {
+ /* We have no more data to send. */
+ fds.events &= ~POLLOUT;
+
+ if ((fds.events & POLLIN) == 0)
+ /* ... and peer also closed already */
+ break;
+
+ /* ... but we still receive.
+ * Close our write side.
+ */
+ shutdown(peerfd, SHUT_WR);
+ } else {
+ if (errno == EINTR)
+ continue;
+ perror("read");
+ return 4;
+ }
+ }
+
+ if (fds.revents & (POLLERR | POLLNVAL)) {
+ fprintf(stderr, "Unexpected revents: "
+ "POLLERR/POLLNVAL(%x)\n", fds.revents);
+ return 5;
+ }
+ }
+
+ close(peerfd);
+ return 0;
+}
+
+static int do_recvfile(int infd, int outfd)
+{
+ ssize_t r;
+
+ do {
+ char buf[16384];
+
+ r = do_rnd_read(infd, buf, sizeof(buf));
+ if (r > 0) {
+ if (write(outfd, buf, r) != r)
+ break;
+ } else if (r < 0) {
+ perror("read");
+ }
+ } while (r > 0);
+
+ return (int)r;
+}
+
+static int do_mmap(int infd, int outfd, unsigned int size)
+{
+ char *inbuf = mmap(NULL, size, PROT_READ, MAP_SHARED, infd, 0);
+ ssize_t ret = 0, off = 0;
+ size_t rem;
+
+ if (inbuf == MAP_FAILED) {
+ perror("mmap");
+ return 1;
+ }
+
+ rem = size;
+
+ while (rem > 0) {
+ ret = write(outfd, inbuf + off, rem);
+
+ if (ret < 0) {
+ perror("write");
+ break;
+ }
+
+ off += ret;
+ rem -= ret;
+ }
+
+ munmap(inbuf, size);
+ return rem;
+}
+
+static int get_infd_size(int fd)
+{
+ struct stat sb;
+ ssize_t count;
+ int err;
+
+ err = fstat(fd, &sb);
+ if (err < 0) {
+ perror("fstat");
+ return -1;
+ }
+
+ if ((sb.st_mode & S_IFMT) != S_IFREG) {
+ fprintf(stderr, "%s: stdin is not a regular file\n", __func__);
+ return -2;
+ }
+
+ count = sb.st_size;
+ if (count > INT_MAX) {
+ fprintf(stderr, "File too large: %zu\n", count);
+ return -3;
+ }
+
+ return (int)count;
+}
+
+static int do_sendfile(int infd, int outfd, unsigned int count)
+{
+ while (count > 0) {
+ ssize_t r;
+
+ r = sendfile(outfd, infd, NULL, count);
+ if (r < 0) {
+ perror("sendfile");
+ return 3;
+ }
+
+ count -= r;
+ }
+
+ return 0;
+}
+
+static int copyfd_io_mmap(int infd, int peerfd, int outfd,
+ unsigned int size)
+{
+ int err;
+
+ if (listen_mode) {
+ err = do_recvfile(peerfd, outfd);
+ if (err)
+ return err;
+
+ err = do_mmap(infd, peerfd, size);
+ } else {
+ err = do_mmap(infd, peerfd, size);
+ if (err)
+ return err;
+
+ shutdown(peerfd, SHUT_WR);
+
+ err = do_recvfile(peerfd, outfd);
+ }
+
+ return err;
+}
+
+static int copyfd_io_sendfile(int infd, int peerfd, int outfd,
+ unsigned int size)
+{
+ int err;
+
+ if (listen_mode) {
+ err = do_recvfile(peerfd, outfd);
+ if (err)
+ return err;
+
+ err = do_sendfile(infd, peerfd, size);
+ } else {
+ err = do_sendfile(infd, peerfd, size);
+ if (err)
+ return err;
+ err = do_recvfile(peerfd, outfd);
+ }
+
+ return err;
+}
+
+static int copyfd_io(int infd, int peerfd, int outfd)
+{
+ int file_size;
+
+ switch (cfg_mode) {
+ case CFG_MODE_POLL:
+ return copyfd_io_poll(infd, peerfd, outfd);
+ case CFG_MODE_MMAP:
+ file_size = get_infd_size(infd);
+ if (file_size < 0)
+ return file_size;
+ return copyfd_io_mmap(infd, peerfd, outfd, file_size);
+ case CFG_MODE_SENDFILE:
+ file_size = get_infd_size(infd);
+ if (file_size < 0)
+ return file_size;
+ return copyfd_io_sendfile(infd, peerfd, outfd, file_size);
+ }
+
+ fprintf(stderr, "Invalid mode %d\n", cfg_mode);
+
+ die_usage();
+ return 1;
+}
+
+static void check_sockaddr(int pf, struct sockaddr_storage *ss,
+ socklen_t salen)
+{
+ struct sockaddr_in6 *sin6;
+ struct sockaddr_in *sin;
+ socklen_t wanted_size = 0;
+
+ switch (pf) {
+ case AF_INET:
+ wanted_size = sizeof(*sin);
+ sin = (void *)ss;
+ if (!sin->sin_port)
+ fprintf(stderr, "accept: something wrong: ip connection from port 0");
+ break;
+ case AF_INET6:
+ wanted_size = sizeof(*sin6);
+ sin6 = (void *)ss;
+ if (!sin6->sin6_port)
+ fprintf(stderr, "accept: something wrong: ipv6 connection from port 0");
+ break;
+ default:
+ fprintf(stderr, "accept: Unknown pf %d, salen %u\n", pf, salen);
+ return;
+ }
+
+ if (salen != wanted_size)
+ fprintf(stderr, "accept: size mismatch, got %d expected %d\n",
+ (int)salen, wanted_size);
+
+ if (ss->ss_family != pf)
+ fprintf(stderr, "accept: pf mismatch, expect %d, ss_family is %d\n",
+ (int)ss->ss_family, pf);
+}
+
+static void check_getpeername(int fd, struct sockaddr_storage *ss, socklen_t salen)
+{
+ struct sockaddr_storage peerss;
+ socklen_t peersalen = sizeof(peerss);
+
+ if (getpeername(fd, (struct sockaddr *)&peerss, &peersalen) < 0) {
+ perror("getpeername");
+ return;
+ }
+
+ if (peersalen != salen) {
+ fprintf(stderr, "%s: %d vs %d\n", __func__, peersalen, salen);
+ return;
+ }
+
+ if (memcmp(ss, &peerss, peersalen)) {
+ char a[INET6_ADDRSTRLEN];
+ char b[INET6_ADDRSTRLEN];
+ char c[INET6_ADDRSTRLEN];
+ char d[INET6_ADDRSTRLEN];
+
+ xgetnameinfo((struct sockaddr *)ss, salen,
+ a, sizeof(a), b, sizeof(b));
+
+ xgetnameinfo((struct sockaddr *)&peerss, peersalen,
+ c, sizeof(c), d, sizeof(d));
+
+ fprintf(stderr, "%s: memcmp failure: accept %s vs peername %s, %s vs %s salen %d vs %d\n",
+ __func__, a, c, b, d, peersalen, salen);
+ }
+}
+
+static void check_getpeername_connect(int fd)
+{
+ struct sockaddr_storage ss;
+ socklen_t salen = sizeof(ss);
+ char a[INET6_ADDRSTRLEN];
+ char b[INET6_ADDRSTRLEN];
+
+ if (getpeername(fd, (struct sockaddr *)&ss, &salen) < 0) {
+ perror("getpeername");
+ return;
+ }
+
+ xgetnameinfo((struct sockaddr *)&ss, salen,
+ a, sizeof(a), b, sizeof(b));
+
+ if (strcmp(cfg_host, a) || strcmp(cfg_port, b))
+ fprintf(stderr, "%s: %s vs %s, %s vs %s\n", __func__,
+ cfg_host, a, cfg_port, b);
+}
+
+static void maybe_close(int fd)
+{
+ unsigned int r = rand();
+
+ if (r & 1)
+ close(fd);
+}
+
+int main_loop_s(int listensock)
+{
+ struct sockaddr_storage ss;
+ struct pollfd polls;
+ socklen_t salen;
+ int remotesock;
+
+ polls.fd = listensock;
+ polls.events = POLLIN;
+
+ switch (poll(&polls, 1, poll_timeout)) {
+ case -1:
+ perror("poll");
+ return 1;
+ case 0:
+ fprintf(stderr, "%s: timed out\n", __func__);
+ close(listensock);
+ return 2;
+ }
+
+ salen = sizeof(ss);
+ remotesock = accept(listensock, (struct sockaddr *)&ss, &salen);
+ if (remotesock >= 0) {
+ maybe_close(listensock);
+ check_sockaddr(pf, &ss, salen);
+ check_getpeername(remotesock, &ss, salen);
+
+ return copyfd_io(0, remotesock, 1);
+ }
+
+ perror("accept");
+
+ return 1;
+}
+
+static void init_rng(void)
+{
+ int fd = open("/dev/urandom", O_RDONLY);
+ unsigned int foo;
+
+ if (fd > 0) {
+ int ret = read(fd, &foo, sizeof(foo));
+
+ if (ret < 0)
+ srand(fd + foo);
+ close(fd);
+ }
+
+ srand(foo);
+}
+
+int main_loop(void)
+{
+ int fd;
+
+ /* listener is ready. */
+ fd = sock_connect_mptcp(cfg_host, cfg_port, cfg_sock_proto);
+ if (fd < 0)
+ return 2;
+
+ check_getpeername_connect(fd);
+
+ if (cfg_sndbuf)
+ set_sndbuf(fd, cfg_sndbuf);
+
+ return copyfd_io(0, fd, 1);
+}
+
+int parse_proto(const char *proto)
+{
+ if (!strcasecmp(proto, "MPTCP"))
+ return IPPROTO_MPTCP;
+ if (!strcasecmp(proto, "TCP"))
+ return IPPROTO_TCP;
+
+ fprintf(stderr, "Unknown protocol: %s\n.", proto);
+ die_usage();
+
+ /* silence compiler warning */
+ return 0;
+}
+
+int parse_mode(const char *mode)
+{
+ if (!strcasecmp(mode, "poll"))
+ return CFG_MODE_POLL;
+ if (!strcasecmp(mode, "mmap"))
+ return CFG_MODE_MMAP;
+ if (!strcasecmp(mode, "sendfile"))
+ return CFG_MODE_SENDFILE;
+
+ fprintf(stderr, "Unknown test mode: %s\n", mode);
+ fprintf(stderr, "Supported modes are:\n");
+ fprintf(stderr, "\t\t\"poll\" - interleaved read/write using poll()\n");
+ fprintf(stderr, "\t\t\"mmap\" - send entire input file (mmap+write), then read response (-l will read input first)\n");
+ fprintf(stderr, "\t\t\"sendfile\" - send entire input file (sendfile), then read response (-l will read input first)\n");
+
+ die_usage();
+
+ /* silence compiler warning */
+ return 0;
+}
+
+int parse_sndbuf(const char *size)
+{
+ unsigned long s;
+
+ errno = 0;
+
+ s = strtoul(size, NULL, 0);
+
+ if (errno) {
+ fprintf(stderr, "Invalid sndbuf size %s (%s)\n",
+ size, strerror(errno));
+ die_usage();
+ }
+
+ if (s > INT_MAX) {
+ fprintf(stderr, "Invalid sndbuf size %s (%s)\n",
+ size, strerror(ERANGE));
+ die_usage();
+ }
+
+ cfg_sndbuf = s;
+
+ return 0;
+}
+
+static void parse_opts(int argc, char **argv)
+{
+ int c;
+
+ while ((c = getopt(argc, argv, "6lp:s:hut:m:b:")) != -1) {
+ switch (c) {
+ case 'l':
+ listen_mode = true;
+ break;
+ case 'p':
+ cfg_port = optarg;
+ break;
+ case 's':
+ cfg_sock_proto = parse_proto(optarg);
+ break;
+ case 'h':
+ die_usage();
+ break;
+ case 'u':
+ tcpulp_audit = true;
+ break;
+ case '6':
+ pf = AF_INET6;
+ break;
+ case 't':
+ poll_timeout = atoi(optarg) * 1000;
+ if (poll_timeout <= 0)
+ poll_timeout = -1;
+ break;
+ case 'm':
+ cfg_mode = parse_mode(optarg);
+ break;
+ case 'b':
+ cfg_sndbuf = parse_sndbuf(optarg);
+ break;
+ }
+ }
+
+ if (optind + 1 != argc)
+ die_usage();
+ cfg_host = argv[optind];
+
+ if (strchr(cfg_host, ':'))
+ pf = AF_INET6;
+}
+
+int main(int argc, char *argv[])
+{
+ init_rng();
+
+ parse_opts(argc, argv);
+
+ if (tcpulp_audit)
+ return sock_test_tcpulp(cfg_host, cfg_port) ? 0 : 1;
+
+ if (listen_mode) {
+ int fd = sock_listen_mptcp(cfg_host, cfg_port);
+
+ if (fd < 0)
+ return 1;
+
+ if (cfg_sndbuf)
+ set_sndbuf(fd, cfg_sndbuf);
+
+ return main_loop_s(fd);
+ }
+
+ return main_loop();
+}
diff --git a/tools/testing/selftests/net/mptcp/mptcp_connect.sh b/tools/testing/selftests/net/mptcp/mptcp_connect.sh
new file mode 100755
index 000000000000..d573a0feb98d
--- /dev/null
+++ b/tools/testing/selftests/net/mptcp/mptcp_connect.sh
@@ -0,0 +1,595 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+time_start=$(date +%s)
+
+optstring="b:d:e:l:r:h4cm:"
+ret=0
+sin=""
+sout=""
+cin=""
+cout=""
+ksft_skip=4
+capture=false
+timeout=30
+ipv6=true
+ethtool_random_on=true
+tc_delay="$((RANDOM%400))"
+tc_loss=$((RANDOM%101))
+tc_reorder=""
+testmode=""
+sndbuf=0
+options_log=true
+
+if [ $tc_loss -eq 100 ];then
+ tc_loss=1%
+elif [ $tc_loss -ge 10 ]; then
+ tc_loss=0.$tc_loss%
+elif [ $tc_loss -ge 1 ]; then
+ tc_loss=0.0$tc_loss%
+else
+ tc_loss=""
+fi
+
+usage() {
+ echo "Usage: $0 [ -a ]"
+ echo -e "\t-d: tc/netem delay in milliseconds, e.g. \"-d 10\" (default random)"
+ echo -e "\t-l: tc/netem loss percentage, e.g. \"-l 0.02\" (default random)"
+ echo -e "\t-r: tc/netem reorder mode, e.g. \"-r 25% 50% gap 5\", use "-r 0" to disable reordering (default random)"
+ echo -e "\t-e: ethtool features to disable, e.g.: \"-e tso -e gso\" (default: randomly disable any of tso/gso/gro)"
+ echo -e "\t-4: IPv4 only: disable IPv6 tests (default: test both IPv4 and IPv6)"
+ echo -e "\t-c: capture packets for each test using tcpdump (default: no capture)"
+ echo -e "\t-b: set sndbuf value (default: use kernel default)"
+ echo -e "\t-m: test mode (poll, sendfile; default: poll)"
+}
+
+while getopts "$optstring" option;do
+ case "$option" in
+ "h")
+ usage $0
+ exit 0
+ ;;
+ "d")
+ if [ $OPTARG -ge 0 ];then
+ tc_delay="$OPTARG"
+ else
+ echo "-d requires numeric argument, got \"$OPTARG\"" 1>&2
+ exit 1
+ fi
+ ;;
+ "e")
+ ethtool_args="$ethtool_args $OPTARG off"
+ ethtool_random_on=false
+ ;;
+ "l")
+ tc_loss="$OPTARG"
+ ;;
+ "r")
+ tc_reorder="$OPTARG"
+ ;;
+ "4")
+ ipv6=false
+ ;;
+ "c")
+ capture=true
+ ;;
+ "b")
+ if [ $OPTARG -ge 0 ];then
+ sndbuf="$OPTARG"
+ else
+ echo "-s requires numeric argument, got \"$OPTARG\"" 1>&2
+ exit 1
+ fi
+ ;;
+ "m")
+ testmode="$OPTARG"
+ ;;
+ "?")
+ usage $0
+ exit 1
+ ;;
+ esac
+done
+
+sec=$(date +%s)
+rndh=$(printf %x $sec)-$(mktemp -u XXXXXX)
+ns1="ns1-$rndh"
+ns2="ns2-$rndh"
+ns3="ns3-$rndh"
+ns4="ns4-$rndh"
+
+TEST_COUNT=0
+
+cleanup()
+{
+ rm -f "$cin" "$cout"
+ rm -f "$sin" "$sout"
+ rm -f "$capout"
+
+ local netns
+ for netns in "$ns1" "$ns2" "$ns3" "$ns4";do
+ ip netns del $netns
+ done
+}
+
+ip -Version > /dev/null 2>&1
+if [ $? -ne 0 ];then
+ echo "SKIP: Could not run test without ip tool"
+ exit $ksft_skip
+fi
+
+sin=$(mktemp)
+sout=$(mktemp)
+cin=$(mktemp)
+cout=$(mktemp)
+capout=$(mktemp)
+trap cleanup EXIT
+
+for i in "$ns1" "$ns2" "$ns3" "$ns4";do
+ ip netns add $i || exit $ksft_skip
+ ip -net $i link set lo up
+done
+
+# "$ns1" ns2 ns3 ns4
+# ns1eth2 ns2eth1 ns2eth3 ns3eth2 ns3eth4 ns4eth3
+# - drop 1% -> reorder 25%
+# <- TSO off -
+
+ip link add ns1eth2 netns "$ns1" type veth peer name ns2eth1 netns "$ns2"
+ip link add ns2eth3 netns "$ns2" type veth peer name ns3eth2 netns "$ns3"
+ip link add ns3eth4 netns "$ns3" type veth peer name ns4eth3 netns "$ns4"
+
+ip -net "$ns1" addr add 10.0.1.1/24 dev ns1eth2
+ip -net "$ns1" addr add dead:beef:1::1/64 dev ns1eth2 nodad
+
+ip -net "$ns1" link set ns1eth2 up
+ip -net "$ns1" route add default via 10.0.1.2
+ip -net "$ns1" route add default via dead:beef:1::2
+
+ip -net "$ns2" addr add 10.0.1.2/24 dev ns2eth1
+ip -net "$ns2" addr add dead:beef:1::2/64 dev ns2eth1 nodad
+ip -net "$ns2" link set ns2eth1 up
+
+ip -net "$ns2" addr add 10.0.2.1/24 dev ns2eth3
+ip -net "$ns2" addr add dead:beef:2::1/64 dev ns2eth3 nodad
+ip -net "$ns2" link set ns2eth3 up
+ip -net "$ns2" route add default via 10.0.2.2
+ip -net "$ns2" route add default via dead:beef:2::2
+ip netns exec "$ns2" sysctl -q net.ipv4.ip_forward=1
+ip netns exec "$ns2" sysctl -q net.ipv6.conf.all.forwarding=1
+
+ip -net "$ns3" addr add 10.0.2.2/24 dev ns3eth2
+ip -net "$ns3" addr add dead:beef:2::2/64 dev ns3eth2 nodad
+ip -net "$ns3" link set ns3eth2 up
+
+ip -net "$ns3" addr add 10.0.3.2/24 dev ns3eth4
+ip -net "$ns3" addr add dead:beef:3::2/64 dev ns3eth4 nodad
+ip -net "$ns3" link set ns3eth4 up
+ip -net "$ns3" route add default via 10.0.2.1
+ip -net "$ns3" route add default via dead:beef:2::1
+ip netns exec "$ns3" sysctl -q net.ipv4.ip_forward=1
+ip netns exec "$ns3" sysctl -q net.ipv6.conf.all.forwarding=1
+
+ip -net "$ns4" addr add 10.0.3.1/24 dev ns4eth3
+ip -net "$ns4" addr add dead:beef:3::1/64 dev ns4eth3 nodad
+ip -net "$ns4" link set ns4eth3 up
+ip -net "$ns4" route add default via 10.0.3.2
+ip -net "$ns4" route add default via dead:beef:3::2
+
+set_ethtool_flags() {
+ local ns="$1"
+ local dev="$2"
+ local flags="$3"
+
+ ip netns exec $ns ethtool -K $dev $flags 2>/dev/null
+ [ $? -eq 0 ] && echo "INFO: set $ns dev $dev: ethtool -K $flags"
+}
+
+set_random_ethtool_flags() {
+ local flags=""
+ local r=$RANDOM
+
+ local pick1=$((r & 1))
+ local pick2=$((r & 2))
+ local pick3=$((r & 4))
+
+ [ $pick1 -ne 0 ] && flags="tso off"
+ [ $pick2 -ne 0 ] && flags="$flags gso off"
+ [ $pick3 -ne 0 ] && flags="$flags gro off"
+
+ [ -z "$flags" ] && return
+
+ set_ethtool_flags "$1" "$2" "$flags"
+}
+
+if $ethtool_random_on;then
+ set_random_ethtool_flags "$ns3" ns3eth2
+ set_random_ethtool_flags "$ns4" ns4eth3
+else
+ set_ethtool_flags "$ns3" ns3eth2 "$ethtool_args"
+ set_ethtool_flags "$ns4" ns4eth3 "$ethtool_args"
+fi
+
+print_file_err()
+{
+ ls -l "$1" 1>&2
+ echo "Trailing bytes are: "
+ tail -c 27 "$1"
+}
+
+check_transfer()
+{
+ local in=$1
+ local out=$2
+ local what=$3
+
+ cmp "$in" "$out" > /dev/null 2>&1
+ if [ $? -ne 0 ] ;then
+ echo "[ FAIL ] $what does not match (in, out):"
+ print_file_err "$in"
+ print_file_err "$out"
+
+ return 1
+ fi
+
+ return 0
+}
+
+check_mptcp_disabled()
+{
+ local disabled_ns
+ disabled_ns="ns_disabled-$sech-$(mktemp -u XXXXXX)"
+ ip netns add ${disabled_ns} || exit $ksft_skip
+
+ # net.mptcp.enabled should be enabled by default
+ if [ "$(ip netns exec ${disabled_ns} sysctl net.mptcp.enabled | awk '{ print $3 }')" -ne 1 ]; then
+ echo -e "net.mptcp.enabled sysctl is not 1 by default\t\t[ FAIL ]"
+ ret=1
+ return 1
+ fi
+ ip netns exec ${disabled_ns} sysctl -q net.mptcp.enabled=0
+
+ local err=0
+ LANG=C ip netns exec ${disabled_ns} ./mptcp_connect -t $timeout -p 10000 -s MPTCP 127.0.0.1 < "$cin" 2>&1 | \
+ grep -q "^socket: Protocol not available$" && err=1
+ ip netns delete ${disabled_ns}
+
+ if [ ${err} -eq 0 ]; then
+ echo -e "New MPTCP socket cannot be blocked via sysctl\t\t[ FAIL ]"
+ ret=1
+ return 1
+ fi
+
+ echo -e "New MPTCP socket can be blocked via sysctl\t\t[ OK ]"
+ return 0
+}
+
+check_mptcp_ulp_setsockopt()
+{
+ local t retval
+ t="ns_ulp-$sech-$(mktemp -u XXXXXX)"
+
+ ip netns add ${t} || exit $ksft_skip
+ if ! ip netns exec ${t} ./mptcp_connect -u -p 10000 -s TCP 127.0.0.1 2>&1; then
+ printf "setsockopt(..., TCP_ULP, \"mptcp\", ...) allowed\t[ FAIL ]\n"
+ retval=1
+ ret=$retval
+ else
+ printf "setsockopt(..., TCP_ULP, \"mptcp\", ...) blocked\t[ OK ]\n"
+ retval=0
+ fi
+ ip netns del ${t}
+ return $retval
+}
+
+# $1: IP address
+is_v6()
+{
+ [ -z "${1##*:*}" ]
+}
+
+do_ping()
+{
+ local listener_ns="$1"
+ local connector_ns="$2"
+ local connect_addr="$3"
+ local ping_args="-q -c 1"
+
+ if is_v6 "${connect_addr}"; then
+ $ipv6 || return 0
+ ping_args="${ping_args} -6"
+ fi
+
+ ip netns exec ${connector_ns} ping ${ping_args} $connect_addr >/dev/null
+ if [ $? -ne 0 ] ; then
+ echo "$listener_ns -> $connect_addr connectivity [ FAIL ]" 1>&2
+ ret=1
+
+ return 1
+ fi
+
+ return 0
+}
+
+# $1: ns, $2: port
+wait_local_port_listen()
+{
+ local listener_ns="${1}"
+ local port="${2}"
+
+ local port_hex i
+
+ port_hex="$(printf "%04X" "${port}")"
+ for i in $(seq 10); do
+ ip netns exec "${listener_ns}" cat /proc/net/tcp* | \
+ awk "BEGIN {rc=1} {if (\$2 ~ /:${port_hex}\$/ && \$4 ~ /0A/) {rc=0; exit}} END {exit rc}" &&
+ break
+ sleep 0.1
+ done
+}
+
+do_transfer()
+{
+ local listener_ns="$1"
+ local connector_ns="$2"
+ local cl_proto="$3"
+ local srv_proto="$4"
+ local connect_addr="$5"
+ local local_addr="$6"
+ local extra_args=""
+
+ local port
+ port=$((10000+$TEST_COUNT))
+ TEST_COUNT=$((TEST_COUNT+1))
+
+ if [ "$sndbuf" -gt 0 ]; then
+ extra_args="$extra_args -b $sndbuf"
+ fi
+
+ if [ -n "$testmode" ]; then
+ extra_args="$extra_args -m $testmode"
+ fi
+
+ if [ -n "$extra_args" ] && $options_log; then
+ options_log=false
+ echo "INFO: extra options: $extra_args"
+ fi
+
+ :> "$cout"
+ :> "$sout"
+ :> "$capout"
+
+ local addr_port
+ addr_port=$(printf "%s:%d" ${connect_addr} ${port})
+ printf "%.3s %-5s -> %.3s (%-20s) %-5s\t" ${connector_ns} ${cl_proto} ${listener_ns} ${addr_port} ${srv_proto}
+
+ if $capture; then
+ local capuser
+ if [ -z $SUDO_USER ] ; then
+ capuser=""
+ else
+ capuser="-Z $SUDO_USER"
+ fi
+
+ local capfile="${listener_ns}-${connector_ns}-${cl_proto}-${srv_proto}-${connect_addr}.pcap"
+
+ ip netns exec ${listener_ns} tcpdump -i any -s 65535 -B 32768 $capuser -w $capfile > "$capout" 2>&1 &
+ local cappid=$!
+
+ sleep 1
+ fi
+
+ ip netns exec ${listener_ns} ./mptcp_connect -t $timeout -l -p $port -s ${srv_proto} $extra_args $local_addr < "$sin" > "$sout" &
+ local spid=$!
+
+ wait_local_port_listen "${listener_ns}" "${port}"
+
+ local start
+ start=$(date +%s%3N)
+ ip netns exec ${connector_ns} ./mptcp_connect -t $timeout -p $port -s ${cl_proto} $extra_args $connect_addr < "$cin" > "$cout" &
+ local cpid=$!
+
+ wait $cpid
+ local retc=$?
+ wait $spid
+ local rets=$?
+
+ local stop
+ stop=$(date +%s%3N)
+
+ if $capture; then
+ sleep 1
+ kill $cappid
+ fi
+
+ local duration
+ duration=$((stop-start))
+ duration=$(printf "(duration %05sms)" $duration)
+ if [ ${rets} -ne 0 ] || [ ${retc} -ne 0 ]; then
+ echo "$duration [ FAIL ] client exit code $retc, server $rets" 1>&2
+ echo "\nnetns ${listener_ns} socket stat for $port:" 1>&2
+ ip netns exec ${listener_ns} ss -nita 1>&2 -o "sport = :$port"
+ echo "\nnetns ${connector_ns} socket stat for $port:" 1>&2
+ ip netns exec ${connector_ns} ss -nita 1>&2 -o "dport = :$port"
+
+ cat "$capout"
+ return 1
+ fi
+
+ check_transfer $sin $cout "file received by client"
+ retc=$?
+ check_transfer $cin $sout "file received by server"
+ rets=$?
+
+ if [ $retc -eq 0 ] && [ $rets -eq 0 ];then
+ echo "$duration [ OK ]"
+ cat "$capout"
+ return 0
+ fi
+
+ cat "$capout"
+ return 1
+}
+
+make_file()
+{
+ local name=$1
+ local who=$2
+
+ local SIZE TSIZE
+ SIZE=$((RANDOM % (1024 * 8)))
+ TSIZE=$((SIZE * 1024))
+
+ dd if=/dev/urandom of="$name" bs=1024 count=$SIZE 2> /dev/null
+
+ SIZE=$((RANDOM % 1024))
+ SIZE=$((SIZE + 128))
+ TSIZE=$((TSIZE + SIZE))
+ dd if=/dev/urandom conv=notrunc of="$name" bs=1 count=$SIZE 2> /dev/null
+ echo -e "\nMPTCP_TEST_FILE_END_MARKER" >> "$name"
+
+ echo "Created $name (size $TSIZE) containing data sent by $who"
+}
+
+run_tests_lo()
+{
+ local listener_ns="$1"
+ local connector_ns="$2"
+ local connect_addr="$3"
+ local loopback="$4"
+ local lret=0
+
+ # skip if test programs are running inside same netns for subsequent runs.
+ if [ $loopback -eq 0 ] && [ ${listener_ns} = ${connector_ns} ]; then
+ return 0
+ fi
+
+ # skip if we don't want v6
+ if ! $ipv6 && is_v6 "${connect_addr}"; then
+ return 0
+ fi
+
+ local local_addr
+ if is_v6 "${connect_addr}"; then
+ local_addr="::"
+ else
+ local_addr="0.0.0.0"
+ fi
+
+ do_transfer ${listener_ns} ${connector_ns} MPTCP MPTCP ${connect_addr} ${local_addr}
+ lret=$?
+ if [ $lret -ne 0 ]; then
+ ret=$lret
+ return 1
+ fi
+
+ # don't bother testing fallback tcp except for loopback case.
+ if [ ${listener_ns} != ${connector_ns} ]; then
+ return 0
+ fi
+
+ do_transfer ${listener_ns} ${connector_ns} MPTCP TCP ${connect_addr} ${local_addr}
+ lret=$?
+ if [ $lret -ne 0 ]; then
+ ret=$lret
+ return 1
+ fi
+
+ do_transfer ${listener_ns} ${connector_ns} TCP MPTCP ${connect_addr} ${local_addr}
+ lret=$?
+ if [ $lret -ne 0 ]; then
+ ret=$lret
+ return 1
+ fi
+
+ return 0
+}
+
+run_tests()
+{
+ run_tests_lo $1 $2 $3 0
+}
+
+make_file "$cin" "client"
+make_file "$sin" "server"
+
+check_mptcp_disabled
+
+check_mptcp_ulp_setsockopt
+
+echo "INFO: validating network environment with pings"
+for sender in "$ns1" "$ns2" "$ns3" "$ns4";do
+ do_ping "$ns1" $sender 10.0.1.1
+ do_ping "$ns1" $sender dead:beef:1::1
+
+ do_ping "$ns2" $sender 10.0.1.2
+ do_ping "$ns2" $sender dead:beef:1::2
+ do_ping "$ns2" $sender 10.0.2.1
+ do_ping "$ns2" $sender dead:beef:2::1
+
+ do_ping "$ns3" $sender 10.0.2.2
+ do_ping "$ns3" $sender dead:beef:2::2
+ do_ping "$ns3" $sender 10.0.3.2
+ do_ping "$ns3" $sender dead:beef:3::2
+
+ do_ping "$ns4" $sender 10.0.3.1
+ do_ping "$ns4" $sender dead:beef:3::1
+done
+
+[ -n "$tc_loss" ] && tc -net "$ns2" qdisc add dev ns2eth3 root netem loss random $tc_loss
+echo -n "INFO: Using loss of $tc_loss "
+test "$tc_delay" -gt 0 && echo -n "delay $tc_delay ms "
+
+if [ -z "${tc_reorder}" ]; then
+ reorder1=$((RANDOM%10))
+ reorder1=$((100 - reorder1))
+ reorder2=$((RANDOM%100))
+
+ if [ $tc_delay -gt 0 ] && [ $reorder1 -lt 100 ] && [ $reorder2 -gt 0 ]; then
+ tc_reorder="reorder ${reorder1}% ${reorder2}%"
+ echo -n "$tc_reorder "
+ fi
+elif [ "$tc_reorder" = "0" ];then
+ tc_reorder=""
+elif [ "$tc_delay" -gt 0 ];then
+ # reordering requires some delay
+ tc_reorder="reorder $tc_reorder"
+ echo -n "$tc_reorder "
+fi
+
+echo "on ns3eth4"
+
+tc -net "$ns3" qdisc add dev ns3eth4 root netem delay ${tc_delay}ms $tc_reorder
+
+for sender in $ns1 $ns2 $ns3 $ns4;do
+ run_tests_lo "$ns1" "$sender" 10.0.1.1 1
+ if [ $ret -ne 0 ] ;then
+ echo "FAIL: Could not even run loopback test" 1>&2
+ exit $ret
+ fi
+ run_tests_lo "$ns1" $sender dead:beef:1::1 1
+ if [ $ret -ne 0 ] ;then
+ echo "FAIL: Could not even run loopback v6 test" 2>&1
+ exit $ret
+ fi
+
+ run_tests "$ns2" $sender 10.0.1.2
+ run_tests "$ns2" $sender dead:beef:1::2
+ run_tests "$ns2" $sender 10.0.2.1
+ run_tests "$ns2" $sender dead:beef:2::1
+
+ run_tests "$ns3" $sender 10.0.2.2
+ run_tests "$ns3" $sender dead:beef:2::2
+ run_tests "$ns3" $sender 10.0.3.2
+ run_tests "$ns3" $sender dead:beef:3::2
+
+ run_tests "$ns4" $sender 10.0.3.1
+ run_tests "$ns4" $sender dead:beef:3::1
+done
+
+time_end=$(date +%s)
+time_run=$((time_end-time_start))
+
+echo "Time: ${time_run} seconds"
+
+exit $ret
diff --git a/tools/testing/selftests/net/mptcp/settings b/tools/testing/selftests/net/mptcp/settings
new file mode 100644
index 000000000000..026384c189c9
--- /dev/null
+++ b/tools/testing/selftests/net/mptcp/settings
@@ -0,0 +1 @@
+timeout=450
diff --git a/tools/testing/selftests/net/nettest.c b/tools/testing/selftests/net/nettest.c
new file mode 100644
index 000000000000..93208caacbe6
--- /dev/null
+++ b/tools/testing/selftests/net/nettest.c
@@ -0,0 +1,1813 @@
+// SPDX-License-Identifier: GPL-2.0
+/* nettest - used for functional tests of networking APIs
+ *
+ * Copyright (c) 2013-2019 David Ahern <dsahern@gmail.com>. All rights reserved.
+ */
+
+#define _GNU_SOURCE
+#include <features.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <linux/tcp.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+#include <errno.h>
+
+#ifndef IPV6_UNICAST_IF
+#define IPV6_UNICAST_IF 76
+#endif
+#ifndef IPV6_MULTICAST_IF
+#define IPV6_MULTICAST_IF 17
+#endif
+
+#define DEFAULT_PORT 12345
+
+#ifndef MAX
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#endif
+#ifndef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+struct sock_args {
+ /* local address */
+ union {
+ struct in_addr in;
+ struct in6_addr in6;
+ } local_addr;
+
+ /* remote address */
+ union {
+ struct in_addr in;
+ struct in6_addr in6;
+ } remote_addr;
+ int scope_id; /* remote scope; v6 send only */
+
+ struct in_addr grp; /* multicast group */
+
+ unsigned int has_local_ip:1,
+ has_remote_ip:1,
+ has_grp:1,
+ has_expected_laddr:1,
+ has_expected_raddr:1,
+ bind_test_only:1;
+
+ unsigned short port;
+
+ int type; /* DGRAM, STREAM, RAW */
+ int protocol;
+ int version; /* AF_INET/AF_INET6 */
+
+ int use_setsockopt;
+ int use_cmsg;
+ const char *dev;
+ int ifindex;
+
+ const char *password;
+ /* prefix for MD5 password */
+ union {
+ struct sockaddr_in v4;
+ struct sockaddr_in6 v6;
+ } md5_prefix;
+ unsigned int prefix_len;
+
+ /* expected addresses and device index for connection */
+ int expected_ifindex;
+
+ /* local address */
+ union {
+ struct in_addr in;
+ struct in6_addr in6;
+ } expected_laddr;
+
+ /* remote address */
+ union {
+ struct in_addr in;
+ struct in6_addr in6;
+ } expected_raddr;
+};
+
+static int server_mode;
+static unsigned int prog_timeout = 5;
+static unsigned int interactive;
+static int iter = 1;
+static char *msg = "Hello world!";
+static int msglen;
+static int quiet;
+static int try_broadcast = 1;
+
+static char *timestamp(char *timebuf, int buflen)
+{
+ time_t now;
+
+ now = time(NULL);
+ if (strftime(timebuf, buflen, "%T", localtime(&now)) == 0) {
+ memset(timebuf, 0, buflen);
+ strncpy(timebuf, "00:00:00", buflen-1);
+ }
+
+ return timebuf;
+}
+
+static void log_msg(const char *format, ...)
+{
+ char timebuf[64];
+ va_list args;
+
+ if (quiet)
+ return;
+
+ fprintf(stdout, "%s %s:",
+ timestamp(timebuf, sizeof(timebuf)),
+ server_mode ? "server" : "client");
+ va_start(args, format);
+ vfprintf(stdout, format, args);
+ va_end(args);
+
+ fflush(stdout);
+}
+
+static void log_error(const char *format, ...)
+{
+ char timebuf[64];
+ va_list args;
+
+ if (quiet)
+ return;
+
+ fprintf(stderr, "%s %s:",
+ timestamp(timebuf, sizeof(timebuf)),
+ server_mode ? "server" : "client");
+ va_start(args, format);
+ vfprintf(stderr, format, args);
+ va_end(args);
+
+ fflush(stderr);
+}
+
+static void log_err_errno(const char *fmt, ...)
+{
+ char timebuf[64];
+ va_list args;
+
+ if (quiet)
+ return;
+
+ fprintf(stderr, "%s %s: ",
+ timestamp(timebuf, sizeof(timebuf)),
+ server_mode ? "server" : "client");
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+
+ fprintf(stderr, ": %d: %s\n", errno, strerror(errno));
+ fflush(stderr);
+}
+
+static void log_address(const char *desc, struct sockaddr *sa)
+{
+ char addrstr[64];
+
+ if (quiet)
+ return;
+
+ if (sa->sa_family == AF_INET) {
+ struct sockaddr_in *s = (struct sockaddr_in *) sa;
+
+ log_msg("%s %s:%d",
+ desc,
+ inet_ntop(AF_INET, &s->sin_addr, addrstr,
+ sizeof(addrstr)),
+ ntohs(s->sin_port));
+
+ } else if (sa->sa_family == AF_INET6) {
+ struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) sa;
+
+ log_msg("%s [%s]:%d",
+ desc,
+ inet_ntop(AF_INET6, &s6->sin6_addr, addrstr,
+ sizeof(addrstr)),
+ ntohs(s6->sin6_port));
+ }
+
+ printf("\n");
+
+ fflush(stdout);
+}
+
+static int tcp_md5sig(int sd, void *addr, socklen_t alen, struct sock_args *args)
+{
+ int keylen = strlen(args->password);
+ struct tcp_md5sig md5sig = {};
+ int opt = TCP_MD5SIG;
+ int rc;
+
+ md5sig.tcpm_keylen = keylen;
+ memcpy(md5sig.tcpm_key, args->password, keylen);
+
+ if (args->prefix_len) {
+ opt = TCP_MD5SIG_EXT;
+ md5sig.tcpm_flags |= TCP_MD5SIG_FLAG_PREFIX;
+
+ md5sig.tcpm_prefixlen = args->prefix_len;
+ addr = &args->md5_prefix;
+ }
+ memcpy(&md5sig.tcpm_addr, addr, alen);
+
+ if (args->ifindex) {
+ opt = TCP_MD5SIG_EXT;
+ md5sig.tcpm_flags |= TCP_MD5SIG_FLAG_IFINDEX;
+
+ md5sig.tcpm_ifindex = args->ifindex;
+ }
+
+ rc = setsockopt(sd, IPPROTO_TCP, opt, &md5sig, sizeof(md5sig));
+ if (rc < 0) {
+ /* ENOENT is harmless. Returned when a password is cleared */
+ if (errno == ENOENT)
+ rc = 0;
+ else
+ log_err_errno("setsockopt(TCP_MD5SIG)");
+ }
+
+ return rc;
+}
+
+static int tcp_md5_remote(int sd, struct sock_args *args)
+{
+ struct sockaddr_in sin = {
+ .sin_family = AF_INET,
+ };
+ struct sockaddr_in6 sin6 = {
+ .sin6_family = AF_INET6,
+ };
+ void *addr;
+ int alen;
+
+ switch (args->version) {
+ case AF_INET:
+ sin.sin_port = htons(args->port);
+ sin.sin_addr = args->remote_addr.in;
+ addr = &sin;
+ alen = sizeof(sin);
+ break;
+ case AF_INET6:
+ sin6.sin6_port = htons(args->port);
+ sin6.sin6_addr = args->remote_addr.in6;
+ addr = &sin6;
+ alen = sizeof(sin6);
+ break;
+ default:
+ log_error("unknown address family\n");
+ exit(1);
+ }
+
+ if (tcp_md5sig(sd, addr, alen, args))
+ return -1;
+
+ return 0;
+}
+
+static int get_ifidx(const char *ifname)
+{
+ struct ifreq ifdata;
+ int sd, rc;
+
+ if (!ifname || *ifname == '\0')
+ return -1;
+
+ memset(&ifdata, 0, sizeof(ifdata));
+
+ strcpy(ifdata.ifr_name, ifname);
+
+ sd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
+ if (sd < 0) {
+ log_err_errno("socket failed");
+ return -1;
+ }
+
+ rc = ioctl(sd, SIOCGIFINDEX, (char *)&ifdata);
+ close(sd);
+ if (rc != 0) {
+ log_err_errno("ioctl(SIOCGIFINDEX) failed");
+ return -1;
+ }
+
+ return ifdata.ifr_ifindex;
+}
+
+static int bind_to_device(int sd, const char *name)
+{
+ int rc;
+
+ rc = setsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, name, strlen(name)+1);
+ if (rc < 0)
+ log_err_errno("setsockopt(SO_BINDTODEVICE)");
+
+ return rc;
+}
+
+static int get_bind_to_device(int sd, char *name, size_t len)
+{
+ int rc;
+ socklen_t optlen = len;
+
+ name[0] = '\0';
+ rc = getsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, name, &optlen);
+ if (rc < 0)
+ log_err_errno("setsockopt(SO_BINDTODEVICE)");
+
+ return rc;
+}
+
+static int check_device(int sd, struct sock_args *args)
+{
+ int ifindex = 0;
+ char name[32];
+
+ if (get_bind_to_device(sd, name, sizeof(name)))
+ *name = '\0';
+ else
+ ifindex = get_ifidx(name);
+
+ log_msg(" bound to device %s/%d\n",
+ *name ? name : "<none>", ifindex);
+
+ if (!args->expected_ifindex)
+ return 0;
+
+ if (args->expected_ifindex != ifindex) {
+ log_error("Device index mismatch: expected %d have %d\n",
+ args->expected_ifindex, ifindex);
+ return 1;
+ }
+
+ log_msg("Device index matches: expected %d have %d\n",
+ args->expected_ifindex, ifindex);
+
+ return 0;
+}
+
+static int set_pktinfo_v4(int sd)
+{
+ int one = 1;
+ int rc;
+
+ rc = setsockopt(sd, SOL_IP, IP_PKTINFO, &one, sizeof(one));
+ if (rc < 0 && rc != -ENOTSUP)
+ log_err_errno("setsockopt(IP_PKTINFO)");
+
+ return rc;
+}
+
+static int set_recvpktinfo_v6(int sd)
+{
+ int one = 1;
+ int rc;
+
+ rc = setsockopt(sd, SOL_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one));
+ if (rc < 0 && rc != -ENOTSUP)
+ log_err_errno("setsockopt(IPV6_RECVPKTINFO)");
+
+ return rc;
+}
+
+static int set_recverr_v4(int sd)
+{
+ int one = 1;
+ int rc;
+
+ rc = setsockopt(sd, SOL_IP, IP_RECVERR, &one, sizeof(one));
+ if (rc < 0 && rc != -ENOTSUP)
+ log_err_errno("setsockopt(IP_RECVERR)");
+
+ return rc;
+}
+
+static int set_recverr_v6(int sd)
+{
+ int one = 1;
+ int rc;
+
+ rc = setsockopt(sd, SOL_IPV6, IPV6_RECVERR, &one, sizeof(one));
+ if (rc < 0 && rc != -ENOTSUP)
+ log_err_errno("setsockopt(IPV6_RECVERR)");
+
+ return rc;
+}
+
+static int set_unicast_if(int sd, int ifindex, int version)
+{
+ int opt = IP_UNICAST_IF;
+ int level = SOL_IP;
+ int rc;
+
+ ifindex = htonl(ifindex);
+
+ if (version == AF_INET6) {
+ opt = IPV6_UNICAST_IF;
+ level = SOL_IPV6;
+ }
+ rc = setsockopt(sd, level, opt, &ifindex, sizeof(ifindex));
+ if (rc < 0)
+ log_err_errno("setsockopt(IP_UNICAST_IF)");
+
+ return rc;
+}
+
+static int set_multicast_if(int sd, int ifindex)
+{
+ struct ip_mreqn mreq = { .imr_ifindex = ifindex };
+ int rc;
+
+ rc = setsockopt(sd, SOL_IP, IP_MULTICAST_IF, &mreq, sizeof(mreq));
+ if (rc < 0)
+ log_err_errno("setsockopt(IP_MULTICAST_IF)");
+
+ return rc;
+}
+
+static int set_membership(int sd, uint32_t grp, uint32_t addr, int ifindex)
+{
+ uint32_t if_addr = addr;
+ struct ip_mreqn mreq;
+ int rc;
+
+ if (addr == htonl(INADDR_ANY) && !ifindex) {
+ log_error("Either local address or device needs to be given for multicast membership\n");
+ return -1;
+ }
+
+ mreq.imr_multiaddr.s_addr = grp;
+ mreq.imr_address.s_addr = if_addr;
+ mreq.imr_ifindex = ifindex;
+
+ rc = setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
+ if (rc < 0) {
+ log_err_errno("setsockopt(IP_ADD_MEMBERSHIP)");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int set_broadcast(int sd)
+{
+ unsigned int one = 1;
+ int rc = 0;
+
+ if (setsockopt(sd, SOL_SOCKET, SO_BROADCAST, &one, sizeof(one)) != 0) {
+ log_err_errno("setsockopt(SO_BROADCAST)");
+ rc = -1;
+ }
+
+ return rc;
+}
+
+static int set_reuseport(int sd)
+{
+ unsigned int one = 1;
+ int rc = 0;
+
+ if (setsockopt(sd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)) != 0) {
+ log_err_errno("setsockopt(SO_REUSEPORT)");
+ rc = -1;
+ }
+
+ return rc;
+}
+
+static int set_reuseaddr(int sd)
+{
+ unsigned int one = 1;
+ int rc = 0;
+
+ if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) != 0) {
+ log_err_errno("setsockopt(SO_REUSEADDR)");
+ rc = -1;
+ }
+
+ return rc;
+}
+
+static int str_to_uint(const char *str, int min, int max, unsigned int *value)
+{
+ int number;
+ char *end;
+
+ errno = 0;
+ number = (unsigned int) strtoul(str, &end, 0);
+
+ /* entire string should be consumed by conversion
+ * and value should be between min and max
+ */
+ if (((*end == '\0') || (*end == '\n')) && (end != str) &&
+ (errno != ERANGE) && (min <= number) && (number <= max)) {
+ *value = number;
+ return 0;
+ }
+
+ return -1;
+}
+
+static int expected_addr_match(struct sockaddr *sa, void *expected,
+ const char *desc)
+{
+ char addrstr[64];
+ int rc = 0;
+
+ if (sa->sa_family == AF_INET) {
+ struct sockaddr_in *s = (struct sockaddr_in *) sa;
+ struct in_addr *exp_in = (struct in_addr *) expected;
+
+ if (s->sin_addr.s_addr != exp_in->s_addr) {
+ log_error("%s address does not match expected %s",
+ desc,
+ inet_ntop(AF_INET, exp_in,
+ addrstr, sizeof(addrstr)));
+ rc = 1;
+ }
+ } else if (sa->sa_family == AF_INET6) {
+ struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) sa;
+ struct in6_addr *exp_in = (struct in6_addr *) expected;
+
+ if (memcmp(&s6->sin6_addr, exp_in, sizeof(*exp_in))) {
+ log_error("%s address does not match expected %s",
+ desc,
+ inet_ntop(AF_INET6, exp_in,
+ addrstr, sizeof(addrstr)));
+ rc = 1;
+ }
+ } else {
+ log_error("%s address does not match expected - unknown family",
+ desc);
+ rc = 1;
+ }
+
+ if (!rc)
+ log_msg("%s address matches expected\n", desc);
+
+ return rc;
+}
+
+static int show_sockstat(int sd, struct sock_args *args)
+{
+ struct sockaddr_in6 local_addr, remote_addr;
+ socklen_t alen = sizeof(local_addr);
+ struct sockaddr *sa;
+ const char *desc;
+ int rc = 0;
+
+ desc = server_mode ? "server local:" : "client local:";
+ sa = (struct sockaddr *) &local_addr;
+ if (getsockname(sd, sa, &alen) == 0) {
+ log_address(desc, sa);
+
+ if (args->has_expected_laddr) {
+ rc = expected_addr_match(sa, &args->expected_laddr,
+ "local");
+ }
+ } else {
+ log_err_errno("getsockname failed");
+ }
+
+ sa = (struct sockaddr *) &remote_addr;
+ desc = server_mode ? "server peer:" : "client peer:";
+ if (getpeername(sd, sa, &alen) == 0) {
+ log_address(desc, sa);
+
+ if (args->has_expected_raddr) {
+ rc |= expected_addr_match(sa, &args->expected_raddr,
+ "remote");
+ }
+ } else {
+ log_err_errno("getpeername failed");
+ }
+
+ return rc;
+}
+
+static int get_index_from_cmsg(struct msghdr *m)
+{
+ struct cmsghdr *cm;
+ int ifindex = 0;
+ char buf[64];
+
+ for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(m);
+ m->msg_controllen != 0 && cm;
+ cm = (struct cmsghdr *)CMSG_NXTHDR(m, cm)) {
+
+ if (cm->cmsg_level == SOL_IP &&
+ cm->cmsg_type == IP_PKTINFO) {
+ struct in_pktinfo *pi;
+
+ pi = (struct in_pktinfo *)(CMSG_DATA(cm));
+ inet_ntop(AF_INET, &pi->ipi_addr, buf, sizeof(buf));
+ ifindex = pi->ipi_ifindex;
+ } else if (cm->cmsg_level == SOL_IPV6 &&
+ cm->cmsg_type == IPV6_PKTINFO) {
+ struct in6_pktinfo *pi6;
+
+ pi6 = (struct in6_pktinfo *)(CMSG_DATA(cm));
+ inet_ntop(AF_INET6, &pi6->ipi6_addr, buf, sizeof(buf));
+ ifindex = pi6->ipi6_ifindex;
+ }
+ }
+
+ if (ifindex) {
+ log_msg(" pktinfo: ifindex %d dest addr %s\n",
+ ifindex, buf);
+ }
+ return ifindex;
+}
+
+static int send_msg_no_cmsg(int sd, void *addr, socklen_t alen)
+{
+ int err;
+
+again:
+ err = sendto(sd, msg, msglen, 0, addr, alen);
+ if (err < 0) {
+ if (errno == EACCES && try_broadcast) {
+ try_broadcast = 0;
+ if (!set_broadcast(sd))
+ goto again;
+ errno = EACCES;
+ }
+
+ log_err_errno("sendto failed");
+ return 1;
+ }
+
+ return 0;
+}
+
+static int send_msg_cmsg(int sd, void *addr, socklen_t alen,
+ int ifindex, int version)
+{
+ unsigned char cmsgbuf[64];
+ struct iovec iov[2];
+ struct cmsghdr *cm;
+ struct msghdr m;
+ int err;
+
+ iov[0].iov_base = msg;
+ iov[0].iov_len = msglen;
+ m.msg_iov = iov;
+ m.msg_iovlen = 1;
+ m.msg_name = (caddr_t)addr;
+ m.msg_namelen = alen;
+
+ memset(cmsgbuf, 0, sizeof(cmsgbuf));
+ cm = (struct cmsghdr *)cmsgbuf;
+ m.msg_control = (caddr_t)cm;
+
+ if (version == AF_INET) {
+ struct in_pktinfo *pi;
+
+ cm->cmsg_level = SOL_IP;
+ cm->cmsg_type = IP_PKTINFO;
+ cm->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
+ pi = (struct in_pktinfo *)(CMSG_DATA(cm));
+ pi->ipi_ifindex = ifindex;
+
+ m.msg_controllen = cm->cmsg_len;
+
+ } else if (version == AF_INET6) {
+ struct in6_pktinfo *pi6;
+
+ cm->cmsg_level = SOL_IPV6;
+ cm->cmsg_type = IPV6_PKTINFO;
+ cm->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
+
+ pi6 = (struct in6_pktinfo *)(CMSG_DATA(cm));
+ pi6->ipi6_ifindex = ifindex;
+
+ m.msg_controllen = cm->cmsg_len;
+ }
+
+again:
+ err = sendmsg(sd, &m, 0);
+ if (err < 0) {
+ if (errno == EACCES && try_broadcast) {
+ try_broadcast = 0;
+ if (!set_broadcast(sd))
+ goto again;
+ errno = EACCES;
+ }
+
+ log_err_errno("sendmsg failed");
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static int send_msg(int sd, void *addr, socklen_t alen, struct sock_args *args)
+{
+ if (args->type == SOCK_STREAM) {
+ if (write(sd, msg, msglen) < 0) {
+ log_err_errno("write failed sending msg to peer");
+ return 1;
+ }
+ } else if (args->ifindex && args->use_cmsg) {
+ if (send_msg_cmsg(sd, addr, alen, args->ifindex, args->version))
+ return 1;
+ } else {
+ if (send_msg_no_cmsg(sd, addr, alen))
+ return 1;
+ }
+
+ log_msg("Sent message:\n");
+ log_msg(" %.24s%s\n", msg, msglen > 24 ? " ..." : "");
+
+ return 0;
+}
+
+static int socket_read_dgram(int sd, struct sock_args *args)
+{
+ unsigned char addr[sizeof(struct sockaddr_in6)];
+ struct sockaddr *sa = (struct sockaddr *) addr;
+ socklen_t alen = sizeof(addr);
+ struct iovec iov[2];
+ struct msghdr m = {
+ .msg_name = (caddr_t)addr,
+ .msg_namelen = alen,
+ .msg_iov = iov,
+ .msg_iovlen = 1,
+ };
+ unsigned char cmsgbuf[256];
+ struct cmsghdr *cm = (struct cmsghdr *)cmsgbuf;
+ char buf[16*1024];
+ int ifindex;
+ int len;
+
+ iov[0].iov_base = (caddr_t)buf;
+ iov[0].iov_len = sizeof(buf);
+
+ memset(cmsgbuf, 0, sizeof(cmsgbuf));
+ m.msg_control = (caddr_t)cm;
+ m.msg_controllen = sizeof(cmsgbuf);
+
+ len = recvmsg(sd, &m, 0);
+ if (len == 0) {
+ log_msg("peer closed connection.\n");
+ return 0;
+ } else if (len < 0) {
+ log_msg("failed to read message: %d: %s\n",
+ errno, strerror(errno));
+ return -1;
+ }
+
+ buf[len] = '\0';
+
+ log_address("Message from:", sa);
+ log_msg(" %.24s%s\n", buf, len > 24 ? " ..." : "");
+
+ ifindex = get_index_from_cmsg(&m);
+ if (args->expected_ifindex) {
+ if (args->expected_ifindex != ifindex) {
+ log_error("Device index mismatch: expected %d have %d\n",
+ args->expected_ifindex, ifindex);
+ return -1;
+ }
+ log_msg("Device index matches: expected %d have %d\n",
+ args->expected_ifindex, ifindex);
+ }
+
+ if (!interactive && server_mode) {
+ if (sa->sa_family == AF_INET6) {
+ struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) sa;
+ struct in6_addr *in6 = &s6->sin6_addr;
+
+ if (IN6_IS_ADDR_V4MAPPED(in6)) {
+ const uint32_t *pa = (uint32_t *) &in6->s6_addr;
+ struct in_addr in4;
+ struct sockaddr_in *sin;
+
+ sin = (struct sockaddr_in *) addr;
+ pa += 3;
+ in4.s_addr = *pa;
+ sin->sin_addr = in4;
+ sin->sin_family = AF_INET;
+ if (send_msg_cmsg(sd, addr, alen,
+ ifindex, AF_INET) < 0)
+ goto out_err;
+ }
+ }
+again:
+ iov[0].iov_len = len;
+
+ if (args->version == AF_INET6) {
+ struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) sa;
+
+ if (args->dev) {
+ /* avoid PKTINFO conflicts with bindtodev */
+ if (sendto(sd, buf, len, 0,
+ (void *) addr, alen) < 0)
+ goto out_err;
+ } else {
+ /* kernel is allowing scope_id to be set to VRF
+ * index for LLA. for sends to global address
+ * reset scope id
+ */
+ s6->sin6_scope_id = ifindex;
+ if (sendmsg(sd, &m, 0) < 0)
+ goto out_err;
+ }
+ } else {
+ int err;
+
+ err = sendmsg(sd, &m, 0);
+ if (err < 0) {
+ if (errno == EACCES && try_broadcast) {
+ try_broadcast = 0;
+ if (!set_broadcast(sd))
+ goto again;
+ errno = EACCES;
+ }
+ goto out_err;
+ }
+ }
+ log_msg("Sent message:\n");
+ log_msg(" %.24s%s\n", buf, len > 24 ? " ..." : "");
+ }
+
+ return 1;
+out_err:
+ log_err_errno("failed to send msg to peer");
+ return -1;
+}
+
+static int socket_read_stream(int sd)
+{
+ char buf[1024];
+ int len;
+
+ len = read(sd, buf, sizeof(buf)-1);
+ if (len == 0) {
+ log_msg("client closed connection.\n");
+ return 0;
+ } else if (len < 0) {
+ log_msg("failed to read message\n");
+ return -1;
+ }
+
+ buf[len] = '\0';
+ log_msg("Incoming message:\n");
+ log_msg(" %.24s%s\n", buf, len > 24 ? " ..." : "");
+
+ if (!interactive && server_mode) {
+ if (write(sd, buf, len) < 0) {
+ log_err_errno("failed to send buf");
+ return -1;
+ }
+ log_msg("Sent message:\n");
+ log_msg(" %.24s%s\n", buf, len > 24 ? " ..." : "");
+ }
+
+ return 1;
+}
+
+static int socket_read(int sd, struct sock_args *args)
+{
+ if (args->type == SOCK_STREAM)
+ return socket_read_stream(sd);
+
+ return socket_read_dgram(sd, args);
+}
+
+static int stdin_to_socket(int sd, int type, void *addr, socklen_t alen)
+{
+ char buf[1024];
+ int len;
+
+ if (fgets(buf, sizeof(buf), stdin) == NULL)
+ return 0;
+
+ len = strlen(buf);
+ if (type == SOCK_STREAM) {
+ if (write(sd, buf, len) < 0) {
+ log_err_errno("failed to send buf");
+ return -1;
+ }
+ } else {
+ int err;
+
+again:
+ err = sendto(sd, buf, len, 0, addr, alen);
+ if (err < 0) {
+ if (errno == EACCES && try_broadcast) {
+ try_broadcast = 0;
+ if (!set_broadcast(sd))
+ goto again;
+ errno = EACCES;
+ }
+ log_err_errno("failed to send msg to peer");
+ return -1;
+ }
+ }
+ log_msg("Sent message:\n");
+ log_msg(" %.24s%s\n", buf, len > 24 ? " ..." : "");
+
+ return 1;
+}
+
+static void set_recv_attr(int sd, int version)
+{
+ if (version == AF_INET6) {
+ set_recvpktinfo_v6(sd);
+ set_recverr_v6(sd);
+ } else {
+ set_pktinfo_v4(sd);
+ set_recverr_v4(sd);
+ }
+}
+
+static int msg_loop(int client, int sd, void *addr, socklen_t alen,
+ struct sock_args *args)
+{
+ struct timeval timeout = { .tv_sec = prog_timeout }, *ptval = NULL;
+ fd_set rfds;
+ int nfds;
+ int rc;
+
+ if (args->type != SOCK_STREAM)
+ set_recv_attr(sd, args->version);
+
+ if (msg) {
+ msglen = strlen(msg);
+
+ /* client sends first message */
+ if (client) {
+ if (send_msg(sd, addr, alen, args))
+ return 1;
+ }
+ if (!interactive) {
+ ptval = &timeout;
+ if (!prog_timeout)
+ timeout.tv_sec = 5;
+ }
+ }
+
+ nfds = interactive ? MAX(fileno(stdin), sd) + 1 : sd + 1;
+ while (1) {
+ FD_ZERO(&rfds);
+ FD_SET(sd, &rfds);
+ if (interactive)
+ FD_SET(fileno(stdin), &rfds);
+
+ rc = select(nfds, &rfds, NULL, NULL, ptval);
+ if (rc < 0) {
+ if (errno == EINTR)
+ continue;
+
+ rc = 1;
+ log_err_errno("select failed");
+ break;
+ } else if (rc == 0) {
+ log_error("Timed out waiting for response\n");
+ rc = 2;
+ break;
+ }
+
+ if (FD_ISSET(sd, &rfds)) {
+ rc = socket_read(sd, args);
+ if (rc < 0) {
+ rc = 1;
+ break;
+ }
+ if (rc == 0)
+ break;
+ }
+
+ rc = 0;
+
+ if (FD_ISSET(fileno(stdin), &rfds)) {
+ if (stdin_to_socket(sd, args->type, addr, alen) <= 0)
+ break;
+ }
+
+ if (interactive)
+ continue;
+
+ if (iter != -1) {
+ --iter;
+ if (iter == 0)
+ break;
+ }
+
+ log_msg("Going into quiet mode\n");
+ quiet = 1;
+
+ if (client) {
+ if (send_msg(sd, addr, alen, args)) {
+ rc = 1;
+ break;
+ }
+ }
+ }
+
+ return rc;
+}
+
+static int msock_init(struct sock_args *args, int server)
+{
+ uint32_t if_addr = htonl(INADDR_ANY);
+ struct sockaddr_in laddr = {
+ .sin_family = AF_INET,
+ .sin_port = htons(args->port),
+ };
+ int one = 1;
+ int sd;
+
+ if (!server && args->has_local_ip)
+ if_addr = args->local_addr.in.s_addr;
+
+ sd = socket(PF_INET, SOCK_DGRAM, 0);
+ if (sd < 0) {
+ log_err_errno("socket");
+ return -1;
+ }
+
+ if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR,
+ (char *)&one, sizeof(one)) < 0) {
+ log_err_errno("Setting SO_REUSEADDR error");
+ goto out_err;
+ }
+
+ if (setsockopt(sd, SOL_SOCKET, SO_BROADCAST,
+ (char *)&one, sizeof(one)) < 0)
+ log_err_errno("Setting SO_BROADCAST error");
+
+ if (args->dev && bind_to_device(sd, args->dev) != 0)
+ goto out_err;
+ else if (args->use_setsockopt &&
+ set_multicast_if(sd, args->ifindex))
+ goto out_err;
+
+ laddr.sin_addr.s_addr = if_addr;
+
+ if (bind(sd, (struct sockaddr *) &laddr, sizeof(laddr)) < 0) {
+ log_err_errno("bind failed");
+ goto out_err;
+ }
+
+ if (server &&
+ set_membership(sd, args->grp.s_addr,
+ args->local_addr.in.s_addr, args->ifindex))
+ goto out_err;
+
+ return sd;
+out_err:
+ close(sd);
+ return -1;
+}
+
+static int msock_server(struct sock_args *args)
+{
+ return msock_init(args, 1);
+}
+
+static int msock_client(struct sock_args *args)
+{
+ return msock_init(args, 0);
+}
+
+static int bind_socket(int sd, struct sock_args *args)
+{
+ struct sockaddr_in serv_addr = {
+ .sin_family = AF_INET,
+ };
+ struct sockaddr_in6 serv6_addr = {
+ .sin6_family = AF_INET6,
+ };
+ void *addr;
+ socklen_t alen;
+
+ if (!args->has_local_ip && args->type == SOCK_RAW)
+ return 0;
+
+ switch (args->version) {
+ case AF_INET:
+ serv_addr.sin_port = htons(args->port);
+ serv_addr.sin_addr = args->local_addr.in;
+ addr = &serv_addr;
+ alen = sizeof(serv_addr);
+ break;
+
+ case AF_INET6:
+ serv6_addr.sin6_port = htons(args->port);
+ serv6_addr.sin6_addr = args->local_addr.in6;
+ addr = &serv6_addr;
+ alen = sizeof(serv6_addr);
+ break;
+
+ default:
+ log_error("Invalid address family\n");
+ return -1;
+ }
+
+ if (bind(sd, addr, alen) < 0) {
+ log_err_errno("error binding socket");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int lsock_init(struct sock_args *args)
+{
+ long flags;
+ int sd;
+
+ sd = socket(args->version, args->type, args->protocol);
+ if (sd < 0) {
+ log_err_errno("Error opening socket");
+ return -1;
+ }
+
+ if (set_reuseaddr(sd) != 0)
+ goto err;
+
+ if (set_reuseport(sd) != 0)
+ goto err;
+
+ if (args->dev && bind_to_device(sd, args->dev) != 0)
+ goto err;
+ else if (args->use_setsockopt &&
+ set_unicast_if(sd, args->ifindex, args->version))
+ goto err;
+
+ if (bind_socket(sd, args))
+ goto err;
+
+ if (args->bind_test_only)
+ goto out;
+
+ if (args->type == SOCK_STREAM && listen(sd, 1) < 0) {
+ log_err_errno("listen failed");
+ goto err;
+ }
+
+ flags = fcntl(sd, F_GETFL);
+ if ((flags < 0) || (fcntl(sd, F_SETFL, flags|O_NONBLOCK) < 0)) {
+ log_err_errno("Failed to set non-blocking option");
+ goto err;
+ }
+
+ if (fcntl(sd, F_SETFD, FD_CLOEXEC) < 0)
+ log_err_errno("Failed to set close-on-exec flag");
+
+out:
+ return sd;
+
+err:
+ close(sd);
+ return -1;
+}
+
+static int do_server(struct sock_args *args)
+{
+ struct timeval timeout = { .tv_sec = prog_timeout }, *ptval = NULL;
+ unsigned char addr[sizeof(struct sockaddr_in6)] = {};
+ socklen_t alen = sizeof(addr);
+ int lsd, csd = -1;
+
+ fd_set rfds;
+ int rc;
+
+ if (prog_timeout)
+ ptval = &timeout;
+
+ if (args->has_grp)
+ lsd = msock_server(args);
+ else
+ lsd = lsock_init(args);
+
+ if (lsd < 0)
+ return 1;
+
+ if (args->bind_test_only) {
+ close(lsd);
+ return 0;
+ }
+
+ if (args->type != SOCK_STREAM) {
+ rc = msg_loop(0, lsd, (void *) addr, alen, args);
+ close(lsd);
+ return rc;
+ }
+
+ if (args->password && tcp_md5_remote(lsd, args)) {
+ close(lsd);
+ return 1;
+ }
+
+ while (1) {
+ log_msg("\n");
+ log_msg("waiting for client connection.\n");
+ FD_ZERO(&rfds);
+ FD_SET(lsd, &rfds);
+
+ rc = select(lsd+1, &rfds, NULL, NULL, ptval);
+ if (rc == 0) {
+ rc = 2;
+ break;
+ }
+
+ if (rc < 0) {
+ if (errno == EINTR)
+ continue;
+
+ log_err_errno("select failed");
+ break;
+ }
+
+ if (FD_ISSET(lsd, &rfds)) {
+
+ csd = accept(lsd, (void *) addr, &alen);
+ if (csd < 0) {
+ log_err_errno("accept failed");
+ break;
+ }
+
+ rc = show_sockstat(csd, args);
+ if (rc)
+ break;
+
+ rc = check_device(csd, args);
+ if (rc)
+ break;
+ }
+
+ rc = msg_loop(0, csd, (void *) addr, alen, args);
+ close(csd);
+
+ if (!interactive)
+ break;
+ }
+
+ close(lsd);
+
+ return rc;
+}
+
+static int wait_for_connect(int sd)
+{
+ struct timeval _tv = { .tv_sec = prog_timeout }, *tv = NULL;
+ fd_set wfd;
+ int val = 0, sz = sizeof(val);
+ int rc;
+
+ FD_ZERO(&wfd);
+ FD_SET(sd, &wfd);
+
+ if (prog_timeout)
+ tv = &_tv;
+
+ rc = select(FD_SETSIZE, NULL, &wfd, NULL, tv);
+ if (rc == 0) {
+ log_error("connect timed out\n");
+ return -2;
+ } else if (rc < 0) {
+ log_err_errno("select failed");
+ return -3;
+ }
+
+ if (getsockopt(sd, SOL_SOCKET, SO_ERROR, &val, (socklen_t *)&sz) < 0) {
+ log_err_errno("getsockopt(SO_ERROR) failed");
+ return -4;
+ }
+
+ if (val != 0) {
+ log_error("connect failed: %d: %s\n", val, strerror(val));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int connectsock(void *addr, socklen_t alen, struct sock_args *args)
+{
+ int sd, rc = -1;
+ long flags;
+
+ sd = socket(args->version, args->type, args->protocol);
+ if (sd < 0) {
+ log_err_errno("Failed to create socket");
+ return -1;
+ }
+
+ flags = fcntl(sd, F_GETFL);
+ if ((flags < 0) || (fcntl(sd, F_SETFL, flags|O_NONBLOCK) < 0)) {
+ log_err_errno("Failed to set non-blocking option");
+ goto err;
+ }
+
+ if (set_reuseport(sd) != 0)
+ goto err;
+
+ if (args->dev && bind_to_device(sd, args->dev) != 0)
+ goto err;
+ else if (args->use_setsockopt &&
+ set_unicast_if(sd, args->ifindex, args->version))
+ goto err;
+
+ if (args->has_local_ip && bind_socket(sd, args))
+ goto err;
+
+ if (args->type != SOCK_STREAM)
+ goto out;
+
+ if (args->password && tcp_md5sig(sd, addr, alen, args))
+ goto err;
+
+ if (args->bind_test_only)
+ goto out;
+
+ if (connect(sd, addr, alen) < 0) {
+ if (errno != EINPROGRESS) {
+ log_err_errno("Failed to connect to remote host");
+ rc = -1;
+ goto err;
+ }
+ rc = wait_for_connect(sd);
+ if (rc < 0)
+ goto err;
+ }
+out:
+ return sd;
+
+err:
+ close(sd);
+ return rc;
+}
+
+static int do_client(struct sock_args *args)
+{
+ struct sockaddr_in sin = {
+ .sin_family = AF_INET,
+ };
+ struct sockaddr_in6 sin6 = {
+ .sin6_family = AF_INET6,
+ };
+ void *addr;
+ int alen;
+ int rc = 0;
+ int sd;
+
+ if (!args->has_remote_ip && !args->has_grp) {
+ fprintf(stderr, "remote IP or multicast group not given\n");
+ return 1;
+ }
+
+ switch (args->version) {
+ case AF_INET:
+ sin.sin_port = htons(args->port);
+ if (args->has_grp)
+ sin.sin_addr = args->grp;
+ else
+ sin.sin_addr = args->remote_addr.in;
+ addr = &sin;
+ alen = sizeof(sin);
+ break;
+ case AF_INET6:
+ sin6.sin6_port = htons(args->port);
+ sin6.sin6_addr = args->remote_addr.in6;
+ sin6.sin6_scope_id = args->scope_id;
+ addr = &sin6;
+ alen = sizeof(sin6);
+ break;
+ }
+
+ if (args->has_grp)
+ sd = msock_client(args);
+ else
+ sd = connectsock(addr, alen, args);
+
+ if (sd < 0)
+ return -sd;
+
+ if (args->bind_test_only)
+ goto out;
+
+ if (args->type == SOCK_STREAM) {
+ rc = show_sockstat(sd, args);
+ if (rc != 0)
+ goto out;
+ }
+
+ rc = msg_loop(1, sd, addr, alen, args);
+
+out:
+ close(sd);
+
+ return rc;
+}
+
+enum addr_type {
+ ADDR_TYPE_LOCAL,
+ ADDR_TYPE_REMOTE,
+ ADDR_TYPE_MCAST,
+ ADDR_TYPE_EXPECTED_LOCAL,
+ ADDR_TYPE_EXPECTED_REMOTE,
+ ADDR_TYPE_MD5_PREFIX,
+};
+
+static int convert_addr(struct sock_args *args, const char *_str,
+ enum addr_type atype)
+{
+ int pfx_len_max = args->version == AF_INET6 ? 128 : 32;
+ int family = args->version;
+ char *str, *dev, *sep;
+ struct in6_addr *in6;
+ struct in_addr *in;
+ const char *desc;
+ void *addr;
+ int rc = 0;
+
+ str = strdup(_str);
+ if (!str)
+ return -ENOMEM;
+
+ switch (atype) {
+ case ADDR_TYPE_LOCAL:
+ desc = "local";
+ addr = &args->local_addr;
+ break;
+ case ADDR_TYPE_REMOTE:
+ desc = "remote";
+ addr = &args->remote_addr;
+ break;
+ case ADDR_TYPE_MCAST:
+ desc = "mcast grp";
+ addr = &args->grp;
+ break;
+ case ADDR_TYPE_EXPECTED_LOCAL:
+ desc = "expected local";
+ addr = &args->expected_laddr;
+ break;
+ case ADDR_TYPE_EXPECTED_REMOTE:
+ desc = "expected remote";
+ addr = &args->expected_raddr;
+ break;
+ case ADDR_TYPE_MD5_PREFIX:
+ desc = "md5 prefix";
+ if (family == AF_INET) {
+ args->md5_prefix.v4.sin_family = AF_INET;
+ addr = &args->md5_prefix.v4.sin_addr;
+ } else if (family == AF_INET6) {
+ args->md5_prefix.v6.sin6_family = AF_INET6;
+ addr = &args->md5_prefix.v6.sin6_addr;
+ } else
+ return 1;
+
+ sep = strchr(str, '/');
+ if (sep) {
+ *sep = '\0';
+ sep++;
+ if (str_to_uint(sep, 1, pfx_len_max,
+ &args->prefix_len) != 0) {
+ fprintf(stderr, "Invalid port\n");
+ return 1;
+ }
+ } else {
+ args->prefix_len = pfx_len_max;
+ }
+ break;
+ default:
+ log_error("unknown address type");
+ exit(1);
+ }
+
+ switch (family) {
+ case AF_INET:
+ in = (struct in_addr *) addr;
+ if (str) {
+ if (inet_pton(AF_INET, str, in) == 0) {
+ log_error("Invalid %s IP address\n", desc);
+ rc = -1;
+ goto out;
+ }
+ } else {
+ in->s_addr = htonl(INADDR_ANY);
+ }
+ break;
+
+ case AF_INET6:
+ dev = strchr(str, '%');
+ if (dev) {
+ *dev = '\0';
+ dev++;
+ }
+
+ in6 = (struct in6_addr *) addr;
+ if (str) {
+ if (inet_pton(AF_INET6, str, in6) == 0) {
+ log_error("Invalid %s IPv6 address\n", desc);
+ rc = -1;
+ goto out;
+ }
+ } else {
+ *in6 = in6addr_any;
+ }
+ if (dev) {
+ args->scope_id = get_ifidx(dev);
+ if (args->scope_id < 0) {
+ log_error("Invalid scope on %s IPv6 address\n",
+ desc);
+ rc = -1;
+ goto out;
+ }
+ }
+ break;
+
+ default:
+ log_error("Invalid address family\n");
+ }
+
+out:
+ free(str);
+ return rc;
+}
+
+static char *random_msg(int len)
+{
+ int i, n = 0, olen = len + 1;
+ char *m;
+
+ if (len <= 0)
+ return NULL;
+
+ m = malloc(olen);
+ if (!m)
+ return NULL;
+
+ while (len > 26) {
+ i = snprintf(m + n, olen - n, "%.26s",
+ "abcdefghijklmnopqrstuvwxyz");
+ n += i;
+ len -= i;
+ }
+ i = snprintf(m + n, olen - n, "%.*s", len,
+ "abcdefghijklmnopqrstuvwxyz");
+ return m;
+}
+
+#define GETOPT_STR "sr:l:p:t:g:P:DRn:M:m:d:SCi6L:0:1:2:Fbq"
+
+static void print_usage(char *prog)
+{
+ printf(
+ "usage: %s OPTS\n"
+ "Required:\n"
+ " -r addr remote address to connect to (client mode only)\n"
+ " -p port port to connect to (client mode)/listen on (server mode)\n"
+ " (default: %d)\n"
+ " -s server mode (default: client mode)\n"
+ " -t timeout seconds (default: none)\n"
+ "\n"
+ "Optional:\n"
+ " -F Restart server loop\n"
+ " -6 IPv6 (default is IPv4)\n"
+ " -P proto protocol for socket: icmp, ospf (default: none)\n"
+ " -D|R datagram (D) / raw (R) socket (default stream)\n"
+ " -l addr local address to bind to\n"
+ "\n"
+ " -d dev bind socket to given device name\n"
+ " -S use setsockopt (IP_UNICAST_IF or IP_MULTICAST_IF)\n"
+ " to set device binding\n"
+ " -C use cmsg and IP_PKTINFO to specify device binding\n"
+ "\n"
+ " -L len send random message of given length\n"
+ " -n num number of times to send message\n"
+ "\n"
+ " -M password use MD5 sum protection\n"
+ " -m prefix/len prefix and length to use for MD5 key\n"
+ " -g grp multicast group (e.g., 239.1.1.1)\n"
+ " -i interactive mode (default is echo and terminate)\n"
+ "\n"
+ " -0 addr Expected local address\n"
+ " -1 addr Expected remote address\n"
+ " -2 dev Expected device name (or index) to receive packet\n"
+ "\n"
+ " -b Bind test only.\n"
+ " -q Be quiet. Run test without printing anything.\n"
+ , prog, DEFAULT_PORT);
+}
+
+int main(int argc, char *argv[])
+{
+ struct sock_args args = {
+ .version = AF_INET,
+ .type = SOCK_STREAM,
+ .port = DEFAULT_PORT,
+ };
+ struct protoent *pe;
+ unsigned int tmp;
+ int forever = 0;
+
+ /* process inputs */
+ extern char *optarg;
+ int rc = 0;
+
+ /*
+ * process input args
+ */
+
+ while ((rc = getopt(argc, argv, GETOPT_STR)) != -1) {
+ switch (rc) {
+ case 's':
+ server_mode = 1;
+ break;
+ case 'F':
+ forever = 1;
+ break;
+ case 'l':
+ args.has_local_ip = 1;
+ if (convert_addr(&args, optarg, ADDR_TYPE_LOCAL) < 0)
+ return 1;
+ break;
+ case 'r':
+ args.has_remote_ip = 1;
+ if (convert_addr(&args, optarg, ADDR_TYPE_REMOTE) < 0)
+ return 1;
+ break;
+ case 'p':
+ if (str_to_uint(optarg, 1, 65535, &tmp) != 0) {
+ fprintf(stderr, "Invalid port\n");
+ return 1;
+ }
+ args.port = (unsigned short) tmp;
+ break;
+ case 't':
+ if (str_to_uint(optarg, 0, INT_MAX,
+ &prog_timeout) != 0) {
+ fprintf(stderr, "Invalid timeout\n");
+ return 1;
+ }
+ break;
+ case 'D':
+ args.type = SOCK_DGRAM;
+ break;
+ case 'R':
+ args.type = SOCK_RAW;
+ args.port = 0;
+ break;
+ case 'P':
+ pe = getprotobyname(optarg);
+ if (pe) {
+ args.protocol = pe->p_proto;
+ } else {
+ if (str_to_uint(optarg, 0, 0xffff, &tmp) != 0) {
+ fprintf(stderr, "Invalid protocol\n");
+ return 1;
+ }
+ args.protocol = tmp;
+ }
+ break;
+ case 'n':
+ iter = atoi(optarg);
+ break;
+ case 'L':
+ msg = random_msg(atoi(optarg));
+ break;
+ case 'M':
+ args.password = optarg;
+ break;
+ case 'm':
+ if (convert_addr(&args, optarg, ADDR_TYPE_MD5_PREFIX) < 0)
+ return 1;
+ break;
+ case 'S':
+ args.use_setsockopt = 1;
+ break;
+ case 'C':
+ args.use_cmsg = 1;
+ break;
+ case 'd':
+ args.dev = optarg;
+ args.ifindex = get_ifidx(optarg);
+ if (args.ifindex < 0) {
+ fprintf(stderr, "Invalid device name\n");
+ return 1;
+ }
+ break;
+ case 'i':
+ interactive = 1;
+ break;
+ case 'g':
+ args.has_grp = 1;
+ if (convert_addr(&args, optarg, ADDR_TYPE_MCAST) < 0)
+ return 1;
+ args.type = SOCK_DGRAM;
+ break;
+ case '6':
+ args.version = AF_INET6;
+ break;
+ case 'b':
+ args.bind_test_only = 1;
+ break;
+ case '0':
+ args.has_expected_laddr = 1;
+ if (convert_addr(&args, optarg,
+ ADDR_TYPE_EXPECTED_LOCAL))
+ return 1;
+ break;
+ case '1':
+ args.has_expected_raddr = 1;
+ if (convert_addr(&args, optarg,
+ ADDR_TYPE_EXPECTED_REMOTE))
+ return 1;
+
+ break;
+ case '2':
+ if (str_to_uint(optarg, 0, INT_MAX, &tmp) == 0) {
+ args.expected_ifindex = (int)tmp;
+ } else {
+ args.expected_ifindex = get_ifidx(optarg);
+ if (args.expected_ifindex < 0) {
+ fprintf(stderr,
+ "Invalid expected device\n");
+ return 1;
+ }
+ }
+ break;
+ case 'q':
+ quiet = 1;
+ break;
+ default:
+ print_usage(argv[0]);
+ return 1;
+ }
+ }
+
+ if (args.password &&
+ ((!args.has_remote_ip && !args.prefix_len) || args.type != SOCK_STREAM)) {
+ log_error("MD5 passwords apply to TCP only and require a remote ip for the password\n");
+ return 1;
+ }
+
+ if (args.prefix_len && !args.password) {
+ log_error("Prefix range for MD5 protection specified without a password\n");
+ return 1;
+ }
+
+ if ((args.use_setsockopt || args.use_cmsg) && !args.ifindex) {
+ fprintf(stderr, "Device binding not specified\n");
+ return 1;
+ }
+ if (args.use_setsockopt || args.use_cmsg)
+ args.dev = NULL;
+
+ if (iter == 0) {
+ fprintf(stderr, "Invalid number of messages to send\n");
+ return 1;
+ }
+
+ if (args.type == SOCK_STREAM && !args.protocol)
+ args.protocol = IPPROTO_TCP;
+ if (args.type == SOCK_DGRAM && !args.protocol)
+ args.protocol = IPPROTO_UDP;
+
+ if ((args.type == SOCK_STREAM || args.type == SOCK_DGRAM) &&
+ args.port == 0) {
+ fprintf(stderr, "Invalid port number\n");
+ return 1;
+ }
+
+ if (!server_mode && !args.has_grp &&
+ !args.has_remote_ip && !args.has_local_ip) {
+ fprintf(stderr,
+ "Local (server mode) or remote IP (client IP) required\n");
+ return 1;
+ }
+
+ if (interactive) {
+ prog_timeout = 0;
+ msg = NULL;
+ }
+
+ if (server_mode) {
+ do {
+ rc = do_server(&args);
+ } while (forever);
+
+ return rc;
+ }
+ return do_client(&args);
+}
diff --git a/tools/testing/selftests/net/pmtu.sh b/tools/testing/selftests/net/pmtu.sh
index ab367e75f095..71a62e7e35b1 100755
--- a/tools/testing/selftests/net/pmtu.sh
+++ b/tools/testing/selftests/net/pmtu.sh
@@ -11,9 +11,9 @@
# R1 and R2 (also implemented with namespaces), with different MTUs:
#
# segment a_r1 segment b_r1 a_r1: 2000
-# .--------------R1--------------. a_r2: 1500
-# A B a_r3: 2000
-# '--------------R2--------------' a_r4: 1400
+# .--------------R1--------------. b_r1: 1400
+# A B a_r2: 2000
+# '--------------R2--------------' b_r2: 1500
# segment a_r2 segment b_r2
#
# Check that PMTU exceptions with the correct PMTU are created. Then
@@ -1249,8 +1249,7 @@ test_list_flush_ipv4_exception() {
done
run_cmd ${ns_a} ping -q -M want -i 0.1 -c 2 -s 1800 "${dst2}"
- # Each exception is printed as two lines
- if [ "$(${ns_a} ip route list cache | wc -l)" -ne 202 ]; then
+ if [ "$(${ns_a} ip -oneline route list cache | wc -l)" -ne 101 ]; then
err " can't list cached exceptions"
fail=1
fi
@@ -1300,7 +1299,7 @@ test_list_flush_ipv6_exception() {
run_cmd ${ns_a} ping -q -M want -i 0.1 -w 1 -s 1800 "${dst_prefix1}${i}"
done
run_cmd ${ns_a} ping -q -M want -i 0.1 -w 1 -s 1800 "${dst2}"
- if [ "$(${ns_a} ip -6 route list cache | wc -l)" -ne 101 ]; then
+ if [ "$(${ns_a} ip -oneline -6 route list cache | wc -l)" -ne 101 ]; then
err " can't list cached exceptions"
fail=1
fi
diff --git a/tools/testing/selftests/net/reuseport_dualstack.c b/tools/testing/selftests/net/reuseport_dualstack.c
index fe3230c55986..fb7a59ed759e 100644
--- a/tools/testing/selftests/net/reuseport_dualstack.c
+++ b/tools/testing/selftests/net/reuseport_dualstack.c
@@ -129,7 +129,7 @@ static void test(int *rcv_fds, int count, int proto)
{
struct epoll_event ev;
int epfd, i, test_fd;
- uint16_t test_family;
+ int test_family;
socklen_t len;
epfd = epoll_create(1);
@@ -146,6 +146,7 @@ static void test(int *rcv_fds, int count, int proto)
send_from_v4(proto);
test_fd = receive_once(epfd, proto);
+ len = sizeof(test_family);
if (getsockopt(test_fd, SOL_SOCKET, SO_DOMAIN, &test_family, &len))
error(1, errno, "failed to read socket domain");
if (test_family != AF_INET)
diff --git a/tools/testing/selftests/net/so_txtime.c b/tools/testing/selftests/net/so_txtime.c
index 53f598f06647..383bac05ac32 100644
--- a/tools/testing/selftests/net/so_txtime.c
+++ b/tools/testing/selftests/net/so_txtime.c
@@ -12,7 +12,11 @@
#include <arpa/inet.h>
#include <error.h>
#include <errno.h>
+#include <inttypes.h>
#include <linux/net_tstamp.h>
+#include <linux/errqueue.h>
+#include <linux/ipv6.h>
+#include <linux/tcp.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
@@ -28,7 +32,7 @@ static int cfg_clockid = CLOCK_TAI;
static bool cfg_do_ipv4;
static bool cfg_do_ipv6;
static uint16_t cfg_port = 8000;
-static int cfg_variance_us = 2000;
+static int cfg_variance_us = 4000;
static uint64_t glob_tstart;
@@ -43,6 +47,9 @@ static struct timed_send cfg_in[MAX_NUM_PKT];
static struct timed_send cfg_out[MAX_NUM_PKT];
static int cfg_num_pkt;
+static int cfg_errq_level;
+static int cfg_errq_type;
+
static uint64_t gettime_ns(void)
{
struct timespec ts;
@@ -90,13 +97,15 @@ static void do_send_one(int fdt, struct timed_send *ts)
}
-static void do_recv_one(int fdr, struct timed_send *ts)
+static bool do_recv_one(int fdr, struct timed_send *ts)
{
int64_t tstop, texpect;
char rbuf[2];
int ret;
ret = recv(fdr, rbuf, sizeof(rbuf), 0);
+ if (ret == -1 && errno == EAGAIN)
+ return true;
if (ret == -1)
error(1, errno, "read");
if (ret != 1)
@@ -105,14 +114,16 @@ static void do_recv_one(int fdr, struct timed_send *ts)
tstop = (gettime_ns() - glob_tstart) / 1000;
texpect = ts->delay_us >= 0 ? ts->delay_us : 0;
- fprintf(stderr, "payload:%c delay:%ld expected:%ld (us)\n",
- rbuf[0], tstop, texpect);
+ fprintf(stderr, "payload:%c delay:%lld expected:%lld (us)\n",
+ rbuf[0], (long long)tstop, (long long)texpect);
if (rbuf[0] != ts->data)
error(1, 0, "payload mismatch. expected %c", ts->data);
if (labs(tstop - texpect) > cfg_variance_us)
error(1, 0, "exceeds variance (%d us)", cfg_variance_us);
+
+ return false;
}
static void do_recv_verify_empty(int fdr)
@@ -125,12 +136,70 @@ static void do_recv_verify_empty(int fdr)
error(1, 0, "recv: not empty as expected (%d, %d)", ret, errno);
}
+static void do_recv_errqueue_timeout(int fdt)
+{
+ char control[CMSG_SPACE(sizeof(struct sock_extended_err)) +
+ CMSG_SPACE(sizeof(struct sockaddr_in6))] = {0};
+ char data[sizeof(struct ipv6hdr) +
+ sizeof(struct tcphdr) + 1];
+ struct sock_extended_err *err;
+ struct msghdr msg = {0};
+ struct iovec iov = {0};
+ struct cmsghdr *cm;
+ int64_t tstamp = 0;
+ int ret;
+
+ iov.iov_base = data;
+ iov.iov_len = sizeof(data);
+
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ msg.msg_control = control;
+ msg.msg_controllen = sizeof(control);
+
+ while (1) {
+ ret = recvmsg(fdt, &msg, MSG_ERRQUEUE);
+ if (ret == -1 && errno == EAGAIN)
+ break;
+ if (ret == -1)
+ error(1, errno, "errqueue");
+ if (msg.msg_flags != MSG_ERRQUEUE)
+ error(1, 0, "errqueue: flags 0x%x\n", msg.msg_flags);
+
+ cm = CMSG_FIRSTHDR(&msg);
+ if (cm->cmsg_level != cfg_errq_level ||
+ cm->cmsg_type != cfg_errq_type)
+ error(1, 0, "errqueue: type 0x%x.0x%x\n",
+ cm->cmsg_level, cm->cmsg_type);
+
+ err = (struct sock_extended_err *)CMSG_DATA(cm);
+ if (err->ee_origin != SO_EE_ORIGIN_TXTIME)
+ error(1, 0, "errqueue: origin 0x%x\n", err->ee_origin);
+ if (err->ee_code != ECANCELED)
+ error(1, 0, "errqueue: code 0x%x\n", err->ee_code);
+
+ tstamp = ((int64_t) err->ee_data) << 32 | err->ee_info;
+ tstamp -= (int64_t) glob_tstart;
+ tstamp /= 1000 * 1000;
+ fprintf(stderr, "send: pkt %c at %" PRId64 "ms dropped\n",
+ data[ret - 1], tstamp);
+
+ msg.msg_flags = 0;
+ msg.msg_controllen = sizeof(control);
+ }
+
+ error(1, 0, "recv: timeout");
+}
+
static void setsockopt_txtime(int fd)
{
struct sock_txtime so_txtime_val = { .clockid = cfg_clockid };
struct sock_txtime so_txtime_val_read = { 0 };
socklen_t vallen = sizeof(so_txtime_val);
+ so_txtime_val.flags = SOF_TXTIME_REPORT_ERRORS;
+
if (setsockopt(fd, SOL_SOCKET, SO_TXTIME,
&so_txtime_val, sizeof(so_txtime_val)))
error(1, errno, "setsockopt txtime");
@@ -194,7 +263,8 @@ static void do_test(struct sockaddr *addr, socklen_t alen)
for (i = 0; i < cfg_num_pkt; i++)
do_send_one(fdt, &cfg_in[i]);
for (i = 0; i < cfg_num_pkt; i++)
- do_recv_one(fdr, &cfg_out[i]);
+ if (do_recv_one(fdr, &cfg_out[i]))
+ do_recv_errqueue_timeout(fdt);
do_recv_verify_empty(fdr);
@@ -280,6 +350,10 @@ int main(int argc, char **argv)
addr6.sin6_family = AF_INET6;
addr6.sin6_port = htons(cfg_port);
addr6.sin6_addr = in6addr_loopback;
+
+ cfg_errq_level = SOL_IPV6;
+ cfg_errq_type = IPV6_RECVERR;
+
do_test((void *)&addr6, sizeof(addr6));
}
@@ -289,6 +363,10 @@ int main(int argc, char **argv)
addr4.sin_family = AF_INET;
addr4.sin_port = htons(cfg_port);
addr4.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ cfg_errq_level = SOL_IP;
+ cfg_errq_type = IP_RECVERR;
+
do_test((void *)&addr4, sizeof(addr4));
}
diff --git a/tools/testing/selftests/net/so_txtime.sh b/tools/testing/selftests/net/so_txtime.sh
index 5aa519328a5b..3f7800eaecb1 100755
--- a/tools/testing/selftests/net/so_txtime.sh
+++ b/tools/testing/selftests/net/so_txtime.sh
@@ -5,7 +5,12 @@
# Run in network namespace
if [[ $# -eq 0 ]]; then
- ./in_netns.sh $0 __subprocess
+ if ! ./in_netns.sh $0 __subprocess; then
+ # test is time sensitive, can be flaky
+ echo "test failed: retry once"
+ ./in_netns.sh $0 __subprocess
+ fi
+
exit $?
fi
@@ -18,7 +23,7 @@ tc qdisc add dev lo root fq
./so_txtime -4 -6 -c mono a,10,b,20 a,10,b,20
./so_txtime -4 -6 -c mono a,20,b,10 b,20,a,20
-if tc qdisc replace dev lo root etf clockid CLOCK_TAI delta 200000; then
+if tc qdisc replace dev lo root etf clockid CLOCK_TAI delta 400000; then
! ./so_txtime -4 -6 -c tai a,-1 a,-1
! ./so_txtime -4 -6 -c tai a,0 a,0
./so_txtime -4 -6 -c tai a,10 a,10
diff --git a/tools/testing/selftests/net/tcp_mmap.c b/tools/testing/selftests/net/tcp_mmap.c
index 31ced79f4f25..35505b31e5cc 100644
--- a/tools/testing/selftests/net/tcp_mmap.c
+++ b/tools/testing/selftests/net/tcp_mmap.c
@@ -71,7 +71,7 @@
#define MSG_ZEROCOPY 0x4000000
#endif
-#define FILE_SZ (1UL << 35)
+#define FILE_SZ (1ULL << 35)
static int cfg_family = AF_INET6;
static socklen_t cfg_alen = sizeof(struct sockaddr_in6);
static int cfg_port = 8787;
@@ -82,7 +82,9 @@ static int zflg; /* zero copy option. (MSG_ZEROCOPY for sender, mmap() for recei
static int xflg; /* hash received data (simple xor) (-h option) */
static int keepflag; /* -k option: receiver shall keep all received file in memory (no munmap() calls) */
-static int chunk_size = 512*1024;
+static size_t chunk_size = 512*1024;
+
+static size_t map_align;
unsigned long htotal;
@@ -118,6 +120,9 @@ void hash_zone(void *zone, unsigned int length)
htotal = temp;
}
+#define ALIGN_UP(x, align_to) (((x) + ((align_to)-1)) & ~((align_to)-1))
+#define ALIGN_PTR_UP(p, ptr_align_to) ((typeof(p))ALIGN_UP((unsigned long)(p), ptr_align_to))
+
void *child_thread(void *arg)
{
unsigned long total_mmap = 0, total = 0;
@@ -126,6 +131,7 @@ void *child_thread(void *arg)
int flags = MAP_SHARED;
struct timeval t0, t1;
char *buffer = NULL;
+ void *raddr = NULL;
void *addr = NULL;
double throughput;
struct rusage ru;
@@ -142,9 +148,13 @@ void *child_thread(void *arg)
goto error;
}
if (zflg) {
- addr = mmap(NULL, chunk_size, PROT_READ, flags, fd, 0);
- if (addr == (void *)-1)
+ raddr = mmap(NULL, chunk_size + map_align, PROT_READ, flags, fd, 0);
+ if (raddr == (void *)-1) {
+ perror("mmap");
zflg = 0;
+ } else {
+ addr = ALIGN_PTR_UP(raddr, map_align);
+ }
}
while (1) {
struct pollfd pfd = { .fd = fd, .events = POLLIN, };
@@ -155,7 +165,7 @@ void *child_thread(void *arg)
socklen_t zc_len = sizeof(zc);
int res;
- zc.address = (__u64)addr;
+ zc.address = (__u64)((unsigned long)addr);
zc.length = chunk_size;
zc.recv_skip_hint = 0;
res = getsockopt(fd, IPPROTO_TCP, TCP_ZEROCOPY_RECEIVE,
@@ -222,7 +232,7 @@ error:
free(buffer);
close(fd);
if (zflg)
- munmap(addr, chunk_size);
+ munmap(raddr, chunk_size + map_align);
pthread_exit(0);
}
@@ -270,6 +280,11 @@ static void setup_sockaddr(int domain, const char *str_addr,
static void do_accept(int fdlisten)
{
+ pthread_attr_t attr;
+
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+
if (setsockopt(fdlisten, SOL_SOCKET, SO_RCVLOWAT,
&chunk_size, sizeof(chunk_size)) == -1) {
perror("setsockopt SO_RCVLOWAT");
@@ -288,7 +303,7 @@ static void do_accept(int fdlisten)
perror("accept");
continue;
}
- res = pthread_create(&th, NULL, child_thread,
+ res = pthread_create(&th, &attr, child_thread,
(void *)(unsigned long)fd);
if (res) {
errno = res;
@@ -298,18 +313,42 @@ static void do_accept(int fdlisten)
}
}
+/* Each thread should reserve a big enough vma to avoid
+ * spinlock collisions in ptl locks.
+ * This size is 2MB on x86_64, and is exported in /proc/meminfo.
+ */
+static unsigned long default_huge_page_size(void)
+{
+ FILE *f = fopen("/proc/meminfo", "r");
+ unsigned long hps = 0;
+ size_t linelen = 0;
+ char *line = NULL;
+
+ if (!f)
+ return 0;
+ while (getline(&line, &linelen, f) > 0) {
+ if (sscanf(line, "Hugepagesize: %lu kB", &hps) == 1) {
+ hps <<= 10;
+ break;
+ }
+ }
+ free(line);
+ fclose(f);
+ return hps;
+}
+
int main(int argc, char *argv[])
{
struct sockaddr_storage listenaddr, addr;
unsigned int max_pacing_rate = 0;
- unsigned long total = 0;
+ size_t total = 0;
char *host = NULL;
int fd, c, on = 1;
char *buffer;
int sflg = 0;
int mss = 0;
- while ((c = getopt(argc, argv, "46p:svr:w:H:zxkP:M:")) != -1) {
+ while ((c = getopt(argc, argv, "46p:svr:w:H:zxkP:M:C:a:")) != -1) {
switch (c) {
case '4':
cfg_family = PF_INET;
@@ -349,10 +388,24 @@ int main(int argc, char *argv[])
case 'P':
max_pacing_rate = atoi(optarg) ;
break;
+ case 'C':
+ chunk_size = atol(optarg);
+ break;
+ case 'a':
+ map_align = atol(optarg);
+ break;
default:
exit(1);
}
}
+ if (!map_align) {
+ map_align = default_huge_page_size();
+ /* if really /proc/meminfo is not helping,
+ * we use the default x86_64 hugepagesize.
+ */
+ if (!map_align)
+ map_align = 2*1024*1024;
+ }
if (sflg) {
int fdlisten = socket(cfg_family, SOCK_STREAM, 0);
@@ -417,7 +470,7 @@ int main(int argc, char *argv[])
zflg = 0;
}
while (total < FILE_SZ) {
- long wr = FILE_SZ - total;
+ ssize_t wr = FILE_SZ - total;
if (wr > chunk_size)
wr = chunk_size;
diff --git a/tools/testing/selftests/net/tls.c b/tools/testing/selftests/net/tls.c
index 4c285b6e1db8..0ea44d975b6c 100644
--- a/tools/testing/selftests/net/tls.c
+++ b/tools/testing/selftests/net/tls.c
@@ -25,10 +25,6 @@
#define TLS_PAYLOAD_MAX_LEN 16384
#define SOL_TLS 282
-#ifndef ENOTSUPP
-#define ENOTSUPP 524
-#endif
-
FIXTURE(tls_basic)
{
int fd, cfd;
@@ -268,6 +264,38 @@ TEST_F(tls, sendmsg_single)
EXPECT_EQ(memcmp(buf, test_str, send_len), 0);
}
+#define MAX_FRAGS 64
+#define SEND_LEN 13
+TEST_F(tls, sendmsg_fragmented)
+{
+ char const *test_str = "test_sendmsg";
+ char buf[SEND_LEN * MAX_FRAGS];
+ struct iovec vec[MAX_FRAGS];
+ struct msghdr msg;
+ int i, frags;
+
+ for (frags = 1; frags <= MAX_FRAGS; frags++) {
+ for (i = 0; i < frags; i++) {
+ vec[i].iov_base = (char *)test_str;
+ vec[i].iov_len = SEND_LEN;
+ }
+
+ memset(&msg, 0, sizeof(struct msghdr));
+ msg.msg_iov = vec;
+ msg.msg_iovlen = frags;
+
+ EXPECT_EQ(sendmsg(self->fd, &msg, 0), SEND_LEN * frags);
+ EXPECT_EQ(recv(self->cfd, buf, SEND_LEN * frags, MSG_WAITALL),
+ SEND_LEN * frags);
+
+ for (i = 0; i < frags; i++)
+ EXPECT_EQ(memcmp(buf + SEND_LEN * i,
+ test_str, SEND_LEN), 0);
+ }
+}
+#undef MAX_FRAGS
+#undef SEND_LEN
+
TEST_F(tls, sendmsg_large)
{
void *mem = malloc(16384);
@@ -898,6 +926,114 @@ TEST_F(tls, nonblocking)
}
}
+static void
+test_mutliproc(struct __test_metadata *_metadata, struct _test_data_tls *self,
+ bool sendpg, unsigned int n_readers, unsigned int n_writers)
+{
+ const unsigned int n_children = n_readers + n_writers;
+ const size_t data = 6 * 1000 * 1000;
+ const size_t file_sz = data / 100;
+ size_t read_bias, write_bias;
+ int i, fd, child_id;
+ char buf[file_sz];
+ pid_t pid;
+
+ /* Only allow multiples for simplicity */
+ ASSERT_EQ(!(n_readers % n_writers) || !(n_writers % n_readers), true);
+ read_bias = n_writers / n_readers ?: 1;
+ write_bias = n_readers / n_writers ?: 1;
+
+ /* prep a file to send */
+ fd = open("/tmp/", O_TMPFILE | O_RDWR, 0600);
+ ASSERT_GE(fd, 0);
+
+ memset(buf, 0xac, file_sz);
+ ASSERT_EQ(write(fd, buf, file_sz), file_sz);
+
+ /* spawn children */
+ for (child_id = 0; child_id < n_children; child_id++) {
+ pid = fork();
+ ASSERT_NE(pid, -1);
+ if (!pid)
+ break;
+ }
+
+ /* parent waits for all children */
+ if (pid) {
+ for (i = 0; i < n_children; i++) {
+ int status;
+
+ wait(&status);
+ EXPECT_EQ(status, 0);
+ }
+
+ return;
+ }
+
+ /* Split threads for reading and writing */
+ if (child_id < n_readers) {
+ size_t left = data * read_bias;
+ char rb[8001];
+
+ while (left) {
+ int res;
+
+ res = recv(self->cfd, rb,
+ left > sizeof(rb) ? sizeof(rb) : left, 0);
+
+ EXPECT_GE(res, 0);
+ left -= res;
+ }
+ } else {
+ size_t left = data * write_bias;
+
+ while (left) {
+ int res;
+
+ ASSERT_EQ(lseek(fd, 0, SEEK_SET), 0);
+ if (sendpg)
+ res = sendfile(self->fd, fd, NULL,
+ left > file_sz ? file_sz : left);
+ else
+ res = send(self->fd, buf,
+ left > file_sz ? file_sz : left, 0);
+
+ EXPECT_GE(res, 0);
+ left -= res;
+ }
+ }
+}
+
+TEST_F(tls, mutliproc_even)
+{
+ test_mutliproc(_metadata, self, false, 6, 6);
+}
+
+TEST_F(tls, mutliproc_readers)
+{
+ test_mutliproc(_metadata, self, false, 4, 12);
+}
+
+TEST_F(tls, mutliproc_writers)
+{
+ test_mutliproc(_metadata, self, false, 10, 2);
+}
+
+TEST_F(tls, mutliproc_sendpage_even)
+{
+ test_mutliproc(_metadata, self, true, 6, 6);
+}
+
+TEST_F(tls, mutliproc_sendpage_readers)
+{
+ test_mutliproc(_metadata, self, true, 4, 12);
+}
+
+TEST_F(tls, mutliproc_sendpage_writers)
+{
+ test_mutliproc(_metadata, self, true, 10, 2);
+}
+
TEST_F(tls, control_msg)
{
if (self->notls)
@@ -1037,11 +1173,11 @@ TEST(non_established) {
/* TLS ULP not supported */
if (errno == ENOENT)
return;
- EXPECT_EQ(errno, ENOTSUPP);
+ EXPECT_EQ(errno, ENOTCONN);
ret = setsockopt(sfd, IPPROTO_TCP, TCP_ULP, "tls", sizeof("tls"));
EXPECT_EQ(ret, -1);
- EXPECT_EQ(errno, ENOTSUPP);
+ EXPECT_EQ(errno, ENOTCONN);
ret = getsockname(sfd, &addr, &len);
ASSERT_EQ(ret, 0);
diff --git a/tools/testing/selftests/net/traceroute.sh b/tools/testing/selftests/net/traceroute.sh
new file mode 100755
index 000000000000..de9ca97abc30
--- /dev/null
+++ b/tools/testing/selftests/net/traceroute.sh
@@ -0,0 +1,322 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Run traceroute/traceroute6 tests
+#
+
+VERBOSE=0
+PAUSE_ON_FAIL=no
+
+################################################################################
+#
+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()
+{
+ local ns
+ local cmd
+ local out
+ local rc
+
+ ns="$1"
+ shift
+ cmd="$*"
+
+ if [ "$VERBOSE" = "1" ]; then
+ printf " COMMAND: $cmd\n"
+ fi
+
+ out=$(eval ip netns exec ${ns} ${cmd} 2>&1)
+ rc=$?
+ if [ "$VERBOSE" = "1" -a -n "$out" ]; then
+ echo " $out"
+ fi
+
+ [ "$VERBOSE" = "1" ] && echo
+
+ return $rc
+}
+
+################################################################################
+# create namespaces and interconnects
+
+create_ns()
+{
+ local ns=$1
+ local addr=$2
+ local addr6=$3
+
+ [ -z "${addr}" ] && addr="-"
+ [ -z "${addr6}" ] && addr6="-"
+
+ ip netns add ${ns}
+
+ ip netns exec ${ns} ip link set lo up
+ if [ "${addr}" != "-" ]; then
+ ip netns exec ${ns} ip addr add dev lo ${addr}
+ fi
+ if [ "${addr6}" != "-" ]; then
+ ip netns exec ${ns} ip -6 addr add dev lo ${addr6}
+ fi
+
+ ip netns exec ${ns} ip ro add unreachable default metric 8192
+ ip netns exec ${ns} ip -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
+
+ ip netns exec ${ns1} ip li add ${ns1_dev} type veth peer name tmp
+ ip netns exec ${ns1} ip li set ${ns1_dev} up
+ ip netns exec ${ns1} ip li set tmp netns ${ns2} name ${ns2_dev}
+ ip netns exec ${ns2} ip li set ${ns2_dev} up
+
+ if [ "${ns1_addr}" != "-" ]; then
+ ip netns exec ${ns1} ip addr add dev ${ns1_dev} ${ns1_addr}
+ fi
+
+ if [ "${ns2_addr}" != "-" ]; then
+ ip netns exec ${ns2} ip addr add dev ${ns2_dev} ${ns2_addr}
+ fi
+
+ if [ "${ns1_addr6}" != "-" ]; then
+ ip netns exec ${ns1} ip addr add dev ${ns1_dev} ${ns1_addr6}
+ fi
+
+ if [ "${ns2_addr6}" != "-" ]; then
+ ip netns exec ${ns2} ip addr add dev ${ns2_dev} ${ns2_addr6}
+ fi
+}
+
+################################################################################
+# traceroute6 test
+#
+# Verify that in this scenario
+#
+# ------------------------ N2
+# | |
+# ------ ------ N3 ----
+# | R1 | | R2 |------|H2|
+# ------ ------ ----
+# | |
+# ------------------------ N1
+# |
+# ----
+# |H1|
+# ----
+#
+# where H1's default route goes through R1 and R1's default route goes
+# through R2 over N2, traceroute6 from H1 to H2 reports R2's address
+# on N2 and not N1.
+#
+# Addresses are assigned as follows:
+#
+# N1: 2000:101::/64
+# N2: 2000:102::/64
+# N3: 2000:103::/64
+#
+# R1's host part of address: 1
+# R2's host part of address: 2
+# H1's host part of address: 3
+# H2's host part of address: 4
+#
+# For example:
+# the IPv6 address of R1's interface on N2 is 2000:102::1/64
+
+cleanup_traceroute6()
+{
+ local ns
+
+ for ns in host-1 host-2 router-1 router-2
+ do
+ ip netns del ${ns} 2>/dev/null
+ done
+}
+
+setup_traceroute6()
+{
+ brdev=br0
+
+ # start clean
+ cleanup_traceroute6
+
+ set -e
+ create_ns host-1
+ create_ns host-2
+ create_ns router-1
+ create_ns router-2
+
+ # Setup N3
+ connect_ns router-2 eth3 - 2000:103::2/64 host-2 eth3 - 2000:103::4/64
+ ip netns exec host-2 ip route add default via 2000:103::2
+
+ # Setup N2
+ connect_ns router-1 eth2 - 2000:102::1/64 router-2 eth2 - 2000:102::2/64
+ ip netns exec router-1 ip route add default via 2000:102::2
+
+ # Setup N1. host-1 and router-2 connect to a bridge in router-1.
+ ip netns exec router-1 ip link add name ${brdev} type bridge
+ ip netns exec router-1 ip link set ${brdev} up
+ ip netns exec router-1 ip addr add 2000:101::1/64 dev ${brdev}
+
+ connect_ns host-1 eth0 - 2000:101::3/64 router-1 eth0 - -
+ ip netns exec router-1 ip link set dev eth0 master ${brdev}
+ ip netns exec host-1 ip route add default via 2000:101::1
+
+ connect_ns router-2 eth1 - 2000:101::2/64 router-1 eth1 - -
+ ip netns exec router-1 ip link set dev eth1 master ${brdev}
+
+ # Prime the network
+ ip netns exec host-1 ping6 -c5 2000:103::4 >/dev/null 2>&1
+
+ set +e
+}
+
+run_traceroute6()
+{
+ if [ ! -x "$(command -v traceroute6)" ]; then
+ echo "SKIP: Could not run IPV6 test without traceroute6"
+ return
+ fi
+
+ setup_traceroute6
+
+ # traceroute6 host-2 from host-1 (expects 2000:102::2)
+ run_cmd host-1 "traceroute6 2000:103::4 | grep -q 2000:102::2"
+ log_test $? 0 "IPV6 traceroute"
+
+ cleanup_traceroute6
+}
+
+################################################################################
+# traceroute test
+#
+# Verify that traceroute from H1 to H2 shows 1.0.1.1 in this scenario
+#
+# 1.0.3.1/24
+# ---- 1.0.1.3/24 1.0.1.1/24 ---- 1.0.2.1/24 1.0.2.4/24 ----
+# |H1|--------------------------|R1|--------------------------|H2|
+# ---- N1 ---- N2 ----
+#
+# where net.ipv4.icmp_errors_use_inbound_ifaddr is set on R1 and
+# 1.0.3.1/24 and 1.0.1.1/24 are respectively R1's primary and secondary
+# address on N1.
+#
+
+cleanup_traceroute()
+{
+ local ns
+
+ for ns in host-1 host-2 router
+ do
+ ip netns del ${ns} 2>/dev/null
+ done
+}
+
+setup_traceroute()
+{
+ # start clean
+ cleanup_traceroute
+
+ set -e
+ create_ns host-1
+ create_ns host-2
+ create_ns router
+
+ connect_ns host-1 eth0 1.0.1.3/24 - \
+ router eth1 1.0.3.1/24 -
+ ip netns exec host-1 ip route add default via 1.0.1.1
+
+ ip netns exec router ip addr add 1.0.1.1/24 dev eth1
+ ip netns exec router sysctl -qw \
+ net.ipv4.icmp_errors_use_inbound_ifaddr=1
+
+ connect_ns host-2 eth0 1.0.2.4/24 - \
+ router eth2 1.0.2.1/24 -
+ ip netns exec host-2 ip route add default via 1.0.2.1
+
+ # Prime the network
+ ip netns exec host-1 ping -c5 1.0.2.4 >/dev/null 2>&1
+
+ set +e
+}
+
+run_traceroute()
+{
+ if [ ! -x "$(command -v traceroute)" ]; then
+ echo "SKIP: Could not run IPV4 test without traceroute"
+ return
+ fi
+
+ setup_traceroute
+
+ # traceroute host-2 from host-1 (expects 1.0.1.1). Takes a while.
+ run_cmd host-1 "traceroute 1.0.2.4 | grep -q 1.0.1.1"
+ log_test $? 0 "IPV4 traceroute"
+
+ cleanup_traceroute
+}
+
+################################################################################
+# Run tests
+
+run_tests()
+{
+ run_traceroute6
+ run_traceroute
+}
+
+################################################################################
+# main
+
+declare -i nfail=0
+declare -i nsuccess=0
+
+while getopts :pv o
+do
+ case $o in
+ p) PAUSE_ON_FAIL=yes;;
+ v) VERBOSE=$(($VERBOSE + 1));;
+ *) exit 1;;
+ esac
+done
+
+run_tests
+
+printf "\nTests passed: %3d\n" ${nsuccess}
+printf "Tests failed: %3d\n" ${nfail}
diff --git a/tools/testing/selftests/net/udpgso.c b/tools/testing/selftests/net/udpgso.c
index b8265ee9923f..c66da6ffd6d8 100644
--- a/tools/testing/selftests/net/udpgso.c
+++ b/tools/testing/selftests/net/udpgso.c
@@ -89,12 +89,9 @@ struct testcase testcases_v4[] = {
.tfail = true,
},
{
- /* send a single MSS: will fail with GSO, because the segment
- * logic in udp4_ufo_fragment demands a gso skb to be > MTU
- */
+ /* send a single MSS: will fall back to no GSO */
.tlen = CONST_MSS_V4,
.gso_len = CONST_MSS_V4,
- .tfail = true,
.r_num_mss = 1,
},
{
@@ -139,10 +136,9 @@ struct testcase testcases_v4[] = {
.tfail = true,
},
{
- /* send a single 1B MSS: will fail, see single MSS above */
+ /* send a single 1B MSS: will fall back to no GSO */
.tlen = 1,
.gso_len = 1,
- .tfail = true,
.r_num_mss = 1,
},
{
@@ -196,12 +192,9 @@ struct testcase testcases_v6[] = {
.tfail = true,
},
{
- /* send a single MSS: will fail with GSO, because the segment
- * logic in udp4_ufo_fragment demands a gso skb to be > MTU
- */
+ /* send a single MSS: will fall back to no GSO */
.tlen = CONST_MSS_V6,
.gso_len = CONST_MSS_V6,
- .tfail = true,
.r_num_mss = 1,
},
{
@@ -246,10 +239,9 @@ struct testcase testcases_v6[] = {
.tfail = true,
},
{
- /* send a single 1B MSS: will fail, see single MSS above */
+ /* send a single 1B MSS: will fall back to no GSO */
.tlen = 1,
.gso_len = 1,
- .tfail = true,
.r_num_mss = 1,
},
{
@@ -448,7 +440,8 @@ static bool __send_one(int fd, struct msghdr *msg, int flags)
if (ret == -1)
error(1, errno, "sendmsg");
if (ret != msg->msg_iov->iov_len)
- error(1, 0, "sendto: %d != %lu", ret, msg->msg_iov->iov_len);
+ error(1, 0, "sendto: %d != %llu", ret,
+ (unsigned long long)msg->msg_iov->iov_len);
if (msg->msg_flags)
error(1, 0, "sendmsg: return flags 0x%x\n", msg->msg_flags);
diff --git a/tools/testing/selftests/net/udpgso_bench_tx.c b/tools/testing/selftests/net/udpgso_bench_tx.c
index ada99496634a..17512a43885e 100644
--- a/tools/testing/selftests/net/udpgso_bench_tx.c
+++ b/tools/testing/selftests/net/udpgso_bench_tx.c
@@ -405,7 +405,8 @@ static int send_udp_segment(int fd, char *data)
if (ret == -1)
error(1, errno, "sendmsg");
if (ret != iov.iov_len)
- error(1, 0, "sendmsg: %u != %lu\n", ret, iov.iov_len);
+ error(1, 0, "sendmsg: %u != %llu\n", ret,
+ (unsigned long long)iov.iov_len);
return 1;
}
diff --git a/tools/testing/selftests/net/xfrm_policy.sh b/tools/testing/selftests/net/xfrm_policy.sh
index 5445943bf07f..7a1bf94c5bd3 100755
--- a/tools/testing/selftests/net/xfrm_policy.sh
+++ b/tools/testing/selftests/net/xfrm_policy.sh
@@ -106,6 +106,13 @@ do_overlap()
#
# 10.0.0.0/24 and 10.0.1.0/24 nodes have been merged as 10.0.0.0/23.
ip -net $ns xfrm policy add src 10.1.0.0/24 dst 10.0.0.0/23 dir fwd priority 200 action block
+
+ # similar to above: add policies (with partially random address), with shrinking prefixes.
+ for p in 29 28 27;do
+ for k in $(seq 1 32); do
+ ip -net $ns xfrm policy add src 10.253.1.$((RANDOM%255))/$p dst 10.254.1.$((RANDOM%255))/$p dir fwd priority $((200+k)) action block 2>/dev/null
+ done
+ done
}
do_esp_policy_get_check() {
OpenPOWER on IntegriCloud