• 内核网络协议栈传输层协议框架


    Linux内核支持的传输层协议包括传统的TCP,UDP,以及IPIP、GRE等隧道协议。内核协议栈使用net_protocol结构体表示其所支持的传输层协议,系统初始化或者相应协议模块加载时,具体的协议将根据协议号将其实现的net_protocol结构体注册到协议栈中,即以协议号为索引添加到全局结构体指针数组inet_protos[]中。inet_protos数据大小为MAX_INET_PROTOS(256),由于IP头部中protocol字段为8bit位,256以足够保存所有协议。
        struct net_protocol {
            int         (*early_demux)(struct sk_buff *skb);
            int         (*early_demux_handler)(struct sk_buff *skb);
            int         (*handler)(struct sk_buff *skb);
            void        (*err_handler)(struct sk_buff *skb, u32 info);
            unsigned int  no_policy:1,
                          netns_ok:1,
                          icmp_strict_tag_validation:1;
        };
        struct net_protocol __rcu *inet_protos[MAX_INET_PROTOS] __read_mostly;

    传输层的预分流功能(early_demux/early_demux_handler)目前只有TCP和UDP协议有实现,其它类型不支持。handler回调函数为协议的核心处理函数。内核提供inet_add_protocol函数向系统注册协议处理结构。

    一、传输层协议
    目前Linux内核协议栈支持的协议有以下,分成3个组列出:

    标准传输协议:

     隧道相关协议:

     
    

     IPSec相关协议:

     对于IPv6与以上IPv4结构相同。内核协议栈使用inet6_protocol结构体表示其所支持的传输层协议,使用inet6_add_protocol向内核注册协议。

        struct inet6_protocol {
            void    (*early_demux)(struct sk_buff *skb);
            void    (*early_demux_handler)(struct sk_buff *skb);
            int     (*handler)(struct sk_buff *skb);
        
            void    (*err_handler)(struct sk_buff *skb, struct inet6_skb_parm *opt, u8 type, u8 code, int offset, __be32 info);
            unsigned int    flags;  /* INET6_PROTO_xxx */
        };

    目前内核支持的IPv6传输层协议:

     隧道相关协议:

     IPSec相关协议:

    二、协议处理
    网络层函数ip_local_deliver_finish(IPv6:ip6_input_finish)负责调用上层传输协议回调函数。如下代码所示,从IP头部取出protocol协议号字段,以此为所以到inet_protos(IPv6:inet6_protos)数组中取出相应的注册协议结构体,调用其中的回调函数(handler)。static int ip_local_deliver_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
    {
        int protocol = ip_hdr(skb)->protocol;
        const struct net_protocol *ipprot;
     
        ipprot = rcu_dereference(inet_protos[protocol]);
        if (ipprot)
            ret = ipprot->handler(skb);
    }

    如果接收到ICMP错误信息,根据协议类型ICMP模块调用相应的协议错误处理函数(err_handler):

    static void icmp_socket_deliver(struct sk_buff *skb, u32 info)
    {
        const struct net_protocol *ipprot;
        int protocol = iph->protocol;
    (...)
        ipprot = rcu_dereference(inet_protos[protocol]);
        if (ipprot && ipprot->err_handler)
            ipprot->err_handler(skb, info);
    }

    三、预分流功能
    预分流时网络层在处理接收到数据包时,在路由查找和netfilter的hook点NF_INET_LOCAL_IN之前,提前查找套接口,取得套接口中缓存路由项的功能。这样省去了之后的路由查找,此功能仅适用处于established状态的套接口。但是对于要转发的报文,增加了不必要的套接口查找操作。

    在ip_rcv_finish函数中,根据协议号找到相应的协议处理结构,调用其中的early_demux回调函数。early_demux成员变量指向同结构中的early_demux_handler,用户可单独关闭TCP或者UDP的预分流,关闭时其early_demux指向空指针。用户可通过proc文件(/proc/sys/net/ipv4/ip_early_demux)整个关闭预分流功能。static int ip_rcv_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
    {
        const struct iphdr *iph = ip_hdr(skb);
        int (*edemux)(struct sk_buff *skb);
        struct net_device *dev = skb->dev;
     
        if (net->ipv4.sysctl_ip_early_demux &&
            !skb_dst(skb) &&
            !skb->sk &&
            !ip_is_fragment(iph)) {
            const struct net_protocol *ipprot;
            int protocol = iph->protocol;
     
            ipprot = rcu_dereference(inet_protos[protocol]);
            if (ipprot && (edemux = READ_ONCE(ipprot->early_demux)))
                err = edemux(skb);
        }
    }

    四、IPSec传输协议
    关于IPSec的协议注册需要注意,内核子模块XFRM首先注册了通用的net_protocol结构和三个协议的回调函数(xfrm4_ah_rcv/xfrm4_esp_rcv/xfrm4_ipcomp_rcv)。但是这三个回调实际上不进行实际的报文处理,而是作为入口,根据注册的子协议结构,调用XFRM系统自身的子协议处理函数。例如对于ESP协议,XFRM模块注册了两个子协议结构esp4_protocol和vti_esp4_protocol,分别对应两个处理函数xfrm4_rcv和vti_rcv。

     XFRM模块提供xfrm4_protocol_register注册子协议结构,ah4_protocol为xfrm4_protocol类型的结构体变量,与以上的主协议变量ah4_protocol(net_protocol结构体)同名但是不同的数据结构。相同协议号的xfrm4_protocol结构体通过其成员next链接起来,内核中为三种协议(AH、ESP和IPCOMP)定义了链表头,分别为ah4_handlers、esp4_handlers。 协议入口函数(如xfrm4_ah_rcv)从头开始遍历链表(如ah4_handlers链表),依次调用其中的子处理函数。struct xfrm4_protocol {
        int (*handler)(struct sk_buff *skb);
        int (*input_handler)(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type);
        int (*cb_handler)(struct sk_buff *skb, int err);
        int (*err_handler)(struct sk_buff *skb, u32 info);
        struct xfrm4_protocol __rcu *next;
        int priority;
    }; 

    对于IPv6 IPSec协议,代码框架与IPv4相同,使用xfrm6_protocol_register函数注册协议结构,子协议链条头指针分别为ah6_handlers、esp6_handlers和ipcomp6_handlers分别对应AH、ESP和IPCOMP三种协议。

     

  • 相关阅读:
    【LeetCode】66. 加一
    ROS从入门到精通5-4:路径规划插件开发案例(以A*算法为例)
    markdown语法简述
    Linux文件元数据操作
    第四节唯物辩证法(两大总特征)
    leetcode:6243. 到达首都的最少油耗【变种子树大小统计 + 从边的角度出发 + 思维转换】
    【数字IC基础】DFT(Design For Test)可测性设计
    uni.createInnerAudioContext`在ios手机无法自动播放,可通过`jweixin-module`来解决
    JSON.stringify()的深入学习和理解
    在数据工厂中刷新PowerBI数据集
  • 原文地址:https://blog.csdn.net/wuyongmao/article/details/126308992