我成功编译了下面的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
以下是工具链和内核信息:
以上都是从源码编译安装的,以下是编译选项:
# 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
我做了什么:
clang -target bpf -g -c nf-monitor.bpf.c -o netfilter_ip4_blacklist.bpf.o
bpftool prog load netfilter_ip4_blacklist.bpf.o /sys/fs/bpf/netfilter_ip4_blacklist
我尝试过的:
bpf_dynptr_from_skb
应该在.o的.BTF部分但实际上不是,可能是clang/llvm在链接时出了问题?(我不这么认为)那么谁能告诉我问题出在哪里?谢谢日志!
您的所有观察都是正确的,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。