[英]ebpf: how to use BPF_FUNC_trace_printk in eBPF assembly program
我有一個小型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
確實可以工作,我對其進行了更改,以便它在堆棧上打印 static 字符串,並在每個到達環回的數據包上打印。
我究竟做錯了什么?
我不相信您正確調用了bpf_trace_printk()
助手(順便說一句, BPF_FUNC_trace_prink
只是一個 integer)。 它的簽名在 kernel UAPI header bpf.h 或bpf-helpers
手冊頁中注釋如下:
long bpf_trace_printk(const char *fmt, u32 fmt_size, ...);
這意味着第一個參數必須是一個常量、以 null 結尾的格式字符串,而不是像你這樣的 integer。
我了解您將 eBPF 程序附加到 sockets 並且無法從 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[] = "%d\n";
bpf_trace_printk(fmt, sizeof(fmt), skb->protocol);
return 0;
}
編譯為 object 文件。 作為記錄,這不會加載,除非我們在加載時同時提供有效的許可證字符串(因為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
我們可以看到,在前兩條指令中,程序將格式字符串(小端序)寫入堆棧: 680997
is 0x000a6425
, \0\nd%
。 r2
仍然包含格式字符串的長度。 協議值存儲在r3
中,它是調用bpf_trace_prink()
的第三個參數。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.