eBPF Talk: 使用 drgn 学习 bpf

文摘   2024-07-01 08:10   新加坡  

drgn[1] 是 Linux 系统可编程的调试器,基于 Python 脚本简单、便利地读取内核数据结构和变量。

理所当然地,drgn 可以用来学习 bpf,用来 inspect bpf map、bpf program、bpf link 等对象。

P.S. 以下内容基于 drgn PR[2] 提供的功能。

inspect bpf map

类似 bpftool map list,可以使用 drgn 来查看 bpf map 的信息。

# drgn ./contrib/bpf_inspect.py m
     2: BPF_MAP_TYPE_PROG_ARRAY          hid_jmp_table
  4207: BPF_MAP_TYPE_PROG_ARRAY          jmp_table
  4208: BPF_MAP_TYPE_ARRAY               tailcall.bss
  4247: BPF_MAP_TYPE_PROG_ARRAY          jmp_table
  4248: BPF_MAP_TYPE_ARRAY               tailcall.bss
  5092: BPF_MAP_TYPE_ARRAY               .rodata

# drgn ./contrib/bpf_inspect.py m --show-details
     2: BPF_MAP_TYPE_PROG_ARRAY          hid_jmp_table
    owner:    BPF_PROG_TYPE_TRACING            JITed
  4207: BPF_MAP_TYPE_PROG_ARRAY          jmp_table
    owner:    BPF_PROG_TYPE_SCHED_CLS          JITed
    poke:       2295: BPF_PROG_TYPE_EXT                entry                            tail_call_reachable
  4208: BPF_MAP_TYPE_ARRAY               tailcall.bss
  4247: BPF_MAP_TYPE_PROG_ARRAY          jmp_table
    owner:    BPF_PROG_TYPE_SCHED_CLS          JITed
    poke:       2400: BPF_PROG_TYPE_EXT                entry                            tail_call_reachable
  4248: BPF_MAP_TYPE_ARRAY               tailcall.bss
  5092: BPF_MAP_TYPE_ARRAY               .rodata

如果想要 inspect 具体的 bpf map 呢?

# drgn ./contrib/bpf_inspect.py i
For help, type help(drgn).
>>> import drgn
>>> from drgn import NULL, Object, cast, container_of, execscript, offsetof, reinterpret, sizeof, stack_trace
>>> from drgn.helpers.common import *
>>> from drgn.helpers.linux import *
>>> list_bpf_maps()
     2: BPF_MAP_TYPE_PROG_ARRAY          hid_jmp_table
  4207: BPF_MAP_TYPE_PROG_ARRAY          jmp_table
  4208: BPF_MAP_TYPE_ARRAY               tailcall.bss
  4247: BPF_MAP_TYPE_PROG_ARRAY          jmp_table
  4248: BPF_MAP_TYPE_ARRAY               tailcall.bss
  5092: BPF_MAP_TYPE_ARRAY               .rodata
>>> m = get_bpf_map_by_id(4247)
>>> print(m.map.format_())
*(struct bpf_map *)0xffff990290259400 = {
    .ops = (const struct bpf_map_ops *)prog_array_map_ops+0x0 = 0xffffffff8583cc20,
    .inner_map_meta = (struct bpf_map *)0x0,
    .security = (void *)0x0,
    .map_type = (enum bpf_map_type)BPF_MAP_TYPE_PROG_ARRAY,
    .key_size = (u32)4,
    .value_size = (u32)4,
    .max_entries = (u32)1,
    .map_extra = (u64)0,
    .map_flags = (u32)0,
    .id = (u32)4247,
    .record = (struct btf_record *)0x0,
    .numa_node = (int)-1,
    .btf_key_type_id = (u32)0,
    .btf_value_type_id = (u32)0,
    .btf_vmlinux_value_type_id = (u32)0,
    .btf = (struct btf *)0x0,
    .objcg = (struct obj_cgroup *)0xffff990290f48540,
    .name = (char [16])"jmp_table",
    // ...
}
>>> arr = BpfProgArrayMap(m.map)
>>> print(arr.prog_array.format_())
*(struct bpf_array *)0xffff990290259400 = {
    .map = (struct bpf_map){
        .ops = (const struct bpf_map_ops *)prog_array_map_ops+0x0 = 0xffffffff8583cc20,
        // ...
    },
    .elem_size = (u32)8,
    .index_mask = (u32)0,
    .aux = (struct bpf_array_aux *)0xffff9902885134e0,
    .__empty_value = (struct <anonymous>){},
    .value = (char []){},
    .__empty_ptrs = (struct <anonymous>){},
    .ptrs = (void *[]){},
    .__empty_pptrs = (struct <anonymous>){},
    .pptrs = (void *[]){},
}

