aboutsummaryrefslogtreecommitdiffstats
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/.gitignore4
-rw-r--r--tools/testing/selftests/net/Makefile7
-rw-r--r--tools/testing/selftests/net/config4
-rwxr-xr-xtools/testing/selftests/net/fib-onlink-tests.sh48
-rwxr-xr-xtools/testing/selftests/net/fib_nexthop_multiprefix.sh290
-rwxr-xr-xtools/testing/selftests/net/fib_nexthops.sh1026
-rwxr-xr-xtools/testing/selftests/net/fib_rule_tests.sh10
-rwxr-xr-xtools/testing/selftests/net/fib_tests.sh35
-rwxr-xr-xtools/testing/selftests/net/forwarding/gre_inner_v4_multipath.sh305
-rwxr-xr-xtools/testing/selftests/net/forwarding/gre_inner_v6_multipath.sh306
-rwxr-xr-xtools/testing/selftests/net/forwarding/ip6gre_inner_v4_multipath.sh304
-rwxr-xr-xtools/testing/selftests/net/forwarding/ip6gre_inner_v6_multipath.sh305
-rwxr-xr-xtools/testing/selftests/net/forwarding/router_broadcast.sh5
-rwxr-xr-xtools/testing/selftests/net/forwarding/router_mpath_nh.sh359
-rwxr-xr-xtools/testing/selftests/net/forwarding/tc_flower.sh60
-rwxr-xr-xtools/testing/selftests/net/forwarding/tc_flower_router.sh172
-rwxr-xr-xtools/testing/selftests/net/forwarding/tc_shblocks.sh29
-rwxr-xr-xtools/testing/selftests/net/icmp_redirect.sh534
-rw-r--r--tools/testing/selftests/net/ipv6_flowlabel.c229
-rwxr-xr-xtools/testing/selftests/net/ipv6_flowlabel.sh21
-rw-r--r--tools/testing/selftests/net/ipv6_flowlabel_mgr.c199
-rwxr-xr-xtools/testing/selftests/net/pmtu.sh401
-rw-r--r--tools/testing/selftests/net/psock_fanout.c16
-rw-r--r--tools/testing/selftests/net/psock_lib.h16
-rw-r--r--tools/testing/selftests/net/psock_tpacket.c16
-rwxr-xr-xtools/testing/selftests/net/route_localnet.sh74
-rwxr-xr-xtools/testing/selftests/net/rtnetlink.sh57
-rwxr-xr-xtools/testing/selftests/net/run_afpackettests14
-rw-r--r--tools/testing/selftests/net/so_txtime.c296
-rwxr-xr-xtools/testing/selftests/net/so_txtime.sh31
-rw-r--r--tools/testing/selftests/net/tcp_fastopen_backup_key.c335
-rwxr-xr-xtools/testing/selftests/net/tcp_fastopen_backup_key.sh55
-rw-r--r--tools/testing/selftests/net/tcp_inq.c12
-rw-r--r--tools/testing/selftests/net/tcp_mmap.c16
-rwxr-xr-xtools/testing/selftests/net/test_blackhole_dev.sh11
-rw-r--r--tools/testing/selftests/net/tls.c60
-rw-r--r--tools/testing/selftests/net/txring_overwrite.c2
-rwxr-xr-xtools/testing/selftests/net/udpgso_bench.sh63
-rw-r--r--tools/testing/selftests/net/udpgso_bench_tx.c309
-rwxr-xr-xtools/testing/selftests/net/xfrm_policy.sh27
40 files changed, 5858 insertions, 205 deletions
diff --git a/tools/testing/selftests/net/.gitignore b/tools/testing/selftests/net/.gitignore
index 6f81130605d7..4ce0bc1612f5 100644
--- a/tools/testing/selftests/net/.gitignore
+++ b/tools/testing/selftests/net/.gitignore
@@ -17,3 +17,7 @@ tcp_inq
tls
txring_overwrite
ip_defrag
+so_txtime
+flowlabel
+flowlabel_mgr
+tcp_fastopen_backup_key
diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile
index 1e6d14d2825c..1b24e36b4047 100644
--- a/tools/testing/selftests/net/Makefile
+++ b/tools/testing/selftests/net/Makefile
@@ -5,16 +5,19 @@ CFLAGS = -Wall -Wl,--no-as-needed -O2 -g
CFLAGS += -I../../../../usr/include/
TEST_PROGS := run_netsocktests run_afpackettests test_bpf.sh netdevice.sh \
- rtnetlink.sh xfrm_policy.sh
+ rtnetlink.sh xfrm_policy.sh test_blackhole_dev.sh
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
+TEST_PROGS += test_vxlan_fdb_changelink.sh so_txtime.sh ipv6_flowlabel.sh
+TEST_PROGS += tcp_fastopen_backup_key.sh
TEST_PROGS_EXTENDED := in_netns.sh
TEST_GEN_FILES = socket
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_PROGS = reuseport_bpf reuseport_bpf_cpu reuseport_bpf_numa
TEST_GEN_PROGS += reuseport_dualstack reuseaddr_conflict tls
diff --git a/tools/testing/selftests/net/config b/tools/testing/selftests/net/config
index 474040448601..b8503a8119b0 100644
--- a/tools/testing/selftests/net/config
+++ b/tools/testing/selftests/net/config
@@ -25,3 +25,7 @@ CONFIG_NF_TABLES_IPV6=y
CONFIG_NF_TABLES_IPV4=y
CONFIG_NFT_CHAIN_NAT_IPV6=m
CONFIG_NFT_CHAIN_NAT_IPV4=m
+CONFIG_NET_SCH_FQ=m
+CONFIG_NET_SCH_ETF=m
+CONFIG_TEST_BLACKHOLE_DEV=m
+CONFIG_KALLSYMS=y
diff --git a/tools/testing/selftests/net/fib-onlink-tests.sh b/tools/testing/selftests/net/fib-onlink-tests.sh
index 864f865eee55..c287b90b8af8 100755
--- a/tools/testing/selftests/net/fib-onlink-tests.sh
+++ b/tools/testing/selftests/net/fib-onlink-tests.sh
@@ -4,6 +4,7 @@
# IPv4 and IPv6 onlink tests
PAUSE_ON_FAIL=${PAUSE_ON_FAIL:=no}
+VERBOSE=0
# Network interfaces
# - odd in current namespace; even in peer ns
@@ -91,10 +92,10 @@ log_test()
if [ ${rc} -eq ${expected} ]; then
nsuccess=$((nsuccess+1))
- printf "\n TEST: %-50s [ OK ]\n" "${msg}"
+ printf " TEST: %-50s [ OK ]\n" "${msg}"
else
nfail=$((nfail+1))
- printf "\n TEST: %-50s [FAIL]\n" "${msg}"
+ printf " TEST: %-50s [FAIL]\n" "${msg}"
if [ "${PAUSE_ON_FAIL}" = "yes" ]; then
echo
echo "hit enter to continue, 'q' to quit"
@@ -121,9 +122,23 @@ log_subsection()
run_cmd()
{
- echo
- echo "COMMAND: $*"
- eval $*
+ local cmd="$*"
+ local out
+ local rc
+
+ if [ "$VERBOSE" = "1" ]; then
+ printf " COMMAND: $cmd\n"
+ fi
+
+ out=$(eval $cmd 2>&1)
+ rc=$?
+ if [ "$VERBOSE" = "1" -a -n "$out" ]; then
+ echo " $out"
+ fi
+
+ [ "$VERBOSE" = "1" ] && echo
+
+ return $rc
}
get_linklocal()
@@ -451,11 +466,34 @@ run_onlink_tests()
}
################################################################################
+# usage
+
+usage()
+{
+ cat <<EOF
+usage: ${0##*/} OPTS
+
+ -p Pause on fail
+ -v verbose mode (show commands and output)
+EOF
+}
+
+################################################################################
# main
nsuccess=0
nfail=0
+while getopts :t:pPhv o
+do
+ case $o in
+ p) PAUSE_ON_FAIL=yes;;
+ v) VERBOSE=$(($VERBOSE + 1));;
+ h) usage; exit 0;;
+ *) usage; exit 1;;
+ esac
+done
+
cleanup
setup
run_onlink_tests
diff --git a/tools/testing/selftests/net/fib_nexthop_multiprefix.sh b/tools/testing/selftests/net/fib_nexthop_multiprefix.sh
new file mode 100755
index 000000000000..e6828732843e
--- /dev/null
+++ b/tools/testing/selftests/net/fib_nexthop_multiprefix.sh
@@ -0,0 +1,290 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Validate cached routes in fib{6}_nh that is used by multiple prefixes.
+# Validate a different # exception is generated in h0 for each remote host.
+#
+# h1
+# /
+# h0 - r1 - h2
+# \
+# h3
+#
+# routing in h0 to hN is done with nexthop objects.
+
+PAUSE_ON_FAIL=no
+VERBOSE=0
+
+################################################################################
+# helpers
+
+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
+
+ [ "$VERBOSE" = "1" ] && echo
+}
+
+run_cmd()
+{
+ local cmd="$*"
+ local out
+ local rc
+
+ if [ "$VERBOSE" = "1" ]; then
+ echo "COMMAND: $cmd"
+ fi
+
+ out=$(eval $cmd 2>&1)
+ rc=$?
+ if [ "$VERBOSE" = "1" -a -n "$out" ]; then
+ echo "$out"
+ fi
+
+ [ "$VERBOSE" = "1" ] && echo
+
+ return $rc
+}
+
+################################################################################
+# config
+
+create_ns()
+{
+ local ns=${1}
+
+ ip netns del ${ns} 2>/dev/null
+
+ ip netns add ${ns}
+ ip -netns ${ns} addr add 127.0.0.1/8 dev lo
+ ip -netns ${ns} link set lo up
+
+ ip netns exec ${ns} sysctl -q -w net.ipv6.conf.all.keep_addr_on_down=1
+ case ${ns} in
+ h*)
+ ip netns exec $ns sysctl -q -w net.ipv6.conf.all.forwarding=0
+ ;;
+ r*)
+ ip netns exec $ns sysctl -q -w net.ipv4.ip_forward=1
+ ip netns exec $ns sysctl -q -w net.ipv6.conf.all.forwarding=1
+ ;;
+ esac
+}
+
+setup()
+{
+ local ns
+ local i
+
+ #set -e
+
+ for ns in h0 r1 h1 h2 h3
+ do
+ create_ns ${ns}
+ done
+
+ #
+ # create interconnects
+ #
+
+ for i in 0 1 2 3
+ do
+ ip -netns h${i} li add eth0 type veth peer name r1h${i}
+ ip -netns h${i} li set eth0 up
+ ip -netns h${i} li set r1h${i} netns r1 name eth${i} up
+
+ ip -netns h${i} addr add dev eth0 172.16.10${i}.1/24
+ ip -netns h${i} -6 addr add dev eth0 2001:db8:10${i}::1/64
+ ip -netns r1 addr add dev eth${i} 172.16.10${i}.254/24
+ ip -netns r1 -6 addr add dev eth${i} 2001:db8:10${i}::64/64
+ done
+
+ ip -netns h0 nexthop add id 4 via 172.16.100.254 dev eth0
+ ip -netns h0 nexthop add id 6 via 2001:db8:100::64 dev eth0
+
+ # routing from h0 to h1-h3 and back
+ for i in 1 2 3
+ do
+ ip -netns h0 ro add 172.16.10${i}.0/24 nhid 4
+ ip -netns h${i} ro add 172.16.100.0/24 via 172.16.10${i}.254
+
+ ip -netns h0 -6 ro add 2001:db8:10${i}::/64 nhid 6
+ ip -netns h${i} -6 ro add 2001:db8:100::/64 via 2001:db8:10${i}::64
+ done
+
+ if [ "$VERBOSE" = "1" ]; then
+ echo
+ echo "host 1 config"
+ ip -netns h0 li sh
+ ip -netns h0 ro sh
+ ip -netns h0 -6 ro sh
+ fi
+
+ #set +e
+}
+
+cleanup()
+{
+ for n in h1 r1 h2 h3 h4
+ do
+ ip netns del ${n} 2>/dev/null
+ done
+}
+
+change_mtu()
+{
+ local hostid=$1
+ local mtu=$2
+
+ run_cmd ip -netns h${hostid} li set eth0 mtu ${mtu}
+ run_cmd ip -netns r1 li set eth${hostid} mtu ${mtu}
+}
+
+################################################################################
+# validate exceptions
+
+validate_v4_exception()
+{
+ local i=$1
+ local mtu=$2
+ local ping_sz=$3
+ local dst="172.16.10${i}.1"
+ local h0=172.16.100.1
+ local r1=172.16.100.254
+ local rc
+
+ if [ ${ping_sz} != "0" ]; then
+ run_cmd ip netns exec h0 ping -s ${ping_sz} -c5 -w5 ${dst}
+ fi
+
+ if [ "$VERBOSE" = "1" ]; then
+ echo "Route get"
+ ip -netns h0 ro get ${dst}
+ echo "Searching for:"
+ echo " cache .* mtu ${mtu}"
+ echo
+ fi
+
+ ip -netns h0 ro get ${dst} | \
+ grep -q "cache .* mtu ${mtu}"
+ rc=$?
+
+ log_test $rc 0 "IPv4: host 0 to host ${i}, mtu ${mtu}"
+}
+
+validate_v6_exception()
+{
+ local i=$1
+ local mtu=$2
+ local ping_sz=$3
+ local dst="2001:db8:10${i}::1"
+ local h0=2001:db8:100::1
+ local r1=2001:db8:100::64
+ local rc
+
+ if [ ${ping_sz} != "0" ]; then
+ run_cmd ip netns exec h0 ping6 -s ${ping_sz} -c5 -w5 ${dst}
+ fi
+
+ if [ "$VERBOSE" = "1" ]; then
+ echo "Route get"
+ ip -netns h0 -6 ro get ${dst}
+ echo "Searching for:"
+ echo " ${dst} from :: via ${r1} dev eth0 src ${h0} .* mtu ${mtu}"
+ echo
+ fi
+
+ ip -netns h0 -6 ro get ${dst} | \
+ grep -q "${dst} from :: via ${r1} dev eth0 src ${h0} .* mtu ${mtu}"
+ rc=$?
+
+ log_test $rc 0 "IPv6: host 0 to host ${i}, mtu ${mtu}"
+}
+
+################################################################################
+# main
+
+while getopts :pv o
+do
+ case $o in
+ p) PAUSE_ON_FAIL=yes;;
+ v) VERBOSE=1;;
+ esac
+done
+
+cleanup
+setup
+sleep 2
+
+cpus=$(cat /sys/devices/system/cpu/online)
+cpus="$(seq ${cpus/-/ })"
+ret=0
+for i in 1 2 3
+do
+ # generate a cached route per-cpu
+ for c in ${cpus}; 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
+ [ $? -ne 0 ] && printf "\nERROR: ping6 to h${i} failed\n" && ret=1
+
+ [ $ret -ne 0 ] && break
+ done
+ [ $ret -ne 0 ] && break
+done
+
+if [ $ret -eq 0 ]; then
+ # generate different exceptions in h0 for h1, h2 and h3
+ change_mtu 1 1300
+ validate_v4_exception 1 1300 1350
+ validate_v6_exception 1 1300 1350
+ echo
+
+ change_mtu 2 1350
+ validate_v4_exception 2 1350 1400
+ validate_v6_exception 2 1350 1400
+ echo
+
+ change_mtu 3 1400
+ validate_v4_exception 3 1400 1450
+ validate_v6_exception 3 1400 1450
+ echo
+
+ validate_v4_exception 1 1300 0
+ validate_v6_exception 1 1300 0
+ echo
+
+ validate_v4_exception 2 1350 0
+ validate_v6_exception 2 1350 0
+ echo
+
+ validate_v4_exception 3 1400 0
+ validate_v6_exception 3 1400 0
+
+ # targeted deletes to trigger cleanup paths in kernel
+ ip -netns h0 ro del 172.16.102.0/24 nhid 4
+ ip -netns h0 -6 ro del 2001:db8:102::/64 nhid 6
+
+ ip -netns h0 nexthop del id 4
+ ip -netns h0 nexthop del id 6
+fi
+
+cleanup
diff --git a/tools/testing/selftests/net/fib_nexthops.sh b/tools/testing/selftests/net/fib_nexthops.sh
new file mode 100755
index 000000000000..c5c93d5fb3ad
--- /dev/null
+++ b/tools/testing/selftests/net/fib_nexthops.sh
@@ -0,0 +1,1026 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# ns: me | ns: peer | ns: remote
+# 2001:db8:91::1 | 2001:db8:91::2 |
+# 172.16.1.1 | 172.16.1.2 |
+# veth1 <---|---> veth2 |
+# | veth5 <--|--> veth6 172.16.101.1
+# veth3 <---|---> veth4 | 2001:db8:101::1
+# 172.16.2.1 | 172.16.2.2 |
+# 2001:db8:92::1 | 2001:db8:92::2 |
+#
+# This test is for checking IPv4 and IPv6 FIB behavior with nexthop
+# objects. Device reference counts and network namespace cleanup tested
+# by use of network namespace for peer.
+
+ret=0
+# Kselftest framework requirement - SKIP code is 4.
+ksft_skip=4
+
+# all tests in this script. Can be overridden with -t option
+IPV4_TESTS="ipv4_fcnal ipv4_grp_fcnal ipv4_withv6_fcnal ipv4_fcnal_runtime"
+IPV6_TESTS="ipv6_fcnal ipv6_grp_fcnal ipv6_fcnal_runtime"
+
+ALL_TESTS="basic ${IPV4_TESTS} ${IPV6_TESTS}"
+TESTS="${ALL_TESTS}"
+VERBOSE=0
+PAUSE_ON_FAIL=no
+PAUSE=no
+
+nsid=100
+
+################################################################################
+# utilities
+
+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 [ "$VERBOSE" = "1" ]; then
+ echo " rc=$rc, expected $expected"
+ fi
+
+ 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
+
+ [ "$VERBOSE" = "1" ] && echo
+}
+
+run_cmd()
+{
+ local cmd="$1"
+ local out
+ local stderr="2>/dev/null"
+
+ if [ "$VERBOSE" = "1" ]; then
+ printf "COMMAND: $cmd\n"
+ stderr=
+ fi
+
+ out=$(eval $cmd $stderr)
+ rc=$?
+ if [ "$VERBOSE" = "1" -a -n "$out" ]; then
+ echo " $out"
+ fi
+
+ return $rc
+}
+
+get_linklocal()
+{
+ local dev=$1
+ local ns
+ local addr
+
+ [ -n "$2" ] && ns="-netns $2"
+ addr=$(ip $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_ns()
+{
+ local n=${1}
+
+ ip netns del ${n} 2>/dev/null
+
+ set -e
+ ip netns add ${n}
+ ip netns set ${n} $((nsid++))
+ ip -netns ${n} addr add 127.0.0.1/8 dev lo
+ ip -netns ${n} link set lo up
+
+ ip netns exec ${n} sysctl -qw net.ipv4.ip_forward=1
+ ip netns exec ${n} sysctl -qw net.ipv4.fib_multipath_use_neigh=1
+ ip netns exec ${n} sysctl -qw net.ipv4.conf.default.ignore_routes_with_linkdown=1
+ ip netns exec ${n} sysctl -qw net.ipv6.conf.all.keep_addr_on_down=1
+ ip netns exec ${n} sysctl -qw net.ipv6.conf.all.forwarding=1
+ ip netns exec ${n} sysctl -qw net.ipv6.conf.default.forwarding=1
+ ip netns exec ${n} sysctl -qw net.ipv6.conf.default.ignore_routes_with_linkdown=1
+ ip netns exec ${n} sysctl -qw net.ipv6.conf.all.accept_dad=0
+ ip netns exec ${n} sysctl -qw net.ipv6.conf.default.accept_dad=0
+
+ set +e
+}
+
+setup()
+{
+ cleanup
+
+ create_ns me
+ create_ns peer
+ create_ns remote
+
+ IP="ip -netns me"
+ set -e
+ $IP li add veth1 type veth peer name veth2
+ $IP li set veth1 up
+ $IP addr add 172.16.1.1/24 dev veth1
+ $IP -6 addr add 2001:db8:91::1/64 dev veth1
+
+ $IP li add veth3 type veth peer name veth4
+ $IP li set veth3 up
+ $IP addr add 172.16.2.1/24 dev veth3
+ $IP -6 addr add 2001:db8:92::1/64 dev veth3
+
+ $IP li set veth2 netns peer up
+ ip -netns peer addr add 172.16.1.2/24 dev veth2
+ ip -netns peer -6 addr add 2001:db8:91::2/64 dev veth2
+
+ $IP li set veth4 netns peer up
+ ip -netns peer addr add 172.16.2.2/24 dev veth4
+ ip -netns peer -6 addr add 2001:db8:92::2/64 dev veth4
+
+ ip -netns remote li add veth5 type veth peer name veth6
+ ip -netns remote li set veth5 up
+ ip -netns remote addr add dev veth5 172.16.101.1/24
+ ip -netns remote addr add dev veth5 2001:db8:101::1/64
+ ip -netns remote ro add 172.16.0.0/22 via 172.16.101.2
+ ip -netns remote -6 ro add 2001:db8:90::/40 via 2001:db8:101::2
+
+ ip -netns remote li set veth6 netns peer up
+ ip -netns peer addr add dev veth6 172.16.101.2/24
+ ip -netns peer addr add dev veth6 2001:db8:101::2/64
+ set +e
+}
+
+cleanup()
+{
+ local ns
+
+ for ns in me peer remote; do
+ ip netns del ${ns} 2>/dev/null
+ done
+}
+
+check_output()
+{
+ local out="$1"
+ local expected="$2"
+ local rc=0
+
+ [ "${out}" = "${expected}" ] && return 0
+
+ if [ -z "${out}" ]; then
+ if [ "$VERBOSE" = "1" ]; then
+ printf "\nNo entry found\n"
+ printf "Expected:\n"
+ printf " ${expected}\n"
+ fi
+ return 1
+ fi
+
+ out=$(echo ${out})
+ if [ "${out}" != "${expected}" ]; then
+ rc=1
+ if [ "${VERBOSE}" = "1" ]; then
+ printf " Unexpected entry. Have:\n"
+ printf " ${out}\n"
+ printf " Expected:\n"
+ printf " ${expected}\n\n"
+ fi
+ fi
+
+ return $rc
+}
+
+check_nexthop()
+{
+ local nharg="$1"
+ local expected="$2"
+ local out
+
+ out=$($IP nexthop ls ${nharg} 2>/dev/null)
+
+ check_output "${out}" "${expected}"
+}
+
+check_route()
+{
+ local pfx="$1"
+ local expected="$2"
+ local out
+
+ out=$($IP route ls match ${pfx} 2>/dev/null)
+
+ check_output "${out}" "${expected}"
+}
+
+check_route6()
+{
+ local pfx="$1"
+ local expected="$2"
+ local out
+
+ out=$($IP -6 route ls match ${pfx} 2>/dev/null)
+
+ check_output "${out}" "${expected}"
+}
+
+################################################################################
+# basic operations (add, delete, replace) on nexthops and nexthop groups
+#
+# IPv6
+
+ipv6_fcnal()
+{
+ local rc
+
+ echo
+ echo "IPv6"
+ echo "----------------------"
+
+ run_cmd "$IP nexthop add id 52 via 2001:db8:91::2 dev veth1"
+ rc=$?
+ log_test $rc 0 "Create nexthop with id, gw, dev"
+ if [ $rc -ne 0 ]; then
+ echo "Basic IPv6 create fails; can not continue"
+ return 1
+ fi
+
+ 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"
+
+ run_cmd "$IP nexthop del id 52"
+ log_test $? 0 "Delete nexthop by id"
+ check_nexthop "id 52" ""
+
+ #
+ # gw, device spec
+ #
+ # gw validation, no device - fails since dev required
+ run_cmd "$IP nexthop add id 52 via 2001:db8:92::3"
+ log_test $? 2 "Create nexthop - gw only"
+
+ # gw is not reachable throught given dev
+ run_cmd "$IP nexthop add id 53 via 2001:db8:3::3 dev veth1"
+ log_test $? 2 "Create nexthop - invalid gw+dev combination"
+
+ # onlink arg overrides gw+dev lookup
+ run_cmd "$IP nexthop add id 53 via 2001:db8:3::3 dev veth1 onlink"
+ log_test $? 0 "Create nexthop - gw+dev and onlink"
+
+ # admin down should delete nexthops
+ set -e
+ run_cmd "$IP -6 nexthop add id 55 via 2001:db8:91::3 dev veth1"
+ run_cmd "$IP nexthop add id 56 via 2001:db8:91::4 dev veth1"
+ run_cmd "$IP nexthop add id 57 via 2001:db8:91::5 dev veth1"
+ run_cmd "$IP li set dev veth1 down"
+ set +e
+ check_nexthop "dev veth1" ""
+ log_test $? 0 "Nexthops removed on admin down"
+}
+
+ipv6_grp_fcnal()
+{
+ local rc
+
+ echo
+ echo "IPv6 groups functional"
+ echo "----------------------"
+
+ # basic functionality: create a nexthop group, default weight
+ run_cmd "$IP nexthop add id 61 via 2001:db8:91::2 dev veth1"
+ run_cmd "$IP nexthop add id 101 group 61"
+ log_test $? 0 "Create nexthop group with single nexthop"
+
+ # get nexthop group
+ run_cmd "$IP nexthop get id 101"
+ log_test $? 0 "Get nexthop group by id"
+ check_nexthop "id 101" "id 101 group 61"
+
+ # delete nexthop group
+ run_cmd "$IP nexthop del id 101"
+ log_test $? 0 "Delete nexthop group by id"
+ check_nexthop "id 101" ""
+
+ $IP nexthop flush >/dev/null 2>&1
+ check_nexthop "id 101" ""
+
+ #
+ # create group with multiple nexthops - mix of gw and dev only
+ #
+ run_cmd "$IP nexthop add id 62 via 2001:db8:91::2 dev veth1"
+ run_cmd "$IP nexthop add id 63 via 2001:db8:91::3 dev veth1"
+ run_cmd "$IP nexthop add id 64 via 2001:db8:91::4 dev veth1"
+ run_cmd "$IP nexthop add id 65 dev veth1"
+ run_cmd "$IP nexthop add id 102 group 62/63/64/65"
+ log_test $? 0 "Nexthop group with multiple nexthops"
+ check_nexthop "id 102" "id 102 group 62/63/64/65"
+
+ # Delete nexthop in a group and group is updated
+ run_cmd "$IP nexthop del id 63"
+ check_nexthop "id 102" "id 102 group 62/64/65"
+ log_test $? 0 "Nexthop group updated when entry is deleted"
+
+ # create group with multiple weighted nexthops
+ run_cmd "$IP nexthop add id 63 via 2001:db8:91::3 dev veth1"
+ run_cmd "$IP nexthop add id 103 group 62/63,2/64,3/65,4"
+ log_test $? 0 "Nexthop group with weighted nexthops"
+ check_nexthop "id 103" "id 103 group 62/63,2/64,3/65,4"
+
+ # Delete nexthop in a weighted group and group is updated
+ run_cmd "$IP nexthop del id 63"
+ check_nexthop "id 103" "id 103 group 62/64,3/65,4"
+ log_test $? 0 "Weighted nexthop group updated when entry is deleted"
+
+ # admin down - nexthop is removed from group
+ run_cmd "$IP li set dev veth1 down"
+ check_nexthop "dev veth1" ""
+ log_test $? 0 "Nexthops in groups removed on admin down"
+
+ # expect groups to have been deleted as well
+ check_nexthop "" ""
+
+ run_cmd "$IP li set dev veth1 up"
+
+ $IP nexthop flush >/dev/null 2>&1
+
+ # group with nexthops using different devices
+ set -e
+ run_cmd "$IP nexthop add id 62 via 2001:db8:91::2 dev veth1"
+ run_cmd "$IP nexthop add id 63 via 2001:db8:91::3 dev veth1"
+ run_cmd "$IP nexthop add id 64 via 2001:db8:91::4 dev veth1"
+ run_cmd "$IP nexthop add id 65 via 2001:db8:91::5 dev veth1"
+
+ run_cmd "$IP nexthop add id 72 via 2001:db8:92::2 dev veth3"
+ run_cmd "$IP nexthop add id 73 via 2001:db8:92::3 dev veth3"
+ run_cmd "$IP nexthop add id 74 via 2001:db8:92::4 dev veth3"
+ run_cmd "$IP nexthop add id 75 via 2001:db8:92::5 dev veth3"
+ set +e
+
+ # multiple groups with same nexthop
+ run_cmd "$IP nexthop add id 104 group 62"
+ run_cmd "$IP nexthop add id 105 group 62"
+ check_nexthop "group" "id 104 group 62 id 105 group 62"
+ log_test $? 0 "Multiple groups with same nexthop"
+
+ run_cmd "$IP nexthop flush groups"
+ [ $? -ne 0 ] && return 1
+
+ # on admin down of veth1, it should be removed from the group
+ run_cmd "$IP nexthop add id 105 group 62/63/72/73/64"
+ run_cmd "$IP li set veth1 down"
+ check_nexthop "id 105" "id 105 group 72/73"
+ log_test $? 0 "Nexthops in group removed on admin down - mixed group"
+
+ run_cmd "$IP nexthop add id 106 group 105/74"
+ log_test $? 2 "Nexthop group can not have a group as an entry"
+
+ # a group can have a blackhole entry only if it is the only
+ # nexthop in the group. Needed for atomic replace with an
+ # actual nexthop group
+ run_cmd "$IP -6 nexthop add id 31 blackhole"
+ run_cmd "$IP nexthop add id 107 group 31"
+ log_test $? 0 "Nexthop group with a blackhole entry"
+
+ run_cmd "$IP nexthop add id 108 group 31/24"
+ log_test $? 2 "Nexthop group can not have a blackhole and another nexthop"
+}
+
+ipv6_fcnal_runtime()
+{
+ local rc
+
+ echo
+ echo "IPv6 functional runtime"
+ echo "-----------------------"
+
+ sleep 5
+
+ #
+ # IPv6 - the basics
+ #
+ run_cmd "$IP nexthop add id 81 via 2001:db8:91::2 dev veth1"
+ run_cmd "$IP ro add 2001:db8:101::1/128 nhid 81"
+ log_test $? 0 "Route add"
+
+ run_cmd "$IP ro delete 2001:db8:101::1/128 nhid 81"
+ log_test $? 0 "Route delete"
+
+ run_cmd "$IP ro add 2001:db8:101::1/128 nhid 81"
+ run_cmd "ip netns exec me ping -c1 -w1 2001:db8:101::1"
+ log_test $? 0 "Ping with nexthop"
+
+ run_cmd "$IP nexthop add id 82 via 2001:db8:92::2 dev veth3"
+ run_cmd "$IP nexthop add id 122 group 81/82"
+ run_cmd "$IP ro replace 2001:db8:101::1/128 nhid 122"
+ run_cmd "ip netns exec me ping -c1 -w1 2001:db8:101::1"
+ log_test $? 0 "Ping - multipath"
+
+ #
+ # IPv6 with blackhole nexthops
+ #
+ run_cmd "$IP -6 nexthop add id 83 blackhole"
+ run_cmd "$IP ro replace 2001:db8:101::1/128 nhid 83"
+ run_cmd "ip netns exec me ping -c1 -w1 2001:db8:101::1"
+ log_test $? 2 "Ping - blackhole"
+
+ run_cmd "$IP nexthop replace id 83 via 2001:db8:91::2 dev veth1"
+ run_cmd "ip netns exec me ping -c1 -w1 2001:db8:101::1"
+ log_test $? 0 "Ping - blackhole replaced with gateway"
+
+ run_cmd "$IP -6 nexthop replace id 83 blackhole"
+ run_cmd "ip netns exec me ping -c1 -w1 2001:db8:101::1"
+ log_test $? 2 "Ping - gateway replaced by blackhole"
+
+ run_cmd "$IP ro replace 2001:db8:101::1/128 nhid 122"
+ run_cmd "ip netns exec me ping -c1 -w1 2001:db8:101::1"
+ if [ $? -eq 0 ]; then
+ run_cmd "$IP nexthop replace id 122 group 83"
+ run_cmd "ip netns exec me ping -c1 -w1 2001:db8:101::1"
+ log_test $? 2 "Ping - group with blackhole"
+
+ run_cmd "$IP nexthop replace id 122 group 81/82"
+ run_cmd "ip netns exec me ping -c1 -w1 2001:db8:101::1"
+ log_test $? 0 "Ping - group blackhole replaced with gateways"
+ else
+ log_test 2 0 "Ping - multipath failed"
+ fi
+
+ #
+ # device only and gw + dev only mix
+ #
+ 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"
+
+ 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"
+
+ #
+ # IPv6 route with v4 nexthop - not allowed
+ #
+ run_cmd "$IP ro delete 2001:db8:101::1/128"
+ run_cmd "$IP nexthop add id 84 via 172.16.1.1 dev veth1"
+ run_cmd "$IP ro add 2001:db8:101::1/128 nhid 84"
+ log_test $? 2 "IPv6 route can not have a v4 gateway"
+
+ run_cmd "$IP ro replace 2001:db8:101::1/128 nhid 81"
+ run_cmd "$IP nexthop replace id 81 via 172.16.1.1 dev veth1"
+ log_test $? 2 "Nexthop replace - v6 route, v4 nexthop"
+
+ run_cmd "$IP ro replace 2001:db8:101::1/128 nhid 122"
+ run_cmd "$IP nexthop replace id 81 via 172.16.1.1 dev veth1"
+ log_test $? 2 "Nexthop replace of group entry - v6 route, v4 nexthop"
+
+ $IP nexthop flush >/dev/null 2>&1
+
+ #
+ # weird IPv6 cases
+ #
+ run_cmd "$IP nexthop add id 86 via 2001:db8:91::2 dev veth1"
+ run_cmd "$IP ro add 2001:db8:101::1/128 nhid 81"
+
+ # TO-DO:
+ # existing route with old nexthop; append route with new nexthop
+ # existing route with old nexthop; replace route with new
+ # existing route with new nexthop; replace route with old
+ # route with src address and using nexthop - not allowed
+}
+
+ipv4_fcnal()
+{
+ local rc
+
+ echo
+ echo "IPv4 functional"
+ echo "----------------------"
+
+ #
+ # basic IPv4 ops - add, get, delete
+ #
+ run_cmd "$IP nexthop add id 12 via 172.16.1.2 dev veth1"
+ rc=$?
+ log_test $rc 0 "Create nexthop with id, gw, dev"
+ if [ $rc -ne 0 ]; then
+ echo "Basic IPv4 create fails; can not continue"
+ return 1
+ fi
+
+ 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"
+
+ run_cmd "$IP nexthop del id 12"
+ log_test $? 0 "Delete nexthop by id"
+ check_nexthop "id 52" ""
+
+ #
+ # gw, device spec
+ #
+ # gw validation, no device - fails since dev is required
+ run_cmd "$IP nexthop add id 12 via 172.16.2.3"
+ log_test $? 2 "Create nexthop - gw only"
+
+ # gw not reachable through given dev
+ run_cmd "$IP nexthop add id 13 via 172.16.3.2 dev veth1"
+ log_test $? 2 "Create nexthop - invalid gw+dev combination"
+
+ # onlink flag overrides gw+dev lookup
+ run_cmd "$IP nexthop add id 13 via 172.16.3.2 dev veth1 onlink"
+ log_test $? 0 "Create nexthop - gw+dev and onlink"
+
+ # admin down should delete nexthops
+ set -e
+ run_cmd "$IP nexthop add id 15 via 172.16.1.3 dev veth1"
+ run_cmd "$IP nexthop add id 16 via 172.16.1.4 dev veth1"
+ run_cmd "$IP nexthop add id 17 via 172.16.1.5 dev veth1"
+ run_cmd "$IP li set dev veth1 down"
+ set +e
+ check_nexthop "dev veth1" ""
+ log_test $? 0 "Nexthops removed on admin down"
+}
+
+ipv4_grp_fcnal()
+{
+ local rc
+
+ echo
+ echo "IPv4 groups functional"
+ echo "----------------------"
+
+ # basic functionality: create a nexthop group, default weight
+ run_cmd "$IP nexthop add id 11 via 172.16.1.2 dev veth1"
+ run_cmd "$IP nexthop add id 101 group 11"
+ log_test $? 0 "Create nexthop group with single nexthop"
+
+ # get nexthop group
+ run_cmd "$IP nexthop get id 101"
+ log_test $? 0 "Get nexthop group by id"
+ check_nexthop "id 101" "id 101 group 11"
+
+ # delete nexthop group
+ run_cmd "$IP nexthop del id 101"
+ log_test $? 0 "Delete nexthop group by id"
+ check_nexthop "id 101" ""
+
+ $IP nexthop flush >/dev/null 2>&1
+
+ #
+ # create group with multiple nexthops
+ run_cmd "$IP nexthop add id 12 via 172.16.1.2 dev veth1"
+ run_cmd "$IP nexthop add id 13 via 172.16.1.3 dev veth1"
+ run_cmd "$IP nexthop add id 14 via 172.16.1.4 dev veth1"
+ run_cmd "$IP nexthop add id 15 via 172.16.1.5 dev veth1"
+ run_cmd "$IP nexthop add id 102 group 12/13/14/15"
+ log_test $? 0 "Nexthop group with multiple nexthops"
+ check_nexthop "id 102" "id 102 group 12/13/14/15"
+
+ # Delete nexthop in a group and group is updated
+ run_cmd "$IP nexthop del id 13"
+ check_nexthop "id 102" "id 102 group 12/14/15"
+ log_test $? 0 "Nexthop group updated when entry is deleted"
+
+ # create group with multiple weighted nexthops
+ run_cmd "$IP nexthop add id 13 via 172.16.1.3 dev veth1"
+ run_cmd "$IP nexthop add id 103 group 12/13,2/14,3/15,4"
+ log_test $? 0 "Nexthop group with weighted nexthops"
+ check_nexthop "id 103" "id 103 group 12/13,2/14,3/15,4"
+
+ # Delete nexthop in a weighted group and group is updated
+ run_cmd "$IP nexthop del id 13"
+ check_nexthop "id 103" "id 103 group 12/14,3/15,4"
+ log_test $? 0 "Weighted nexthop group updated when entry is deleted"
+
+ # admin down - nexthop is removed from group
+ run_cmd "$IP li set dev veth1 down"
+ check_nexthop "dev veth1" ""
+ log_test $? 0 "Nexthops in groups removed on admin down"
+
+ # expect groups to have been deleted as well
+ check_nexthop "" ""
+
+ run_cmd "$IP li set dev veth1 up"
+
+ $IP nexthop flush >/dev/null 2>&1
+
+ # group with nexthops using different devices
+ set -e
+ run_cmd "$IP nexthop add id 12 via 172.16.1.2 dev veth1"
+ run_cmd "$IP nexthop add id 13 via 172.16.1.3 dev veth1"
+ run_cmd "$IP nexthop add id 14 via 172.16.1.4 dev veth1"
+ run_cmd "$IP nexthop add id 15 via 172.16.1.5 dev veth1"
+
+ run_cmd "$IP nexthop add id 22 via 172.16.2.2 dev veth3"
+ run_cmd "$IP nexthop add id 23 via 172.16.2.3 dev veth3"
+ run_cmd "$IP nexthop add id 24 via 172.16.2.4 dev veth3"
+ run_cmd "$IP nexthop add id 25 via 172.16.2.5 dev veth3"
+ set +e
+
+ # multiple groups with same nexthop
+ run_cmd "$IP nexthop add id 104 group 12"
+ run_cmd "$IP nexthop add id 105 group 12"
+ check_nexthop "group" "id 104 group 12 id 105 group 12"
+ log_test $? 0 "Multiple groups with same nexthop"
+
+ run_cmd "$IP nexthop flush groups"
+ [ $? -ne 0 ] && return 1
+
+ # on admin down of veth1, it should be removed from the group
+ run_cmd "$IP nexthop add id 105 group 12/13/22/23/14"
+ run_cmd "$IP li set veth1 down"
+ check_nexthop "id 105" "id 105 group 22/23"
+ log_test $? 0 "Nexthops in group removed on admin down - mixed group"
+
+ run_cmd "$IP nexthop add id 106 group 105/24"
+ log_test $? 2 "Nexthop group can not have a group as an entry"
+
+ # a group can have a blackhole entry only if it is the only
+ # nexthop in the group. Needed for atomic replace with an
+ # actual nexthop group
+ run_cmd "$IP nexthop add id 31 blackhole"
+ run_cmd "$IP nexthop add id 107 group 31"
+ log_test $? 0 "Nexthop group with a blackhole entry"
+
+ run_cmd "$IP nexthop add id 108 group 31/24"
+ log_test $? 2 "Nexthop group can not have a blackhole and another nexthop"
+}
+
+ipv4_withv6_fcnal()
+{
+ local lladdr
+
+ set -e
+ lladdr=$(get_linklocal veth2 peer)
+ run_cmd "$IP nexthop add id 11 via ${lladdr} dev veth1"
+ 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"
+
+ set -e
+ run_cmd "$IP nexthop add id 12 via 172.16.1.2 dev veth1"
+ run_cmd "$IP nexthop add id 101 group 11/12"
+ set +e
+ 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"
+
+ 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"
+
+ 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"
+}
+
+ipv4_fcnal_runtime()
+{
+ local lladdr
+ local rc
+
+ echo
+ echo "IPv4 functional runtime"
+ echo "-----------------------"
+
+ run_cmd "$IP nexthop add id 21 via 172.16.1.2 dev veth1"
+ run_cmd "$IP ro add 172.16.101.1/32 nhid 21"
+ log_test $? 0 "Route add"
+ check_route "172.16.101.1" "172.16.101.1 nhid 21 via 172.16.1.2 dev veth1"
+
+ run_cmd "$IP ro delete 172.16.101.1/32 nhid 21"
+ log_test $? 0 "Route delete"
+
+ #
+ # scope mismatch
+ #
+ run_cmd "$IP nexthop add id 22 via 172.16.1.2 dev veth1"
+ run_cmd "$IP ro add 172.16.101.1/32 nhid 22 scope host"
+ log_test $? 2 "Route add - scope conflict with nexthop"
+
+ run_cmd "$IP nexthop replace id 22 dev veth3"
+ run_cmd "$IP ro add 172.16.101.1/32 nhid 22 scope host"
+ run_cmd "$IP nexthop replace id 22 via 172.16.2.2 dev veth3"
+ log_test $? 2 "Nexthop replace with invalid scope for existing route"
+
+ #
+ # add route with nexthop and check traffic
+ #
+ run_cmd "$IP nexthop replace id 21 via 172.16.1.2 dev veth1"
+ run_cmd "$IP ro replace 172.16.101.1/32 nhid 21"
+ run_cmd "ip netns exec me ping -c1 -w1 172.16.101.1"
+ log_test $? 0 "Basic ping"
+
+ run_cmd "$IP nexthop replace id 22 via 172.16.2.2 dev veth3"
+ run_cmd "$IP nexthop add id 122 group 21/22"
+ run_cmd "$IP ro replace 172.16.101.1/32 nhid 122"
+ run_cmd "ip netns exec me ping -c1 -w1 172.16.101.1"
+ log_test $? 0 "Ping - multipath"
+
+ #
+ # IPv4 with blackhole nexthops
+ #
+ run_cmd "$IP nexthop add id 23 blackhole"
+ run_cmd "$IP ro replace 172.16.101.1/32 nhid 23"
+ run_cmd "ip netns exec me ping -c1 -w1 172.16.101.1"
+ log_test $? 2 "Ping - blackhole"
+
+ run_cmd "$IP nexthop replace id 23 via 172.16.1.2 dev veth1"
+ run_cmd "ip netns exec me ping -c1 -w1 172.16.101.1"
+ log_test $? 0 "Ping - blackhole replaced with gateway"
+
+ run_cmd "$IP nexthop replace id 23 blackhole"
+ run_cmd "ip netns exec me ping -c1 -w1 172.16.101.1"
+ log_test $? 2 "Ping - gateway replaced by blackhole"
+
+ run_cmd "$IP ro replace 172.16.101.1/32 nhid 122"
+ run_cmd "ip netns exec me ping -c1 -w1 172.16.101.1"
+ if [ $? -eq 0 ]; then
+ run_cmd "$IP nexthop replace id 122 group 23"
+ run_cmd "ip netns exec me ping -c1 -w1 172.16.101.1"
+ log_test $? 2 "Ping - group with blackhole"
+
+ run_cmd "$IP nexthop replace id 122 group 21/22"
+ run_cmd "ip netns exec me ping -c1 -w1 172.16.101.1"
+ log_test $? 0 "Ping - group blackhole replaced with gateways"
+ else
+ log_test 2 0 "Ping - multipath failed"
+ fi
+
+ #
+ # device only and gw + dev only mix
+ #
+ run_cmd "$IP nexthop add id 85 dev veth1"
+ run_cmd "$IP ro replace 172.16.101.1/32 nhid 85"
+ 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"
+ 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"
+
+ #
+ # IPv4 with IPv6
+ #
+ set -e
+ lladdr=$(get_linklocal veth2 peer)
+ run_cmd "$IP nexthop add id 24 via ${lladdr} dev veth1"
+ set +e
+ run_cmd "$IP ro replace 172.16.101.1/32 nhid 24"
+ run_cmd "ip netns exec me ping -c1 -w1 172.16.101.1"
+ log_test $? 0 "IPv6 nexthop with IPv4 route"
+
+ $IP neigh sh | grep -q "${lladdr} dev veth1"
+ if [ $? -eq 1 ]; then
+ echo " WARNING: Neigh entry missing for ${lladdr}"
+ $IP neigh sh | grep 'dev veth1'
+ fi
+
+ $IP neigh sh | grep -q "172.16.101.1 dev eth1"
+ if [ $? -eq 0 ]; then
+ echo " WARNING: Neigh entry exists for 172.16.101.1"
+ $IP neigh sh | grep 'dev veth1'
+ fi
+
+ set -e
+ run_cmd "$IP nexthop add id 25 via 172.16.1.2 dev veth1"
+ run_cmd "$IP nexthop add id 101 group 24/25"
+ set +e
+ 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"
+
+ run_cmd "ip netns exec me ping -c1 -w1 172.16.101.1"
+ log_test $? 0 "IPv6 nexthop with IPv4 route"
+
+ run_cmd "$IP ro replace 172.16.101.1/32 via inet6 ${lladdr} dev veth1"
+ run_cmd "ip netns exec me ping -c1 -w1 172.16.101.1"
+ log_test $? 0 "IPv4 route with IPv6 gateway"
+
+ $IP neigh sh | grep -q "${lladdr} dev veth1"
+ if [ $? -eq 1 ]; then
+ echo " WARNING: Neigh entry missing for ${lladdr}"
+ $IP neigh sh | grep 'dev veth1'
+ fi
+
+ $IP neigh sh | grep -q "172.16.101.1 dev eth1"
+ if [ $? -eq 0 ]; then
+ echo " WARNING: Neigh entry exists for 172.16.101.1"
+ $IP neigh sh | grep 'dev veth1'
+ fi
+
+ #
+ # MPLS as an example of LWT encap
+ #
+ run_cmd "$IP nexthop add id 51 encap mpls 101 via 172.16.1.2 dev veth1"
+ log_test $? 0 "IPv4 route with MPLS encap"
+ check_nexthop "id 51" "id 51 encap mpls 101 via 172.16.1.2 dev veth1 scope link"
+ log_test $? 0 "IPv4 route with MPLS encap - check"
+
+ run_cmd "$IP nexthop add id 52 encap mpls 102 via inet6 2001:db8:91::2 dev veth1"
+ log_test $? 0 "IPv4 route with MPLS encap and v6 gateway"
+ check_nexthop "id 52" "id 52 encap mpls 102 via 2001:db8:91::2 dev veth1 scope link"
+ log_test $? 0 "IPv4 route with MPLS encap, v6 gw - check"
+}
+
+basic()
+{
+ echo
+ echo "Basic functional tests"
+ echo "----------------------"
+ run_cmd "$IP nexthop ls"
+ log_test $? 0 "List with nothing defined"
+
+ run_cmd "$IP nexthop get id 1"
+ log_test $? 2 "Nexthop get on non-existent id"
+
+ # attempt to create nh without a device or gw - fails
+ run_cmd "$IP nexthop add id 1"
+ log_test $? 2 "Nexthop with no device or gateway"
+
+ # attempt to create nh with down device - fails
+ $IP li set veth1 down
+ run_cmd "$IP nexthop add id 1 dev veth1"
+ log_test $? 2 "Nexthop with down device"
+
+ # create nh with linkdown device - fails
+ $IP li set veth1 up
+ ip -netns peer li set veth2 down
+ run_cmd "$IP nexthop add id 1 dev veth1"
+ log_test $? 2 "Nexthop with device that is linkdown"
+ ip -netns peer li set veth2 up
+
+ # device only
+ run_cmd "$IP nexthop add id 1 dev veth1"
+ log_test $? 0 "Nexthop with device only"
+
+ # create nh with duplicate id
+ run_cmd "$IP nexthop add id 1 dev veth3"
+ log_test $? 2 "Nexthop with duplicate id"
+
+ # blackhole nexthop
+ run_cmd "$IP nexthop add id 2 blackhole"
+ log_test $? 0 "Blackhole nexthop"
+
+ # blackhole nexthop can not have other specs
+ run_cmd "$IP nexthop replace id 2 blackhole dev veth1"
+ log_test $? 2 "Blackhole nexthop with other attributes"
+
+ #
+ # groups
+ #
+
+ run_cmd "$IP nexthop add id 101 group 1"
+ log_test $? 0 "Create group"
+
+ run_cmd "$IP nexthop add id 102 group 2"
+ log_test $? 0 "Create group with blackhole nexthop"
+
+ # multipath group can not have a blackhole as 1 path
+ run_cmd "$IP nexthop add id 103 group 1/2"
+ log_test $? 2 "Create multipath group where 1 path is a blackhole"
+
+ # multipath group can not have a member replaced by a blackhole
+ run_cmd "$IP nexthop replace id 2 dev veth3"
+ run_cmd "$IP nexthop replace id 102 group 1/2"
+ run_cmd "$IP nexthop replace id 2 blackhole"
+ log_test $? 2 "Multipath group can not have a member replaced by blackhole"
+
+ # attempt to create group with non-existent nexthop
+ run_cmd "$IP nexthop add id 103 group 12"
+ log_test $? 2 "Create group with non-existent nexthop"
+
+ # attempt to create group with same nexthop
+ run_cmd "$IP nexthop add id 103 group 1/1"
+ log_test $? 2 "Create group with same nexthop multiple times"
+
+ # replace nexthop with a group - fails
+ run_cmd "$IP nexthop replace id 2 group 1"
+ log_test $? 2 "Replace nexthop with nexthop group"
+
+ # replace nexthop group with a nexthop - fails
+ run_cmd "$IP nexthop replace id 101 dev veth1"
+ log_test $? 2 "Replace nexthop group with nexthop"
+
+ # nexthop group with other attributes fail
+ run_cmd "$IP nexthop add id 104 group 1 dev veth1"
+ log_test $? 2 "Nexthop group and device"
+
+ run_cmd "$IP nexthop add id 104 group 1 blackhole"
+ log_test $? 2 "Nexthop group and blackhole"
+
+ $IP nexthop flush >/dev/null 2>&1
+}
+
+################################################################################
+# usage
+
+usage()
+{
+ cat <<EOF
+usage: ${0##*/} OPTS
+
+ -t <test> Test(s) to run (default: all)
+ (options: $ALL_TESTS)
+ -4 IPv4 tests only
+ -6 IPv6 tests only
+ -p Pause on fail
+ -P Pause after each test before cleanup
+ -v verbose mode (show commands and output)
+
+ Runtime test
+ -n num Number of nexthops to target
+ -N Use new style to install routes in DUT
+
+done
+EOF
+}
+
+################################################################################
+# main
+
+while getopts :t:pP46hv o
+do
+ case $o in
+ t) TESTS=$OPTARG;;
+ 4) TESTS=${IPV4_TESTS};;
+ 6) TESTS=${IPV6_TESTS};;
+ p) PAUSE_ON_FAIL=yes;;
+ P) PAUSE=yes;;
+ v) VERBOSE=$(($VERBOSE + 1));;
+ h) usage; exit 0;;
+ *) usage; exit 1;;
+ esac
+done
+
+# make sure we don't pause twice
+[ "${PAUSE}" = "yes" ] && PAUSE_ON_FAIL=no
+
+if [ "$(id -u)" -ne 0 ];then
+ echo "SKIP: Need root privileges"
+ exit $ksft_skip;
+fi
+
+if [ ! -x "$(command -v ip)" ]; then
+ echo "SKIP: Could not run test without ip tool"
+ exit $ksft_skip
+fi
+
+ip help 2>&1 | grep -q nexthop
+if [ $? -ne 0 ]; then
+ echo "SKIP: iproute2 too old, missing nexthop command"
+ exit $ksft_skip
+fi
+
+out=$(ip nexthop ls 2>&1 | grep -q "Operation not supported")
+if [ $? -eq 0 ]; then
+ echo "SKIP: kernel lacks nexthop support"
+ exit $ksft_skip
+fi
+
+for t in $TESTS
+do
+ case $t in
+ none) IP="ip -netns peer"; setup; exit 0;;
+ *) setup; $t; cleanup;;
+ esac
+done
+
+if [ "$TESTS" != "none" ]; then
+ printf "\nTests passed: %3d\n" ${nsuccess}
+ printf "Tests failed: %3d\n" ${nfail}
+fi
+
+exit $ret
diff --git a/tools/testing/selftests/net/fib_rule_tests.sh b/tools/testing/selftests/net/fib_rule_tests.sh
index 4b7e107865bf..a93e6b690e06 100755
--- a/tools/testing/selftests/net/fib_rule_tests.sh
+++ b/tools/testing/selftests/net/fib_rule_tests.sh
@@ -15,6 +15,7 @@ GW_IP6=2001:db8:1::2
SRC_IP6=2001:db8:1::3
DEV_ADDR=192.51.100.1
+DEV_ADDR6=2001:db8:1::1
DEV=dummy0
log_test()
@@ -55,8 +56,8 @@ setup()
$IP link add dummy0 type dummy
$IP link set dev dummy0 up
- $IP address add 198.51.100.1/24 dev dummy0
- $IP -6 address add 2001:db8:1::1/64 dev dummy0
+ $IP address add $DEV_ADDR/24 dev dummy0
+ $IP -6 address add $DEV_ADDR6/64 dev dummy0
set +e
}
@@ -186,8 +187,13 @@ fib_rule4_test()
match="oif $DEV"
fib_rule4_test_match_n_redirect "$match" "$match" "oif redirect to table"
+ # need enable forwarding and disable rp_filter temporarily as all the
+ # addresses are in the same subnet and egress device == ingress device.
+ ip netns exec testns sysctl -w net.ipv4.ip_forward=1
+ ip netns exec testns sysctl -w net.ipv4.conf.$DEV.rp_filter=0
match="from $SRC_IP iif $DEV"
fib_rule4_test_match_n_redirect "$match" "$match" "iif redirect to table"
+ ip netns exec testns sysctl -w net.ipv4.ip_forward=0
match="tos 0x10"
fib_rule4_test_match_n_redirect "$match" "$match" "tos redirect to table"
diff --git a/tools/testing/selftests/net/fib_tests.sh b/tools/testing/selftests/net/fib_tests.sh
index 9457aaeae092..4465fc2dae14 100755
--- a/tools/testing/selftests/net/fib_tests.sh
+++ b/tools/testing/selftests/net/fib_tests.sh
@@ -9,12 +9,13 @@ 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"
+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"
VERBOSE=0
PAUSE_ON_FAIL=no
PAUSE=no
IP="ip -netns ns1"
+NS_EXEC="ip netns exec ns1"
log_test()
{
@@ -433,6 +434,37 @@ fib_carrier_test()
fib_carrier_unicast_test
}
+fib_rp_filter_test()
+{
+ echo
+ echo "IPv4 rp_filter tests"
+
+ setup
+
+ set -e
+ $IP link set dev lo address 52:54:00:6a:c7:5e
+ $IP link set dummy0 address 52:54:00:6a:c7:5e
+ $IP link add dummy1 type dummy
+ $IP link set dummy1 address 52:54:00:6a:c7:5e
+ $IP link set dev dummy1 up
+ $NS_EXEC sysctl -qw net.ipv4.conf.all.rp_filter=1
+ $NS_EXEC sysctl -qw net.ipv4.conf.all.accept_local=1
+ $NS_EXEC sysctl -qw net.ipv4.conf.all.route_localnet=1
+
+ $NS_EXEC tc qd add dev dummy1 parent root handle 1: fq_codel
+ $NS_EXEC tc filter add dev dummy1 parent 1: protocol arp basic action mirred egress redirect dev lo
+ $NS_EXEC tc filter add dev dummy1 parent 1: protocol ip basic action mirred egress redirect dev lo
+ set +e
+
+ run_cmd "ip netns exec ns1 ping -I dummy1 -w1 -c1 198.51.100.1"
+ log_test $? 0 "rp_filter passes local packets"
+
+ run_cmd "ip netns exec ns1 ping -I dummy1 -w1 -c1 127.0.0.1"
+ log_test $? 0 "rp_filter passes loopback packets"
+
+ cleanup
+}
+
################################################################################
# Tests on nexthop spec
@@ -1557,6 +1589,7 @@ do
fib_unreg_test|unregister) fib_unreg_test;;
fib_down_test|down) fib_down_test;;
fib_carrier_test|carrier) fib_carrier_test;;
+ fib_rp_filter_test|rp_filter) fib_rp_filter_test;;
fib_nexthop_test|nexthop) fib_nexthop_test;;
ipv6_route_test|ipv6_rt) ipv6_route_test;;
ipv4_route_test|ipv4_rt) ipv4_route_test;;
diff --git a/tools/testing/selftests/net/forwarding/gre_inner_v4_multipath.sh b/tools/testing/selftests/net/forwarding/gre_inner_v4_multipath.sh
new file mode 100755
index 000000000000..e4009f658003
--- /dev/null
+++ b/tools/testing/selftests/net/forwarding/gre_inner_v4_multipath.sh
@@ -0,0 +1,305 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+# Test traffic distribution when there are multiple routes between an IPv4
+# GRE tunnel. The tunnel carries IPv4 traffic between multiple hosts.
+# Multiple routes are in the underlay network. With the default multipath
+# policy, SW2 will only look at the outer IP addresses, hence only a single
+# route would be used.
+#
+# +-------------------------+
+# | H1 |
+# | $h1 + |
+# | 192.0.3.{2-62}/24 | |
+# +-------------------|-----+
+# |
+# +-------------------|------------------------+
+# | SW1 | |
+# | $ol1 + |
+# | 192.0.3.1/24 |
+# | |
+# | + g1 (gre) |
+# | loc=192.0.2.65 |
+# | rem=192.0.2.66 --. |
+# | tos=inherit | |
+# | v |
+# | + $ul1 |
+# | | 192.0.2.129/28 |
+# +---------------------|----------------------+
+# |
+# +---------------------|----------------------+
+# | SW2 | |
+# | $ul21 + |
+# | 192.0.2.130/28 |
+# | | |
+# ! ________________|_____ |
+# | / \ |
+# | | | |
+# | + $ul22.111 (vlan) + $ul22.222 (vlan) |
+# | | 192.0.2.145/28 | 192.0.2.161/28 |
+# | | | |
+# +--|----------------------|------------------+
+# | |
+# +--|----------------------|------------------+
+# | | | |
+# | + $ul32.111 (vlan) + $ul32.222 (vlan) |
+# | | 192.0.2.146/28 | 192.0.2.162/28 |
+# | | | |
+# | \______________________/ |
+# | | |
+# | | |
+# | $ul31 + |
+# | 192.0.2.177/28 | SW3 |
+# +---------------------|----------------------+
+# |
+# +---------------------|----------------------+
+# | + $ul4 |
+# | ^ 192.0.2.178/28 |
+# | | |
+# | + g2 (gre) | |
+# | loc=192.0.2.66 | |
+# | rem=192.0.2.65 --' |
+# | tos=inherit |
+# | |
+# | $ol4 + |
+# | 192.0.4.1/24 | SW4 |
+# +--------------------|-----------------------+
+# |
+# +--------------------|---------+
+# | | |
+# | $h2 + |
+# | 192.0.4.{2-62}/24 H2 |
+# +------------------------------+
+
+ALL_TESTS="
+ ping_ipv4
+ multipath_ipv4
+"
+
+NUM_NETIFS=10
+source lib.sh
+
+h1_create()
+{
+ simple_if_init $h1 192.0.3.2/24
+ ip route add vrf v$h1 192.0.4.0/24 via 192.0.3.1
+}
+
+h1_destroy()
+{
+ ip route del vrf v$h1 192.0.4.0/24 via 192.0.3.1
+ simple_if_fini $h1 192.0.3.2/24
+}
+
+sw1_create()
+{
+ simple_if_init $ol1 192.0.3.1/24
+ __simple_if_init $ul1 v$ol1 192.0.2.129/28
+
+ tunnel_create g1 gre 192.0.2.65 192.0.2.66 tos inherit dev v$ol1
+ __simple_if_init g1 v$ol1 192.0.2.65/32
+ ip route add vrf v$ol1 192.0.2.66/32 via 192.0.2.130
+
+ ip route add vrf v$ol1 192.0.4.0/24 nexthop dev g1
+}
+
+sw1_destroy()
+{
+ ip route del vrf v$ol1 192.0.4.0/24
+
+ ip route del vrf v$ol1 192.0.2.66/32
+ __simple_if_fini g1 192.0.2.65/32
+ tunnel_destroy g1
+
+ __simple_if_fini $ul1 192.0.2.129/28
+ simple_if_fini $ol1 192.0.3.1/24
+}
+
+sw2_create()
+{
+ simple_if_init $ul21 192.0.2.130/28
+ __simple_if_init $ul22 v$ul21
+ vlan_create $ul22 111 v$ul21 192.0.2.145/28
+ vlan_create $ul22 222 v$ul21 192.0.2.161/28
+
+ ip route add vrf v$ul21 192.0.2.65/32 via 192.0.2.129
+ ip route add vrf v$ul21 192.0.2.66/32 \
+ nexthop via 192.0.2.146 \
+ nexthop via 192.0.2.162
+}
+
+sw2_destroy()
+{
+ ip route del vrf v$ul21 192.0.2.66/32
+ ip route del vrf v$ul21 192.0.2.65/32
+
+ vlan_destroy $ul22 222
+ vlan_destroy $ul22 111
+ __simple_if_fini $ul22
+ simple_if_fini $ul21 192.0.2.130/28
+}
+
+sw3_create()
+{
+ simple_if_init $ul31 192.0.2.177/28
+ __simple_if_init $ul32 v$ul31
+ vlan_create $ul32 111 v$ul31 192.0.2.146/28
+ vlan_create $ul32 222 v$ul31 192.0.2.162/28
+
+ ip route add vrf v$ul31 192.0.2.66/32 via 192.0.2.178
+ ip route add vrf v$ul31 192.0.2.65/32 \
+ nexthop via 192.0.2.145 \
+ nexthop via 192.0.2.161
+
+ tc qdisc add dev $ul32 clsact
+ tc filter add dev $ul32 ingress pref 111 prot 802.1Q \
+ flower vlan_id 111 action pass
+ tc filter add dev $ul32 ingress pref 222 prot 802.1Q \
+ flower vlan_id 222 action pass
+}
+
+sw3_destroy()
+{
+ tc qdisc del dev $ul32 clsact
+
+ ip route del vrf v$ul31 192.0.2.65/32
+ ip route del vrf v$ul31 192.0.2.66/32
+
+ vlan_destroy $ul32 222
+ vlan_destroy $ul32 111
+ __simple_if_fini $ul32
+ simple_if_fini $ul31 192.0.2.177/28
+}
+
+sw4_create()
+{
+ simple_if_init $ol4 192.0.4.1/24
+ __simple_if_init $ul4 v$ol4 192.0.2.178/28
+
+ tunnel_create g2 gre 192.0.2.66 192.0.2.65 tos inherit dev v$ol4
+ __simple_if_init g2 v$ol4 192.0.2.66/32
+ ip route add vrf v$ol4 192.0.2.65/32 via 192.0.2.177
+
+ ip route add vrf v$ol4 192.0.3.0/24 nexthop dev g2
+}
+
+sw4_destroy()
+{
+ ip route del vrf v$ol4 192.0.3.0/24
+
+ ip route del vrf v$ol4 192.0.2.65/32
+ __simple_if_fini g2 192.0.2.66/32
+ tunnel_destroy g2
+
+ __simple_if_fini $ul4 192.0.2.178/28
+ simple_if_fini $ol4 192.0.4.1/24
+}
+
+h2_create()
+{
+ simple_if_init $h2 192.0.4.2/24
+ ip route add vrf v$h2 192.0.3.0/24 via 192.0.4.1
+}
+
+h2_destroy()
+{
+ ip route del vrf v$h2 192.0.3.0/24 via 192.0.4.1
+ simple_if_fini $h2 192.0.4.2/24
+}
+
+setup_prepare()
+{
+ h1=${NETIFS[p1]}
+
+ ol1=${NETIFS[p2]}
+ ul1=${NETIFS[p3]}
+
+ ul21=${NETIFS[p4]}
+ ul22=${NETIFS[p5]}
+
+ ul32=${NETIFS[p6]}
+ ul31=${NETIFS[p7]}
+
+ ul4=${NETIFS[p8]}
+ ol4=${NETIFS[p9]}
+
+ h2=${NETIFS[p10]}
+
+ vrf_prepare
+ h1_create
+ sw1_create
+ sw2_create
+ sw3_create
+ sw4_create
+ h2_create
+
+ forwarding_enable
+}
+
+cleanup()
+{
+ pre_cleanup
+
+ forwarding_restore
+
+ h2_destroy
+ sw4_destroy
+ sw3_destroy
+ sw2_destroy
+ sw1_destroy
+ h1_destroy
+ vrf_cleanup
+}
+
+multipath4_test()
+{
+ local what=$1; shift
+ local weight1=$1; shift
+ local weight2=$1; shift
+
+ sysctl_set net.ipv4.fib_multipath_hash_policy 2
+ ip route replace vrf v$ul21 192.0.2.66/32 \
+ nexthop via 192.0.2.146 weight $weight1 \
+ nexthop via 192.0.2.162 weight $weight2
+
+ local t0_111=$(tc_rule_stats_get $ul32 111 ingress)
+ local t0_222=$(tc_rule_stats_get $ul32 222 ingress)
+
+ ip vrf exec v$h1 \
+ $MZ $h1 -q -p 64 -A "192.0.3.2-192.0.3.62" -B "192.0.4.2-192.0.4.62" \
+ -d 1msec -c 50 -t udp "sp=1024,dp=1024"
+ sleep 1
+
+ local t1_111=$(tc_rule_stats_get $ul32 111 ingress)
+ local t1_222=$(tc_rule_stats_get $ul32 222 ingress)
+
+ local d111=$((t1_111 - t0_111))
+ local d222=$((t1_222 - t0_222))
+ multipath_eval "$what" $weight1 $weight2 $d111 $d222
+
+ ip route replace vrf v$ul21 192.0.2.66/32 \
+ nexthop via 192.0.2.146 \
+ nexthop via 192.0.2.162
+ sysctl_restore net.ipv4.fib_multipath_hash_policy
+}
+
+ping_ipv4()
+{
+ ping_test $h1 192.0.4.2
+}
+
+multipath_ipv4()
+{
+ log_info "Running IPv4 over GRE over IPv4 multipath tests"
+ multipath4_test "ECMP" 1 1
+ multipath4_test "Weighted MP 2:1" 2 1
+ multipath4_test "Weighted MP 11:45" 11 45
+}
+
+trap cleanup EXIT
+
+setup_prepare
+setup_wait
+tests_run
+
+exit $EXIT_STATUS
diff --git a/tools/testing/selftests/net/forwarding/gre_inner_v6_multipath.sh b/tools/testing/selftests/net/forwarding/gre_inner_v6_multipath.sh
new file mode 100755
index 000000000000..e449475c4d3e
--- /dev/null
+++ b/tools/testing/selftests/net/forwarding/gre_inner_v6_multipath.sh
@@ -0,0 +1,306 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+# Test traffic distribution when there are multiple routes between an IPv4
+# GRE tunnel. The tunnel carries IPv6 traffic between multiple hosts.
+# Multiple routes are in the underlay network. With the default multipath
+# policy, SW2 will only look at the outer IP addresses, hence only a single
+# route would be used.
+#
+# +-------------------------+
+# | H1 |
+# | $h1 + |
+# | 2001:db8:1::2/64 | |
+# +-------------------|-----+
+# |
+# +-------------------|------------------------+
+# | SW1 | |
+# | $ol1 + |
+# | 2001:db8:1::1/64 |
+# | |
+# | + g1 (gre) |
+# | loc=192.0.2.65 |
+# | rem=192.0.2.66 --. |
+# | tos=inherit | |
+# | v |
+# | + $ul1 |
+# | | 192.0.2.129/28 |
+# +---------------------|----------------------+
+# |
+# +---------------------|----------------------+
+# | SW2 | |
+# | $ul21 + |
+# | 192.0.2.130/28 |
+# | | |
+# ! ________________|_____ |
+# | / \ |
+# | | | |
+# | + $ul22.111 (vlan) + $ul22.222 (vlan) |
+# | | 192.0.2.145/28 | 192.0.2.161/28 |
+# | | | |
+# +--|----------------------|------------------+
+# | |
+# +--|----------------------|------------------+
+# | | | |
+# | + $ul32.111 (vlan) + $ul32.222 (vlan) |
+# | | 192.0.2.146/28 | 192.0.2.162/28 |
+# | | | |
+# | \______________________/ |
+# | | |
+# | | |
+# | $ul31 + |
+# | 192.0.2.177/28 | SW3 |
+# +---------------------|----------------------+
+# |
+# +---------------------|----------------------+
+# | + $ul4 |
+# | ^ 192.0.2.178/28 |
+# | | |
+# | + g2 (gre) | |
+# | loc=192.0.2.66 | |
+# | rem=192.0.2.65 --' |
+# | tos=inherit |
+# | |
+# | $ol4 + |
+# | 2001:db8:2::1/64 | SW4 |
+# +--------------------|-----------------------+
+# |
+# +--------------------|---------+
+# | | |
+# | $h2 + |
+# | 2001:db8:2::2/64 H2 |
+# +------------------------------+
+
+ALL_TESTS="
+ ping_ipv6
+ multipath_ipv6
+"
+
+NUM_NETIFS=10
+source lib.sh
+
+h1_create()
+{
+ simple_if_init $h1 2001:db8:1::2/64
+ ip -6 route add vrf v$h1 2001:db8:2::/64 via 2001:db8:1::1
+}
+
+h1_destroy()
+{
+ ip -6 route del vrf v$h1 2001:db8:2::/64 via 2001:db8:1::1
+ simple_if_fini $h1 2001:db8:1::2/64
+}
+
+sw1_create()
+{
+ simple_if_init $ol1 2001:db8:1::1/64
+ __simple_if_init $ul1 v$ol1 192.0.2.129/28
+
+ tunnel_create g1 gre 192.0.2.65 192.0.2.66 tos inherit dev v$ol1
+ __simple_if_init g1 v$ol1 192.0.2.65/32
+ ip route add vrf v$ol1 192.0.2.66/32 via 192.0.2.130
+
+ ip -6 route add vrf v$ol1 2001:db8:2::/64 dev g1
+}
+
+sw1_destroy()
+{
+ ip -6 route del vrf v$ol1 2001:db8:2::/64
+
+ ip route del vrf v$ol1 192.0.2.66/32
+ __simple_if_fini g1 192.0.2.65/32
+ tunnel_destroy g1
+
+ __simple_if_fini $ul1 192.0.2.129/28
+ simple_if_fini $ol1 2001:db8:1::1/64
+}
+
+sw2_create()
+{
+ simple_if_init $ul21 192.0.2.130/28
+ __simple_if_init $ul22 v$ul21
+ vlan_create $ul22 111 v$ul21 192.0.2.145/28
+ vlan_create $ul22 222 v$ul21 192.0.2.161/28
+
+ ip route add vrf v$ul21 192.0.2.65/32 via 192.0.2.129
+ ip route add vrf v$ul21 192.0.2.66/32 \
+ nexthop via 192.0.2.146 \
+ nexthop via 192.0.2.162
+}
+
+sw2_destroy()
+{
+ ip route del vrf v$ul21 192.0.2.66/32
+ ip route del vrf v$ul21 192.0.2.65/32
+
+ vlan_destroy $ul22 222
+ vlan_destroy $ul22 111
+ __simple_if_fini $ul22
+ simple_if_fini $ul21 192.0.2.130/28
+}
+
+sw3_create()
+{
+ simple_if_init $ul31 192.0.2.177/28
+ __simple_if_init $ul32 v$ul31
+ vlan_create $ul32 111 v$ul31 192.0.2.146/28
+ vlan_create $ul32 222 v$ul31 192.0.2.162/28
+
+ ip route add vrf v$ul31 192.0.2.66/32 via 192.0.2.178
+ ip route add vrf v$ul31 192.0.2.65/32 \
+ nexthop via 192.0.2.145 \
+ nexthop via 192.0.2.161
+
+ tc qdisc add dev $ul32 clsact
+ tc filter add dev $ul32 ingress pref 111 prot 802.1Q \
+ flower vlan_id 111 action pass
+ tc filter add dev $ul32 ingress pref 222 prot 802.1Q \
+ flower vlan_id 222 action pass
+}
+
+sw3_destroy()
+{
+ tc qdisc del dev $ul32 clsact
+
+ ip route del vrf v$ul31 192.0.2.65/32
+ ip route del vrf v$ul31 192.0.2.66/32
+
+ vlan_destroy $ul32 222
+ vlan_destroy $ul32 111
+ __simple_if_fini $ul32
+ simple_if_fini $ul31 192.0.2.177/28
+}
+
+sw4_create()
+{
+ simple_if_init $ol4 2001:db8:2::1/64
+ __simple_if_init $ul4 v$ol4 192.0.2.178/28
+
+ tunnel_create g2 gre 192.0.2.66 192.0.2.65 tos inherit dev v$ol4
+ __simple_if_init g2 v$ol4 192.0.2.66/32
+ ip route add vrf v$ol4 192.0.2.65/32 via 192.0.2.177
+
+ ip -6 route add vrf v$ol4 2001:db8:1::/64 dev g2
+}
+
+sw4_destroy()
+{
+ ip -6 route del vrf v$ol4 2001:db8:1::/64
+
+ ip route del vrf v$ol4 192.0.2.65/32
+ __simple_if_fini g2 192.0.2.66/32
+ tunnel_destroy g2
+
+ __simple_if_fini $ul4 192.0.2.178/28
+ simple_if_fini $ol4 2001:db8:2::1/64
+}
+
+h2_create()
+{
+ simple_if_init $h2 2001:db8:2::2/64
+ ip -6 route add vrf v$h2 2001:db8:1::/64 via 2001:db8:2::1
+}
+
+h2_destroy()
+{
+ ip -6 route del vrf v$h2 2001:db8:1::/64 via 2001:db8:2::1
+ simple_if_fini $h2 2001:db8:2::2/64
+}
+
+setup_prepare()
+{
+ h1=${NETIFS[p1]}
+
+ ol1=${NETIFS[p2]}
+ ul1=${NETIFS[p3]}
+
+ ul21=${NETIFS[p4]}
+ ul22=${NETIFS[p5]}
+
+ ul32=${NETIFS[p6]}
+ ul31=${NETIFS[p7]}
+
+ ul4=${NETIFS[p8]}
+ ol4=${NETIFS[p9]}
+
+ h2=${NETIFS[p10]}
+
+ vrf_prepare
+ h1_create
+ sw1_create
+ sw2_create
+ sw3_create
+ sw4_create
+ h2_create
+
+ forwarding_enable
+}
+
+cleanup()
+{
+ pre_cleanup
+
+ forwarding_restore
+
+ h2_destroy
+ sw4_destroy
+ sw3_destroy
+ sw2_destroy
+ sw1_destroy
+ h1_destroy
+ vrf_cleanup
+}
+
+multipath6_test()
+{
+ local what=$1; shift
+ local weight1=$1; shift
+ local weight2=$1; shift
+
+ sysctl_set net.ipv4.fib_multipath_hash_policy 2
+ ip route replace vrf v$ul21 192.0.2.66/32 \
+ nexthop via 192.0.2.146 weight $weight1 \
+ nexthop via 192.0.2.162 weight $weight2
+
+ local t0_111=$(tc_rule_stats_get $ul32 111 ingress)
+ local t0_222=$(tc_rule_stats_get $ul32 222 ingress)
+
+ ip vrf exec v$h1 \
+ $MZ $h1 -6 -q -p 64 -A "2001:db8:1::2-2001:db8:1::1e" \
+ -B "2001:db8:2::2-2001:db8:2::1e" \
+ -d 1msec -c 50 -t udp "sp=1024,dp=1024"
+ sleep 1
+
+ local t1_111=$(tc_rule_stats_get $ul32 111 ingress)
+ local t1_222=$(tc_rule_stats_get $ul32 222 ingress)
+
+ local d111=$((t1_111 - t0_111))
+ local d222=$((t1_222 - t0_222))
+ multipath_eval "$what" $weight1 $weight2 $d111 $d222
+
+ ip route replace vrf v$ul21 192.0.2.66/32 \
+ nexthop via 192.0.2.146 \
+ nexthop via 192.0.2.162
+ sysctl_restore net.ipv4.fib_multipath_hash_policy
+}
+
+ping_ipv6()
+{
+ ping_test $h1 2001:db8:2::2
+}
+
+multipath_ipv6()
+{
+ log_info "Running IPv6 over GRE over IPv4 multipath tests"
+ multipath6_test "ECMP" 1 1
+ multipath6_test "Weighted MP 2:1" 2 1
+ multipath6_test "Weighted MP 11:45" 11 45
+}
+
+trap cleanup EXIT
+
+setup_prepare
+setup_wait
+tests_run
+
+exit $EXIT_STATUS
diff --git a/tools/testing/selftests/net/forwarding/ip6gre_inner_v4_multipath.sh b/tools/testing/selftests/net/forwarding/ip6gre_inner_v4_multipath.sh
new file mode 100755
index 000000000000..a257979d3fc5
--- /dev/null
+++ b/tools/testing/selftests/net/forwarding/ip6gre_inner_v4_multipath.sh
@@ -0,0 +1,304 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+# Test traffic distribution when there are multiple routes between an IPv6
+# GRE tunnel. The tunnel carries IPv4 traffic between multiple hosts.
+# Multiple routes are in the underlay network. With the default multipath
+# policy, SW2 will only look at the outer IP addresses, hence only a single
+# route would be used.
+#
+# +-------------------------+
+# | H1 |
+# | $h1 + |
+# | 192.0.3.{2-62}/24 | |
+# +-------------------|-----+
+# |
+# +-------------------|-------------------------+
+# | SW1 | |
+# | $ol1 + |
+# | 192.0.3.1/24 |
+# | |
+# | + g1 (gre) |
+# | loc=2001:db8:40::1 |
+# | rem=2001:db8:40::2 --. |
+# | tos=inherit | |
+# | v |
+# | + $ul1 |
+# | | 2001:db8:80::1/64 |
+# +-------------------------|-------------------+
+# |
+# +-------------------------|-------------------+
+# | SW2 | |
+# | $ul21 + |
+# | 2001:db8:80::2/64 |
+# | | |
+# ! ________________|_____ |
+# | / \ |
+# | | | |
+# | + $ul22.111 (vlan) + $ul22.222 (vlan) |
+# | | 2001:db8:81::1/64 | 2001:db8:82::1/64 |
+# | | | |
+# +--|----------------------|-------------------+
+# | |
+# +--|----------------------|-------------------+
+# | | | |
+# | + $ul32.111 (vlan) + $ul32.222 (vlan) |
+# | | 2001:db8:81::2/64 | 2001:db8:82::2/64 |
+# | | | |
+# | \______________________/ |
+# | | |
+# | | |
+# | $ul31 + |
+# | 2001:db8:83::2/64 | SW3 |
+# +-------------------------|-------------------+
+# |
+# +-------------------------|-------------------+
+# | + $ul4 |
+# | ^ 2001:db8:83::1/64 |
+# | + g2 (gre) | |
+# | loc=2001:db8:40::2 | |
+# | rem=2001:db8:40::1 --' |
+# | tos=inherit |
+# | |
+# | $ol4 + |
+# | 192.0.4.1/24 | SW4 |
+# +--------------------|------------------------+
+# |
+# +--------------------|---------+
+# | | |
+# | $h2 + |
+# | 192.0.4.{2-62}/24 H2 |
+# +------------------------------+
+
+ALL_TESTS="
+ ping_ipv4
+ multipath_ipv4
+"
+
+NUM_NETIFS=10
+source lib.sh
+
+h1_create()
+{
+ simple_if_init $h1 192.0.3.2/24
+ ip route add vrf v$h1 192.0.4.0/24 via 192.0.3.1
+}
+
+h1_destroy()
+{
+ ip route del vrf v$h1 192.0.4.0/24 via 192.0.3.1
+ simple_if_fini $h1 192.0.3.2/24
+}
+
+sw1_create()
+{
+ simple_if_init $ol1 192.0.3.1/24
+ __simple_if_init $ul1 v$ol1 2001:db8:80::1/64
+
+ tunnel_create g1 ip6gre 2001:db8:40::1 2001:db8:40::2 tos inherit dev v$ol1
+ __simple_if_init g1 v$ol1 2001:db8:40::1/128
+ ip -6 route add vrf v$ol1 2001:db8:40::2/128 via 2001:db8:80::2
+
+ ip route add vrf v$ol1 192.0.4.0/24 nexthop dev g1
+}
+
+sw1_destroy()
+{
+ ip route del vrf v$ol1 192.0.4.0/24
+
+ ip -6 route del vrf v$ol1 2001:db8:40::2/128
+ __simple_if_fini g1 2001:db8:40::1/128
+ tunnel_destroy g1
+
+ __simple_if_fini $ul1 2001:db8:80::1/64
+ simple_if_fini $ol1 192.0.3.1/24
+}
+
+sw2_create()
+{
+ simple_if_init $ul21 2001:db8:80::2/64
+ __simple_if_init $ul22 v$ul21
+ vlan_create $ul22 111 v$ul21 2001:db8:81::1/64
+ vlan_create $ul22 222 v$ul21 2001:db8:82::1/64
+
+ ip -6 route add vrf v$ul21 2001:db8:40::1/128 via 2001:db8:80::1
+ ip -6 route add vrf v$ul21 2001:db8:40::2/128 \
+ nexthop via 2001:db8:81::2 \
+ nexthop via 2001:db8:82::2
+}
+
+sw2_destroy()
+{
+ ip -6 route del vrf v$ul21 2001:db8:40::2/128
+ ip -6 route del vrf v$ul21 2001:db8:40::1/128
+
+ vlan_destroy $ul22 222
+ vlan_destroy $ul22 111
+ __simple_if_fini $ul22
+ simple_if_fini $ul21 2001:db8:80::2/64
+}
+
+sw3_create()
+{
+ simple_if_init $ul31 2001:db8:83::2/64
+ __simple_if_init $ul32 v$ul31
+ vlan_create $ul32 111 v$ul31 2001:db8:81::2/64
+ vlan_create $ul32 222 v$ul31 2001:db8:82::2/64
+
+ ip -6 route add vrf v$ul31 2001:db8:40::2/128 via 2001:db8:83::1
+ ip -6 route add vrf v$ul31 2001:db8:40::1/128 \
+ nexthop via 2001:db8:81::1 \
+ nexthop via 2001:db8:82::1
+
+ tc qdisc add dev $ul32 clsact
+ tc filter add dev $ul32 ingress pref 111 prot 802.1Q \
+ flower vlan_id 111 action pass
+ tc filter add dev $ul32 ingress pref 222 prot 802.1Q \
+ flower vlan_id 222 action pass
+}
+
+sw3_destroy()
+{
+ tc qdisc del dev $ul32 clsact
+
+ ip -6 route del vrf v$ul31 2001:db8:40::1/128
+ ip -6 route del vrf v$ul31 2001:db8:40::2/128
+
+ vlan_destroy $ul32 222
+ vlan_destroy $ul32 111
+ __simple_if_fini $ul32
+ simple_if_fini $ul31 2001:Db8:83::2/64
+}
+
+sw4_create()
+{
+ simple_if_init $ol4 192.0.4.1/24
+ __simple_if_init $ul4 v$ol4 2001:db8:83::1/64
+
+ tunnel_create g2 ip6gre 2001:db8:40::2 2001:db8:40::1 tos inherit dev v$ol4
+ __simple_if_init g2 v$ol4 2001:db8:40::2/128
+ ip -6 route add vrf v$ol4 2001:db8:40::1/128 via 2001:db8:83::2
+
+ ip route add vrf v$ol4 192.0.3.0/24 nexthop dev g2
+}
+
+sw4_destroy()
+{
+ ip route del vrf v$ol4 192.0.3.0/24
+
+ ip -6 route del vrf v$ol4 2001:db8:40::1/128
+ __simple_if_fini g2 2001:db8:40::2/128
+ tunnel_destroy g2
+
+ __simple_if_fini $ul4 2001:db8:83::1/64
+ simple_if_fini $ol4 192.0.4.1/24
+}
+
+h2_create()
+{
+ simple_if_init $h2 192.0.4.2/24
+ ip route add vrf v$h2 192.0.3.0/24 via 192.0.4.1
+}
+
+h2_destroy()
+{
+ ip route del vrf v$h2 192.0.3.0/24 via 192.0.4.1
+ simple_if_fini $h2 192.0.4.2/24
+}
+
+setup_prepare()
+{
+ h1=${NETIFS[p1]}
+
+ ol1=${NETIFS[p2]}
+ ul1=${NETIFS[p3]}
+
+ ul21=${NETIFS[p4]}
+ ul22=${NETIFS[p5]}
+
+ ul32=${NETIFS[p6]}
+ ul31=${NETIFS[p7]}
+
+ ul4=${NETIFS[p8]}
+ ol4=${NETIFS[p9]}
+
+ h2=${NETIFS[p10]}
+
+ vrf_prepare
+ h1_create
+ sw1_create
+ sw2_create
+ sw3_create
+ sw4_create
+ h2_create
+
+ forwarding_enable
+}
+
+cleanup()
+{
+ pre_cleanup
+
+ forwarding_restore
+
+ h2_destroy
+ sw4_destroy
+ sw3_destroy
+ sw2_destroy
+ sw1_destroy
+ h1_destroy
+ vrf_cleanup
+}
+
+multipath4_test()
+{
+ local what=$1; shift
+ local weight1=$1; shift
+ local weight2=$1; shift
+
+ sysctl_set net.ipv6.fib_multipath_hash_policy 2
+ ip route replace vrf v$ul21 2001:db8:40::2/128 \
+ nexthop via 2001:db8:81::2 weight $weight1 \
+ nexthop via 2001:db8:82::2 weight $weight2
+
+ local t0_111=$(tc_rule_stats_get $ul32 111 ingress)
+ local t0_222=$(tc_rule_stats_get $ul32 222 ingress)
+
+ ip vrf exec v$h1 \
+ $MZ $h1 -q -p 64 -A "192.0.3.2-192.0.3.62" -B "192.0.4.2-192.0.4.62" \
+ -d 1msec -c 50 -t udp "sp=1024,dp=1024"
+ sleep 1
+
+ local t1_111=$(tc_rule_stats_get $ul32 111 ingress)
+ local t1_222=$(tc_rule_stats_get $ul32 222 ingress)
+
+ local d111=$((t1_111 - t0_111))
+ local d222=$((t1_222 - t0_222))
+ multipath_eval "$what" $weight1 $weight2 $d111 $d222
+
+ ip route replace vrf v$ul21 2001:db8:40::2/128 \
+ nexthop via 2001:db8:81::2 \
+ nexthop via 2001:db8:82::2
+ sysctl_restore net.ipv6.fib_multipath_hash_policy
+}
+
+ping_ipv4()
+{
+ ping_test $h1 192.0.4.2
+}
+
+multipath_ipv4()
+{
+ log_info "Running IPv4 over GRE over IPv6 multipath tests"
+ multipath4_test "ECMP" 1 1
+ multipath4_test "Weighted MP 2:1" 2 1
+ multipath4_test "Weighted MP 11:45" 11 45
+}
+
+trap cleanup EXIT
+
+setup_prepare
+setup_wait
+tests_run
+
+exit $EXIT_STATUS
diff --git a/tools/testing/selftests/net/forwarding/ip6gre_inner_v6_multipath.sh b/tools/testing/selftests/net/forwarding/ip6gre_inner_v6_multipath.sh
new file mode 100755
index 000000000000..d208f5243ade
--- /dev/null
+++ b/tools/testing/selftests/net/forwarding/ip6gre_inner_v6_multipath.sh
@@ -0,0 +1,305 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+# Test traffic distribution when there are multiple routes between an IPv6
+# GRE tunnel. The tunnel carries IPv6 traffic between multiple hosts.
+# Multiple routes are in the underlay network. With the default multipath
+# policy, SW2 will only look at the outer IP addresses, hence only a single
+# route would be used.
+#
+# +-------------------------+
+# | H1 |
+# | $h1 + |
+# | 2001:db8:1::2/64 | |
+# +-------------------|-----+
+# |
+# +-------------------|-------------------------+
+# | SW1 | |
+# | $ol1 + |
+# | 2001:db8:1::1/64 |
+# | |
+# | + g1 (gre) |
+# | loc=2001:db8:40::1 |
+# | rem=2001:db8:40::2 --. |
+# | tos=inherit | |
+# | v |
+# | + $ul1 |
+# | | 2001:db8:80::1/64 |
+# +-------------------------|-------------------+
+# |
+# +-------------------------|-------------------+
+# | SW2 | |
+# | $ul21 + |
+# | 2001:db8:80::2/64 |
+# | | |
+# ! ________________|_____ |
+# | / \ |
+# | | | |
+# | + $ul22.111 (vlan) + $ul22.222 (vlan) |
+# | | 2001:db8:81::1/64 | 2001:db8:82::1/64 |
+# | | | |
+# +--|----------------------|-------------------+
+# | |
+# +--|----------------------|-------------------+
+# | | | |
+# | + $ul32.111 (vlan) + $ul32.222 (vlan) |
+# | | 2001:db8:81::2/64 | 2001:db8:82::2/64 |
+# | | | |
+# | \______________________/ |
+# | | |
+# | | |
+# | $ul31 + |
+# | 2001:db8:83::2/64 | SW3 |
+# +-------------------------|-------------------+
+# |
+# +-------------------------|-------------------+
+# | + $ul4 |
+# | ^ 2001:db8:83::1/64 |
+# | + g2 (gre) | |
+# | loc=2001:db8:40::2 | |
+# | rem=2001:db8:40::1 --' |
+# | tos=inherit |
+# | |
+# | $ol4 + |
+# | 2001:db8:2::1/64 | SW4 |
+# +--------------------|------------------------+
+# |
+# +--------------------|---------+
+# | | |
+# | $h2 + |
+# | 2001:db8:2::2/64 H2 |
+# +------------------------------+
+
+ALL_TESTS="
+ ping_ipv6
+ multipath_ipv6
+"
+
+NUM_NETIFS=10
+source lib.sh
+
+h1_create()
+{
+ simple_if_init $h1 2001:db8:1::2/64
+ ip -6 route add vrf v$h1 2001:db8:2::/64 via 2001:db8:1::1
+}
+
+h1_destroy()
+{
+ ip -6 route del vrf v$h1 2001:db8:2::/64 via 2001:db8:1::1
+ simple_if_fini $h1 2001:db8:1::2/64
+}
+
+sw1_create()
+{
+ simple_if_init $ol1 2001:db8:1::1/64
+ __simple_if_init $ul1 v$ol1 2001:db8:80::1/64
+
+ tunnel_create g1 ip6gre 2001:db8:40::1 2001:db8:40::2 tos inherit dev v$ol1
+ __simple_if_init g1 v$ol1 2001:db8:40::1/128
+ ip -6 route add vrf v$ol1 2001:db8:40::2/128 via 2001:db8:80::2
+
+ ip -6 route add vrf v$ol1 2001:db8:2::/64 dev g1
+}
+
+sw1_destroy()
+{
+ ip -6 route del vrf v$ol1 2001:db8:2::/64
+
+ ip -6 route del vrf v$ol1 2001:db8:40::2/128
+ __simple_if_fini g1 2001:db8:40::1/128
+ tunnel_destroy g1
+
+ __simple_if_fini $ul1 2001:db8:80::1/64
+ simple_if_fini $ol1 2001:db8:1::1/64
+}
+
+sw2_create()
+{
+ simple_if_init $ul21 2001:db8:80::2/64
+ __simple_if_init $ul22 v$ul21
+ vlan_create $ul22 111 v$ul21 2001:db8:81::1/64
+ vlan_create $ul22 222 v$ul21 2001:db8:82::1/64
+
+ ip -6 route add vrf v$ul21 2001:db8:40::1/128 via 2001:db8:80::1
+ ip -6 route add vrf v$ul21 2001:db8:40::2/128 \
+ nexthop via 2001:db8:81::2 \
+ nexthop via 2001:db8:82::2
+}
+
+sw2_destroy()
+{
+ ip -6 route del vrf v$ul21 2001:db8:40::2/128
+ ip -6 route del vrf v$ul21 2001:db8:40::1/128
+
+ vlan_destroy $ul22 222
+ vlan_destroy $ul22 111
+ __simple_if_fini $ul22
+ simple_if_fini $ul21 2001:db8:80::2/64
+}
+
+sw3_create()
+{
+ simple_if_init $ul31 2001:db8:83::2/64
+ __simple_if_init $ul32 v$ul31
+ vlan_create $ul32 111 v$ul31 2001:db8:81::2/64
+ vlan_create $ul32 222 v$ul31 2001:db8:82::2/64
+
+ ip -6 route add vrf v$ul31 2001:db8:40::2/128 via 2001:db8:83::1
+ ip -6 route add vrf v$ul31 2001:db8:40::1/128 \
+ nexthop via 2001:db8:81::1 \
+ nexthop via 2001:db8:82::1
+
+ tc qdisc add dev $ul32 clsact
+ tc filter add dev $ul32 ingress pref 111 prot 802.1Q \
+ flower vlan_id 111 action pass
+ tc filter add dev $ul32 ingress pref 222 prot 802.1Q \
+ flower vlan_id 222 action pass
+}
+
+sw3_destroy()
+{
+ tc qdisc del dev $ul32 clsact
+
+ ip -6 route del vrf v$ul31 2001:db8:40::1/128
+ ip -6 route del vrf v$ul31 2001:db8:40::2/128
+
+ vlan_destroy $ul32 222
+ vlan_destroy $ul32 111
+ __simple_if_fini $ul32
+ simple_if_fini $ul31 2001:Db8:83::2/64
+}
+
+sw4_create()
+{
+ simple_if_init $ol4 2001:db8:2::1/64
+ __simple_if_init $ul4 v$ol4 2001:db8:83::1/64
+
+ tunnel_create g2 ip6gre 2001:db8:40::2 2001:db8:40::1 tos inherit dev v$ol4
+ __simple_if_init g2 v$ol4 2001:db8:40::2/128
+ ip -6 route add vrf v$ol4 2001:db8:40::1/128 via 2001:db8:83::2
+
+ ip -6 route add vrf v$ol4 2001:db8:1::/64 dev g2
+}
+
+sw4_destroy()
+{
+ ip -6 route del vrf v$ol4 2001:db8:1::/64
+
+ ip -6 route del vrf v$ol4 2001:db8:40::1/128
+ __simple_if_fini g2 2001:db8:40::2/128
+ tunnel_destroy g2
+
+ __simple_if_fini $ul4 2001:db8:83::1/64
+ simple_if_fini $ol4 2001:db8:2::1/64
+}
+
+h2_create()
+{
+ simple_if_init $h2 2001:db8:2::2/64
+ ip -6 route add vrf v$h2 2001:db8:1::/64 via 2001:db8:2::1
+}
+
+h2_destroy()
+{
+ ip -6 route del vrf v$h2 2001:db8:1::/64 via 2001:db8:2::1
+ simple_if_fini $h2 2001:db8:2::2/64
+}
+
+setup_prepare()
+{
+ h1=${NETIFS[p1]}
+
+ ol1=${NETIFS[p2]}
+ ul1=${NETIFS[p3]}
+
+ ul21=${NETIFS[p4]}
+ ul22=${NETIFS[p5]}
+
+ ul32=${NETIFS[p6]}
+ ul31=${NETIFS[p7]}
+
+ ul4=${NETIFS[p8]}
+ ol4=${NETIFS[p9]}
+
+ h2=${NETIFS[p10]}
+
+ vrf_prepare
+ h1_create
+ sw1_create
+ sw2_create
+ sw3_create
+ sw4_create
+ h2_create
+
+ forwarding_enable
+}
+
+cleanup()
+{
+ pre_cleanup
+
+ forwarding_restore
+
+ h2_destroy
+ sw4_destroy
+ sw3_destroy
+ sw2_destroy
+ sw1_destroy
+ h1_destroy
+ vrf_cleanup
+}
+
+multipath6_test()
+{
+ local what=$1; shift
+ local weight1=$1; shift
+ local weight2=$1; shift
+
+ sysctl_set net.ipv6.fib_multipath_hash_policy 2
+ ip route replace vrf v$ul21 2001:db8:40::2/128 \
+ nexthop via 2001:db8:81::2 weight $weight1 \
+ nexthop via 2001:db8:82::2 weight $weight2
+
+ local t0_111=$(tc_rule_stats_get $ul32 111 ingress)
+ local t0_222=$(tc_rule_stats_get $ul32 222 ingress)
+
+ ip vrf exec v$h1 \
+ $MZ $h1 -6 -q -p 64 -A "2001:db8:1::2-2001:db8:1::1e" \
+ -B "2001:db8:2::2-2001:db8:2::1e" \
+ -d 1msec -c 50 -t udp "sp=1024,dp=1024"
+ sleep 1
+
+ local t1_111=$(tc_rule_stats_get $ul32 111 ingress)
+ local t1_222=$(tc_rule_stats_get $ul32 222 ingress)
+
+ local d111=$((t1_111 - t0_111))
+ local d222=$((t1_222 - t0_222))
+ multipath_eval "$what" $weight1 $weight2 $d111 $d222
+
+ ip route replace vrf v$ul21 2001:db8:40::2/128 \
+ nexthop via 2001:db8:81::2 \
+ nexthop via 2001:db8:82::2
+ sysctl_restore net.ipv6.fib_multipath_hash_policy
+}
+
+ping_ipv6()
+{
+ ping_test $h1 2001:db8:2::2
+}
+
+multipath_ipv6()
+{
+ log_info "Running IPv6 over GRE over IPv6 multipath tests"
+ multipath6_test "ECMP" 1 1
+ multipath6_test "Weighted MP 2:1" 2 1
+ multipath6_test "Weighted MP 11:45" 11 45
+}
+
+trap cleanup EXIT
+
+setup_prepare
+setup_wait
+tests_run
+
+exit $EXIT_STATUS
diff --git a/tools/testing/selftests/net/forwarding/router_broadcast.sh b/tools/testing/selftests/net/forwarding/router_broadcast.sh
index 9a678ece32b4..4eac0a06f451 100755
--- a/tools/testing/selftests/net/forwarding/router_broadcast.sh
+++ b/tools/testing/selftests/net/forwarding/router_broadcast.sh
@@ -145,16 +145,19 @@ bc_forwarding_disable()
{
sysctl_set net.ipv4.conf.all.bc_forwarding 0
sysctl_set net.ipv4.conf.$rp1.bc_forwarding 0
+ sysctl_set net.ipv4.conf.$rp2.bc_forwarding 0
}
bc_forwarding_enable()
{
sysctl_set net.ipv4.conf.all.bc_forwarding 1
sysctl_set net.ipv4.conf.$rp1.bc_forwarding 1
+ sysctl_set net.ipv4.conf.$rp2.bc_forwarding 1
}
bc_forwarding_restore()
{
+ sysctl_restore net.ipv4.conf.$rp2.bc_forwarding
sysctl_restore net.ipv4.conf.$rp1.bc_forwarding
sysctl_restore net.ipv4.conf.all.bc_forwarding
}
@@ -171,7 +174,7 @@ ping_test_from()
log_info "ping $dip, expected reply from $from"
ip vrf exec $(master_name_get $oif) \
$PING -I $oif $dip -c 10 -i 0.1 -w $PING_TIMEOUT -b 2>&1 \
- | grep $from &> /dev/null
+ | grep "bytes from $from" > /dev/null
check_err_fail $fail $?
}
diff --git a/tools/testing/selftests/net/forwarding/router_mpath_nh.sh b/tools/testing/selftests/net/forwarding/router_mpath_nh.sh
new file mode 100755
index 000000000000..cf3d26c233e8
--- /dev/null
+++ b/tools/testing/selftests/net/forwarding/router_mpath_nh.sh
@@ -0,0 +1,359 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+ALL_TESTS="ping_ipv4 ping_ipv6 multipath_test"
+NUM_NETIFS=8
+source lib.sh
+
+h1_create()
+{
+ vrf_create "vrf-h1"
+ ip link set dev $h1 master vrf-h1
+
+ ip link set dev vrf-h1 up
+ ip link set dev $h1 up
+
+ ip address add 192.0.2.2/24 dev $h1
+ ip address add 2001:db8:1::2/64 dev $h1
+
+ ip route add 198.51.100.0/24 vrf vrf-h1 nexthop via 192.0.2.1
+ ip route add 2001:db8:2::/64 vrf vrf-h1 nexthop via 2001:db8:1::1
+}
+
+h1_destroy()
+{
+ ip route del 2001:db8:2::/64 vrf vrf-h1
+ ip route del 198.51.100.0/24 vrf vrf-h1
+
+ ip address del 2001:db8:1::2/64 dev $h1
+ ip address del 192.0.2.2/24 dev $h1
+
+ ip link set dev $h1 down
+ vrf_destroy "vrf-h1"
+}
+
+h2_create()
+{
+ vrf_create "vrf-h2"
+ ip link set dev $h2 master vrf-h2
+
+ ip link set dev vrf-h2 up
+ ip link set dev $h2 up
+
+ ip address add 198.51.100.2/24 dev $h2
+ ip address add 2001:db8:2::2/64 dev $h2
+
+ ip route add 192.0.2.0/24 vrf vrf-h2 nexthop via 198.51.100.1
+ ip route add 2001:db8:1::/64 vrf vrf-h2 nexthop via 2001:db8:2::1
+}
+
+h2_destroy()
+{
+ ip route del 2001:db8:1::/64 vrf vrf-h2
+ ip route del 192.0.2.0/24 vrf vrf-h2
+
+ ip address del 2001:db8:2::2/64 dev $h2
+ ip address del 198.51.100.2/24 dev $h2
+
+ ip link set dev $h2 down
+ vrf_destroy "vrf-h2"
+}
+
+router1_create()
+{
+ vrf_create "vrf-r1"
+ ip link set dev $rp11 master vrf-r1
+ ip link set dev $rp12 master vrf-r1
+ ip link set dev $rp13 master vrf-r1
+
+ ip link set dev vrf-r1 up
+ ip link set dev $rp11 up
+ ip link set dev $rp12 up
+ ip link set dev $rp13 up
+
+ ip address add 192.0.2.1/24 dev $rp11
+ ip address add 2001:db8:1::1/64 dev $rp11
+
+ ip address add 169.254.2.12/24 dev $rp12
+ ip address add fe80:2::12/64 dev $rp12
+
+ ip address add 169.254.3.13/24 dev $rp13
+ ip address add fe80:3::13/64 dev $rp13
+}
+
+router1_destroy()
+{
+ ip route del 2001:db8:2::/64 vrf vrf-r1
+ ip route del 198.51.100.0/24 vrf vrf-r1
+
+ ip address del fe80:3::13/64 dev $rp13
+ ip address del 169.254.3.13/24 dev $rp13
+
+ ip address del fe80:2::12/64 dev $rp12
+ ip address del 169.254.2.12/24 dev $rp12
+
+ ip address del 2001:db8:1::1/64 dev $rp11
+ ip address del 192.0.2.1/24 dev $rp11
+
+ ip nexthop del id 103
+ ip nexthop del id 101
+ ip nexthop del id 102
+ ip nexthop del id 106
+ ip nexthop del id 104
+ ip nexthop del id 105
+
+ ip link set dev $rp13 down
+ ip link set dev $rp12 down
+ ip link set dev $rp11 down
+
+ vrf_destroy "vrf-r1"
+}
+
+router2_create()
+{
+ vrf_create "vrf-r2"
+ ip link set dev $rp21 master vrf-r2
+ ip link set dev $rp22 master vrf-r2
+ ip link set dev $rp23 master vrf-r2
+
+ ip link set dev vrf-r2 up
+ ip link set dev $rp21 up
+ ip link set dev $rp22 up
+ ip link set dev $rp23 up
+
+ ip address add 198.51.100.1/24 dev $rp21
+ ip address add 2001:db8:2::1/64 dev $rp21
+
+ ip address add 169.254.2.22/24 dev $rp22
+ ip address add fe80:2::22/64 dev $rp22
+
+ ip address add 169.254.3.23/24 dev $rp23
+ ip address add fe80:3::23/64 dev $rp23
+}
+
+router2_destroy()
+{
+ ip route del 2001:db8:1::/64 vrf vrf-r2
+ ip route del 192.0.2.0/24 vrf vrf-r2
+
+ ip address del fe80:3::23/64 dev $rp23
+ ip address del 169.254.3.23/24 dev $rp23
+
+ ip address del fe80:2::22/64 dev $rp22
+ ip address del 169.254.2.22/24 dev $rp22
+
+ ip address del 2001:db8:2::1/64 dev $rp21
+ ip address del 198.51.100.1/24 dev $rp21
+
+ ip nexthop del id 201
+ ip nexthop del id 202
+ ip nexthop del id 204
+ ip nexthop del id 205
+
+ ip link set dev $rp23 down
+ ip link set dev $rp22 down
+ ip link set dev $rp21 down
+
+ vrf_destroy "vrf-r2"
+}
+
+routing_nh_obj()
+{
+ ip nexthop add id 101 via 169.254.2.22 dev $rp12
+ ip nexthop add id 102 via 169.254.3.23 dev $rp13
+ ip nexthop add id 103 group 101/102
+ ip route add 198.51.100.0/24 vrf vrf-r1 nhid 103
+
+ ip nexthop add id 104 via fe80:2::22 dev $rp12
+ ip nexthop add id 105 via fe80:3::23 dev $rp13
+ ip nexthop add id 106 group 104/105
+ ip route add 2001:db8:2::/64 vrf vrf-r1 nhid 106
+
+ ip nexthop add id 201 via 169.254.2.12 dev $rp22
+ ip nexthop add id 202 via 169.254.3.13 dev $rp23
+ ip nexthop add id 203 group 201/202
+ ip route add 192.0.2.0/24 vrf vrf-r2 nhid 203
+
+ ip nexthop add id 204 via fe80:2::12 dev $rp22
+ ip nexthop add id 205 via fe80:3::13 dev $rp23
+ ip nexthop add id 206 group 204/205
+ ip route add 2001:db8:1::/64 vrf vrf-r2 nhid 206
+}
+
+multipath4_test()
+{
+ local desc="$1"
+ local weight_rp12=$2
+ local weight_rp13=$3
+ local t0_rp12 t0_rp13 t1_rp12 t1_rp13
+ local packets_rp12 packets_rp13
+
+ # Transmit multiple flows from h1 to h2 and make sure they are
+ # distributed between both multipath links (rp12 and rp13)
+ # according to the configured weights.
+ sysctl_set net.ipv4.fib_multipath_hash_policy 1
+ ip nexthop replace id 103 group 101,$weight_rp12/102,$weight_rp13
+
+ t0_rp12=$(link_stats_tx_packets_get $rp12)
+ t0_rp13=$(link_stats_tx_packets_get $rp13)
+
+ ip vrf exec vrf-h1 $MZ -q -p 64 -A 192.0.2.2 -B 198.51.100.2 \
+ -d 1msec -t udp "sp=1024,dp=0-32768"
+
+ t1_rp12=$(link_stats_tx_packets_get $rp12)
+ t1_rp13=$(link_stats_tx_packets_get $rp13)
+
+ let "packets_rp12 = $t1_rp12 - $t0_rp12"
+ let "packets_rp13 = $t1_rp13 - $t0_rp13"
+ multipath_eval "$desc" $weight_rp12 $weight_rp13 $packets_rp12 $packets_rp13
+
+ # Restore settings.
+ ip nexthop replace id 103 group 101/102
+ sysctl_restore net.ipv4.fib_multipath_hash_policy
+}
+
+multipath6_l4_test()
+{
+ local desc="$1"
+ local weight_rp12=$2
+ local weight_rp13=$3
+ local t0_rp12 t0_rp13 t1_rp12 t1_rp13
+ local packets_rp12 packets_rp13
+
+ # Transmit multiple flows from h1 to h2 and make sure they are
+ # distributed between both multipath links (rp12 and rp13)
+ # according to the configured weights.
+ sysctl_set net.ipv6.fib_multipath_hash_policy 1
+
+ ip nexthop replace id 106 group 104,$weight_rp12/105,$weight_rp13
+
+ t0_rp12=$(link_stats_tx_packets_get $rp12)
+ t0_rp13=$(link_stats_tx_packets_get $rp13)
+
+ $MZ $h1 -6 -q -p 64 -A 2001:db8:1::2 -B 2001:db8:2::2 \
+ -d 1msec -t udp "sp=1024,dp=0-32768"
+
+ t1_rp12=$(link_stats_tx_packets_get $rp12)
+ t1_rp13=$(link_stats_tx_packets_get $rp13)
+
+ let "packets_rp12 = $t1_rp12 - $t0_rp12"
+ let "packets_rp13 = $t1_rp13 - $t0_rp13"
+ multipath_eval "$desc" $weight_rp12 $weight_rp13 $packets_rp12 $packets_rp13
+
+ ip nexthop replace id 106 group 104/105
+
+ sysctl_restore net.ipv6.fib_multipath_hash_policy
+}
+
+multipath6_test()
+{
+ local desc="$1"
+ local weight_rp12=$2
+ local weight_rp13=$3
+ local t0_rp12 t0_rp13 t1_rp12 t1_rp13
+ local packets_rp12 packets_rp13
+
+ ip nexthop replace id 106 group 104,$weight_rp12/105,$weight_rp13
+
+ t0_rp12=$(link_stats_tx_packets_get $rp12)
+ t0_rp13=$(link_stats_tx_packets_get $rp13)
+
+ # Generate 16384 echo requests, each with a random flow label.
+ for _ in $(seq 1 16384); do
+ ip vrf exec vrf-h1 $PING6 2001:db8:2::2 -F 0 -c 1 -q >/dev/null 2>&1
+ done
+
+ t1_rp12=$(link_stats_tx_packets_get $rp12)
+ t1_rp13=$(link_stats_tx_packets_get $rp13)
+
+ let "packets_rp12 = $t1_rp12 - $t0_rp12"
+ let "packets_rp13 = $t1_rp13 - $t0_rp13"
+ multipath_eval "$desc" $weight_rp12 $weight_rp13 $packets_rp12 $packets_rp13
+
+ ip nexthop replace id 106 group 104/105
+}
+
+multipath_test()
+{
+ log_info "Running IPv4 multipath tests"
+ multipath4_test "ECMP" 1 1
+ multipath4_test "Weighted MP 2:1" 2 1
+ multipath4_test "Weighted MP 11:45" 11 45
+
+ log_info "Running IPv6 multipath tests"
+ multipath6_test "ECMP" 1 1
+ multipath6_test "Weighted MP 2:1" 2 1
+ multipath6_test "Weighted MP 11:45" 11 45
+
+ log_info "Running IPv6 L4 hash multipath tests"
+ multipath6_l4_test "ECMP" 1 1
+ multipath6_l4_test "Weighted MP 2:1" 2 1
+ multipath6_l4_test "Weighted MP 11:45" 11 45
+}
+
+setup_prepare()
+{
+ h1=${NETIFS[p1]}
+ rp11=${NETIFS[p2]}
+
+ rp12=${NETIFS[p3]}
+ rp22=${NETIFS[p4]}
+
+ rp13=${NETIFS[p5]}
+ rp23=${NETIFS[p6]}
+
+ rp21=${NETIFS[p7]}
+ h2=${NETIFS[p8]}
+
+ vrf_prepare
+
+ h1_create
+ h2_create
+
+ router1_create
+ router2_create
+ routing_nh_obj
+
+ forwarding_enable
+}
+
+cleanup()
+{
+ pre_cleanup
+
+ forwarding_restore
+
+ router2_destroy
+ router1_destroy
+
+ h2_destroy
+ h1_destroy
+
+ vrf_cleanup
+}
+
+ping_ipv4()
+{
+ ping_test $h1 198.51.100.2
+}
+
+ping_ipv6()
+{
+ ping6_test $h1 2001:db8:2::2
+}
+
+ip nexthop ls >/dev/null 2>&1
+if [ $? -ne 0 ]; then
+ echo "Nexthop objects not supported; skipping tests"
+ exit 0
+fi
+
+trap cleanup EXIT
+
+setup_prepare
+setup_wait
+routing_nh_obj
+
+tests_run
+
+exit $EXIT_STATUS
diff --git a/tools/testing/selftests/net/forwarding/tc_flower.sh b/tools/testing/selftests/net/forwarding/tc_flower.sh
index 29bcfa84aec7..058c746ee300 100755
--- a/tools/testing/selftests/net/forwarding/tc_flower.sh
+++ b/tools/testing/selftests/net/forwarding/tc_flower.sh
@@ -2,7 +2,8 @@
# SPDX-License-Identifier: GPL-2.0
ALL_TESTS="match_dst_mac_test match_src_mac_test match_dst_ip_test \
- match_src_ip_test match_ip_flags_test match_pcp_test match_vlan_test"
+ match_src_ip_test match_ip_flags_test match_pcp_test match_vlan_test \
+ match_ip_tos_test match_indev_test"
NUM_NETIFS=2
source tc_common.sh
source lib.sh
@@ -276,6 +277,63 @@ match_vlan_test()
log_test "VLAN match ($tcflags)"
}
+match_ip_tos_test()
+{
+ RET=0
+
+ tc filter add dev $h2 ingress protocol ip pref 1 handle 101 flower \
+ $tcflags dst_ip 192.0.2.2 ip_tos 0x20 action drop
+ tc filter add dev $h2 ingress protocol ip pref 2 handle 102 flower \
+ $tcflags dst_ip 192.0.2.2 ip_tos 0x18 action drop
+
+ $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \
+ -t ip tos=18 -q
+
+ tc_check_packets "dev $h2 ingress" 101 1
+ check_fail $? "Matched on a wrong filter (0x18)"
+
+ tc_check_packets "dev $h2 ingress" 102 1
+ check_err $? "Did not match on correct filter (0x18)"
+
+ $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \
+ -t ip tos=20 -q
+
+ tc_check_packets "dev $h2 ingress" 102 2
+ check_fail $? "Matched on a wrong filter (0x20)"
+
+ tc_check_packets "dev $h2 ingress" 101 1
+ check_err $? "Did not match on correct filter (0x20)"
+
+ tc filter del dev $h2 ingress protocol ip pref 2 handle 102 flower
+ tc filter del dev $h2 ingress protocol ip pref 1 handle 101 flower
+
+ log_test "ip_tos match ($tcflags)"
+}
+
+match_indev_test()
+{
+ RET=0
+
+ tc filter add dev $h2 ingress protocol ip pref 1 handle 101 flower \
+ $tcflags indev $h1 dst_mac $h2mac action drop
+ tc filter add dev $h2 ingress protocol ip pref 2 handle 102 flower \
+ $tcflags indev $h2 dst_mac $h2mac action drop
+
+ $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \
+ -t ip -q
+
+ tc_check_packets "dev $h2 ingress" 101 1
+ check_fail $? "Matched on a wrong filter"
+
+ tc_check_packets "dev $h2 ingress" 102 1
+ check_err $? "Did not match on correct filter"
+
+ tc filter del dev $h2 ingress protocol ip pref 2 handle 102 flower
+ tc filter del dev $h2 ingress protocol ip pref 1 handle 101 flower
+
+ log_test "indev match ($tcflags)"
+}
+
setup_prepare()
{
h1=${NETIFS[p1]}
diff --git a/tools/testing/selftests/net/forwarding/tc_flower_router.sh b/tools/testing/selftests/net/forwarding/tc_flower_router.sh
new file mode 100755
index 000000000000..4aee9c9e69f6
--- /dev/null
+++ b/tools/testing/selftests/net/forwarding/tc_flower_router.sh
@@ -0,0 +1,172 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+ALL_TESTS="match_indev_egress_test"
+NUM_NETIFS=6
+source tc_common.sh
+source lib.sh
+
+h1_create()
+{
+ simple_if_init $h1 192.0.1.1/24
+
+ ip route add 192.0.2.0/24 vrf v$h1 nexthop via 192.0.1.2
+ ip route add 192.0.3.0/24 vrf v$h1 nexthop via 192.0.1.2
+}
+
+h1_destroy()
+{
+ ip route del 192.0.3.0/24 vrf v$h1
+ ip route del 192.0.2.0/24 vrf v$h1
+
+ simple_if_fini $h1 192.0.1.1/24
+}
+
+h2_create()
+{
+ simple_if_init $h2 192.0.2.1/24
+
+ ip route add 192.0.1.0/24 vrf v$h2 nexthop via 192.0.2.2
+ ip route add 192.0.3.0/24 vrf v$h2 nexthop via 192.0.2.2
+}
+
+h2_destroy()
+{
+ ip route del 192.0.3.0/24 vrf v$h2
+ ip route del 192.0.1.0/24 vrf v$h2
+
+ simple_if_fini $h2 192.0.2.1/24
+}
+
+h3_create()
+{
+ simple_if_init $h3 192.0.3.1/24
+
+ ip route add 192.0.1.0/24 vrf v$h3 nexthop via 192.0.3.2
+ ip route add 192.0.2.0/24 vrf v$h3 nexthop via 192.0.3.2
+}
+
+h3_destroy()
+{
+ ip route del 192.0.2.0/24 vrf v$h3
+ ip route del 192.0.1.0/24 vrf v$h3
+
+ simple_if_fini $h3 192.0.3.1/24
+}
+
+
+router_create()
+{
+ ip link set dev $rp1 up
+ ip link set dev $rp2 up
+ ip link set dev $rp3 up
+
+ tc qdisc add dev $rp3 clsact
+
+ ip address add 192.0.1.2/24 dev $rp1
+ ip address add 192.0.2.2/24 dev $rp2
+ ip address add 192.0.3.2/24 dev $rp3
+}
+
+router_destroy()
+{
+ ip address del 192.0.3.2/24 dev $rp3
+ ip address del 192.0.2.2/24 dev $rp2
+ ip address del 192.0.1.2/24 dev $rp1
+
+ tc qdisc del dev $rp3 clsact
+
+ ip link set dev $rp3 down
+ ip link set dev $rp2 down
+ ip link set dev $rp1 down
+}
+
+match_indev_egress_test()
+{
+ RET=0
+
+ tc filter add dev $rp3 egress protocol ip pref 1 handle 101 flower \
+ $tcflags indev $rp1 dst_ip 192.0.3.1 action drop
+ tc filter add dev $rp3 egress protocol ip pref 2 handle 102 flower \
+ $tcflags indev $rp2 dst_ip 192.0.3.1 action drop
+
+ $MZ $h1 -c 1 -p 64 -a $h1mac -b $rp1mac -A 192.0.1.1 -B 192.0.3.1 \
+ -t ip -q
+
+ tc_check_packets "dev $rp3 egress" 102 1
+ check_fail $? "Matched on a wrong filter"
+
+ tc_check_packets "dev $rp3 egress" 101 1
+ check_err $? "Did not match on correct filter"
+
+ $MZ $h2 -c 1 -p 64 -a $h2mac -b $rp2mac -A 192.0.2.1 -B 192.0.3.1 \
+ -t ip -q
+
+ tc_check_packets "dev $rp3 egress" 101 2
+ check_fail $? "Matched on a wrong filter"
+
+ tc_check_packets "dev $rp3 egress" 102 1
+ check_err $? "Did not match on correct filter"
+
+ tc filter del dev $rp3 egress protocol ip pref 2 handle 102 flower
+ tc filter del dev $rp3 egress protocol ip pref 1 handle 101 flower
+
+ log_test "indev egress match ($tcflags)"
+}
+
+setup_prepare()
+{
+ h1=${NETIFS[p1]}
+ rp1=${NETIFS[p2]}
+
+ h2=${NETIFS[p3]}
+ rp2=${NETIFS[p4]}
+
+ h3=${NETIFS[p5]}
+ rp3=${NETIFS[p6]}
+
+ h1mac=$(mac_get $h1)
+ rp1mac=$(mac_get $rp1)
+ h2mac=$(mac_get $h2)
+ rp2mac=$(mac_get $rp2)
+
+ vrf_prepare
+
+ h1_create
+ h2_create
+ h3_create
+
+ router_create
+
+ forwarding_enable
+}
+
+cleanup()
+{
+ pre_cleanup
+
+ forwarding_restore
+
+ router_destroy
+
+ h3_destroy
+ h2_destroy
+ h1_destroy
+
+ vrf_cleanup
+}
+
+trap cleanup EXIT
+
+setup_prepare
+setup_wait
+
+tc_offload_check
+if [[ $? -ne 0 ]]; then
+ log_info "Could not test offloaded functionality"
+else
+ tcflags="skip_sw"
+ tests_run
+fi
+
+exit $EXIT_STATUS
diff --git a/tools/testing/selftests/net/forwarding/tc_shblocks.sh b/tools/testing/selftests/net/forwarding/tc_shblocks.sh
index 9826a446e2c0..772e00ac3230 100755
--- a/tools/testing/selftests/net/forwarding/tc_shblocks.sh
+++ b/tools/testing/selftests/net/forwarding/tc_shblocks.sh
@@ -1,7 +1,7 @@
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
-ALL_TESTS="shared_block_test"
+ALL_TESTS="shared_block_test match_indev_test"
NUM_NETIFS=4
source tc_common.sh
source lib.sh
@@ -70,6 +70,33 @@ shared_block_test()
log_test "shared block ($tcflags)"
}
+match_indev_test()
+{
+ RET=0
+
+ tc filter add block 22 protocol ip pref 1 handle 101 flower \
+ $tcflags indev $swp1 dst_mac $swmac action drop
+ tc filter add block 22 protocol ip pref 2 handle 102 flower \
+ $tcflags indev $swp2 dst_mac $swmac action drop
+
+ $MZ $h1 -c 1 -p 64 -a $h1mac -b $swmac -A 192.0.2.1 -B 192.0.2.2 \
+ -t ip -q
+
+ tc_check_packets "block 22" 101 1
+ check_err $? "Did not match first incoming packet on a block"
+
+ $MZ $h2 -c 1 -p 64 -a $h2mac -b $swmac -A 192.0.2.1 -B 192.0.2.2 \
+ -t ip -q
+
+ tc_check_packets "block 22" 102 1
+ check_err $? "Did not match second incoming packet on a block"
+
+ tc filter del block 22 protocol ip pref 1 handle 101 flower
+ tc filter del block 22 protocol ip pref 2 handle 102 flower
+
+ log_test "indev match ($tcflags)"
+}
+
setup_prepare()
{
h1=${NETIFS[p1]}
diff --git a/tools/testing/selftests/net/icmp_redirect.sh b/tools/testing/selftests/net/icmp_redirect.sh
new file mode 100755
index 000000000000..18c5de53558a
--- /dev/null
+++ b/tools/testing/selftests/net/icmp_redirect.sh
@@ -0,0 +1,534 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# redirect test
+#
+# .253 +----+
+# +----| r1 |
+# | +----+
+# +----+ | |.1
+# | h1 |--------------+ | 10.1.1.0/30 2001:db8:1::0/126
+# +----+ .1 | |.2
+# 172.16.1/24 | +----+ +----+
+# 2001:db8:16:1/64 +----| r2 |-------------------| h2 |
+# .254 +----+ .254 .2 +----+
+# 172.16.2/24
+# 2001:db8:16:2/64
+#
+# Route from h1 to h2 goes through r1, eth1 - connection between r1 and r2.
+# Route on r1 changed to go to r2 via eth0. This causes a redirect to be sent
+# from r1 to h1 telling h1 to use r2 when talking to h2.
+
+VERBOSE=0
+PAUSE_ON_FAIL=no
+
+H1_N1_IP=172.16.1.1
+R1_N1_IP=172.16.1.253
+R2_N1_IP=172.16.1.254
+
+H1_N1_IP6=2001:db8:16:1::1
+R1_N1_IP6=2001:db8:16:1::253
+R2_N1_IP6=2001:db8:16:1::254
+
+R1_R2_N1_IP=10.1.1.1
+R2_R1_N1_IP=10.1.1.2
+
+R1_R2_N1_IP6=2001:db8:1::1
+R2_R1_N1_IP6=2001:db8:1::2
+
+H2_N2=172.16.2.0/24
+H2_N2_6=2001:db8:16:2::/64
+H2_N2_IP=172.16.2.2
+R2_N2_IP=172.16.2.254
+H2_N2_IP6=2001:db8:16:2::2
+R2_N2_IP6=2001:db8:16:2::254
+
+VRF=red
+VRF_TABLE=1111
+
+################################################################################
+# helpers
+
+log_section()
+{
+ echo
+ echo "###########################################################################"
+ echo "$*"
+ echo "###########################################################################"
+ echo
+}
+
+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
+}
+
+log_debug()
+{
+ if [ "$VERBOSE" = "1" ]; then
+ echo "$*"
+ fi
+}
+
+run_cmd()
+{
+ local cmd="$*"
+ local out
+ local rc
+
+ if [ "$VERBOSE" = "1" ]; then
+ echo "COMMAND: $cmd"
+ fi
+
+ out=$(eval $cmd 2>&1)
+ rc=$?
+ if [ "$VERBOSE" = "1" -a -n "$out" ]; then
+ echo "$out"
+ fi
+
+ [ "$VERBOSE" = "1" ] && echo
+
+ return $rc
+}
+
+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
+}
+
+################################################################################
+# setup and teardown
+
+cleanup()
+{
+ local ns
+
+ for ns in h1 h2 r1 r2; do
+ ip netns del $ns 2>/dev/null
+ done
+}
+
+create_vrf()
+{
+ local ns=$1
+
+ ip -netns ${ns} link add ${VRF} type vrf table ${VRF_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
+
+ 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
+}
+
+setup()
+{
+ local ns
+
+ #
+ # create nodes as namespaces
+ #
+ for ns in h1 h2 r1 r2; do
+ ip netns add $ns
+ ip -netns $ns li set lo up
+
+ case "${ns}" in
+ h[12]) ip netns exec $ns sysctl -q -w net.ipv4.conf.all.accept_redirects=1
+ ip netns exec $ns sysctl -q -w net.ipv6.conf.all.forwarding=0
+ ip netns exec $ns sysctl -q -w net.ipv6.conf.all.accept_redirects=1
+ ip netns exec $ns sysctl -q -w net.ipv6.conf.all.keep_addr_on_down=1
+ ;;
+ r[12]) ip netns exec $ns sysctl -q -w net.ipv4.ip_forward=1
+ ip netns exec $ns sysctl -q -w net.ipv4.conf.all.send_redirects=1
+
+ ip netns exec $ns sysctl -q -w net.ipv6.conf.all.forwarding=1
+ ip netns exec $ns sysctl -q -w net.ipv6.route.mtu_expires=10
+ esac
+ done
+
+ #
+ # create interconnects
+ #
+ ip -netns h1 li add eth0 type veth peer name r1h1
+ ip -netns h1 li set r1h1 netns r1 name eth0 up
+
+ ip -netns h1 li add eth1 type veth peer name r2h1
+ ip -netns h1 li set r2h1 netns r2 name eth0 up
+
+ ip -netns h2 li add eth0 type veth peer name r2h2
+ ip -netns h2 li set eth0 up
+ ip -netns h2 li set r2h2 netns r2 name eth2 up
+
+ ip -netns r1 li add eth1 type veth peer name r2r1
+ ip -netns r1 li set eth1 up
+ ip -netns r1 li set r2r1 netns r2 name eth1 up
+
+ #
+ # h1
+ #
+ if [ "${WITH_VRF}" = "yes" ]; then
+ create_vrf "h1"
+ H1_VRF_ARG="vrf ${VRF}"
+ H1_PING_ARG="-I ${VRF}"
+ else
+ H1_VRF_ARG=
+ H1_PING_ARG=
+ fi
+ ip -netns h1 li add br0 type bridge
+ if [ "${WITH_VRF}" = "yes" ]; then
+ ip -netns h1 li set br0 vrf ${VRF} up
+ else
+ ip -netns h1 li set br0 up
+ fi
+ ip -netns h1 addr add dev br0 ${H1_N1_IP}/24
+ ip -netns h1 -6 addr add dev br0 ${H1_N1_IP6}/64 nodad
+ ip -netns h1 li set eth0 master br0 up
+ ip -netns h1 li set eth1 master br0 up
+
+ #
+ # h2
+ #
+ ip -netns h2 addr add dev eth0 ${H2_N2_IP}/24
+ ip -netns h2 ro add default via ${R2_N2_IP} dev eth0
+ ip -netns h2 -6 addr add dev eth0 ${H2_N2_IP6}/64 nodad
+ ip -netns h2 -6 ro add default via ${R2_N2_IP6} dev eth0
+
+ #
+ # r1
+ #
+ ip -netns r1 addr add dev eth0 ${R1_N1_IP}/24
+ ip -netns r1 -6 addr add dev eth0 ${R1_N1_IP6}/64 nodad
+ ip -netns r1 addr add dev eth1 ${R1_R2_N1_IP}/30
+ ip -netns r1 -6 addr add dev eth1 ${R1_R2_N1_IP6}/126 nodad
+
+ #
+ # r2
+ #
+ ip -netns r2 addr add dev eth0 ${R2_N1_IP}/24
+ ip -netns r2 -6 addr add dev eth0 ${R2_N1_IP6}/64 nodad
+ ip -netns r2 addr add dev eth1 ${R2_R1_N1_IP}/30
+ ip -netns r2 -6 addr add dev eth1 ${R2_R1_N1_IP6}/126 nodad
+ ip -netns r2 addr add dev eth2 ${R2_N2_IP}/24
+ ip -netns r2 -6 addr add dev eth2 ${R2_N2_IP6}/64 nodad
+
+ sleep 2
+
+ R1_LLADDR=$(get_linklocal r1 eth0)
+ if [ $? -ne 0 ]; then
+ echo "Error: Failed to get link-local address of r1's eth0"
+ exit 1
+ fi
+ log_debug "initial gateway is R1's lladdr = ${R1_LLADDR}"
+
+ R2_LLADDR=$(get_linklocal r2 eth0)
+ if [ $? -ne 0 ]; then
+ echo "Error: Failed to get link-local address of r2's eth0"
+ exit 1
+ fi
+ log_debug "initial gateway is R2's lladdr = ${R2_LLADDR}"
+}
+
+change_h2_mtu()
+{
+ local mtu=$1
+
+ run_cmd ip -netns h2 li set eth0 mtu ${mtu}
+ run_cmd ip -netns r2 li set eth2 mtu ${mtu}
+}
+
+check_exception()
+{
+ local mtu="$1"
+ local with_redirect="$2"
+ local desc="$3"
+
+ # From 172.16.1.101: icmp_seq=1 Redirect Host(New nexthop: 172.16.1.102)
+ if [ "$VERBOSE" = "1" ]; then
+ echo "Commands to check for exception:"
+ run_cmd ip -netns h1 ro get ${H1_VRF_ARG} ${H2_N2_IP}
+ run_cmd ip -netns h1 -6 ro get ${H1_VRF_ARG} ${H2_N2_IP6}
+ fi
+
+ if [ -n "${mtu}" ]; then
+ mtu=" mtu ${mtu}"
+ fi
+ if [ "$with_redirect" = "yes" ]; then
+ ip -netns h1 ro get ${H1_VRF_ARG} ${H2_N2_IP} | \
+ grep -q "cache <redirected> expires [0-9]*sec${mtu}"
+ elif [ -n "${mtu}" ]; then
+ ip -netns h1 ro get ${H1_VRF_ARG} ${H2_N2_IP} | \
+ grep -q "cache expires [0-9]*sec${mtu}"
+ else
+ # want to verify that neither mtu nor redirected appears in
+ # the route get output. The -v will wipe out the cache line
+ # if either are set so the last grep -q will not find a match
+ ip -netns h1 ro get ${H1_VRF_ARG} ${H2_N2_IP} | \
+ grep -E -v 'mtu|redirected' | grep -q "cache"
+ fi
+ log_test $? 0 "IPv4: ${desc}"
+
+ if [ "$with_redirect" = "yes" ]; then
+ ip -netns h1 -6 ro get ${H1_VRF_ARG} ${H2_N2_IP6} | \
+ grep -q "${H2_N2_IP6} from :: via ${R2_LLADDR} dev br0.*${mtu}"
+ elif [ -n "${mtu}" ]; then
+ ip -netns h1 -6 ro get ${H1_VRF_ARG} ${H2_N2_IP6} | \
+ grep -q "${mtu}"
+ else
+ # IPv6 is a bit harder. First strip out the match if it
+ # contains an mtu exception and then look for the first
+ # gateway - R1's lladdr
+ ip -netns h1 -6 ro get ${H1_VRF_ARG} ${H2_N2_IP6} | \
+ grep -v "mtu" | grep -q "${R1_LLADDR}"
+ fi
+ log_test $? 0 "IPv6: ${desc}"
+}
+
+run_ping()
+{
+ local sz=$1
+
+ run_cmd ip netns exec h1 ping -q -M want -i 0.5 -c 10 -w 2 -s ${sz} ${H1_PING_ARG} ${H2_N2_IP}
+ run_cmd ip netns exec h1 ${ping6} -q -M want -i 0.5 -c 10 -w 2 -s ${sz} ${H1_PING_ARG} ${H2_N2_IP6}
+}
+
+replace_route_new()
+{
+ # r1 to h2 via r2 and eth0
+ run_cmd ip -netns r1 nexthop replace id 1 via ${R2_N1_IP} dev eth0
+ run_cmd ip -netns r1 nexthop replace id 2 via ${R2_LLADDR} dev eth0
+}
+
+reset_route_new()
+{
+ run_cmd ip -netns r1 nexthop flush
+ run_cmd ip -netns h1 nexthop flush
+
+ initial_route_new
+}
+
+initial_route_new()
+{
+ # r1 to h2 via r2 and eth1
+ run_cmd ip -netns r1 nexthop add id 1 via ${R2_R1_N1_IP} dev eth1
+ run_cmd ip -netns r1 ro add ${H2_N2} nhid 1
+
+ run_cmd ip -netns r1 nexthop add id 2 via ${R2_R1_N1_IP6} dev eth1
+ run_cmd ip -netns r1 -6 ro add ${H2_N2_6} nhid 2
+
+ # h1 to h2 via r1
+ run_cmd ip -netns h1 nexthop add id 1 via ${R1_N1_IP} dev br0
+ run_cmd ip -netns h1 ro add ${H1_VRF_ARG} ${H2_N2} nhid 1
+
+ run_cmd ip -netns h1 nexthop add id 2 via ${R1_LLADDR} dev br0
+ run_cmd ip -netns h1 -6 ro add ${H1_VRF_ARG} ${H2_N2_6} nhid 2
+}
+
+replace_route_legacy()
+{
+ # r1 to h2 via r2 and eth0
+ run_cmd ip -netns r1 ro replace ${H2_N2} via ${R2_N1_IP} dev eth0
+ run_cmd ip -netns r1 -6 ro replace ${H2_N2_6} via ${R2_LLADDR} dev eth0
+}
+
+reset_route_legacy()
+{
+ run_cmd ip -netns r1 ro del ${H2_N2}
+ run_cmd ip -netns r1 -6 ro del ${H2_N2_6}
+
+ run_cmd ip -netns h1 ro del ${H1_VRF_ARG} ${H2_N2}
+ run_cmd ip -netns h1 -6 ro del ${H1_VRF_ARG} ${H2_N2_6}
+
+ initial_route_legacy
+}
+
+initial_route_legacy()
+{
+ # r1 to h2 via r2 and eth1
+ run_cmd ip -netns r1 ro add ${H2_N2} via ${R2_R1_N1_IP} dev eth1
+ run_cmd ip -netns r1 -6 ro add ${H2_N2_6} via ${R2_R1_N1_IP6} dev eth1
+
+ # h1 to h2 via r1
+ # - IPv6 redirect only works if gateway is the LLA
+ run_cmd ip -netns h1 ro add ${H1_VRF_ARG} ${H2_N2} via ${R1_N1_IP} dev br0
+ run_cmd ip -netns h1 -6 ro add ${H1_VRF_ARG} ${H2_N2_6} via ${R1_LLADDR} dev br0
+}
+
+check_connectivity()
+{
+ local rc
+
+ run_cmd ip netns exec h1 ping -c1 -w1 ${H1_PING_ARG} ${H2_N2_IP}
+ rc=$?
+ run_cmd ip netns exec h1 ${ping6} -c1 -w1 ${H1_PING_ARG} ${H2_N2_IP6}
+ [ $? -ne 0 ] && rc=$?
+
+ return $rc
+}
+
+do_test()
+{
+ local ttype="$1"
+
+ eval initial_route_${ttype}
+
+ # verify connectivity
+ check_connectivity
+ if [ $? -ne 0 ]; then
+ echo "Error: Basic connectivity is broken"
+ ret=1
+ return
+ fi
+
+ # redirect exception followed by mtu
+ eval replace_route_${ttype}
+ run_ping 64
+ check_exception "" "yes" "redirect exception"
+
+ check_connectivity
+ if [ $? -ne 0 ]; then
+ echo "Error: Basic connectivity is broken after redirect"
+ ret=1
+ return
+ fi
+
+ change_h2_mtu 1300
+ run_ping 1350
+ check_exception "1300" "yes" "redirect exception plus mtu"
+
+ # remove exceptions and restore routing
+ change_h2_mtu 1500
+ eval reset_route_${ttype}
+
+ check_connectivity
+ if [ $? -ne 0 ]; then
+ echo "Error: Basic connectivity is broken after reset"
+ ret=1
+ return
+ fi
+ check_exception "" "no" "routing reset"
+
+ # MTU exception followed by redirect
+ change_h2_mtu 1300
+ run_ping 1350
+ check_exception "1300" "no" "mtu exception"
+
+ eval replace_route_${ttype}
+ run_ping 64
+ check_exception "1300" "yes" "mtu exception plus redirect"
+
+ check_connectivity
+ if [ $? -ne 0 ]; then
+ echo "Error: Basic connectivity is broken after redirect"
+ ret=1
+ return
+ fi
+}
+
+################################################################################
+# usage
+
+usage()
+{
+ cat <<EOF
+usage: ${0##*/} OPTS
+
+ -p Pause on fail
+ -v verbose mode (show commands and output)
+EOF
+}
+
+################################################################################
+# main
+
+# Some systems don't have a ping6 binary anymore
+which ping6 > /dev/null 2>&1 && ping6=$(which ping6) || ping6=$(which ping)
+
+ret=0
+nsuccess=0
+nfail=0
+
+while getopts :pv o
+do
+ case $o in
+ p) PAUSE_ON_FAIL=yes;;
+ v) VERBOSE=$(($VERBOSE + 1));;
+ *) usage; exit 1;;
+ esac
+done
+
+trap cleanup EXIT
+
+cleanup
+WITH_VRF=no
+setup
+
+log_section "Legacy routing"
+do_test "legacy"
+
+cleanup
+log_section "Legacy routing with VRF"
+WITH_VRF=yes
+setup
+do_test "legacy"
+
+cleanup
+log_section "Routing with nexthop objects"
+ip nexthop ls >/dev/null 2>&1
+if [ $? -eq 0 ]; then
+ WITH_VRF=no
+ setup
+ do_test "new"
+
+ cleanup
+ log_section "Routing with nexthop objects and VRF"
+ WITH_VRF=yes
+ setup
+ do_test "new"
+else
+ echo "Nexthop objects not supported; skipping tests"
+fi
+
+printf "\nTests passed: %3d\n" ${nsuccess}
+printf "Tests failed: %3d\n" ${nfail}
+
+exit $ret
diff --git a/tools/testing/selftests/net/ipv6_flowlabel.c b/tools/testing/selftests/net/ipv6_flowlabel.c
new file mode 100644
index 000000000000..a7c41375374f
--- /dev/null
+++ b/tools/testing/selftests/net/ipv6_flowlabel.c
@@ -0,0 +1,229 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Test IPV6_FLOWINFO cmsg on send and recv */
+
+#define _GNU_SOURCE
+
+#include <arpa/inet.h>
+#include <asm/byteorder.h>
+#include <error.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <linux/in6.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+/* uapi/glibc weirdness may leave this undefined */
+#ifndef IPV6_FLOWINFO
+#define IPV6_FLOWINFO 11
+#endif
+
+#ifndef IPV6_FLOWLABEL_MGR
+#define IPV6_FLOWLABEL_MGR 32
+#endif
+
+#define FLOWLABEL_WILDCARD ((uint32_t) -1)
+
+static const char cfg_data[] = "a";
+static uint32_t cfg_label = 1;
+
+static void do_send(int fd, bool with_flowlabel, uint32_t flowlabel)
+{
+ char control[CMSG_SPACE(sizeof(flowlabel))] = {0};
+ struct msghdr msg = {0};
+ struct iovec iov = {0};
+ int ret;
+
+ iov.iov_base = (char *)cfg_data;
+ iov.iov_len = sizeof(cfg_data);
+
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ if (with_flowlabel) {
+ struct cmsghdr *cm;
+
+ cm = (void *)control;
+ cm->cmsg_len = CMSG_LEN(sizeof(flowlabel));
+ cm->cmsg_level = SOL_IPV6;
+ cm->cmsg_type = IPV6_FLOWINFO;
+ *(uint32_t *)CMSG_DATA(cm) = htonl(flowlabel);
+
+ msg.msg_control = control;
+ msg.msg_controllen = sizeof(control);
+ }
+
+ ret = sendmsg(fd, &msg, 0);
+ if (ret == -1)
+ error(1, errno, "send");
+
+ if (with_flowlabel)
+ fprintf(stderr, "sent with label %u\n", flowlabel);
+ else
+ fprintf(stderr, "sent without label\n");
+}
+
+static void do_recv(int fd, bool with_flowlabel, uint32_t expect)
+{
+ char control[CMSG_SPACE(sizeof(expect))];
+ char data[sizeof(cfg_data)];
+ struct msghdr msg = {0};
+ struct iovec iov = {0};
+ struct cmsghdr *cm;
+ uint32_t flowlabel;
+ int ret;
+
+ iov.iov_base = data;
+ iov.iov_len = sizeof(data);
+
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ memset(control, 0, sizeof(control));
+ msg.msg_control = control;
+ msg.msg_controllen = sizeof(control);
+
+ ret = recvmsg(fd, &msg, 0);
+ if (ret == -1)
+ error(1, errno, "recv");
+ if (msg.msg_flags & (MSG_TRUNC | MSG_CTRUNC))
+ error(1, 0, "recv: truncated");
+ if (ret != sizeof(cfg_data))
+ error(1, 0, "recv: length mismatch");
+ if (memcmp(data, cfg_data, sizeof(data)))
+ error(1, 0, "recv: data mismatch");
+
+ cm = CMSG_FIRSTHDR(&msg);
+ if (with_flowlabel) {
+ if (!cm)
+ error(1, 0, "recv: missing cmsg");
+ if (CMSG_NXTHDR(&msg, cm))
+ error(1, 0, "recv: too many cmsg");
+ if (cm->cmsg_level != SOL_IPV6 ||
+ cm->cmsg_type != IPV6_FLOWINFO)
+ error(1, 0, "recv: unexpected cmsg level or type");
+
+ flowlabel = ntohl(*(uint32_t *)CMSG_DATA(cm));
+ fprintf(stderr, "recv with label %u\n", flowlabel);
+
+ if (expect != FLOWLABEL_WILDCARD && expect != flowlabel)
+ fprintf(stderr, "recv: incorrect flowlabel %u != %u\n",
+ flowlabel, expect);
+
+ } else {
+ fprintf(stderr, "recv without label\n");
+ }
+}
+
+static bool get_autoflowlabel_enabled(void)
+{
+ int fd, ret;
+ char val;
+
+ fd = open("/proc/sys/net/ipv6/auto_flowlabels", O_RDONLY);
+ if (fd == -1)
+ error(1, errno, "open sysctl");
+
+ ret = read(fd, &val, 1);
+ if (ret == -1)
+ error(1, errno, "read sysctl");
+ if (ret == 0)
+ error(1, 0, "read sysctl: 0");
+
+ if (close(fd))
+ error(1, errno, "close sysctl");
+
+ return val == '1';
+}
+
+static void flowlabel_get(int fd, uint32_t label, uint8_t share, uint16_t flags)
+{
+ struct in6_flowlabel_req req = {
+ .flr_action = IPV6_FL_A_GET,
+ .flr_label = htonl(label),
+ .flr_flags = flags,
+ .flr_share = share,
+ };
+
+ /* do not pass IPV6_ADDR_ANY or IPV6_ADDR_MAPPED */
+ req.flr_dst.s6_addr[0] = 0xfd;
+ req.flr_dst.s6_addr[15] = 0x1;
+
+ if (setsockopt(fd, SOL_IPV6, IPV6_FLOWLABEL_MGR, &req, sizeof(req)))
+ error(1, errno, "setsockopt flowlabel get");
+}
+
+static void parse_opts(int argc, char **argv)
+{
+ int c;
+
+ while ((c = getopt(argc, argv, "l:")) != -1) {
+ switch (c) {
+ case 'l':
+ cfg_label = strtoul(optarg, NULL, 0);
+ break;
+ default:
+ error(1, 0, "%s: parse error", argv[0]);
+ }
+ }
+}
+
+int main(int argc, char **argv)
+{
+ struct sockaddr_in6 addr = {
+ .sin6_family = AF_INET6,
+ .sin6_port = htons(8000),
+ .sin6_addr = IN6ADDR_LOOPBACK_INIT,
+ };
+ const int one = 1;
+ int fdt, fdr;
+
+ parse_opts(argc, argv);
+
+ fdt = socket(PF_INET6, SOCK_DGRAM, 0);
+ if (fdt == -1)
+ error(1, errno, "socket t");
+
+ fdr = socket(PF_INET6, SOCK_DGRAM, 0);
+ if (fdr == -1)
+ error(1, errno, "socket r");
+
+ if (connect(fdt, (void *)&addr, sizeof(addr)))
+ error(1, errno, "connect");
+ if (bind(fdr, (void *)&addr, sizeof(addr)))
+ error(1, errno, "bind");
+
+ flowlabel_get(fdt, cfg_label, IPV6_FL_S_EXCL, IPV6_FL_F_CREATE);
+
+ if (setsockopt(fdr, SOL_IPV6, IPV6_FLOWINFO, &one, sizeof(one)))
+ error(1, errno, "setsockopt flowinfo");
+
+ if (get_autoflowlabel_enabled()) {
+ fprintf(stderr, "send no label: recv auto flowlabel\n");
+ do_send(fdt, false, 0);
+ do_recv(fdr, true, FLOWLABEL_WILDCARD);
+ } else {
+ fprintf(stderr, "send no label: recv no label (auto off)\n");
+ do_send(fdt, false, 0);
+ do_recv(fdr, false, 0);
+ }
+
+ fprintf(stderr, "send label\n");
+ do_send(fdt, true, cfg_label);
+ do_recv(fdr, true, cfg_label);
+
+ if (close(fdr))
+ error(1, errno, "close r");
+ if (close(fdt))
+ error(1, errno, "close t");
+
+ return 0;
+}
diff --git a/tools/testing/selftests/net/ipv6_flowlabel.sh b/tools/testing/selftests/net/ipv6_flowlabel.sh
new file mode 100755
index 000000000000..d3bc6442704e
--- /dev/null
+++ b/tools/testing/selftests/net/ipv6_flowlabel.sh
@@ -0,0 +1,21 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0
+#
+# Regression tests for IPv6 flowlabels
+#
+# run in separate namespaces to avoid mgmt db conflicts betweent tests
+
+set -e
+
+echo "TEST management"
+./in_netns.sh ./ipv6_flowlabel_mgr
+
+echo "TEST datapath"
+./in_netns.sh \
+ sh -c 'sysctl -q -w net.ipv6.auto_flowlabels=0 && ./ipv6_flowlabel -l 1'
+
+echo "TEST datapath (with auto-flowlabels)"
+./in_netns.sh \
+ sh -c 'sysctl -q -w net.ipv6.auto_flowlabels=1 && ./ipv6_flowlabel -l 1'
+
+echo OK. All tests passed
diff --git a/tools/testing/selftests/net/ipv6_flowlabel_mgr.c b/tools/testing/selftests/net/ipv6_flowlabel_mgr.c
new file mode 100644
index 000000000000..af95b48acea9
--- /dev/null
+++ b/tools/testing/selftests/net/ipv6_flowlabel_mgr.c
@@ -0,0 +1,199 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Test IPV6_FLOWINFO_MGR */
+
+#define _GNU_SOURCE
+
+#include <arpa/inet.h>
+#include <error.h>
+#include <errno.h>
+#include <limits.h>
+#include <linux/in6.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+/* uapi/glibc weirdness may leave this undefined */
+#ifndef IPV6_FLOWLABEL_MGR
+#define IPV6_FLOWLABEL_MGR 32
+#endif
+
+/* from net/ipv6/ip6_flowlabel.c */
+#define FL_MIN_LINGER 6
+
+#define explain(x) \
+ do { if (cfg_verbose) fprintf(stderr, " " x "\n"); } while (0)
+
+#define __expect(x) \
+ do { \
+ if (!(x)) \
+ fprintf(stderr, "[OK] " #x "\n"); \
+ else \
+ error(1, 0, "[ERR] " #x " (line %d)", __LINE__); \
+ } while (0)
+
+#define expect_pass(x) __expect(x)
+#define expect_fail(x) __expect(!(x))
+
+static bool cfg_long_running;
+static bool cfg_verbose;
+
+static int flowlabel_get(int fd, uint32_t label, uint8_t share, uint16_t flags)
+{
+ struct in6_flowlabel_req req = {
+ .flr_action = IPV6_FL_A_GET,
+ .flr_label = htonl(label),
+ .flr_flags = flags,
+ .flr_share = share,
+ };
+
+ /* do not pass IPV6_ADDR_ANY or IPV6_ADDR_MAPPED */
+ req.flr_dst.s6_addr[0] = 0xfd;
+ req.flr_dst.s6_addr[15] = 0x1;
+
+ return setsockopt(fd, SOL_IPV6, IPV6_FLOWLABEL_MGR, &req, sizeof(req));
+}
+
+static int flowlabel_put(int fd, uint32_t label)
+{
+ struct in6_flowlabel_req req = {
+ .flr_action = IPV6_FL_A_PUT,
+ .flr_label = htonl(label),
+ };
+
+ return setsockopt(fd, SOL_IPV6, IPV6_FLOWLABEL_MGR, &req, sizeof(req));
+}
+
+static void run_tests(int fd)
+{
+ int wstatus;
+ pid_t pid;
+
+ explain("cannot get non-existent label");
+ expect_fail(flowlabel_get(fd, 1, IPV6_FL_S_ANY, 0));
+
+ explain("cannot put non-existent label");
+ expect_fail(flowlabel_put(fd, 1));
+
+ explain("cannot create label greater than 20 bits");
+ expect_fail(flowlabel_get(fd, 0x1FFFFF, IPV6_FL_S_ANY,
+ IPV6_FL_F_CREATE));
+
+ explain("create a new label (FL_F_CREATE)");
+ expect_pass(flowlabel_get(fd, 1, IPV6_FL_S_ANY, IPV6_FL_F_CREATE));
+ explain("can get the label (without FL_F_CREATE)");
+ expect_pass(flowlabel_get(fd, 1, IPV6_FL_S_ANY, 0));
+ explain("can get it again with create flag set, too");
+ expect_pass(flowlabel_get(fd, 1, IPV6_FL_S_ANY, IPV6_FL_F_CREATE));
+ explain("cannot get it again with the exclusive (FL_FL_EXCL) flag");
+ expect_fail(flowlabel_get(fd, 1, IPV6_FL_S_ANY,
+ IPV6_FL_F_CREATE | IPV6_FL_F_EXCL));
+ explain("can now put exactly three references");
+ expect_pass(flowlabel_put(fd, 1));
+ expect_pass(flowlabel_put(fd, 1));
+ expect_pass(flowlabel_put(fd, 1));
+ expect_fail(flowlabel_put(fd, 1));
+
+ explain("create a new exclusive label (FL_S_EXCL)");
+ expect_pass(flowlabel_get(fd, 2, IPV6_FL_S_EXCL, IPV6_FL_F_CREATE));
+ explain("cannot get it again in non-exclusive mode");
+ expect_fail(flowlabel_get(fd, 2, IPV6_FL_S_ANY, IPV6_FL_F_CREATE));
+ explain("cannot get it again in exclusive mode either");
+ expect_fail(flowlabel_get(fd, 2, IPV6_FL_S_EXCL, IPV6_FL_F_CREATE));
+ expect_pass(flowlabel_put(fd, 2));
+
+ if (cfg_long_running) {
+ explain("cannot reuse the label, due to linger");
+ expect_fail(flowlabel_get(fd, 2, IPV6_FL_S_ANY,
+ IPV6_FL_F_CREATE));
+ explain("after sleep, can reuse");
+ sleep(FL_MIN_LINGER * 2 + 1);
+ expect_pass(flowlabel_get(fd, 2, IPV6_FL_S_ANY,
+ IPV6_FL_F_CREATE));
+ }
+
+ explain("create a new user-private label (FL_S_USER)");
+ expect_pass(flowlabel_get(fd, 3, IPV6_FL_S_USER, IPV6_FL_F_CREATE));
+ explain("cannot get it again in non-exclusive mode");
+ expect_fail(flowlabel_get(fd, 3, IPV6_FL_S_ANY, 0));
+ explain("cannot get it again in exclusive mode");
+ expect_fail(flowlabel_get(fd, 3, IPV6_FL_S_EXCL, 0));
+ explain("can get it again in user mode");
+ expect_pass(flowlabel_get(fd, 3, IPV6_FL_S_USER, 0));
+ explain("child process can get it too, but not after setuid(nobody)");
+ pid = fork();
+ if (pid == -1)
+ error(1, errno, "fork");
+ if (!pid) {
+ expect_pass(flowlabel_get(fd, 3, IPV6_FL_S_USER, 0));
+ if (setuid(USHRT_MAX))
+ fprintf(stderr, "[INFO] skip setuid child test\n");
+ else
+ expect_fail(flowlabel_get(fd, 3, IPV6_FL_S_USER, 0));
+ exit(0);
+ }
+ if (wait(&wstatus) == -1)
+ error(1, errno, "wait");
+ if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0)
+ error(1, errno, "wait: unexpected child result");
+
+ explain("create a new process-private label (FL_S_PROCESS)");
+ expect_pass(flowlabel_get(fd, 4, IPV6_FL_S_PROCESS, IPV6_FL_F_CREATE));
+ explain("can get it again");
+ expect_pass(flowlabel_get(fd, 4, IPV6_FL_S_PROCESS, 0));
+ explain("child process cannot can get it");
+ pid = fork();
+ if (pid == -1)
+ error(1, errno, "fork");
+ if (!pid) {
+ expect_fail(flowlabel_get(fd, 4, IPV6_FL_S_PROCESS, 0));
+ exit(0);
+ }
+ if (wait(&wstatus) == -1)
+ error(1, errno, "wait");
+ if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0)
+ error(1, errno, "wait: unexpected child result");
+}
+
+static void parse_opts(int argc, char **argv)
+{
+ int c;
+
+ while ((c = getopt(argc, argv, "lv")) != -1) {
+ switch (c) {
+ case 'l':
+ cfg_long_running = true;
+ break;
+ case 'v':
+ cfg_verbose = true;
+ break;
+ default:
+ error(1, 0, "%s: parse error", argv[0]);
+ }
+ }
+}
+
+int main(int argc, char **argv)
+{
+ int fd;
+
+ parse_opts(argc, argv);
+
+ fd = socket(PF_INET6, SOCK_DGRAM, 0);
+ if (fd == -1)
+ error(1, errno, "socket");
+
+ run_tests(fd);
+
+ if (close(fd))
+ error(1, errno, "close");
+
+ return 0;
+}
diff --git a/tools/testing/selftests/net/pmtu.sh b/tools/testing/selftests/net/pmtu.sh
index 524b15dabb3c..ab367e75f095 100755
--- a/tools/testing/selftests/net/pmtu.sh
+++ b/tools/testing/selftests/net/pmtu.sh
@@ -111,6 +111,14 @@
#
# - cleanup_ipv6_exception
# Same as above, but use IPv6 transport from A to B
+#
+# - list_flush_ipv4_exception
+# Using the same topology as in pmtu_ipv4, create exceptions, and check
+# they are shown when listing exception caches, gone after flushing them
+#
+# - list_flush_ipv6_exception
+# Using the same topology as in pmtu_ipv6, create exceptions, and check
+# they are shown when listing exception caches, gone after flushing them
# Kselftest framework requirement - SKIP code is 4.
@@ -123,39 +131,42 @@ TRACING=0
# Some systems don't have a ping6 binary anymore
which ping6 > /dev/null 2>&1 && ping6=$(which ping6) || ping6=$(which ping)
+# Name Description re-run with nh
tests="
- pmtu_ipv4_exception ipv4: PMTU exceptions
- pmtu_ipv6_exception ipv6: PMTU exceptions
- pmtu_ipv4_vxlan4_exception IPv4 over vxlan4: PMTU exceptions
- pmtu_ipv6_vxlan4_exception IPv6 over vxlan4: PMTU exceptions
- pmtu_ipv4_vxlan6_exception IPv4 over vxlan6: PMTU exceptions
- pmtu_ipv6_vxlan6_exception IPv6 over vxlan6: PMTU exceptions
- pmtu_ipv4_geneve4_exception IPv4 over geneve4: PMTU exceptions
- pmtu_ipv6_geneve4_exception IPv6 over geneve4: PMTU exceptions
- pmtu_ipv4_geneve6_exception IPv4 over geneve6: PMTU exceptions
- pmtu_ipv6_geneve6_exception IPv6 over geneve6: PMTU exceptions
- pmtu_ipv4_fou4_exception IPv4 over fou4: PMTU exceptions
- pmtu_ipv6_fou4_exception IPv6 over fou4: PMTU exceptions
- pmtu_ipv4_fou6_exception IPv4 over fou6: PMTU exceptions
- pmtu_ipv6_fou6_exception IPv6 over fou6: PMTU exceptions
- pmtu_ipv4_gue4_exception IPv4 over gue4: PMTU exceptions
- pmtu_ipv6_gue4_exception IPv6 over gue4: PMTU exceptions
- pmtu_ipv4_gue6_exception IPv4 over gue6: PMTU exceptions
- pmtu_ipv6_gue6_exception IPv6 over gue6: PMTU exceptions
- pmtu_vti6_exception vti6: PMTU exceptions
- pmtu_vti4_exception vti4: PMTU exceptions
- pmtu_vti4_default_mtu vti4: default MTU assignment
- pmtu_vti6_default_mtu vti6: default MTU assignment
- pmtu_vti4_link_add_mtu vti4: MTU setting on link creation
- pmtu_vti6_link_add_mtu vti6: MTU setting on link creation
- pmtu_vti6_link_change_mtu vti6: MTU changes on link changes
- cleanup_ipv4_exception ipv4: cleanup of cached exceptions
- cleanup_ipv6_exception ipv6: cleanup of cached exceptions"
-
-NS_A="ns-$(mktemp -u XXXXXX)"
-NS_B="ns-$(mktemp -u XXXXXX)"
-NS_R1="ns-$(mktemp -u XXXXXX)"
-NS_R2="ns-$(mktemp -u XXXXXX)"
+ pmtu_ipv4_exception ipv4: PMTU exceptions 1
+ pmtu_ipv6_exception ipv6: PMTU exceptions 1
+ pmtu_ipv4_vxlan4_exception IPv4 over vxlan4: PMTU exceptions 1
+ pmtu_ipv6_vxlan4_exception IPv6 over vxlan4: PMTU exceptions 1
+ pmtu_ipv4_vxlan6_exception IPv4 over vxlan6: PMTU exceptions 1
+ pmtu_ipv6_vxlan6_exception IPv6 over vxlan6: PMTU exceptions 1
+ pmtu_ipv4_geneve4_exception IPv4 over geneve4: PMTU exceptions 1
+ pmtu_ipv6_geneve4_exception IPv6 over geneve4: PMTU exceptions 1
+ pmtu_ipv4_geneve6_exception IPv4 over geneve6: PMTU exceptions 1
+ pmtu_ipv6_geneve6_exception IPv6 over geneve6: PMTU exceptions 1
+ pmtu_ipv4_fou4_exception IPv4 over fou4: PMTU exceptions 1
+ pmtu_ipv6_fou4_exception IPv6 over fou4: PMTU exceptions 1
+ pmtu_ipv4_fou6_exception IPv4 over fou6: PMTU exceptions 1
+ pmtu_ipv6_fou6_exception IPv6 over fou6: PMTU exceptions 1
+ pmtu_ipv4_gue4_exception IPv4 over gue4: PMTU exceptions 1
+ pmtu_ipv6_gue4_exception IPv6 over gue4: PMTU exceptions 1
+ pmtu_ipv4_gue6_exception IPv4 over gue6: PMTU exceptions 1
+ pmtu_ipv6_gue6_exception IPv6 over gue6: PMTU exceptions 1
+ pmtu_vti6_exception vti6: PMTU exceptions 0
+ pmtu_vti4_exception vti4: PMTU exceptions 0
+ pmtu_vti4_default_mtu vti4: default MTU assignment 0
+ pmtu_vti6_default_mtu vti6: default MTU assignment 0
+ pmtu_vti4_link_add_mtu vti4: MTU setting on link creation 0
+ pmtu_vti6_link_add_mtu vti6: MTU setting on link creation 0
+ pmtu_vti6_link_change_mtu vti6: MTU changes on link changes 0
+ cleanup_ipv4_exception ipv4: cleanup of cached exceptions 1
+ cleanup_ipv6_exception ipv6: cleanup of cached exceptions 1
+ list_flush_ipv4_exception ipv4: list and flush cached exceptions 1
+ list_flush_ipv6_exception ipv6: list and flush cached exceptions 1"
+
+NS_A="ns-A"
+NS_B="ns-B"
+NS_R1="ns-R1"
+NS_R2="ns-R2"
ns_a="ip netns exec ${NS_A}"
ns_b="ip netns exec ${NS_B}"
ns_r1="ip netns exec ${NS_R1}"
@@ -194,6 +205,30 @@ routes="
B default ${prefix6}:${b_r1}::2
"
+USE_NH="no"
+# ns family nh id destination gateway
+nexthops="
+ A 4 41 ${prefix4}.${a_r1}.2 veth_A-R1
+ A 4 42 ${prefix4}.${a_r2}.2 veth_A-R2
+ B 4 41 ${prefix4}.${b_r1}.2 veth_B-R1
+
+ A 6 61 ${prefix6}:${a_r1}::2 veth_A-R1
+ A 6 62 ${prefix6}:${a_r2}::2 veth_A-R2
+ B 6 61 ${prefix6}:${b_r1}::2 veth_B-R1
+"
+
+# nexthop id correlates to id in nexthops config above
+# ns family prefix nh id
+routes_nh="
+ A 4 default 41
+ A 4 ${prefix4}.${b_r2}.1 42
+ B 4 default 41
+
+ A 6 default 61
+ A 6 ${prefix6}:${b_r2}::1 62
+ B 6 default 61
+"
+
veth4_a_addr="192.168.1.1"
veth4_b_addr="192.168.1.2"
veth4_mask="24"
@@ -208,11 +243,10 @@ tunnel6_a_addr="fd00:2::a"
tunnel6_b_addr="fd00:2::b"
tunnel6_mask="64"
-dummy6_0_addr="fc00:1000::0"
-dummy6_1_addr="fc00:1001::0"
+dummy6_0_prefix="fc00:1000::"
+dummy6_1_prefix="fc00:1001::"
dummy6_mask="64"
-cleanup_done=1
err_buf=
tcpdump_pids=
@@ -430,15 +464,15 @@ setup_xfrm() {
veth_a_addr="${2}"
veth_b_addr="${3}"
- run_cmd "${ns_a} ip -${proto} xfrm state add src ${veth_a_addr} dst ${veth_b_addr} spi 0x1000 proto esp aead 'rfc4106(gcm(aes))' 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f 128 mode tunnel" || return 1
- run_cmd "${ns_a} ip -${proto} xfrm state add src ${veth_b_addr} dst ${veth_a_addr} spi 0x1001 proto esp aead 'rfc4106(gcm(aes))' 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f 128 mode tunnel"
- run_cmd "${ns_a} ip -${proto} xfrm policy add dir out mark 10 tmpl src ${veth_a_addr} dst ${veth_b_addr} proto esp mode tunnel"
- run_cmd "${ns_a} ip -${proto} xfrm policy add dir in mark 10 tmpl src ${veth_b_addr} dst ${veth_a_addr} proto esp mode tunnel"
+ run_cmd ${ns_a} ip -${proto} xfrm state add src ${veth_a_addr} dst ${veth_b_addr} spi 0x1000 proto esp aead 'rfc4106(gcm(aes))' 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f 128 mode tunnel || return 1
+ run_cmd ${ns_a} ip -${proto} xfrm state add src ${veth_b_addr} dst ${veth_a_addr} spi 0x1001 proto esp aead 'rfc4106(gcm(aes))' 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f 128 mode tunnel
+ run_cmd ${ns_a} ip -${proto} xfrm policy add dir out mark 10 tmpl src ${veth_a_addr} dst ${veth_b_addr} proto esp mode tunnel
+ run_cmd ${ns_a} ip -${proto} xfrm policy add dir in mark 10 tmpl src ${veth_b_addr} dst ${veth_a_addr} proto esp mode tunnel
- run_cmd "${ns_b} ip -${proto} xfrm state add src ${veth_a_addr} dst ${veth_b_addr} spi 0x1000 proto esp aead 'rfc4106(gcm(aes))' 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f 128 mode tunnel"
- run_cmd "${ns_b} ip -${proto} xfrm state add src ${veth_b_addr} dst ${veth_a_addr} spi 0x1001 proto esp aead 'rfc4106(gcm(aes))' 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f 128 mode tunnel"
- run_cmd "${ns_b} ip -${proto} xfrm policy add dir out mark 10 tmpl src ${veth_b_addr} dst ${veth_a_addr} proto esp mode tunnel"
- run_cmd "${ns_b} ip -${proto} xfrm policy add dir in mark 10 tmpl src ${veth_a_addr} dst ${veth_b_addr} proto esp mode tunnel"
+ run_cmd ${ns_b} ip -${proto} xfrm state add src ${veth_a_addr} dst ${veth_b_addr} spi 0x1000 proto esp aead 'rfc4106(gcm(aes))' 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f 128 mode tunnel
+ run_cmd ${ns_b} ip -${proto} xfrm state add src ${veth_b_addr} dst ${veth_a_addr} spi 0x1001 proto esp aead 'rfc4106(gcm(aes))' 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f 128 mode tunnel
+ run_cmd ${ns_b} ip -${proto} xfrm policy add dir out mark 10 tmpl src ${veth_b_addr} dst ${veth_a_addr} proto esp mode tunnel
+ run_cmd ${ns_b} ip -${proto} xfrm policy add dir in mark 10 tmpl src ${veth_a_addr} dst ${veth_b_addr} proto esp mode tunnel
}
setup_xfrm4() {
@@ -449,6 +483,50 @@ setup_xfrm6() {
setup_xfrm 6 ${veth6_a_addr} ${veth6_b_addr}
}
+setup_routing_old() {
+ for i in ${routes}; do
+ [ "${ns}" = "" ] && ns="${i}" && continue
+ [ "${addr}" = "" ] && addr="${i}" && continue
+ [ "${gw}" = "" ] && gw="${i}"
+
+ ns_name="$(nsname ${ns})"
+
+ ip -n ${ns_name} route add ${addr} via ${gw}
+
+ ns=""; addr=""; gw=""
+ done
+}
+
+setup_routing_new() {
+ for i in ${nexthops}; do
+ [ "${ns}" = "" ] && ns="${i}" && continue
+ [ "${fam}" = "" ] && fam="${i}" && continue
+ [ "${nhid}" = "" ] && nhid="${i}" && continue
+ [ "${gw}" = "" ] && gw="${i}" && continue
+ [ "${dev}" = "" ] && dev="${i}"
+
+ ns_name="$(nsname ${ns})"
+
+ ip -n ${ns_name} -${fam} nexthop add id ${nhid} via ${gw} dev ${dev}
+
+ ns=""; fam=""; nhid=""; gw=""; dev=""
+
+ done
+
+ for i in ${routes_nh}; do
+ [ "${ns}" = "" ] && ns="${i}" && continue
+ [ "${fam}" = "" ] && fam="${i}" && continue
+ [ "${addr}" = "" ] && addr="${i}" && continue
+ [ "${nhid}" = "" ] && nhid="${i}"
+
+ ns_name="$(nsname ${ns})"
+
+ ip -n ${ns_name} -${fam} route add ${addr} nhid ${nhid}
+
+ ns=""; fam=""; addr=""; nhid=""
+ done
+}
+
setup_routing() {
for i in ${NS_R1} ${NS_R2}; do
ip netns exec ${i} sysctl -q net/ipv4/ip_forward=1
@@ -479,23 +557,19 @@ setup_routing() {
ns=""; peer=""; segment=""
done
- for i in ${routes}; do
- [ "${ns}" = "" ] && ns="${i}" && continue
- [ "${addr}" = "" ] && addr="${i}" && continue
- [ "${gw}" = "" ] && gw="${i}"
-
- ns_name="$(nsname ${ns})"
-
- ip -n ${ns_name} route add ${addr} via ${gw}
+ if [ "$USE_NH" = "yes" ]; then
+ setup_routing_new
+ else
+ setup_routing_old
+ fi
- ns=""; addr=""; gw=""
- done
+ return 0
}
setup() {
[ "$(id -u)" -ne 0 ] && echo " need to run as root" && return $ksft_skip
- cleanup_done=0
+ cleanup
for arg do
eval setup_${arg} || { echo " ${arg} not supported"; return 1; }
done
@@ -519,11 +593,9 @@ cleanup() {
done
tcpdump_pids=
- [ ${cleanup_done} -eq 1 ] && return
for n in ${NS_A} ${NS_B} ${NS_R1} ${NS_R2}; do
ip netns del ${n} 2> /dev/null
done
- cleanup_done=1
}
mtu() {
@@ -1005,13 +1077,13 @@ test_pmtu_vti6_link_change_mtu() {
run_cmd ${ns_a} ip link set dummy0 up
run_cmd ${ns_a} ip link set dummy1 up
- run_cmd ${ns_a} ip addr add ${dummy6_0_addr}/${dummy6_mask} dev dummy0
- run_cmd ${ns_a} ip addr add ${dummy6_1_addr}/${dummy6_mask} dev dummy1
+ run_cmd ${ns_a} ip addr add ${dummy6_0_prefix}1/${dummy6_mask} dev dummy0
+ run_cmd ${ns_a} ip addr add ${dummy6_1_prefix}1/${dummy6_mask} dev dummy1
fail=0
# Create vti6 interface bound to device, passing MTU, check it
- run_cmd ${ns_a} ip link add vti6_a mtu 1300 type vti6 remote ${dummy6_0_addr} local ${dummy6_0_addr}
+ run_cmd ${ns_a} ip link add vti6_a mtu 1300 type vti6 remote ${dummy6_0_prefix}2 local ${dummy6_0_prefix}1
mtu="$(link_get_mtu "${ns_a}" vti6_a)"
if [ ${mtu} -ne 1300 ]; then
err " vti6 MTU ${mtu} doesn't match configured value 1300"
@@ -1020,7 +1092,7 @@ test_pmtu_vti6_link_change_mtu() {
# Move to another device with different MTU, without passing MTU, check
# MTU is adjusted
- run_cmd ${ns_a} ip link set vti6_a type vti6 remote ${dummy6_1_addr} local ${dummy6_1_addr}
+ run_cmd ${ns_a} ip link set vti6_a type vti6 remote ${dummy6_1_prefix}2 local ${dummy6_1_prefix}1
mtu="$(link_get_mtu "${ns_a}" vti6_a)"
if [ ${mtu} -ne $((3000 - 40)) ]; then
err " vti MTU ${mtu} is not dummy MTU 3000 minus IPv6 header length"
@@ -1028,7 +1100,7 @@ test_pmtu_vti6_link_change_mtu() {
fi
# Move it back, passing MTU, check MTU is not overridden
- run_cmd ${ns_a} ip link set vti6_a mtu 1280 type vti6 remote ${dummy6_0_addr} local ${dummy6_0_addr}
+ run_cmd ${ns_a} ip link set vti6_a mtu 1280 type vti6 remote ${dummy6_0_prefix}2 local ${dummy6_0_prefix}1
mtu="$(link_get_mtu "${ns_a}" vti6_a)"
if [ ${mtu} -ne 1280 ]; then
err " vti6 MTU ${mtu} doesn't match configured value 1280"
@@ -1093,6 +1165,158 @@ test_cleanup_ipv4_exception() {
test_cleanup_vxlanX_exception 4
}
+run_test() {
+ (
+ tname="$1"
+ tdesc="$2"
+
+ unset IFS
+
+ if [ "$VERBOSE" = "1" ]; then
+ printf "\n##########################################################################\n\n"
+ fi
+
+ eval test_${tname}
+ ret=$?
+
+ if [ $ret -eq 0 ]; then
+ printf "TEST: %-60s [ OK ]\n" "${tdesc}"
+ elif [ $ret -eq 1 ]; then
+ printf "TEST: %-60s [FAIL]\n" "${tdesc}"
+ if [ "${PAUSE_ON_FAIL}" = "yes" ]; then
+ echo
+ echo "Pausing. Hit enter to continue"
+ read a
+ fi
+ err_flush
+ exit 1
+ elif [ $ret -eq 2 ]; then
+ printf "TEST: %-60s [SKIP]\n" "${tdesc}"
+ err_flush
+ fi
+
+ return $ret
+ )
+ ret=$?
+ [ $ret -ne 0 ] && exitcode=1
+
+ return $ret
+}
+
+run_test_nh() {
+ tname="$1"
+ tdesc="$2"
+
+ USE_NH=yes
+ run_test "${tname}" "${tdesc} - nexthop objects"
+ USE_NH=no
+}
+
+test_list_flush_ipv4_exception() {
+ setup namespaces routing || return 2
+ trace "${ns_a}" veth_A-R1 "${ns_r1}" veth_R1-A \
+ "${ns_r1}" veth_R1-B "${ns_b}" veth_B-R1 \
+ "${ns_a}" veth_A-R2 "${ns_r2}" veth_R2-A \
+ "${ns_r2}" veth_R2-B "${ns_b}" veth_B-R2
+
+ dst_prefix1="${prefix4}.${b_r1}."
+ dst2="${prefix4}.${b_r2}.1"
+
+ # Set up initial MTU values
+ mtu "${ns_a}" veth_A-R1 2000
+ mtu "${ns_r1}" veth_R1-A 2000
+ mtu "${ns_r1}" veth_R1-B 1500
+ mtu "${ns_b}" veth_B-R1 1500
+
+ mtu "${ns_a}" veth_A-R2 2000
+ mtu "${ns_r2}" veth_R2-A 2000
+ mtu "${ns_r2}" veth_R2-B 1500
+ mtu "${ns_b}" veth_B-R2 1500
+
+ fail=0
+
+ # Add 100 addresses for veth endpoint on B reached by default A route
+ for i in $(seq 100 199); do
+ run_cmd ${ns_b} ip addr add "${dst_prefix1}${i}" dev veth_B-R1
+ done
+
+ # Create 100 cached route exceptions for path via R1, one via R2. Note
+ # that with IPv4 we need to actually cause a route lookup that matches
+ # the exception caused by ICMP, in order to actually have a cached
+ # route, so we need to ping each destination twice
+ for i in $(seq 100 199); do
+ run_cmd ${ns_a} ping -q -M want -i 0.1 -c 2 -s 1800 "${dst_prefix1}${i}"
+ 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
+ err " can't list cached exceptions"
+ fail=1
+ fi
+
+ run_cmd ${ns_a} ip route flush cache
+ pmtu1="$(route_get_dst_pmtu_from_exception "${ns_a}" ${dst_prefix}1)"
+ pmtu2="$(route_get_dst_pmtu_from_exception "${ns_a}" ${dst_prefix}2)"
+ if [ -n "${pmtu1}" ] || [ -n "${pmtu2}" ] || \
+ [ -n "$(${ns_a} ip route list cache)" ]; then
+ err " can't flush cached exceptions"
+ fail=1
+ fi
+
+ return ${fail}
+}
+
+test_list_flush_ipv6_exception() {
+ setup namespaces routing || return 2
+ trace "${ns_a}" veth_A-R1 "${ns_r1}" veth_R1-A \
+ "${ns_r1}" veth_R1-B "${ns_b}" veth_B-R1 \
+ "${ns_a}" veth_A-R2 "${ns_r2}" veth_R2-A \
+ "${ns_r2}" veth_R2-B "${ns_b}" veth_B-R2
+
+ dst_prefix1="${prefix6}:${b_r1}::"
+ dst2="${prefix6}:${b_r2}::1"
+
+ # Set up initial MTU values
+ mtu "${ns_a}" veth_A-R1 2000
+ mtu "${ns_r1}" veth_R1-A 2000
+ mtu "${ns_r1}" veth_R1-B 1500
+ mtu "${ns_b}" veth_B-R1 1500
+
+ mtu "${ns_a}" veth_A-R2 2000
+ mtu "${ns_r2}" veth_R2-A 2000
+ mtu "${ns_r2}" veth_R2-B 1500
+ mtu "${ns_b}" veth_B-R2 1500
+
+ fail=0
+
+ # Add 100 addresses for veth endpoint on B reached by default A route
+ for i in $(seq 100 199); do
+ run_cmd ${ns_b} ip addr add "${dst_prefix1}${i}" dev veth_B-R1
+ done
+
+ # Create 100 cached route exceptions for path via R1, one via R2
+ for i in $(seq 100 199); do
+ 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
+ err " can't list cached exceptions"
+ fail=1
+ fi
+
+ run_cmd ${ns_a} ip -6 route flush cache
+ pmtu1="$(route_get_dst_pmtu_from_exception "${ns_a}" "${dst_prefix1}100")"
+ pmtu2="$(route_get_dst_pmtu_from_exception "${ns_a}" ${dst2})"
+ if [ -n "${pmtu1}" ] || [ -n "${pmtu2}" ] || \
+ [ -n "$(${ns_a} ip -6 route list cache)" ]; then
+ err " can't flush cached exceptions"
+ fail=1
+ fi
+
+ return ${fail}
+}
+
usage() {
echo
echo "$0 [OPTIONS] [TEST]..."
@@ -1136,8 +1360,23 @@ done
trap cleanup EXIT
+# start clean
+cleanup
+
+HAVE_NH=no
+ip nexthop ls >/dev/null 2>&1
+[ $? -eq 0 ] && HAVE_NH=yes
+
+name=""
+desc=""
+rerun_nh=0
for t in ${tests}; do
- [ $desc -eq 0 ] && name="${t}" && desc=1 && continue || desc=0
+ [ "${name}" = "" ] && name="${t}" && continue
+ [ "${desc}" = "" ] && desc="${t}" && continue
+
+ if [ "${HAVE_NH}" = "yes" ]; then
+ rerun_nh="${t}"
+ fi
run_this=1
for arg do
@@ -1145,36 +1384,18 @@ for t in ${tests}; do
[ "${arg}" = "${name}" ] && run_this=1 && break
run_this=0
done
- [ $run_this -eq 0 ] && continue
-
- (
- unset IFS
+ if [ $run_this -eq 1 ]; then
+ run_test "${name}" "${desc}"
+ # if test was skipped no need to retry with nexthop objects
+ [ $? -eq 2 ] && rerun_nh=0
- if [ "$VERBOSE" = "1" ]; then
- printf "\n##########################################################################\n\n"
+ if [ "${rerun_nh}" = "1" ]; then
+ run_test_nh "${name}" "${desc}"
fi
-
- eval test_${name}
- ret=$?
- cleanup
-
- if [ $ret -eq 0 ]; then
- printf "TEST: %-60s [ OK ]\n" "${t}"
- elif [ $ret -eq 1 ]; then
- printf "TEST: %-60s [FAIL]\n" "${t}"
- if [ "${PAUSE_ON_FAIL}" = "yes" ]; then
- echo
- echo "Pausing. Hit enter to continue"
- read a
- fi
- err_flush
- exit 1
- elif [ $ret -eq 2 ]; then
- printf "TEST: %-60s [SKIP]\n" "${t}"
- err_flush
- fi
- )
- [ $? -ne 0 ] && exitcode=1
+ fi
+ name=""
+ desc=""
+ rerun_nh=0
done
exit ${exitcode}
diff --git a/tools/testing/selftests/net/psock_fanout.c b/tools/testing/selftests/net/psock_fanout.c
index bd9b9632c72b..8c8c7d79c38d 100644
--- a/tools/testing/selftests/net/psock_fanout.c
+++ b/tools/testing/selftests/net/psock_fanout.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2013 Google Inc.
* Author: Willem de Bruijn (willemb@google.com)
@@ -24,21 +25,6 @@
*
* Todo:
* - functionality: PACKET_FANOUT_FLAG_DEFRAG
- *
- * License (GPLv2):
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*/
#define _GNU_SOURCE /* for sched_setaffinity */
diff --git a/tools/testing/selftests/net/psock_lib.h b/tools/testing/selftests/net/psock_lib.h
index 7d990d6c861b..faa884385c45 100644
--- a/tools/testing/selftests/net/psock_lib.h
+++ b/tools/testing/selftests/net/psock_lib.h
@@ -1,22 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright 2013 Google Inc.
* Author: Willem de Bruijn <willemb@google.com>
* Daniel Borkmann <dborkman@redhat.com>
- *
- * License (GPLv2):
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef PSOCK_LIB_H
diff --git a/tools/testing/selftests/net/psock_tpacket.c b/tools/testing/selftests/net/psock_tpacket.c
index 7ec4fa4d55dc..404a2ce759ab 100644
--- a/tools/testing/selftests/net/psock_tpacket.c
+++ b/tools/testing/selftests/net/psock_tpacket.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2013 Red Hat, Inc.
* Author: Daniel Borkmann <dborkman@redhat.com>
@@ -19,21 +20,6 @@
* - TPACKET_V1: RX_RING, TX_RING
* - TPACKET_V2: RX_RING, TX_RING
* - TPACKET_V3: RX_RING
- *
- * License (GPLv2):
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <stdio.h>
diff --git a/tools/testing/selftests/net/route_localnet.sh b/tools/testing/selftests/net/route_localnet.sh
new file mode 100755
index 000000000000..116bfeab72fa
--- /dev/null
+++ b/tools/testing/selftests/net/route_localnet.sh
@@ -0,0 +1,74 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Run a couple of tests when route_localnet = 1.
+
+readonly PEER_NS="ns-peer-$(mktemp -u XXXXXX)"
+
+setup() {
+ ip netns add "${PEER_NS}"
+ ip -netns "${PEER_NS}" link set dev lo up
+ ip link add name veth0 type veth peer name veth1
+ ip link set dev veth0 up
+ ip link set dev veth1 netns "${PEER_NS}"
+
+ # Enable route_localnet and delete useless route 127.0.0.0/8.
+ sysctl -w net.ipv4.conf.veth0.route_localnet=1
+ ip netns exec "${PEER_NS}" sysctl -w net.ipv4.conf.veth1.route_localnet=1
+ ip route del 127.0.0.0/8 dev lo table local
+ ip netns exec "${PEER_NS}" ip route del 127.0.0.0/8 dev lo table local
+
+ ifconfig veth0 127.25.3.4/24 up
+ ip netns exec "${PEER_NS}" ifconfig veth1 127.25.3.14/24 up
+
+ ip route flush cache
+ ip netns exec "${PEER_NS}" ip route flush cache
+}
+
+cleanup() {
+ ip link del veth0
+ ip route add local 127.0.0.0/8 dev lo proto kernel scope host src 127.0.0.1
+ local -r ns="$(ip netns list|grep $PEER_NS)"
+ [ -n "$ns" ] && ip netns del $ns 2>/dev/null
+}
+
+# Run test when arp_announce = 2.
+run_arp_announce_test() {
+ echo "run arp_announce test"
+ setup
+
+ sysctl -w net.ipv4.conf.veth0.arp_announce=2
+ ip netns exec "${PEER_NS}" sysctl -w net.ipv4.conf.veth1.arp_announce=2
+ ping -c5 -I veth0 127.25.3.14
+ if [ $? -ne 0 ];then
+ echo "failed"
+ else
+ echo "ok"
+ fi
+
+ cleanup
+}
+
+# Run test when arp_ignore = 3.
+run_arp_ignore_test() {
+ echo "run arp_ignore test"
+ setup
+
+ sysctl -w net.ipv4.conf.veth0.arp_ignore=3
+ ip netns exec "${PEER_NS}" sysctl -w net.ipv4.conf.veth1.arp_ignore=3
+ ping -c5 -I veth0 127.25.3.14
+ if [ $? -ne 0 ];then
+ echo "failed"
+ else
+ echo "ok"
+ fi
+
+ cleanup
+}
+
+run_all_tests() {
+ run_arp_announce_test
+ run_arp_ignore_test
+}
+
+run_all_tests
diff --git a/tools/testing/selftests/net/rtnetlink.sh b/tools/testing/selftests/net/rtnetlink.sh
index b25c9fe019d2..bdbf4b3125b6 100755
--- a/tools/testing/selftests/net/rtnetlink.sh
+++ b/tools/testing/selftests/net/rtnetlink.sh
@@ -249,6 +249,45 @@ kci_test_route_get()
echo "PASS: route get"
}
+kci_test_addrlft()
+{
+ for i in $(seq 10 100) ;do
+ lft=$(((RANDOM%3) + 1))
+ ip addr add 10.23.11.$i/32 dev "$devdummy" preferred_lft $lft valid_lft $((lft+1))
+ check_err $?
+ done
+
+ sleep 5
+
+ ip addr show dev "$devdummy" | grep "10.23.11."
+ if [ $? -eq 0 ]; then
+ echo "FAIL: preferred_lft addresses remaining"
+ check_err 1
+ return
+ fi
+
+ echo "PASS: preferred_lft addresses have expired"
+}
+
+kci_test_promote_secondaries()
+{
+ promote=$(sysctl -n net.ipv4.conf.$devdummy.promote_secondaries)
+
+ sysctl -q net.ipv4.conf.$devdummy.promote_secondaries=1
+
+ for i in $(seq 2 254);do
+ IP="10.23.11.$i"
+ ip -f inet addr add $IP/16 brd + dev "$devdummy"
+ ifconfig "$devdummy" $IP netmask 255.255.0.0
+ done
+
+ ip addr flush dev "$devdummy"
+
+ [ $promote -eq 0 ] && sysctl -q net.ipv4.conf.$devdummy.promote_secondaries=0
+
+ echo "PASS: promote_secondaries complete"
+}
+
kci_test_addrlabel()
{
ret=0
@@ -699,13 +738,17 @@ kci_test_ipsec_offload()
sysfsd=/sys/kernel/debug/netdevsim/netdevsim0/ports/0/
sysfsf=$sysfsd/ipsec
sysfsnet=/sys/bus/netdevsim/devices/netdevsim0/net/
+ probed=false
# setup netdevsim since dummydev doesn't have offload support
- modprobe netdevsim
- check_err $?
- if [ $ret -ne 0 ]; then
- echo "FAIL: ipsec_offload can't load netdevsim"
- return 1
+ if [ ! -w /sys/bus/netdevsim/new_device ] ; then
+ modprobe -q netdevsim
+ check_err $?
+ if [ $ret -ne 0 ]; then
+ echo "SKIP: ipsec_offload can't load netdevsim"
+ return $ksft_skip
+ fi
+ probed=true
fi
echo "0" > /sys/bus/netdevsim/new_device
@@ -785,7 +828,7 @@ EOF
fi
# clean up any leftovers
- rmmod netdevsim
+ $probed && rmmod netdevsim
if [ $ret -ne 0 ]; then
echo "FAIL: ipsec_offload"
@@ -1140,6 +1183,8 @@ kci_test_rtnl()
kci_test_polrouting
kci_test_route_get
+ kci_test_addrlft
+ kci_test_promote_secondaries
kci_test_tc
kci_test_gre
kci_test_gretap
diff --git a/tools/testing/selftests/net/run_afpackettests b/tools/testing/selftests/net/run_afpackettests
index ea5938ec009a..8b42e8b04e0f 100755
--- a/tools/testing/selftests/net/run_afpackettests
+++ b/tools/testing/selftests/net/run_afpackettests
@@ -21,12 +21,16 @@ fi
echo "--------------------"
echo "running psock_tpacket test"
echo "--------------------"
-./in_netns.sh ./psock_tpacket
-if [ $? -ne 0 ]; then
- echo "[FAIL]"
- ret=1
+if [ -f /proc/kallsyms ]; then
+ ./in_netns.sh ./psock_tpacket
+ if [ $? -ne 0 ]; then
+ echo "[FAIL]"
+ ret=1
+ else
+ echo "[PASS]"
+ fi
else
- echo "[PASS]"
+ echo "[SKIP] CONFIG_KALLSYMS not enabled"
fi
echo "--------------------"
diff --git a/tools/testing/selftests/net/so_txtime.c b/tools/testing/selftests/net/so_txtime.c
new file mode 100644
index 000000000000..53f598f06647
--- /dev/null
+++ b/tools/testing/selftests/net/so_txtime.c
@@ -0,0 +1,296 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Test the SO_TXTIME API
+ *
+ * Takes two streams of { payload, delivery time }[], one input and one output.
+ * Sends the input stream and verifies arrival matches the output stream.
+ * The two streams can differ due to out-of-order delivery and drops.
+ */
+
+#define _GNU_SOURCE
+
+#include <arpa/inet.h>
+#include <error.h>
+#include <errno.h>
+#include <linux/net_tstamp.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+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 uint64_t glob_tstart;
+
+/* encode one timed transmission (of a 1B payload) */
+struct timed_send {
+ char data;
+ int64_t delay_us;
+};
+
+#define MAX_NUM_PKT 8
+static struct timed_send cfg_in[MAX_NUM_PKT];
+static struct timed_send cfg_out[MAX_NUM_PKT];
+static int cfg_num_pkt;
+
+static uint64_t gettime_ns(void)
+{
+ struct timespec ts;
+
+ if (clock_gettime(cfg_clockid, &ts))
+ error(1, errno, "gettime");
+
+ return ts.tv_sec * (1000ULL * 1000 * 1000) + ts.tv_nsec;
+}
+
+static void do_send_one(int fdt, struct timed_send *ts)
+{
+ char control[CMSG_SPACE(sizeof(uint64_t))];
+ struct msghdr msg = {0};
+ struct iovec iov = {0};
+ struct cmsghdr *cm;
+ uint64_t tdeliver;
+ int ret;
+
+ iov.iov_base = &ts->data;
+ iov.iov_len = 1;
+
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ if (ts->delay_us >= 0) {
+ memset(control, 0, sizeof(control));
+ msg.msg_control = &control;
+ msg.msg_controllen = sizeof(control);
+
+ tdeliver = glob_tstart + ts->delay_us * 1000;
+
+ cm = CMSG_FIRSTHDR(&msg);
+ cm->cmsg_level = SOL_SOCKET;
+ cm->cmsg_type = SCM_TXTIME;
+ cm->cmsg_len = CMSG_LEN(sizeof(tdeliver));
+ memcpy(CMSG_DATA(cm), &tdeliver, sizeof(tdeliver));
+ }
+
+ ret = sendmsg(fdt, &msg, 0);
+ if (ret == -1)
+ error(1, errno, "write");
+ if (ret == 0)
+ error(1, 0, "write: 0B");
+
+}
+
+static void 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)
+ error(1, errno, "read");
+ if (ret != 1)
+ error(1, 0, "read: %dB", ret);
+
+ 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);
+
+ 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);
+}
+
+static void do_recv_verify_empty(int fdr)
+{
+ char rbuf[1];
+ int ret;
+
+ ret = recv(fdr, rbuf, sizeof(rbuf), 0);
+ if (ret != -1 || errno != EAGAIN)
+ error(1, 0, "recv: not empty as expected (%d, %d)", ret, errno);
+}
+
+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);
+
+ if (setsockopt(fd, SOL_SOCKET, SO_TXTIME,
+ &so_txtime_val, sizeof(so_txtime_val)))
+ error(1, errno, "setsockopt txtime");
+
+ if (getsockopt(fd, SOL_SOCKET, SO_TXTIME,
+ &so_txtime_val_read, &vallen))
+ error(1, errno, "getsockopt txtime");
+
+ if (vallen != sizeof(so_txtime_val) ||
+ memcmp(&so_txtime_val, &so_txtime_val_read, vallen))
+ error(1, 0, "getsockopt txtime: mismatch");
+}
+
+static int setup_tx(struct sockaddr *addr, socklen_t alen)
+{
+ int fd;
+
+ fd = socket(addr->sa_family, SOCK_DGRAM, 0);
+ if (fd == -1)
+ error(1, errno, "socket t");
+
+ if (connect(fd, addr, alen))
+ error(1, errno, "connect");
+
+ setsockopt_txtime(fd);
+
+ return fd;
+}
+
+static int setup_rx(struct sockaddr *addr, socklen_t alen)
+{
+ struct timeval tv = { .tv_usec = 100 * 1000 };
+ int fd;
+
+ fd = socket(addr->sa_family, SOCK_DGRAM, 0);
+ if (fd == -1)
+ error(1, errno, "socket r");
+
+ if (bind(fd, addr, alen))
+ error(1, errno, "bind");
+
+ if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)))
+ error(1, errno, "setsockopt rcv timeout");
+
+ return fd;
+}
+
+static void do_test(struct sockaddr *addr, socklen_t alen)
+{
+ int fdt, fdr, i;
+
+ fprintf(stderr, "\nSO_TXTIME ipv%c clock %s\n",
+ addr->sa_family == PF_INET ? '4' : '6',
+ cfg_clockid == CLOCK_TAI ? "tai" : "monotonic");
+
+ fdt = setup_tx(addr, alen);
+ fdr = setup_rx(addr, alen);
+
+ glob_tstart = gettime_ns();
+
+ 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]);
+
+ do_recv_verify_empty(fdr);
+
+ if (close(fdr))
+ error(1, errno, "close r");
+ if (close(fdt))
+ error(1, errno, "close t");
+}
+
+static int parse_io(const char *optarg, struct timed_send *array)
+{
+ char *arg, *tok;
+ int aoff = 0;
+
+ arg = strdup(optarg);
+ if (!arg)
+ error(1, errno, "strdup");
+
+ while ((tok = strtok(arg, ","))) {
+ arg = NULL; /* only pass non-zero on first call */
+
+ if (aoff / 2 == MAX_NUM_PKT)
+ error(1, 0, "exceeds max pkt count (%d)", MAX_NUM_PKT);
+
+ if (aoff & 1) { /* parse delay */
+ array->delay_us = strtol(tok, NULL, 0) * 1000;
+ array++;
+ } else { /* parse character */
+ array->data = tok[0];
+ }
+
+ aoff++;
+ }
+
+ free(arg);
+
+ return aoff / 2;
+}
+
+static void parse_opts(int argc, char **argv)
+{
+ int c, ilen, olen;
+
+ while ((c = getopt(argc, argv, "46c:")) != -1) {
+ switch (c) {
+ case '4':
+ cfg_do_ipv4 = true;
+ break;
+ case '6':
+ cfg_do_ipv6 = true;
+ break;
+ case 'c':
+ if (!strcmp(optarg, "tai"))
+ cfg_clockid = CLOCK_TAI;
+ else if (!strcmp(optarg, "monotonic") ||
+ !strcmp(optarg, "mono"))
+ cfg_clockid = CLOCK_MONOTONIC;
+ else
+ error(1, 0, "unknown clock id %s", optarg);
+ break;
+ default:
+ error(1, 0, "parse error at %d", optind);
+ }
+ }
+
+ if (argc - optind != 2)
+ error(1, 0, "Usage: %s [-46] -c <clock> <in> <out>", argv[0]);
+
+ ilen = parse_io(argv[optind], cfg_in);
+ olen = parse_io(argv[optind + 1], cfg_out);
+ if (ilen != olen)
+ error(1, 0, "i/o streams len mismatch (%d, %d)\n", ilen, olen);
+ cfg_num_pkt = ilen;
+}
+
+int main(int argc, char **argv)
+{
+ parse_opts(argc, argv);
+
+ if (cfg_do_ipv6) {
+ struct sockaddr_in6 addr6 = {0};
+
+ addr6.sin6_family = AF_INET6;
+ addr6.sin6_port = htons(cfg_port);
+ addr6.sin6_addr = in6addr_loopback;
+ do_test((void *)&addr6, sizeof(addr6));
+ }
+
+ if (cfg_do_ipv4) {
+ struct sockaddr_in addr4 = {0};
+
+ addr4.sin_family = AF_INET;
+ addr4.sin_port = htons(cfg_port);
+ addr4.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ do_test((void *)&addr4, sizeof(addr4));
+ }
+
+ return 0;
+}
diff --git a/tools/testing/selftests/net/so_txtime.sh b/tools/testing/selftests/net/so_txtime.sh
new file mode 100755
index 000000000000..5aa519328a5b
--- /dev/null
+++ b/tools/testing/selftests/net/so_txtime.sh
@@ -0,0 +1,31 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Regression tests for the SO_TXTIME interface
+
+# Run in network namespace
+if [[ $# -eq 0 ]]; then
+ ./in_netns.sh $0 __subprocess
+ exit $?
+fi
+
+set -e
+
+tc qdisc add dev lo root fq
+./so_txtime -4 -6 -c mono a,-1 a,-1
+./so_txtime -4 -6 -c mono a,0 a,0
+./so_txtime -4 -6 -c mono a,10 a,10
+./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
+ ! ./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
+ ./so_txtime -4 -6 -c tai a,10,b,20 a,10,b,20
+ ./so_txtime -4 -6 -c tai a,20,b,10 b,10,a,20
+else
+ echo "tc ($(tc -V)) does not support qdisc etf. skipping"
+fi
+
+echo OK. All tests passed
diff --git a/tools/testing/selftests/net/tcp_fastopen_backup_key.c b/tools/testing/selftests/net/tcp_fastopen_backup_key.c
new file mode 100644
index 000000000000..9c55ec44fc43
--- /dev/null
+++ b/tools/testing/selftests/net/tcp_fastopen_backup_key.c
@@ -0,0 +1,335 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Test key rotation for TFO.
+ * New keys are 'rotated' in two steps:
+ * 1) Add new key as the 'backup' key 'behind' the primary key
+ * 2) Make new key the primary by swapping the backup and primary keys
+ *
+ * The rotation is done in stages using multiple sockets bound
+ * to the same port via SO_REUSEPORT. This simulates key rotation
+ * behind say a load balancer. We verify that across the rotation
+ * there are no cases in which a cookie is not accepted by verifying
+ * that TcpExtTCPFastOpenPassiveFail remains 0.
+ */
+#define _GNU_SOURCE
+#include <arpa/inet.h>
+#include <errno.h>
+#include <error.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/epoll.h>
+#include <unistd.h>
+#include <netinet/tcp.h>
+#include <fcntl.h>
+#include <time.h>
+
+#ifndef TCP_FASTOPEN_KEY
+#define TCP_FASTOPEN_KEY 33
+#endif
+
+#define N_LISTEN 10
+#define PROC_FASTOPEN_KEY "/proc/sys/net/ipv4/tcp_fastopen_key"
+#define KEY_LENGTH 16
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+#endif
+
+static bool do_ipv6;
+static bool do_sockopt;
+static bool do_rotate;
+static int key_len = KEY_LENGTH;
+static int rcv_fds[N_LISTEN];
+static int proc_fd;
+static const char *IP4_ADDR = "127.0.0.1";
+static const char *IP6_ADDR = "::1";
+static const int PORT = 8891;
+
+static void get_keys(int fd, uint32_t *keys)
+{
+ char buf[128];
+ socklen_t len = KEY_LENGTH * 2;
+
+ if (do_sockopt) {
+ if (getsockopt(fd, SOL_TCP, TCP_FASTOPEN_KEY, keys, &len))
+ error(1, errno, "Unable to get key");
+ return;
+ }
+ lseek(proc_fd, 0, SEEK_SET);
+ if (read(proc_fd, buf, sizeof(buf)) <= 0)
+ error(1, errno, "Unable to read %s", PROC_FASTOPEN_KEY);
+ if (sscanf(buf, "%x-%x-%x-%x,%x-%x-%x-%x", keys, keys + 1, keys + 2,
+ keys + 3, keys + 4, keys + 5, keys + 6, keys + 7) != 8)
+ error(1, 0, "Unable to parse %s", PROC_FASTOPEN_KEY);
+}
+
+static void set_keys(int fd, uint32_t *keys)
+{
+ char buf[128];
+
+ if (do_sockopt) {
+ if (setsockopt(fd, SOL_TCP, TCP_FASTOPEN_KEY, keys,
+ key_len))
+ error(1, errno, "Unable to set key");
+ return;
+ }
+ if (do_rotate)
+ snprintf(buf, 128, "%08x-%08x-%08x-%08x,%08x-%08x-%08x-%08x",
+ keys[0], keys[1], keys[2], keys[3], keys[4], keys[5],
+ keys[6], keys[7]);
+ else
+ snprintf(buf, 128, "%08x-%08x-%08x-%08x",
+ keys[0], keys[1], keys[2], keys[3]);
+ lseek(proc_fd, 0, SEEK_SET);
+ if (write(proc_fd, buf, sizeof(buf)) <= 0)
+ error(1, errno, "Unable to write %s", PROC_FASTOPEN_KEY);
+}
+
+static void build_rcv_fd(int family, int proto, int *rcv_fds)
+{
+ struct sockaddr_in addr4 = {0};
+ struct sockaddr_in6 addr6 = {0};
+ struct sockaddr *addr;
+ int opt = 1, i, sz;
+ int qlen = 100;
+ uint32_t keys[8];
+
+ switch (family) {
+ case AF_INET:
+ addr4.sin_family = family;
+ addr4.sin_addr.s_addr = htonl(INADDR_ANY);
+ addr4.sin_port = htons(PORT);
+ sz = sizeof(addr4);
+ addr = (struct sockaddr *)&addr4;
+ break;
+ case AF_INET6:
+ addr6.sin6_family = AF_INET6;
+ addr6.sin6_addr = in6addr_any;
+ addr6.sin6_port = htons(PORT);
+ sz = sizeof(addr6);
+ addr = (struct sockaddr *)&addr6;
+ break;
+ default:
+ error(1, 0, "Unsupported family %d", family);
+ /* clang does not recognize error() above as terminating
+ * the program, so it complains that saddr, sz are
+ * not initialized when this code path is taken. Silence it.
+ */
+ return;
+ }
+ for (i = 0; i < ARRAY_SIZE(keys); i++)
+ keys[i] = rand();
+ for (i = 0; i < N_LISTEN; i++) {
+ rcv_fds[i] = socket(family, proto, 0);
+ if (rcv_fds[i] < 0)
+ error(1, errno, "failed to create receive socket");
+ if (setsockopt(rcv_fds[i], SOL_SOCKET, SO_REUSEPORT, &opt,
+ sizeof(opt)))
+ error(1, errno, "failed to set SO_REUSEPORT");
+ if (bind(rcv_fds[i], addr, sz))
+ error(1, errno, "failed to bind receive socket");
+ if (setsockopt(rcv_fds[i], SOL_TCP, TCP_FASTOPEN, &qlen,
+ sizeof(qlen)))
+ error(1, errno, "failed to set TCP_FASTOPEN");
+ set_keys(rcv_fds[i], keys);
+ if (proto == SOCK_STREAM && listen(rcv_fds[i], 10))
+ error(1, errno, "failed to listen on receive port");
+ }
+}
+
+static int connect_and_send(int family, int proto)
+{
+ struct sockaddr_in saddr4 = {0};
+ struct sockaddr_in daddr4 = {0};
+ struct sockaddr_in6 saddr6 = {0};
+ struct sockaddr_in6 daddr6 = {0};
+ struct sockaddr *saddr, *daddr;
+ int fd, sz, ret;
+ char data[1];
+
+ switch (family) {
+ case AF_INET:
+ saddr4.sin_family = AF_INET;
+ saddr4.sin_addr.s_addr = htonl(INADDR_ANY);
+ saddr4.sin_port = 0;
+
+ daddr4.sin_family = AF_INET;
+ if (!inet_pton(family, IP4_ADDR, &daddr4.sin_addr.s_addr))
+ error(1, errno, "inet_pton failed: %s", IP4_ADDR);
+ daddr4.sin_port = htons(PORT);
+
+ sz = sizeof(saddr4);
+ saddr = (struct sockaddr *)&saddr4;
+ daddr = (struct sockaddr *)&daddr4;
+ break;
+ case AF_INET6:
+ saddr6.sin6_family = AF_INET6;
+ saddr6.sin6_addr = in6addr_any;
+
+ daddr6.sin6_family = AF_INET6;
+ if (!inet_pton(family, IP6_ADDR, &daddr6.sin6_addr))
+ error(1, errno, "inet_pton failed: %s", IP6_ADDR);
+ daddr6.sin6_port = htons(PORT);
+
+ sz = sizeof(saddr6);
+ saddr = (struct sockaddr *)&saddr6;
+ daddr = (struct sockaddr *)&daddr6;
+ break;
+ default:
+ error(1, 0, "Unsupported family %d", family);
+ /* clang does not recognize error() above as terminating
+ * the program, so it complains that saddr, daddr, sz are
+ * not initialized when this code path is taken. Silence it.
+ */
+ return -1;
+ }
+ fd = socket(family, proto, 0);
+ if (fd < 0)
+ error(1, errno, "failed to create send socket");
+ if (bind(fd, saddr, sz))
+ error(1, errno, "failed to bind send socket");
+ data[0] = 'a';
+ ret = sendto(fd, data, 1, MSG_FASTOPEN, daddr, sz);
+ if (ret != 1)
+ error(1, errno, "failed to sendto");
+
+ return fd;
+}
+
+static bool is_listen_fd(int fd)
+{
+ int i;
+
+ for (i = 0; i < N_LISTEN; i++) {
+ if (rcv_fds[i] == fd)
+ return true;
+ }
+ return false;
+}
+
+static void rotate_key(int fd)
+{
+ static int iter;
+ static uint32_t new_key[4];
+ uint32_t keys[8];
+ uint32_t tmp_key[4];
+ int i;
+
+ if (iter < N_LISTEN) {
+ /* first set new key as backups */
+ if (iter == 0) {
+ for (i = 0; i < ARRAY_SIZE(new_key); i++)
+ new_key[i] = rand();
+ }
+ get_keys(fd, keys);
+ memcpy(keys + 4, new_key, KEY_LENGTH);
+ set_keys(fd, keys);
+ } else {
+ /* swap the keys */
+ get_keys(fd, keys);
+ memcpy(tmp_key, keys + 4, KEY_LENGTH);
+ memcpy(keys + 4, keys, KEY_LENGTH);
+ memcpy(keys, tmp_key, KEY_LENGTH);
+ set_keys(fd, keys);
+ }
+ if (++iter >= (N_LISTEN * 2))
+ iter = 0;
+}
+
+static void run_one_test(int family)
+{
+ struct epoll_event ev;
+ int i, send_fd;
+ int n_loops = 10000;
+ int rotate_key_fd = 0;
+ int key_rotate_interval = 50;
+ int fd, epfd;
+ char buf[1];
+
+ build_rcv_fd(family, SOCK_STREAM, rcv_fds);
+ epfd = epoll_create(1);
+ if (epfd < 0)
+ error(1, errno, "failed to create epoll");
+ ev.events = EPOLLIN;
+ for (i = 0; i < N_LISTEN; i++) {
+ ev.data.fd = rcv_fds[i];
+ if (epoll_ctl(epfd, EPOLL_CTL_ADD, rcv_fds[i], &ev))
+ error(1, errno, "failed to register sock epoll");
+ }
+ while (n_loops--) {
+ send_fd = connect_and_send(family, SOCK_STREAM);
+ if (do_rotate && ((n_loops % key_rotate_interval) == 0)) {
+ rotate_key(rcv_fds[rotate_key_fd]);
+ if (++rotate_key_fd >= N_LISTEN)
+ rotate_key_fd = 0;
+ }
+ while (1) {
+ i = epoll_wait(epfd, &ev, 1, -1);
+ if (i < 0)
+ error(1, errno, "epoll_wait failed");
+ if (is_listen_fd(ev.data.fd)) {
+ fd = accept(ev.data.fd, NULL, NULL);
+ if (fd < 0)
+ error(1, errno, "failed to accept");
+ ev.data.fd = fd;
+ if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev))
+ error(1, errno, "failed epoll add");
+ continue;
+ }
+ i = recv(ev.data.fd, buf, sizeof(buf), 0);
+ if (i != 1)
+ error(1, errno, "failed recv data");
+ if (epoll_ctl(epfd, EPOLL_CTL_DEL, ev.data.fd, NULL))
+ error(1, errno, "failed epoll del");
+ close(ev.data.fd);
+ break;
+ }
+ close(send_fd);
+ }
+ for (i = 0; i < N_LISTEN; i++)
+ close(rcv_fds[i]);
+}
+
+static void parse_opts(int argc, char **argv)
+{
+ int c;
+
+ while ((c = getopt(argc, argv, "46sr")) != -1) {
+ switch (c) {
+ case '4':
+ do_ipv6 = false;
+ break;
+ case '6':
+ do_ipv6 = true;
+ break;
+ case 's':
+ do_sockopt = true;
+ break;
+ case 'r':
+ do_rotate = true;
+ key_len = KEY_LENGTH * 2;
+ break;
+ default:
+ error(1, 0, "%s: parse error", argv[0]);
+ }
+ }
+}
+
+int main(int argc, char **argv)
+{
+ parse_opts(argc, argv);
+ proc_fd = open(PROC_FASTOPEN_KEY, O_RDWR);
+ if (proc_fd < 0)
+ error(1, errno, "Unable to open %s", PROC_FASTOPEN_KEY);
+ srand(time(NULL));
+ if (do_ipv6)
+ run_one_test(AF_INET6);
+ else
+ run_one_test(AF_INET);
+ close(proc_fd);
+ fprintf(stderr, "PASS\n");
+ return 0;
+}
diff --git a/tools/testing/selftests/net/tcp_fastopen_backup_key.sh b/tools/testing/selftests/net/tcp_fastopen_backup_key.sh
new file mode 100755
index 000000000000..41476399e184
--- /dev/null
+++ b/tools/testing/selftests/net/tcp_fastopen_backup_key.sh
@@ -0,0 +1,55 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# rotate TFO keys for ipv4/ipv6 and verify that the client does
+# not present an invalid cookie.
+
+set +x
+set -e
+
+readonly NETNS="ns-$(mktemp -u XXXXXX)"
+
+setup() {
+ ip netns add "${NETNS}"
+ ip -netns "${NETNS}" link set lo up
+ ip netns exec "${NETNS}" sysctl -w net.ipv4.tcp_fastopen=3 \
+ >/dev/null 2>&1
+}
+
+cleanup() {
+ ip netns del "${NETNS}"
+}
+
+trap cleanup EXIT
+setup
+
+do_test() {
+ # flush routes before each run, otherwise successive runs can
+ # initially present an old TFO cookie
+ ip netns exec "${NETNS}" ip tcp_metrics flush
+ ip netns exec "${NETNS}" ./tcp_fastopen_backup_key "$1"
+ val=$(ip netns exec "${NETNS}" nstat -az | \
+ grep TcpExtTCPFastOpenPassiveFail | awk '{print $2}')
+ if [ $val -ne 0 ]; then
+ echo "FAIL: TcpExtTCPFastOpenPassiveFail non-zero"
+ return 1
+ fi
+}
+
+do_test "-4"
+do_test "-6"
+do_test "-4"
+do_test "-6"
+do_test "-4s"
+do_test "-6s"
+do_test "-4s"
+do_test "-6s"
+do_test "-4r"
+do_test "-6r"
+do_test "-4r"
+do_test "-6r"
+do_test "-4sr"
+do_test "-6sr"
+do_test "-4sr"
+do_test "-6sr"
+echo "all tests done"
diff --git a/tools/testing/selftests/net/tcp_inq.c b/tools/testing/selftests/net/tcp_inq.c
index d044b29ddabc..bd6a9c7a3e8a 100644
--- a/tools/testing/selftests/net/tcp_inq.c
+++ b/tools/testing/selftests/net/tcp_inq.c
@@ -1,19 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2018 Google Inc.
* Author: Soheil Hassas Yeganeh (soheil@google.com)
*
* Simple example on how to use TCP_INQ and TCP_CM_INQ.
- *
- * License (GPLv2):
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for
- * more details.
*/
#define _GNU_SOURCE
diff --git a/tools/testing/selftests/net/tcp_mmap.c b/tools/testing/selftests/net/tcp_mmap.c
index e8c5dff448eb..31ced79f4f25 100644
--- a/tools/testing/selftests/net/tcp_mmap.c
+++ b/tools/testing/selftests/net/tcp_mmap.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2018 Google Inc.
* Author: Eric Dumazet (edumazet@google.com)
@@ -44,21 +45,6 @@
* cpu usage user:0.046 sys:3.559, 110.016 usec per MB, 65529 c-switches
* received 32768 MB (99.9939 % mmap'ed) in 7.43764 s, 36.9577 Gbit
* cpu usage user:0.035 sys:3.467, 106.873 usec per MB, 65530 c-switches
- *
- * License (GPLv2):
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*/
#define _GNU_SOURCE
#include <pthread.h>
diff --git a/tools/testing/selftests/net/test_blackhole_dev.sh b/tools/testing/selftests/net/test_blackhole_dev.sh
new file mode 100755
index 000000000000..3119b80e711f
--- /dev/null
+++ b/tools/testing/selftests/net/test_blackhole_dev.sh
@@ -0,0 +1,11 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0
+# Runs blackhole-dev test using blackhole-dev kernel module
+
+if /sbin/modprobe -q test_blackhole_dev ; then
+ /sbin/modprobe -q -r test_blackhole_dev;
+ echo "test_blackhole_dev: ok";
+else
+ echo "test_blackhole_dev: [FAIL]";
+ exit 1;
+fi
diff --git a/tools/testing/selftests/net/tls.c b/tools/testing/selftests/net/tls.c
index 47ddfc154036..090fff9dbc48 100644
--- a/tools/testing/selftests/net/tls.c
+++ b/tools/testing/selftests/net/tls.c
@@ -442,6 +442,21 @@ TEST_F(tls, multiple_send_single_recv)
EXPECT_EQ(memcmp(send_mem, recv_mem + send_len, send_len), 0);
}
+TEST_F(tls, single_send_multiple_recv_non_align)
+{
+ const unsigned int total_len = 15;
+ const unsigned int recv_len = 10;
+ char recv_mem[recv_len * 2];
+ char send_mem[total_len];
+
+ EXPECT_GE(send(self->fd, send_mem, total_len, 0), 0);
+ memset(recv_mem, 0, total_len);
+
+ EXPECT_EQ(recv(self->cfd, recv_mem, recv_len, 0), recv_len);
+ EXPECT_EQ(recv(self->cfd, recv_mem + recv_len, recv_len, 0), 5);
+ EXPECT_EQ(memcmp(send_mem, recv_mem, total_len), 0);
+}
+
TEST_F(tls, recv_partial)
{
char const *test_str = "test_read_partial";
@@ -575,6 +590,25 @@ TEST_F(tls, recv_peek_large_buf_mult_recs)
EXPECT_EQ(memcmp(test_str, buf, len), 0);
}
+TEST_F(tls, recv_lowat)
+{
+ char send_mem[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+ char recv_mem[20];
+ int lowat = 8;
+
+ EXPECT_EQ(send(self->fd, send_mem, 10, 0), 10);
+ EXPECT_EQ(send(self->fd, send_mem, 5, 0), 5);
+
+ memset(recv_mem, 0, 20);
+ EXPECT_EQ(setsockopt(self->cfd, SOL_SOCKET, SO_RCVLOWAT,
+ &lowat, sizeof(lowat)), 0);
+ EXPECT_EQ(recv(self->cfd, recv_mem, 1, MSG_WAITALL), 1);
+ EXPECT_EQ(recv(self->cfd, recv_mem + 1, 6, MSG_WAITALL), 6);
+ EXPECT_EQ(recv(self->cfd, recv_mem + 7, 10, 0), 8);
+
+ EXPECT_EQ(memcmp(send_mem, recv_mem, 10), 0);
+ EXPECT_EQ(memcmp(send_mem, recv_mem + 10, 5), 0);
+}
TEST_F(tls, pollin)
{
@@ -610,6 +644,32 @@ TEST_F(tls, poll_wait)
EXPECT_EQ(recv(self->cfd, recv_mem, send_len, MSG_WAITALL), send_len);
}
+TEST_F(tls, poll_wait_split)
+{
+ struct pollfd fd = { 0, 0, 0 };
+ char send_mem[20] = {};
+ char recv_mem[15];
+
+ fd.fd = self->cfd;
+ fd.events = POLLIN;
+ /* Send 20 bytes */
+ EXPECT_EQ(send(self->fd, send_mem, sizeof(send_mem), 0),
+ sizeof(send_mem));
+ /* Poll with inf. timeout */
+ EXPECT_EQ(poll(&fd, 1, -1), 1);
+ EXPECT_EQ(fd.revents & POLLIN, 1);
+ EXPECT_EQ(recv(self->cfd, recv_mem, sizeof(recv_mem), MSG_WAITALL),
+ sizeof(recv_mem));
+
+ /* Now the remaining 5 bytes of record data are in TLS ULP */
+ fd.fd = self->cfd;
+ fd.events = POLLIN;
+ EXPECT_EQ(poll(&fd, 1, -1), 1);
+ EXPECT_EQ(fd.revents & POLLIN, 1);
+ EXPECT_EQ(recv(self->cfd, recv_mem, sizeof(recv_mem), 0),
+ sizeof(send_mem) - sizeof(recv_mem));
+}
+
TEST_F(tls, blocking)
{
size_t data = 100000;
diff --git a/tools/testing/selftests/net/txring_overwrite.c b/tools/testing/selftests/net/txring_overwrite.c
index fd8b1c663c39..7d9ea039450a 100644
--- a/tools/testing/selftests/net/txring_overwrite.c
+++ b/tools/testing/selftests/net/txring_overwrite.c
@@ -113,7 +113,7 @@ static int setup_tx(char **ring)
*ring = mmap(0, req.tp_block_size * req.tp_block_nr,
PROT_READ | PROT_WRITE, MAP_SHARED, fdt, 0);
- if (!*ring)
+ if (*ring == MAP_FAILED)
error(1, errno, "mmap");
return fdt;
diff --git a/tools/testing/selftests/net/udpgso_bench.sh b/tools/testing/selftests/net/udpgso_bench.sh
index 5670a9ffd8eb..80b5d352702e 100755
--- a/tools/testing/selftests/net/udpgso_bench.sh
+++ b/tools/testing/selftests/net/udpgso_bench.sh
@@ -3,6 +3,48 @@
#
# Run a series of udpgso benchmarks
+readonly GREEN='\033[0;92m'
+readonly YELLOW='\033[0;33m'
+readonly RED='\033[0;31m'
+readonly NC='\033[0m' # No Color
+
+readonly KSFT_PASS=0
+readonly KSFT_FAIL=1
+readonly KSFT_SKIP=4
+
+num_pass=0
+num_err=0
+num_skip=0
+
+kselftest_test_exitcode() {
+ local -r exitcode=$1
+
+ if [[ ${exitcode} -eq ${KSFT_PASS} ]]; then
+ num_pass=$(( $num_pass + 1 ))
+ elif [[ ${exitcode} -eq ${KSFT_SKIP} ]]; then
+ num_skip=$(( $num_skip + 1 ))
+ else
+ num_err=$(( $num_err + 1 ))
+ fi
+}
+
+kselftest_exit() {
+ echo -e "$(basename $0): PASS=${num_pass} SKIP=${num_skip} FAIL=${num_err}"
+
+ if [[ $num_err -ne 0 ]]; then
+ echo -e "$(basename $0): ${RED}FAIL${NC}"
+ exit ${KSFT_FAIL}
+ fi
+
+ if [[ $num_skip -ne 0 ]]; then
+ echo -e "$(basename $0): ${YELLOW}SKIP${NC}"
+ exit ${KSFT_SKIP}
+ fi
+
+ echo -e "$(basename $0): ${GREEN}PASS${NC}"
+ exit ${KSFT_PASS}
+}
+
wake_children() {
local -r jobs="$(jobs -p)"
@@ -25,6 +67,7 @@ run_in_netns() {
local -r args=$@
./in_netns.sh $0 __subprocess ${args}
+ kselftest_test_exitcode $?
}
run_udp() {
@@ -38,6 +81,18 @@ run_udp() {
echo "udp gso zerocopy"
run_in_netns ${args} -S 0 -z
+
+ echo "udp gso timestamp"
+ run_in_netns ${args} -S 0 -T
+
+ echo "udp gso zerocopy audit"
+ run_in_netns ${args} -S 0 -z -a
+
+ echo "udp gso timestamp audit"
+ run_in_netns ${args} -S 0 -T -a
+
+ echo "udp gso zerocopy timestamp audit"
+ run_in_netns ${args} -S 0 -T -z -a
}
run_tcp() {
@@ -48,10 +103,15 @@ run_tcp() {
echo "tcp zerocopy"
run_in_netns ${args} -t -z
+
+ # excluding for now because test fails intermittently
+ # add -P option to include poll() to reduce possibility of lost messages
+ #echo "tcp zerocopy audit"
+ #run_in_netns ${args} -t -z -P -a
}
run_all() {
- local -r core_args="-l 4"
+ local -r core_args="-l 3"
local -r ipv4_args="${core_args} -4 -D 127.0.0.1"
local -r ipv6_args="${core_args} -6 -D ::1"
@@ -66,6 +126,7 @@ run_all() {
if [[ $# -eq 0 ]]; then
run_all
+ kselftest_exit
elif [[ $1 == "__subprocess" ]]; then
shift
run_one $@
diff --git a/tools/testing/selftests/net/udpgso_bench_tx.c b/tools/testing/selftests/net/udpgso_bench_tx.c
index 4074538b5df5..ada99496634a 100644
--- a/tools/testing/selftests/net/udpgso_bench_tx.c
+++ b/tools/testing/selftests/net/udpgso_bench_tx.c
@@ -5,6 +5,8 @@
#include <arpa/inet.h>
#include <errno.h>
#include <error.h>
+#include <linux/errqueue.h>
+#include <linux/net_tstamp.h>
#include <netinet/if_ether.h>
#include <netinet/in.h>
#include <netinet/ip.h>
@@ -19,9 +21,12 @@
#include <string.h>
#include <sys/socket.h>
#include <sys/time.h>
+#include <sys/poll.h>
#include <sys/types.h>
#include <unistd.h>
+#include "../kselftest.h"
+
#ifndef ETH_MAX_MTU
#define ETH_MAX_MTU 0xFFFFU
#endif
@@ -34,10 +39,18 @@
#define SO_ZEROCOPY 60
#endif
+#ifndef SO_EE_ORIGIN_ZEROCOPY
+#define SO_EE_ORIGIN_ZEROCOPY 5
+#endif
+
#ifndef MSG_ZEROCOPY
#define MSG_ZEROCOPY 0x4000000
#endif
+#ifndef ENOTSUPP
+#define ENOTSUPP 524
+#endif
+
#define NUM_PKT 100
static bool cfg_cache_trash;
@@ -48,12 +61,24 @@ static uint16_t cfg_mss;
static int cfg_payload_len = (1472 * 42);
static int cfg_port = 8000;
static int cfg_runtime_ms = -1;
+static bool cfg_poll;
static bool cfg_segment;
static bool cfg_sendmmsg;
static bool cfg_tcp;
+static uint32_t cfg_tx_ts = SOF_TIMESTAMPING_TX_SOFTWARE;
+static bool cfg_tx_tstamp;
+static bool cfg_audit;
+static bool cfg_verbose;
static bool cfg_zerocopy;
static int cfg_msg_nr;
static uint16_t cfg_gso_size;
+static unsigned long total_num_msgs;
+static unsigned long total_num_sends;
+static unsigned long stat_tx_ts;
+static unsigned long stat_tx_ts_errors;
+static unsigned long tstart;
+static unsigned long tend;
+static unsigned long stat_zcopies;
static socklen_t cfg_alen;
static struct sockaddr_storage cfg_dst_addr;
@@ -110,23 +135,125 @@ static void setup_sockaddr(int domain, const char *str_addr, void *sockaddr)
}
}
-static void flush_zerocopy(int fd)
+static void flush_cmsg(struct cmsghdr *cmsg)
+{
+ struct sock_extended_err *err;
+ struct scm_timestamping *tss;
+ __u32 lo;
+ __u32 hi;
+ int i;
+
+ switch (cmsg->cmsg_level) {
+ case SOL_SOCKET:
+ if (cmsg->cmsg_type == SO_TIMESTAMPING) {
+ i = (cfg_tx_ts == SOF_TIMESTAMPING_TX_HARDWARE) ? 2 : 0;
+ tss = (struct scm_timestamping *)CMSG_DATA(cmsg);
+ if (tss->ts[i].tv_sec == 0)
+ stat_tx_ts_errors++;
+ } else {
+ error(1, 0, "unknown SOL_SOCKET cmsg type=%u\n",
+ cmsg->cmsg_type);
+ }
+ break;
+ case SOL_IP:
+ case SOL_IPV6:
+ switch (cmsg->cmsg_type) {
+ case IP_RECVERR:
+ case IPV6_RECVERR:
+ {
+ err = (struct sock_extended_err *)CMSG_DATA(cmsg);
+ switch (err->ee_origin) {
+ case SO_EE_ORIGIN_TIMESTAMPING:
+ /* Got a TX timestamp from error queue */
+ stat_tx_ts++;
+ break;
+ case SO_EE_ORIGIN_ICMP:
+ case SO_EE_ORIGIN_ICMP6:
+ if (cfg_verbose)
+ fprintf(stderr,
+ "received ICMP error: type=%u, code=%u\n",
+ err->ee_type, err->ee_code);
+ break;
+ case SO_EE_ORIGIN_ZEROCOPY:
+ {
+ lo = err->ee_info;
+ hi = err->ee_data;
+ /* range of IDs acknowledged */
+ stat_zcopies += hi - lo + 1;
+ break;
+ }
+ case SO_EE_ORIGIN_LOCAL:
+ if (cfg_verbose)
+ fprintf(stderr,
+ "received packet with local origin: %u\n",
+ err->ee_origin);
+ break;
+ default:
+ error(0, 1, "received packet with origin: %u",
+ err->ee_origin);
+ }
+ break;
+ }
+ default:
+ error(0, 1, "unknown IP msg type=%u\n",
+ cmsg->cmsg_type);
+ break;
+ }
+ break;
+ default:
+ error(0, 1, "unknown cmsg level=%u\n",
+ cmsg->cmsg_level);
+ }
+}
+
+static void flush_errqueue_recv(int fd)
{
- struct msghdr msg = {0}; /* flush */
+ char control[CMSG_SPACE(sizeof(struct scm_timestamping)) +
+ CMSG_SPACE(sizeof(struct sock_extended_err)) +
+ CMSG_SPACE(sizeof(struct sockaddr_in6))] = {0};
+ struct msghdr msg = {0};
+ struct cmsghdr *cmsg;
int ret;
while (1) {
+ msg.msg_control = control;
+ msg.msg_controllen = sizeof(control);
ret = recvmsg(fd, &msg, MSG_ERRQUEUE);
if (ret == -1 && errno == EAGAIN)
break;
if (ret == -1)
error(1, errno, "errqueue");
- if (msg.msg_flags != (MSG_ERRQUEUE | MSG_CTRUNC))
+ if (msg.msg_flags != MSG_ERRQUEUE)
error(1, 0, "errqueue: flags 0x%x\n", msg.msg_flags);
+ if (cfg_audit) {
+ for (cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg;
+ cmsg = CMSG_NXTHDR(&msg, cmsg))
+ flush_cmsg(cmsg);
+ }
msg.msg_flags = 0;
}
}
+static void flush_errqueue(int fd, const bool do_poll)
+{
+ if (do_poll) {
+ struct pollfd fds = {0};
+ int ret;
+
+ fds.fd = fd;
+ ret = poll(&fds, 1, 500);
+ if (ret == 0) {
+ if (cfg_verbose)
+ fprintf(stderr, "poll timeout\n");
+ } else if (ret < 0) {
+ error(1, errno, "poll");
+ }
+ }
+
+ flush_errqueue_recv(fd);
+}
+
static int send_tcp(int fd, char *data)
{
int ret, done = 0, count = 0;
@@ -168,16 +295,40 @@ static int send_udp(int fd, char *data)
return count;
}
+static void send_ts_cmsg(struct cmsghdr *cm)
+{
+ uint32_t *valp;
+
+ cm->cmsg_level = SOL_SOCKET;
+ cm->cmsg_type = SO_TIMESTAMPING;
+ cm->cmsg_len = CMSG_LEN(sizeof(cfg_tx_ts));
+ valp = (void *)CMSG_DATA(cm);
+ *valp = cfg_tx_ts;
+}
+
static int send_udp_sendmmsg(int fd, char *data)
{
+ char control[CMSG_SPACE(sizeof(cfg_tx_ts))] = {0};
const int max_nr_msg = ETH_MAX_MTU / ETH_DATA_LEN;
struct mmsghdr mmsgs[max_nr_msg];
struct iovec iov[max_nr_msg];
unsigned int off = 0, left;
+ size_t msg_controllen = 0;
int i = 0, ret;
memset(mmsgs, 0, sizeof(mmsgs));
+ if (cfg_tx_tstamp) {
+ struct msghdr msg = {0};
+ struct cmsghdr *cmsg;
+
+ msg.msg_control = control;
+ msg.msg_controllen = sizeof(control);
+ cmsg = CMSG_FIRSTHDR(&msg);
+ send_ts_cmsg(cmsg);
+ msg_controllen += CMSG_SPACE(sizeof(cfg_tx_ts));
+ }
+
left = cfg_payload_len;
while (left) {
if (i == max_nr_msg)
@@ -189,6 +340,13 @@ static int send_udp_sendmmsg(int fd, char *data)
mmsgs[i].msg_hdr.msg_iov = iov + i;
mmsgs[i].msg_hdr.msg_iovlen = 1;
+ mmsgs[i].msg_hdr.msg_name = (void *)&cfg_dst_addr;
+ mmsgs[i].msg_hdr.msg_namelen = cfg_alen;
+ if (msg_controllen) {
+ mmsgs[i].msg_hdr.msg_control = control;
+ mmsgs[i].msg_hdr.msg_controllen = msg_controllen;
+ }
+
off += iov[i].iov_len;
left -= iov[i].iov_len;
i++;
@@ -214,9 +372,12 @@ static void send_udp_segment_cmsg(struct cmsghdr *cm)
static int send_udp_segment(int fd, char *data)
{
- char control[CMSG_SPACE(sizeof(cfg_gso_size))] = {0};
+ char control[CMSG_SPACE(sizeof(cfg_gso_size)) +
+ CMSG_SPACE(sizeof(cfg_tx_ts))] = {0};
struct msghdr msg = {0};
struct iovec iov = {0};
+ size_t msg_controllen;
+ struct cmsghdr *cmsg;
int ret;
iov.iov_base = data;
@@ -227,8 +388,16 @@ static int send_udp_segment(int fd, char *data)
msg.msg_control = control;
msg.msg_controllen = sizeof(control);
- send_udp_segment_cmsg(CMSG_FIRSTHDR(&msg));
+ cmsg = CMSG_FIRSTHDR(&msg);
+ send_udp_segment_cmsg(cmsg);
+ msg_controllen = CMSG_SPACE(sizeof(cfg_mss));
+ if (cfg_tx_tstamp) {
+ cmsg = CMSG_NXTHDR(&msg, cmsg);
+ send_ts_cmsg(cmsg);
+ msg_controllen += CMSG_SPACE(sizeof(cfg_tx_ts));
+ }
+ msg.msg_controllen = msg_controllen;
msg.msg_name = (void *)&cfg_dst_addr;
msg.msg_namelen = cfg_alen;
@@ -243,7 +412,7 @@ static int send_udp_segment(int fd, char *data)
static void usage(const char *filepath)
{
- error(1, 0, "Usage: %s [-46cmtuz] [-C cpu] [-D dst ip] [-l secs] [-m messagenr] [-p port] [-s sendsize] [-S gsosize]",
+ error(1, 0, "Usage: %s [-46acmHPtTuvz] [-C cpu] [-D dst ip] [-l secs] [-M messagenr] [-p port] [-s sendsize] [-S gsosize]",
filepath);
}
@@ -252,7 +421,7 @@ static void parse_opts(int argc, char **argv)
int max_len, hdrlen;
int c;
- while ((c = getopt(argc, argv, "46cC:D:l:mM:p:s:S:tuz")) != -1) {
+ while ((c = getopt(argc, argv, "46acC:D:Hl:mM:p:s:PS:tTuvz")) != -1) {
switch (c) {
case '4':
if (cfg_family != PF_UNSPEC)
@@ -266,6 +435,9 @@ static void parse_opts(int argc, char **argv)
cfg_family = PF_INET6;
cfg_alen = sizeof(struct sockaddr_in6);
break;
+ case 'a':
+ cfg_audit = true;
+ break;
case 'c':
cfg_cache_trash = true;
break;
@@ -287,6 +459,9 @@ static void parse_opts(int argc, char **argv)
case 'p':
cfg_port = strtoul(optarg, NULL, 0);
break;
+ case 'P':
+ cfg_poll = true;
+ break;
case 's':
cfg_payload_len = strtoul(optarg, NULL, 0);
break;
@@ -294,12 +469,22 @@ static void parse_opts(int argc, char **argv)
cfg_gso_size = strtoul(optarg, NULL, 0);
cfg_segment = true;
break;
+ case 'H':
+ cfg_tx_ts = SOF_TIMESTAMPING_TX_HARDWARE;
+ cfg_tx_tstamp = true;
+ break;
case 't':
cfg_tcp = true;
break;
+ case 'T':
+ cfg_tx_tstamp = true;
+ break;
case 'u':
cfg_connected = false;
break;
+ case 'v':
+ cfg_verbose = true;
+ break;
case 'z':
cfg_zerocopy = true;
break;
@@ -315,6 +500,8 @@ static void parse_opts(int argc, char **argv)
error(1, 0, "connectionless tcp makes no sense");
if (cfg_segment && cfg_sendmmsg)
error(1, 0, "cannot combine segment offload and sendmmsg");
+ if (cfg_tx_tstamp && !(cfg_segment || cfg_sendmmsg))
+ error(1, 0, "Options -T and -H require either -S or -m option");
if (cfg_family == PF_INET)
hdrlen = sizeof(struct iphdr) + sizeof(struct udphdr);
@@ -349,11 +536,80 @@ static void set_pmtu_discover(int fd, bool is_ipv4)
error(1, errno, "setsockopt path mtu");
}
+static void set_tx_timestamping(int fd)
+{
+ int val = SOF_TIMESTAMPING_OPT_CMSG | SOF_TIMESTAMPING_OPT_ID |
+ SOF_TIMESTAMPING_OPT_TSONLY;
+
+ if (cfg_tx_ts == SOF_TIMESTAMPING_TX_SOFTWARE)
+ val |= SOF_TIMESTAMPING_SOFTWARE;
+ else
+ val |= SOF_TIMESTAMPING_RAW_HARDWARE;
+
+ if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING, &val, sizeof(val)))
+ error(1, errno, "setsockopt tx timestamping");
+}
+
+static void print_audit_report(unsigned long num_msgs, unsigned long num_sends)
+{
+ unsigned long tdelta;
+
+ tdelta = tend - tstart;
+ if (!tdelta)
+ return;
+
+ fprintf(stderr, "Summary over %lu.%03lu seconds...\n",
+ tdelta / 1000, tdelta % 1000);
+ fprintf(stderr,
+ "sum %s tx: %6lu MB/s %10lu calls (%lu/s) %10lu msgs (%lu/s)\n",
+ cfg_tcp ? "tcp" : "udp",
+ ((num_msgs * cfg_payload_len) >> 10) / tdelta,
+ num_sends, num_sends * 1000 / tdelta,
+ num_msgs, num_msgs * 1000 / tdelta);
+
+ if (cfg_tx_tstamp) {
+ if (stat_tx_ts_errors)
+ error(1, 0,
+ "Expected clean TX Timestamps: %9lu msgs received %6lu errors",
+ stat_tx_ts, stat_tx_ts_errors);
+ if (stat_tx_ts != num_sends)
+ error(1, 0,
+ "Unexpected number of TX Timestamps: %9lu expected %9lu received",
+ num_sends, stat_tx_ts);
+ fprintf(stderr,
+ "Tx Timestamps: %19lu received %17lu errors\n",
+ stat_tx_ts, stat_tx_ts_errors);
+ }
+
+ if (cfg_zerocopy) {
+ if (stat_zcopies != num_sends)
+ error(1, 0, "Unexpected number of Zerocopy completions: %9lu expected %9lu received",
+ num_sends, stat_zcopies);
+ fprintf(stderr,
+ "Zerocopy acks: %19lu\n",
+ stat_zcopies);
+ }
+}
+
+static void print_report(unsigned long num_msgs, unsigned long num_sends)
+{
+ fprintf(stderr,
+ "%s tx: %6lu MB/s %8lu calls/s %6lu msg/s\n",
+ cfg_tcp ? "tcp" : "udp",
+ (num_msgs * cfg_payload_len) >> 20,
+ num_sends, num_msgs);
+
+ if (cfg_audit) {
+ total_num_msgs += num_msgs;
+ total_num_sends += num_sends;
+ }
+}
+
int main(int argc, char **argv)
{
unsigned long num_msgs, num_sends;
unsigned long tnow, treport, tstop;
- int fd, i, val;
+ int fd, i, val, ret;
parse_opts(argc, argv);
@@ -373,8 +629,16 @@ int main(int argc, char **argv)
if (cfg_zerocopy) {
val = 1;
- if (setsockopt(fd, SOL_SOCKET, SO_ZEROCOPY, &val, sizeof(val)))
+
+ ret = setsockopt(fd, SOL_SOCKET, SO_ZEROCOPY,
+ &val, sizeof(val));
+ if (ret) {
+ if (errno == ENOPROTOOPT || errno == ENOTSUPP) {
+ fprintf(stderr, "SO_ZEROCOPY not supported");
+ exit(KSFT_SKIP);
+ }
error(1, errno, "setsockopt zerocopy");
+ }
}
if (cfg_connected &&
@@ -384,8 +648,13 @@ int main(int argc, char **argv)
if (cfg_segment)
set_pmtu_discover(fd, cfg_family == PF_INET);
+ if (cfg_tx_tstamp)
+ set_tx_timestamping(fd);
+
num_msgs = num_sends = 0;
tnow = gettimeofday_ms();
+ tstart = tnow;
+ tend = tnow;
tstop = tnow + cfg_runtime_ms;
treport = tnow + 1000;
@@ -400,19 +669,15 @@ int main(int argc, char **argv)
else
num_sends += send_udp(fd, buf[i]);
num_msgs++;
- if (cfg_zerocopy && ((num_msgs & 0xF) == 0))
- flush_zerocopy(fd);
+ if ((cfg_zerocopy && ((num_msgs & 0xF) == 0)) || cfg_tx_tstamp)
+ flush_errqueue(fd, cfg_poll);
if (cfg_msg_nr && num_msgs >= cfg_msg_nr)
break;
tnow = gettimeofday_ms();
- if (tnow > treport) {
- fprintf(stderr,
- "%s tx: %6lu MB/s %8lu calls/s %6lu msg/s\n",
- cfg_tcp ? "tcp" : "udp",
- (num_msgs * cfg_payload_len) >> 20,
- num_sends, num_msgs);
+ if (tnow >= treport) {
+ print_report(num_msgs, num_sends);
num_msgs = num_sends = 0;
treport = tnow + 1000;
}
@@ -423,8 +688,18 @@ int main(int argc, char **argv)
} while (!interrupted && (cfg_runtime_ms == -1 || tnow < tstop));
+ if (cfg_zerocopy || cfg_tx_tstamp)
+ flush_errqueue(fd, true);
+
if (close(fd))
error(1, errno, "close");
+ if (cfg_audit) {
+ tend = tnow;
+ total_num_msgs += num_msgs;
+ total_num_sends += num_sends;
+ print_audit_report(total_num_msgs, total_num_sends);
+ }
+
return 0;
}
diff --git a/tools/testing/selftests/net/xfrm_policy.sh b/tools/testing/selftests/net/xfrm_policy.sh
index 71d7fdc513c1..5445943bf07f 100755
--- a/tools/testing/selftests/net/xfrm_policy.sh
+++ b/tools/testing/selftests/net/xfrm_policy.sh
@@ -257,6 +257,29 @@ check_exceptions()
return $lret
}
+check_hthresh_repeat()
+{
+ local log=$1
+ i=0
+
+ for i in $(seq 1 10);do
+ ip -net ns1 xfrm policy update src e000:0001::0000 dst ff01::0014:0000:0001 dir in tmpl src :: dst :: proto esp mode tunnel priority 100 action allow || break
+ ip -net ns1 xfrm policy set hthresh6 0 28 || break
+
+ ip -net ns1 xfrm policy update src e000:0001::0000 dst ff01::01 dir in tmpl src :: dst :: proto esp mode tunnel priority 100 action allow || break
+ ip -net ns1 xfrm policy set hthresh6 0 28 || break
+ done
+
+ if [ $i -ne 10 ] ;then
+ echo "FAIL: $log" 1>&2
+ ret=1
+ return 1
+ fi
+
+ echo "PASS: $log"
+ return 0
+}
+
#check for needed privileges
if [ "$(id -u)" -ne 0 ];then
echo "SKIP: Need root privileges"
@@ -404,7 +427,9 @@ for n in ns3 ns4;do
ip -net $n xfrm policy set hthresh4 32 32 hthresh6 128 128
sleep $((RANDOM%5))
done
-check_exceptions "exceptions and block policies after hresh change to normal"
+check_exceptions "exceptions and block policies after htresh change to normal"
+
+check_hthresh_repeat "policies with repeated htresh change"
for i in 1 2 3 4;do ip netns del ns$i;done