ebpf:怎么去搞第一个比较完整的ebpf的程序
普通的ebpf程序是比较简单的,包括当前cilium/bpf中其实都提供了比较完整的案例,对于比较复杂的程序,即比如这个trace点是放在了kprobe上面,对于这种的bpf程序的入口函数其实是变成了bt_regs,这种的话,首先你是需要去获得寄存器的信息的,第二个呢,从寄存器中找到相应的函数的信息之后,你需要从指针去解析出这个地方对应的结构体的的(ebpf在这里是如何保证这个地址的正确性的),ebpf是如何保证这个结构体的解析是正确的(所有语言的类型转换应该都面临着相同的问题),解析出对应的结构体之后,就能够去使用结构体中的数据引用了,从而拿到数据,所以这里最关键的问题:
1. 在编译ebpf程序的时候,是应该如何去引用内核的头;
2. 如何从结构体中拿到,对应的类型的转换;
都是可以侵
以内核代码samples/bpf中的tracex2_kern.c中的代码为例来看下:
下面这个代码还是还是比较简单的,直接从寄存器中拿到第3个参数,这第三个参数表示的是,写入的文件的长度
然后是根据pid和uid去获得进程的名字,去获得进程的名字,其实最主要的是这些bpf api函数的使用,其实这里可以做更多的事情
int bpf_prog3(struct pt_regs *ctx) { long write_size = PT_REGS_PARM3(ctx); long init_val = 1; long *value; struct hist_key key; key.index = log2l(write_size); key.pid_tgid = bpf_get_current_pid_tgid(); key.uid_gid = bpf_get_current_uid_gid(); bpf_get_current_comm(&key.comm, sizeof(key.comm)); value = bpf_map_lookup_elem(&my_hist_map, &key); if (value) __sync_fetch_and_add(value, 1); else bpf_map_update_elem(&my_hist_map, &key, &init_val, BPF_ANY); return 0; }
内核代码中samples/tracex1_kern.c中有一个稍微复杂一点的bpf代码
下面这个代码是是在函数__netif_receive_skb_core函数中,加了一个kprobe点,这个函数:
static int __netif_receive_skb_core(struct sk_buff *skb, bool pfmemalloc,
struct packet_type **ppt_prev)
这个函数是根据的第一个参数是sk_buff,这里涉及到x86的调用规约。这里是读出来了集群器之后,然后做了一层的转义:
skb = (struct sk_buff *) PT_REGS_PARM1(ctx)
这部分,然后再得到结构体中的字段 skb->dev 和 skb->len,然后再去析取里面的东西,这里也是有个很重要的考虑就是bpf_probe_read函数是如何检查访问越界的,按理说的话,c语言其实是没有办法去,这个后面去做一下调研.
SEC("kprobe/__netif_receive_skb_core") int bpf_prog1(struct pt_regs *ctx) { /* attaches to kprobe netif_receive_skb, * looks for packets on loobpack device and prints them */ char devname[IFNAMSIZ]; struct net_device *dev; struct sk_buff *skb; int len; /* non-portable! works for the given kernel only */ skb = (struct sk_buff *) PT_REGS_PARM1(ctx); dev = _(skb->dev); len = _(skb->len); bpf_probe_read(devname, sizeof(devname), dev->name);
【bpf_probe_read函数这里是如何保证访问devname字段是不会越界的】 if (devname[0] == 'l' && devname[1] == 'o') { char fmt[] = "skb %p len %d\n"; /* using bpf_trace_printk() for DEBUG ONLY */ bpf_trace_printk(fmt, sizeof(fmt), skb, len); } return 0; }
[安装相关的源码的header文件:make headers_install,否则在机器上]