On Sat, May 18, 2024 at 3:13 AM Lorenzo Bianconi <lorenzo@xxxxxxxxxx> wrote: > > Introduce e2e selftest for bpf_xdp_flow_offload_lookup kfunc through > xdp_flowtable utility. > > Signed-off-by: Lorenzo Bianconi <lorenzo@xxxxxxxxxx> > --- > tools/testing/selftests/bpf/Makefile | 10 +- > tools/testing/selftests/bpf/config | 4 + > .../selftests/bpf/progs/xdp_flowtable.c | 141 +++++++++++++++++ > .../selftests/bpf/test_xdp_flowtable.sh | 112 ++++++++++++++ > tools/testing/selftests/bpf/xdp_flowtable.c | 142 ++++++++++++++++++ > 5 files changed, 407 insertions(+), 2 deletions(-) > create mode 100644 tools/testing/selftests/bpf/progs/xdp_flowtable.c > create mode 100755 tools/testing/selftests/bpf/test_xdp_flowtable.sh > create mode 100644 tools/testing/selftests/bpf/xdp_flowtable.c > > diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile > index e0b3887b3d2df..7361c429bed62 100644 > --- a/tools/testing/selftests/bpf/Makefile > +++ b/tools/testing/selftests/bpf/Makefile > @@ -133,7 +133,8 @@ TEST_PROGS := test_kmod.sh \ > test_bpftool_metadata.sh \ > test_doc_build.sh \ > test_xsk.sh \ > - test_xdp_features.sh > + test_xdp_features.sh \ > + test_xdp_flowtable.sh > > TEST_PROGS_EXTENDED := with_addr.sh \ > with_tunnels.sh ima_setup.sh verify_sig_setup.sh \ > @@ -144,7 +145,7 @@ TEST_GEN_PROGS_EXTENDED = 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 \ > - xdp_features bpf_test_no_cfi.ko > + xdp_features bpf_test_no_cfi.ko xdp_flowtable > > TEST_GEN_FILES += liburandom_read.so urandom_read sign-file uprobe_multi > > @@ -476,6 +477,7 @@ 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 > +xdp_flowtable.skel.h-deps := xdp_flowtable.bpf.o > > LINKED_BPF_SRCS := $(patsubst %.bpf.o,%.c,$(foreach skel,$(LINKED_SKELS),$($(skel)-deps))) > > @@ -710,6 +712,10 @@ $(OUTPUT)/xdp_features: xdp_features.c $(OUTPUT)/network_helpers.o $(OUTPUT)/xdp > $(call msg,BINARY,,$@) > $(Q)$(CC) $(CFLAGS) $(filter %.a %.o %.c,$^) $(LDLIBS) -o $@ > > +$(OUTPUT)/xdp_flowtable: xdp_flowtable.c $(OUTPUT)/xdp_flowtable.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/config b/tools/testing/selftests/bpf/config > index eeabd798bc3ae..1a9aea01145f7 100644 > --- a/tools/testing/selftests/bpf/config > +++ b/tools/testing/selftests/bpf/config > @@ -82,6 +82,10 @@ CONFIG_NF_CONNTRACK=y > CONFIG_NF_CONNTRACK_MARK=y > CONFIG_NF_DEFRAG_IPV4=y > CONFIG_NF_DEFRAG_IPV6=y > +CONFIG_NF_TABLES=y > +CONFIG_NETFILTER_INGRESS=y > +CONFIG_NF_FLOW_TABLE=y > +CONFIG_NF_FLOW_TABLE_INET=y > CONFIG_NF_NAT=y > CONFIG_RC_CORE=y > CONFIG_SECURITY=y > diff --git a/tools/testing/selftests/bpf/progs/xdp_flowtable.c b/tools/testing/selftests/bpf/progs/xdp_flowtable.c > new file mode 100644 > index 0000000000000..888ac87790f90 > --- /dev/null > +++ b/tools/testing/selftests/bpf/progs/xdp_flowtable.c > @@ -0,0 +1,141 @@ > +// SPDX-License-Identifier: GPL-2.0 > +#include <vmlinux.h> > +#include <bpf/bpf_helpers.h> > +#include <bpf/bpf_endian.h> > + > +#define MAX_ERRNO 4095 > + > +#define ETH_P_IP 0x0800 > +#define ETH_P_IPV6 0x86dd > +#define IP_MF 0x2000 /* "More Fragments" */ > +#define IP_OFFSET 0x1fff /* "Fragment Offset" */ > +#define AF_INET 2 > +#define AF_INET6 10 > + > +struct flow_offload_tuple_rhash * > +bpf_xdp_flow_offload_lookup(struct xdp_md *, > + struct bpf_fib_lookup *) __ksym; > + > +struct { > + __uint(type, BPF_MAP_TYPE_ARRAY); > + __type(key, __u32); > + __type(value, __u32); > + __uint(max_entries, 1); > +} stats SEC(".maps"); > + > +static __always_inline bool > +xdp_flowtable_offload_check_iphdr(struct iphdr *iph) Please do not use __always_inline in bpf code. It was needed 10 years ago. Not any more. > +{ > + /* ip fragmented traffic */ > + if (iph->frag_off & bpf_htons(IP_MF | IP_OFFSET)) > + return false; > + > + /* ip options */ > + if (iph->ihl * 4 != sizeof(*iph)) > + return false; > + > + if (iph->ttl <= 1) > + return false; > + > + return true; > +} > + > +static __always_inline bool > +xdp_flowtable_offload_check_tcp_state(void *ports, void *data_end, u8 proto) > +{ > + if (proto == IPPROTO_TCP) { > + struct tcphdr *tcph = ports; > + > + if (tcph + 1 > data_end) > + return false; > + > + if (tcph->fin || tcph->rst) > + return false; > + } > + > + return true; > +} > + > +SEC("xdp.frags") > +int xdp_flowtable_do_lookup(struct xdp_md *ctx) > +{ > + void *data_end = (void *)(long)ctx->data_end; > + struct flow_offload_tuple_rhash *tuplehash; > + struct bpf_fib_lookup tuple = { > + .ifindex = ctx->ingress_ifindex, > + }; > + void *data = (void *)(long)ctx->data; > + struct ethhdr *eth = data; > + struct flow_ports *ports; > + __u32 *val, key = 0; > + > + if (eth + 1 > data_end) > + return XDP_DROP; > + > + switch (eth->h_proto) { > + case bpf_htons(ETH_P_IP): { > + struct iphdr *iph = data + sizeof(*eth); > + > + ports = (struct flow_ports *)(iph + 1); > + if (ports + 1 > data_end) > + return XDP_PASS; > + > + /* sanity check on ip header */ > + if (!xdp_flowtable_offload_check_iphdr(iph)) > + return XDP_PASS; > + > + if (!xdp_flowtable_offload_check_tcp_state(ports, data_end, > + iph->protocol)) > + return XDP_PASS; > + > + tuple.family = AF_INET; > + tuple.tos = iph->tos; > + tuple.l4_protocol = iph->protocol; > + tuple.tot_len = bpf_ntohs(iph->tot_len); > + tuple.ipv4_src = iph->saddr; > + tuple.ipv4_dst = iph->daddr; > + tuple.sport = ports->source; > + tuple.dport = ports->dest; > + break; > + } > + case bpf_htons(ETH_P_IPV6): { > + struct in6_addr *src = (struct in6_addr *)tuple.ipv6_src; > + struct in6_addr *dst = (struct in6_addr *)tuple.ipv6_dst; > + struct ipv6hdr *ip6h = data + sizeof(*eth); > + > + ports = (struct flow_ports *)(ip6h + 1); > + if (ports + 1 > data_end) > + return XDP_PASS; > + > + if (ip6h->hop_limit <= 1) > + return XDP_PASS; > + > + if (!xdp_flowtable_offload_check_tcp_state(ports, data_end, > + ip6h->nexthdr)) > + return XDP_PASS; > + > + tuple.family = AF_INET6; > + tuple.l4_protocol = ip6h->nexthdr; > + tuple.tot_len = bpf_ntohs(ip6h->payload_len); > + *src = ip6h->saddr; > + *dst = ip6h->daddr; > + tuple.sport = ports->source; > + tuple.dport = ports->dest; > + break; > + } > + default: > + return XDP_PASS; > + } > + > + tuplehash = bpf_xdp_flow_offload_lookup(ctx, &tuple); > + if (!tuplehash) > + return XDP_PASS; > + > + val = bpf_map_lookup_elem(&stats, &key); > + if (val) > + __sync_add_and_fetch(val, 1); > + > + return XDP_PASS; > +} > + > +char _license[] SEC("license") = "GPL"; > diff --git a/tools/testing/selftests/bpf/test_xdp_flowtable.sh b/tools/testing/selftests/bpf/test_xdp_flowtable.sh > new file mode 100755 > index 0000000000000..1a8a40aebbdf1 > --- /dev/null > +++ b/tools/testing/selftests/bpf/test_xdp_flowtable.sh Sorry shell scripts are not allowed. Integrate it into test_progs. pw-bot: cr