Linux RDMA and InfiniBand development
 help / color / mirror / Atom feed
* [PATCH 0/2] selftests/rdma: Add an eBPF-based testcase using netkit for RXE
@ 2026-10-19  4:19 Zhu Yanjun
  2026-10-19  4:19 ` [PATCH 1/2] selftests/rdma: Introduce eBPF kernel and user dump utilities " Zhu Yanjun
                   ` (2 more replies)
  0 siblings, 3 replies; 6+ messages in thread
From: Zhu Yanjun @ 2026-10-19  4:19 UTC (permalink / raw)
  To: jgg, leon, shuah, zyjzyj2000, linux-rdma, linux-kselftest; +Cc: Zhu Yanjun

Add a new self-test case to verify RXE (Soft-RoCE) traffic using
netkit virtual interfaces and an eBPF tcx ingress program to dump
and parse RDMA transport headers (BTH, etc.).

Zhu Yanjun (2):
  selftests/rdma: Introduce eBPF kernel and user dump utilities for RXE
  selftests/rdma: Add netkit-based automated test script for RXE

 tools/testing/selftests/rdma/Makefile         |   23 +-
 tools/testing/selftests/rdma/config           |    3 +
 .../selftests/rdma/rxe_pkg_kernel_dump.c      |   62 +
 .../testing/selftests/rdma/rxe_pkg_netkit.sh  |   63 +
 .../selftests/rdma/rxe_pkg_user_dump.c        | 1275 +++++++++++++++++
 5 files changed, 1425 insertions(+), 1 deletion(-)
 create mode 100644 tools/testing/selftests/rdma/rxe_pkg_kernel_dump.c
 create mode 100755 tools/testing/selftests/rdma/rxe_pkg_netkit.sh
 create mode 100644 tools/testing/selftests/rdma/rxe_pkg_user_dump.c

-- 
2.54.0


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 1/2] selftests/rdma: Introduce eBPF kernel and user dump utilities for RXE
  2026-10-19  4:19 [PATCH 0/2] selftests/rdma: Add an eBPF-based testcase using netkit for RXE Zhu Yanjun
@ 2026-10-19  4:19 ` Zhu Yanjun
  2026-10-19  4:35   ` sashiko-bot
  2026-10-19  4:19 ` [PATCH 2/2] selftests/rdma: Add netkit-based automated test script " Zhu Yanjun
  2026-10-19  4:23 ` [PATCH 0/2] selftests/rdma: Add an eBPF-based testcase using netkit " Zhu Yanjun
  2 siblings, 1 reply; 6+ messages in thread
From: Zhu Yanjun @ 2026-10-19  4:19 UTC (permalink / raw)
  To: jgg, leon, shuah, zyjzyj2000, linux-rdma, linux-kselftest; +Cc: Zhu Yanjun

Add eBPF-based kernel and user-space packet dump utilities along with a
netkit test script (rxe_pkg_netkit.sh) to tools/testing/selftests/rdma/.
This enables capturing and parsing Soft-RoCE (RXE) traffic over netkit
devices for testing purposes.

Signed-off-by: Zhu Yanjun <yanjun.zhu@linux.dev>
---
 tools/testing/selftests/rdma/Makefile         |   23 +-
 tools/testing/selftests/rdma/config           |    3 +
 .../selftests/rdma/rxe_pkg_kernel_dump.c      |   67 +
 .../selftests/rdma/rxe_pkg_user_dump.c        | 1294 +++++++++++++++++
 4 files changed, 1386 insertions(+), 1 deletion(-)
 create mode 100644 tools/testing/selftests/rdma/rxe_pkg_kernel_dump.c
 create mode 100644 tools/testing/selftests/rdma/rxe_pkg_user_dump.c

diff --git a/tools/testing/selftests/rdma/Makefile b/tools/testing/selftests/rdma/Makefile
index a91c14c45006..6e099cabf90b 100644
--- a/tools/testing/selftests/rdma/Makefile
+++ b/tools/testing/selftests/rdma/Makefile
@@ -4,6 +4,27 @@ TEST_PROGS := rxe_rping_between_netns.sh \
 		rxe_socket_with_netns.sh \
 		rxe_test_NETDEV_UNREGISTER.sh \
 		rxe_sent_rcvd_bytes.sh \
-		rxe_netns_names.sh
+		rxe_netns_names.sh \
+		rxe_pkg_netkit.sh
 
+TEST_GEN_PROGS_EXTENDED := rxe_netkit_dump
 include ../lib.mk
+
+CLANG	?= clang
+LLC	?= llc
+BPFTOOL	?= bpftool
+
+INCLUDES	:= -I../../../../usr/include -I../../../../include/uapi -I../bpf
+CFLAGS		+= -g -Wall $(INCLUDES)
+LDFLAGS		+= -lbpf -lelf -lz
+
+$(OUTPUT)/rxe_netkit_dump: rxe_pkg_user_dump.c $(OUTPUT)/rxe_pkg_kernel.skel.h
+	$(CC) $(CFLAGS) $< $(LDFLAGS) -o $@
+
+$(OUTPUT)/rxe_pkg_kernel_dump.o: rxe_pkg_kernel_dump.c
+	$(CLANG) -g -O2 -target bpf -D__TARGET_ARCH_x86 $(INCLUDES) -c $< -o $@
+
+$(OUTPUT)/rxe_pkg_kernel.skel.h: $(OUTPUT)/rxe_pkg_kernel_dump.o
+	$(BPFTOOL) gen skeleton $< > $@
+
+EXTRA_CLEAN := $(OUTPUT)/rxe_pkg_kernel_dump.o $(OUTPUT)/rxe_pkg_kernel.skel.h
diff --git a/tools/testing/selftests/rdma/config b/tools/testing/selftests/rdma/config
index e1ff54ec0f57..a98ffa95b0b7 100644
--- a/tools/testing/selftests/rdma/config
+++ b/tools/testing/selftests/rdma/config
@@ -3,3 +3,6 @@ CONFIG_VETH
 CONFIG_DUMMY
 CONFIG_NET_NS
 CONFIG_RDMA_RXE
