From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 8D257C636D4 for ; Mon, 30 Jan 2023 19:00:00 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237228AbjA3S77 (ORCPT ); Mon, 30 Jan 2023 13:59:59 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34068 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229573AbjA3S75 (ORCPT ); Mon, 30 Jan 2023 13:59:57 -0500 Received: from mail-pj1-x1049.google.com (mail-pj1-x1049.google.com [IPv6:2607:f8b0:4864:20::1049]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 5D93A37B40 for ; Mon, 30 Jan 2023 10:59:54 -0800 (PST) Received: by mail-pj1-x1049.google.com with SMTP id l6-20020a17090a72c600b0022bffd5f3ffso4985920pjk.3 for ; Mon, 30 Jan 2023 10:59:54 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=gPh2GGLzuKt77ji41T5HziXC5BtDvNF9DYeqboe8l9g=; b=BjH11f/qFDn3sLzpJbBD5q4h9nKS/kIpVF1ob74T9DiPAG5vSS8Qu58wLEtUkX4UbM 2nR/0Za+CpEzQFPsCwYPbbPyv44mWQ6nCm7bS4dXaoyt7gL3v1tmI29PK3NU5Y9h5upY M5NvLq2b5X720409ZzYhPrK/exqvfz/N+Jm5wAVStBKajUORZvtvfLlZPeWYRaDCaJb0 wqcOPNvO6zUzl1LKgNuFOKIXM65fwuHnN1Pia0caC6fi/YilV+ilwOXiYRrmYrv4ZJ4s oeYFJySorzJQ8L2DcFbxcFwYlgTzh+T2Od57XShgOR21TJmnug2Wffm7/tV/48DeROfB DF9g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=gPh2GGLzuKt77ji41T5HziXC5BtDvNF9DYeqboe8l9g=; b=5RvYtWkDUzyXvo2iWiAQZYSvBmEE+LOmczuA2emyTj8pZ2loXsqFQ7dX7qggrPJE9n f8DHVtPb6v8liiwkMUnbKjRr3gqCDDdlPWjoJYPzHkvFmIip14vYCMtUlX6QaJWs/iLw nV595BrvIuH7huiokNbRmHp3GrgREXjFw0P5wvtLQ6cm5fD8/3kpt+5zN7xF6GAOVjb7 6WYEWFoyuCbBSuhOPO7Qn1YtTXOFadg/QGPsHfoljONyKMGVOaGiqZp2sXn9nmN7vu15 jeNBbKSbUieQ9OiwuHGwcDtBPqrwMZhI4v2B7iCFFLjYqK8xDQEIguJhNQ/cgMER8Z95 SqYw== X-Gm-Message-State: AO0yUKXCWjkTUu3bFkUTwL2ocr3EtjRkViza93UyBF/SDCmHfnk05Zl9 HlcI544S7MdAmVELFHMX5fyY948= X-Google-Smtp-Source: AK7set8KUJA+/PZ5enP38sSDTCip2AV036jWXj/AeHr+sYyKcjI8phJAAYvHizWo2VjMLAR3AYyCYFo= X-Received: from sdf.c.googlers.com ([fda3:e722:ac3:cc00:7f:e700:c0a8:5935]) (user=sdf job=sendgmr) by 2002:a62:c501:0:b0:593:cbba:db0e with SMTP id j1-20020a62c501000000b00593cbbadb0emr618359pfg.51.1675105193762; Mon, 30 Jan 2023 10:59:53 -0800 (PST) Date: Mon, 30 Jan 2023 10:59:52 -0800 In-Reply-To: Mime-Version: 1.0 References: Message-ID: Subject: Re: [PATCH v4 bpf-next 8/8] selftests/bpf: introduce XDP compliance test tool From: Stanislav Fomichev To: Lorenzo Bianconi Cc: bpf@vger.kernel.org, netdev@vger.kernel.org, ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org, davem@davemloft.net, kuba@kernel.org, pabeni@redhat.com, edumazet@google.com, hawk@kernel.org, toke@redhat.com, memxor@gmail.com, alardam@gmail.com, saeedm@nvidia.com, anthony.l.nguyen@intel.com, gospo@broadcom.com, vladimir.oltean@nxp.com, nbd@nbd.name, john@phrozen.org, leon@kernel.org, simon.horman@corigine.com, aelior@marvell.com, christophe.jaillet@wanadoo.fr, ecree.xilinx@gmail.com, mst@redhat.com, bjorn@kernel.org, magnus.karlsson@intel.com, maciej.fijalkowski@intel.com, intel-wired-lan@lists.osuosl.org, lorenzo.bianconi@redhat.com, martin.lau@linux.dev Content-Type: text/plain; charset="UTF-8"; format=flowed; delsp=yes Precedence: bulk List-ID: X-Mailing-List: bpf@vger.kernel.org On 01/28, Lorenzo Bianconi wrote: > Introduce xdp_features tool in order to test XDP features supported by > the NIC and match them against advertised ones. > In order to test supported/advertised XDP features, xdp_features must > run on the Device Under Test (DUT) and on a Tester device. > xdp_features opens a control TCP channel between DUT and Tester devices > to send control commands from Tester to the DUT and a UDP data channel > where the Tester sends UDP 'echo' packets and the DUT is expected to > reply back with the same packet. DUT installs multiple XDP programs on the > NIC to test XDP capabilities and reports back to the Tester some XDP > stats. > Currently xdp_features supports the following XDP features: > - XDP_DROP > - XDP_PASS > - XDP_TX > - XDP_REDIRECT > - XDP_REDIRECT_TARGET > Co-developed-by: Kumar Kartikeya Dwivedi > Signed-off-by: Kumar Kartikeya Dwivedi > Signed-off-by: Lorenzo Bianconi > --- > tools/testing/selftests/bpf/.gitignore | 1 + > tools/testing/selftests/bpf/Makefile | 11 +- > .../selftests/bpf/progs/xdp_features.c | 275 +++++++ > .../selftests/bpf/test_xdp_features.sh | 100 +++ > tools/testing/selftests/bpf/xdp_features.c | 731 ++++++++++++++++++ > tools/testing/selftests/bpf/xdp_features.h | 33 + > 6 files changed, 1149 insertions(+), 2 deletions(-) > create mode 100644 tools/testing/selftests/bpf/progs/xdp_features.c > create mode 100755 tools/testing/selftests/bpf/test_xdp_features.sh > create mode 100644 tools/testing/selftests/bpf/xdp_features.c > create mode 100644 tools/testing/selftests/bpf/xdp_features.h > diff --git a/tools/testing/selftests/bpf/.gitignore > b/tools/testing/selftests/bpf/.gitignore > index 4aa5bba956ff..116fecf80ca1 100644 > --- a/tools/testing/selftests/bpf/.gitignore > +++ b/tools/testing/selftests/bpf/.gitignore > @@ -48,3 +48,4 @@ xskxceiver > xdp_redirect_multi > xdp_synproxy > xdp_hw_metadata > +xdp_features > diff --git a/tools/testing/selftests/bpf/Makefile > b/tools/testing/selftests/bpf/Makefile > index 53eae7be8dff..6f06b03d93cd 100644 > --- a/tools/testing/selftests/bpf/Makefile > +++ b/tools/testing/selftests/bpf/Makefile > @@ -73,7 +73,8 @@ TEST_PROGS := test_kmod.sh \ > test_bpftool.sh \ > test_bpftool_metadata.sh \ > test_doc_build.sh \ > - test_xsk.sh > + test_xsk.sh \ > + test_xdp_features.sh > TEST_PROGS_EXTENDED := with_addr.sh \ > with_tunnels.sh ima_setup.sh verify_sig_setup.sh \ > @@ -83,7 +84,8 @@ TEST_PROGS_EXTENDED := with_addr.sh \ > TEST_GEN_PROGS_EXTENDED = test_sock_addr test_skb_cgroup_id_user \ > flow_dissector_load test_flow_dissector test_tcp_check_syncookie_user \ > test_lirc_mode2_user xdping test_cpp runqslower bench bpf_testmod.ko \ > - xskxceiver xdp_redirect_multi xdp_synproxy veristat xdp_hw_metadata > + xskxceiver xdp_redirect_multi xdp_synproxy veristat xdp_hw_metadata \ > + xdp_features > TEST_CUSTOM_PROGS = $(OUTPUT)/urandom_read $(OUTPUT)/sign-file > TEST_GEN_FILES += liburandom_read.so > @@ -385,6 +387,7 @@ test_subskeleton_lib.skel.h-deps := > test_subskeleton_lib2.bpf.o test_subskeleton > test_usdt.skel.h-deps := test_usdt.bpf.o test_usdt_multispec.bpf.o > xsk_xdp_progs.skel.h-deps := xsk_xdp_progs.bpf.o > xdp_hw_metadata.skel.h-deps := xdp_hw_metadata.bpf.o > +xdp_features.skel.h-deps := xdp_features.bpf.o > LINKED_BPF_SRCS := $(patsubst %.bpf.o,%.c,$(foreach > skel,$(LINKED_SKELS),$($(skel)-deps))) > @@ -586,6 +589,10 @@ $(OUTPUT)/xdp_hw_metadata: xdp_hw_metadata.c > $(OUTPUT)/network_helpers.o $(OUTPU > $(call msg,BINARY,,$@) > $(Q)$(CC) $(CFLAGS) $(filter %.a %.o %.c,$^) $(LDLIBS) -o $@ > +$(OUTPUT)/xdp_features: xdp_features.c $(OUTPUT)/network_helpers.o > $(OUTPUT)/xdp_features.skel.h | $(OUTPUT) > + $(call msg,BINARY,,$@) > + $(Q)$(CC) $(CFLAGS) $(filter %.a %.o %.c,$^) $(LDLIBS) -o $@ > + > # Make sure we are able to include and link libbpf against c++. > $(OUTPUT)/test_cpp: test_cpp.cpp $(OUTPUT)/test_core_extern.skel.h > $(BPFOBJ) > $(call msg,CXX,,$@) > diff --git a/tools/testing/selftests/bpf/progs/xdp_features.c > b/tools/testing/selftests/bpf/progs/xdp_features.c > new file mode 100644 > index 000000000000..205a4526ea68 > --- /dev/null > +++ b/tools/testing/selftests/bpf/progs/xdp_features.c > @@ -0,0 +1,275 @@ > +// SPDX-License-Identifier: GPL-2.0 > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include "xdp_features.h" > + > +#define ipv6_addr_equal(a, b) ((a).s6_addr32[0] == (b).s6_addr32[0] && \ > + (a).s6_addr32[1] == (b).s6_addr32[1] && \ > + (a).s6_addr32[2] == (b).s6_addr32[2] && \ > + (a).s6_addr32[3] == (b).s6_addr32[3]) > + > +struct net_device; > +struct bpf_prog; > + > +struct xdp_cpumap_stats { > + unsigned int redirect; > + unsigned int pass; > + unsigned int drop; > +}; > + > +struct { > + __uint(type, BPF_MAP_TYPE_ARRAY); > + __type(key, __u32); > + __type(value, __u32); > + __uint(max_entries, 1); > +} stats SEC(".maps"); > + > +struct { > + __uint(type, BPF_MAP_TYPE_ARRAY); > + __type(key, __u32); > + __type(value, __u32); > + __uint(max_entries, 1); > +} dut_stats SEC(".maps"); > + > +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"); > + > +struct { > + __uint(type, BPF_MAP_TYPE_DEVMAP); > + __uint(key_size, sizeof(__u32)); > + __uint(value_size, sizeof(struct bpf_devmap_val)); > + __uint(max_entries, 1); > +} dev_map SEC(".maps"); > + > +const volatile __u32 expected_feature = XDP_FEATURE_PASS; > +const volatile union { > + struct in_addr ip; > + struct in6_addr ip6; > +} tester_addr; > +const volatile union { > + struct in_addr ip; > + struct in6_addr ip6; > +} dut_addr; > + > +static __always_inline int xdp_process_echo_packet(struct xdp_md *xdp, > bool dut) > +{ > + void *data_end = (void *)(long)xdp->data_end; > + void *data = (void *)(long)xdp->data; > + struct ethhdr *eh = data; > + struct tlv_hdr *tlv; > + struct udphdr *uh; > + __be16 port; > + __u8 *cmd; > + > + if (eh + 1 > (struct ethhdr *)data_end) > + return -EINVAL; > + > + if (eh->h_proto == bpf_htons(ETH_P_IP)) { > + struct iphdr *ih = (struct iphdr *)(eh + 1); > + __be32 saddr = dut ? tester_addr.ip.s_addr : dut_addr.ip.s_addr; > + __be32 daddr = dut ? dut_addr.ip.s_addr : tester_addr.ip.s_addr; > + > + ih = (struct iphdr *)(eh + 1); > + if (ih + 1 > (struct iphdr *)data_end) > + return -EINVAL; > + > + if (saddr != ih->saddr) > + return -EINVAL; > + > + if (daddr != ih->daddr) > + return -EINVAL; > + > + if (ih->protocol != IPPROTO_UDP) > + return -EINVAL; > + > + uh = (struct udphdr *)(ih + 1); > + } else if (eh->h_proto == bpf_htons(ETH_P_IPV6)) { > + struct in6_addr saddr = dut ? tester_addr.ip6 : dut_addr.ip6; > + struct in6_addr daddr = dut ? dut_addr.ip6 : tester_addr.ip6; > + struct ipv6hdr *ih6 = (struct ipv6hdr *)(eh + 1); > + > + if (ih6 + 1 > (struct ipv6hdr *)data_end) > + return -EINVAL; > + > + if (!ipv6_addr_equal(saddr, ih6->saddr)) > + return -EINVAL; > + > + if (!ipv6_addr_equal(daddr, ih6->daddr)) > + return -EINVAL; > + > + if (ih6->nexthdr != IPPROTO_UDP) > + return -EINVAL; > + > + uh = (struct udphdr *)(ih6 + 1); > + } else { > + return -EINVAL; > + } > + > + if (uh + 1 > (struct udphdr *)data_end) > + return -EINVAL; > + > + port = dut ? uh->dest : uh->source; > + if (port != bpf_htons(DUT_ECHO_PORT)) > + return -EINVAL; > + > + tlv = (struct tlv_hdr *)(uh + 1); > + if (tlv + 1 > data_end) > + return -EINVAL; > + > + return bpf_htons(tlv->type) == CMD_ECHO ? 0 : -EINVAL; > +} > + > +SEC("xdp") > +int xdp_tester(struct xdp_md *xdp) > +{ > + __u32 *val, key = 0; > + > + switch (expected_feature) { > + case XDP_FEATURE_NDO_XMIT: > + case XDP_FEATURE_TX: > + if (xdp_process_echo_packet(xdp, true)) > + goto out; > + break; > + case XDP_FEATURE_DROP: > + case XDP_FEATURE_PASS: > + case XDP_FEATURE_REDIRECT: > + if (xdp_process_echo_packet(xdp, false)) > + goto out; > + break; > + default: > + goto out; > + } > + > + val = bpf_map_lookup_elem(&stats, &key); > + if (val) > + __sync_add_and_fetch(val, 1); > + > +out: > + return XDP_PASS; > +} > + > +SEC("xdp") > +int xdp_do_pass(struct xdp_md *xdp) > +{ > + __u32 *val, key = 0; > + > + val = bpf_map_lookup_elem(&dut_stats, &key); > + if (val) > + __sync_add_and_fetch(val, 1); > + > + return XDP_PASS; > +} > + > +SEC("xdp") > +int xdp_do_drop(struct xdp_md *xdp) > +{ > + __u32 *val, key = 0; > + > + if (xdp_process_echo_packet(xdp, true)) > + return XDP_PASS; > + > + val = bpf_map_lookup_elem(&dut_stats, &key); > + if (val) > + __sync_add_and_fetch(val, 1); > + > + return XDP_DROP; > +} > + > +SEC("xdp") > +int xdp_do_aborted(struct xdp_md *xdp) > +{ > + return xdp_process_echo_packet(xdp, true) ? XDP_PASS : XDP_ABORTED; > +} > + > +SEC("xdp") > +int xdp_do_tx(struct xdp_md *xdp) > +{ > + void *data = (void *)(long)xdp->data; > + struct ethhdr *eh = data; > + __u8 tmp_mac[ETH_ALEN]; > + __u32 *val, key = 0; > + > + if (xdp_process_echo_packet(xdp, true)) > + return XDP_PASS; > + > + __builtin_memcpy(tmp_mac, eh->h_source, ETH_ALEN); > + __builtin_memcpy(eh->h_source, eh->h_dest, ETH_ALEN); > + __builtin_memcpy(eh->h_dest, tmp_mac, ETH_ALEN); > + > + val = bpf_map_lookup_elem(&dut_stats, &key); > + if (val) > + __sync_add_and_fetch(val, 1); > + > + return XDP_TX; > +} > + > +SEC("xdp") > +int xdp_do_redirect(struct xdp_md *xdp) > +{ > + if (xdp_process_echo_packet(xdp, true)) > + return XDP_PASS; > + > + return bpf_redirect_map(&cpu_map, 0, 0); > +} > + > +SEC("tp_btf/xdp_exception") > +int BPF_PROG(xdp_exception, const struct net_device *dev, > + const struct bpf_prog *xdp, __u32 act) > +{ > + __u32 *val, key = 0; > + > + val = bpf_map_lookup_elem(&dut_stats, &key); > + if (val) > + __sync_add_and_fetch(val, 1); > + > + return 0; > +} > + > +SEC("tp_btf/xdp_cpumap_kthread") > +int BPF_PROG(tp_xdp_cpumap_kthread, int map_id, unsigned int processed, > + unsigned int drops, int sched, struct xdp_cpumap_stats *xdp_stats) > +{ > + __u32 *val, key = 0; > + > + val = bpf_map_lookup_elem(&dut_stats, &key); > + if (val) > + __sync_add_and_fetch(val, 1); > + > + return 0; > +} > + > +SEC("xdp/cpumap") > +int xdp_do_redirect_cpumap(struct xdp_md *xdp) > +{ > + void *data = (void *)(long)xdp->data; > + struct ethhdr *eh = data; > + __u8 tmp_mac[ETH_ALEN]; > + > + if (xdp_process_echo_packet(xdp, true)) > + return XDP_PASS; > + > + __builtin_memcpy(tmp_mac, eh->h_source, ETH_ALEN); > + __builtin_memcpy(eh->h_source, eh->h_dest, ETH_ALEN); > + __builtin_memcpy(eh->h_dest, tmp_mac, ETH_ALEN); > + > + return bpf_redirect_map(&dev_map, 0, 0); > +} > + > +char _license[] SEC("license") = "GPL"; > diff --git a/tools/testing/selftests/bpf/test_xdp_features.sh > b/tools/testing/selftests/bpf/test_xdp_features.sh > new file mode 100755 > index 000000000000..987845e18ea7 > --- /dev/null > +++ b/tools/testing/selftests/bpf/test_xdp_features.sh > @@ -0,0 +1,100 @@ > +#!/bin/bash > +# SPDX-License-Identifier: GPL-2.0 > + > +readonly NS="ns1-$(mktemp -u XXXXXX)" > +readonly V0_IP=10.10.0.11 > +readonly V1_IP=10.10.0.1 > +readonly V0_IP6=2001:db8::11 > +readonly V1_IP6=2001:db8::1 > + > +ret=1 > + > +setup() { > + { > + ip netns add ${NS} > + > + ip link add v1 type veth peer name v0 netns ${NS} > + > + ip link set v1 up > + ip addr add $V1_IP/24 dev v1 > + ip addr add $V1_IP6/64 nodad dev v1 > + ip -n ${NS} link set dev v0 up > + ip -n ${NS} addr add $V0_IP/24 dev v0 > + ip -n ${NS} addr add $V0_IP6/64 nodad dev v0 > + > + sysctl -w net.ipv4.ip_forward=1 > + > + # Enable XDP mode and disable checksum offload > + ethtool -K v1 gro on > + ethtool -K v1 tx-checksumming off > + ip netns exec ${NS} ethtool -K v0 gro on > + ip netns exec ${NS} ethtool -K v0 tx-checksumming off > + } > /dev/null 2>&1 > +} > + > +cleanup() { > + ip link del v1 2> /dev/null > + ip netns del ${NS} 2> /dev/null > + [ "$(pidof xdp_features)" = "" ] || kill $(pidof xdp_features) 2> > /dev/null > +} > + > +wait_for_dut_server() { > + while sleep 1; do > + ss -tlp | grep -q xdp_features > + [ $? -eq 0 ] && break > + done > +} > + > +test_xdp_features() { > + setup > + > + ## XDP_PASS > + ./xdp_features -6 -f XDP_PASS -D $V1_IP6 -T $V0_IP6 v1 & > + wait_for_dut_server > + ip netns exec ${NS} ./xdp_features -6 -t -f XDP_PASS -D $V1_IP6 -C > $V1_IP6 -T $V0_IP6 v0 > + > + [ $? -ne 0 ] && exit > + > + ## XDP_DROP > + ./xdp_features -f XDP_DROP -D $V1_IP -T $V0_IP v1 & > + wait_for_dut_server > + ip netns exec ${NS} ./xdp_features -t -f XDP_DROP -D $V1_IP -C $V1_IP > -T $V0_IP v0 > + > + [ $? -ne 0 ] && exit > + > + ## XDP_ABORTED > + ./xdp_features -6 -f XDP_ABORTED -D $V1_IP6 -T $V0_IP6 v1 & > + wait_for_dut_server > + ip netns exec ${NS} ./xdp_features -6 -t -f XDP_ABORTED -D $V1_IP6 -C > $V1_IP6 -T $V0_IP6 v0 > + > + [ $? -ne 0 ] && exit > + > + ## XDP_TX > + ./xdp_features -f XDP_TX -D $V1_IP -T $V0_IP v1 & > + wait_for_dut_server > + ip netns exec ${NS} ./xdp_features -t -f XDP_TX -D $V1_IP -C $V1_IP -T > $V0_IP v0 > + > + [ $? -ne 0 ] && exit > + > + ## XDP_REDIRECT > + ./xdp_features -6 -f XDP_REDIRECT -D $V1_IP6 -T $V0_IP6 v1 & > + wait_for_dut_server > + ip netns exec ${NS} ./xdp_features -6 -t -f XDP_REDIRECT -D $V1_IP6 -C > $V1_IP6 -T $V0_IP6 v0 > + > + [ $? -ne 0 ] && exit > + > + ## XDP_NDO_XMIT > + ./xdp_features -f XDP_NDO_XMIT -D $V1_IP -T $V0_IP v1 & > + wait_for_dut_server > + ip netns exec ${NS} ./xdp_features -t -f XDP_NDO_XMIT -D $V1_IP -C > $V1_IP -T $V0_IP v0 > + > + ret=$? > + cleanup > +} > + > +set -e > +trap cleanup 2 3 6 9 > + > +test_xdp_features > + > +exit $ret > diff --git a/tools/testing/selftests/bpf/xdp_features.c > b/tools/testing/selftests/bpf/xdp_features.c > new file mode 100644 > index 000000000000..9b94d5cdf654 > --- /dev/null > +++ b/tools/testing/selftests/bpf/xdp_features.c > @@ -0,0 +1,731 @@ > +// SPDX-License-Identifier: GPL-2.0 > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include > + > +#include "xdp_features.skel.h" > +#include "xdp_features.h" > + > +#define RED(str) "\033[0;31m" str "\033[0m" > +#define GREEN(str) "\033[0;32m" str "\033[0m" > +#define YELLOW(str) "\033[0;33m" str "\033[0m" > + > +static struct env { > + bool verbosity; > + int ifindex; > + unsigned int feature; > + bool is_tester; > + int family; > + struct { > + struct sockaddr_storage addr; > + socklen_t addrlen; > + } dut_ctrl; > + struct { > + struct sockaddr_storage addr; > + socklen_t addrlen; > + } dut; > + struct { > + struct sockaddr_storage addr; > + socklen_t addrlen; > + } tester; > +} env; > + > +#define BUFSIZE 128 > + > +void test__fail(void) { /* for network_helpers.c */ } > + > +static int libbpf_print_fn(enum libbpf_print_level level, > + const char *format, va_list args) > +{ > + if (level == LIBBPF_DEBUG && !env.verbosity) > + return 0; > + return vfprintf(stderr, format, args); > +} > + > +static volatile bool exiting; > + > +static void sig_handler(int sig) > +{ > + exiting = true; > +} > + > +const char *argp_program_version = "xdp-features 0.0"; > +const char argp_program_doc[] = > +"XDP features detecion application.\n" > +"\n" > +"XDP features application checks the XDP advertised features match > detected ones.\n" > +"\n" > +"USAGE: ./xdp-features [-6vt] [-f ] [-D ] [-T > ] [-C ] \n" > +"\n" > +"XDP features\n:" > +"- XDP_PASS\n" > +"- XDP_DROP\n" > +"- XDP_ABORTED\n" > +"- XDP_REDIRECT\n" > +"- XDP_NDO_XMIT\n" > +"- XDP_TX\n"; > + > +static const struct argp_option opts[] = { > + { "ipv6", '6', NULL, 0, "Use IPv6 network stack" }, > + { "verbose", 'v', NULL, 0, "Verbose debug output" }, > + { "tester", 't', NULL, 0, "Tester mode" }, > + { "feature", 'f', "XDP-FEATURE", 0, "XDP feature to test" }, > + { "dut_data_ip", 'D', "DUT-DATA-IP", 0, "DUT IP data channel" }, > + { "dut_ctrl_ip", 'C', "DUT-CTRL-IP", 0, "DUT IP control channel" }, > + { "tester_data_ip", 'T', "TESTER-DATA-IP", 0, "Tester IP data channel" > }, > + {}, > +}; > + > +static int get_xdp_feature(const char *arg) > +{ > + if (!strcmp(arg, "XDP_PASS")) > + return XDP_FEATURE_PASS; > + else if (!strcmp(arg, "XDP_DROP")) > + return XDP_FEATURE_DROP; > + else if (!strcmp(arg, "XDP_ABORTED")) > + return XDP_FEATURE_ABORTED; > + else if (!strcmp(arg, "XDP_REDIRECT")) > + return XDP_FEATURE_REDIRECT; > + else if (!strcmp(arg, "XDP_NDO_XMIT")) > + return XDP_FEATURE_NDO_XMIT; > + else if (!strcmp(arg, "XDP_TX")) > + return XDP_FEATURE_TX; > + > + return -EINVAL; > +} > + > +static char *get_xdp_feature_str(int feature) > +{ > + switch (feature) { > + case XDP_FEATURE_PASS: > + return YELLOW("XDP_PASS"); > + case XDP_FEATURE_DROP: > + return YELLOW("XDP_DROP"); > + case XDP_FEATURE_ABORTED: > + return YELLOW("XDP_ABORTED"); > + case XDP_FEATURE_TX: > + return YELLOW("XDP_TX"); > + case XDP_FEATURE_REDIRECT: > + return YELLOW("XDP_REDIRECT"); > + case XDP_FEATURE_NDO_XMIT: > + return YELLOW("XDP_NDO_XMIT"); > + default: > + return ""; > + } > +} > + > +static error_t parse_arg(int key, char *arg, struct argp_state *state) > +{ > + switch (key) { > + case '6': > + env.family = AF_INET6; > + break; Thanks for making the changes! Since you're doing another respin to address Alexei's feedback, maybe one thing we can also improve here regarding v4-v6? Optionally, up to you, but IMO, it's much easier to hard-code AF_INET6 everywhere: socket(AF_INET6, ...) inet_pton(AF_INET6, ...) ... And then, if you want to use v4 by default, use '::ffff:127.0.0.1' as the address. If the users need to use it with some other v4, they can also pass the v4-mapped v6 address. Up to you on whether to implement it or not, feel free to ignore me, but IMO, that should avoid a lot of those env.family arguments and making special cases for v4. Plus also removes the need for -6 argument. The following would work: ./xdp_features ::ffff:: ./xdp_features (I'm assuming nothing runs exclusive non-dualstack v4 envs anymore) > + case 'v': > + env.verbosity = true; > + break; > + case 't': > + env.is_tester = true; > + break; > + case 'f': > + env.feature = get_xdp_feature(arg); > + if (env.feature < 0) { > + fprintf(stderr, "Invalid xdp feature: %s\n", arg); > + argp_usage(state); > + return ARGP_ERR_UNKNOWN; > + } > + break; > + case 'D': > + if (make_sockaddr(env.family, arg, DUT_ECHO_PORT, > + &env.dut.addr, &env.dut.addrlen)) { > + fprintf(stderr, "Invalid DUT address: %s\n", arg); > + return ARGP_ERR_UNKNOWN; > + } > + break; > + case 'C': > + if (make_sockaddr(env.family, arg, DUT_CTRL_PORT, > + &env.dut_ctrl.addr, &env.dut_ctrl.addrlen)) { > + fprintf(stderr, "Invalid DUT CTRL address: %s\n", arg); > + return ARGP_ERR_UNKNOWN; > + } > + break; > + case 'T': > + if (make_sockaddr(env.family, arg, 0, &env.tester.addr, > + &env.tester.addrlen)) { > + fprintf(stderr, "Invalid Tester address: %s\n", arg); > + return ARGP_ERR_UNKNOWN; > + } > + break; > + case ARGP_KEY_ARG: > + errno = 0; > + if (strlen(arg) >= IF_NAMESIZE) { > + fprintf(stderr, "Invalid device name: %s\n", arg); > + argp_usage(state); > + return ARGP_ERR_UNKNOWN; > + } > + > + env.ifindex = if_nametoindex(arg); > + if (!env.ifindex) > + env.ifindex = strtoul(arg, NULL, 0); > + if (!env.ifindex) { > + fprintf(stderr, > + "Bad interface index or name (%d): %s\n", > + errno, strerror(errno)); > + argp_usage(state); > + return ARGP_ERR_UNKNOWN; > + } > + break; > + default: > + return ARGP_ERR_UNKNOWN; > + } > + > + return 0; > +} > + > +static const struct argp argp = { > + .options = opts, > + .parser = parse_arg, > + .doc = argp_program_doc, > +}; > + > +static void set_env_defaul(void) > +{ > + env.feature = XDP_FEATURE_PASS; > + env.ifindex = -ENODEV; > + env.family = AF_INET; > + make_sockaddr(AF_INET, "127.0.0.1", DUT_CTRL_PORT, &env.dut_ctrl.addr, > + &env.dut_ctrl.addrlen); > + make_sockaddr(AF_INET, "127.0.0.1", DUT_ECHO_PORT, &env.dut.addr, > + &env.dut.addrlen); > + make_sockaddr(AF_INET, "127.0.0.1", 0, &env.tester.addr, > + &env.tester.addrlen); > +} > + > +static void *dut_echo_thread(void *arg) > +{ > + unsigned char buf[sizeof(struct tlv_hdr)]; > + int sockfd = *(int *)arg; > + > + while (!exiting) { > + struct tlv_hdr *tlv = (struct tlv_hdr *)buf; > + struct sockaddr_storage addr; > + socklen_t addrlen; > + size_t n; > + > + n = recvfrom(sockfd, buf, sizeof(buf), MSG_WAITALL, > + (struct sockaddr *)&addr, &addrlen); > + if (n != ntohs(tlv->len)) > + continue; > + > + if (ntohs(tlv->type) != CMD_ECHO) > + continue; > + > + sendto(sockfd, buf, sizeof(buf), MSG_NOSIGNAL | MSG_CONFIRM, > + (struct sockaddr *)&addr, addrlen); > + } > + > + pthread_exit((void *)0); > + close(sockfd); > + > + return NULL; > +} > + > +static int dut_run_echo_thread(pthread_t *t, int *sockfd) > +{ > + int err; > + > + sockfd = start_reuseport_server(env.family, SOCK_DGRAM, NULL, > + DUT_ECHO_PORT, 0, 1); > + if (!sockfd) { > + fprintf(stderr, "Failed to create echo socket\n"); > + return -errno; > + } > + > + /* start echo channel */ > + err = pthread_create(t, NULL, dut_echo_thread, sockfd); > + if (err) { > + fprintf(stderr, "Failed creating dut_echo thread: %s\n", > + strerror(-err)); > + free_fds(sockfd, 1); > + return -EINVAL; > + } > + > + return 0; > +} > + > +static int dut_attach_xdp_prog(struct xdp_features *skel, int feature, > + int flags) > +{ > + struct bpf_program *prog; > + unsigned int key = 0; > + int err, fd = 0; > + > + switch (feature) { > + case XDP_FEATURE_TX: > + prog = skel->progs.xdp_do_tx; > + break; > + case XDP_FEATURE_DROP: > + prog = skel->progs.xdp_do_drop; > + break; > + case XDP_FEATURE_ABORTED: > + prog = skel->progs.xdp_do_aborted; > + break; > + case XDP_FEATURE_PASS: > + prog = skel->progs.xdp_do_pass; > + break; > + case XDP_FEATURE_NDO_XMIT: { > + struct bpf_devmap_val entry = { > + .ifindex = env.ifindex, > + }; > + > + err = bpf_map__update_elem(skel->maps.dev_map, > + &key, sizeof(key), > + &entry, sizeof(entry), 0); > + if (err < 0) > + return err; > + > + fd = bpf_program__fd(skel->progs.xdp_do_redirect_cpumap); > + } > + case XDP_FEATURE_REDIRECT: { > + struct bpf_cpumap_val entry = { > + .qsize = 2048, > + .bpf_prog.fd = fd, > + }; > + > + err = bpf_map__update_elem(skel->maps.cpu_map, > + &key, sizeof(key), > + &entry, sizeof(entry), 0); > + if (err < 0) > + return err; > + > + prog = skel->progs.xdp_do_redirect; > + break; > + } > + default: > + return -EINVAL; > + } > + > + err = bpf_xdp_attach(env.ifindex, bpf_program__fd(prog), flags, NULL); > + if (err) > + fprintf(stderr, > + "Failed to attach XDP program to ifindex %d\n", > + env.ifindex); > + return err; > +} > + > +static int __recv_msg(int sockfd, void *buf, size_t bufsize, > + unsigned int *val, unsigned int val_size) > +{ > + struct tlv_hdr *tlv = (struct tlv_hdr *)buf; > + int len, n = sizeof(*tlv), i = 0; > + > + len = recv(sockfd, buf, bufsize, 0); > + if (len != ntohs(tlv->len)) > + return -EINVAL; > + > + while (n < len && i < val_size) { > + val[i] = ntohl(tlv->data[i]); > + n += sizeof(tlv->data[0]); > + i++; > + } > + > + return i; > +} > + > +static int recv_msg(int sockfd, void *buf, size_t bufsize) > +{ > + return __recv_msg(sockfd, buf, bufsize, NULL, 0); > +} > + > +static int dut_run(struct xdp_features *skel) > +{ > + int flags = XDP_FLAGS_UPDATE_IF_NOEXIST | XDP_FLAGS_DRV_MODE; > + int state, err, *sockfd, ctrl_sockfd, echo_sockfd; > + struct sockaddr_storage ctrl_addr; > + pthread_t dut_thread; > + socklen_t addrlen; > + > + sockfd = start_reuseport_server(env.family, SOCK_STREAM, NULL, > + DUT_CTRL_PORT, 0, 1); > + if (!sockfd) { > + fprintf(stderr, "Failed to create DUT socket\n"); > + return -errno; > + } > + > + ctrl_sockfd = accept(*sockfd, (struct sockaddr *)&ctrl_addr, &addrlen); > + if (ctrl_sockfd < 0) { > + fprintf(stderr, "Failed to accept connection on DUT socket\n"); > + free_fds(sockfd, 1); > + return -errno; > + } > + > + /* CTRL loop */ > + while (!exiting) { > + unsigned char buf[BUFSIZE] = {}; > + struct tlv_hdr *tlv = (struct tlv_hdr *)buf; > + > + err = recv_msg(ctrl_sockfd, buf, BUFSIZE); > + if (err) > + continue; > + > + switch (ntohs(tlv->type)) { > + case CMD_START: { > + if (state == CMD_START) > + continue; > + > + state = CMD_START; > + /* Load the XDP program on the DUT */ > + err = dut_attach_xdp_prog(skel, ntohl(tlv->data[0]), flags); > + if (err) > + goto out; > + > + err = dut_run_echo_thread(&dut_thread, &echo_sockfd); > + if (err < 0) > + goto out; > + > + tlv->type = htons(CMD_ACK); > + tlv->len = htons(sizeof(*tlv)); > + err = send(ctrl_sockfd, buf, sizeof(*tlv), 0); > + if (err < 0) > + goto end_thread; > + break; > + } > + case CMD_STOP: > + if (state != CMD_START) > + break; > + > + state = CMD_STOP; > + > + exiting = true; > + bpf_xdp_detach(env.ifindex, flags, NULL); > + > + tlv->type = htons(CMD_ACK); > + tlv->len = htons(sizeof(*tlv)); > + err = send(ctrl_sockfd, buf, sizeof(*tlv), 0); > + goto end_thread; > + case CMD_GET_XDP_CAP: { > + LIBBPF_OPTS(bpf_xdp_query_opts, opts); > + size_t n; > + > + err = bpf_xdp_query(env.ifindex, XDP_FLAGS_DRV_MODE, > + &opts); > + if (err) { > + fprintf(stderr, > + "Failed to query XDP cap for ifindex %d\n", > + env.ifindex); > + goto end_thread; > + } > + > + tlv->type = htons(CMD_ACK); > + n = sizeof(*tlv) + sizeof(opts.feature_flags); > + tlv->len = htons(n); > + tlv->data[0] = htonl(opts.feature_flags); > + > + err = send(ctrl_sockfd, buf, n, 0); > + if (err < 0) > + goto end_thread; > + break; > + } > + case CMD_GET_STATS: { > + unsigned int key = 0, val; > + size_t n; > + > + err = bpf_map__lookup_elem(skel->maps.dut_stats, > + &key, sizeof(key), > + &val, sizeof(val), 0); > + if (err) { > + fprintf(stderr, "bpf_map_lookup_elem failed\n"); > + goto end_thread; > + } > + > + tlv->type = htons(CMD_ACK); > + n = sizeof(*tlv) + sizeof(val); > + tlv->len = htons(n); > + tlv->data[0] = htonl(val); > + > + err = send(ctrl_sockfd, buf, n, 0); > + if (err < 0) > + goto end_thread; > + break; > + } > + default: > + break; > + } > + } > + > +end_thread: > + pthread_join(dut_thread, NULL); > +out: > + bpf_xdp_detach(env.ifindex, flags, NULL); > + close(ctrl_sockfd); > + free_fds(sockfd, 1); > + > + return err; > +} > + > +static bool tester_collect_advertised_cap(unsigned int cap) > +{ > + switch (env.feature) { > + case XDP_FEATURE_ABORTED: > + case XDP_FEATURE_DROP: > + case XDP_FEATURE_PASS: > + case XDP_FEATURE_TX: > + return cap & NETDEV_XDP_ACT_BASIC; > + case XDP_FEATURE_REDIRECT: > + return cap & NETDEV_XDP_ACT_REDIRECT; > + case XDP_FEATURE_NDO_XMIT: > + return cap & NETDEV_XDP_ACT_NDO_XMIT; > + default: > + return false; > + } > +} > + > +static bool tester_collect_detected_cap(struct xdp_features *skel, > + unsigned int dut_stats) > +{ > + unsigned int err, key = 0, val; > + > + if (!dut_stats) > + return false; > + > + err = bpf_map__lookup_elem(skel->maps.stats, &key, sizeof(key), > + &val, sizeof(val), 0); > + if (err) { > + fprintf(stderr, "bpf_map_lookup_elem failed\n"); > + return false; > + } > + > + switch (env.feature) { > + case XDP_FEATURE_PASS: > + case XDP_FEATURE_TX: > + case XDP_FEATURE_REDIRECT: > + case XDP_FEATURE_NDO_XMIT: > + return val > 0; > + case XDP_FEATURE_DROP: > + case XDP_FEATURE_ABORTED: > + return val == 0; > + default: > + return false; > + } > +} > + > +static int __send_and_recv_msg(int sockfd, enum test_commands cmd, > + unsigned int *val, unsigned int val_size) > +{ > + unsigned char buf[BUFSIZE] = {}; > + struct tlv_hdr *tlv = (struct tlv_hdr *)buf; > + int n = sizeof(*tlv), err; > + > + tlv->type = htons(cmd); > + switch (cmd) { > + case CMD_START: > + tlv->data[0] = htonl(env.feature); > + n += sizeof(*val); > + break; > + default: > + break; > + } > + tlv->len = htons(n); > + > + err = send(sockfd, buf, n, 0); > + if (err < 0) > + return err; > + > + err = __recv_msg(sockfd, buf, BUFSIZE, val, val_size); > + if (err < 0) > + return err; > + > + return ntohs(tlv->type) == CMD_ACK ? 0 : -EINVAL; > +} > + > +static int send_and_recv_msg(int sockfd, enum test_commands cmd) > +{ > + return __send_and_recv_msg(sockfd, cmd, NULL, 0); > +} > + > +static int send_echo_msg(void) > +{ > + unsigned char buf[sizeof(struct tlv_hdr)]; > + struct tlv_hdr *tlv = (struct tlv_hdr *)buf; > + int sockfd, n; > + > + sockfd = socket(env.family, SOCK_DGRAM, 0); > + if (sockfd < 0) { > + fprintf(stderr, "Failed to create echo socket\n"); > + return -errno; > + } > + > + tlv->type = htons(CMD_ECHO); > + tlv->len = htons(sizeof(*tlv)); > + > + n = sendto(sockfd, buf, sizeof(*tlv), MSG_NOSIGNAL | MSG_CONFIRM, > + (struct sockaddr *)&env.dut.addr, env.dut.addrlen); > + close(sockfd); > + > + return n == ntohs(tlv->len) ? 0 : -EINVAL; > +} > + > +static int tester_run(struct xdp_features *skel) > +{ > + int flags = XDP_FLAGS_UPDATE_IF_NOEXIST | XDP_FLAGS_DRV_MODE; > + bool advertised_cap; > + unsigned int val[1]; > + int i, err, sockfd; > + bool detected_cap; > + > + sockfd = socket(env.family, SOCK_STREAM, 0); > + if (sockfd < 0) { > + fprintf(stderr, "Failed to create tester socket\n"); > + return -errno; > + } > + > + if (settimeo(sockfd, 1000) < 0) > + return -EINVAL; > + > + err = connect(sockfd, (struct sockaddr *)&env.dut_ctrl.addr, > + env.dut_ctrl.addrlen); > + if (err) { > + fprintf(stderr, "Failed to connect to the DUT\n"); > + return -errno; > + } > + > + err = __send_and_recv_msg(sockfd, CMD_GET_XDP_CAP, val, > + ARRAY_SIZE(val)); > + if (err < 0) { > + close(sockfd); > + return err; > + } > + > + advertised_cap = tester_collect_advertised_cap(val[0]); > + > + err = bpf_xdp_attach(env.ifindex, > + bpf_program__fd(skel->progs.xdp_tester), > + flags, NULL); > + if (err) { > + fprintf(stderr, "Failed to attach XDP program to ifindex %d\n", > + env.ifindex); > + goto out; > + } > + > + err = send_and_recv_msg(sockfd, CMD_START); > + if (err) > + goto out; > + > + for (i = 0; i < 10 && !exiting; i++) { > + err = send_echo_msg(); > + if (err < 0) > + goto out; > + > + sleep(1); > + } > + > + err = __send_and_recv_msg(sockfd, CMD_GET_STATS, val, ARRAY_SIZE(val)); > + if (err) > + goto out; > + > + /* stop the test */ > + err = send_and_recv_msg(sockfd, CMD_STOP); > + /* send a new echo message to wake echo thread of the dut */ > + send_echo_msg(); > + > + detected_cap = tester_collect_detected_cap(skel, val[0]); > + > + fprintf(stdout, "Feature %s: [%s][%s]\n", > get_xdp_feature_str(env.feature), > + detected_cap ? GREEN("DETECTED") : RED("NOT DETECTED"), > + advertised_cap ? GREEN("ADVERTISED") : RED("NOT ADVERTISED")); > +out: > + bpf_xdp_detach(env.ifindex, flags, NULL); > + close(sockfd); > + return err < 0 ? err : 0; > +} > + > +static void set_skel_rodata(struct xdp_features *skel) > +{ > + skel->rodata->expected_feature = env.feature; > + if (env.family == AF_INET6) { > + struct sockaddr_in6 *tester_addr = (void *)&env.tester.addr; > + struct sockaddr_in6 *dut_addr = (void *)&env.dut.addr; > + > + skel->rodata->tester_addr.ip6 = tester_addr->sin6_addr; > + skel->rodata->dut_addr.ip6 = dut_addr->sin6_addr; > + } else { > + struct sockaddr_in *tester_addr = (void *)&env.tester.addr; > + struct sockaddr_in *dut_addr = (void *)&env.dut.addr; > + > + skel->rodata->tester_addr.ip = tester_addr->sin_addr; > + skel->rodata->dut_addr.ip = dut_addr->sin_addr; > + } > +} > + > +int main(int argc, char **argv) > +{ > + struct xdp_features *skel; > + int err; > + > + libbpf_set_strict_mode(LIBBPF_STRICT_ALL); > + libbpf_set_print(libbpf_print_fn); > + > + signal(SIGINT, sig_handler); > + signal(SIGTERM, sig_handler); > + > + set_env_defaul(); > + > + /* Parse command line arguments */ > + err = argp_parse(&argp, argc, argv, 0, NULL, NULL); > + if (err) > + return err; > + > + if (env.ifindex < 0) { > + fprintf(stderr, "Invalid ifindex\n"); > + return -ENODEV; > + } > + > + /* Load and verify BPF application */ > + skel = xdp_features__open(); > + if (!skel) { > + fprintf(stderr, "Failed to open and load BPF skeleton\n"); > + return -EINVAL; > + } > + > + set_skel_rodata(skel); > + > + /* Load & verify BPF programs */ > + err = xdp_features__load(skel); > + if (err) { > + fprintf(stderr, "Failed to load and verify BPF skeleton\n"); > + goto cleanup; > + } > + > + err = xdp_features__attach(skel); > + if (err) { > + fprintf(stderr, "Failed to attach BPF skeleton\n"); > + goto cleanup; > + } > + > + if (env.is_tester) { > + /* Tester */ > + fprintf(stdout, "Starting tester on device %d\n", env.ifindex); > + err = tester_run(skel); > + } else { > + /* DUT */ > + fprintf(stdout, "Starting DUT on device %d\n", env.ifindex); > + err = dut_run(skel); > + } > + > +cleanup: > + xdp_features__destroy(skel); > + > + return err < 0 ? -err : 0; > +} > diff --git a/tools/testing/selftests/bpf/xdp_features.h > b/tools/testing/selftests/bpf/xdp_features.h > new file mode 100644 > index 000000000000..28d7614c4f02 > --- /dev/null > +++ b/tools/testing/selftests/bpf/xdp_features.h > @@ -0,0 +1,33 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > + > +/* test commands */ > +enum test_commands { > + CMD_STOP, /* CMD */ > + CMD_START, /* CMD + xdp feature */ > + CMD_ECHO, /* CMD */ > + CMD_ACK, /* CMD + data */ > + CMD_GET_XDP_CAP, /* CMD */ > + CMD_GET_STATS, /* CMD */ > +}; > + > +#define DUT_CTRL_PORT 12345 > +#define DUT_ECHO_PORT 12346 > + > +struct tlv_hdr { > + __be16 type; > + __be16 len; > + __be32 data[]; > +}; > + > +enum { > + XDP_FEATURE_ABORTED, > + XDP_FEATURE_DROP, > + XDP_FEATURE_PASS, > + XDP_FEATURE_TX, > + XDP_FEATURE_REDIRECT, > + XDP_FEATURE_NDO_XMIT, > + XDP_FEATURE_XSK_ZEROCOPY, > + XDP_FEATURE_HW_OFFLOAD, > + XDP_FEATURE_RX_SG, > + XDP_FEATURE_NDO_XMIT_SG, > +}; > -- > 2.39.1 From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from smtp3.osuosl.org (smtp3.osuosl.org [140.211.166.136]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 819B6C636D6 for ; Mon, 30 Jan 2023 19:00:02 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp3.osuosl.org (Postfix) with ESMTP id F294D60FC8; Mon, 30 Jan 2023 19:00:01 +0000 (UTC) DKIM-Filter: OpenDKIM Filter v2.11.0 smtp3.osuosl.org F294D60FC8 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=osuosl.org; s=default; t=1675105202; bh=hx38J+bewmfO/EMf5z9Xd8JkvIGFvNycdO35gC1uNaY=; h=Date:In-Reply-To:References:From:To:Subject:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: Cc:From; b=csul+jYUKpEwf6xePe+cvt3Rfb/b1ITcqFzXI5td8RyPxnY52z6F8J0cZGbi4KJhL iaQY5oZkygTUTq4HARTJxfsNk7onEXY8bJ7IqQ0mVzf7JzX4zEPuU7WE8QRJh8QFzX GQe6Ud+8Tq4YagLFfjo0uWdoSo19Te7CDwgGm9AWMdiMG88Yl+Nk15Dwt87OjdPOfA Zo9CGIJWRLwsL0y5yL1j9sdrM6HANcEcLqXRN087ZGHthN8Qn2TQrJiMofnKZGTEwO MVB8KHjuR2G8YZcQzVM7dOfY9Rrka6Wv7zsj2dYeZ6skhHTZeToyqcaxal/AXIN7E4 JsDROLWDfJzsw== X-Virus-Scanned: amavisd-new at osuosl.org Received: from smtp3.osuosl.org ([127.0.0.1]) by localhost (smtp3.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id FCQ1gPQJiw4K; Mon, 30 Jan 2023 19:00:00 +0000 (UTC) Received: from ash.osuosl.org (ash.osuosl.org [140.211.166.34]) by smtp3.osuosl.org (Postfix) with ESMTP id A41C960FDA; Mon, 30 Jan 2023 18:59:59 +0000 (UTC) DKIM-Filter: OpenDKIM Filter v2.11.0 smtp3.osuosl.org A41C960FDA Received: from smtp2.osuosl.org (smtp2.osuosl.org [140.211.166.133]) by ash.osuosl.org (Postfix) with ESMTP id 4D60E1BF215 for ; Mon, 30 Jan 2023 18:59:58 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp2.osuosl.org (Postfix) with ESMTP id 1B0E2404EC for ; Mon, 30 Jan 2023 18:59:58 +0000 (UTC) DKIM-Filter: OpenDKIM Filter v2.11.0 smtp2.osuosl.org 1B0E2404EC X-Virus-Scanned: amavisd-new at osuosl.org Received: from smtp2.osuosl.org ([127.0.0.1]) by localhost (smtp2.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id W5S6B_XRw2sO for ; Mon, 30 Jan 2023 18:59:54 +0000 (UTC) X-Greylist: whitelisted by SQLgrey-1.8.0 DKIM-Filter: OpenDKIM Filter v2.11.0 smtp2.osuosl.org 79BF240AF4 Received: from mail-pj1-x1049.google.com (mail-pj1-x1049.google.com [IPv6:2607:f8b0:4864:20::1049]) by smtp2.osuosl.org (Postfix) with ESMTPS id 79BF240AF4 for ; Mon, 30 Jan 2023 18:59:54 +0000 (UTC) Received: by mail-pj1-x1049.google.com with SMTP id y23-20020a17090aca9700b0022beb2b98a9so4987394pjt.2 for ; Mon, 30 Jan 2023 10:59:54 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=gPh2GGLzuKt77ji41T5HziXC5BtDvNF9DYeqboe8l9g=; b=t7vamRxi+3kxk1w/elDGWzzSVxyFIQ+7Un0YqH140RTVE4p1Ob15ZungcJ6tWUWARC RxJcpzE6RT6U89LT1Dxliu/SvBISsp5fUtjsFBJIXBHGcNde7lvWczPCCvlb+Zlqk6we 8J8EauRUZ5k68cDwU9fs77HaK/f9klozXrTgRZuNgbhuzd9ZCf/RFjGrAYjYxBC1FmzA CjlLysyF5maNqswrROZR83i4gH8KKwov4yPHO+QNlaXpHl9YxZBybZvKFzEiuiW76Dgy +PyIOKVpIf3LubWVM/J8RBGrtMQn5Lxno/LQ1TSvNcUYby/OruqNsjY6nCJE4909C9Ln GSTQ== X-Gm-Message-State: AO0yUKULhiDKHoxM1oOElEbmrRYmJKHsyCOgEuhB9JzDMbIzJ1Lf1Ukc 0yu1y/Q0Qe56Bo3bSyDbstur7m0= X-Google-Smtp-Source: AK7set8KUJA+/PZ5enP38sSDTCip2AV036jWXj/AeHr+sYyKcjI8phJAAYvHizWo2VjMLAR3AYyCYFo= X-Received: from sdf.c.googlers.com ([fda3:e722:ac3:cc00:7f:e700:c0a8:5935]) (user=sdf job=sendgmr) by 2002:a62:c501:0:b0:593:cbba:db0e with SMTP id j1-20020a62c501000000b00593cbbadb0emr618359pfg.51.1675105193762; Mon, 30 Jan 2023 10:59:53 -0800 (PST) Date: Mon, 30 Jan 2023 10:59:52 -0800 In-Reply-To: Mime-Version: 1.0 References: Message-ID: From: Stanislav Fomichev To: Lorenzo Bianconi X-Mailman-Original-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=gPh2GGLzuKt77ji41T5HziXC5BtDvNF9DYeqboe8l9g=; b=BjH11f/qFDn3sLzpJbBD5q4h9nKS/kIpVF1ob74T9DiPAG5vSS8Qu58wLEtUkX4UbM 2nR/0Za+CpEzQFPsCwYPbbPyv44mWQ6nCm7bS4dXaoyt7gL3v1tmI29PK3NU5Y9h5upY M5NvLq2b5X720409ZzYhPrK/exqvfz/N+Jm5wAVStBKajUORZvtvfLlZPeWYRaDCaJb0 wqcOPNvO6zUzl1LKgNuFOKIXM65fwuHnN1Pia0caC6fi/YilV+ilwOXiYRrmYrv4ZJ4s oeYFJySorzJQ8L2DcFbxcFwYlgTzh+T2Od57XShgOR21TJmnug2Wffm7/tV/48DeROfB DF9g== X-Mailman-Original-Authentication-Results: smtp2.osuosl.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.a=rsa-sha256 header.s=20210112 header.b=BjH11f/q Subject: Re: [Intel-wired-lan] [PATCH v4 bpf-next 8/8] selftests/bpf: introduce XDP compliance test tool X-BeenThere: intel-wired-lan@osuosl.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Intel Wired Ethernet Linux Kernel Driver Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: mst@redhat.com, vladimir.oltean@nxp.com, ast@kernel.org, edumazet@google.com, anthony.l.nguyen@intel.com, daniel@iogearbox.net, andrii@kernel.org, intel-wired-lan@lists.osuosl.org, simon.horman@corigine.com, kuba@kernel.org, pabeni@redhat.com, aelior@marvell.com, hawk@kernel.org, christophe.jaillet@wanadoo.fr, memxor@gmail.com, john@phrozen.org, bjorn@kernel.org, bpf@vger.kernel.org, magnus.karlsson@intel.com, leon@kernel.org, netdev@vger.kernel.org, toke@redhat.com, martin.lau@linux.dev, ecree.xilinx@gmail.com, alardam@gmail.com, gospo@broadcom.com, saeedm@nvidia.com, davem@davemloft.net, nbd@nbd.name Content-Transfer-Encoding: 7bit Content-Type: text/plain; charset="us-ascii"; Format="flowed"; DelSp="yes" Errors-To: intel-wired-lan-bounces@osuosl.org Sender: "Intel-wired-lan" On 01/28, Lorenzo Bianconi wrote: > Introduce xdp_features tool in order to test XDP features supported by > the NIC and match them against advertised ones. > In order to test supported/advertised XDP features, xdp_features must > run on the Device Under Test (DUT) and on a Tester device. > xdp_features opens a control TCP channel between DUT and Tester devices > to send control commands from Tester to the DUT and a UDP data channel > where the Tester sends UDP 'echo' packets and the DUT is expected to > reply back with the same packet. DUT installs multiple XDP programs on the > NIC to test XDP capabilities and reports back to the Tester some XDP > stats. > Currently xdp_features supports the following XDP features: > - XDP_DROP > - XDP_PASS > - XDP_TX > - XDP_REDIRECT > - XDP_REDIRECT_TARGET > Co-developed-by: Kumar Kartikeya Dwivedi > Signed-off-by: Kumar Kartikeya Dwivedi > Signed-off-by: Lorenzo Bianconi > --- > tools/testing/selftests/bpf/.gitignore | 1 + > tools/testing/selftests/bpf/Makefile | 11 +- > .../selftests/bpf/progs/xdp_features.c | 275 +++++++ > .../selftests/bpf/test_xdp_features.sh | 100 +++ > tools/testing/selftests/bpf/xdp_features.c | 731 ++++++++++++++++++ > tools/testing/selftests/bpf/xdp_features.h | 33 + > 6 files changed, 1149 insertions(+), 2 deletions(-) > create mode 100644 tools/testing/selftests/bpf/progs/xdp_features.c > create mode 100755 tools/testing/selftests/bpf/test_xdp_features.sh > create mode 100644 tools/testing/selftests/bpf/xdp_features.c > create mode 100644 tools/testing/selftests/bpf/xdp_features.h > diff --git a/tools/testing/selftests/bpf/.gitignore > b/tools/testing/selftests/bpf/.gitignore > index 4aa5bba956ff..116fecf80ca1 100644 > --- a/tools/testing/selftests/bpf/.gitignore > +++ b/tools/testing/selftests/bpf/.gitignore > @@ -48,3 +48,4 @@ xskxceiver > xdp_redirect_multi > xdp_synproxy > xdp_hw_metadata > +xdp_features > diff --git a/tools/testing/selftests/bpf/Makefile > b/tools/testing/selftests/bpf/Makefile > index 53eae7be8dff..6f06b03d93cd 100644 > --- a/tools/testing/selftests/bpf/Makefile > +++ b/tools/testing/selftests/bpf/Makefile > @@ -73,7 +73,8 @@ TEST_PROGS := test_kmod.sh \ > test_bpftool.sh \ > test_bpftool_metadata.sh \ > test_doc_build.sh \ > - test_xsk.sh > + test_xsk.sh \ > + test_xdp_features.sh > TEST_PROGS_EXTENDED := with_addr.sh \ > with_tunnels.sh ima_setup.sh verify_sig_setup.sh \ > @@ -83,7 +84,8 @@ TEST_PROGS_EXTENDED := with_addr.sh \ > TEST_GEN_PROGS_EXTENDED = test_sock_addr test_skb_cgroup_id_user \ > flow_dissector_load test_flow_dissector test_tcp_check_syncookie_user \ > test_lirc_mode2_user xdping test_cpp runqslower bench bpf_testmod.ko \ > - xskxceiver xdp_redirect_multi xdp_synproxy veristat xdp_hw_metadata > + xskxceiver xdp_redirect_multi xdp_synproxy veristat xdp_hw_metadata \ > + xdp_features > TEST_CUSTOM_PROGS = $(OUTPUT)/urandom_read $(OUTPUT)/sign-file > TEST_GEN_FILES += liburandom_read.so > @@ -385,6 +387,7 @@ test_subskeleton_lib.skel.h-deps := > test_subskeleton_lib2.bpf.o test_subskeleton > test_usdt.skel.h-deps := test_usdt.bpf.o test_usdt_multispec.bpf.o > xsk_xdp_progs.skel.h-deps := xsk_xdp_progs.bpf.o > xdp_hw_metadata.skel.h-deps := xdp_hw_metadata.bpf.o > +xdp_features.skel.h-deps := xdp_features.bpf.o > LINKED_BPF_SRCS := $(patsubst %.bpf.o,%.c,$(foreach > skel,$(LINKED_SKELS),$($(skel)-deps))) > @@ -586,6 +589,10 @@ $(OUTPUT)/xdp_hw_metadata: xdp_hw_metadata.c > $(OUTPUT)/network_helpers.o $(OUTPU > $(call msg,BINARY,,$@) > $(Q)$(CC) $(CFLAGS) $(filter %.a %.o %.c,$^) $(LDLIBS) -o $@ > +$(OUTPUT)/xdp_features: xdp_features.c $(OUTPUT)/network_helpers.o > $(OUTPUT)/xdp_features.skel.h | $(OUTPUT) > + $(call msg,BINARY,,$@) > + $(Q)$(CC) $(CFLAGS) $(filter %.a %.o %.c,$^) $(LDLIBS) -o $@ > + > # Make sure we are able to include and link libbpf against c++. > $(OUTPUT)/test_cpp: test_cpp.cpp $(OUTPUT)/test_core_extern.skel.h > $(BPFOBJ) > $(call msg,CXX,,$@) > diff --git a/tools/testing/selftests/bpf/progs/xdp_features.c > b/tools/testing/selftests/bpf/progs/xdp_features.c > new file mode 100644 > index 000000000000..205a4526ea68 > --- /dev/null > +++ b/tools/testing/selftests/bpf/progs/xdp_features.c > @@ -0,0 +1,275 @@ > +// SPDX-License-Identifier: GPL-2.0 > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include "xdp_features.h" > + > +#define ipv6_addr_equal(a, b) ((a).s6_addr32[0] == (b).s6_addr32[0] && \ > + (a).s6_addr32[1] == (b).s6_addr32[1] && \ > + (a).s6_addr32[2] == (b).s6_addr32[2] && \ > + (a).s6_addr32[3] == (b).s6_addr32[3]) > + > +struct net_device; > +struct bpf_prog; > + > +struct xdp_cpumap_stats { > + unsigned int redirect; > + unsigned int pass; > + unsigned int drop; > +}; > + > +struct { > + __uint(type, BPF_MAP_TYPE_ARRAY); > + __type(key, __u32); > + __type(value, __u32); > + __uint(max_entries, 1); > +} stats SEC(".maps"); > + > +struct { > + __uint(type, BPF_MAP_TYPE_ARRAY); > + __type(key, __u32); > + __type(value, __u32); > + __uint(max_entries, 1); > +} dut_stats SEC(".maps"); > + > +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"); > + > +struct { > + __uint(type, BPF_MAP_TYPE_DEVMAP); > + __uint(key_size, sizeof(__u32)); > + __uint(value_size, sizeof(struct bpf_devmap_val)); > + __uint(max_entries, 1); > +} dev_map SEC(".maps"); > + > +const volatile __u32 expected_feature = XDP_FEATURE_PASS; > +const volatile union { > + struct in_addr ip; > + struct in6_addr ip6; > +} tester_addr; > +const volatile union { > + struct in_addr ip; > + struct in6_addr ip6; > +} dut_addr; > + > +static __always_inline int xdp_process_echo_packet(struct xdp_md *xdp, > bool dut) > +{ > + void *data_end = (void *)(long)xdp->data_end; > + void *data = (void *)(long)xdp->data; > + struct ethhdr *eh = data; > + struct tlv_hdr *tlv; > + struct udphdr *uh; > + __be16 port; > + __u8 *cmd; > + > + if (eh + 1 > (struct ethhdr *)data_end) > + return -EINVAL; > + > + if (eh->h_proto == bpf_htons(ETH_P_IP)) { > + struct iphdr *ih = (struct iphdr *)(eh + 1); > + __be32 saddr = dut ? tester_addr.ip.s_addr : dut_addr.ip.s_addr; > + __be32 daddr = dut ? dut_addr.ip.s_addr : tester_addr.ip.s_addr; > + > + ih = (struct iphdr *)(eh + 1); > + if (ih + 1 > (struct iphdr *)data_end) > + return -EINVAL; > + > + if (saddr != ih->saddr) > + return -EINVAL; > + > + if (daddr != ih->daddr) > + return -EINVAL; > + > + if (ih->protocol != IPPROTO_UDP) > + return -EINVAL; > + > + uh = (struct udphdr *)(ih + 1); > + } else if (eh->h_proto == bpf_htons(ETH_P_IPV6)) { > + struct in6_addr saddr = dut ? tester_addr.ip6 : dut_addr.ip6; > + struct in6_addr daddr = dut ? dut_addr.ip6 : tester_addr.ip6; > + struct ipv6hdr *ih6 = (struct ipv6hdr *)(eh + 1); > + > + if (ih6 + 1 > (struct ipv6hdr *)data_end) > + return -EINVAL; > + > + if (!ipv6_addr_equal(saddr, ih6->saddr)) > + return -EINVAL; > + > + if (!ipv6_addr_equal(daddr, ih6->daddr)) > + return -EINVAL; > + > + if (ih6->nexthdr != IPPROTO_UDP) > + return -EINVAL; > + > + uh = (struct udphdr *)(ih6 + 1); > + } else { > + return -EINVAL; > + } > + > + if (uh + 1 > (struct udphdr *)data_end) > + return -EINVAL; > + > + port = dut ? uh->dest : uh->source; > + if (port != bpf_htons(DUT_ECHO_PORT)) > + return -EINVAL; > + > + tlv = (struct tlv_hdr *)(uh + 1); > + if (tlv + 1 > data_end) > + return -EINVAL; > + > + return bpf_htons(tlv->type) == CMD_ECHO ? 0 : -EINVAL; > +} > + > +SEC("xdp") > +int xdp_tester(struct xdp_md *xdp) > +{ > + __u32 *val, key = 0; > + > + switch (expected_feature) { > + case XDP_FEATURE_NDO_XMIT: > + case XDP_FEATURE_TX: > + if (xdp_process_echo_packet(xdp, true)) > + goto out; > + break; > + case XDP_FEATURE_DROP: > + case XDP_FEATURE_PASS: > + case XDP_FEATURE_REDIRECT: > + if (xdp_process_echo_packet(xdp, false)) > + goto out; > + break; > + default: > + goto out; > + } > + > + val = bpf_map_lookup_elem(&stats, &key); > + if (val) > + __sync_add_and_fetch(val, 1); > + > +out: > + return XDP_PASS; > +} > + > +SEC("xdp") > +int xdp_do_pass(struct xdp_md *xdp) > +{ > + __u32 *val, key = 0; > + > + val = bpf_map_lookup_elem(&dut_stats, &key); > + if (val) > + __sync_add_and_fetch(val, 1); > + > + return XDP_PASS; > +} > + > +SEC("xdp") > +int xdp_do_drop(struct xdp_md *xdp) > +{ > + __u32 *val, key = 0; > + > + if (xdp_process_echo_packet(xdp, true)) > + return XDP_PASS; > + > + val = bpf_map_lookup_elem(&dut_stats, &key); > + if (val) > + __sync_add_and_fetch(val, 1); > + > + return XDP_DROP; > +} > + > +SEC("xdp") > +int xdp_do_aborted(struct xdp_md *xdp) > +{ > + return xdp_process_echo_packet(xdp, true) ? XDP_PASS : XDP_ABORTED; > +} > + > +SEC("xdp") > +int xdp_do_tx(struct xdp_md *xdp) > +{ > + void *data = (void *)(long)xdp->data; > + struct ethhdr *eh = data; > + __u8 tmp_mac[ETH_ALEN]; > + __u32 *val, key = 0; > + > + if (xdp_process_echo_packet(xdp, true)) > + return XDP_PASS; > + > + __builtin_memcpy(tmp_mac, eh->h_source, ETH_ALEN); > + __builtin_memcpy(eh->h_source, eh->h_dest, ETH_ALEN); > + __builtin_memcpy(eh->h_dest, tmp_mac, ETH_ALEN); > + > + val = bpf_map_lookup_elem(&dut_stats, &key); > + if (val) > + __sync_add_and_fetch(val, 1); > + > + return XDP_TX; > +} > + > +SEC("xdp") > +int xdp_do_redirect(struct xdp_md *xdp) > +{ > + if (xdp_process_echo_packet(xdp, true)) > + return XDP_PASS; > + > + return bpf_redirect_map(&cpu_map, 0, 0); > +} > + > +SEC("tp_btf/xdp_exception") > +int BPF_PROG(xdp_exception, const struct net_device *dev, > + const struct bpf_prog *xdp, __u32 act) > +{ > + __u32 *val, key = 0; > + > + val = bpf_map_lookup_elem(&dut_stats, &key); > + if (val) > + __sync_add_and_fetch(val, 1); > + > + return 0; > +} > + > +SEC("tp_btf/xdp_cpumap_kthread") > +int BPF_PROG(tp_xdp_cpumap_kthread, int map_id, unsigned int processed, > + unsigned int drops, int sched, struct xdp_cpumap_stats *xdp_stats) > +{ > + __u32 *val, key = 0; > + > + val = bpf_map_lookup_elem(&dut_stats, &key); > + if (val) > + __sync_add_and_fetch(val, 1); > + > + return 0; > +} > + > +SEC("xdp/cpumap") > +int xdp_do_redirect_cpumap(struct xdp_md *xdp) > +{ > + void *data = (void *)(long)xdp->data; > + struct ethhdr *eh = data; > + __u8 tmp_mac[ETH_ALEN]; > + > + if (xdp_process_echo_packet(xdp, true)) > + return XDP_PASS; > + > + __builtin_memcpy(tmp_mac, eh->h_source, ETH_ALEN); > + __builtin_memcpy(eh->h_source, eh->h_dest, ETH_ALEN); > + __builtin_memcpy(eh->h_dest, tmp_mac, ETH_ALEN); > + > + return bpf_redirect_map(&dev_map, 0, 0); > +} > + > +char _license[] SEC("license") = "GPL"; > diff --git a/tools/testing/selftests/bpf/test_xdp_features.sh > b/tools/testing/selftests/bpf/test_xdp_features.sh > new file mode 100755 > index 000000000000..987845e18ea7 > --- /dev/null > +++ b/tools/testing/selftests/bpf/test_xdp_features.sh > @@ -0,0 +1,100 @@ > +#!/bin/bash > +# SPDX-License-Identifier: GPL-2.0 > + > +readonly NS="ns1-$(mktemp -u XXXXXX)" > +readonly V0_IP=10.10.0.11 > +readonly V1_IP=10.10.0.1 > +readonly V0_IP6=2001:db8::11 > +readonly V1_IP6=2001:db8::1 > + > +ret=1 > + > +setup() { > + { > + ip netns add ${NS} > + > + ip link add v1 type veth peer name v0 netns ${NS} > + > + ip link set v1 up > + ip addr add $V1_IP/24 dev v1 > + ip addr add $V1_IP6/64 nodad dev v1 > + ip -n ${NS} link set dev v0 up > + ip -n ${NS} addr add $V0_IP/24 dev v0 > + ip -n ${NS} addr add $V0_IP6/64 nodad dev v0 > + > + sysctl -w net.ipv4.ip_forward=1 > + > + # Enable XDP mode and disable checksum offload > + ethtool -K v1 gro on > + ethtool -K v1 tx-checksumming off > + ip netns exec ${NS} ethtool -K v0 gro on > + ip netns exec ${NS} ethtool -K v0 tx-checksumming off > + } > /dev/null 2>&1 > +} > + > +cleanup() { > + ip link del v1 2> /dev/null > + ip netns del ${NS} 2> /dev/null > + [ "$(pidof xdp_features)" = "" ] || kill $(pidof xdp_features) 2> > /dev/null > +} > + > +wait_for_dut_server() { > + while sleep 1; do > + ss -tlp | grep -q xdp_features > + [ $? -eq 0 ] && break > + done > +} > + > +test_xdp_features() { > + setup > + > + ## XDP_PASS > + ./xdp_features -6 -f XDP_PASS -D $V1_IP6 -T $V0_IP6 v1 & > + wait_for_dut_server > + ip netns exec ${NS} ./xdp_features -6 -t -f XDP_PASS -D $V1_IP6 -C > $V1_IP6 -T $V0_IP6 v0 > + > + [ $? -ne 0 ] && exit > + > + ## XDP_DROP > + ./xdp_features -f XDP_DROP -D $V1_IP -T $V0_IP v1 & > + wait_for_dut_server > + ip netns exec ${NS} ./xdp_features -t -f XDP_DROP -D $V1_IP -C $V1_IP > -T $V0_IP v0 > + > + [ $? -ne 0 ] && exit > + > + ## XDP_ABORTED > + ./xdp_features -6 -f XDP_ABORTED -D $V1_IP6 -T $V0_IP6 v1 & > + wait_for_dut_server > + ip netns exec ${NS} ./xdp_features -6 -t -f XDP_ABORTED -D $V1_IP6 -C > $V1_IP6 -T $V0_IP6 v0 > + > + [ $? -ne 0 ] && exit > + > + ## XDP_TX > + ./xdp_features -f XDP_TX -D $V1_IP -T $V0_IP v1 & > + wait_for_dut_server > + ip netns exec ${NS} ./xdp_features -t -f XDP_TX -D $V1_IP -C $V1_IP -T > $V0_IP v0 > + > + [ $? -ne 0 ] && exit > + > + ## XDP_REDIRECT > + ./xdp_features -6 -f XDP_REDIRECT -D $V1_IP6 -T $V0_IP6 v1 & > + wait_for_dut_server > + ip netns exec ${NS} ./xdp_features -6 -t -f XDP_REDIRECT -D $V1_IP6 -C > $V1_IP6 -T $V0_IP6 v0 > + > + [ $? -ne 0 ] && exit > + > + ## XDP_NDO_XMIT > + ./xdp_features -f XDP_NDO_XMIT -D $V1_IP -T $V0_IP v1 & > + wait_for_dut_server > + ip netns exec ${NS} ./xdp_features -t -f XDP_NDO_XMIT -D $V1_IP -C > $V1_IP -T $V0_IP v0 > + > + ret=$? > + cleanup > +} > + > +set -e > +trap cleanup 2 3 6 9 > + > +test_xdp_features > + > +exit $ret > diff --git a/tools/testing/selftests/bpf/xdp_features.c > b/tools/testing/selftests/bpf/xdp_features.c > new file mode 100644 > index 000000000000..9b94d5cdf654 > --- /dev/null > +++ b/tools/testing/selftests/bpf/xdp_features.c > @@ -0,0 +1,731 @@ > +// SPDX-License-Identifier: GPL-2.0 > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include > + > +#include "xdp_features.skel.h" > +#include "xdp_features.h" > + > +#define RED(str) "\033[0;31m" str "\033[0m" > +#define GREEN(str) "\033[0;32m" str "\033[0m" > +#define YELLOW(str) "\033[0;33m" str "\033[0m" > + > +static struct env { > + bool verbosity; > + int ifindex; > + unsigned int feature; > + bool is_tester; > + int family; > + struct { > + struct sockaddr_storage addr; > + socklen_t addrlen; > + } dut_ctrl; > + struct { > + struct sockaddr_storage addr; > + socklen_t addrlen; > + } dut; > + struct { > + struct sockaddr_storage addr; > + socklen_t addrlen; > + } tester; > +} env; > + > +#define BUFSIZE 128 > + > +void test__fail(void) { /* for network_helpers.c */ } > + > +static int libbpf_print_fn(enum libbpf_print_level level, > + const char *format, va_list args) > +{ > + if (level == LIBBPF_DEBUG && !env.verbosity) > + return 0; > + return vfprintf(stderr, format, args); > +} > + > +static volatile bool exiting; > + > +static void sig_handler(int sig) > +{ > + exiting = true; > +} > + > +const char *argp_program_version = "xdp-features 0.0"; > +const char argp_program_doc[] = > +"XDP features detecion application.\n" > +"\n" > +"XDP features application checks the XDP advertised features match > detected ones.\n" > +"\n" > +"USAGE: ./xdp-features [-6vt] [-f ] [-D ] [-T > ] [-C ] \n" > +"\n" > +"XDP features\n:" > +"- XDP_PASS\n" > +"- XDP_DROP\n" > +"- XDP_ABORTED\n" > +"- XDP_REDIRECT\n" > +"- XDP_NDO_XMIT\n" > +"- XDP_TX\n"; > + > +static const struct argp_option opts[] = { > + { "ipv6", '6', NULL, 0, "Use IPv6 network stack" }, > + { "verbose", 'v', NULL, 0, "Verbose debug output" }, > + { "tester", 't', NULL, 0, "Tester mode" }, > + { "feature", 'f', "XDP-FEATURE", 0, "XDP feature to test" }, > + { "dut_data_ip", 'D', "DUT-DATA-IP", 0, "DUT IP data channel" }, > + { "dut_ctrl_ip", 'C', "DUT-CTRL-IP", 0, "DUT IP control channel" }, > + { "tester_data_ip", 'T', "TESTER-DATA-IP", 0, "Tester IP data channel" > }, > + {}, > +}; > + > +static int get_xdp_feature(const char *arg) > +{ > + if (!strcmp(arg, "XDP_PASS")) > + return XDP_FEATURE_PASS; > + else if (!strcmp(arg, "XDP_DROP")) > + return XDP_FEATURE_DROP; > + else if (!strcmp(arg, "XDP_ABORTED")) > + return XDP_FEATURE_ABORTED; > + else if (!strcmp(arg, "XDP_REDIRECT")) > + return XDP_FEATURE_REDIRECT; > + else if (!strcmp(arg, "XDP_NDO_XMIT")) > + return XDP_FEATURE_NDO_XMIT; > + else if (!strcmp(arg, "XDP_TX")) > + return XDP_FEATURE_TX; > + > + return -EINVAL; > +} > + > +static char *get_xdp_feature_str(int feature) > +{ > + switch (feature) { > + case XDP_FEATURE_PASS: > + return YELLOW("XDP_PASS"); > + case XDP_FEATURE_DROP: > + return YELLOW("XDP_DROP"); > + case XDP_FEATURE_ABORTED: > + return YELLOW("XDP_ABORTED"); > + case XDP_FEATURE_TX: > + return YELLOW("XDP_TX"); > + case XDP_FEATURE_REDIRECT: > + return YELLOW("XDP_REDIRECT"); > + case XDP_FEATURE_NDO_XMIT: > + return YELLOW("XDP_NDO_XMIT"); > + default: > + return ""; > + } > +} > + > +static error_t parse_arg(int key, char *arg, struct argp_state *state) > +{ > + switch (key) { > + case '6': > + env.family = AF_INET6; > + break; Thanks for making the changes! Since you're doing another respin to address Alexei's feedback, maybe one thing we can also improve here regarding v4-v6? Optionally, up to you, but IMO, it's much easier to hard-code AF_INET6 everywhere: socket(AF_INET6, ...) inet_pton(AF_INET6, ...) ... And then, if you want to use v4 by default, use '::ffff:127.0.0.1' as the address. If the users need to use it with some other v4, they can also pass the v4-mapped v6 address. Up to you on whether to implement it or not, feel free to ignore me, but IMO, that should avoid a lot of those env.family arguments and making special cases for v4. Plus also removes the need for -6 argument. The following would work: ./xdp_features ::ffff:: ./xdp_features (I'm assuming nothing runs exclusive non-dualstack v4 envs anymore) > + case 'v': > + env.verbosity = true; > + break; > + case 't': > + env.is_tester = true; > + break; > + case 'f': > + env.feature = get_xdp_feature(arg); > + if (env.feature < 0) { > + fprintf(stderr, "Invalid xdp feature: %s\n", arg); > + argp_usage(state); > + return ARGP_ERR_UNKNOWN; > + } > + break; > + case 'D': > + if (make_sockaddr(env.family, arg, DUT_ECHO_PORT, > + &env.dut.addr, &env.dut.addrlen)) { > + fprintf(stderr, "Invalid DUT address: %s\n", arg); > + return ARGP_ERR_UNKNOWN; > + } > + break; > + case 'C': > + if (make_sockaddr(env.family, arg, DUT_CTRL_PORT, > + &env.dut_ctrl.addr, &env.dut_ctrl.addrlen)) { > + fprintf(stderr, "Invalid DUT CTRL address: %s\n", arg); > + return ARGP_ERR_UNKNOWN; > + } > + break; > + case 'T': > + if (make_sockaddr(env.family, arg, 0, &env.tester.addr, > + &env.tester.addrlen)) { > + fprintf(stderr, "Invalid Tester address: %s\n", arg); > + return ARGP_ERR_UNKNOWN; > + } > + break; > + case ARGP_KEY_ARG: > + errno = 0; > + if (strlen(arg) >= IF_NAMESIZE) { > + fprintf(stderr, "Invalid device name: %s\n", arg); > + argp_usage(state); > + return ARGP_ERR_UNKNOWN; > + } > + > + env.ifindex = if_nametoindex(arg); > + if (!env.ifindex) > + env.ifindex = strtoul(arg, NULL, 0); > + if (!env.ifindex) { > + fprintf(stderr, > + "Bad interface index or name (%d): %s\n", > + errno, strerror(errno)); > + argp_usage(state); > + return ARGP_ERR_UNKNOWN; > + } > + break; > + default: > + return ARGP_ERR_UNKNOWN; > + } > + > + return 0; > +} > + > +static const struct argp argp = { > + .options = opts, > + .parser = parse_arg, > + .doc = argp_program_doc, > +}; > + > +static void set_env_defaul(void) > +{ > + env.feature = XDP_FEATURE_PASS; > + env.ifindex = -ENODEV; > + env.family = AF_INET; > + make_sockaddr(AF_INET, "127.0.0.1", DUT_CTRL_PORT, &env.dut_ctrl.addr, > + &env.dut_ctrl.addrlen); > + make_sockaddr(AF_INET, "127.0.0.1", DUT_ECHO_PORT, &env.dut.addr, > + &env.dut.addrlen); > + make_sockaddr(AF_INET, "127.0.0.1", 0, &env.tester.addr, > + &env.tester.addrlen); > +} > + > +static void *dut_echo_thread(void *arg) > +{ > + unsigned char buf[sizeof(struct tlv_hdr)]; > + int sockfd = *(int *)arg; > + > + while (!exiting) { > + struct tlv_hdr *tlv = (struct tlv_hdr *)buf; > + struct sockaddr_storage addr; > + socklen_t addrlen; > + size_t n; > + > + n = recvfrom(sockfd, buf, sizeof(buf), MSG_WAITALL, > + (struct sockaddr *)&addr, &addrlen); > + if (n != ntohs(tlv->len)) > + continue; > + > + if (ntohs(tlv->type) != CMD_ECHO) > + continue; > + > + sendto(sockfd, buf, sizeof(buf), MSG_NOSIGNAL | MSG_CONFIRM, > + (struct sockaddr *)&addr, addrlen); > + } > + > + pthread_exit((void *)0); > + close(sockfd); > + > + return NULL; > +} > + > +static int dut_run_echo_thread(pthread_t *t, int *sockfd) > +{ > + int err; > + > + sockfd = start_reuseport_server(env.family, SOCK_DGRAM, NULL, > + DUT_ECHO_PORT, 0, 1); > + if (!sockfd) { > + fprintf(stderr, "Failed to create echo socket\n"); > + return -errno; > + } > + > + /* start echo channel */ > + err = pthread_create(t, NULL, dut_echo_thread, sockfd); > + if (err) { > + fprintf(stderr, "Failed creating dut_echo thread: %s\n", > + strerror(-err)); > + free_fds(sockfd, 1); > + return -EINVAL; > + } > + > + return 0; > +} > + > +static int dut_attach_xdp_prog(struct xdp_features *skel, int feature, > + int flags) > +{ > + struct bpf_program *prog; > + unsigned int key = 0; > + int err, fd = 0; > + > + switch (feature) { > + case XDP_FEATURE_TX: > + prog = skel->progs.xdp_do_tx; > + break; > + case XDP_FEATURE_DROP: > + prog = skel->progs.xdp_do_drop; > + break; > + case XDP_FEATURE_ABORTED: > + prog = skel->progs.xdp_do_aborted; > + break; > + case XDP_FEATURE_PASS: > + prog = skel->progs.xdp_do_pass; > + break; > + case XDP_FEATURE_NDO_XMIT: { > + struct bpf_devmap_val entry = { > + .ifindex = env.ifindex, > + }; > + > + err = bpf_map__update_elem(skel->maps.dev_map, > + &key, sizeof(key), > + &entry, sizeof(entry), 0); > + if (err < 0) > + return err; > + > + fd = bpf_program__fd(skel->progs.xdp_do_redirect_cpumap); > + } > + case XDP_FEATURE_REDIRECT: { > + struct bpf_cpumap_val entry = { > + .qsize = 2048, > + .bpf_prog.fd = fd, > + }; > + > + err = bpf_map__update_elem(skel->maps.cpu_map, > + &key, sizeof(key), > + &entry, sizeof(entry), 0); > + if (err < 0) > + return err; > + > + prog = skel->progs.xdp_do_redirect; > + break; > + } > + default: > + return -EINVAL; > + } > + > + err = bpf_xdp_attach(env.ifindex, bpf_program__fd(prog), flags, NULL); > + if (err) > + fprintf(stderr, > + "Failed to attach XDP program to ifindex %d\n", > + env.ifindex); > + return err; > +} > + > +static int __recv_msg(int sockfd, void *buf, size_t bufsize, > + unsigned int *val, unsigned int val_size) > +{ > + struct tlv_hdr *tlv = (struct tlv_hdr *)buf; > + int len, n = sizeof(*tlv), i = 0; > + > + len = recv(sockfd, buf, bufsize, 0); > + if (len != ntohs(tlv->len)) > + return -EINVAL; > + > + while (n < len && i < val_size) { > + val[i] = ntohl(tlv->data[i]); > + n += sizeof(tlv->data[0]); > + i++; > + } > + > + return i; > +} > + > +static int recv_msg(int sockfd, void *buf, size_t bufsize) > +{ > + return __recv_msg(sockfd, buf, bufsize, NULL, 0); > +} > + > +static int dut_run(struct xdp_features *skel) > +{ > + int flags = XDP_FLAGS_UPDATE_IF_NOEXIST | XDP_FLAGS_DRV_MODE; > + int state, err, *sockfd, ctrl_sockfd, echo_sockfd; > + struct sockaddr_storage ctrl_addr; > + pthread_t dut_thread; > + socklen_t addrlen; > + > + sockfd = start_reuseport_server(env.family, SOCK_STREAM, NULL, > + DUT_CTRL_PORT, 0, 1); > + if (!sockfd) { > + fprintf(stderr, "Failed to create DUT socket\n"); > + return -errno; > + } > + > + ctrl_sockfd = accept(*sockfd, (struct sockaddr *)&ctrl_addr, &addrlen); > + if (ctrl_sockfd < 0) { > + fprintf(stderr, "Failed to accept connection on DUT socket\n"); > + free_fds(sockfd, 1); > + return -errno; > + } > + > + /* CTRL loop */ > + while (!exiting) { > + unsigned char buf[BUFSIZE] = {}; > + struct tlv_hdr *tlv = (struct tlv_hdr *)buf; > + > + err = recv_msg(ctrl_sockfd, buf, BUFSIZE); > + if (err) > + continue; > + > + switch (ntohs(tlv->type)) { > + case CMD_START: { > + if (state == CMD_START) > + continue; > + > + state = CMD_START; > + /* Load the XDP program on the DUT */ > + err = dut_attach_xdp_prog(skel, ntohl(tlv->data[0]), flags); > + if (err) > + goto out; > + > + err = dut_run_echo_thread(&dut_thread, &echo_sockfd); > + if (err < 0) > + goto out; > + > + tlv->type = htons(CMD_ACK); > + tlv->len = htons(sizeof(*tlv)); > + err = send(ctrl_sockfd, buf, sizeof(*tlv), 0); > + if (err < 0) > + goto end_thread; > + break; > + } > + case CMD_STOP: > + if (state != CMD_START) > + break; > + > + state = CMD_STOP; > + > + exiting = true; > + bpf_xdp_detach(env.ifindex, flags, NULL); > + > + tlv->type = htons(CMD_ACK); > + tlv->len = htons(sizeof(*tlv)); > + err = send(ctrl_sockfd, buf, sizeof(*tlv), 0); > + goto end_thread; > + case CMD_GET_XDP_CAP: { > + LIBBPF_OPTS(bpf_xdp_query_opts, opts); > + size_t n; > + > + err = bpf_xdp_query(env.ifindex, XDP_FLAGS_DRV_MODE, > + &opts); > + if (err) { > + fprintf(stderr, > + "Failed to query XDP cap for ifindex %d\n", > + env.ifindex); > + goto end_thread; > + } > + > + tlv->type = htons(CMD_ACK); > + n = sizeof(*tlv) + sizeof(opts.feature_flags); > + tlv->len = htons(n); > + tlv->data[0] = htonl(opts.feature_flags); > + > + err = send(ctrl_sockfd, buf, n, 0); > + if (err < 0) > + goto end_thread; > + break; > + } > + case CMD_GET_STATS: { > + unsigned int key = 0, val; > + size_t n; > + > + err = bpf_map__lookup_elem(skel->maps.dut_stats, > + &key, sizeof(key), > + &val, sizeof(val), 0); > + if (err) { > + fprintf(stderr, "bpf_map_lookup_elem failed\n"); > + goto end_thread; > + } > + > + tlv->type = htons(CMD_ACK); > + n = sizeof(*tlv) + sizeof(val); > + tlv->len = htons(n); > + tlv->data[0] = htonl(val); > + > + err = send(ctrl_sockfd, buf, n, 0); > + if (err < 0) > + goto end_thread; > + break; > + } > + default: > + break; > + } > + } > + > +end_thread: > + pthread_join(dut_thread, NULL); > +out: > + bpf_xdp_detach(env.ifindex, flags, NULL); > + close(ctrl_sockfd); > + free_fds(sockfd, 1); > + > + return err; > +} > + > +static bool tester_collect_advertised_cap(unsigned int cap) > +{ > + switch (env.feature) { > + case XDP_FEATURE_ABORTED: > + case XDP_FEATURE_DROP: > + case XDP_FEATURE_PASS: > + case XDP_FEATURE_TX: > + return cap & NETDEV_XDP_ACT_BASIC; > + case XDP_FEATURE_REDIRECT: > + return cap & NETDEV_XDP_ACT_REDIRECT; > + case XDP_FEATURE_NDO_XMIT: > + return cap & NETDEV_XDP_ACT_NDO_XMIT; > + default: > + return false; > + } > +} > + > +static bool tester_collect_detected_cap(struct xdp_features *skel, > + unsigned int dut_stats) > +{ > + unsigned int err, key = 0, val; > + > + if (!dut_stats) > + return false; > + > + err = bpf_map__lookup_elem(skel->maps.stats, &key, sizeof(key), > + &val, sizeof(val), 0); > + if (err) { > + fprintf(stderr, "bpf_map_lookup_elem failed\n"); > + return false; > + } > + > + switch (env.feature) { > + case XDP_FEATURE_PASS: > + case XDP_FEATURE_TX: > + case XDP_FEATURE_REDIRECT: > + case XDP_FEATURE_NDO_XMIT: > + return val > 0; > + case XDP_FEATURE_DROP: > + case XDP_FEATURE_ABORTED: > + return val == 0; > + default: > + return false; > + } > +} > + > +static int __send_and_recv_msg(int sockfd, enum test_commands cmd, > + unsigned int *val, unsigned int val_size) > +{ > + unsigned char buf[BUFSIZE] = {}; > + struct tlv_hdr *tlv = (struct tlv_hdr *)buf; > + int n = sizeof(*tlv), err; > + > + tlv->type = htons(cmd); > + switch (cmd) { > + case CMD_START: > + tlv->data[0] = htonl(env.feature); > + n += sizeof(*val); > + break; > + default: > + break; > + } > + tlv->len = htons(n); > + > + err = send(sockfd, buf, n, 0); > + if (err < 0) > + return err; > + > + err = __recv_msg(sockfd, buf, BUFSIZE, val, val_size); > + if (err < 0) > + return err; > + > + return ntohs(tlv->type) == CMD_ACK ? 0 : -EINVAL; > +} > + > +static int send_and_recv_msg(int sockfd, enum test_commands cmd) > +{ > + return __send_and_recv_msg(sockfd, cmd, NULL, 0); > +} > + > +static int send_echo_msg(void) > +{ > + unsigned char buf[sizeof(struct tlv_hdr)]; > + struct tlv_hdr *tlv = (struct tlv_hdr *)buf; > + int sockfd, n; > + > + sockfd = socket(env.family, SOCK_DGRAM, 0); > + if (sockfd < 0) { > + fprintf(stderr, "Failed to create echo socket\n"); > + return -errno; > + } > + > + tlv->type = htons(CMD_ECHO); > + tlv->len = htons(sizeof(*tlv)); > + > + n = sendto(sockfd, buf, sizeof(*tlv), MSG_NOSIGNAL | MSG_CONFIRM, > + (struct sockaddr *)&env.dut.addr, env.dut.addrlen); > + close(sockfd); > + > + return n == ntohs(tlv->len) ? 0 : -EINVAL; > +} > + > +static int tester_run(struct xdp_features *skel) > +{ > + int flags = XDP_FLAGS_UPDATE_IF_NOEXIST | XDP_FLAGS_DRV_MODE; > + bool advertised_cap; > + unsigned int val[1]; > + int i, err, sockfd; > + bool detected_cap; > + > + sockfd = socket(env.family, SOCK_STREAM, 0); > + if (sockfd < 0) { > + fprintf(stderr, "Failed to create tester socket\n"); > + return -errno; > + } > + > + if (settimeo(sockfd, 1000) < 0) > + return -EINVAL; > + > + err = connect(sockfd, (struct sockaddr *)&env.dut_ctrl.addr, > + env.dut_ctrl.addrlen); > + if (err) { > + fprintf(stderr, "Failed to connect to the DUT\n"); > + return -errno; > + } > + > + err = __send_and_recv_msg(sockfd, CMD_GET_XDP_CAP, val, > + ARRAY_SIZE(val)); > + if (err < 0) { > + close(sockfd); > + return err; > + } > + > + advertised_cap = tester_collect_advertised_cap(val[0]); > + > + err = bpf_xdp_attach(env.ifindex, > + bpf_program__fd(skel->progs.xdp_tester), > + flags, NULL); > + if (err) { > + fprintf(stderr, "Failed to attach XDP program to ifindex %d\n", > + env.ifindex); > + goto out; > + } > + > + err = send_and_recv_msg(sockfd, CMD_START); > + if (err) > + goto out; > + > + for (i = 0; i < 10 && !exiting; i++) { > + err = send_echo_msg(); > + if (err < 0) > + goto out; > + > + sleep(1); > + } > + > + err = __send_and_recv_msg(sockfd, CMD_GET_STATS, val, ARRAY_SIZE(val)); > + if (err) > + goto out; > + > + /* stop the test */ > + err = send_and_recv_msg(sockfd, CMD_STOP); > + /* send a new echo message to wake echo thread of the dut */ > + send_echo_msg(); > + > + detected_cap = tester_collect_detected_cap(skel, val[0]); > + > + fprintf(stdout, "Feature %s: [%s][%s]\n", > get_xdp_feature_str(env.feature), > + detected_cap ? GREEN("DETECTED") : RED("NOT DETECTED"), > + advertised_cap ? GREEN("ADVERTISED") : RED("NOT ADVERTISED")); > +out: > + bpf_xdp_detach(env.ifindex, flags, NULL); > + close(sockfd); > + return err < 0 ? err : 0; > +} > + > +static void set_skel_rodata(struct xdp_features *skel) > +{ > + skel->rodata->expected_feature = env.feature; > + if (env.family == AF_INET6) { > + struct sockaddr_in6 *tester_addr = (void *)&env.tester.addr; > + struct sockaddr_in6 *dut_addr = (void *)&env.dut.addr; > + > + skel->rodata->tester_addr.ip6 = tester_addr->sin6_addr; > + skel->rodata->dut_addr.ip6 = dut_addr->sin6_addr; > + } else { > + struct sockaddr_in *tester_addr = (void *)&env.tester.addr; > + struct sockaddr_in *dut_addr = (void *)&env.dut.addr; > + > + skel->rodata->tester_addr.ip = tester_addr->sin_addr; > + skel->rodata->dut_addr.ip = dut_addr->sin_addr; > + } > +} > + > +int main(int argc, char **argv) > +{ > + struct xdp_features *skel; > + int err; > + > + libbpf_set_strict_mode(LIBBPF_STRICT_ALL); > + libbpf_set_print(libbpf_print_fn); > + > + signal(SIGINT, sig_handler); > + signal(SIGTERM, sig_handler); > + > + set_env_defaul(); > + > + /* Parse command line arguments */ > + err = argp_parse(&argp, argc, argv, 0, NULL, NULL); > + if (err) > + return err; > + > + if (env.ifindex < 0) { > + fprintf(stderr, "Invalid ifindex\n"); > + return -ENODEV; > + } > + > + /* Load and verify BPF application */ > + skel = xdp_features__open(); > + if (!skel) { > + fprintf(stderr, "Failed to open and load BPF skeleton\n"); > + return -EINVAL; > + } > + > + set_skel_rodata(skel); > + > + /* Load & verify BPF programs */ > + err = xdp_features__load(skel); > + if (err) { > + fprintf(stderr, "Failed to load and verify BPF skeleton\n"); > + goto cleanup; > + } > + > + err = xdp_features__attach(skel); > + if (err) { > + fprintf(stderr, "Failed to attach BPF skeleton\n"); > + goto cleanup; > + } > + > + if (env.is_tester) { > + /* Tester */ > + fprintf(stdout, "Starting tester on device %d\n", env.ifindex); > + err = tester_run(skel); > + } else { > + /* DUT */ > + fprintf(stdout, "Starting DUT on device %d\n", env.ifindex); > + err = dut_run(skel); > + } > + > +cleanup: > + xdp_features__destroy(skel); > + > + return err < 0 ? -err : 0; > +} > diff --git a/tools/testing/selftests/bpf/xdp_features.h > b/tools/testing/selftests/bpf/xdp_features.h > new file mode 100644 > index 000000000000..28d7614c4f02 > --- /dev/null > +++ b/tools/testing/selftests/bpf/xdp_features.h > @@ -0,0 +1,33 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > + > +/* test commands */ > +enum test_commands { > + CMD_STOP, /* CMD */ > + CMD_START, /* CMD + xdp feature */ > + CMD_ECHO, /* CMD */ > + CMD_ACK, /* CMD + data */ > + CMD_GET_XDP_CAP, /* CMD */ > + CMD_GET_STATS, /* CMD */ > +}; > + > +#define DUT_CTRL_PORT 12345 > +#define DUT_ECHO_PORT 12346 > + > +struct tlv_hdr { > + __be16 type; > + __be16 len; > + __be32 data[]; > +}; > + > +enum { > + XDP_FEATURE_ABORTED, > + XDP_FEATURE_DROP, > + XDP_FEATURE_PASS, > + XDP_FEATURE_TX, > + XDP_FEATURE_REDIRECT, > + XDP_FEATURE_NDO_XMIT, > + XDP_FEATURE_XSK_ZEROCOPY, > + XDP_FEATURE_HW_OFFLOAD, > + XDP_FEATURE_RX_SG, > + XDP_FEATURE_NDO_XMIT_SG, > +}; > -- > 2.39.1 _______________________________________________ Intel-wired-lan mailing list Intel-wired-lan@osuosl.org https://lists.osuosl.org/mailman/listinfo/intel-wired-lan