[PATCH net-next v4 3/3] selftests: net: add coverage for fdb nexthop dst_port

From: Jack Ma

Date: Wed Jul 22 2026 - 21:23:06 EST


Add coverage for the new per-nexthop VXLAN destination port
(NHA_DST_PORT).

In fib_nexthops.sh, new ipv4_fdb_port_fcnal() and ipv6_fdb_port_fcnal()
tests check that a dst_port is accepted on an fdb nexthop that has a
gateway and echoed back on dump, that it is rejected without a gateway
and rejected when zero, that a group may hold legs that differ only in
UDP port, and that a portless fdb nexthop omits the attribute. The
tests SKIP when iproute2 lacks the "dst_port" keyword.

In test_vxlan_nh.sh, basic_tx_common() gains a second fdb nexthop group
whose nexthop carries a destination port that differs from the VXLAN
device default, plus a flower filter keyed on that port, to confirm the
per-nexthop port is used on the wire. The test now requires an iproute2
with dst_port support.

Signed-off-by: Jack Ma <jack4it@xxxxxxxxx>
---
tools/testing/selftests/net/fib_nexthops.sh | 81 ++++++++++++++++++++++++++++
tools/testing/selftests/net/test_vxlan_nh.sh | 34 +++++++++++-
2 files changed, 113 insertions(+), 2 deletions(-)

diff --git a/tools/testing/selftests/net/fib_nexthops.sh b/tools/testing/selftests/net/fib_nexthops.sh
index ac868a7316946..834ba1c0676c8 100755
--- a/tools/testing/selftests/net/fib_nexthops.sh
+++ b/tools/testing/selftests/net/fib_nexthops.sh
@@ -30,6 +30,7 @@ IPV4_TESTS="
ipv4_large_res_grp
ipv4_compat_mode
ipv4_fdb_grp_fcnal
+ ipv4_fdb_port_fcnal
ipv4_mpath_select
ipv4_torture
ipv4_res_torture
@@ -44,6 +45,7 @@ IPV6_TESTS="
ipv6_large_res_grp
ipv6_compat_mode
ipv6_fdb_grp_fcnal
+ ipv6_fdb_port_fcnal
ipv6_mpath_select
ipv6_torture
ipv6_res_torture
@@ -432,6 +434,15 @@ check_nexthop_fdb_support()
fi
}

+check_nexthop_fdb_port_support()
+{
+ $IP nexthop help 2>&1 | grep -q "dst_port"
+ if [ $? -ne 0 ]; then
+ echo "SKIP: iproute2 too old, missing nexthop dst_port support"
+ return $ksft_skip
+ fi
+}
+
check_nexthop_res_support()
{
$IP nexthop help 2>&1 | grep -q resilient
@@ -541,6 +552,41 @@ ipv6_fdb_grp_fcnal()
$IP link del dev vx10
}

+ipv6_fdb_port_fcnal()
+{
+ echo
+ echo "IPv6 fdb nexthop dst_port functional"
+ echo "------------------------------------"
+
+ check_nexthop_fdb_port_support
+ if [ $? -eq $ksft_skip ]; then
+ return $ksft_skip
+ fi
+
+ # NHA_DST_PORT: optional per-nexthop VXLAN destination UDP port,
+ # letting an fdb nexthop group balance a flow across legs that share
+ # an underlay IP but listen on different UDP ports.
+ run_cmd "$IP nexthop add id 80 via 2001:db8:91::2 fdb dst_port 4790"
+ check_nexthop "id 80" "id 80 via 2001:db8:91::2 scope link fdb dst_port 4790"
+ log_test $? 0 "Fdb nexthop with dst_port"
+
+ run_cmd "$IP nexthop add id 81 fdb dst_port 4790"
+ log_test $? 2 "Fdb nexthop with dst_port but no gateway"
+
+ run_cmd "$IP nexthop add id 81 via 2001:db8:91::2 fdb dst_port 0"
+ log_test $? 2 "Fdb nexthop with dst_port 0"
+
+ run_cmd "$IP nexthop add id 82 via 2001:db8:91::2 fdb dst_port 4789"
+ run_cmd "$IP nexthop add id 83 via 2001:db8:91::3 fdb dst_port 5789"
+ run_cmd "$IP nexthop add id 106 group 82/83 fdb"
+ check_nexthop "id 106" "id 106 group 82/83 fdb"
+ log_test $? 0 "Fdb nexthop group with legs differing in dst_port"
+
+ run_cmd "$IP nexthop add id 84 via 2001:db8:91::2 fdb"
+ check_nexthop "id 84" "id 84 via 2001:db8:91::2 scope link fdb"
+ log_test $? 0 "Fdb nexthop without dst_port omits dst_port"
+}
+
ipv4_fdb_grp_fcnal()
{
local rc
@@ -641,6 +687,41 @@ ipv4_fdb_grp_fcnal()
$IP link del dev vx10
}

