EBPF:如何在eBPF组装程序中使用BPF_FUNC_trace_printk



我有一个小型socket filter类型的eBPF程序,我正在尝试打印从上下文__sk_buff读取的协议值:

struct bpf_insn prog[] = {
BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_6, offsetof(struct __sk_buff, protocol)),
BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_0, -4),
BPF_MOV64_REG(BPF_REG_1, BPF_REG_10),
BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -4),
BPF_MOV64_IMM(BPF_REG_2, 4),
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_trace_printk),
BPF_MOV64_IMM(BPF_REG_0, 0),
BPF_EXIT_INSN(),
};

我创建一个原始socket并将其绑定到lo接口,然后setsockopt(fd, SOL_SOCKET, SO_ATTACH_BPF, ...).它的编译和加载没有问题,但是每当我ping 127.0.0.1时,我都不会在trace_pipe中看到痕迹。

因此,为了确保它实际上BPF_FUNC_trace_printk可以工作,我对其进行了更改,以便它在堆栈上打印一个静态字符串,并且它确实会在到达环回的每个数据包上打印。

我做错了什么?

阅读友好的手册:)

我不相信您正确调用了bpf_trace_printk()助手(顺便说一下,BPF_FUNC_trace_prink只是一个整数)。它的签名在内核 UAPI 标头 bpf.h 或bpf-helpers手册页中注释如下:

long bpf_trace_printk(const char *fmt, u32 fmt_size, ...);

这意味着第一个参数必须是常量、以 null 结尾的格式字符串,而不是像您那样的整数。

叮当是做什么的?

我知道您正在将 eBPF 程序附加到套接字,并且无法从 C 编译整个程序。但是,为什么不将该特定部分编译为通用网络eBPF程序,以查看字节码应该是什么样子呢?让我们编写 C 代码:

#include <linux/bpf.h>
static long (*bpf_trace_printk)(const char *fmt, __u32 fmt_size, ...) = (void *) BPF_FUNC_trace_printk;
int printk_proto(struct __sk_buff *skb) {
char fmt[] = "%dn";
bpf_trace_printk(fmt, sizeof(fmt), skb->protocol);
return 0;
}

编译为目标文件。作为记录,除非我们在加载时提供有效的许可证字符串(因为bpf_trace_prink()需要GPL兼容的程序)和兼容的程序类型,否则不会加载。但在我们的情况下没关系,我们只想查看生成的指令。

$ clang -O2 -g -emit-llvm -c prink_protocol.c  -o - | 
llc -march=bpf -mcpu=probe -filetype=obj -o prink_protocol.o 

转储字节码:

$ llvm-objdump -d prink_protocol.o 
prink_protocol.o:       file format elf64-bpf

Disassembly of section .text:
0000000000000000 <printk_proto>:
0:       b4 02 00 00 25 64 0a 00 w2 = 680997
1:       63 2a fc ff 00 00 00 00 *(u32 *)(r10 - 4) = r2
2:       61 13 10 00 00 00 00 00 r3 = *(u32 *)(r1 + 16)
3:       bf a1 00 00 00 00 00 00 r1 = r10
4:       07 01 00 00 fc ff ff ff r1 += -4
5:       b4 02 00 00 04 00 00 00 w2 = 4
6:       85 00 00 00 06 00 00 00 call 6
7:       b4 00 00 00 00 00 00 00 w0 = 0
8:       95 00 00 00 00 00 00 00 exit

我们可以看到,在前两条指令中,程序将格式字符串(以小端序为单位)写入堆栈:6809970x000a6425nd%r2仍包含格式字符串的长度。协议值存储在r3,调用bpf_trace_prink()的第三个参数。

相关内容

  • 没有找到相关文章

最新更新