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 --- .../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 +#include +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 -e -i \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