Netdev List
 help / color / mirror / Atom feed
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


  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