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:
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
what i tried:
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!
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.