• eBPF Talk:变量声明的位置


    据了解(未查证),从 clang12 开始,eBPF 代码中的变量声明不再要求写在函数体的最前方,而是可以按需声明并初始化。

    写法一:一次性声明全部的变量

    static __always_inline void
    set_output_headers(struct __sk_buff *skb, struct event *ev)
    {
        struct ethhdr *eth;
        struct vlan_hdr *vh;
        struct iphdr *iph;
        struct udphdr *udph;
        struct tcphdr *tcph;
        struct icmphdr *icmph;
        int l3_off = 0, l4_off, var_off = 0, cpy_off = 0;
    
        // ...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    如上代码片段,将所有需要处理的协议头对应的 struct 变量放在一起声明。如果后续需要处理其它协议,则需要在此处添加对应的 struct 变量的声明。

    先声明后使用

    我不喜欢这种写法,不过这种写法能在编码的时候有一个好处:使用 typeof(hdr) 获取类型、使用 sizeof(*hdr) 获取协议头大小,而无需啰嗦地再写一遍 struct xxxhdr

    写法二:按需声明并初始化

    static __always_inline __u16
    calc_l3_off(struct sk_buff *skb)
    {
        __u16 skb_network_header = BPF_CORE_READ(skb, network_header);
        if (skb_network_header != (__u16)~0ul)
            return skb_network_header;
    
        // calculate l3_off from eth layer
        __u16 skb_mac_header = BPF_CORE_READ(skb, mac_header);
        __u16 l2_off = 0;
        if (skb_mac_header != (__u16)~0ul)
            l2_off = skb_mac_header;
    
        __u16 l3_off = 0;
    
        unsigned char *skb_head = BPF_CORE_READ(skb, head);
        struct ethhdr *eth = (struct ethhdr *)(skb_head + l2_off);
        l3_off += sizeof(*eth);
    
        __be16 l3_proto = BPF_CORE_READ(eth, h_proto);
        if (is_vlan_proto(l3_proto)) {
            struct vlan_hdr *vh = (struct vlan_hdr *)(eth + 1);
            l3_off += sizeof(*vh);
    
            l3_proto = BPF_CORE_READ(vh, h_vlan_encapsulated_proto);
        }
    
        if (!is_ipv4_proto(l3_proto))
            return 0;
    
        return l3_off;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    如上代码片段,将需要用到的变量在需要的地方声明并初始化即可。

    对比写法一,此种写法对变量的使用更加灵活,对程序员更加友好。

    写法三:混合写法一和写法二

    static volatile const struct config __cfg = {};
    
    static __always_inline bool
    filter_skb(struct sk_buff *skb)
    {
        struct config cfg = __cfg;
    
        if (!cfg.target_ip && !cfg.target_port && !cfg.l4_proto)
            return false;
    
        __u16 l3_off = calc_l3_off(skb);
        if (l3_off == 0)
            return true;
    
        unsigned char *skb_head = BPF_CORE_READ(skb, head);
    
        struct iphdr *iph;
        iph = (typeof(iph))(skb_head + l3_off);
        __u8 l4_proto = BPF_CORE_READ(iph, protocol);
        if (filter_proto(l4_proto))
            return true;
    
        if (cfg.l4_proto && cfg.l4_proto != l4_proto)
            return true;
    
        __be32 dip = BPF_CORE_READ(iph, daddr);
        if (cfg.target_ip && cfg.target_ip != dip)
            return true;
    
        struct udphdr *udph;
        udph = (typeof(udph))(skb_head + l3_off + calc_ipv4_hdr_size(iph));
        __be16 dport = BPF_CORE_READ(udph, dest);
        if (cfg.target_port && cfg.target_port != dport)
            return true;
    
        return false;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    如上代码片段,对于协议头的变量,先声明而后使用 typeof() 初始化,其它变量则可以直接声明并初始化。

    小结

    编写的 eBPF 代码量逐渐增大后,逐渐地形成了自己的代码风格。

    而且,有了自己的代码风格后,可以一次性地编写 eBPF 代码并编译、加载通过,而无需反复地来回调试(为了通过编译器、eBPF verifier/校验器的检查)。


    永久链接:eBPF Talk: 变量声明的位置

  • 相关阅读:
    解决 @RefreshScope 导致定时任务注解 @Scheduled 失效
    基于最小二乘法和SVM从天气预报中预测太阳能发电量(Matlab代码实现)
    章节十六:复习与反爬虫
    工作八年的程序员,却拿着毕业三年的工资,再不开窍就真晚了...
    JAVA毕业设计教工公寓管理计算机源码+lw文档+系统+调试部署+数据库
    Advent of Code --- day 5
    新品上市|米尔RZ/G2UL核心板上市,助力工业4.0发展!
    基于SSM+MySQL+Bootstrap的停车场管理系统
    orcal创建索引
    依据象限搜索及混合预计耗费的A*改进算法,包含8邻域及24邻域的改进
  • 原文地址:https://blog.csdn.net/u010102162/article/details/127563299