+CONFIG_BPF=y
+CONFIG_BPF_SYSCALL=y
+CONFIG_NETKIT=y
diff --git a/tools/testing/selftests/rdma/rxe_pkg_kernel_dump.c b/tools/testing/selftests/rdma/rxe_pkg_kernel_dump.c
new file mode 100644
index 000000000000..54dfc8028e90
--- /dev/null
+++ b/tools/testing/selftests/rdma/rxe_pkg_kernel_dump.c
@@ -0,0 +1,67 @@
+// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
+/*
+ * Copyright (c) 2026 Zhu Yanjun <yanjun.zhu@linux.dev>
+ */
+
+#include <linux/bpf.h>
+#include <bpf/bpf_helpers.h>
+#include <linux/if_ether.h>
+#include <linux/ip.h>
+#include <arpa/inet.h>
+
+struct {
+	__uint(type, BPF_MAP_TYPE_RINGBUF);
+	__uint(max_entries, 256 * 1024);/* Shared by all CPUs */
+} rb SEC(".maps");
+
+SEC("tcx/ingress")
+int handle_netkit_ingress(struct __sk_buff *skb)
+{
+	/* Get the header pointer of skb */
+	void *data_end = (void *)(long)skb->data_end;
+	void *data = (void *)(long)skb->data;
+
+	struct ethhdr *eth = data;
+
+	/* Verify the data */
+	if (data + sizeof(*eth) > data_end)
+		return BPF_OK;
+
+	/* Check if it is IP skb */
+	if (eth->h_proto == htons(ETH_P_IP)) {
+		struct iphdr *iph = data + sizeof(*eth);
+
+		if ((void *)iph + sizeof(*iph) <= data_end) {
+			bpf_printk("Got IP packet from netkit ingress, %d\n", skb->len);
+
+			__u32 len = skb->len & 0x1FF;
+
+			if (len == 0) {
+				bpf_printk("len is 0\n");
+				return BPF_OK;
+			}
+
+			void *reserve = bpf_ringbuf_reserve(&rb, 512, 0);
+
+			if (!reserve) {
+				bpf_printk("bpf_ringbuf_reserve fails\n");
+				return BPF_OK;
+			}
+
+			if (bpf_skb_load_bytes(skb, 0, reserve, len) < 0) {
+				bpf_ringbuf_discard(reserve, 0);
+				bpf_printk("bpf_skb_load_bytes fails\n");
+				return BPF_OK;
+			}
+
+			bpf_ringbuf_submit(reserve, 0);
+		}
+	}
+
+	/* BPF_OK:	Pass
+	 * BPF_DROP:	Drop skb
+	 */
+	return BPF_OK;
+}
+
+char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/rdma/rxe_pkg_user_dump.c b/tools/testing/selftests/rdma/rxe_pkg_user_dump.c
new file mode 100644
index 000000000000..39747ae1e06a
--- /dev/null
+++ b/tools/testing/selftests/rdma/rxe_pkg_user_dump.c
@@ -0,0 +1,1294 @@
+// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
+/*
+ * Copyright (c) 2026 Zhu Yanjun <yanjun.zhu@linux.dev>
+ */
+
+#include <stdio.h>
+#include <sys/resource.h>
+#include <net/if.h>
+#include "rxe_pkg_kernel.skel.h"
+
+#include <linux/ip.h>
+#include <linux/udp.h>
+#include <ctype.h>
+
+#include <net/ethernet.h>
+#include <arpa/inet.h>
+
+typedef unsigned char __u8;
+typedef unsigned short __u16;
+typedef unsigned int __u32;
+typedef unsigned long long __u64;
+typedef __u8 u8;
+typedef __u16 u16;
+typedef __u32 u32;
+typedef __u64 u64;
+
+struct rxe_bth {
+	__u8		opcode;
+	__u8		flags;
+	__be16		pkey;
+	__be32		qpn;
+	__be32		apsn;
+};
+
+struct rxe_immdt {
+	__be32		imm;
+};
+
+struct rxe_reth {
+	__be64		va;
+	__be32		rkey;
+	__be32		len;
+};
+
+struct rxe_aeth {
+	__be32		smsn;
+};
+
+/******************************************************************************
+ * Atomic Ack Extended Transport Header
+ ******************************************************************************/
+struct rxe_atmack {
+	__be64		orig;
+};
+
+/******************************************************************************
+ * Invalidate Extended Transport Header
+ ******************************************************************************/
+struct rxe_ieth {
+	__be32		rkey;
+};
+
+/******************************************************************************
+ * Datagram Extended Transport Header
+ ******************************************************************************/
+struct rxe_deth {
+	__be32	qkey;
+	__be32	sqp;
+};
+
+#define IB_OPCODE(transport, op) \
+	IB_OPCODE_ ## transport ## _ ## op = \
+		IB_OPCODE_ ## transport + IB_OPCODE_ ## op
+
+enum {
+	/* transport types -- just used to define real constants */
+	IB_OPCODE_RC                                = 0x00,
+	IB_OPCODE_UC                                = 0x20,
+	IB_OPCODE_RD                                = 0x40,
+	IB_OPCODE_UD                                = 0x60,
+	/* per IBTA 1.3 vol 1 Table 38, A10.3.2 */
+	IB_OPCODE_CNP                               = 0x80,
+	/* Manufacturer specific */
+	IB_OPCODE_MSP                               = 0xe0,
+
+	/* operations -- just used to define real constants */
+	IB_OPCODE_SEND_FIRST                        = 0x00,
+	IB_OPCODE_SEND_MIDDLE                       = 0x01,
+	IB_OPCODE_SEND_LAST                         = 0x02,
+	IB_OPCODE_SEND_LAST_WITH_IMMEDIATE          = 0x03,
+	IB_OPCODE_SEND_ONLY                         = 0x04,
+	IB_OPCODE_SEND_ONLY_WITH_IMMEDIATE          = 0x05,
+	IB_OPCODE_RDMA_WRITE_FIRST                  = 0x06,
+	IB_OPCODE_RDMA_WRITE_MIDDLE                 = 0x07,
+	IB_OPCODE_RDMA_WRITE_LAST                   = 0x08,
+	IB_OPCODE_RDMA_WRITE_LAST_WITH_IMMEDIATE    = 0x09,
+	IB_OPCODE_RDMA_WRITE_ONLY                   = 0x0a,
+	IB_OPCODE_RDMA_WRITE_ONLY_WITH_IMMEDIATE    = 0x0b,
+	IB_OPCODE_RDMA_READ_REQUEST                 = 0x0c,
+	IB_OPCODE_RDMA_READ_RESPONSE_FIRST          = 0x0d,
+	IB_OPCODE_RDMA_READ_RESPONSE_MIDDLE         = 0x0e,
+	IB_OPCODE_RDMA_READ_RESPONSE_LAST           = 0x0f,
+	IB_OPCODE_RDMA_READ_RESPONSE_ONLY           = 0x10,
+	IB_OPCODE_ACKNOWLEDGE                       = 0x11,
+	IB_OPCODE_ATOMIC_ACKNOWLEDGE                = 0x12,
+	IB_OPCODE_COMPARE_SWAP                      = 0x13,
+	IB_OPCODE_FETCH_ADD                         = 0x14,
+	/* opcode 0x15 is reserved */
+	IB_OPCODE_SEND_LAST_WITH_INVALIDATE         = 0x16,
+	IB_OPCODE_SEND_ONLY_WITH_INVALIDATE         = 0x17,
+
+	/* real constants follow -- see comment about above IB_OPCODE()
+	 * macro for more details
+	 */
+
+	/* RC */
+	IB_OPCODE(RC, SEND_FIRST),
+	IB_OPCODE(RC, SEND_MIDDLE),
+	IB_OPCODE(RC, SEND_LAST),
+	IB_OPCODE(RC, SEND_LAST_WITH_IMMEDIATE),
+	IB_OPCODE(RC, SEND_ONLY),
+	IB_OPCODE(RC, SEND_ONLY_WITH_IMMEDIATE),
+	IB_OPCODE(RC, RDMA_WRITE_FIRST),
+	IB_OPCODE(RC, RDMA_WRITE_MIDDLE),
+	IB_OPCODE(RC, RDMA_WRITE_LAST),
+	IB_OPCODE(RC, RDMA_WRITE_LAST_WITH_IMMEDIATE),
+	IB_OPCODE(RC, RDMA_WRITE_ONLY),
+	IB_OPCODE(RC, RDMA_WRITE_ONLY_WITH_IMMEDIATE),
+	IB_OPCODE(RC, RDMA_READ_REQUEST),
+	IB_OPCODE(RC, RDMA_READ_RESPONSE_FIRST),
+	IB_OPCODE(RC, RDMA_READ_RESPONSE_MIDDLE),
+	IB_OPCODE(RC, RDMA_READ_RESPONSE_LAST),
+	IB_OPCODE(RC, RDMA_READ_RESPONSE_ONLY),
+	IB_OPCODE(RC, ACKNOWLEDGE),
+	IB_OPCODE(RC, ATOMIC_ACKNOWLEDGE),
+	IB_OPCODE(RC, COMPARE_SWAP),
+	IB_OPCODE(RC, FETCH_ADD),
+	IB_OPCODE(RC, SEND_LAST_WITH_INVALIDATE),
+	IB_OPCODE(RC, SEND_ONLY_WITH_INVALIDATE),
+
+	/* UC */
+	IB_OPCODE(UC, SEND_FIRST),
+	IB_OPCODE(UC, SEND_MIDDLE),
+	IB_OPCODE(UC, SEND_LAST),
+	IB_OPCODE(UC, SEND_LAST_WITH_IMMEDIATE),
+	IB_OPCODE(UC, SEND_ONLY),
+	IB_OPCODE(UC, SEND_ONLY_WITH_IMMEDIATE),
+	IB_OPCODE(UC, RDMA_WRITE_FIRST),
+	IB_OPCODE(UC, RDMA_WRITE_MIDDLE),
+	IB_OPCODE(UC, RDMA_WRITE_LAST),
+	IB_OPCODE(UC, RDMA_WRITE_LAST_WITH_IMMEDIATE),
+	IB_OPCODE(UC, RDMA_WRITE_ONLY),
+	IB_OPCODE(UC, RDMA_WRITE_ONLY_WITH_IMMEDIATE),
+
+	/* RD */
+	IB_OPCODE(RD, SEND_FIRST),
+	IB_OPCODE(RD, SEND_MIDDLE),
+	IB_OPCODE(RD, SEND_LAST),
+	IB_OPCODE(RD, SEND_LAST_WITH_IMMEDIATE),
+	IB_OPCODE(RD, SEND_ONLY),
+	IB_OPCODE(RD, SEND_ONLY_WITH_IMMEDIATE),
+	IB_OPCODE(RD, RDMA_WRITE_FIRST),
+	IB_OPCODE(RD, RDMA_WRITE_MIDDLE),
+	IB_OPCODE(RD, RDMA_WRITE_LAST),
+	IB_OPCODE(RD, RDMA_WRITE_LAST_WITH_IMMEDIATE),
+	IB_OPCODE(RD, RDMA_WRITE_ONLY),
+	IB_OPCODE(RD, RDMA_WRITE_ONLY_WITH_IMMEDIATE),
+	IB_OPCODE(RD, RDMA_READ_REQUEST),
+	IB_OPCODE(RD, RDMA_READ_RESPONSE_FIRST),
+	IB_OPCODE(RD, RDMA_READ_RESPONSE_MIDDLE),
+	IB_OPCODE(RD, RDMA_READ_RESPONSE_LAST),
+	IB_OPCODE(RD, RDMA_READ_RESPONSE_ONLY),
+	IB_OPCODE(RD, ACKNOWLEDGE),
+	IB_OPCODE(RD, ATOMIC_ACKNOWLEDGE),
+	IB_OPCODE(RD, COMPARE_SWAP),
+	IB_OPCODE(RD, FETCH_ADD),
+
+	/* UD */
+	IB_OPCODE(UD, SEND_ONLY),
+	IB_OPCODE(UD, SEND_ONLY_WITH_IMMEDIATE)
+};
+
+enum rxe_hdr_type {
+	RXE_LRH,
+	RXE_GRH,
+	RXE_BTH,
+	RXE_RETH,
+	RXE_AETH,
+	RXE_ATMETH,
+	RXE_ATMACK,
+	RXE_IETH,
+	RXE_RDETH,
+	RXE_DETH,
+	RXE_IMMDT,
+	RXE_PAYLOAD,
+	NUM_HDR_TYPES
+};
+
+#define RXE_NUM_OPCODE		256
+
+struct rxe_opcode_info {
+	char			*name;
+	int			length;
+	int			offset[NUM_HDR_TYPES];
+};
+
+/******************************************************************************
+ * Atomic Extended Transport Header
+ ******************************************************************************/
+struct rxe_atmeth {
+	__be64			va;
+	__be32			rkey;
+	__be64			swap_add;
+	__be64			comp;
+} __packed;
+
+/******************************************************************************
+ * Reliable Datagram Extended Transport Header
+ ******************************************************************************/
+struct rxe_rdeth {
+	__be32			een;
+};
+
+enum rxe_hdr_length {
+	RXE_BTH_BYTES		= sizeof(struct rxe_bth),
+	RXE_DETH_BYTES		= sizeof(struct rxe_deth),
+	RXE_IMMDT_BYTES		= sizeof(struct rxe_immdt),
+	RXE_RETH_BYTES		= sizeof(struct rxe_reth),
+	RXE_AETH_BYTES		= sizeof(struct rxe_aeth),
+	RXE_ATMACK_BYTES	= sizeof(struct rxe_atmack),
+	RXE_ATMETH_BYTES	= sizeof(struct rxe_atmeth),
+	RXE_IETH_BYTES		= sizeof(struct rxe_ieth),
+	RXE_RDETH_BYTES		= sizeof(struct rxe_rdeth),
+};
+
+struct rxe_opcode_info rxe_opcode[RXE_NUM_OPCODE] = {
+	[IB_OPCODE_RC_SEND_FIRST]			= {
+		.name	= "IB_OPCODE_RC_SEND_FIRST",
+		.length = RXE_BTH_BYTES,
+		.offset = {
+			[RXE_BTH]	= 0,
+			[RXE_PAYLOAD]	= RXE_BTH_BYTES,
+		}
+	},
+	[IB_OPCODE_RC_SEND_MIDDLE]		= {
+		.name	= "IB_OPCODE_RC_SEND_MIDDLE",
+		.length = RXE_BTH_BYTES,
+		.offset = {
+			[RXE_BTH]	= 0,
+			[RXE_PAYLOAD]	= RXE_BTH_BYTES,
+		}
+	},
+	[IB_OPCODE_RC_SEND_LAST]			= {
+		.name	= "IB_OPCODE_RC_SEND_LAST",
+		.length = RXE_BTH_BYTES,
+		.offset = {
+			[RXE_BTH]	= 0,
+			[RXE_PAYLOAD]	= RXE_BTH_BYTES,
+		}
+	},
+	[IB_OPCODE_RC_SEND_LAST_WITH_IMMEDIATE]		= {
+		.name	= "IB_OPCODE_RC_SEND_LAST_WITH_IMMEDIATE",
+		.length = RXE_BTH_BYTES + RXE_IMMDT_BYTES,
+		.offset = {
+			[RXE_BTH]	= 0,
+			[RXE_IMMDT]	= RXE_BTH_BYTES,
+			[RXE_PAYLOAD]	= RXE_BTH_BYTES +
+					  RXE_IMMDT_BYTES,
+		}
+	},
+	[IB_OPCODE_RC_SEND_ONLY]			= {
+		.name	= "IB_OPCODE_RC_SEND_ONLY",
+		.length = RXE_BTH_BYTES,
+		.offset = {
+			[RXE_BTH]	= 0,
+			[RXE_PAYLOAD]	= RXE_BTH_BYTES,
+		}
+	},
+	[IB_OPCODE_RC_SEND_ONLY_WITH_IMMEDIATE]		= {
+		.name	= "IB_OPCODE_RC_SEND_ONLY_WITH_IMMEDIATE",
+		.length = RXE_BTH_BYTES + RXE_IMMDT_BYTES,
+		.offset = {
+			[RXE_BTH]	= 0,
+			[RXE_IMMDT]	= RXE_BTH_BYTES,
+			[RXE_PAYLOAD]	= RXE_BTH_BYTES +
+					  RXE_IMMDT_BYTES,
+		}
+	},
+	[IB_OPCODE_RC_RDMA_WRITE_FIRST]		= {
+		.name	= "IB_OPCODE_RC_RDMA_WRITE_FIRST",
+		.length = RXE_BTH_BYTES + RXE_RETH_BYTES,
+		.offset = {
+			[RXE_BTH]	= 0,
+			[RXE_RETH]	= RXE_BTH_BYTES,
+			[RXE_PAYLOAD]	= RXE_BTH_BYTES +
+					  RXE_RETH_BYTES,
+		}
+	},
+	[IB_OPCODE_RC_RDMA_WRITE_MIDDLE]		= {
+		.name	= "IB_OPCODE_RC_RDMA_WRITE_MIDDLE",
+		.length = RXE_BTH_BYTES,
+		.offset = {
+			[RXE_BTH]	= 0,
+			[RXE_PAYLOAD]	= RXE_BTH_BYTES,
+		}
+	},
+	[IB_OPCODE_RC_RDMA_WRITE_LAST]			= {
+		.name	= "IB_OPCODE_RC_RDMA_WRITE_LAST",
+		.length = RXE_BTH_BYTES,
+		.offset = {
+			[RXE_BTH]	= 0,
+			[RXE_PAYLOAD]	= RXE_BTH_BYTES,
+		}
+	},
+	[IB_OPCODE_RC_RDMA_WRITE_LAST_WITH_IMMEDIATE]		= {
+		.name	= "IB_OPCODE_RC_RDMA_WRITE_LAST_WITH_IMMEDIATE",
+		.length = RXE_BTH_BYTES + RXE_IMMDT_BYTES,
+		.offset = {
+			[RXE_BTH]	= 0,
+			[RXE_IMMDT]	= RXE_BTH_BYTES,
+			[RXE_PAYLOAD]	= RXE_BTH_BYTES +
+					  RXE_IMMDT_BYTES,
+		}
+	},
+	[IB_OPCODE_RC_RDMA_WRITE_ONLY]			= {
+		.name	= "IB_OPCODE_RC_RDMA_WRITE_ONLY",
+		.length = RXE_BTH_BYTES + RXE_RETH_BYTES,
+		.offset = {
+			[RXE_BTH]	= 0,
+			[RXE_RETH]	= RXE_BTH_BYTES,
+			[RXE_PAYLOAD]	= RXE_BTH_BYTES +
+					  RXE_RETH_BYTES,
+		}
+	},
+	[IB_OPCODE_RC_RDMA_WRITE_ONLY_WITH_IMMEDIATE]		= {
+		.name	= "IB_OPCODE_RC_RDMA_WRITE_ONLY_WITH_IMMEDIATE",
+		.length = RXE_BTH_BYTES + RXE_IMMDT_BYTES + RXE_RETH_BYTES,
+		.offset = {
+			[RXE_BTH]	= 0,
+			[RXE_RETH]	= RXE_BTH_BYTES,
+			[RXE_IMMDT]	= RXE_BTH_BYTES +
+					  RXE_RETH_BYTES,
+			[RXE_PAYLOAD]	= RXE_BTH_BYTES +
+					  RXE_RETH_BYTES +
+					  RXE_IMMDT_BYTES,
+		}
+	},
+	[IB_OPCODE_RC_RDMA_READ_REQUEST]			= {
+		.name	= "IB_OPCODE_RC_RDMA_READ_REQUEST",
+		.length = RXE_BTH_BYTES + RXE_RETH_BYTES,
+		.offset = {
+			[RXE_BTH]	= 0,
+			[RXE_RETH]	= RXE_BTH_BYTES,
+			[RXE_PAYLOAD]	= RXE_BTH_BYTES +
+					  RXE_RETH_BYTES,
+		}
+	},
+	[IB_OPCODE_RC_RDMA_READ_RESPONSE_FIRST]		= {
+		.name	= "IB_OPCODE_RC_RDMA_READ_RESPONSE_FIRST",
+		.length = RXE_BTH_BYTES + RXE_AETH_BYTES,
+		.offset = {
+			[RXE_BTH]	= 0,
+			[RXE_AETH]	= RXE_BTH_BYTES,
+			[RXE_PAYLOAD]	= RXE_BTH_BYTES +
+					  RXE_AETH_BYTES,
+		}
+	},
+	[IB_OPCODE_RC_RDMA_READ_RESPONSE_MIDDLE]		= {
+		.name	= "IB_OPCODE_RC_RDMA_READ_RESPONSE_MIDDLE",
+		.length = RXE_BTH_BYTES,
+		.offset = {
+			[RXE_BTH]	= 0,
+			[RXE_PAYLOAD]	= RXE_BTH_BYTES,
+		}
+	},
+	[IB_OPCODE_RC_RDMA_READ_RESPONSE_LAST]		= {
+		.name	= "IB_OPCODE_RC_RDMA_READ_RESPONSE_LAST",
+		.length = RXE_BTH_BYTES + RXE_AETH_BYTES,
+		.offset = {
+			[RXE_BTH]	= 0,
+			[RXE_AETH]	= RXE_BTH_BYTES,
+			[RXE_PAYLOAD]	= RXE_BTH_BYTES +
+					  RXE_AETH_BYTES,
+		}
+	},
+	[IB_OPCODE_RC_RDMA_READ_RESPONSE_ONLY]		= {
+		.name	= "IB_OPCODE_RC_RDMA_READ_RESPONSE_ONLY",
+		.length = RXE_BTH_BYTES + RXE_AETH_BYTES,
+		.offset = {
+			[RXE_BTH]	= 0,
+			[RXE_AETH]	= RXE_BTH_BYTES,
+			[RXE_PAYLOAD]	= RXE_BTH_BYTES +
+					  RXE_AETH_BYTES,
+		}
+	},
+	[IB_OPCODE_RC_ACKNOWLEDGE]			= {
+		.name	= "IB_OPCODE_RC_ACKNOWLEDGE",
+		.length = RXE_BTH_BYTES + RXE_AETH_BYTES,
+		.offset = {
+			[RXE_BTH]	= 0,
+			[RXE_AETH]	= RXE_BTH_BYTES,
+			[RXE_PAYLOAD]	= RXE_BTH_BYTES +
+					  RXE_AETH_BYTES,
+		}
+	},
+	[IB_OPCODE_RC_ATOMIC_ACKNOWLEDGE]			= {
+		.name	= "IB_OPCODE_RC_ATOMIC_ACKNOWLEDGE",
+		.length = RXE_BTH_BYTES + RXE_ATMACK_BYTES + RXE_AETH_BYTES,
+		.offset = {
+			[RXE_BTH]	= 0,
+			[RXE_AETH]	= RXE_BTH_BYTES,
+			[RXE_ATMACK]	= RXE_BTH_BYTES +
+					  RXE_AETH_BYTES,
+			[RXE_PAYLOAD]	= RXE_BTH_BYTES +
+					  RXE_ATMACK_BYTES +
+					  RXE_AETH_BYTES,
+		}
+	},
+	[IB_OPCODE_RC_COMPARE_SWAP]			= {
+		.name	= "IB_OPCODE_RC_COMPARE_SWAP",
+		.length = RXE_BTH_BYTES + RXE_ATMETH_BYTES,
+		.offset = {
+			[RXE_BTH]	= 0,
+			[RXE_ATMETH]	= RXE_BTH_BYTES,
+			[RXE_PAYLOAD]	= RXE_BTH_BYTES +
+					  RXE_ATMETH_BYTES,
+		}
+	},
+	[IB_OPCODE_RC_FETCH_ADD]			= {
+		.name	= "IB_OPCODE_RC_FETCH_ADD",
+		.length = RXE_BTH_BYTES + RXE_ATMETH_BYTES,
+		.offset = {
+			[RXE_BTH]	= 0,
+			[RXE_ATMETH]	= RXE_BTH_BYTES,
+			[RXE_PAYLOAD]	= RXE_BTH_BYTES +
+					  RXE_ATMETH_BYTES,
+		}
+	},
+	[IB_OPCODE_RC_SEND_LAST_WITH_INVALIDATE]		= {
+		.name	= "IB_OPCODE_RC_SEND_LAST_WITH_INVALIDATE",
+		.length = RXE_BTH_BYTES + RXE_IETH_BYTES,
+		.offset = {
+			[RXE_BTH]	= 0,
+			[RXE_IETH]	= RXE_BTH_BYTES,
+			[RXE_PAYLOAD]	= RXE_BTH_BYTES +
+					  RXE_IETH_BYTES,
+		}
+	},
+	[IB_OPCODE_RC_SEND_ONLY_WITH_INVALIDATE]		= {
+		.name	= "IB_OPCODE_RC_SEND_ONLY_INV",
+		.length = RXE_BTH_BYTES + RXE_IETH_BYTES,
+		.offset = {
+			[RXE_BTH]	= 0,
+			[RXE_IETH]	= RXE_BTH_BYTES,
+			[RXE_PAYLOAD]	= RXE_BTH_BYTES +
+					  RXE_IETH_BYTES,
+		}
+	},
+
+	/* UC */
+	[IB_OPCODE_UC_SEND_FIRST]			= {
+		.name	= "IB_OPCODE_UC_SEND_FIRST",
+		.length = RXE_BTH_BYTES,
+		.offset = {
+			[RXE_BTH]	= 0,
+			[RXE_PAYLOAD]	= RXE_BTH_BYTES,
+		}
+	},
+	[IB_OPCODE_UC_SEND_MIDDLE]		= {
+		.name	= "IB_OPCODE_UC_SEND_MIDDLE",
+		.length = RXE_BTH_BYTES,
+		.offset = {
+			[RXE_BTH]	= 0,
+			[RXE_PAYLOAD]	= RXE_BTH_BYTES,
+		}
+	},
+	[IB_OPCODE_UC_SEND_LAST]			= {
+		.name	= "IB_OPCODE_UC_SEND_LAST",
+		.length = RXE_BTH_BYTES,
+		.offset = {
+			[RXE_BTH]	= 0,
+			[RXE_PAYLOAD]	= RXE_BTH_BYTES,
+		}
+	},
+	[IB_OPCODE_UC_SEND_LAST_WITH_IMMEDIATE]		= {
+		.name	= "IB_OPCODE_UC_SEND_LAST_WITH_IMMEDIATE",
+		.length = RXE_BTH_BYTES + RXE_IMMDT_BYTES,
+		.offset = {
+			[RXE_BTH]	= 0,
+			[RXE_IMMDT]	= RXE_BTH_BYTES,
+			[RXE_PAYLOAD]	= RXE_BTH_BYTES +
+					  RXE_IMMDT_BYTES,
+		}
+	},
+	[IB_OPCODE_UC_SEND_ONLY]			= {
+		.name	= "IB_OPCODE_UC_SEND_ONLY",
+		.length = RXE_BTH_BYTES,
+		.offset = {
+			[RXE_BTH]	= 0,
+			[RXE_PAYLOAD]	= RXE_BTH_BYTES,
+		}
+	},
+	[IB_OPCODE_UC_SEND_ONLY_WITH_IMMEDIATE]		= {
+		.name	= "IB_OPCODE_UC_SEND_ONLY_WITH_IMMEDIATE",
+		.length = RXE_BTH_BYTES + RXE_IMMDT_BYTES,
+		.offset = {
+			[RXE_BTH]	= 0,
+			[RXE_IMMDT]	= RXE_BTH_BYTES,
+			[RXE_PAYLOAD]	= RXE_BTH_BYTES +
+					  RXE_IMMDT_BYTES,
+		}
+	},
+	[IB_OPCODE_UC_RDMA_WRITE_FIRST]		= {
+		.name	= "IB_OPCODE_UC_RDMA_WRITE_FIRST",
+		.length = RXE_BTH_BYTES + RXE_RETH_BYTES,
+		.offset = {
+			[RXE_BTH]	= 0,
+			[RXE_RETH]	= RXE_BTH_BYTES,
+			[RXE_PAYLOAD]	= RXE_BTH_BYTES +
+					  RXE_RETH_BYTES,
+		}
+	},
+	[IB_OPCODE_UC_RDMA_WRITE_MIDDLE]		= {
+		.name	= "IB_OPCODE_UC_RDMA_WRITE_MIDDLE",
+		.length = RXE_BTH_BYTES,
+		.offset = {
+			[RXE_BTH]	= 0,
+			[RXE_PAYLOAD]	= RXE_BTH_BYTES,
+		}
+	},
+	[IB_OPCODE_UC_RDMA_WRITE_LAST]			= {
+		.name	= "IB_OPCODE_UC_RDMA_WRITE_LAST",
+		.length = RXE_BTH_BYTES,
+		.offset = {
+			[RXE_BTH]	= 0,
+			[RXE_PAYLOAD]	= RXE_BTH_BYTES,
+		}
+	},
+	[IB_OPCODE_UC_RDMA_WRITE_LAST_WITH_IMMEDIATE]		= {
+		.name	= "IB_OPCODE_UC_RDMA_WRITE_LAST_WITH_IMMEDIATE",
+		.length = RXE_BTH_BYTES + RXE_IMMDT_BYTES,
+		.offset = {
+			[RXE_BTH]	= 0,
+			[RXE_IMMDT]	= RXE_BTH_BYTES,
+			[RXE_PAYLOAD]	= RXE_BTH_BYTES +
+					  RXE_IMMDT_BYTES,
+		}
+	},
+	[IB_OPCODE_UC_RDMA_WRITE_ONLY]			= {
+		.name	= "IB_OPCODE_UC_RDMA_WRITE_ONLY",
+		.length = RXE_BTH_BYTES + RXE_RETH_BYTES,
+		.offset = {
+			[RXE_BTH]	= 0,
+			[RXE_RETH]	= RXE_BTH_BYTES,
+			[RXE_PAYLOAD]	= RXE_BTH_BYTES +
+					  RXE_RETH_BYTES,
+		}
+	},
+	[IB_OPCODE_UC_RDMA_WRITE_ONLY_WITH_IMMEDIATE]		= {
+		.name	= "IB_OPCODE_UC_RDMA_WRITE_ONLY_WITH_IMMEDIATE",
+		.length = RXE_BTH_BYTES + RXE_IMMDT_BYTES + RXE_RETH_BYTES,
+		.offset = {
+			[RXE_BTH]	= 0,
+			[RXE_RETH]	= RXE_BTH_BYTES,
+			[RXE_IMMDT]	= RXE_BTH_BYTES +
+					  RXE_RETH_BYTES,
+			[RXE_PAYLOAD]	= RXE_BTH_BYTES +
+					  RXE_RETH_BYTES +
+					  RXE_IMMDT_BYTES,
+		}
+	},
+
+	/* RD */
+	[IB_OPCODE_RD_SEND_FIRST]			= {
+		.name	= "IB_OPCODE_RD_SEND_FIRST",
+		.length = RXE_BTH_BYTES + RXE_DETH_BYTES + RXE_RDETH_BYTES,
+		.offset = {
+			[RXE_BTH]	= 0,
+			[RXE_RDETH]	= RXE_BTH_BYTES,
+			[RXE_DETH]	= RXE_BTH_BYTES +
+					  RXE_RDETH_BYTES,
+			[RXE_PAYLOAD]	= RXE_BTH_BYTES +
+					  RXE_RDETH_BYTES +
+					  RXE_DETH_BYTES,
+		}
+	},
+	[IB_OPCODE_RD_SEND_MIDDLE]		= {
+		.name	= "IB_OPCODE_RD_SEND_MIDDLE",
+		.length = RXE_BTH_BYTES + RXE_DETH_BYTES + RXE_RDETH_BYTES,
+		.offset = {
+			[RXE_BTH]	= 0,
+			[RXE_RDETH]	= RXE_BTH_BYTES,
+			[RXE_DETH]	= RXE_BTH_BYTES +
+					  RXE_RDETH_BYTES,
+			[RXE_PAYLOAD]	= RXE_BTH_BYTES +
+					  RXE_RDETH_BYTES +
+					  RXE_DETH_BYTES,
+		}
+	},
+	[IB_OPCODE_RD_SEND_LAST]			= {
+		.name	= "IB_OPCODE_RD_SEND_LAST",
+		.length = RXE_BTH_BYTES + RXE_DETH_BYTES + RXE_RDETH_BYTES,
+		.offset = {
+			[RXE_BTH]	= 0,
+			[RXE_RDETH]	= RXE_BTH_BYTES,
+			[RXE_DETH]	= RXE_BTH_BYTES +
+					  RXE_RDETH_BYTES,
+			[RXE_PAYLOAD]	= RXE_BTH_BYTES +
+					  RXE_RDETH_BYTES +
+					  RXE_DETH_BYTES,
+		}
+	},
+	[IB_OPCODE_RD_SEND_LAST_WITH_IMMEDIATE]		= {
+		.name	= "IB_OPCODE_RD_SEND_LAST_WITH_IMMEDIATE",
+		.length = RXE_BTH_BYTES + RXE_IMMDT_BYTES + RXE_DETH_BYTES +
+			  RXE_RDETH_BYTES,
+		.offset = {
+			[RXE_BTH]	= 0,
+			[RXE_RDETH]	= RXE_BTH_BYTES,
+			[RXE_DETH]	= RXE_BTH_BYTES +
+					  RXE_RDETH_BYTES,
+			[RXE_IMMDT]	= RXE_BTH_BYTES +
+					  RXE_RDETH_BYTES +
+					  RXE_DETH_BYTES,
+			[RXE_PAYLOAD]	= RXE_BTH_BYTES +
+					  RXE_RDETH_BYTES +
+					  RXE_DETH_BYTES +
+					  RXE_IMMDT_BYTES,
+		}
+	},
+	[IB_OPCODE_RD_SEND_ONLY]			= {
+		.name	= "IB_OPCODE_RD_SEND_ONLY",
+		.length = RXE_BTH_BYTES + RXE_DETH_BYTES + RXE_RDETH_BYTES,
+		.offset = {
+			[RXE_BTH]	= 0,
+			[RXE_RDETH]	= RXE_BTH_BYTES,
+			[RXE_DETH]	= RXE_BTH_BYTES +
+					  RXE_RDETH_BYTES,
+			[RXE_PAYLOAD]	= RXE_BTH_BYTES +
+					  RXE_RDETH_BYTES +
+					  RXE_DETH_BYTES,
+		}
+	},
+	[IB_OPCODE_RD_SEND_ONLY_WITH_IMMEDIATE]		= {
+		.name	= "IB_OPCODE_RD_SEND_ONLY_WITH_IMMEDIATE",
+		.length = RXE_BTH_BYTES + RXE_IMMDT_BYTES + RXE_DETH_BYTES +
+			  RXE_RDETH_BYTES,
+		.offset = {
+			[RXE_BTH]	= 0,
+			[RXE_RDETH]	= RXE_BTH_BYTES,
+			[RXE_DETH]	= RXE_BTH_BYTES +
+					  RXE_RDETH_BYTES,
+			[RXE_IMMDT]	= RXE_BTH_BYTES +
+					  RXE_RDETH_BYTES +
+					  RXE_DETH_BYTES,
+			[RXE_PAYLOAD]	= RXE_BTH_BYTES +
+					  RXE_RDETH_BYTES +
+					  RXE_DETH_BYTES +
+					  RXE_IMMDT_BYTES,
+		}
+	},
+	[IB_OPCODE_RD_RDMA_WRITE_FIRST]		= {
+		.name	= "IB_OPCODE_RD_RDMA_WRITE_FIRST",
+		.length = RXE_BTH_BYTES + RXE_RETH_BYTES + RXE_DETH_BYTES +
+			  RXE_RDETH_BYTES,
+		.offset = {
+			[RXE_BTH]	= 0,
+			[RXE_RDETH]	= RXE_BTH_BYTES,
+			[RXE_DETH]	= RXE_BTH_BYTES +
+					  RXE_RDETH_BYTES,
+			[RXE_RETH]	= RXE_BTH_BYTES +
+					  RXE_RDETH_BYTES +
+					  RXE_DETH_BYTES,
+			[RXE_PAYLOAD]	= RXE_BTH_BYTES +
+					  RXE_RDETH_BYTES +
+					  RXE_DETH_BYTES +
+					  RXE_RETH_BYTES,
+		}
+	},
+	[IB_OPCODE_RD_RDMA_WRITE_MIDDLE]		= {
+		.name	= "IB_OPCODE_RD_RDMA_WRITE_MIDDLE",
+		.length = RXE_BTH_BYTES + RXE_DETH_BYTES + RXE_RDETH_BYTES,
+		.offset = {
+			[RXE_BTH]	= 0,
+			[RXE_RDETH]	= RXE_BTH_BYTES,
+			[RXE_DETH]	= RXE_BTH_BYTES +
+					  RXE_RDETH_BYTES,
+			[RXE_PAYLOAD]	= RXE_BTH_BYTES +
+					  RXE_RDETH_BYTES +
+					  RXE_DETH_BYTES,
+		}
+	},
+	[IB_OPCODE_RD_RDMA_WRITE_LAST]			= {
+		.name	= "IB_OPCODE_RD_RDMA_WRITE_LAST",
+		.length = RXE_BTH_BYTES + RXE_DETH_BYTES + RXE_RDETH_BYTES,
+		.offset = {
+			[RXE_BTH]	= 0,
+			[RXE_RDETH]	= RXE_BTH_BYTES,
+			[RXE_DETH]	= RXE_BTH_BYTES +
+					  RXE_RDETH_BYTES,
+			[RXE_PAYLOAD]	= RXE_BTH_BYTES +
+					  RXE_RDETH_BYTES +
+					  RXE_DETH_BYTES,
+		}
+	},
+	[IB_OPCODE_RD_RDMA_WRITE_LAST_WITH_IMMEDIATE]		= {
+		.name	= "IB_OPCODE_RD_RDMA_WRITE_LAST_WITH_IMMEDIATE",
+		.length = RXE_BTH_BYTES + RXE_IMMDT_BYTES + RXE_DETH_BYTES +
+			  RXE_RDETH_BYTES,
+		.offset = {
+			[RXE_BTH]	= 0,
+			[RXE_RDETH]	= RXE_BTH_BYTES,
+			[RXE_DETH]	= RXE_BTH_BYTES +
+					  RXE_RDETH_BYTES,
+			[RXE_IMMDT]	= RXE_BTH_BYTES +
+					  RXE_RDETH_BYTES +
+					  RXE_DETH_BYTES,
+			[RXE_PAYLOAD]	= RXE_BTH_BYTES +
+					  RXE_RDETH_BYTES +
+					  RXE_DETH_BYTES +
+					  RXE_IMMDT_BYTES,
+		}
+	},
+	[IB_OPCODE_RD_RDMA_WRITE_ONLY]			= {
+		.name	= "IB_OPCODE_RD_RDMA_WRITE_ONLY",
+		.length = RXE_BTH_BYTES + RXE_RETH_BYTES + RXE_DETH_BYTES +
+			  RXE_RDETH_BYTES,
+		.offset = {
+			[RXE_BTH]	= 0,
+			[RXE_RDETH]	= RXE_BTH_BYTES,
+			[RXE_DETH]	= RXE_BTH_BYTES +
+					  RXE_RDETH_BYTES,
+			[RXE_RETH]	= RXE_BTH_BYTES +
+					  RXE_RDETH_BYTES +
+					  RXE_DETH_BYTES,
+			[RXE_PAYLOAD]	= RXE_BTH_BYTES +
+					  RXE_RDETH_BYTES +
+					  RXE_DETH_BYTES +
+					  RXE_RETH_BYTES,
+		}
+	},
+	[IB_OPCODE_RD_RDMA_WRITE_ONLY_WITH_IMMEDIATE]		= {
+		.name	= "IB_OPCODE_RD_RDMA_WRITE_ONLY_WITH_IMMEDIATE",
+		.length = RXE_BTH_BYTES + RXE_IMMDT_BYTES + RXE_RETH_BYTES +
+			  RXE_DETH_BYTES + RXE_RDETH_BYTES,
+		.offset = {
+			[RXE_BTH]	= 0,
+			[RXE_RDETH]	= RXE_BTH_BYTES,
+			[RXE_DETH]	= RXE_BTH_BYTES +
+					  RXE_RDETH_BYTES,
+			[RXE_RETH]	= RXE_BTH_BYTES +
+					  RXE_RDETH_BYTES +
+					  RXE_DETH_BYTES,
+			[RXE_IMMDT]	= RXE_BTH_BYTES +
+					  RXE_RDETH_BYTES +
+					  RXE_DETH_BYTES +
+					  RXE_RETH_BYTES,
+			[RXE_PAYLOAD]	= RXE_BTH_BYTES +
+					  RXE_RDETH_BYTES +
+					  RXE_DETH_BYTES +
+					  RXE_RETH_BYTES +
+					  RXE_IMMDT_BYTES,
+		}
+	},
+	[IB_OPCODE_RD_RDMA_READ_REQUEST]			= {
+		.name	= "IB_OPCODE_RD_RDMA_READ_REQUEST",
+		.length = RXE_BTH_BYTES + RXE_RETH_BYTES + RXE_DETH_BYTES +
+			  RXE_RDETH_BYTES,
+		.offset = {
+			[RXE_BTH]	= 0,
+			[RXE_RDETH]	= RXE_BTH_BYTES,
+			[RXE_DETH]	= RXE_BTH_BYTES +
+					  RXE_RDETH_BYTES,
+			[RXE_RETH]	= RXE_BTH_BYTES +
+					  RXE_RDETH_BYTES +
+					  RXE_DETH_BYTES,
+			[RXE_PAYLOAD]	= RXE_BTH_BYTES +
+					  RXE_RETH_BYTES +
+					  RXE_DETH_BYTES +
+					  RXE_RDETH_BYTES,
+		}
+	},
+	[IB_OPCODE_RD_RDMA_READ_RESPONSE_FIRST]		= {
+		.name	= "IB_OPCODE_RD_RDMA_READ_RESPONSE_FIRST",
+		.length = RXE_BTH_BYTES + RXE_AETH_BYTES + RXE_RDETH_BYTES,
+		.offset = {
+			[RXE_BTH]	= 0,
+			[RXE_RDETH]	= RXE_BTH_BYTES,
+			[RXE_AETH]	= RXE_BTH_BYTES +
+					  RXE_RDETH_BYTES,
+			[RXE_PAYLOAD]	= RXE_BTH_BYTES +
+					  RXE_RDETH_BYTES +
+					  RXE_AETH_BYTES,
+		}
+	},
+	[IB_OPCODE_RD_RDMA_READ_RESPONSE_MIDDLE]		= {
+		.name	= "IB_OPCODE_RD_RDMA_READ_RESPONSE_MIDDLE",
+		.length = RXE_BTH_BYTES + RXE_RDETH_BYTES,
+		.offset = {
+			[RXE_BTH]	= 0,
+			[RXE_RDETH]	= RXE_BTH_BYTES,
+			[RXE_PAYLOAD]	= RXE_BTH_BYTES +
+					  RXE_RDETH_BYTES,
+		}
+	},
+	[IB_OPCODE_RD_RDMA_READ_RESPONSE_LAST]		= {
+		.name	= "IB_OPCODE_RD_RDMA_READ_RESPONSE_LAST",
+		.length = RXE_BTH_BYTES + RXE_AETH_BYTES + RXE_RDETH_BYTES,
+		.offset = {
+			[RXE_BTH]	= 0,
+			[RXE_RDETH]	= RXE_BTH_BYTES,
+			[RXE_AETH]	= RXE_BTH_BYTES +
+					  RXE_RDETH_BYTES,
+			[RXE_PAYLOAD]	= RXE_BTH_BYTES +
+					  RXE_RDETH_BYTES +
+					  RXE_AETH_BYTES,
+		}
+	},
+	[IB_OPCODE_RD_RDMA_READ_RESPONSE_ONLY]		= {
+		.name	= "IB_OPCODE_RD_RDMA_READ_RESPONSE_ONLY",
+		.length = RXE_BTH_BYTES + RXE_AETH_BYTES + RXE_RDETH_BYTES,
+		.offset = {
+			[RXE_BTH]	= 0,
+			[RXE_RDETH]	= RXE_BTH_BYTES,
+			[RXE_AETH]	= RXE_BTH_BYTES +
+					  RXE_RDETH_BYTES,
+			[RXE_PAYLOAD]	= RXE_BTH_BYTES +
+					  RXE_RDETH_BYTES +
+					  RXE_AETH_BYTES,
+		}
+	},
+	[IB_OPCODE_RD_ACKNOWLEDGE]			= {
+		.name	= "IB_OPCODE_RD_ACKNOWLEDGE",
+		.length = RXE_BTH_BYTES + RXE_AETH_BYTES + RXE_RDETH_BYTES,
+		.offset = {
+			[RXE_BTH]	= 0,
+			[RXE_RDETH]	= RXE_BTH_BYTES,
+			[RXE_AETH]	= RXE_BTH_BYTES +
+					  RXE_RDETH_BYTES,
+		}
+	},
+	[IB_OPCODE_RD_ATOMIC_ACKNOWLEDGE]			= {
+		.name	= "IB_OPCODE_RD_ATOMIC_ACKNOWLEDGE",
+		.length = RXE_BTH_BYTES + RXE_ATMACK_BYTES + RXE_AETH_BYTES +
+			  RXE_RDETH_BYTES,
+		.offset = {
+			[RXE_BTH]	= 0,
+			[RXE_RDETH]	= RXE_BTH_BYTES,
+			[RXE_AETH]	= RXE_BTH_BYTES +
+					  RXE_RDETH_BYTES,
+			[RXE_ATMACK]	= RXE_BTH_BYTES +
+					  RXE_RDETH_BYTES +
+					  RXE_AETH_BYTES,
+		}
+	},
+	[IB_OPCODE_RD_COMPARE_SWAP]			= {
+		.name	= "RD_COMPARE_SWAP",
+		.length = RXE_BTH_BYTES + RXE_ATMETH_BYTES + RXE_DETH_BYTES +
+			  RXE_RDETH_BYTES,
+		.offset = {
+			[RXE_BTH]	= 0,
+			[RXE_RDETH]	= RXE_BTH_BYTES,
+			[RXE_DETH]	= RXE_BTH_BYTES +
+					  RXE_RDETH_BYTES,
+			[RXE_ATMETH]	= RXE_BTH_BYTES +
+					  RXE_RDETH_BYTES +
+					  RXE_DETH_BYTES,
+			[RXE_PAYLOAD]	= RXE_BTH_BYTES +
+					  RXE_ATMETH_BYTES +
+					  RXE_DETH_BYTES +
+					  RXE_RDETH_BYTES,
+		}
+	},
+	[IB_OPCODE_RD_FETCH_ADD]			= {
+		.name	= "IB_OPCODE_RD_FETCH_ADD",
+		.length = RXE_BTH_BYTES + RXE_ATMETH_BYTES + RXE_DETH_BYTES +
+			  RXE_RDETH_BYTES,
+		.offset = {
+			[RXE_BTH]	= 0,
+			[RXE_RDETH]	= RXE_BTH_BYTES,
+			[RXE_DETH]	= RXE_BTH_BYTES +
+					  RXE_RDETH_BYTES,
+			[RXE_ATMETH]	= RXE_BTH_BYTES +
+					  RXE_RDETH_BYTES +
+					  RXE_DETH_BYTES,
+			[RXE_PAYLOAD]	= RXE_BTH_BYTES +
+					  RXE_ATMETH_BYTES +
+					  RXE_DETH_BYTES +
+					  RXE_RDETH_BYTES,
+		}
+	},
+
+	/* UD */
+	[IB_OPCODE_UD_SEND_ONLY]			= {
+		.name	= "IB_OPCODE_UD_SEND_ONLY",
+		.length = RXE_BTH_BYTES + RXE_DETH_BYTES,
+		.offset = {
+			[RXE_BTH]	= 0,
+			[RXE_DETH]	= RXE_BTH_BYTES,
+			[RXE_PAYLOAD]	= RXE_BTH_BYTES +
+					  RXE_DETH_BYTES,
+		}
+	},
+	[IB_OPCODE_UD_SEND_ONLY_WITH_IMMEDIATE]		= {
+		.name	= "IB_OPCODE_UD_SEND_ONLY_WITH_IMMEDIATE",
+		.length = RXE_BTH_BYTES + RXE_IMMDT_BYTES + RXE_DETH_BYTES,
+		.offset = {
+			[RXE_BTH]	= 0,
+			[RXE_DETH]	= RXE_BTH_BYTES,
+			[RXE_IMMDT]	= RXE_BTH_BYTES +
+					  RXE_DETH_BYTES,
+			[RXE_PAYLOAD]	= RXE_BTH_BYTES +
+					  RXE_DETH_BYTES +
+					  RXE_IMMDT_BYTES,
+		}
+	},
+
+};
+
+static inline u8 __bth_opcode(void *arg)
+{
+	struct rxe_bth *bth = arg;
+
+	return bth->opcode;
+}
+
+static inline u8 bth_opcode(struct rxe_bth *bth)
+{
+	return __bth_opcode(bth);
+}
+
+static inline u8 bth_flags(struct rxe_bth *bth)
+{
+	return bth->flags;
+}
+
+static inline u16 __bth_pkey(void *arg)
+{
+	struct rxe_bth *bth = arg;
+
+	return ntohs(bth->pkey);
+}
+
+static inline u16 bth_pkey(struct rxe_bth *bth)
+{
+	return __bth_pkey(bth);
+}
+
+#define BTH_QPN_MASK		(0x00ffffff)
+#define BTH_PSN_MASK		((1 << 24) - 1)
+
+static inline u32 __bth_qpn(void *arg)
+{
+	struct rxe_bth *bth = arg;
+
+	return BTH_QPN_MASK & ntohl(bth->qpn);
+}
+
+static inline u32 bth_qpn(struct rxe_bth *bth)
+{
+	return __bth_qpn(bth);
+}
+
+static inline u32 __bth_psn(void *arg)
+{
+	struct rxe_bth *bth = arg;
+
+	return BTH_PSN_MASK & ntohl(bth->apsn);
+}
+
+static inline u32 bth_psn(struct rxe_bth *bth)
+{
+	return __bth_psn(bth);
+}
+
+static inline void print_bth(struct rxe_bth *bth)
+{
+	printf("struct rxe_bth {\n");
+	printf("\t__u8            opcode; 0x%x\n", bth_opcode(bth));
+	printf("\t__u8            flags;  0x%x\n", bth_flags(bth));
+	printf("\t__be16          pkey;   0x%x\n", bth_pkey(bth));
+	printf("\t__be32          qpn;    0x%x\n", bth_qpn(bth));
+	printf("\t__be32          apsn;   0x%x\n", bth_psn(bth));
+	printf("};\n");
+}
+
+static inline void print_reth(struct rxe_reth *reth)
+{
+	printf("struct rxe_reth {\n");
+	printf("\t__be64		va;\n");
+	printf("\t__be32		rkey;\n");
+	printf("\t__be32		len;\n");
+	printf("};\n");
+}
+
+#define AETH_MSN_MASK		(0x00ffffff)
+
+static inline u32 aeth_msn(struct rxe_aeth *aeth)
+{
+	return AETH_MSN_MASK & ntohl(aeth->smsn);
+}
+
+static inline void print_aeth(struct rxe_aeth *aeth)
+{
+	printf("struct rxe_aeth {\n");
+	printf("\t___be32		smsn; 0x%x\n", aeth_msn(aeth));
+	printf("};\n");
+}
+
+static inline u64 atmeth_va(struct rxe_atmeth *atmeth)
+{
+	return ntohl(atmeth->va);
+}
+
+static inline u32 atmeth_rkey(struct rxe_atmeth *atmeth)
+{
+	return ntohl(atmeth->rkey);
+}
+
+static inline u64 atmeth_swap_add(struct rxe_atmeth *atmeth)
+{
+	return ntohl(atmeth->swap_add);
+}
+
+static inline u64 atmeth_comp(struct rxe_atmeth *atmeth)
+{
+	return ntohl(atmeth->comp);
+}
+
+static inline void print_atmeth(struct rxe_atmeth *atmeth)
+{
+	printf("struct rxe_atmeth {\n");
+	printf("\t__be64		va;       0x%llx\n", atmeth_va(atmeth));
+	printf("\t__be32		rkey;     0x%x\n", atmeth_rkey(atmeth));
+	printf("\t__be64		swap_add; 0x%llx\n", atmeth_swap_add(atmeth));
+	printf("\t__be64		comp;     0x%llx\n", atmeth_comp(atmeth));
+	printf("} __packed;\n");
+}
+
+static inline u64 atmack_orig(struct rxe_atmack *atmack)
+{
+	return ntohl(atmack->orig);
+}
+
+static inline void print_atmack(struct rxe_atmack *atmack)
+{
+	printf("struct rxe_atmack {\n");
+	printf("\t__be64		orig; 0x%llx\n", atmack_orig(atmack));
+	printf("};\n");
+}
+
+static inline u32 ieth_rkey(struct rxe_ieth *ieth)
+{
+	return ntohl(ieth->rkey);
+}
+
+static inline void print_ieth(struct rxe_ieth *ieth)
+{
+	printf("struct rxe_ieth {\n");
+	printf("\t__be32		rkey; 0x%x\n", ieth_rkey(ieth));
+	printf("};\n");
+}
+
+#define RDETH_EEN_MASK		(0x00ffffff)
+
+static inline u32 rdeth_een(struct rxe_rdeth *rdeth)
+{
+	return RDETH_EEN_MASK & ntohl(rdeth->een);
+}
+
+static inline void print_rdeth(struct rxe_rdeth *rdeth)
+{
+	printf("struct rxe_rdeth {\n");
+	printf("\t__be32		een; 0x%x\n", rdeth_een(rdeth));
+	printf("};\n");
+}
+
+#define DETH_SQP_MASK		(0x00ffffff)
+
+static inline u32 deth_qkey(struct rxe_deth *deth)
+{
+	return ntohl(deth->qkey);
+}
+
+static inline u32 deth_sqp(struct rxe_deth *deth)
+{
+	return DETH_SQP_MASK & ntohl(deth->sqp);
+}
+
+static inline void print_deth(struct rxe_deth *deth)
+{
+	printf("struct rxe_deth {\n");
+	printf("\t__be32	qkey; 0x%x\n", deth_qkey(deth));
+	printf("\t__be32	sqp;  0x%x\n", deth_sqp(deth));
+	printf("};\n");
+}
+
+static inline __be32 immdt_imm(struct rxe_immdt *immdt)
+{
+	return immdt->imm;
+}
+
+static inline void print_immdt(struct rxe_immdt *immdt)
+{
+	printf("struct rxe_immdt {\n");
+	printf("\t__be32		imm; 0x%x\n", immdt_imm(immdt));
+	printf("};\n");
+}
+
+static inline void print_ipaddr(struct iphdr *iph)
+{
+	unsigned char *ip_addr = (unsigned char *)&iph->saddr;
+
+	printf("struct iphdr {\n");
+	printf("\t__be32	saddr; %d.%d.%d.%d\n",
+			ip_addr[0], ip_addr[1], ip_addr[2], ip_addr[3]);
+	ip_addr = (unsigned char *)&iph->daddr;
+	printf("\t__be32	daddr; %d.%d.%d.%d\n",
+			ip_addr[0], ip_addr[1], ip_addr[2], ip_addr[3]);
+	printf("};\n");
+}
+
+static inline void print_udphdr(struct udphdr *udp_hdr)
+{
+	printf("struct udphdr {\n");
+	printf("\t__be16	source; %d\n", ntohs(udp_hdr->source));
+	printf("\t__be16	dest;   %d\n", ntohs(udp_hdr->dest));
+	printf("\t__be16	len;    %d\n", ntohs(udp_hdr->len));
+	printf("\t__sum16	check;  %d\n", ntohs(udp_hdr->check));
+	printf("};\n");
+}
+
+static void print_bpf_output(void *data, __u32 size)
+{
+	struct iphdr *iph = (struct iphdr *)data;
+	struct udphdr *udp_hdr;
+	struct rxe_bth *bth;
+
+	print_ipaddr(iph);
+	udp_hdr = (struct udphdr *)(data + sizeof(struct iphdr));
+
+	printf("udp hdr:\n");
+	print_udphdr(udp_hdr);
+	bth = (struct rxe_bth *)(data + sizeof(struct iphdr) + sizeof(struct udphdr));
+
+	printf("%s\n", rxe_opcode[bth->opcode].name);
+	print_bth(bth);
+
+	for (int i = RXE_BTH + 1; i < NUM_HDR_TYPES; i++) {
+		int offset = rxe_opcode[bth->opcode].offset[i];
+
+		if (offset) {
+			char *cur_p = (char *)bth + offset;
+
+			switch (i) {
+			case	RXE_RETH:
+				printf("RXE_RETH\n");
+				struct rxe_reth *reth = (struct rxe_reth *)cur_p;
+
+				print_reth(reth);
+				break;
+
+			case	RXE_AETH:
+				printf("RXE_AETH\n");
+				struct rxe_aeth *aeth = (struct rxe_aeth *)cur_p;
+
+				print_aeth(aeth);
+				break;
+
+			case	RXE_ATMETH:
+				printf("RXE_ATMETH\n");
+				struct rxe_atmeth *atmeth = (struct rxe_atmeth *)cur_p;
+
+				print_atmeth(atmeth);
+				break;
+
+			case	RXE_ATMACK:
+				printf("RXE_ATMACK\n");
+				struct rxe_atmack *atmack = (struct rxe_atmack *)cur_p;
+
+				print_atmack(atmack);
+				break;
+
+			case	RXE_IETH:
+				printf("RXE_IETH\n");
+				struct rxe_ieth *ieth = (struct rxe_ieth *)cur_p;
+
+				print_ieth(ieth);
+				break;
+
+			case	RXE_RDETH:
+				printf("RXE_RDETH\n");
+				struct rxe_rdeth *rdeth = (struct rxe_rdeth *)cur_p;
+
+				print_rdeth(rdeth);
+				break;
+
+			case	RXE_DETH:
+				printf("RXE_DETH\n");
+				struct rxe_deth *deth = (struct rxe_deth *)cur_p;
+
+				print_deth(deth);
+				break;
+
+			case	RXE_IMMDT:
+				printf("RXE_IMMDT\n");
+				struct rxe_immdt *immdt = (struct rxe_immdt *)cur_p;
+
+				print_immdt(immdt);
+				break;
+
+			case	RXE_PAYLOAD:
+				printf("RXE_PAYLOAD\n");
+				int len = ntohs(udp_hdr->len) - rxe_opcode[bth->opcode].length;
+
+				for (int j = 0; j < len; j++) {
+					if (isprint(cur_p[j]))
+						printf("%c", cur_p[j]);
+					else
+						printf("%02x", cur_p[j]);
+				}
+				printf("\n");
+				break;
+			default:
+				break;
+			}
+		}
+	}
+
+	printf("\n");
+}
+
+static int handle_event(void *ctx, void *data, unsigned long data_sz)
+{
+	print_bpf_output(data+sizeof(struct ethhdr),
+			 data_sz-sizeof(struct ethhdr));
+	return 0;
+}
+
+int main(void)
+{
+	struct rxe_pkg_kernel_dump *skel;
+	int err;
+	struct ring_buffer *ringbuf = NULL;
+	struct rlimit rlim = { .rlim_cur = RLIM_INFINITY, .rlim_max = RLIM_INFINITY };
+
+	if (setrlimit(RLIMIT_MEMLOCK, &rlim)) {
+		perror("setrlimit failed");
+		return 1;
+	}
+
+	skel = rxe_pkg_kernel_dump__open_and_load();
+	if (!skel) {
+		fprintf(stderr, "Failed to open and load BPF skeleton\n");
+		return 1;
+	}
+
+	int ifindex = if_nametoindex("nk1");
+
+	if (ifindex == 0) {
+		perror("if_nametoindex nk1 failed");
+		return 1;
+	}
+
+	skel->links.handle_netkit_ingress = bpf_program__attach_netkit(
+		skel->progs.handle_netkit_ingress, ifindex, NULL);
+
+	if (!skel->links.handle_netkit_ingress) {
+		fprintf(stderr, "Failed to attach to netkit nk1\n");
+		goto cleanup;
+	}
+
+	err = rxe_pkg_kernel_dump__attach(skel);
+	if (err) {
+		fprintf(stderr, "Failed to attach BPF skeleton\n");
+		goto cleanup;
+	}
+
+	ringbuf = ring_buffer__new(bpf_map__fd(skel->maps.rb),
+				   handle_event, NULL, NULL);
+
+	while (1) {
+		err = ring_buffer__poll(ringbuf, 100 /* timeout */);
+		if (err < 0 && err != -EINTR) {
+			fprintf(stderr, "poll error\n");
+			break;
+		}
+	}
+
+cleanup:
+	ring_buffer__free(ringbuf);
+	rxe_pkg_kernel_dump__destroy(skel);
+	return 0;
+}
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 2/2] selftests/rdma: Add netkit-based automated test script for RXE
  2026-10-19  4:19 [PATCH 0/2] selftests/rdma: Add an eBPF-based testcase using netkit for RXE Zhu Yanjun
  2026-10-19  4:19 ` [PATCH 1/2] selftests/rdma: Introduce eBPF kernel and user dump utilities " Zhu Yanjun
@ 2026-10-19  4:19 ` Zhu Yanjun
  2026-10-19  4:32   ` sashiko-bot
  2026-10-19  4:23 ` [PATCH 0/2] selftests/rdma: Add an eBPF-based testcase using netkit " Zhu Yanjun
  2 siblings, 1 reply; 6+ messages in thread
