From: Vladimir Vdovin <deliran@verdict.gg>
To: bpf@vger.kernel.org, netdev@vger.kernel.org
Cc: ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org,
martin.lau@linux.dev, sdf@fomichev.me, hawk@kernel.org,
john.fastabend@gmail.com, kuba@kernel.org,
Vladimir Vdovin <deliran@verdict.gg>
Subject: [RFC PATCH bpf-next v1 2/7] selftests/bpf: add test for bpf_xdp_assert_rx_csum over cpumap
Date: Tue, 30 Jun 2026 22:15:05 +0300 [thread overview]
Message-ID: <20260630191510.81402-3-deliran@verdict.gg> (raw)
In-Reply-To: <20260630191510.81402-1-deliran@verdict.gg>
Drive a frame through a native-XDP veth into a cpumap redirect and
observe, via fexit on __xdp_build_skb_from_frame(), that the rebuilt skb
is CHECKSUM_UNNECESSARY when the program called bpf_xdp_assert_rx_csum()
and CHECKSUM_NONE otherwise. fexit is used because cpumap GRO would
otherwise normalize ip_summed before any later hook can observe it.
Signed-off-by: Vladimir Vdovin <deliran@verdict.gg>
---
.../bpf/prog_tests/xdp_cpumap_rx_csum.c | 150 ++++++++++++++++++
.../selftests/bpf/progs/bpf_tracing_net.h | 1 +
.../bpf/progs/test_xdp_cpumap_rx_csum.c | 51 ++++++
3 files changed, 202 insertions(+)
create mode 100644 tools/testing/selftests/bpf/prog_tests/xdp_cpumap_rx_csum.c
create mode 100644 tools/testing/selftests/bpf/progs/test_xdp_cpumap_rx_csum.c
diff --git a/tools/testing/selftests/bpf/prog_tests/xdp_cpumap_rx_csum.c b/tools/testing/selftests/bpf/prog_tests/xdp_cpumap_rx_csum.c
new file mode 100644
index 000000000000..2def92fe1111
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/xdp_cpumap_rx_csum.c
@@ -0,0 +1,150 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <net/if.h>
+#include <linux/if_ether.h>
+#include <linux/if_link.h>
+#include <linux/if_packet.h>
+#include <linux/ipv6.h>
+#include <netinet/in.h>
+#include <netinet/udp.h>
+#include <sys/socket.h>
+
+#include "test_progs.h"
+#include "network_helpers.h"
+#include <bpf/bpf_endian.h>
+#include "test_xdp_cpumap_rx_csum.skel.h"
+
+#define TEST_NS "xdp_cm_csum_ns"
+#define UDP_TEST_PORT 7777
+
+/* Kernel skb->ip_summed values, not exported to userspace headers. */
+#define CHECKSUM_NONE 0
+#define CHECKSUM_UNNECESSARY 1
+
+struct udp_pkt {
+ struct ethhdr eth;
+ struct ipv6hdr iph;
+ struct udphdr udp;
+ __u8 payload[16];
+} __packed;
+
+static struct udp_pkt pkt = {
+ .eth.h_proto = __bpf_constant_htons(ETH_P_IPV6),
+ .eth.h_dest = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ .eth.h_source = {0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb},
+ .iph.version = 6,
+ .iph.nexthdr = IPPROTO_UDP,
+ .iph.payload_len = __bpf_constant_htons(sizeof(struct udphdr) + 16),
+ .iph.hop_limit = 64,
+ .udp.source = __bpf_constant_htons(1),
+ .udp.dest = __bpf_constant_htons(UDP_TEST_PORT),
+ .udp.len = __bpf_constant_htons(sizeof(struct udphdr) + 16),
+};
+
+/* Inject one frame on veth0; it is received on veth1 where native XDP
+ * redirects it into the cpumap. Report the ip_summed the rebuilt skb carried.
+ */
+static int inject_and_observe(struct test_xdp_cpumap_rx_csum *skel, int sfd,
+ int ifindex_src, bool assert_csum, int *ip_summed)
+{
+ struct sockaddr_ll sll = {
+ .sll_family = AF_PACKET,
+ .sll_ifindex = ifindex_src,
+ .sll_halen = 0,
+ };
+ int i, n;
+
+ skel->bss->assert_csum = assert_csum;
+ skel->bss->seen = false;
+ skel->data->observed_ip_summed = -1;
+
+ n = sendto(sfd, &pkt, sizeof(pkt), 0, (void *)&sll, sizeof(sll));
+ if (!ASSERT_EQ(n, sizeof(pkt), "sendto"))
+ return -1;
+
+ /* The skb is built asynchronously by the cpumap kthread. */
+ for (i = 0; i < 20 && !skel->bss->seen; i++)
+ usleep(50000);
+
+ if (!ASSERT_TRUE(skel->bss->seen, "skb built from frame"))
+ return -1;
+
+ *ip_summed = skel->data->observed_ip_summed;
+ return 0;
+}
+
+void test_xdp_cpumap_rx_csum(void)
+{
+ struct test_xdp_cpumap_rx_csum *skel = NULL;
+ struct bpf_cpumap_val val = { .qsize = 192 };
+ struct bpf_link *fexit_link = NULL;
+ struct nstoken *nstoken = NULL;
+ int err, map_fd, ifindex_dst = 0, ifindex_src, sfd = -1, ip_summed;
+ bool xdp_attached = false;
+ __u32 idx = 0;
+
+ SYS(out, "ip netns add %s", TEST_NS);
+ nstoken = open_netns(TEST_NS);
+ if (!ASSERT_OK_PTR(nstoken, "open_netns"))
+ goto out;
+
+ /* veth pair: a frame TX'd on veth0 is RX'd on veth1. */
+ SYS(out, "ip link add veth0 type veth peer name veth1");
+ SYS(out, "ip link set veth0 up");
+ SYS(out, "ip link set veth1 up");
+
+ skel = test_xdp_cpumap_rx_csum__open_and_load();
+ if (!ASSERT_OK_PTR(skel, "skel open_and_load"))
+ goto out;
+
+ /* cpumap entry without a program: a plain redirect that forces the
+ * frame->skb conversion in __xdp_build_skb_from_frame().
+ */
+ map_fd = bpf_map__fd(skel->maps.cpu_map);
+ err = bpf_map_update_elem(map_fd, &idx, &val, 0);
+ if (!ASSERT_OK(err, "cpumap update"))
+ goto out;
+
+ ifindex_dst = if_nametoindex("veth1");
+ ifindex_src = if_nametoindex("veth0");
+ if (!ASSERT_GT(ifindex_dst, 0, "veth1 ifindex") ||
+ !ASSERT_GT(ifindex_src, 0, "veth0 ifindex"))
+ goto out;
+
+ /* Native XDP so the redirect goes through xdp_convert_buff_to_frame(),
+ * which propagates the rx-csum flag into the frame. Generic mode would
+ * redirect a ready-made skb and never hit our code path.
+ */
+ err = bpf_xdp_attach(ifindex_dst, bpf_program__fd(skel->progs.xdp_redir),
+ XDP_FLAGS_DRV_MODE, NULL);
+ if (!ASSERT_OK(err, "attach native xdp"))
+ goto out;
+ xdp_attached = true;
+
+ fexit_link = bpf_program__attach(skel->progs.on_build);
+ if (!ASSERT_OK_PTR(fexit_link, "attach fexit"))
+ goto out;
+
+ sfd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
+ if (!ASSERT_GE(sfd, 0, "AF_PACKET socket"))
+ goto out;
+
+ /* Program asserts the checksum -> CHECKSUM_UNNECESSARY. */
+ if (!inject_and_observe(skel, sfd, ifindex_src, true, &ip_summed))
+ ASSERT_EQ(ip_summed, CHECKSUM_UNNECESSARY,
+ "ip_summed marked unnecessary");
+
+ /* No assertion -> skb is left CHECKSUM_NONE for the stack to validate. */
+ if (!inject_and_observe(skel, sfd, ifindex_src, false, &ip_summed))
+ ASSERT_EQ(ip_summed, CHECKSUM_NONE, "ip_summed left none");
+
+out:
+ if (sfd >= 0)
+ close(sfd);
+ bpf_link__destroy(fexit_link);
+ if (xdp_attached)
+ bpf_xdp_detach(ifindex_dst, XDP_FLAGS_DRV_MODE, NULL);
+ test_xdp_cpumap_rx_csum__destroy(skel);
+ if (nstoken)
+ close_netns(nstoken);
+ SYS_NOFAIL("ip netns del %s", TEST_NS);
+}
diff --git a/tools/testing/selftests/bpf/progs/bpf_tracing_net.h b/tools/testing/selftests/bpf/progs/bpf_tracing_net.h
index d8dacef37c16..c3a0b2696035 100644
--- a/tools/testing/selftests/bpf/progs/bpf_tracing_net.h
+++ b/tools/testing/selftests/bpf/progs/bpf_tracing_net.h
@@ -87,6 +87,7 @@
#define TCPOLEN_SACK_PERM 2
#define CHECKSUM_NONE 0
+#define CHECKSUM_UNNECESSARY 1
#define CHECKSUM_PARTIAL 3
#define IFNAMSIZ 16
diff --git a/tools/testing/selftests/bpf/progs/test_xdp_cpumap_rx_csum.c b/tools/testing/selftests/bpf/progs/test_xdp_cpumap_rx_csum.c
new file mode 100644
index 000000000000..86c691887d25
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/test_xdp_cpumap_rx_csum.c
@@ -0,0 +1,51 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "vmlinux.h"
+#include "bpf_tracing_net.h"
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+#include <bpf/bpf_endian.h>
+
+extern int bpf_xdp_assert_rx_csum(struct xdp_md *ctx) __ksym;
+
+struct {
+ __uint(type, BPF_MAP_TYPE_CPUMAP);
+ __uint(key_size, sizeof(__u32));
+ __uint(value_size, sizeof(struct bpf_cpumap_val));
+ __uint(max_entries, 1);
+} cpu_map SEC(".maps");
+
+/* Set from userspace before injecting each packet. */
+bool assert_csum = false;
+
+/* Filled in by the fexit program when the cpumap skb is built. */
+bool seen = false;
+int observed_ip_summed = -1;
+
+SEC("xdp")
+int xdp_redir(struct xdp_md *ctx)
+{
+ /* Assert the L4 checksum so the skb built on the cpumap redirect
+ * path is marked CHECKSUM_UNNECESSARY instead of validated in software.
+ */
+ if (assert_csum)
+ bpf_xdp_assert_rx_csum(ctx);
+
+ return bpf_redirect_map(&cpu_map, 0, 0);
+}
+
+/* Observe ip_summed exactly as __xdp_build_skb_from_frame() leaves it, before
+ * GRO in the cpumap kthread can normalize it. tc-ingress would be too late:
+ * GRO software-validates a CHECKSUM_NONE skb and marks it UNNECESSARY anyway.
+ */
+SEC("fexit/__xdp_build_skb_from_frame")
+int BPF_PROG(on_build, struct xdp_frame *xdpf, struct sk_buff *skb,
+ struct net_device *dev, struct sk_buff *ret)
+{
+ if (ret && ret->protocol == bpf_htons(ETH_P_IPV6)) {
+ observed_ip_summed = ret->ip_summed;
+ seen = true;
+ }
+ return 0;
+}
+
+char _license[] SEC("license") = "GPL";
--
2.47.0
next prev parent reply other threads:[~2026-06-30 19:17 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-30 19:15 [RFC PATCH bpf-next v1 0/7] xdp: RX checksum metadata hint and checksum assertion over redirect Vladimir Vdovin
2026-06-30 19:15 ` [RFC PATCH bpf-next v1 1/7] xdp: let XDP programs assert the RX checksum " Vladimir Vdovin
2026-06-30 19:15 ` Vladimir Vdovin [this message]
2026-06-30 19:15 ` [RFC PATCH bpf-next v1 3/7] xdp: add bpf_xdp_metadata_rx_csum() RX metadata kfunc Vladimir Vdovin
2026-06-30 19:15 ` [RFC PATCH bpf-next v1 4/7] net/mlx5e: support the rx_csum XDP metadata hint Vladimir Vdovin
2026-06-30 19:15 ` [RFC PATCH bpf-next v1 5/7] ice: " Vladimir Vdovin
2026-06-30 19:15 ` [RFC PATCH bpf-next v1 6/7] veth: " Vladimir Vdovin
2026-06-30 19:15 ` [RFC PATCH bpf-next v1 7/7] selftests/bpf: cover bpf_xdp_metadata_rx_csum in xdp_metadata Vladimir Vdovin
2026-06-30 21:18 ` [RFC PATCH bpf-next v1 0/7] xdp: RX checksum metadata hint and checksum assertion over redirect Stanislav Fomichev
2026-06-30 22:16 ` Lorenzo Bianconi
2026-07-01 17:10 ` Vladimir Vdovin
2026-07-02 14:52 ` Lorenzo Bianconi
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260630191510.81402-3-deliran@verdict.gg \
--to=deliran@verdict.gg \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=daniel@iogearbox.net \
--cc=hawk@kernel.org \
--cc=john.fastabend@gmail.com \
--cc=kuba@kernel.org \
--cc=martin.lau@linux.dev \
--cc=netdev@vger.kernel.org \
--cc=sdf@fomichev.me \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox