内核使用inet_add_protocol注册了三种类型的隧道接收处理函数,三种类型分别为IPv4-in-IPv4隧道类型IPPROTO_IPIP(4)、IPv6-in-IPv4隧道IPPROTO_IPV6(41)和MIPS-in-IP隧道IPPROTO_MPLS(137)。接收处理函数分别为:tunnel4_rcv、tunnel64_rcv和tunnelmpls4_rcv。具体参见tunnel4_protocol、tunnel64_protocol和tunnelmpls4_protocol网络协议结构体的实例。
static int __init tunnel4_init(void)
{
if (inet_add_protocol(&tunnel4_protocol, IPPROTO_IPIP))
goto err;
if (inet_add_protocol(&tunnel64_protocol, IPPROTO_IPV6))
goto err;
if (inet_add_protocol(&tunnelmpls4_protocol, IPPROTO_MPLS))
goto err;
}
IPPROTO_IPIP类型的网络协议结构tunnel4_protocol如下,其handler回调函数指定为tunnel4_rcv:
static const struct net_protocol tunnel4_protocol = {
.handler = tunnel4_rcv,
.err_handler = tunnel4_err,
.no_policy = 1, /* no XFRM_POLICY_IN */
};
IPPROTO_IPIP类型数据包由注册的函数tunnel4_rcv处理。其为一个分发函数,遍历所有注册在tunnel4_handlers链表上的xfrm_tunnel类型结构,调用其成员中的handler函数指针处理接收到的数据包。优先级高的xfrm_tunnel结构先处理数据包,成功处理后(结果为0),终止遍历。
static int tunnel4_rcv(struct sk_buff *skb)
{
struct xfrm_tunnel *handler;
for_each_tunnel_rcu(tunnel4_handlers, handler)
if (!handler->handler(skb))
return 0;
}
类似的,IPPROTO_IPV6类型数据包由注册的函数tunnel64_rcv处理,其遍历注册在tunnel64_handlers链表的xfrm_tunnel元素,调用成员handler回调函数。
static int tunnel64_rcv(struct sk_buff *skb)
{
struct xfrm_tunnel *handler;
for_each_tunnel_rcu(tunnel64_handlers, handler)
if (!handler->handler(skb))
return 0;
}
IPPROTO_MPLS类型数据包由注册的函数tunnelmpls4_rcv处理,其遍历tunnelmpls4_handlers链表的xfrm_tunnel元素。
static int tunnelmpls4_rcv(struct sk_buff *skb)
{
struct xfrm_tunnel *handler;
for_each_tunnel_rcu(tunnelmpls4_handlers, handler)
if (!handler->handler(skb))
return 0;
}
函数xfrm4_tunnel_register负责向系统中注册xfrm_tunnel结构。目前内核支持的xfrm_tunnel结构分为三个大类:
1)对应于AF_INET协议族(即IPPROTO_IPIP)的ipip_handler(net/ipv6/sit.c),优先级为1;ipip_handler(net/ipv4/ipip.c),优先级2;以及xfrm_tunnel_handler(net/ipv4/xfrm4_tunnel.c),优先级为3。
2)对应于AF_INET6协议族(即IPPROTO_IPV6)的sit_handler(net/ipv6/sit.c),优先级为1;和xfrm64_tunnel_handler(net/ipv4/xfrm4_tunnel.c),优先级为2。
3)最后是,AF_MPLS协议族(即IPPROTO_MPLS协议)的mplsip_handler(net/ipv4/ipip.c),优先级为1;和mplsip_handler(net/ipv6/sit.c),优先级为2。
int xfrm4_tunnel_register(struct xfrm_tunnel *handler, unsigned short family)
{
struct xfrm_tunnel __rcu **pprev;
struct xfrm_tunnel *t;
int priority = handler->priority;
for (pprev = fam_handlers(family);
(t = rcu_dereference_protected(*pprev, lockdep_is_held(&tunnel4_mutex))) != NULL;
pprev = &t->next) {
if (t->priority > priority)
break;
}
handler->next = *pprev;
rcu_assign_pointer(*pprev, handler);
}
函数fam_handlers根据协议族的值返回其xfrm_tunnel链表的首指针,对于三个协议族AF_INET、AF_INET6和AF_MPLS,首指针分别为tunnel4_handlers、tunnel64_handlers和tunnelmpls4_handlers。三个链表中的元素都是按照优先级由高到低排序,priority数值越小,优先级越高。
static inline struct xfrm_tunnel __rcu **fam_handlers(unsigned short family)
{
return (family == AF_INET) ? &tunnel4_handlers :
(family == AF_INET6) ? &tunnel64_handlers :
&tunnelmpls4_handlers;
}
如下图:
ip_local_deliver_finish
|
inet_protos[proto]
|
IPPROTO_IPIP (4)
| |
| tunnel4_rcv
| |
| +---- ipip_rcv (net/ipv4/ipip.c)
| | |----- ipip_tunnel_rcv(skb, IPPROTO_IPIP)
| |
| +---- ipip_rcv (net/ipv6/sit.c)
| | |----- sit_tunnel_rcv(skb, IPPROTO_IPIP)
| |
| +---- xfrm_tunnel_rcv (net/ipv4/xfrm4_tunnel.c)
| |----- xfrm4_rcv_spi(skb, IPPROTO_IPIP, ip_hdr(skb)->saddr)
|
IPPROTO_IPV6 (41)
| |
| tunnel64_rcv
| |
| +---- ipip6_rcv (net/ipv6/sit.c)
| |
| +---- xfrm_tunnel_rcv (net/ipv4/xfrm4_tunnel.c)
| |----- xfrm4_rcv_spi(skb, IPPROTO_IPIP, ip_hdr(skb)->saddr)
|
IPPROTO_MPLS (137)
| |
| tunnelmpls4_rcv
| |
| +---- mplsip_rcv (net/ipv4/ipip.c)
| | |----- ipip_tunnel_rcv(skb, IPPROTO_MPLS)
| |
| +---- mplsip_rcv (net/ipv6/sit.c)
+++ |----- sit_tunnel_rcv(skb, IPPROTO_MPLS)
Linux-4.15