eBPF:libbpf:无法找到 extern 'bpf_dynptr_from_skb' 的 BTF:-2

问题描述 投票:0回答:1

我成功编译了下面的bpf代码(netfilter_ip4_blacklist.bpf.c),但是当我用

bpftool prog load netfilter_ip4_blacklist.bpf.o /sys/fs/bpf/netfilter_ip4_blacklist
加载netfilter_ip4_blacklist.bpf.o时,它抛出
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";

实际上这个bpf代码来自linux上游的一个补丁,链接如下: samples-bpf-Add-sample-usage-for-BPF_PROG_TYPE_NETFILTER.patch

以下是工具链和内核信息:

  • 内核:6.6.0
  • clang/llvm:18.0.1
  • libbpf:1.4.1
  • bpf工具:7.4.0

以上都是从源码编译安装的,以下是编译选项:

# 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

我做了什么:

  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

我尝试过的:

  1. 查看libbpf源代码看看为什么,看起来
    bpf_dynptr_from_skb
    应该在.o的.BTF部分但实际上不是,可能是clang/llvm在链接时出了问题?(我不这么认为)

那么谁能告诉我问题出在哪里?谢谢日志!

linux-kernel clang ebpf libbpf
1个回答
0
投票

您的所有观察都是正确的,kfuncs 的 BTF 信息不会进入 .BTF。原因是你在没有优化的情况下调用 clang

-O2
.

将 clang 调用更改为:

clang -target bpf -g -O2 -c nf-monitor.bpf.c -o netfilter_ip4_blacklist.bpf.o
可解决该问题。

造成这种情况的不幸原因是 eBPF 集成到 clang 中的方式。简而言之,许多关键功能是在仅在

-O2
模式下启用的优化过程中实现的。因此需要指定它才能获得可加载的 eBPF。

© www.soinside.com 2019 - 2024. All rights reserved.