From: Zhu Yanjun @ 2026-10-19  4:19 UTC (permalink / raw)
  To: jgg, leon, shuah, zyjzyj2000, linux-rdma, linux-kselftest; +Cc: Zhu Yanjun

Add a shell script to automate functional and tracing tests for the
Soft-RoCE (RXE) driver over netkit network interfaces. The script creates
an isolated network namespace, sets up a netkit link pair (nk0/nk1),
attaches RXE links, and runs an rping client/server verification test
along with kernel tracing output collection.

Signed-off-by: Zhu Yanjun <yanjun.zhu@linux.dev>
---
 .../testing/selftests/rdma/rxe_pkg_netkit.sh  | 63 +++++++++++++++++++
 1 file changed, 63 insertions(+)
 create mode 100755 tools/testing/selftests/rdma/rxe_pkg_netkit.sh

diff --git a/tools/testing/selftests/rdma/rxe_pkg_netkit.sh b/tools/testing/selftests/rdma/rxe_pkg_netkit.sh
new file mode 100755
index 000000000000..6b544d9f49ae
--- /dev/null
+++ b/tools/testing/selftests/rdma/rxe_pkg_netkit.sh
@@ -0,0 +1,63 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
+# Copyright (c) 2026 Zhu Yanjun <yanjun.zhu@linux.dev>
+
+exec > /dev/null
+
+set -e
+
+CUR_WD="$(dirname "$0")"
+[ "$CUR_WD" = "." ] && CUR_WD="$(pwd)"
+
+cleanup() {
+    set +e
+    [ -n "$DUMP_PID" ] && kill -9 "$DUMP_PID" 2>/dev/null
+    unset DUMP_PID
+    ip netns delete ns0 2>/dev/null
+    rm -f "$CUR_WD/rxe_netkit_dump" "$CUR_WD/rxe_pkg_kernel_dump.o" "$CUR_WD/rxe_pkg_kernel.skel.h"
+}
+
+trap cleanup EXIT
+
+# Clear tracing buffer
+echo > /sys/kernel/debug/tracing/trace
+
+# Create network namespace and netkit peer links
+ip netns add ns0
+ip link add nk0 type netkit mode l2 peer name nk1
+ip link set nk0 up
+ip link set nk1 up
+
+# Configure namespace interface
+ip link set nk0 netns ns0
+ip netns exec ns0 ip addr add 10.0.0.1/24 dev nk0
+ip netns exec ns0 ip link set nk0 up
+
+# Configure host interface
+ip addr add 10.0.0.2/24 dev nk1
+
+# Start dump utility in background
+"$CUR_WD/rxe_netkit_dump" &
+DUMP_PID=$!
+
+# Add RXE links over netkit devices
+ip netns exec ns0 rdma link add rxe0 type rxe netdev nk0
+rdma link add rxe1 type rxe netdev nk1
+
+# Start rping server in namespace
+ip netns exec ns0 rping -s -a 10.0.0.1 -C 3 &
+
+# Add wait time
+sleep 1
+
+# Run rping client on host
+rping -c -a 10.0.0.1 -C 3
+
+# Tear down RDMA links and netkit devices
+ip netns exec ns0 rdma link del rxe0
+rdma link del rxe1
+ip link del nk1
+ip netns delete ns0
+
+# Display kernel trace output
+cat /sys/kernel/debug/tracing/trace
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH 0/2] selftests/rdma: Add an eBPF-based testcase using netkit for RXE
  2026-10-19  4:19 [PATCH 0/2] selftests/rdma: Add an eBPF-based testcase using netkit for RXE Zhu Yanjun
  2026-10-19  4:19 ` [PATCH 1/2] selftests/rdma: Introduce eBPF kernel and user dump utilities " Zhu Yanjun
  2026-10-19  4:19 ` [PATCH 2/2] selftests/rdma: Add netkit-based automated test script " Zhu Yanjun
@ 2026-10-19  4:23 ` Zhu Yanjun
  2 siblings, 0 replies; 6+ messages in thread
From: Zhu Yanjun @ 2026-10-19  4:23 UTC (permalink / raw)
  To: jgg, leon, shuah, zyjzyj2000, linux-rdma, linux-kselftest,
	yanjun.zhu@linux.dev

在 2026/10/18 21:19, Zhu Yanjun 写道:
> Add a new self-test case to verify RXE (Soft-RoCE) traffic using
> netkit virtual interfaces and an eBPF tcx ingress program to dump
> and parse RDMA transport headers (BTH, etc.).

The followings are the test results.

# make -C tools/testing/selftests/ TARGETS=rdma run_tests 
  
  make: Entering directory '/root/linux/tools/testing/selftests'
clang -g -O2 -target bpf -D__TARGET_ARCH_x86 -I../../../../usr/include 
-I../../../../include/uapi -I../bpf -c rxe_pkg_kernel_dump.c -o 
/root/linux/tools/testing/selftests/rdma/rxe_pkg_kernel_dump.o
bpftool gen skeleton 
/root/linux/tools/testing/selftests/rdma/rxe_pkg_kernel_dump.o > 
/root/linux/tools/testing/selftests/rdma/rxe_pkg_kernel.skel.h
gcc -D_GNU_SOURCE= 
-I/root/linux/tools/testing/selftests/../../../tools/testing/selftests 
-g -Wall -I../../../../usr/include -I../../../../include/uapi -I../bpf 
rxe_pkg_user_dump.c  -lbpf -lelf -lz -o 
/root/linux/tools/testing/selftests/rdma/rxe_netkit_dump
# timeout set to 45
# selftests: rdma: rxe_rping_between_netns.sh
ok 1 selftests: rdma: rxe_rping_between_netns.sh
# timeout set to 45
# selftests: rdma: rxe_ipv6.sh
ok 2 selftests: rdma: rxe_ipv6.sh
# timeout set to 45
# selftests: rdma: rxe_socket_with_netns.sh
ok 3 selftests: rdma: rxe_socket_with_netns.sh
# timeout set to 45
# selftests: rdma: rxe_test_NETDEV_UNREGISTER.sh
ok 4 selftests: rdma: rxe_test_NETDEV_UNREGISTER.sh
# timeout set to 45
# selftests: rdma: rxe_sent_rcvd_bytes.sh
# server DISCONNECT EVENT...
# wait for RDMA_READ_ADV state 10
ok 5 selftests: rdma: rxe_sent_rcvd_bytes.sh
# timeout set to 45
# selftests: rdma: rxe_netns_names.sh
# 1..0 # SKIP rdma netns exclusive mode is not supported
ok 6 selftests: rdma: rxe_netns_names.sh # SKIP
# timeout set to 45
# selftests: rdma: rxe_pkg_netkit.sh
# server DISCONNECT EVENT...
# wait for RDMA_READ_ADV state 10
# client DISCONNECT EVENT...
# ./rxe_pkg_netkit.sh: line 12:  5642 Killed 
"$CUR_WD/rxe_netkit_dump"
ok 7 selftests: rdma: rxe_pkg_netkit.sh
make: Leaving directory '/root/linux/tools/testing/selftests'

Yanjun Zhu

> 
> Zhu Yanjun (2):
>    selftests/rdma: Introduce eBPF kernel and user dump utilities for RXE
>    selftests/rdma: Add netkit-based automated test script for RXE
> 
>   tools/testing/selftests/rdma/Makefile         |   23 +-
>   tools/testing/selftests/rdma/config           |    3 +
>   .../selftests/rdma/rxe_pkg_kernel_dump.c      |   62 +
>   .../testing/selftests/rdma/rxe_pkg_netkit.sh  |   63 +
>   .../selftests/rdma/rxe_pkg_user_dump.c        | 1275 +++++++++++++++++
>   5 files changed, 1425 insertions(+), 1 deletion(-)
>   create mode 100644 tools/testing/selftests/rdma/rxe_pkg_kernel_dump.c
>   create mode 100755 tools/testing/selftests/rdma/rxe_pkg_netkit.sh
>   create mode 100644 tools/testing/selftests/rdma/rxe_pkg_user_dump.c
> 


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH 2/2] selftests/rdma: Add netkit-based automated test script for RXE
  2026-10-19  4:19 ` [PATCH 2/2] selftests/rdma: Add netkit-based automated test script " Zhu Yanjun
