【问题标题】:ebpf: how to use BPF_FUNC_trace_printk in eBPF assembly programebpf:如何在 eBPF 汇编程序中使用 BPF_FUNC_trace_printk
【发布时间】:2021-05-13 23:23:26
【问题描述】:

我有一个小的 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 ebpf


    【解决方案1】:

    阅读友好手册:)

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

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

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

    clang 是做什么的?

    我了解您将 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[] = "%d\n";
    
        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
    

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-06-10
      • 1970-01-01
      • 1970-01-01
      • 2020-01-02
      • 2020-04-24
      • 2020-04-24
      • 2022-11-13
      相关资源
      最近更新 更多