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

发布于:2023-01-20 ⋅ 阅读:(231) ⋅ 点赞:(0)

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三种协议。

 

本文含有隐藏内容,请 开通VIP 后查看