bcc Reference Guide
用于检索(Ctrl-F)和参考。对于教程,请从tutorial.md开始。
本指南不完整。如果感觉缺少了什么,请检查bcc和内核源代码。如果你确认我们遗漏了什么,请发送一个pull request来修复它,并帮助每个人。
Contents
- BPF C
- Events & Arguments
- Data
- 1. bpf_probe_read_kernel()
- 2. bpf_probe_read_kernel_str()
- 3. bpf_ktime_get_ns()
- 4. bpf_get_current_pid_tgid()
- 5. bpf_get_current_uid_gid()
- 6. bpf_get_current_comm()
- 7. bpf_get_current_task()
- 8. bpf_log2l()
- 9. bpf_get_prandom_u32()
- 10. bpf_probe_read_user()
- 11. bpf_probe_read_user_str()
- 12. bpf_get_ns_current_pid_tgid()
- Debugging
- Output
- Maps
- 1. BPF_TABLE
- 2. BPF_HASH
- 3. BPF_ARRAY
- 4. BPF_HISTOGRAM
- 5. BPF_STACK_TRACE
- 6. BPF_PERF_ARRAY
- 7. BPF_PERCPU_HASH
- 8. BPF_PERCPU_ARRAY
- 9. BPF_LPM_TRIE
- 10. BPF_PROG_ARRAY
- 11. BPF_DEVMAP
- 12. BPF_CPUMAP
- 13. BPF_XSKMAP
- 14. BPF_ARRAY_OF_MAPS
- 15. BPF_HASH_OF_MAPS
- 16. BPF_STACK
- 17. BPF_QUEUE
- 18. BPF_SOCKHASH
- 19. map.lookup()
- 20. map.lookup_or_try_init()
- 21. map.delete()
- 22. map.update()
- 23. map.insert()
- 24. map.increment()
- 25. map.get_stackid()
- 26. map.perf_read()
- 27. map.call()
- 28. map.redirect_map()
- 29. map.push()
- 30. map.pop()
- 31. map.peek()
- 32. map.sock_hash_update()
- 33. map.msg_redirect_hash()
- 34. map.sk_redirect_hash()
- Licensing
- Rewriter
BPF C
本节描述bcc程序的C部分。
Events & Arguments
1. kprobes
语法:kprobe__kernel_function_name
kprobe__
是一个特殊的前缀,它为作为余数提供的内核函数名创建kprobe(内核函数调用的动态跟踪)。您也可以通过声明一个普通的C函数来使用kprobes,然后使用Python BPF.attach_kprobe()
(稍后介绍)将其与内核函数相关联。
参数在函数声明中指定:kprobe__kernel_function_name(struct pt_regs *ctx [,argument1 …])
举例来说:
For example:
int kprobe__tcp_v4_connect(struct pt_regs *ctx, struct sock *sk) {
[...]
}
这将使用kprobe对tcp_v4_connect()内核函数进行检测,参数如下:
struct pt_regs *ctx
:寄存器和BPF上下文。struct sock *sk
:tcp_v4_connect()的第一个参数。
第一个参数总是struct pt_regs *
,其余的是函数的参数(如果你不打算使用它们,就不需要指定它们)。
现场示例:
code (output),
code (output)
2. kretprobes
语法:kretprobe__kernel_function_name
kretprobe__
是一个特殊的前缀,它为作为余数提供的内核函数名创建kretprobe(内核函数返回的动态跟踪)。您也可以通过声明一个普通的C函数来使用kretprobes,然后使用Python BPF.attach_kretprobe()
(稍后介绍)将其与内核函数相关联。
返回值可作为PT_REGS_RC(ctx)
,给定函数声明:kretprobe__kernel_function_name(struct pt_regs *ctx)
举例来说:
int kretprobe__tcp_v4_connect(struct pt_regs *ctx)
{
int ret = PT_REGS_RC(ctx);
[...]
}
这将使用kretprobe检测tcp_v4_connect()内核函数的返回,并将返回值存储在ret
中。
3. Tracepoints
语法:TRACEPOINT_PROBE(category,event)
这是一个宏,用于检测由category:event定义的跟踪点。
跟踪点名称为<category>:<event>
。
The probe function name is tracepoint__<category>__<event>
.
参数在args
struct中可用,它们是跟踪点参数。列出这些的一种方法是在/sys/kernel/debug/tracing/events/category/event/format下cat相关的格式文件。
在每个需要上下文作为参数的函数中,可以使用args
struct代替ctx
。这包括perf_submit()。
举例来说:
TRACEPOINT_PROBE(random, urandom_read) {
// args is from /sys/kernel/debug/tracing/events/random/urandom_read/format
bpf_trace_printk("%d\\n", args->got_bits);
return 0;
}
这将检测跟踪点random:urandom_read tracepoint
,并打印跟踪点参数got_bits
。
When using Python API, this probe is automatically attached to the right tracepoint target.
三号
注意,上面定义的探测函数的名称是BPF::attach_tracepoint("random:urandom_read", "tracepoint__random__urandom_read")
。
Note the name of the probe function defined above is tracepoint__random__urandom_read
.
现场示例:
code (output),
search /examples,
search /tools
4. uprobes
通过在C中声明一个普通函数,然后通过BPF.attach_uprobe()
(稍后介绍)将其关联为Python中的uprobe探测器来检测它们。
参数可以使用PT_REGS_PARM
宏来检查。
举例来说:
int count(struct pt_regs *ctx) {
char buf[64];
bpf_probe_read_user(&buf, sizeof(buf), (void *)PT_REGS_PARM1(ctx));
bpf_trace_printk("%s %d", buf, PT_REGS_PARM2(ctx));
return(0);
}
它将第一个参数作为字符串读取,然后将第二个参数作为整数打印出来。
现场示例:
code
5. uretprobes
通过在C中声明一个普通函数,然后通过BPF.attach_uretprobe()
(稍后介绍)将其关联为Python中的uretprobe探测器来检测这些。
返回值可作为PT_REGS_RC(ctx)
,给定函数声明:function_name(struct pt_regs *ctx)
举例来说:
BPF_HISTOGRAM(dist);
int count(struct pt_regs *ctx) {
dist.increment(PT_REGS_RC(ctx));
return 0;
}
这将递增dist
直方图中由返回值索引的存储桶。
现场示例:
code (output),
code (output)
6. USDT probes
这些是用户静态定义跟踪(USDT)探测器,可以放置在某些应用程序或库中,以提供用户级的跟踪点等效物。为USDT支持方法提供的主要BPF方法是enable_probe()
。USDT探测器是通过在C中声明一个普通函数,然后通过USDT.enable_probe()
将其关联为Python中的USDT探测器来检测的。
参数可以通过以下方式读取:bpf_usdt_readarg(index,ctx,&addr)
举例来说:
int do_trace(struct pt_regs *ctx) {
uint64_t addr;
char path[128];
bpf_usdt_readarg(6, ctx, &addr);
bpf_probe_read_user(&path, sizeof(path), (void *)addr);
bpf_trace_printk("path:%s\\n", path);
return 0;
};
这将读取第六个USDT参数,然后将其作为字符串拉入path
。
当通过C API中BPF::init
的第三个参数初始化USDT时,如果任何USDT失败到init
,则整个BPF::init
将失败。如果您对某些USDT无法执行init
没有问题,请在调用BPF::init_usdt
之前使用BPF::init
。
现场示例:
code,
search /examples,
search /tools
7. Raw Tracepoints
语法:RAW_TRACEPOINT_PROBE(event)
这是一个宏,用于检测由事件定义的原始跟踪点。
参数是指向结构bpf_raw_tracepoint_args
的指针,它在bpf. h中定义。struct字段args
包含原始跟踪点的所有参数,您可以在linux树include/trace/events中找到这些参数
目录。
举例来说:
RAW_TRACEPOINT_PROBE(sched_switch)
{
// TP_PROTO(bool preempt, struct task_struct *prev, struct task_struct *next)
struct task_struct *prev = (struct task_struct *)ctx->args[1];
struct task_struct *next= (struct task_struct *)ctx->args[2];
s32 prev_tgid, next_tgid;
bpf_probe_read_kernel(&prev_tgid, sizeof(prev->tgid), &prev->tgid);
bpf_probe_read_kernel(&next_tgid, sizeof(next->tgid), &next->tgid);
bpf_trace_printk("%d -> %d\\n", prev_tgid, next_tgid);
}
这将检测sched:sched_switch跟踪点,并打印上一个和下一个tgid。
现场示例:
search /tools
8. system call tracepoints
语法:syscall__SYSCALLNAME
syscall__
是一个特殊的前缀,它为作为余数提供的系统调用名称创建一个kprobe。你可以通过声明一个普通的C函数来使用它,然后使用Python BPF.get_syscall_fnname(SYSCALLNAME)
和BPF.attach_kprobe()
来关联它。
参数在函数声明中指定:syscall__SYSCALLNAME(struct pt_regs *ctx, [, argument1 ...])
.
举例来说:
int syscall__execve(struct pt_regs *ctx,
const char __user *filename,
const char __user *const __user *__argv,
const char __user *const __user *__envp)
{
[...]
}
这将检测execve系统调用。
第一个参数总是struct pt_regs *
,其余的是函数的参数(如果你不打算使用它们,就不需要指定它们)。
对应的Python代码:
b = BPF(text=bpf_text)
execve_fnname = b.get_syscall_fnname("execve")
b.attach_kprobe(event=execve_fnname, fn_name="syscall__execve")
Examples in situ:
code (output)
9. kfuncs
语法:KFUNC_PROBE(function,typeof(arg1)arg1,typeof(arg2)arge …)
MODULE_KFUNC_PROBE(module,function,typeof(arg1)arg1,typeof(arg2)arge …)
这是一个通过trampoline实现内核函数的宏
在函数执行之前。它由函数名和
定义为argX的函数参数。
举例来说:
KFUNC_PROBE(do_sys_open, int dfd, const char *filename, int flags, int mode)
{
...
这将检测do_sys_open内核函数并生成其参数
可作为标准参数值访问。
现场示例:
search /tools
10. kretfuncs
语法:KRETFUNC_PROBE(event,typeof(arg1)arg1,typeof(arg2)arge …,int ret)
MODULE_KRETFUNC_PROBE(module,function,typeof(arg1)arg1,typeof(arg2)arge …)
这是一个通过trampoline实现内核函数的宏
在函数执行之后。它由函数名和
定义为argX的函数参数。
探测器的最后一个参数是检测函数的返回值。
举例来说:
KRETFUNC_PROBE(do_sys_open, int dfd, const char *filename, int flags, int mode, int ret)
{
...
这将检测do_sys_open内核函数并生成其参数
可作为标准参数值与其返回值一起访问。
现场示例:
search /tools
11. LSM Probes
语法:LSM_PROBE(hook,typeof(arg1)arg1,typeof(arg2)arg2 …)
这是一个将LSM钩子作为BPF程序进行检测的宏。它可以是
用于审核安全事件并在BPF中实施MAC安全策略。
It is defined by specifying the hook name followed by its arguments.
挂钩名称可以在
include/linux/security.h
通过使用像security_hookname
这样的函数,只使用hookname
部分。
For example, security_bpf
would simply become bpf
.
与其他BPF程序类型不同,LSM探测器中指定的返回值
很重要返回值0允许钩子成功,而
任何非零的返回值都将导致钩子失败并拒绝
安全操作。
以下示例检测拒绝所有未来BPF操作的挂接:
LSM_PROBE(bpf, int cmd, union bpf_attr *attr, unsigned int size)
{
return -EPERM;
}
这将对security_bpf
钩进行器械操作,并使其返回-EPERM
。
以允许该操作代替。
to allow the operation instead.
LSM探测器至少需要一个5.7+内核,并设置以下配置选项:
CONFIG_BPF_LSM=y
CONFIG_LSM
逗号分隔的字符串必须包含“bpf”(例如,
#2)
现场示例:
search /tests
12. BPF ITERATORS
语法:BPF_ITER(target)
这是一个为bpf迭代器程序定义程序签名的宏。参数target指定程序要迭代的内容。
目前,内核没有接口来发现支持哪些目标。要查找支持的内容,最好在tools/testing/selftests/bpf/prog_test/bpf_iter.c中,一些示例bpf iter程序在tools/testing/selftests/bpf/progs中,文件名前缀为bpf_iter。
下面的示例为目标任务定义了一个程序,它遍历内核中的所有任务。
BPF_ITER(task)
{
struct seq_file *seq = ctx->meta->seq;
struct task_struct *task = ctx->task;
if (task == (void *)0)
return 0;
... task->pid, task->tgid, task->comm, ...
return 0;
}
在5.8内核中引入了BPF迭代器,用于task、task_file、bpf_map、netlink_sock和ipv6_route。在5.9中,增加了对tcp/udp套接字和bpf映射元素(hashmap、arraymap和sk_local_storage_map)遍历的支持。
Data
1. bpf_probe_read_kernel()
语法:syscall__SYSCALLNAME
返回:成功时返回0
这将大小字节从内核地址空间复制到BPF堆栈,以便BPF稍后可以对其进行操作。为了安全起见,所有内核内存读取必须通过bpf_probe_read_kernel()。这在某些情况下会自动发生,例如解除内核变量的引用,因为bcc会重写BPF程序以包含必要的bpf_probe_read_kernel()。
现场示例:
search /examples,
search /tools
2. bpf_probe_read_kernel_str()
语法:syscall__SYSCALLNAME
退货:
- >0字符串长度,成功时包括尾随NULL
- < 0错误
这会将一个以NULL
结尾的字符串从内核地址空间复制到BPF堆栈,以便BPF稍后可以对其进行操作。在字符串长度小于size的情况下,不使用另外的NULL
字节填充目标。如果字符串长度大于size,则仅复制size - 1
字节,并将最后一个字节设置为NULL
。
现场示例:
search /examples,
search /tools
3. bpf_ktime_get_ns()
语法:syscall__SYSCALLNAME
返回:u64纳秒数。在系统靴子时启动,但在挂起期间停止。
现场示例:
search /examples,
search /tools
4. bpf_get_current_pid_tgid()
语法:syscall__SYSCALLNAME
返回:current->tgid << 32 | current->pid
返回低32位的进程ID(内核的PID视图,在用户空间中通常显示为线程ID),以及高32位的线程组ID(用户空间通常认为是PID)。通过直接将其设置为u32,我们丢弃了高32位。
现场示例:
search /examples,
search /tools
5. bpf_get_current_uid_gid()
语法:syscall__SYSCALLNAME
返回:current->tgid << 32 | current->pid
返回用户ID和组ID。
现场示例:
search /examples,
search /tools
6. bpf_get_current_comm()
语法:syscall__SYSCALLNAME
返回:成功时返回0
用当前进程名填充第一个参数地址。它应该是一个指向至少大小为TASK_COMM_LEN的字符数组的指针,该数组在linux/sched. h中定义。举例来说:
#include <linux/sched.h>
int do_trace(struct pt_regs *ctx) {
char comm[TASK_COMM_LEN];
bpf_get_current_comm(&comm, sizeof(comm));
[...]
Examples in situ:
search /examples,
search /tools
7. bpf_get_current_task()
语法:syscall__SYSCALLNAME
返回:当前任务作为指向结构task_struct的指针。
返回指向当前任务的task_struct对象的指针。这个帮助器可以用来计算进程的CPU时间、识别内核线程、获取当前CPU的运行队列或检索许多其他信息。
在Linux 4.13中,由于字段随机化的问题,您可能需要在includes之前使用两个#define指令:
#define randomized_struct_fields_start struct {
#define randomized_struct_fields_end };
#include <linux/sched.h>
int do_trace(void *ctx) {
struct task_struct *t = (struct task_struct *)bpf_get_current_task();
[...]
Examples in situ:
search /examples,
search /tools
8. bpf_log2l()
语法:syscall__SYSCALLNAME
返回所提供值的log-2。这通常用于创建直方图的索引,以构建2的幂直方图。
现场示例:
search /examples,
search /tools
9. bpf_get_prandom_u32()
语法:syscall__SYSCALLNAME
返回伪随机u32。
原位示例:
search /examples,
search /tools
10. bpf_probe_read_user()
语法:syscall__SYSCALLNAME
返回:成功时返回0
这会尝试将大小字节从用户地址空间安全地读取到BPF堆栈,以便BPF稍后可以对其进行操作。为了安全起见,所有用户地址空间内存读取都必须通过bpf_probe_read_user()。
现场示例:
search /examples,
search /tools
11. bpf_probe_read_user_str()
语法:syscall__SYSCALLNAME
退货:
- >0字符串长度,成功时包括尾随NULL
- < 0错误
这会将一个以NULL
结尾的字符串从用户地址空间复制到BPF堆栈,以便BPF稍后可以对其进行操作。在字符串长度小于size的情况下,不使用另外的NULL
字节填充目标。如果字符串长度大于size,则仅复制size - 1
字节,并将最后一个字节设置为NULL
。
现场示例:
search /examples,
search /tools
12. bpf_get_ns_current_pid_tgid()
语法:syscall__SYSCALLNAME
从当前命名空间看到的pid和tgid的值将在nsdata中返回。
成功时返回0,失败时返回以下之一:
- -EINVAL,如果提供的dev和inum与当前任务的nsfs不匹配dev_t和inode号,或者如果dev转换为dev_t丢失了高位。
- -ENOENT,如果当前任务不存在pidns。
现场示例:
search /examples,
search /tools
Debugging
1. bpf_override_return()
语法:syscall__SYSCALLNAME
返回:成功时返回0
在附加到函数项kprobe的程序中使用时,将导致
执行要跳过的函数,立即返回rc
。
This is used for targeted error injection.
bpf_override_return仅在kprobed函数被列入白名单时才起作用
允许错误注入。白名单需要用
ALLOW_ERROR_INJECTION()
在内核源代码树中;参见io_ctl_init
举个例子。如果kprobed函数没有被列入白名单,那么bpf程序将
无法与 ioctl(PERF_EVENT_IOC_SET_BPF): Invalid argument
连接
int kprobe__io_ctl_init(void *ctx) {
bpf_override_return(ctx, -ENOMEM);
return 0;
}
Output
1. bpf_trace_printk()
语法:syscall__SYSCALLNAME
返回:成功时返回0
一个简单的内核工具,用于printf()到公共trace_pipe(/sys/kernel/debug/tracing/trace_pipe)。这对于一些快速的示例来说是可以的,但有局限性:最多3个参数,仅1% s,并且trace_pipe是全局共享的,因此并发程序将具有冲突输出。更好的接口是通过BPF_PERF_OUTPUT()。请注意,调用这个helper比原始内核版本更简单,原始内核版本的第二个参数是fmt_size
。
现场示例:
search /examples,
search /tools
2. BPF_PERF_OUTPUT
语法:syscall__SYSCALLNAME
创建一个BPF表,用于通过性能缓冲区将自定义事件数据推送到用户空间。这是将每个事件的数据推送到用户空间的首选方法。
举例来说:
struct data_t {
u32 pid;
u64 ts;
char comm[TASK_COMM_LEN];
};
BPF_PERF_OUTPUT(events);
int hello(struct pt_regs *ctx) {
struct data_t data = {};
data.pid = bpf_get_current_pid_tgid();
data.ts = bpf_ktime_get_ns();
bpf_get_current_comm(&data.comm, sizeof(data.comm));
events.perf_submit(ctx, &data, sizeof(data));
return 0;
}
输出表名为events
,数据通过events.perf_submit()
推送到该表。
现场示例:
search /examples,
search /tools
3. perf_submit()
语法:syscall__SYSCALLNAME
返回:成功时返回0
BPF_PERF_OUTPUT表的一种方法,用于向用户空间提交自定义事件数据。参见BPF_PERF_OUTPUT条目。(This最终调用bpf_perf_event_output()。
kprobes或kretprobes中提供了ctx
参数。对于SCHED_CLS
或SOCKET_FILTER
程序,必须使用struct __sk_buff *skb
。
现场示例:
search /examples,
search /tools
4. perf_submit_skb()
语法:syscall__SYSCALLNAME
返回:成功时返回0
网络程序类型中可用的BPF_PERF_OUTPUT表的一种方法,用于向用户空间提交自定义事件数据沿着数据包缓冲区的前packet_size
字节。参见BPF_PERF_OUTPUT条目。(This最终调用bpf_perf_event_output()。
现场示例:
search /examples,
search /tools
5. BPF_RINGBUF_OUTPUT
语法:syscall__SYSCALLNAME
创建一个BPF表,用于通过ringbuf环形缓冲区将自定义事件数据推送到用户空间。
BPF_RINGBUF_OUTPUT
has several advantages over BPF_PERF_OUTPUT
, summarized as follows:
- 缓冲区在所有CPU之间共享,这意味着没有每个CPU分配
- 支持BPF程序的两个API
map.ringbuf_output()
的工作原理与map.perf_submit()
类似(包含在ringbuf_output中)map.ringbuf_reserve()
/map.ringbuf_submit()
/map.ringbuf_discard()
将保留缓冲区空间和提交事件的过程分为两个步骤
(包含在ringbuf_reserve、ringbuf_submit、ringbuf_discover中)
- BPF API不需要访问CPU ctx参数
- 共享的环形缓冲区管理器带来上级的性能和用户空间延迟
- 支持在用户空间中使用数据的两种方式
从Linux 5.8开始,这应该是将每个事件数据推送到用户空间的首选方法。
两个API的示例:
struct data_t {
u32 pid;
u64 ts;
char comm[TASK_COMM_LEN];
};
// Creates a ringbuf called events with 8 pages of space, shared across all CPUs
BPF_RINGBUF_OUTPUT(events, 8);
int first_api_example(struct pt_regs *ctx) {
struct data_t data = {};
data.pid = bpf_get_current_pid_tgid();
data.ts = bpf_ktime_get_ns();
bpf_get_current_comm(&data.comm, sizeof(data.comm));
events.ringbuf_output(&data, sizeof(data), 0 /* flags */);
return 0;
}
int second_api_example(struct pt_regs *ctx) {
struct data_t *data = events.ringbuf_reserve(sizeof(struct data_t));
if (!data) { // Failed to reserve space
return 1;
}
data->pid = bpf_get_current_pid_tgid();
data->ts = bpf_ktime_get_ns();
bpf_get_current_comm(&data->comm, sizeof(data->comm));
events.ringbuf_submit(data, 0 /* flags */);
return 0;
}
输出表名为events
。数据通过events.ringbuf_reserve()
分配,并通过events.ringbuf_submit()
推送。
现场示例:
search /examples,
6. ringbuf_output()
语法:syscall__SYSCALLNAME
返回:成功时返回0
标志:
BPF_RB_NO_WAKEUP
:不发送新数据可用性通知BPF_RB_FORCE_WAKEUP
:无条件发送新数据可用性通知
BPF_RINGBUF_OUTPUT表的一种方法,用于将自定义事件数据提交到用户空间。这个方法的工作原理类似于perf_submit()
,
尽管它不需要CTX变元。
现场示例:
search /examples,
7. ringbuf_reserve()
语法:syscall__SYSCALLNAME
返回:成功时指向数据结构的指针,失败时为NULL
BPF_RINGBUF_OUTPUT表的一种方法,用于在环形缓冲区中预留空间,同时
分配用于输出的数据结构。必须与ringbuf_submit
或ringbuf_discard
之一配合使用。
现场示例:
search /examples,
8. ringbuf_submit()
语法:syscall__SYSCALLNAME
回报:没事,总是成功
标志:
BPF_RB_NO_WAKEUP
:不发送新数据可用性通知BPF_RB_FORCE_WAKEUP
:无条件发送新数据可用性通知
BPF_RINGBUF_OUTPUT表的一种方法,用于将自定义事件数据提交到用户空间。必须在调用
#1为数据保留空间。
现场示例:
search /examples,
9. ringbuf_discard()
语法:syscall__SYSCALLNAME
回报:没事,总是成功
标志:
BPF_RB_NO_WAKEUP
:不发送新数据可用性通知BPF_RB_FORCE_WAKEUP
:无条件发送新数据可用性通知
BPF_RINGBUF_OUTPUT表的方法,用于丢弃自定义事件数据;用户空间
忽略与被丢弃的事件相关联的数据。必须在调用
#1为数据保留空间。
现场示例:
search /examples,
10. ringbuf_query()
语法:syscall__SYSCALLNAME
返回:请求的值,或者0,如果标志不被识别
标志:
BPF_RB_AVAIL_DATA
:尚未使用的数据量BPF_RB_RING_SIZE
:环形缓冲区的大小BPF_RB_CONS_POS
:消费者立场BPF_RB_PROD_POS
:制片人职位
BPF_RINGBUF_OUTPUT表的一个方法,用于获取环形缓冲区的各种属性。返回的值是环形缓冲区状态的瞬时快照,并且在帮助器返回时可能关闭,因此这应该仅用于调试/报告原因或用于实现各种启发式方法,这些方法考虑了其中一些特征的高度可变性质。
现场示例:
search /examples,
Maps
映射是BPF数据存储,并且是包括表、散列和直方图在内的更高级别对象类型的基础。
1. BPF_TABLE
语法:syscall__SYSCALLNAME
创建名为_name
的贴图。大多数情况下,这将通过更高级别的宏来使用,如BPF_HASH,BPF_ARRAY,BPF_HISTOGRAM等。
BPF_F_TABLE
是在最后一个参数中带有标志的变体。BPF_TABLE(...)
实际上是BPF_F_TABLE(..., 0 /* flag */)
的包装器。
方法(稍后介绍):map.lookup()、map.lookup_or_try_init()、map.delete()、map.update()、map.insert()、map.increment()。
现场示例:
search /examples,
search /tools
Pinned Maps
语法:syscall__SYSCALLNAME
如果映射不存在,则创建一个新映射,并将其作为FILE固定到bpffs,否则使用固定到bpffs的映射。类型信息不强制执行,实际的贴图类型取决于固定到该位置的贴图。
举例来说:
BPF_TABLE_PINNED("hash", u64, u64, ids, 1024, "/sys/fs/bpf/ids");
2. BPF_HASH
语法:syscall__SYSCALLNAME
创建一个名为name
的散列映射(关联数组),并带有可选参数。
默认值:BPF_HASH(name, key_type=u64, leaf_type=u64, size=10240)
举例来说:
BPF_HASH(start, struct request *);
这将创建一个名为start
的散列,其中键是struct request *
,值默认为u64。disksnoop.py示例使用此散列来保存每个I/O请求的时间戳,其中键是指向struct请求的指针,值是时间戳。
这是一个用于BPF_TABLE("hash", ...)
的包装器宏。
方法(稍后介绍):map.lookup()、map.lookup_or_try_init()、map.delete()、map.update()、map.insert()、map.increment()。
现场示例:
search /examples,
search /tools
3. BPF_ARRAY
这将创建一个名为counts
where的数组,其中包含32个桶和64位整数值。此数组由funccount.py示例用于保存每个函数的调用计数。
这是一个用于BPF_TABLE("hash", ...)
的包装器宏。
方法(稍后介绍):map.lookup()、map.update()、map.increment()。请注意,所有数组元素都预先分配了零值,因此无法删除。
现场示例:
BPF_ARRAY(counts, u64, 32);
这将创建一个名为counts
where的数组,其中包含32个桶和64位整数值。此数组由funccount.py示例用于保存每个函数的调用计数。
这是一个用于BPF_TABLE("hash", ...)
的包装器宏。
方法(稍后介绍):map.lookup()、map.update()、map.increment()。请注意,所有数组元素都预先分配了零值,因此无法删除。
现场示例:
search /examples,
search /tools
4. BPF_HISTOGRAM
语法:syscall__SYSCALLNAME
使用可选参数创建名为name
的直方图。
默认值:BPF_HASH(name, key_type=u64, leaf_type=u64, size=10240)
举例来说:
BPF_HISTOGRAM(dist);
这将创建一个名为dist
的直方图,它默认为64个由int类型的键索引的桶。
这是一个用于BPF_TABLE("hash", ...)
的包装器宏。
方法(稍后介绍):map.increment().
现场示例:
search /examples,
search /tools
5. BPF_STACK_TRACE
语法:syscall__SYSCALLNAME
创建名为name
的堆栈跟踪映射,并提供最大条目计数。这些映射用于存储堆栈跟踪。
举例来说:
BPF_STACK_TRACE(stack_traces, 1024);
这将创建名为stack_traces
的堆栈跟踪映射,最大堆栈跟踪条目数为1024。
这是一个用于BPF_TABLE("hash", ...)
的包装器宏。
方法(稍后介绍):map.get_stackid().
现场示例:
search /examples,
search /tools
6. BPF_PERF_ARRAY
语法:syscall__SYSCALLNAME
创建名为name
的perf数组,并提供最大条目计数,该计数必须等于系统cpu的数量。这些映射用于获取硬件性能计数器。
举例来说:
text="""
BPF_PERF_ARRAY(cpu_cycles, NUM_CPUS);
"""
b = bcc.BPF(text=text, cflags=["-DNUM_CPUS=%d" % multiprocessing.cpu_count()])
b["cpu_cycles"].open_perf_event(b["cpu_cycles"].HW_CPU_CYCLES)
这将创建一个名为cpu_cycles
的perf数组,其条目数等于cpu/核心数。这个数组被配置为,稍后调用map.perf_read()将返回一个硬件计算的计数器,表示从过去某个时间点经过的周期数。一次只能为每个表配置一种类型的硬件计数器。
方法(稍后介绍):map.perf_read().
现场示例:
search /tests
7. BPF_PERCPU_HASH
语法:syscall__SYSCALLNAME
创建名为name
的NUM_CPU整数索引哈希映射(关联数组),并带有可选参数。每个CPU都将有此阵列的单独副本。副本不会以任何方式保持同步。
请注意,由于内核中定义的限制(在linux/mm/percpu. c中),leaf_type
的大小不能超过32 KB。
In other words, BPF_PERCPU_HASH
elements cannot be larger than 32KB in size.
默认值:BPF_HASH(name, key_type=u64, leaf_type=u64, size=10240)
举例来说:
BPF_PERCPU_HASH(start, struct request *);
这将创建名为start
的NUM_CPU散列,其中键是struct request *
,值默认为u64。
这是一个用于BPF_TABLE("hash", ...)
的包装器宏。
方法(稍后介绍):map.lookup()、map.lookup_or_try_init()、map.delete()、map.update()、map.insert()、map.increment()。
现场示例:
search /examples,
search /tools
8. BPF_PERCPU_ARRAY
语法:syscall__SYSCALLNAME
创建NUM_CPU整数索引数组,该数组已针对最快的查找和更新进行优化,名为name
,具有可选参数。每个CPU都将有此阵列的单独副本。副本不会以任何方式保持同步。
请注意,由于内核中定义的限制(在linux/mm/percpu. c中),leaf_type
的大小不能超过32 KB。
In other words, BPF_PERCPU_HASH
elements cannot be larger than 32KB in size.
默认值:BPF_HASH(name, key_type=u64, leaf_type=u64, size=10240)
举例来说:
BPF_PERCPU_ARRAY(counts, u64, 32);
这将创建名为counts
where的NUM_CPU数组,其中包含32个桶和64位整数值。
这是一个用于BPF_TABLE("hash", ...)
的包装器宏。
方法(稍后介绍):map.lookup()、map.update()、map.increment()。请注意,所有数组元素都预先分配了零值,因此无法删除。
现场示例:
search /examples,
search /tools
9. BPF_LPM_TRIE
语法:syscall__SYSCALLNAME
创建一个名为name
的最长前缀匹配trie映射,并带有可选参数。
默认值:BPF_HASH(name, key_type=u64, leaf_type=u64, size=10240)
举例来说:
BPF_LPM_TRIE(trie, struct key_v6);
这将创建一个名为trie
的LPM trie映射,其中键是struct key_v6
,值默认为u64。
这是一个包装器宏BPF_F_TABLE("lpm_trie", ..., BPF_F_NO_PREALLOC)
。
方法(稍后介绍):map.lookup()、map.lookup_or_try_init()、map.delete()、map.update()、map.insert()、map.increment()。
现场示例:
search /examples,
search /tools
10. BPF_PROG_ARRAY
语法:syscall__SYSCALLNAME
这将创建一个名为name
的程序数组,其中有size
个条目。数组的每个条目要么是bpf程序的文件描述符,要么是NULL
。该数组充当跳转表,以便bpf程序可以“尾调用”其他bpf程序。
这是一个用于BPF_TABLE("hash", ...)
的包装器宏。
方法(稍后介绍):map.call()。
现场示例:
search /examples,
search /tests,
assign fd
11. BPF_DEVMAP
语法:syscall__SYSCALLNAME
这将创建一个名为name
的设备映射,其中包含size
个条目。映射的每个条目是到网络接口的ifindex
。此贴图仅在XDP中使用。
举例来说:
BPF_DEVMAP(devmap, 10);
方法(稍后介绍):map.redirect_map().
现场示例:
search /examples,
12. BPF_CPUMAP
语法:syscall__SYSCALLNAME
这将创建一个名为name
的cpu映射,其中包含size
个条目。映射的索引表示CPU id,并且每个条目是为CPU分配的环形缓冲区的大小。此贴图仅在XDP中使用。
举例来说:
BPF_CPUMAP(cpumap, 16);
方法(稍后介绍):map.redirect_map().
现场示例:
search /examples,
13. BPF_XSKMAP
语法:syscall__SYSCALLNAME
这将创建一个名为name
的xsk映射,其中包含size
个条目,并将其作为FILE固定到bpffs。每个条目表示一个NIC的队列ID。此映射仅在XDP中用于将数据包重定向到AF_XDP套接字。如果AF_XDP套接字绑定到与当前数据包的队列ID不同的队列,则数据包将被丢弃。对于内核v5.3和更高版本,可以使用lookup
方法来检查AF_XDP套接字是否可用于当前数据包的队列ID。更多详情请访问AF_XDP。
举例来说:
BPF_XSKMAP(xsks_map, 8);
方法(稍后介绍):map.redirect_map(). map.lookup()
现场示例:
search /examples,
14. BPF_ARRAY_OF_MAPS
语法:syscall__SYSCALLNAME
这将创建一个数组映射,其映射类型为映射中映射(BPF_MAP_TYPE_HASH_OF_MAPS),名为name
,具有size
个条目。内部映射Meta数据由映射inner_map_name
提供,并且可以是除了BPF_MAP_TYPE_PROG_ARRAY
、BPF_MAP_TYPE_CGROUP_STORAGE
和BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE
之外的大多数阵列或散列映射。
举例来说:
BPF_TABLE("hash", int, int, ex1, 1024);
BPF_TABLE("hash", int, int, ex2, 1024);
BPF_ARRAY_OF_MAPS(maps_array, "ex1", 10);
15. BPF_HASH_OF_MAPS
语法:syscall__SYSCALLNAME
这将创建一个散列映射,其映射类型为(BPF_MAP_TYPE_HASH_OF_MAPS)映射,名为name
,具有size
个条目。内部映射Meta数据由映射inner_map_name
提供,并且可以是除了BPF_MAP_TYPE_PROG_ARRAY
、BPF_MAP_TYPE_CGROUP_STORAGE
和BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE
之外的大多数阵列或散列映射。
举例来说:
BPF_ARRAY(ex1, int, 1024);
BPF_ARRAY(ex2, int, 1024);
BPF_HASH_OF_MAPS(maps_hash, struct custom_key, "ex1", 10);
16. BPF_STACK
语法:syscall__SYSCALLNAME
创建一个名为name
的堆栈,值类型为leaf_type
,最大条目数为max_entries
。
Stack and Queue maps are only available from Linux 4.20+.
举例来说:
BPF_STACK(stack, struct event, 10240);
这将创建一个名为stack
的堆栈,其中值类型为struct event
,最多可容纳10240个条目。
方法(稍后介绍):map.push()、map.pop()、map.peek()。
现场示例:
search /tests,
17. BPF_QUEUE
语法:syscall__SYSCALLNAME
创建一个名为name
的队列,值类型为leaf_type
,最大条目数为max_entries
。
Stack and Queue maps are only available from Linux 4.20+.
举例来说:
BPF_QUEUE(queue, struct event, 10240);
这将创建一个名为queue
的队列,其中值类型为struct event
,最多可容纳10240个条目。
方法(稍后介绍):map.push()、map.pop()、map.peek()。
现场示例:
search /tests,
18. BPF_SOCKHASH
语法:syscall__SYSCALLNAME
创建一个名为name
的哈希,并带有可选参数。sockhash仅在Linux 4.18+中可用。
默认值:BPF_SOCKHASH(name, key_type=u32, max_entries=10240)
举例来说:
struct sock_key {
u32 remote_ip4;
u32 local_ip4;
u32 remote_port;
u32 local_port;
};
BPF_HASH(skh, struct sock_key, 65535);
这将创建一个名为skh
的散列,其中键是struct sock_key
。
sockhash是一种BPF映射类型,它保存对sock结构的引用。然后使用新的sk/msg redirect bpf helper BPF程序可以使用映射在套接字之间重定向skbs/msgs(map.sk_redirect_hash()/map.msg_redirect_hash()
)。
BPF_SOCKHASH
和BPF_SOCKMAP
之间的区别在于BPF_SOCKMAP
是基于数组实现的,并且强制密钥为四个字节。而BPF_SOCKHASH
基于哈希表实现,可以自由指定键的类型。
方法(稍后介绍):map.sock_hash_update(),map.msg_redirect_hash(),map.sk_redirect_hash()。
19. map.lookup()
语法:syscall__SYSCALLNAME
在映射中查找键,如果存在,则返回指向其值的指针,否则返回NULL。我们把键作为一个地址传递给一个指针。
现场示例:
search /examples,
search /tools
20. map.lookup_or_try_init()
语法:syscall__SYSCALLNAME
在映射中查找键,如果存在,则返回指向其值的指针,否则将键的值初始化为第二个参数。这通常用于将值初始化为零。如果无法插入密钥(例如映射已满)则返回NULL。
现场示例:
search /examples,
search /tools
注意:旧的map.lookup_or_init()可能会导致函数返回,因此建议使用lookup_or_try_init(),因为它没有这种副作用。
21. map.delete()
语法:syscall__SYSCALLNAME
从哈希中删除键。
现场示例:
search /examples,
search /tools
22. map.update()
语法:syscall__SYSCALLNAME
将第二个参数中的值与键关联,覆盖之前的任何值。
现场示例:
search /examples,
search /tools
23. map.insert()
语法:syscall__SYSCALLNAME
将第二个参数中的值关联到键,仅当没有以前的值时。
现场示例:
search /examples,
search /tools
24. map.increment()
语法:syscall__SYSCALLNAME
将键的值按increment_amount
递增,默认值为1。用于直方图。
#1不是原子。在并发的情况下。如果您想要更准确的结果,请使用#2而不是#3。map.increment()
和map.atomic_increment()
的开销类似。
注意了。当使用map.atomic_increment()
对BPF_MAP_TYPE_HASH
类型的BPF映射进行操作时,当指定的键不存在时,map.atomic_increment()
不能保证操作的原子性。
现场示例:
search /examples,
search /tools
25. map.get_stackid()
语法:syscall__SYSCALLNAME
这将遍历通过ctx
中的结构pt_regs找到的堆栈,将其保存在堆栈跟踪映射中,并返回堆栈跟踪的唯一ID。
现场示例:
search /examples,
search /tools
26. map.perf_read()
语法:syscall__SYSCALLNAME
这将返回5中配置的硬件性能计数器。BPF_性能_阵列
现场示例:
search /tests
27. map.call()
语法:syscall__SYSCALLNAME
这将调用bpf_tail_call()
来尾调用BPF_PROG_ARRAY中的index
条目所指向的bpf程序。尾呼叫不同于正常呼叫。它在跳转到另一个BPF程序后重新使用当前堆栈帧,并且永远不会返回。如果index
entry为空,它不会跳转到任何地方,程序继续正常执行。
举例来说:
BPF_PROG_ARRAY(prog_array, 10);
int tail_call(void *ctx) {
bpf_trace_printk("Tail-call\n");
return 0;
}
int do_tail_call(void *ctx) {
bpf_trace_printk("Original program\n");
prog_array.call(ctx, 2);
return 0;
}
b = BPF(src_file="example.c")
tail_fn = b.load_func("tail_call", BPF.KPROBE)
prog_array = b.get_table("prog_array")
prog_array[c_int(2)] = c_int(tail_fn.fd)
b.attach_kprobe(event="some_kprobe_event", fn_name="do_tail_call")
这将tail_call()
分配给prog_array[2]
。在do_tail_call()
结束时,prog_array.call(ctx, 2)
尾调用tail_call()
并执行它。
注意:为了防止无限循环,尾调用的最大数量为32(MAX_TAIL_CALL_CNT
)。
现场示例:
search /examples,
search /tests
28. map.redirect_map()
语法:syscall__SYSCALLNAME
这将基于index
条目重定向传入数据包。如果映射为BPF_DEVMAP,则数据包将被发送到入口指向的网络接口的传输队列。如果映射为BPF_CPUMAP,则将该数据包发送到index
CPU的环形缓冲区,并由CPU稍后处理。如果映射是BPF_XSKMAP,则数据包将被发送到附加到队列的AF_XDP套接字。
如果数据包成功重定向,函数将返回XDP_REDIRECT。否则,它将返回XDP_ABORTED以丢弃数据包。
举例来说:
BPF_DEVMAP(devmap, 1);
int redirect_example(struct xdp_md *ctx) {
return devmap.redirect_map(0, 0);
}
int xdp_dummy(struct xdp_md *ctx) {
return XDP_PASS;
}
ip = pyroute2.IPRoute()
idx = ip.link_lookup(ifname="eth1")[0]
b = bcc.BPF(src_file="example.c")
devmap = b.get_table("devmap")
devmap[c_uint32(0)] = c_int(idx)
in_fn = b.load_func("redirect_example", BPF.XDP)
out_fn = b.load_func("xdp_dummy", BPF.XDP)
b.attach_xdp("eth0", in_fn, 0)
b.attach_xdp("eth1", out_fn, 0)
Examples in situ:
search /examples,
29. map.push()
语法:syscall__SYSCALLNAME
将元素推入堆栈或队列表。
Passing BPF_EXIST as a flag causes the Queue or Stack to discard the oldest element if it is full.
Returns 0 on success, negative error on failure.
现场示例:
search /tests,
30. map.pop()
语法:syscall__SYSCALLNAME
从Stack或Queue表中弹出元素。*val
#
Unlike peeking, popping removes the element.
Returns 0 on success, negative error on failure.
现场示例:
search /tests,
31. map.peek()
语法:syscall__SYSCALLNAME
查看堆栈或队列表头部的元素。*val
#
Unlike popping, peeking does not remove the element.
Returns 0 on success, negative error on failure.
现场示例:
search /tests,
32. map.sock_hash_update()
语法:syscall__SYSCALLNAME
向引用套接字的sockhash映射添加条目或更新引用套接字的sockhash映射。skops被用作与键相关联的条目的新值。flags是其中之一:
BPF_NOEXIST: The entry for key must not exist in the map.
BPF_EXIST: The entry for key must already exist in the map.
BPF_ANY: No condition on the existence of the entry for key.
如果映射具有eBPF程序(解析器和判决器),则这些程序将被添加的套接字继承。如果套接字已连接到eBPF程序,则会导致错误。
成功返回0,失败返回负错误。
现场示例:
search /tests,
33. map.msg_redirect_hash()
语法:syscall__SYSCALLNAME
此帮助器用于在套接字级别实现策略的程序中。如果消息msg被允许通过(即如果判决eBPF程序返回SK_PASS),则使用散列键将其重定向到map(类型为BPF_MAP_TYPE_SOCKHASH)引用的套接字。入口和出口接口都可以用于重定向。标志中的BPF_F_INGRESS值用于进行区分(如果标志存在,则选择入口路径,否则选择出口路径)。这是目前唯一支持的标志。
成功时返回SK_PASS,错误时返回SK_DROP。
现场示例:
search /tests,
34. map.sk_redirect_hash()
语法:syscall__SYSCALLNAME
此帮助器用于在skb套接字级别实现策略的程序中。如果sk_buff skb被允许通过(即,如果判决eBPF程序返回SK_PASS),则使用散列键将其重定向到map(类型为BPF_MAP_TYPE_SOCKHASH)引用的套接字。入口和出口接口都可以用于重定向。标志中的BPF_F_INGRESS值用于进行区分(如果标志存在,则选择入口路径,否则选择出口路径)。这是目前唯一支持的标志。
成功时返回SK_PASS,错误时返回SK_DROP。
现场示例:
search /tests,
Licensing
根据所使用的BPF帮助程序,需要GPL兼容许可证。
特殊BCC宏BPF_LICENSE
指定BPF程序的许可证。您可以在源代码中将许可证设置为注释,但是内核有一个特殊的接口以编程方式指定它。如果你需要使用纯GPL的帮助器,建议在你的C代码中指定宏,以便内核能够理解它:
// SPDX-License-Identifier: GPL-2.0+
#define BPF_LICENSE GPL
否则,内核可能会拒绝加载程序(请参阅下面的错误描述)。请注意,它支持多个单词,不需要引号:
// SPDX-License-Identifier: GPL-2.0+ OR BSD-2-Clause
#define BPF_LICENSE Dual BSD/GPL
检查BPF助手引用,看看哪些助手是纯GPL的,以及内核认为哪些助手是GPL兼容的。
如果未指定宏,BCC将自动将程序的许可证定义为GPL。
Rewriter
重写器的工作之一是使用内核助手将隐式内存访问转换为显式内存访问。最近的内核引入了一个配置选项ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE,该配置选项将被设置为用户地址空间和内核地址不相交的架构。x86和arm设置了此配置选项,而s390没有。如果未设置ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE,则bpf旧助手bpf_probe_read()
将不可用。一些现有用户可能具有隐式内存访问来访问用户内存,因此使用bpf_probe_read_kernel()
将导致其应用程序失败。因此,对于非s390,重写器将使用bpf_probe_read()
进行这些隐式存储器访问。对于s390,bpf_probe_read_kernel()
被用作默认值,并且用户在访问用户存储器时应该显式地使用bpf_probe_read_user()
。
BPF Errors
请参阅内核源代码中Documentation/networking/filter.txt下的“Understanding eBPFverifier messages”一节。
1. Invalid mem access
当从非GPL BPF程序调用仅GPL帮助程序时,会发生此错误。若要修复此错误,请不要使用专有BPF程序中的仅限GPL的帮助程序,或在GPL兼容许可证下重新许可BPF程序。检查哪些BPF helper是仅限GPL的,以及哪些许可证被认为是GPL兼容的。
从专有程序(bpf_get_stackid()
)调用#define BPF_LICENSE Proprietary
(仅限GPL的BPF帮助程序)的示例:
bpf: Permission denied
0: (bf) r6 = r1
1: (79) r7 = *(u64 *)(r6 +80)
2: (85) call 14
3: (bf) r8 = r0
[...]
23: (69) r1 = *(u16 *)(r7 +16)
R7 invalid mem access 'inv'
Traceback (most recent call last):
File "./tcpaccept", line 179, in <module>
b = BPF(text=bpf_text)
File "/usr/lib/python2.7/dist-packages/bcc/__init__.py", line 172, in __init__
self._trace_autoload()
File "/usr/lib/python2.7/dist-packages/bcc/__init__.py", line 612, in _trace_autoload
fn = self.load_func(func_name, BPF.KPROBE)
File "/usr/lib/python2.7/dist-packages/bcc/__init__.py", line 212, in load_func
raise Exception("Failed to load BPF program %s" % func_name)
Exception: Failed to load BPF program kretprobe__inet_csk_accept
2. Cannot call GPL only function from proprietary program
当从非GPL BPF程序调用仅GPL帮助程序时,会发生此错误。若要修复此错误,请不要使用专有BPF程序中的仅限GPL的帮助程序,或在GPL兼容许可证下重新许可BPF程序。检查哪些BPF helper是仅限GPL的,以及哪些许可证被认为是GPL兼容的。
从专有程序(bpf_get_stackid()
)调用#define BPF_LICENSE Proprietary
(仅限GPL的BPF帮助程序)的示例:
bpf: Failed to load program: Invalid argument
[...]
8: (85) call bpf_get_stackid#27
cannot call GPL only function from proprietary program
Environment Variables
1. Kernel source directory
eBPF程序编译需要内核源代码或带有头的内核头编译。如果您的内核源代码位于BCC找不到,则可以向BCC提供位置的绝对路径BCC_KERNEL_SOURCE
1##
2. Kernel version overriding
默认情况下,BCC将LINUX_VERSION_CODE
存储在生成的eBPF对象中然后在加载eBPF程序时将其沿着到内核。
更新,
例如LTS内核版本。updated such as an LTS kernel release.这不太可能不匹配将导致加载的eBPF程序出现任何问题。
通过设置BCC_LINUX_VERSION_CODE
到正在运行的内核版本,检查用于验证内核版本的验证可以被绕过。这是程序所需要的使用kprobes。
这需要以以下格式进行编码:(VERSION * 65536) + (PATCHLEVEL * 256) + SUBLEVEL
.例如,如果运行的内核是4.9.10
,然后可以设置export BCC_LINUX_VERSION_CODE=264458
来覆盖内核版本检查成功。rnel-versions.md#helpers) are GPL-only, and what licenses are considered GPL-compatible.
从专有程序(bpf_get_stackid()
)调用#define BPF_LICENSE Proprietary
(仅限GPL的BPF帮助程序)的示例:
bpf: Failed to load program: Invalid argument
[...]
8: (85) call bpf_get_stackid#27
cannot call GPL only function from proprietary program
Environment Variables
1. Kernel source directory
eBPF程序编译需要内核源代码或带有头的内核头编译。
如果您的内核源代码位于BCC找不到,则可以向BCC提供位置的绝对路径BCC_KERNEL_SOURCE
1##
2. Kernel version overriding
默认情况下,BCC将LINUX_VERSION_CODE
存储在生成的eBPF对象中然后在加载eBPF程序时将其沿着到内核。
更新,例如LTS内核版本。
updated such as an LTS kernel release.这不太可能不匹配将导致加载的eBPF程序出现任何问题。
通过设置BCC_LINUX_VERSION_CODE
到正在运行的内核版本,检查用于验证内核版本的验证可以被绕过。
这是程序所需要的使用kprobes。
这需要以以下格式进行编码:(VERSION * 65536) + (PATCHLEVEL * 256) + SUBLEVEL
.例如,如果运行的内核是4.9.10
,然后可以设置export BCC_LINUX_VERSION_CODE=264458
来覆盖内核版本检查成功。