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,否则在机器上]

 

posted @ 2022-02-19 13:15  honpey  阅读(438)  评论(0编辑  收藏  举报