@ 2026-10-19  4:32   ` sashiko-bot
  0 siblings, 0 replies; 6+ messages in thread
From: sashiko-bot @ 2026-10-19  4:32 UTC (permalink / raw)
  To: Zhu Yanjun, linux-rdma

Thank you for your contribution! Sashiko AI review found 6 potential issue(s) to consider:
- [Medium] selftests/rdma: cleanup() in rxe_pkg_netkit.sh deletes built test binary
- [Medium] selftests/rdma: incomplete resource teardown in cleanup() on error exit
- [Medium] selftests/rdma: rxe_pkg_netkit.sh discards all stdout and trace output
- [Medium] selftests/rdma: missing prerequisite and KSFT_SKIP checks in rxe_pkg_netkit.sh
- [Medium] selftests/rdma: out-of-bounds and uninitialized reads in rxe_netkit_dump
- [Medium] selftests/rdma: missing ring_buffer__new() check and cleanup in main()
--

commit e9728fe036ee321c65ecc0a2cfb9996d436c85d4
Author: Zhu Yanjun <yanjun.zhu@linux.dev>

selftests/rdma: Add netkit-based automated test script for RXE

This commit adds a shell script to automate functional and tracing tests
for the Soft-RoCE (RXE) driver over netkit network interfaces using rping
and rxe_netkit_dump.

> diff --git a/tools/testing/selftests/rdma/rxe_pkg_netkit.sh b/tools/testing/selftests/rdma/rxe_pkg_netkit.sh
> new file mode 100755
> index 0000000000000..6b544d9f49ae8
> --- /dev/null
> +++ b/tools/testing/selftests/rdma/rxe_pkg_netkit.sh
> @@ -0,0 +1,63 @@
> +#!/bin/sh
> +# SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
> +# Copyright (c) 2026 Zhu Yanjun <yanjun.zhu@linux.dev>
> +
> +exec > /dev/null
> +
> +set -e

[Severity: Medium]
What happens if this test is run in an environment without root privileges,
mounted tracefs (/sys/kernel/debug/tracing/trace), kernel support or loaded
modules for netkit and rdma_rxe, or userspace utilities (ip, rdma, rping)?

Because set -e is enabled without checking prerequisites or sourcing
ktap_helpers.sh, any missing feature or command causes the script to abort
with exit status 1 instead of skipping with KSFT_SKIP (4).

> +
> +CUR_WD="$(dirname "$0")"
> +[ "$CUR_WD" = "." ] && CUR_WD="$(pwd)"
> +
> +cleanup() {
> +    set +e
> +    [ -n "$DUMP_PID" ] && kill -9 "$DUMP_PID" 2>/dev/null
> +    unset DUMP_PID
> +    ip netns delete ns0 2>/dev/null
> +    rm -f "$CUR_WD/rxe_netkit_dump" "$CUR_WD/rxe_pkg_kernel_dump.o" "$CUR_WD/rxe_pkg_kernel.skel.h"
> +}

[Severity: Medium]
Should cleanup() be deleting rxe_netkit_dump, rxe_pkg_kernel_dump.o, and
rxe_pkg_kernel.skel.h?

Because cleanup() is registered via trap cleanup EXIT, running
rxe_pkg_netkit.sh once deletes the compiled rxe_netkit_dump binary and build
artifacts from the working or installed kselftest directory. Since the script
does not build rxe_netkit_dump itself and invokes it in the background:

    "$CUR_WD/rxe_netkit_dump" &

any subsequent run of the test will fail to execute rxe_netkit_dump unless the
test suite is rebuilt, and the parent shell will not catch the missing binary
under set -e because the background subshell launch itself returns 0.

> +
> +trap cleanup EXIT
> +
> +# Clear tracing buffer
> +echo > /sys/kernel/debug/tracing/trace
> +
> +# Create network namespace and netkit peer links
> +ip netns add ns0
> +ip link add nk0 type netkit mode l2 peer name nk1
> +ip link set nk0 up
> +ip link set nk1 up
> +
> +# Configure namespace interface
> +ip link set nk0 netns ns0
> +ip netns exec ns0 ip addr add 10.0.0.1/24 dev nk0
> +ip netns exec ns0 ip link set nk0 up
> +
> +# Configure host interface
> +ip addr add 10.0.0.2/24 dev nk1
> +
> +# Start dump utility in background
> +"$CUR_WD/rxe_netkit_dump" &
> +DUMP_PID=$!

[Severity: Medium]
Does launching rxe_netkit_dump and running rping over nk1 expose unchecked
allocations and out-of-bounds reads in the helper utility?

First, in tools/testing/selftests/rdma/rxe_pkg_user_dump.c:main(), if
if_nametoindex("nk1") returns 0 after rxe_pkg_kernel_dump__open_and_load(),
main() returns 1 directly without jumping to cleanup to call
rxe_pkg_kernel_dump__destroy(skel):

    skel = rxe_pkg_kernel_dump__open_and_load();
    ...
    ifindex = if_nametoindex("nk1");
    if (ifindex == 0) {
        perror("if_nametoindex nk1 failed");
        return 1;
    }

Later in main(), ring_buffer__new() is passed directly to ring_buffer__poll()
without a NULL check:

    ringbuf = ring_buffer__new(bpf_map__fd(skel->maps.rb),
                   handle_event, NULL, NULL);

    while (1) {
        err = ring_buffer__poll(ringbuf, 100 /* timeout */);

If ring_buffer__new() returns NULL, ring_buffer__poll() in
tools/lib/bpf/ringbuf.c immediately dereferences rb->epoll_fd.

Second, in tools/testing/selftests/rdma/rxe_pkg_kernel_dump.c,
handle_netkit_ingress() captures every ETH_P_IP packet into a 512-byte ringbuf
record using:

    __u32 len = skb->len & 0x1FF;
    ...
    void *reserve = bpf_ringbuf_reserve(&rb, 512, 0);

Using & 0x1FF wraps packet lengths >= 512 modulo 512 instead of clamping to
512, and leaves trailing bytes in the 512-byte ringbuf slot uninitialized.

When rping runs here, it first establishes an RDMA CM connection over TCP/IPv4
before sending RoCEv2 UDP traffic. In
tools/testing/selftests/rdma/rxe_pkg_user_dump.c:print_bpf_output(), the
size parameter is ignored, iph->protocol and the UDP port are not checked,
rxe_opcode[bth->opcode].name is passed to printf() even when it is NULL on
non-RoCEv2 packets or uninitialized ringbuf bytes, and the RXE_PAYLOAD loop:

    int len = ntohs(udp_hdr->len) - rxe_opcode[bth->opcode].length;

    for (int j = 0; j < len; j++) {

reads cur_p[j] without bounding against the captured buffer size. Also in
rxe_pkg_user_dump.c, atmeth_va(), atmeth_swap_add(), atmeth_comp(), and
atmack_orig() apply 32-bit ntohl() to 64-bit __be64 fields.

> +
> +# Add RXE links over netkit devices
> +ip netns exec ns0 rdma link add rxe0 type rxe netdev nk0
> +rdma link add rxe1 type rxe netdev nk1
> +
> +# Start rping server in namespace
> +ip netns exec ns0 rping -s -a 10.0.0.1 -C 3 &
> +
> +# Add wait time
> +sleep 1
> +
> +# Run rping client on host
> +rping -c -a 10.0.0.1 -C 3
> +
> +# Tear down RDMA links and netkit devices
> +ip netns exec ns0 rdma link del rxe0
> +rdma link del rxe1
> +ip link del nk1
> +ip netns delete ns0

[Severity: Medium]
Can an error prior to this teardown block leak the background rping server,
the ns0 network namespace, the rxe0 and rxe1 RDMA links, and the nk0 and nk1
netkit interfaces?

The rping server is started in the background without saving its PID, and the
teardown of rxe0, rxe1, and nk1 only happens on the normal completion path
above. If an earlier command such as the client rping -c fails under set -e,
execution jumps to cleanup(), which only kills DUMP_PID and runs
ip netns delete ns0.

Because ip netns delete ns0 only unlinks the namespace mount without killing
processes inside it, the live rping -s process keeps ns0, nk0, rxe0, and the
host peers nk1 and rxe1 pinned in the kernel, causing subsequent test runs to
fail.

> +
> +# Display kernel trace output
> +cat /sys/kernel/debug/tracing/trace

[Severity: Medium]
Is all output from cat /sys/kernel/debug/tracing/trace and rxe_netkit_dump
intentionally discarded here?

Because exec > /dev/null is at the top of the script, all standard output for
the script and its child processes goes to /dev/null. In addition,
rxe_netkit_dump is launched asynchronously in the background without waiting
for the BPF program to load and attach to nk1 before rping starts, and nothing
verifies that either the BPF packet dump or the kernel trace buffer captured
any events.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261019041912.29488-1-yanjun.zhu@linux.dev?part=2

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH 1/2] selftests/rdma: Introduce eBPF kernel and user dump utilities for RXE
  2026-10-19  4:19 ` [PATCH 1/2] selftests/rdma: Introduce eBPF kernel and user dump utilities " Zhu Yanjun
