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个组列出:
标准传输协议:
-------------------------------------------------------------------------------
| 协议号 | 协议栈注册结构 | 处理函数(handler) |
-------------------------------------------------------------------------------
| IPPROTO_UDP(17) | udp_protocol | udp_rcv |
| IPPROTO_TCP(6) | tcp_protocol | tcp_v4_rcv |
| IPPROTO_ICMP(1) | icmp_protocol | icmp_rcv |
| IPPROTO_IGMP(2) | igmp_protocol | igmp_rcv |
| IPPROTO_DCCP(33) | dccp_v4_protocol | dccp_v4_rcv |
| IPPROTO_SCTP(132) | sctp_protocol | sctp_rcv |
| IPPROTO_UDPLITE(136) | udplite_protocol | udplite_rcv |
| IPPROTO_PIM(103) | pim_protocol | pim_rcv |
-------------------------------------------------------------------------------
隧道相关协议:
-------------------------------------------------------------------------------
| IPPROTO_L2TP(115) | _ip_protocol | _ip_recv |
| IPPROTO_IPIP(4) | tunnel4_protocol | tunnel4_rcv |
| IPPROTO_IPV6(41) | tunnel64_protocol | tunnel64_rcv |
| IPPROTO_MPLS(137) | tunnelmpls4_protocol | tunnelmpls4_rcv |
| IPPROTO_GRE(47) | net_gre_protocol | gre_rcv |
-------------------------------------------------------------------------------
IPSec相关协议:
-------------------------------------------------------------------------------
| IPPROTO_AH(51) | ah4_protocol | xfrm4_ah_rcv |
| IPPROTO_ESP(50) | esp4_protocol | xfrm4_esp_rcv |
| IPPROTO_COMP(108) | ipcomp4_protocol | xfrm4_ipcomp_rcv |
-------------------------------------------------------------------------------
对于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传输层协议:
-------------------------------------------------------------------------------
| 协议号 | 协议栈注册结构 | 处理函数(handler) |
-------------------------------------------------------------------------------
| IPPROTO_SCTP | sctpv6_protocol | sctp6_rcv |
| IPPROTO_UDPLITE | udplitev6_protocol | udplitev6_rcv |
| IPPROTO_UDP | udpv6_protocol | udpv6_rcv |
| IPPROTO_FRAGMENT | frag_protocol | ipv6_frag_rcv |
| IPPROTO_ROUTING | rthdr_protocol | ipv6_rthdr_rcv |
| IPPROTO_DSTOPTS | destopt_protocol | ipv6_destopt_rcv |
| IPPROTO_NONE | nodata_protocol | dst_discard |
| IPPROTO_TCP | tcpv6_protocol | tcp_v6_rcv |
| IPPROTO_PIM | pim6_protocol | pim6_rcv |
| IPPROTO_ICMPV6 | icmpv6_protocol | icmpv6_rcv |
| IPPROTO_DCCP | dccp_v6_protocol | dccp_v6_rcv |
-------------------------------------------------------------------------------
隧道相关协议:
-------------------------------------------------------------------------------
| IPPROTO_L2TP | _ip6_protocol | _ip6_recv |
| IPPROTO_IPV6 | tunnel6_protocol | tunnel6_rcv |
| IPPROTO_IPIP | tunnel46_protocol | tunnel46_rcv |
| IPPROTO_GRE | ip6gre_protocol | gre_rcv |
-------------------------------------------------------------------------------
IPSec相关协议:
-------------------------------------------------------------------------------
| IPPROTO_AH(51) | ah4_protocol | xfrm6_ah_rcv |
| IPPROTO_ESP(50) | esp4_protocol | xfrm6_esp_rcv |
| IPPROTO_COMP(108) | ipcomp4_protocol | xfrm6_ipcomp_rcv |
-------------------------------------------------------------------------------
网络层函数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的协议注册需要注意,内核子模块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子协议结构 | XFRM子处理函数 |
--------------------------------------------------
| ah4_protocol | |
| esp4_protocol | xfrm4_rcv |
| ipcomp4_protocol | |
--------------------------------------------------
| vti_ah4_protocol | |
| vti_esp4_protocol | vti_rcv |
| vti_ipcomp4_protocol | |
--------------------------------------------------
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三种协议。
--------------------------------------------------
| XFRM子协议结构 | XFRM子处理函数 |
--------------------------------------------------
| ah6_protocol | |
| esp6_protocol | xfrm6_rcv |
| ipcomp6_protocol | |
--------------------------------------------------
| vti_ah6_protocol | |
| vti_esp6_protocol | vti6_rcv |
| vti_ipcomp6_protocol | |
--------------------------------------------------
Linux-4.15