linux-kernelclangebpflibbpf

eBPF: libbpf: failed to find BTF for extern 'bpf_dynptr_from_skb': -2


I compiled the bpf code below(netfilter_ip4_blacklist.bpf.c) successfully, but when i load the netfilter_ip4_blacklist.bpf.o with bpftool prog load netfilter_ip4_blacklist.bpf.o /sys/fs/bpf/netfilter_ip4_blacklist, it throws libbpf: failed to find BTF for extern 'bpf_dynptr_from_skb': -2

// SPDX-License-Identifier: GPL-2.0

#include "vmlinux.h"
#include <bpf/bpf_helpers.h>


#define NF_DROP 0
#define NF_ACCEPT 1

extern int bpf_dynptr_from_skb(struct sk_buff *skb,
        __u64 flags, struct bpf_dynptr *ptr__uninit) __ksym;
extern void *bpf_dynptr_slice(const struct bpf_dynptr *ptr,
        uint32_t offset, void *buffer, uint32_t buffer__sz) __ksym;


struct ipv4_lpm_key {
    __u32 prefixlen;
    __u32 data;
};

struct {
    __uint(type, BPF_MAP_TYPE_LPM_TRIE);
    __type(key, struct ipv4_lpm_key);
    __type(value, __u32);
    __uint(map_flags, BPF_F_NO_PREALLOC);
    __uint(max_entries, 200);
} ipv4_lpm_map SEC(".maps");


SEC("netfilter")
int netfilter_ip4block(struct bpf_nf_ctx *ctx)
{
    struct sk_buff *skb = ctx->skb;
    struct bpf_dynptr ptr;
    struct iphdr *p, iph = {};
    struct ipv4_lpm_key key;
    __u32 *pvalue;

    if (skb->len <= 20 || bpf_dynptr_from_skb(skb, 0, &ptr))
        return NF_ACCEPT;
    p = bpf_dynptr_slice(&ptr, 0, &iph, sizeof(iph));
    if (!p)
        return NF_ACCEPT;

    /* ip4 only */
    if (p->version != 4)
        return NF_ACCEPT;

    /* search p->daddr in trie */
    key.prefixlen = 32;
    key.data = p->daddr;
    pvalue = bpf_map_lookup_elem(&ipv4_lpm_map, &key);
    if (pvalue) {
        /* cat /sys/kernel/debug/tracing/trace_pipe */
        bpf_printk("rule matched with %d...\n", *pvalue);
        return NF_DROP;
    }
    return NF_ACCEPT;
}

char _license[] SEC("license") = "GPL";

Actually this bpf code comes from a patch from linux up stream, here is the link: samples-bpf-Add-sample-usage-for-BPF_PROG_TYPE_NETFILTER.patch

Here are the informations of the tool chain and kernel:

all above compiled and installed from source, here are the compiling options:

# for clang/llvm
cmake -S llvm -B build -G 'Unix Makefiles' -DLLVM_ENABLE_PROJECTS="clang;lld;lldb" -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Release -DLLVM_USE_LINKER=lld -DLLVM_BINUTILS_INCDIR=/usr/include -DLLVM_BUILD_LLVM_DYLIB=true -DLLVM_LINK_LLVM_DYLIB=true -DCLANG_LINK_CLANG_DYLIB=true -DLLVM_PARALLEL_LINK_JOBS=8 -DLLVM_TARGETS_TO_BUILD="X86;BPF"

# for kernel
CONFIG_BPF=y
CONFIG_HAVE_EBPF_JIT=y
CONFIG_ARCH_WANT_DEFAULT_BPF_JIT=y
CONFIG_BPF_SYSCALL=y
CONFIG_BPF_JIT=y
CONFIG_BPF_JIT_ALWAYS_ON=y
CONFIG_BPF_JIT_DEFAULT_ON=y
CONFIG_BPF_UNPRIV_DEFAULT_OFF=y
CONFIG_BPF_LSM=y
CONFIG_NETFILTER_BPF_LINK=y
CONFIG_BPFILTER=y
CONFIG_BPF_STREAM_PARSER=y
CONFIG_LWTUNNEL_BPF=y
CONFIG_BPF_EVENTS=y
# some else not concerning

# for libbpf
nothing

# for bpftool
nothing

what i did:

  1. clang -target bpf -g -c nf-monitor.bpf.c -o netfilter_ip4_blacklist.bpf.o
  2. bpftool prog load netfilter_ip4_blacklist.bpf.o /sys/fs/bpf/netfilter_ip4_blacklist

what i tried:

  1. lookinto the libbpf source code for why,it looks like bpf_dynptr_from_skb should be in the .BTF section of .o but actually not, may it be something wrong with clang/llvm while linking?(i don't think so)

So can anyone tell me where the problem is?Thanks a log!


Solution

  • You are right in all of your observations, the BTF info for the kfuncs doesn't make it into the .BTF section. This is because you are calling clang without optimizations -O2.

    Changing the clang invocation to: clang -target bpf -g -O2 -c nf-monitor.bpf.c -o netfilter_ip4_blacklist.bpf.o resolves the issue.

    The unfortunate reason for this is the way eBPF is integrated into clang. Simply put a number of critical features are implemented in optimization passes only enabled in -O2 mode. So specifying it is required to get load-able eBPF object files.