@ 2026-10-19  4:35   ` sashiko-bot
  0 siblings, 0 replies; 6+ messages in thread
From: sashiko-bot @ 2026-10-19  4:35 UTC (permalink / raw)
  To: Zhu Yanjun, linux-rdma

Thank you for your contribution! Sashiko AI review found 6 potential issue(s) to consider:
- [Medium] selftests/rdma: packet length modulo mask in handle_netkit_ingress()
- [Medium] selftests/rdma: out-of-bounds read and NULL deref in print_bpf_output()
- [Medium] selftests/rdma: unpacked rxe_atmeth and 32-bit ntohl() on 64-bit fields
- [Medium] selftests/rdma: wrong SEC(tcx/ingress) for bpf_program__attach_netkit()
- [Medium] selftests/rdma: unchecked ring_buffer__new() and error handling in main()
- [Low] selftests/rdma: missing -I$(OUTPUT) for skel header in out-of-tree build
--

commit 204669a3455849a1a36762bf8c4136de9ad033c1
Author: Zhu Yanjun <yanjun.zhu@linux.dev>

selftests/rdma: Introduce eBPF kernel and user dump utilities for RXE

This commit adds eBPF kernel and user-space packet dump utilities to capture
and parse Soft-RoCE (RXE) traffic over netkit devices in the RDMA selftests.

> diff --git a/tools/testing/selftests/rdma/Makefile b/tools/testing/selftests/rdma/Makefile
> index a91c14c45006e..6e099cabf90b2 100644
> --- a/tools/testing/selftests/rdma/Makefile
> +++ b/tools/testing/selftests/rdma/Makefile
> @@ -4,6 +4,27 @@ TEST_PROGS := rxe_rping_between_netns.sh \
>  		rxe_socket_with_netns.sh \
>  		rxe_test_NETDEV_UNREGISTER.sh \
>  		rxe_sent_rcvd_bytes.sh \
> -		rxe_netns_names.sh
> +		rxe_netns_names.sh \
> +		rxe_pkg_netkit.sh
>  
> +TEST_GEN_PROGS_EXTENDED := rxe_netkit_dump
>  include ../lib.mk
> +
> +CLANG	?= clang
> +LLC	?= llc
> +BPFTOOL	?= bpftool
> +
> +INCLUDES	:= -I../../../../usr/include -I../../../../include/uapi -I../bpf
> +CFLAGS		+= -g -Wall $(INCLUDES)
> +LDFLAGS		+= -lbpf -lelf -lz
> +
> +$(OUTPUT)/rxe_netkit_dump: rxe_pkg_user_dump.c $(OUTPUT)/rxe_pkg_kernel.skel.h
> +	$(CC) $(CFLAGS) $< $(LDFLAGS) -o $@
> +
> +$(OUTPUT)/rxe_pkg_kernel_dump.o: rxe_pkg_kernel_dump.c
> +	$(CLANG) -g -O2 -target bpf -D__TARGET_ARCH_x86 $(INCLUDES) -c $< -o $@
> +
> +$(OUTPUT)/rxe_pkg_kernel.skel.h: $(OUTPUT)/rxe_pkg_kernel_dump.o
> +	$(BPFTOOL) gen skeleton $< > $@

[Severity: Low]
Will out-of-tree selftest builds (with O=... or OUTPUT=...) fail to locate
rxe_pkg_kernel.skel.h when compiling rxe_pkg_user_dump.c?

The rule generates $(OUTPUT)/rxe_pkg_kernel.skel.h, and rxe_pkg_user_dump.c
includes it with #include "rxe_pkg_kernel.skel.h", but INCLUDES does not
contain -I$(OUTPUT).

Also, should the $(OUTPUT)/rxe_pkg_kernel_dump.o rule derive the target
architecture instead of hardcoding -D__TARGET_ARCH_x86?

[ ... ]

> diff --git a/tools/testing/selftests/rdma/rxe_pkg_kernel_dump.c b/tools/testing/selftests/rdma/rxe_pkg_kernel_dump.c
> new file mode 100644
> index 0000000000000..54dfc8028e904
> --- /dev/null
> +++ b/tools/testing/selftests/rdma/rxe_pkg_kernel_dump.c
> @@ -0,0 +1,67 @@
> +// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
> +/*
> + * Copyright (c) 2026 Zhu Yanjun <yanjun.zhu@linux.dev>
> + */
> +
> +#include <linux/bpf.h>
> +#include <bpf/bpf_helpers.h>
> +#include <linux/if_ether.h>
> +#include <linux/ip.h>
> +#include <arpa/inet.h>
> +
> +struct {
> +	__uint(type, BPF_MAP_TYPE_RINGBUF);
> +	__uint(max_entries, 256 * 1024);/* Shared by all CPUs */
> +} rb SEC(".maps");
> +
> +SEC("tcx/ingress")
> +int handle_netkit_ingress(struct __sk_buff *skb)

[Severity: Medium]
Should handle_netkit_ingress() use SEC("netkit/primary") or SEC("netkit/peer")
instead of SEC("tcx/ingress")?

In main() in rxe_pkg_user_dump.c, the program is attached with
bpf_program__attach_netkit(), which uses bpf_program__expected_attach_type().
Because SEC("tcx/ingress") sets the expected attach type to BPF_TCX_INGRESS,
the kernel link_create path dispatches to tcx_link_attach() rather than
netkit_link_attach().

> +{
> +	/* Get the header pointer of skb */
> +	void *data_end = (void *)(long)skb->data_end;
> +	void *data = (void *)(long)skb->data;
> +
> +	struct ethhdr *eth = data;
> +
> +	/* Verify the data */
> +	if (data + sizeof(*eth) > data_end)
> +		return BPF_OK;
> +
> +	/* Check if it is IP skb */
> +	if (eth->h_proto == htons(ETH_P_IP)) {
> +		struct iphdr *iph = data + sizeof(*eth);
> +
> +		if ((void *)iph + sizeof(*iph) <= data_end) {
> +			bpf_printk("Got IP packet from netkit ingress, %d\n", skb->len);
> +
> +			__u32 len = skb->len & 0x1FF;
> +
> +			if (len == 0) {
> +				bpf_printk("len is 0\n");
> +				return BPF_OK;
> +			}
> +
> +			void *reserve = bpf_ringbuf_reserve(&rb, 512, 0);
> +
> +			if (!reserve) {
> +				bpf_printk("bpf_ringbuf_reserve fails\n");
> +				return BPF_OK;
> +			}
> +
> +			if (bpf_skb_load_bytes(skb, 0, reserve, len) < 0) {
> +				bpf_ringbuf_discard(reserve, 0);
> +				bpf_printk("bpf_skb_load_bytes fails\n");
> +				return BPF_OK;
> +			}
> +
> +			bpf_ringbuf_submit(reserve, 0);
> +		}
> +	}

[Severity: Medium]
Does masking skb->len with 0x1FF in handle_netkit_ingress() truncate larger
packets and leave part of the ring buffer record uninitialized?

Using skb->len & 0x1FF computes skb->len modulo 512 rather than clamping the
length to 512. Any packet whose length is a multiple of 512 bytes results in
len == 0 and is skipped, while packets larger than 512 bytes (such as a
520-byte or 1082-byte RDMA frame) only copy the remainder (8 or 58 bytes).

In addition, bpf_ringbuf_reserve(&rb, 512, 0) always reserves and submits a
512-byte record to userspace, but bpf_skb_load_bytes() only writes len bytes,
leaving the trailing 512 - len bytes of reserve uninitialized.

[ ... ]

> diff --git a/tools/testing/selftests/rdma/rxe_pkg_user_dump.c b/tools/testing/selftests/rdma/rxe_pkg_user_dump.c
> new file mode 100644
> index 0000000000000..39747ae1e06a3
> --- /dev/null
> +++ b/tools/testing/selftests/rdma/rxe_pkg_user_dump.c

[ ... ]

> +/******************************************************************************
> + * Atomic Extended Transport Header
> + ******************************************************************************/
> +struct rxe_atmeth {
> +	__be64			va;
> +	__be32			rkey;
> +	__be64			swap_add;
> +	__be64			comp;
> +} __packed;

[Severity: Medium]
Is __packed defined in this userspace program?

Because __packed is a kernel macro not provided by the included userspace
headers, this definition declares a global variable named __packed of type
struct rxe_atmeth rather than applying __attribute__((packed)). On 64-bit
architectures, 4 bytes of padding are inserted after rkey, shifting swap_add
and comp by 4 bytes and changing RXE_ATMETH_BYTES from 28 to 32.

[ ... ]

> +static inline void print_reth(struct rxe_reth *reth)
> +{
> +	printf("struct rxe_reth {\n");
> +	printf("\t__be64		va;\n");
> +	printf("\t__be32		rkey;\n");
> +	printf("\t__be32		len;\n");
> +	printf("};\n");
> +}
> +
> +#define AETH_MSN_MASK		(0x00ffffff)
> +
> +static inline u32 aeth_msn(struct rxe_aeth *aeth)
> +{
> +	return AETH_MSN_MASK & ntohl(aeth->smsn);
> +}
> +
> +static inline void print_aeth(struct rxe_aeth *aeth)
> +{
> +	printf("struct rxe_aeth {\n");
> +	printf("\t___be32		smsn; 0x%x\n", aeth_msn(aeth));
> +	printf("};\n");
> +}
> +
> +static inline u64 atmeth_va(struct rxe_atmeth *atmeth)
> +{
> +	return ntohl(atmeth->va);
> +}
> +
> +static inline u32 atmeth_rkey(struct rxe_atmeth *atmeth)
> +{
> +	return ntohl(atmeth->rkey);
> +}
> +
> +static inline u64 atmeth_swap_add(struct rxe_atmeth *atmeth)
> +{
> +	return ntohl(atmeth->swap_add);
> +}
> +
> +static inline u64 atmeth_comp(struct rxe_atmeth *atmeth)
> +{
> +	return ntohl(atmeth->comp);
> +}
> +
> +static inline void print_atmeth(struct rxe_atmeth *atmeth)
> +{
> +	printf("struct rxe_atmeth {\n");
> +	printf("\t__be64		va;       0x%llx\n", atmeth_va(atmeth));
> +	printf("\t__be32		rkey;     0x%x\n", atmeth_rkey(atmeth));
> +	printf("\t__be64		swap_add; 0x%llx\n", atmeth_swap_add(atmeth));
> +	printf("\t__be64		comp;     0x%llx\n", atmeth_comp(atmeth));
> +	printf("} __packed;\n");
> +}
> +
> +static inline u64 atmack_orig(struct rxe_atmack *atmack)
> +{
> +	return ntohl(atmack->orig);
> +}

[Severity: Medium]
Don't atmeth_va(), atmeth_swap_add(), atmeth_comp(), and atmack_orig()
truncate 64-bit fields by passing __be64 values to 32-bit ntohl() instead of
be64toh() or __be64_to_cpu()?

[ ... ]

> +static inline __be32 immdt_imm(struct rxe_immdt *immdt)
> +{
> +	return immdt->imm;
> +}
> +
> +static inline void print_immdt(struct rxe_immdt *immdt)
> +{
> +	printf("struct rxe_immdt {\n");
> +	printf("\t__be32		imm; 0x%x\n", immdt_imm(immdt));
> +	printf("};\n");
> +}

[Severity: Medium]
Should immdt_imm() also convert immdt->imm with ntohl() to match the other
header helpers?

[ ... ]

> +static void print_bpf_output(void *data, __u32 size)
> +{
> +	struct iphdr *iph = (struct iphdr *)data;
> +	struct udphdr *udp_hdr;
> +	struct rxe_bth *bth;
> +
> +	print_ipaddr(iph);
> +	udp_hdr = (struct udphdr *)(data + sizeof(struct iphdr));
> +
> +	printf("udp hdr:\n");
> +	print_udphdr(udp_hdr);
> +	bth = (struct rxe_bth *)(data + sizeof(struct iphdr) + sizeof(struct udphdr));
> +
> +	printf("%s\n", rxe_opcode[bth->opcode].name);
> +	print_bth(bth);
> +
> +	for (int i = RXE_BTH + 1; i < NUM_HDR_TYPES; i++) {
> +		int offset = rxe_opcode[bth->opcode].offset[i];
> +
> +		if (offset) {
> +			char *cur_p = (char *)bth + offset;
> +
> +			switch (i) {

[ ... ]

> +			case	RXE_PAYLOAD:
> +				printf("RXE_PAYLOAD\n");
> +				int len = ntohs(udp_hdr->len) - rxe_opcode[bth->opcode].length;
> +
> +				for (int j = 0; j < len; j++) {
> +					if (isprint(cur_p[j]))
> +						printf("%c", cur_p[j]);
> +					else
> +						printf("%02x", cur_p[j]);
> +				}
> +				printf("\n");
> +				break;
> +			default:
> +				break;
> +			}
> +		}
> +	}
> +
> +	printf("\n");
> +}

[Severity: Medium]
Can print_bpf_output() dereference a NULL opcode name or read past the end of
the ring buffer sample?

First, neither handle_netkit_ingress() nor print_bpf_output() verifies that
iph->protocol == IPPROTO_UDP, that the UDP destination port is 4791 (RoCEv2),
or that size covers the headers. Because only 58 of the 256 entries in
rxe_opcode[] are populated, non-RoCEv2 IPv4 packets (such as TCP or ICMP
during rping setup) or unpopulated opcodes have rxe_opcode[bth->opcode].name
== NULL, which crashes when passed to printf("%s\n", ...).

Second, in the RXE_PAYLOAD case:

	int len = ntohs(udp_hdr->len) - rxe_opcode[bth->opcode].length;

this calculation does not subtract sizeof(struct udphdr), BTH padding, or the
4-byte ICRC, and does not clamp len against size. When ntohs(udp_hdr->len)
exceeds the 512-byte ring buffer record, the loop reads cur_p[j] past the end
of the buffer.

Third, cur_p is a signed char pointer on x86_64, so bytes >= 0x80 pass
negative values to isprint() and sign-extend in printf("%02x", cur_p[j]).
Also, print_reth() above prints the field names of struct rxe_reth without
printing the values of va, rkey, and len.

> +
> +static int handle_event(void *ctx, void *data, unsigned long data_sz)
> +{
> +	print_bpf_output(data+sizeof(struct ethhdr),
> +			 data_sz-sizeof(struct ethhdr));
> +	return 0;
> +}
> +
> +int main(void)
> +{
> +	struct rxe_pkg_kernel_dump *skel;
> +	int err;
> +	struct ring_buffer *ringbuf = NULL;
> +	struct rlimit rlim = { .rlim_cur = RLIM_INFINITY, .rlim_max = RLIM_INFINITY };
> +
> +	if (setrlimit(RLIMIT_MEMLOCK, &rlim)) {
> +		perror("setrlimit failed");
> +		return 1;
> +	}
> +
> +	skel = rxe_pkg_kernel_dump__open_and_load();
> +	if (!skel) {
> +		fprintf(stderr, "Failed to open and load BPF skeleton\n");
> +		return 1;
> +	}
> +
> +	int ifindex = if_nametoindex("nk1");
> +
> +	if (ifindex == 0) {
> +		perror("if_nametoindex nk1 failed");
> +		return 1;
> +	}
> +
> +	skel->links.handle_netkit_ingress = bpf_program__attach_netkit(
> +		skel->progs.handle_netkit_ingress, ifindex, NULL);
> +
> +	if (!skel->links.handle_netkit_ingress) {
> +		fprintf(stderr, "Failed to attach to netkit nk1\n");
> +		goto cleanup;
> +	}
> +
> +	err = rxe_pkg_kernel_dump__attach(skel);
> +	if (err) {
> +		fprintf(stderr, "Failed to attach BPF skeleton\n");
> +		goto cleanup;
> +	}
> +
> +	ringbuf = ring_buffer__new(bpf_map__fd(skel->maps.rb),
> +				   handle_event, NULL, NULL);
> +
> +	while (1) {
> +		err = ring_buffer__poll(ringbuf, 100 /* timeout */);
> +		if (err < 0 && err != -EINTR) {
> +			fprintf(stderr, "poll error\n");
> +			break;
> +		}
> +	}
> +
> +cleanup:
> +	ring_buffer__free(ringbuf);
> +	rxe_pkg_kernel_dump__destroy(skel);
> +	return 0;
> +}

[Severity: Medium]
What happens in main() if ring_buffer__new() returns NULL?

ringbuf is passed directly to ring_buffer__poll(ringbuf, 100) without a NULL
check, which dereferences rb->epoll_fd and crashes.

Also, if if_nametoindex("nk1") fails, main() returns 1 without calling
rxe_pkg_kernel_dump__destroy(skel), whereas the later error paths that jump to
cleanup return 0 instead of a non-zero exit code. Finally, since
handle_netkit_ingress is already attached via bpf_program__attach_netkit(), is
the subsequent call to rxe_pkg_kernel_dump__attach(skel) needed?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261019041912.29488-1-yanjun.zhu@linux.dev?part=1

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2026-10-19  4:35 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-19  4:19 [PATCH 0/2] selftests/rdma: Add an eBPF-based testcase using netkit for RXE Zhu Yanjun
2026-10-19  4:19 ` [PATCH 1/2] selftests/rdma: Introduce eBPF kernel and user dump utilities " Zhu Yanjun
2026-10-19  4:35   ` sashiko-bot
2026-10-19  4:19 ` [PATCH 2/2] selftests/rdma: Add netkit-based automated test script " Zhu Yanjun
2026-10-19  4:32   ` sashiko-bot
2026-10-19  4:23 ` [PATCH 0/2] selftests/rdma: Add an eBPF-based testcase using netkit " Zhu Yanjun

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox