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

  • 相关阅读:
    Entity Framework 学习高级篇1—改善EF代码的方法(上)
    Entity Framework 学习高级篇2—改善EF代码的方法(下)
    Entity Framework 学习中级篇5—使EF支持Oracle9i
    Window API 之 PostMessage (消息投递)
    从image/xpng谈ContentType(s)
    SQL中使用 N 前缀的原因
    Delphi初浅入门笔记之一 :ObjectPascal基础
    写入DLL文件
    SQL Server 2005和SQL Server 2000中bit类型的区别
    HTML <a> 标签的 target 属性说明
  • 原文地址:https://www.cnblogs.com/honpey/p/15912357.html
Copyright © 2020-2023  润新知