inspect bpf program

类似 bpftool prog list,可以使用 drgn 来查看 bpf program 的信息。

# drgn ./contrib/bpf_inspect.py p
     2: BPF_PROG_TYPE_TRACING            hid_tail_call
  2295: BPF_PROG_TYPE_EXT                entry                            tail_call_reachable
  2400: BPF_PROG_TYPE_EXT                entry                            tail_call_reachable
  4235: BPF_PROG_TYPE_SCHED_CLS          entry1

# drgn ./contrib/bpf_inspect.py p --show-details
     2: BPF_PROG_TYPE_TRACING            hid_tail_call
    used map:      2: BPF_MAP_TYPE_PROG_ARRAY          hid_jmp_table
    ksym:     bpf_prog_7cc47bbf07148bfe_hid_tail_call  0x00ffffffffc009e948 105 bytes
  2295: BPF_PROG_TYPE_EXT                entry                            tail_call_reachable
    used map:   4208: BPF_MAP_TYPE_ARRAY               tailcall.bss
    used map:   4207: BPF_MAP_TYPE_PROG_ARRAY          jmp_table
    ksym:     bpf_prog_0c0f4c2413ef19b1_entry          0x00ffffffffc00b4720 74 bytes
    func[ 1]:      0: BPF_PROG_TYPE_EXT                subprog_tail
    funcksym: bpf_prog_3a140cef239a4b4f_subprog_tail   0x00ffffffffc00b4788 76 bytes
  2400: BPF_PROG_TYPE_EXT                entry                            tail_call_reachable
    used map:   4248: BPF_MAP_TYPE_ARRAY               tailcall.bss
    used map:   4247: BPF_MAP_TYPE_PROG_ARRAY          jmp_table
    ksym:     bpf_prog_0c0f4c2413ef19b1_entry          0x00ffffffffc00af864 74 bytes
    func[ 1]:      0: BPF_PROG_TYPE_EXT                subprog_tail
    funcksym: bpf_prog_3a140cef239a4b4f_subprog_tail   0x00ffffffffc00b3fdc 76 bytes
  4235: BPF_PROG_TYPE_SCHED_CLS          entry1
    used map:   5092: BPF_MAP_TYPE_ARRAY               .rodata
    ksym:     bpf_prog_e20a685998fd41c8_entry1         0x00ffffffffc009ece0 26 bytes
    func[ 1]:      0: BPF_PROG_TYPE_SCHED_CLS          subprog1
    funcksym: bpf_prog_9e6e84b2270d0b1f_subprog1       0x00ffffffffc009ed0c 36 bytes
    func[ 2]:      0: BPF_PROG_TYPE_SCHED_CLS          subprog2
    funcksym: bpf_prog_9e6e84b2270d0b1f_subprog2       0x00ffffffffc009ed48 36 bytes

如果想要 inspect 具体的 bpf program 呢?

# drgn ./contrib/bpf_inspect.py i
For help, type help(drgn).
>>> import drgn
>>> from drgn import NULL, Object, cast, container_of, execscript, offsetof, reinterpret, sizeof, stack_trace
>>> from drgn.helpers.common import *
>>> from drgn.helpers.linux import *
>>> list_bpf_progs()
     2: BPF_PROG_TYPE_TRACING            hid_tail_call
  2295: BPF_PROG_TYPE_EXT                entry                            tail_call_reachable
  2400: BPF_PROG_TYPE_EXT                entry                            tail_call_reachable
  4235: BPF_PROG_TYPE_SCHED_CLS          entry1
