【发布时间】:2020-11-16 15:45:46
【问题描述】:
我正在尝试编写一个 EBPF 程序,但在某些地方我卡住了。在文档中,据说您定义的函数是绝对有效且可调用的,但是,即使在这个最简单的示例(尽管使用 map)中,使用tc 将程序加载到界面时也会出错,并且验证器绝对不会发出任何信号。
这是我的示例程序,我还插入了必要的 BPF-helpers,以便立即编译:
#include <linux/bpf.h>
#include <linux/pkt_cls.h>
#ifndef __BPF_HELPERS_H
#define __BPF_HELPERS_H
/* helper macro to place programs, maps, license in
* different sections in elf_bpf file. Section names
* are interpreted by elf_bpf loader
*/
#define SEC(NAME) __attribute__((section(NAME), used))
/* a helper structure used by eBPF C program
* to describe map attributes to elf_bpf loader
*/
struct bpf_map_def {
unsigned int type;
unsigned int key_size;
unsigned int value_size;
unsigned int max_entries;
unsigned int map_flags;
unsigned int inner_map_idx;
unsigned int numa_node;
};
/* helper functions called from eBPF programs written in C */
static void *(*bpf_map_lookup_elem)(void *map, const void *key) =
(void *) BPF_FUNC_map_lookup_elem;
static int (*bpf_clone_redirect)(void *ctx, int ifindex, int flags) =
(void *) BPF_FUNC_clone_redirect;
#endif
struct bpf_map_def SEC("maps") my_map = {
.type = BPF_MAP_TYPE_ARRAY,
.key_size = sizeof(__u32),
.value_size = sizeof(int),
.max_entries = 256,
};
static int foo(int value) { return value == 1; }
SEC("action")
int filter_and_redirect(struct __sk_buff* skb)
{
int key = 0;
int *value = (int*)bpf_map_lookup_elem(&my_map, &key);
if (!value) return TC_ACT_SHOT;
if (foo(*value)) bpf_clone_redirect(skb, 1, 1);
// if (*value == 1) bpf_clone_redirect(skb, 1, 1);
return TC_ACT_SHOT;
}
SEC("classifier")
int cls_main(struct __sk_buff* skb) { return -1; }
我用下面的命令编译,挺标准的
clang -O2 -fno-inline-functions -emit-llvm -c example.c -o - | llc -march=bpf -filetype=obj -o example.o
然后通过加载到界面
sudo tc filter add dev foo0 parent ffff: bpf obj example.o sec classifier flowid ffff:1 action bpf obj example.o sec action ok
如果我用函数调用注释该行并取消注释下一个,它会完美运行。但是,用原程序加载的结果是:
Error fetching program/map!
bad action parsing
parse_action: bad value (6:bpf)!
Illegal "action"
应该有一些日志,具体说明为什么我的程序对验证程序无效,但没有,所以我真的被卡住了。为什么它不允许我使用该功能?谢谢大家的建议!
如果您想查看llvm-objdump -S 命令的结果,可以找到here。
[编辑]
从接受的答案看来,tc 中有一个错误(?),它基本上不允许不使用地图的函数。这就是为什么作为临时解决方法,我将示例中的函数更改为:
struct bpf_map_def SEC("maps") dummy_map = {
.type = BPF_MAP_TYPE_ARRAY,
.key_size = sizeof(int),
.value_size = sizeof(int),
.max_entries = 1,
};
int foo(int value) {
int key = 0;
int *v = (int*)bpf_map_lookup_elem(&dummy_map, &key);
return value == 1;
}
现在它可以工作了,但是需要额外的地图操作,这不是必需的,所以我期待更多关于这个问题的消息。
【问题讨论】: