From: Bochao Cao <[email protected]>

Replace the private-veth script with a NetDrvEpEnv test for a local DUT
and remote tester. Build the helper with the shared BPF rules and
installed kernel headers.

Check detected features against advertised support. Skip unsupported
native XDP and veth XDP_TX without a peer XDP program. Wait for DUT
readiness and run the remote helper under timeout.

Signed-off-by: Bochao Cao <[email protected]>
---
 tools/testing/selftests/bpf/Makefile               |   9 +-
 tools/testing/selftests/bpf/test_xdp_features.sh   | 169 ---------------------
 tools/testing/selftests/drivers/net/README.rst     |  18 +++
 tools/testing/selftests/drivers/net/hw/.gitignore  |   3 +
 tools/testing/selftests/drivers/net/hw/Makefile    |  15 +-
 tools/testing/selftests/drivers/net/hw/config      |   3 +
 .../net/hw/xdp_features.bpf.c}                     |   0
 .../{bpf => drivers/net/hw}/xdp_features.c         |   2 +
 .../{bpf => drivers/net/hw}/xdp_features.h         |   0
 .../selftests/drivers/net/hw/xdp_features.py       | 137 +++++++++++++++++
 10 files changed, 178 insertions(+), 178 deletions(-)

diff --git a/tools/testing/selftests/bpf/Makefile 
b/tools/testing/selftests/bpf/Makefile
index 883c92701b9f1..cb76b68c97dbb 100644
--- a/tools/testing/selftests/bpf/Makefile
+++ b/tools/testing/selftests/bpf/Makefile
@@ -124,11 +124,11 @@ TEST_FILES = xsk_prereqs.sh $(wildcard 
progs/btf_dump_test_case_*.c)
 TEST_PROGS := test_kmod.sh \
        test_lirc_mode2.sh \
        test_bpftool_build.sh \
        test_doc_build.sh \
        test_xsk.sh \
-       test_xdp_features.sh
+       #
 
 TEST_PROGS_EXTENDED := \
        ima_setup.sh verify_sig_setup.sh
 
 TEST_KMODS := bpf_testmod.ko bpf_test_no_cfi.ko bpf_test_modorder_x.ko \
@@ -140,11 +140,10 @@ TEST_GEN_PROGS_EXTENDED = \
        bench \
        flow_dissector_load \
        test_cpp \
        test_lirc_mode2_user \
        veristat \
-       xdp_features \
        xdp_hw_metadata \
        xdp_synproxy \
        xskxceiver
 
 TEST_GEN_FILES += $(TEST_KMODS) liburandom_read.so urandom_read sign-file 
uprobe_multi
@@ -542,11 +541,10 @@ linked_maps.skel.h-deps := linked_maps1.bpf.o 
linked_maps2.bpf.o
 test_subskeleton.skel.h-deps := test_subskeleton_lib2.bpf.o 
test_subskeleton_lib.bpf.o test_subskeleton.bpf.o
 test_subskeleton_lib.skel.h-deps := test_subskeleton_lib2.bpf.o 
test_subskeleton_lib.bpf.o
 test_usdt.skel.h-deps := test_usdt.bpf.o test_usdt_multispec.bpf.o
 xsk_xdp_progs.skel.h-deps := xsk_xdp_progs.bpf.o
 xdp_hw_metadata.skel.h-deps := xdp_hw_metadata.bpf.o
-xdp_features.skel.h-deps := xdp_features.bpf.o
 tracing_multi.skel.h-deps := tracing_multi_attach.bpf.o 
tracing_multi_check.bpf.o
 tracing_multi_module.skel.h-deps := tracing_multi_attach_module.bpf.o 
tracing_multi_check.bpf.o
 tracing_multi_intersect.skel.h-deps := tracing_multi_intersect_attach.bpf.o 
tracing_multi_check.bpf.o
 tracing_multi_session.skel.h-deps := tracing_multi_session_attach.bpf.o 
tracing_multi_check.bpf.o
 
@@ -945,15 +943,10 @@ $(OUTPUT)/xdp_hw_metadata: xdp_hw_metadata.c xsk.c 
network_helpers.c \
                           xsk.h network_helpers.h test_progs.h bpf_util.h \
                           $(OUTPUT)/xdp_hw_metadata.skel.h $(BPFOBJ) | 
$(OUTPUT)
        $(call msg,BINARY,,$@)
        $(Q)$(CC) $(CFLAGS) $(filter %.a %.o %.c,$^) $(LDLIBS) -o $@
 
-$(OUTPUT)/xdp_features: xdp_features.c xdp_features.h \
-                          $(OUTPUT)/xdp_features.skel.h $(BPFOBJ) | $(OUTPUT)
-       $(call msg,BINARY,,$@)
-       $(Q)$(CC) $(CFLAGS) $(filter %.a %.o %.c,$^) $(LDLIBS) -o $@
-
 # Make sure we are able to include and link libbpf against c++.
 CXXFLAGS += $(CFLAGS)
 CXXFLAGS := $(subst -D_GNU_SOURCE=,,$(CXXFLAGS))
 CXXFLAGS := $(subst -std=gnu11,-std=gnu++11,$(CXXFLAGS))
 $(OUTPUT)/test_cpp: test_cpp.cpp $(OUTPUT)/test_core_extern.skel.h $(BPFOBJ)
diff --git a/tools/testing/selftests/bpf/test_xdp_features.sh 
b/tools/testing/selftests/bpf/test_xdp_features.sh
deleted file mode 100755
index a28021ba74726..0000000000000
--- a/tools/testing/selftests/bpf/test_xdp_features.sh
+++ /dev/null
@@ -1,169 +0,0 @@
-#!/bin/bash
-# SPDX-License-Identifier: GPL-2.0
-
-readonly NS="ns1-$(mktemp -u XXXXXX)"
-readonly DUT_NS="dut-${NS}"
-readonly V0_IP4=10.10.0.11
-readonly V1_IP4=10.10.0.1
-readonly V0_IP6=2001:db8::11
-readonly V1_IP6=2001:db8::1
-
-ret=1
-dut_pid=""
-peer_created=false
-dut_created=false
-setup_in_progress=true
-pending_signal=0
-
-setup() {
-       {
-               ip netns add "${NS}"
-               peer_created=true
-               ip netns add "${DUT_NS}"
-               dut_created=true
-
-               ip -n "$DUT_NS" link add v1 type veth peer name v0 netns ${NS}
-
-               ip -n "$DUT_NS" link set v1 up
-               ip -n "$DUT_NS" addr add $V1_IP4/24 dev v1
-               ip -n "$DUT_NS" addr add $V1_IP6/64 nodad dev v1
-               ip -n ${NS} link set dev v0 up
-               ip -n ${NS} addr add $V0_IP4/24 dev v0
-               ip -n ${NS} addr add $V0_IP6/64 nodad dev v0
-
-               # Enable XDP mode and disable checksum offload
-               ip netns exec "$DUT_NS" ethtool -K v1 gro on
-               ip netns exec "$DUT_NS" ethtool -K v1 tx-checksumming off
-               ip netns exec ${NS} ethtool -K v0 gro on
-               ip netns exec ${NS} ethtool -K v0 tx-checksumming off
-       } > /dev/null 2>&1
-       setup_in_progress=false
-       [ "$pending_signal" -eq 0 ] || exit "$pending_signal"
-}
-
-terminate_dut_server() {
-       local pid
-
-       # Use the shell job instead of dut_pid, which may not have been assigned
-       # yet if a signal arrived immediately after the server was started.
-       pid=$(jobs -pr %% 2> /dev/null) || true
-       if [ -z "$pid" ]; then
-               dut_pid=""
-               return
-       fi
-
-       kill -KILL %% 2> /dev/null || true
-       wait "$pid" 2> /dev/null || true
-       dut_pid=""
-}
-
-cleanup() {
-       trap - EXIT
-       trap '' HUP INT QUIT ABRT TERM
-       terminate_dut_server
-       if "$dut_created"; then
-               ip netns del "$DUT_NS" 2> /dev/null || true
-       fi
-       if "$peer_created"; then
-               ip netns del "$NS" 2> /dev/null || true
-       fi
-}
-
-wait_for_dut_server() {
-       while true; do
-               if [ "$(jobs -pr %% 2> /dev/null)" != "$dut_pid" ]; then
-                       echo "xdp_features server $dut_pid exited before 
readiness" >&2
-                       return 1
-               fi
-
-               if ip netns exec "$DUT_NS" ss -tlp 2> /dev/null |
-                  grep -q "pid=$dut_pid,"; then
-                       return 0
-               fi
-
-               sleep 1
-       done
-}
-
-start_dut_server() {
-       ip netns exec "$DUT_NS" ./xdp_features "$@" &
-       dut_pid=$!
-       wait_for_dut_server
-}
-
-reap_dut_server() {
-       local status=0
-
-       wait "$dut_pid" || status=$?
-       dut_pid=""
-       return "$status"
-}
-
-test_xdp_features() {
-       setup
-
-       ## XDP_PASS
-       start_dut_server -f XDP_PASS -D $V1_IP6 -T $V0_IP6 v1
-       ip netns exec ${NS} ./xdp_features -t -f XDP_PASS \
-                                          -D $V1_IP6 -C $V1_IP6 \
-                                          -T $V0_IP6 v0
-       [ $? -ne 0 ] && exit
-       reap_dut_server
-
-       ## XDP_DROP
-       start_dut_server -f XDP_DROP -D ::ffff:$V1_IP4 -T ::ffff:$V0_IP4 v1
-       ip netns exec ${NS} ./xdp_features -t -f XDP_DROP \
-                                          -D ::ffff:$V1_IP4 \
-                                          -C ::ffff:$V1_IP4 \
-                                          -T ::ffff:$V0_IP4 v0
-       [ $? -ne 0 ] && exit
-       reap_dut_server
-
-       ## XDP_ABORTED
-       start_dut_server -f XDP_ABORTED -D $V1_IP6 -T $V0_IP6 v1
-       ip netns exec ${NS} ./xdp_features -t -f XDP_ABORTED \
-                                          -D $V1_IP6 -C $V1_IP6 \
-                                          -T $V0_IP6 v0
-       [ $? -ne 0 ] && exit
-       reap_dut_server
-
-       # veth only flushes XDP_TX to a peer with native XDP attached.
-       echo "SKIP: XDP_TX needs a peer native XDP program"
-
-       ## XDP_REDIRECT
-       start_dut_server -f XDP_REDIRECT -D $V1_IP6 -T $V0_IP6 v1
-       ip netns exec ${NS} ./xdp_features -t -f XDP_REDIRECT \
-                                          -D $V1_IP6 -C $V1_IP6 \
-                                          -T $V0_IP6 v0
-       [ $? -ne 0 ] && exit
-       reap_dut_server
-
-       ## XDP_NDO_XMIT
-       start_dut_server -f XDP_NDO_XMIT -D ::ffff:$V1_IP4 -T ::ffff:$V0_IP4 v1
-       ip netns exec ${NS} ./xdp_features -t -f XDP_NDO_XMIT \
-                                          -D ::ffff:$V1_IP4 \
-                                          -C ::ffff:$V1_IP4 \
-                                          -T ::ffff:$V0_IP4 v0
-       ret=$?
-       reap_dut_server
-}
-
-handle_signal() {
-       if "$setup_in_progress"; then
-               pending_signal=$1
-       else
-               exit "$1"
-       fi
-}
-
-set -e
-trap cleanup EXIT
-trap 'handle_signal 129' HUP
-trap 'handle_signal 130' INT
-trap 'handle_signal 131' QUIT
-trap 'handle_signal 134' ABRT
-trap 'handle_signal 143' TERM
-
-test_xdp_features
-
-exit $ret
diff --git a/tools/testing/selftests/drivers/net/README.rst 
b/tools/testing/selftests/drivers/net/README.rst
index 3fe49bce4f3a5..0791f02ba44b4 100644
--- a/tools/testing/selftests/drivers/net/README.rst
+++ b/tools/testing/selftests/drivers/net/README.rst
@@ -308,5 +308,23 @@ The code should be clean, avoid disabling pylint warnings 
explicitly!
 Running tests CI-style
 ======================
 
 See https://github.com/linux-netdev/nipa/wiki for instructions on how
 to easily run the tests using ``virtme-ng``.
+
+XDP feature test
+===============
+
+``hw/xdp_features.py`` compares the DUT's advertised XDP capabilities with
+packet observations. The endpoints must be directly connected at layer 2.
+The remote helper needs packet-socket privileges and GNU ``timeout``, but
+does not load BPF, attach XDP, or change interface/offload settings.
+Probe checksums are computed in software; TX checksum offload may remain
+at its configured value on both endpoints.
+
+A DUT without advertised native XDP support is skipped. On veth,
+``XDP_TX`` additionally requires an existing native XDP program on the
+peer, because veth only flushes its peer receive ring in that case.
+Without this prerequisite the TX subtest is skipped; the test does not
+change the shared remote endpoint to enable it. Other advertised versus
+observed mismatches are failures, including unsupported features that
+unexpectedly work.
diff --git a/tools/testing/selftests/drivers/net/hw/.gitignore 
b/tools/testing/selftests/drivers/net/hw/.gitignore
index 46540468a7753..a0cd8bc3739d8 100644
--- a/tools/testing/selftests/drivers/net/hw/.gitignore
+++ b/tools/testing/selftests/drivers/net/hw/.gitignore
@@ -1,4 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0-only
 iou-zcrx
 ncdevmem
 toeplitz
+tools/
+xdp_features
+xdp_features.skel.h
diff --git a/tools/testing/selftests/drivers/net/hw/Makefile 
b/tools/testing/selftests/drivers/net/hw/Makefile
index 18ce235a77f84..bd818e8625c23 100644
--- a/tools/testing/selftests/drivers/net/hw/Makefile
+++ b/tools/testing/selftests/drivers/net/hw/Makefile
@@ -49,10 +49,11 @@ TEST_PROGS = \
        toeplitz.py \
        tso.py \
        userns_devmem.py \
        uso.py \
        vlan.py \
+       xdp_features.py \
        xdp_metadata.py \
        xsk_reconfig.py \
        #
 
 TEST_PROGS_EXTENDED := \
@@ -77,11 +78,11 @@ TEST_INCLUDES := \
 YNL_GEN_FILES := \
        ncdevmem \
        toeplitz \
        $(COND_GEN_FILES) \
 # end of YNL_GEN_FILES
-TEST_GEN_FILES += $(YNL_GEN_FILES)
+TEST_GEN_FILES += $(YNL_GEN_FILES) xdp_features
 TEST_GEN_FILES += $(patsubst %.c,%.o,$(wildcard *.bpf.c))
 
 include ../../../lib.mk
 
 # YNL build
@@ -92,8 +93,20 @@ YNL_GENS := \
 
 include ../../../net/ynl.mk
 
 include ../../../net/bpf.mk
 
+$(OUTPUT)/xdp_features.bpf.o: xdp_features.h
+
+$(OUTPUT)/xdp_features: CFLAGS += -I$(OUTPUT) -I$(SCRATCH_DIR)/include
+$(OUTPUT)/xdp_features: CFLAGS += -I../../../net/lib
+$(OUTPUT)/xdp_features: LDLIBS += -lelf -lz -lpthread
+$(OUTPUT)/xdp_features: xdp_features.c xdp_features.h \
+                          $(OUTPUT)/xdp_features.skel.h $(BPFOBJ)
+       $(call msg,BINARY,,$@)
+       $(Q)$(CC) $(CFLAGS) $(CPPFLAGS) $(KHDR_INCLUDES) \
+               $(filter %.c %.o,$^) $(BPFOBJ) \
+               $(LDFLAGS) $(LDLIBS) -o $@
+
 ifeq ($(HAS_IOURING_ZCRX),y)
 $(OUTPUT)/iou-zcrx: LDLIBS += -luring -lpthread
 endif
diff --git a/tools/testing/selftests/drivers/net/hw/config 
b/tools/testing/selftests/drivers/net/hw/config
index c6c2b64bb7124..14a8041b468ba 100644
--- a/tools/testing/selftests/drivers/net/hw/config
+++ b/tools/testing/selftests/drivers/net/hw/config
@@ -1,6 +1,8 @@
+CONFIG_BPF_EVENTS=y
 CONFIG_BPF_SYSCALL=y
+CONFIG_DEBUG_INFO_BTF=y
 CONFIG_FAIL_FUNCTION=y
 CONFIG_FAULT_INJECTION=y
 CONFIG_FAULT_INJECTION_DEBUG_FS=y
 CONFIG_FUNCTION_ERROR_INJECTION=y
 CONFIG_HUGETLBFS=y
@@ -19,10 +21,11 @@ CONFIG_NET_DEVMEM=y
 CONFIG_NET_IPGRE=y
 CONFIG_NET_IPGRE_DEMUX=y
 CONFIG_NET_IPIP=y
 CONFIG_NETKIT=y
 CONFIG_NET_SCH_INGRESS=y
+CONFIG_PACKET=y
 CONFIG_SYNC_FILE=y
 CONFIG_UDMABUF=y
 CONFIG_USER_NS=y
 CONFIG_VLAN_8021Q=m
 CONFIG_VXLAN=y
diff --git a/tools/testing/selftests/bpf/progs/xdp_features.c 
b/tools/testing/selftests/drivers/net/hw/xdp_features.bpf.c
similarity index 100%
rename from tools/testing/selftests/bpf/progs/xdp_features.c
rename to tools/testing/selftests/drivers/net/hw/xdp_features.bpf.c
diff --git a/tools/testing/selftests/bpf/xdp_features.c 
b/tools/testing/selftests/drivers/net/hw/xdp_features.c
similarity index 99%
rename from tools/testing/selftests/bpf/xdp_features.c
rename to tools/testing/selftests/drivers/net/hw/xdp_features.c
index 521c0e24a27ca..6aa5ac902448a 100644
--- a/tools/testing/selftests/bpf/xdp_features.c
+++ b/tools/testing/selftests/drivers/net/hw/xdp_features.c
@@ -27,10 +27,11 @@
 #include <unistd.h>
 #include <arpa/inet.h>
 #include <bpf/bpf.h>
 #include <bpf/libbpf.h>
 
+#include "ksft.h"
 #include "xdp_features.skel.h"
 #include "xdp_features.h"
 
 #define RED(str)       "\033[0;31m" str "\033[0m"
 #define GREEN(str)     "\033[0;32m" str "\033[0m"
@@ -478,10 +479,11 @@ static int dut_run(struct xdp_features *skel)
        if (sockfd < 0) {
                fprintf(stderr,
                        "Failed creating control socket on device %s\n", 
env.ifname);
                return -errno;
        }
+       ksft_ready();
 
        /* Allow remote startup and BPF loading on slow test machines. */
        ctrl_sockfd = -1;
        for (attempt = 0; attempt < 20 && !should_exit(); attempt++) {
                addrlen = sizeof(ctrl_addr);
diff --git a/tools/testing/selftests/bpf/xdp_features.h 
b/tools/testing/selftests/drivers/net/hw/xdp_features.h
similarity index 100%
rename from tools/testing/selftests/bpf/xdp_features.h
rename to tools/testing/selftests/drivers/net/hw/xdp_features.h
diff --git a/tools/testing/selftests/drivers/net/hw/xdp_features.py 
b/tools/testing/selftests/drivers/net/hw/xdp_features.py
new file mode 100755
index 0000000000000..1d59772b2a4d0
--- /dev/null
+++ b/tools/testing/selftests/drivers/net/hw/xdp_features.py
@@ -0,0 +1,137 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+
+"""Check that a device's advertised XDP features match its behavior."""
+
+import ipaddress
+from pathlib import Path
+import re
+import shlex
+
+from lib.py import bkg, cmd, ip, ksft_eq, ksft_exit, ksft_run, NetDrvEpEnv
+from lib.py import KsftFailEx, KsftSkipEx
+
+
+ANSI_ESCAPE = re.compile(r"\x1b\[[0-9;]*m")
+RESULT = re.compile(
+    r"Feature .*: \[(NOT )?DETECTED\]\[(NOT )?ADVERTISED\]"
+)
+
+
+def mapped_address(address):
+    """Return an IPv6 or IPv4-mapped IPv6 address for xdp_features."""
+    address = ipaddress.ip_address(address)
+    if address.version == 4:
+        return f"::ffff:{address}"
+    return str(address)
+
+
+def feature_command(binary, feature, dut_addr, tester_addr, ifname,
+                    tester=False):
+    """Build the helper command with the configured endpoint addresses."""
+    args = [str(binary)]
+    if tester:
+        args.append("-t")
+    args += ["-f", feature, "-D", dut_addr]
+    if tester:
+        args += ["-C", dut_addr]
+    args += ["-T", tester_addr, ifname]
+    return shlex.join(args)
+
+
+def run_feature(cfg, feature, ipver):
+    """Run one feature with bounded helper lifetimes and verify its result."""
+    cfg.require_cmd("timeout", local=False, remote=True)
+    probe = cmd(shlex.join([str(cfg.xdp_features), "-q", cfg.ifname]),
+                fail=False)
+    if probe.ret == 4:
+        raise KsftSkipEx(f"{cfg.ifname} does not advertise native XDP")
+    if probe.ret:
+        raise KsftFailEx(f"Unable to query XDP support: {probe}")
+    if feature == "XDP_TX":
+        link = ip(f"-d link show dev {cfg.ifname}", json=True)[0]
+        if link.get("linkinfo", {}).get("info_kind") == "veth":
+            peer = ip(f"-d link show dev {cfg.remote_ifname}", json=True,
+                      host=cfg.remote)[0]
+            # veth_xdp_flush() only schedules a peer with native XDP.
+            if peer.get("xdp", {}).get("mode") != 1:
+                raise KsftSkipEx("veth XDP_TX needs an existing peer native "
+                                 "XDP program to flush its receive ring")
+    if not cfg.addr_v[ipver]:
+        ipver = "4" if ipver == "6" else "6"
+    cfg.require_ipver(ipver)
+    dut_addr = mapped_address(cfg.addr_v[ipver])
+    tester_addr = mapped_address(cfg.remote_addr_v[ipver])
+
+    dut_cmd = feature_command(cfg.xdp_features, feature, dut_addr,
+                              tester_addr, cfg.ifname)
+    tester_cmd = feature_command(cfg.remote_xdp_features, feature, dut_addr,
+                                 tester_addr, cfg.remote_ifname, tester=True)
+
+    with bkg(dut_cmd, exit_wait=True, ksft_ready=True):
+        # Bound the remote process itself, not just the local SSH wait.
+        result = cmd(f"timeout --signal=TERM --kill-after=5 90 {tester_cmd}",
+                     host=cfg.remote, timeout=100)
+
+    output = ANSI_ESCAPE.sub("", result.stdout)
+    match = RESULT.search(output)
+    if not match:
+        raise KsftFailEx(f"Unable to parse xdp_features output: {output}")
+
+    detected = match.group(1) is None
+    advertised = match.group(2) is None
+    ksft_eq(detected, advertised,
+            comment=f"{feature}: detected and advertised support")
+
+
+def test_xdp_pass(cfg):
+    """Check XDP_PASS detection against the advertised capability."""
+    run_feature(cfg, "XDP_PASS", "6")
+
+
+def test_xdp_drop(cfg):
+    """Check XDP_DROP detection against the advertised capability."""
+    run_feature(cfg, "XDP_DROP", "4")
+
+
+def test_xdp_aborted(cfg):
+    """Check XDP_ABORTED detection against the advertised capability."""
+    run_feature(cfg, "XDP_ABORTED", "6")
+
+
+def test_xdp_tx(cfg):
+    """Check XDP_TX detection against the advertised capability."""
+    run_feature(cfg, "XDP_TX", "4")
+
+
+def test_xdp_redirect(cfg):
+    """Check XDP_REDIRECT detection against the advertised capability."""
+    run_feature(cfg, "XDP_REDIRECT", "6")
+
+
+def test_xdp_ndo_xmit(cfg):
+    """Check XDP_NDO_XMIT detection against the advertised capability."""
+    run_feature(cfg, "XDP_NDO_XMIT", "4")
+
+
+def main():
+    """Deploy the helper and run the six feature checks."""
+    with NetDrvEpEnv(__file__, nsim_test=False) as cfg:
+        cfg.xdp_features = (Path(__file__).parent / "xdp_features").resolve()
+        cfg.remote_xdp_features = cfg.remote.deploy(
+            cfg.xdp_features.as_posix()
+        )
+
+        ksft_run([
+            test_xdp_pass,
+            test_xdp_drop,
+            test_xdp_aborted,
+            test_xdp_tx,
+            test_xdp_redirect,
+            test_xdp_ndo_xmit,
+        ], args=(cfg,))
+    ksft_exit()
+
+
+if __name__ == "__main__":
+    main()

-- 
Git-155)



Reply via email to