>>> p = get_bpf_prog_by_id(2400)
>>> print(p.prog.format_())
*(struct bpf_prog *)0xffffb2e08075a000 = {
    .pages = (u16)1,
    .jited = (u16)1,
    .jit_requested = (u16)1,
    .gpl_compatible = (u16)1,
    .cb_access = (u16)1,
    .dst_needed = (u16)0,
    .blinding_requested = (u16)0,
    .blinded = (u16)0,
    .is_func = (u16)0,
    .kprobe_override = (u16)0,
    .has_callchain_buf = (u16)0,
    .enforce_expected_attach_type = (u16)0,
    .call_get_stack = (u16)0,
    .call_get_func_ip = (u16)0,
    .tstamp_type_access = (u16)0,
    .type = (enum bpf_prog_type)BPF_PROG_TYPE_EXT,
    .expected_attach_type = (enum bpf_attach_type)BPF_CGROUP_INET_INGRESS,
    .len = (u32)20,
    .jited_len = (u32)74,
    .tag = (u8 [8]){ 121576361923925177 },
    .stats = (struct bpf_prog_stats *)0x4a590,
    .active = (int *)0x4a24c,
    .bpf_func = (unsigned int (*)(const void *, const struct bpf_insn *))0xffffffffc00af864,
    .aux = (struct bpf_prog_aux *)0xffff9902bbdbb800,
    .orig_prog = (struct sock_fprog_kern *)0x0,
    .__empty_insns = (struct <anonymous>){},
    .insns = (struct sock_filter []){},
    .__empty_insnsi = (struct <anonymous>){},
    .insnsi = (struct bpf_insn []){},
}
>>> print(p.prog.aux.format_())
*(struct bpf_prog_aux *)0xffff9902bbdbb800 = {
    .refcnt = (atomic64_t){
        .counter = (s64)1,
    },
    .used_map_cnt = (u32)2,
    // ...
    .stack_depth = (u32)0,
    .id = (u32)2400,
    // ...
    .name = (char [16])"entry",
    // ...
}

inspect bpf link

类似 bpftool link list,可以使用 drgn 来查看 bpf link 的信息。

# drgn ./contrib/bpf_inspect.py l --show-details
     1: BPF_LINK_TYPE_TRACING
    prog:        2: BPF_PROG_TYPE_TRACING            hid_tail_call
    attach:   __hid_bpf_tail_call

如果想要 inspect 具体的 bpf link 呢?

# drgn ./contrib/bpf_inspect.py i
For help, type help(drgn).
>>> import drgn
>>> from drgn import NULL, Object, cast, container_of, execscript, offsetof, reinterpret, sizeof, stack_trace
>>> from drgn.helpers.common import *
>>> from drgn.helpers.linux import *
>>> list_bpf_links()
     1: BPF_LINK_TYPE_TRACING
    prog:        2: BPF_PROG_TYPE_TRACING            hid_tail_call
    attach:   __hid_bpf_tail_call
>>> l = get_bpf_link_by_id(1)
>>> print(l.link.format_())
*(struct bpf_link *)0xffff990282c31200 = {
    .refcnt = (atomic64_t){
        .counter = (s64)1,
    },
    .id = (u32)1,
    .type = (enum bpf_link_type)BPF_LINK_TYPE_TRACING,
    .ops = (const struct bpf_link_ops *)bpf_tracing_link_lops+0x0 = 0xffffffff85839000,
    .prog = (struct bpf_prog *)0xffffb2e080075000,
    // ...
}
>>> t = BpfTracingLink(l.link)
>>> print(t.tracing.format_())
*(struct bpf_tracing_link *)0xffff990282c31200 = {
    .link = (struct bpf_tramp_link){
        .link = (struct bpf_link){
            .refcnt = (atomic64_t){
                .counter = (s64)1,
            },
            .id = (u32)1,
            .type = (enum bpf_link_type)BPF_LINK_TYPE_TRACING,
            .ops = (const struct bpf_link_ops *)bpf_tracing_link_lops+0x0 = 0xffffffff85839000,
            .prog = (struct bpf_prog *)0xffffb2e080075000,
            // ...
        },
        .tramp_hlist = (struct hlist_node){
            .next = (struct hlist_node *)0x0,
            .pprev = (struct hlist_node **)0xffff990282c30ed0,
        },
        .cookie = (u64)0,
    },
    .attach_type = (enum bpf_attach_type)BPF_MODIFY_RETURN,
    .trampoline = (struct bpf_trampoline *)0xffff990282c30e40,
    .tgt_prog = (struct bpf_prog *)0x0,
}

总结

drgn 是一个强大的调试工具,可以用来 inspect bpf map、bpf program、bpf link 等对象。

参考资料
[1]

drgn: https://github.com/osandov/drgn

[2]

drgn PR: https://github.com/osandov/drgn/pull/409

eBPF Talk
专注于 eBPF 技术,以及 Linux 网络上的 eBPF 技术应用