[Intel-wired-lan] [PATCH bpf-next v5 8/8] selftests: net: add test for XDP_PASS skb checksum invalidation
Lorenzo Bianconi
lorenzo at kernel.org
Wed Jul 15 21:39:48 UTC 2026
Add a test that verifies skb->ip_summed is set to CHECKSUM_NONE
when a device running in XDP mode creates an skb from a xdp_buff
if the attached ebpf program returns an XDP_PASS.
The test attaches an XDP program returning XDP_PASS, and a TC
ingress program that runs the bpf_skb_rx_checksum() kfunc to
inspect the resulting skb. After XDP_PASS the driver must invalidate
any previously computed hardware RX checksum since XDP may have
modified the packet data.
The BPF program counts packets per checksum type in a map, and the
test runner verifies that after sending traffic the CHECKSUM_NONE
counter is non-zero while CHECKSUM_UNNECESSARY and CHECKSUM_COMPLETE
counters are zero.
Signed-off-by: Lorenzo Bianconi <lorenzo at kernel.org>
---
Documentation/networking/xdp-rx-metadata.rst | 5 ++
.../selftests/drivers/net/hw/xdp_metadata.py | 55 +++++++++++++++-
.../selftests/net/lib/skb_metadata_csum.bpf.c | 73 ++++++++++++++++++++++
3 files changed, 132 insertions(+), 1 deletion(-)
diff --git a/Documentation/networking/xdp-rx-metadata.rst b/Documentation/networking/xdp-rx-metadata.rst
index 93918b3769a3..7434ac98242a 100644
--- a/Documentation/networking/xdp-rx-metadata.rst
+++ b/Documentation/networking/xdp-rx-metadata.rst
@@ -90,6 +90,11 @@ conversion, and the XDP metadata is not used by the kernel when building
``skbs``. However, TC-BPF programs can access the XDP metadata area using
the ``data_meta`` pointer.
+If a driver is running in XDP mode, any existing hardware RX checksum
+(``CHECKSUM_UNNECESSARY`` or ``CHECKSUM_COMPLETE``) must be invalidated
+by setting ``skb->ip_summed`` to ``CHECKSUM_NONE`` before passing the
+skb to the kernel, since XDP may have modified the packet data.
+
In the future, we'd like to support a case where an XDP program
can override some of the metadata used for building ``skbs``.
diff --git a/tools/testing/selftests/drivers/net/hw/xdp_metadata.py b/tools/testing/selftests/drivers/net/hw/xdp_metadata.py
old mode 100644
new mode 100755
index 33a1985356d9..8ccd34b776b4
--- a/tools/testing/selftests/drivers/net/hw/xdp_metadata.py
+++ b/tools/testing/selftests/drivers/net/hw/xdp_metadata.py
@@ -8,12 +8,13 @@ These tests load device-bound XDP programs from xdp_metadata.bpf.o
that call metadata kfuncs, send traffic, and verify the extracted
metadata via BPF maps.
"""
+import json
from lib.py import ksft_run, ksft_eq, ksft_exit, ksft_ge, ksft_ne, ksft_pr
from lib.py import KsftNamedVariant, ksft_variants
from lib.py import CmdExitFailure, KsftSkipEx, NetDrvEpEnv
from lib.py import NetdevFamily
from lib.py import bkg, cmd, rand_port, wait_port_listen
-from lib.py import ip, bpftool, defer
+from lib.py import ip, bpftool, defer, ethtool
from lib.py import bpf_map_set, bpf_map_dump, bpf_prog_map_ids
@@ -130,6 +131,57 @@ def test_xdp_rss_hash(cfg, proto):
f"RSS hash type should include L4 for {proto.upper()} traffic")
+def test_xdp_pass_rx_csum(cfg):
+ """Test xdp_pass sets CHECKSUM_NONE on the resulting skb.
+
+ Attaches an XDP program that returns XDP_PASS and a TC ingress
+ program that checks skb->ip_summed via bpf_skb_rx_checksum().
+ Verifies the value is CHECKSUM_NONE.
+ """
+
+ bpf_obj = cfg.net_lib_dir / "skb_metadata_csum.bpf.o"
+ xdp_obj = cfg.net_lib_dir / "xdp_dummy.bpf.o"
+
+ # GRO may overwrite skb->ip_summed after the driver sets it,
+ # so disable it to preserve the checksum set by the driver.
+ ethtool(f"-K {cfg.ifname} gro off")
+ defer(ethtool, f"-K {cfg.ifname} gro on")
+ ip(f"link set dev {cfg.ifname} xdp obj {xdp_obj} sec xdp")
+ defer(ip, f"link set dev {cfg.ifname} xdp off")
+
+ qdiscs = json.loads(cmd(f"tc -j qdisc show dev {cfg.ifname}").stdout)
+ if not any(q['kind'] == 'clsact' for q in qdiscs):
+ cmd(f"tc qdisc add dev {cfg.ifname} clsact")
+ defer(cmd, f"tc qdisc del dev {cfg.ifname} clsact")
+ cmd(f"tc filter add dev {cfg.ifname} ingress bpf da obj {bpf_obj} sec tc")
+
+ progs = bpftool("prog list", json=True)
+ tc_prog_id = None
+ for p in progs:
+ if p.get("name") == "tc_check_csum":
+ tc_prog_id = p["id"]
+ break
+
+ if tc_prog_id is None:
+ raise KsftSkipEx("Could not find tc_check_csum BPF program")
+
+ maps = bpf_prog_map_ids(tc_prog_id)
+ csum_map_id = maps.get("map_csum_result")
+ if csum_map_id is None:
+ raise KsftSkipEx("Could not find map_csum_result map")
+
+ for _ in range(10):
+ _send_probe(cfg, 12345, proto="udp")
+
+ result = bpf_map_dump(csum_map_id)
+ csum_none = result.get(0, 0)
+ csum_unnecessary = result.get(1, 0)
+ csum_complete = result.get(2, 0)
+ ksft_ge(csum_none, 1, "skb->ip_summed should be CHECKSUM_NONE after XDP_PASS")
+ ksft_eq(csum_unnecessary, 0, "CHECKSUM_UNNECESSARY should not be set")
+ ksft_eq(csum_complete, 0, "CHECKSUM_COMPLETE should not be set")
+
+
def main():
"""Run XDP metadata kfunc tests against a real device."""
with NetDrvEpEnv(__file__) as cfg:
@@ -137,6 +189,7 @@ def main():
ksft_run(
[
test_xdp_rss_hash,
+ test_xdp_pass_rx_csum,
],
args=(cfg,))
ksft_exit()
diff --git a/tools/testing/selftests/net/lib/skb_metadata_csum.bpf.c b/tools/testing/selftests/net/lib/skb_metadata_csum.bpf.c
new file mode 100644
index 000000000000..6a953c4acfe7
--- /dev/null
+++ b/tools/testing/selftests/net/lib/skb_metadata_csum.bpf.c
@@ -0,0 +1,73 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/bpf.h>
+#include <linux/if_ether.h>
+#include <linux/in.h>
+#include <linux/ipv6.h>
+#include <linux/pkt_cls.h>
+#include <linux/udp.h>
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_endian.h>
+
+#define UDP_PORT 12345
+
+enum skb_csum {
+ SKB_CSUM_NONE = 0,
+ SKB_CSUM_UNNECESSARY = 1,
+ SKB_CSUM_COMPLETE = 2,
+ SKB_CSUM_PARTIAL = 3,
+};
+
+struct {
+ __uint(type, BPF_MAP_TYPE_ARRAY);
+ __uint(max_entries, SKB_CSUM_PARTIAL);
+ __type(key, __u32);
+ __type(value, __u64);
+} map_csum_result SEC(".maps");
+
+extern int bpf_skb_rx_checksum(struct __sk_buff *skb, __u32 *ip_summed,
+ __u32 *csum_meta) __ksym;
+
+SEC("tc")
+int tc_check_csum(struct __sk_buff *skb)
+{
+ void *data_end = (void *)(long)skb->data_end;
+ void *data = (void *)(long)skb->data;
+ __u32 ip_summed, csum_meta;
+ struct ethhdr *eth = data;
+ struct ipv6hdr *ip6;
+ struct udphdr *udp;
+
+ if ((void *)(eth + 1) > data_end)
+ return TC_ACT_OK;
+
+ if (eth->h_proto != bpf_htons(ETH_P_IPV6))
+ return TC_ACT_OK;
+
+ ip6 = (void *)(eth + 1);
+ if ((void *)(ip6 + 1) > data_end)
+ return TC_ACT_OK;
+
+ if (ip6->nexthdr != IPPROTO_UDP)
+ return TC_ACT_OK;
+
+ udp = (void *)(ip6 + 1);
+ if ((void *)(udp + 1) > data_end)
+ return TC_ACT_OK;
+
+ if (udp->dest != bpf_htons(UDP_PORT))
+ return TC_ACT_OK;
+
+ bpf_skb_rx_checksum(skb, &ip_summed, &csum_meta);
+ if (ip_summed < SKB_CSUM_PARTIAL) {
+ __u64 *cnt;
+
+ cnt = bpf_map_lookup_elem(&map_csum_result, &ip_summed);
+ if (cnt)
+ __sync_fetch_and_add(cnt, 1);
+ }
+
+ return TC_ACT_OK;
+}
+
+char _license[] SEC("license") = "GPL";
--
2.55.0
More information about the Intel-wired-lan
mailing list