+ipv4_fdb_port_fcnal()
+{
+ echo
+ echo "IPv4 fdb nexthop dst_port functional"
+ echo "------------------------------------"
+
+ check_nexthop_fdb_port_support
+ if [ $? -eq $ksft_skip ]; then
+ return $ksft_skip
+ fi
+
+ # NHA_DST_PORT: optional per-nexthop VXLAN destination UDP port,
+ # letting an fdb nexthop group balance a flow across legs that share
+ # an underlay IP but listen on different UDP ports.
+ run_cmd "$IP nexthop add id 30 via 172.16.1.2 fdb dst_port 4790"
+ check_nexthop "id 30" "id 30 via 172.16.1.2 scope link fdb dst_port 4790"
+ log_test $? 0 "Fdb nexthop with dst_port"
+
+ run_cmd "$IP nexthop add id 31 fdb dst_port 4790"
+ log_test $? 2 "Fdb nexthop with dst_port but no gateway"
+
+ run_cmd "$IP nexthop add id 31 via 172.16.1.2 fdb dst_port 0"
+ log_test $? 2 "Fdb nexthop with dst_port 0"
+
+ run_cmd "$IP nexthop add id 32 via 172.16.1.2 fdb dst_port 4789"
+ run_cmd "$IP nexthop add id 33 via 172.16.1.3 fdb dst_port 5789"
+ run_cmd "$IP nexthop add id 105 group 32/33 fdb"
+ check_nexthop "id 105" "id 105 group 32/33 fdb"
+ log_test $? 0 "Fdb nexthop group with legs differing in dst_port"
+
+ run_cmd "$IP nexthop add id 34 via 172.16.1.2 fdb"
+ check_nexthop "id 34" "id 34 via 172.16.1.2 scope link fdb"
+ log_test $? 0 "Fdb nexthop without dst_port omits dst_port"
+}
+
ipv4_mpath_select()
{
local rc dev match h addr
diff --git a/tools/testing/selftests/net/test_vxlan_nh.sh b/tools/testing/selftests/net/test_vxlan_nh.sh
index 20f3369f776b1..7b5dc1d37fa02 100755
--- a/tools/testing/selftests/net/test_vxlan_nh.sh
+++ b/tools/testing/selftests/net/test_vxlan_nh.sh
@@ -56,6 +56,16 @@ tc_stats_get()
tc_rule_handle_stats_get "dev dummy1 egress" 101 ".packets" "-n $ns1"
}

+nh_stats_get_port()
+{
+ ip -n "$ns1" -s -j nexthop show id 20 | jq ".[][\"group_stats\"][][\"packets\"]"
+}
+
+tc_stats_get_port()
+{
+ tc_rule_handle_stats_get "dev dummy1 egress" 102 ".packets" "-n $ns1"
+}
+
basic_tx_common()
{
local af_str=$1; shift
@@ -90,6 +100,26 @@ basic_tx_common()
busywait "$BUSYWAIT_TIMEOUT" until_counter_is "== 1" tc_stats_get > /dev/null
check_err $? "tc filter stats did not increase"

+ # Add a second FDB nexthop group whose nexthop carries a per-nexthop
+ # destination port (NHA_DST_PORT) that differs from the VXLAN device
+ # default. Matching outer traffic must egress with that port, so a
+ # separate flower filter keyed on the new port catches it.
+ run_cmd "tc -n $ns1 filter add dev dummy1 egress proto $proto pref 1 handle 102 \
+ flower ip_proto udp dst_ip $remote_addr dst_port 4790 action pass"
+
+ run_cmd "ip -n $ns1 nexthop add id 2 via $remote_addr fdb dst_port 4790"
+ run_cmd "ip -n $ns1 nexthop add id 20 group 2 fdb"
+
+ run_cmd "bridge -n $ns1 fdb add 00:11:22:33:44:66 dev vx0 self static nhid 20"
+
+ run_cmd "ip netns exec $ns1 mausezahn vx0 -a own -b 00:11:22:33:44:66 -c 1 -q"
+
+ busywait "$BUSYWAIT_TIMEOUT" until_counter_is "== 1" nh_stats_get_port > /dev/null
+ check_err $? "FDB nexthop group stats did not increase (with port)"
+
+ busywait "$BUSYWAIT_TIMEOUT" until_counter_is "== 1" tc_stats_get_port > /dev/null
+ check_err $? "tc filter stats did not increase (with port)"
+
log_test "VXLAN FDB nexthop: $af_str basic Tx"
}

@@ -210,8 +240,8 @@ require_command arping
require_command ndisc6
require_command jq

-if ! ip nexthop help 2>&1 | grep -q "stats"; then
- echo "SKIP: iproute2 ip too old, missing nexthop stats support"
+if ! ip nexthop help 2>&1 | grep -q "dst_port"; then
+ echo "SKIP: iproute2 ip too old, missing nexthop dst_port support"
exit "$ksft_skip"
fi


--
2.43.0