[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20251120033016.3809474-6-dw@davidwei.uk>
Date: Wed, 19 Nov 2025 19:30:14 -0800
From: David Wei <dw@...idwei.uk>
To: netdev@...r.kernel.org
Cc: Andrew Lunn <andrew+netdev@...n.ch>,
"David S. Miller" <davem@...emloft.net>,
Eric Dumazet <edumazet@...gle.com>,
Jakub Kicinski <kuba@...nel.org>,
Paolo Abeni <pabeni@...hat.com>,
Daniel Borkmann <daniel@...earbox.net>
Subject: [PATCH net-next v1 5/7] selftests/net: add bpf skb forwarding program
This is needed for netkit container datapath selftests. Add two things:
1. nk_forward.bpf.c, a bpf program that forwards skbs matching some
IPv6 prefix received on eth0 ifindex to a specified netkit ifindex.
2. nk_forward.c, a C loader program that accepts eth0/netkit ifindex
and IPv6 prefix.
Selftests will load and unload this bpf program via the loader.
Signed-off-by: David Wei <dw@...idwei.uk>
---
.../selftests/drivers/net/hw/.gitignore | 3 +
.../testing/selftests/drivers/net/hw/Makefile | 9 +-
.../selftests/drivers/net/hw/nk_forward.bpf.c | 49 +++++++++
.../selftests/drivers/net/hw/nk_forward.c | 102 ++++++++++++++++++
4 files changed, 162 insertions(+), 1 deletion(-)
create mode 100644 tools/testing/selftests/drivers/net/hw/nk_forward.bpf.c
create mode 100644 tools/testing/selftests/drivers/net/hw/nk_forward.c
diff --git a/tools/testing/selftests/drivers/net/hw/.gitignore b/tools/testing/selftests/drivers/net/hw/.gitignore
index 6942bf575497..ca6947f30561 100644
--- a/tools/testing/selftests/drivers/net/hw/.gitignore
+++ b/tools/testing/selftests/drivers/net/hw/.gitignore
@@ -1,3 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
iou-zcrx
ncdevmem
+nk_forward
+*.skel.h
+tools/
diff --git a/tools/testing/selftests/drivers/net/hw/Makefile b/tools/testing/selftests/drivers/net/hw/Makefile
index 8133d1a0051c..855363bc8d48 100644
--- a/tools/testing/selftests/drivers/net/hw/Makefile
+++ b/tools/testing/selftests/drivers/net/hw/Makefile
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-2.0+ OR MIT
-TEST_GEN_FILES = iou-zcrx
+TEST_GEN_FILES = iou-zcrx nk_forward
TEST_PROGS = \
csum.py \
@@ -55,3 +55,10 @@ include ../../../net/ynl.mk
include ../../../net/bpf.mk
$(OUTPUT)/iou-zcrx: LDLIBS += -luring
+
+$(OUTPUT)/nk_forward: $(OUTPUT)/nk_forward.skel.h $(BPFOBJ)
+$(OUTPUT)/nk_forward: CFLAGS += $(CCINCLUDE) -I$(OUTPUT)
+$(OUTPUT)/nk_forward: LDLIBS += $(BPFOBJ) -lelf -lz
+
+$(OUTPUT)/nk_forward.skel.h: $(OUTPUT)/nk_forward.bpf.o
+ bpftool gen skeleton $< name nk_forward > $@
diff --git a/tools/testing/selftests/drivers/net/hw/nk_forward.bpf.c b/tools/testing/selftests/drivers/net/hw/nk_forward.bpf.c
new file mode 100644
index 000000000000..103b259d288a
--- /dev/null
+++ b/tools/testing/selftests/drivers/net/hw/nk_forward.bpf.c
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/bpf.h>
+#include <linux/pkt_cls.h>
+#include <linux/if_ether.h>
+#include <linux/ipv6.h>
+#include <linux/in6.h>
+#include <bpf/bpf_endian.h>
+#include <bpf/bpf_helpers.h>
+
+#define TC_ACT_OK 0
+#define ETH_P_IPV6 0x86DD
+
+#define ctx_ptr(field) (void *)(long)(field)
+
+#define v6_p64_equal(a, b) (a.s6_addr32[0] == b.s6_addr32[0] && \
+ a.s6_addr32[1] == b.s6_addr32[1])
+
+volatile __u32 netkit_ifindex;
+volatile __u8 ipv6_prefix[16] __attribute__((aligned(4)));
+
+SEC("tc/ingress")
+int tc_redirect_peer(struct __sk_buff *skb)
+{
+ void *data_end = ctx_ptr(skb->data_end);
+ void *data = ctx_ptr(skb->data);
+ struct in6_addr *peer_addr;
+ struct ipv6hdr *ip6h;
+ struct ethhdr *eth;
+
+ peer_addr = (struct in6_addr *)ipv6_prefix;
+
+ if (skb->protocol != bpf_htons(ETH_P_IPV6))
+ return TC_ACT_OK;
+
+ eth = data;
+ if ((void *)(eth + 1) > data_end)
+ return TC_ACT_OK;
+
+ ip6h = data + sizeof(struct ethhdr);
+ if ((void *)(ip6h + 1) > data_end)
+ return TC_ACT_OK;
+
+ if (!v6_p64_equal(ip6h->daddr, (*peer_addr)))
+ return TC_ACT_OK;
+
+ return bpf_redirect_peer(netkit_ifindex, 0);
+}
+
+char __license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/drivers/net/hw/nk_forward.c b/tools/testing/selftests/drivers/net/hw/nk_forward.c
new file mode 100644
index 000000000000..9519d20cd363
--- /dev/null
+++ b/tools/testing/selftests/drivers/net/hw/nk_forward.c
@@ -0,0 +1,102 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <net/if.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <linux/in6.h>
+#include <linux/if_link.h>
+#include <bpf/libbpf.h>
+#include <bpf/bpf.h>
+
+#include "nk_forward.skel.h"
+
+static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args)
+{
+ return vfprintf(stderr, format, args);
+}
+
+static void usage(const char *prog)
+{
+ fprintf(stderr, "Usage: %s -n <netkit_ifindex> -e <eth0_ifindex> -i <ipv6_prefix>\n", prog);
+ fprintf(stderr, " -n netkit interface index\n");
+ fprintf(stderr, " -e eth0 interface index\n");
+ fprintf(stderr, " -i IPv6 prefix to match\n");
+ fprintf(stderr, " -h show this help\n");
+}
+
+int main(int argc, char **argv)
+{
+ unsigned int netkit_ifindex = 0;
+ const char *ipv6_prefix = NULL;
+ unsigned int eth0_ifindex = 0;
+ struct nk_forward *skel;
+ struct in6_addr ip6_addr;
+ struct bpf_link *link;
+ int opt, err, i;
+
+ while ((opt = getopt(argc, argv, "n:e:i:h")) != -1) {
+ switch (opt) {
+ case 'n':
+ netkit_ifindex = atoi(optarg);
+ break;
+ case 'e':
+ eth0_ifindex = atoi(optarg);
+ break;
+ case 'i':
+ ipv6_prefix = optarg;
+ break;
+ case 'h':
+ default:
+ usage(argv[0]);
+ return opt == 'h' ? 0 : 1;
+ }
+ }
+
+ if (!netkit_ifindex || !eth0_ifindex || !ipv6_prefix) {
+ fprintf(stderr, "Error: All options -n, -e, and -i are required\n\n");
+ usage(argv[0]);
+ return 1;
+ }
+
+ if (inet_pton(AF_INET6, ipv6_prefix, &ip6_addr) != 1) {
+ fprintf(stderr, "Error: Invalid IPv6 address: %s\n", ipv6_prefix);
+ return 1;
+ }
+
+ libbpf_set_print(libbpf_print_fn);
+ skel = nk_forward__open();
+ if (!skel) {
+ fprintf(stderr, "Error: Failed to open BPF skeleton\n");
+ return 1;
+ }
+
+ skel->bss->netkit_ifindex = netkit_ifindex;
+ memcpy((void *)&skel->bss->ipv6_prefix, &ip6_addr, sizeof(struct in6_addr));
+
+ err = nk_forward__load(skel);
+ if (err) {
+ fprintf(stderr, "Error: Failed to load BPF skeleton: %d\n", err);
+ goto cleanup;
+ }
+
+ LIBBPF_OPTS(bpf_tcx_opts, opts);
+ link = bpf_program__attach_tcx(skel->progs.tc_redirect_peer, eth0_ifindex, &opts);
+ if (!link) {
+ err = -errno;
+ fprintf(stderr, "Error: Failed to attach TC program to ifindex %u: %s\n",
+ eth0_ifindex, strerror(errno));
+ goto cleanup;
+ }
+
+ while (1)
+ sleep(1);
+
+cleanup:
+ bpf_link__destroy(link);
+ nk_forward__destroy(skel);
+ return err != 0;
+}
--
2.47.3
Powered by blists - more mailing lists