内核IP隧道的FallBack设备与隧道

编程入门 行业动态 更新时间:2024-10-09 10:31:28

内核IP<a href=https://www.elefans.com/category/jswz/34/1762965.html style=隧道的FallBack设备与隧道"/>

内核IP隧道的FallBack设备与隧道

基于内核IP TUNNEL体系的隧道,在初始化时默认创建一个FallBack设备及相应的FallBack隧道。例如GRE类隧道、IPIP和VTI隧道。
GRE IPv4模块加载之后,默认创建三个设备,分别为gre0、gretap0和erspan0,IPIP隧道默认创建tunl0名字的设备,VTI隧道创建的默认设备名为ip_vti0。这些设备为隧道的FallBack设备。每种类型的FB设备每个网络命名空间中仅有一个,并且不能在命名空间之间移动。FallBack设备及FallBack隧道不同于其它的IP隧道及设备,其没有进行隧道封装所需的源地址、目的地址、秘钥(GRE)、序列号等信息。

使用ip命令查看系统中的IPIP、GRE类与VTI隧道的默认FB设备:

$ ip link show
2: tunl0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN mode DEFAULT group default qlen 1000link/ipip 0.0.0.0 brd 0.0.0.0
3: gre0@NONE: <NOARP> mtu 1476 qdisc noop state DOWN mode DEFAULT group default qlen 1000link/gre 0.0.0.0 brd 0.0.0.0
4: gretap0@NONE: <BROADCAST,MULTICAST> mtu 1462 qdisc noop state DOWN mode DEFAULT group default qlen 1000link/ether 00:00:00:00:00:00 brd ff:ff:ff:ff:ff:ff
5: erspan0@NONE: <BROADCAST,MULTICAST> mtu 1450 qdisc noop state DOWN mode DEFAULT group default qlen 1000link/ether 00:00:00:00:00:00 brd ff:ff:ff:ff:ff:ff
6: ip_vti0@NONE: <NOARP> mtu 1332 qdisc noop state DOWN mode DEFAULT group default qlen 1000link/ipip 0.0.0.0 brd 0.0.0.0

以下配置一个通用的GRE隧道设备gre1,以及显示其与GRE隧道默认FB设备的区别:

$ sudo ip tunnel add gre1 mode gre remote 192.168.1.123 local 192.168.1.113 ttl 255 ikey 0x111111 okey 0x222222 csum seq
$ ip -d link show type gre
5: gre0@NONE: <NOARP> mtu 1476 qdisc noop state DOWN mode DEFAULT group default qlen 1000link/gre 0.0.0.0 brd 0.0.0.0 promiscuity 0 gre remote any local any ttl inherit nopmtudisc addrgenmode eui64 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535  
12: gre1@NONE: <POINTOPOINT,NOARP> mtu 1464 qdisc noop state DOWN mode DEFAULT group default qlen 1000link/gre 192.168.1.113 peer 192.168.1.123 promiscuity 0 gre remote 192.168.1.123 local 192.168.1.113 ttl 255 ikey 0.17.17.17 okey 0.34.34.34 iseq oseq icsum ocsum addrgenmode eui64 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535 

FB设备的创建

参见初始化函数ip_tunnel_init_net,其通过__ip_tunnel_create调用创建FB设备,此设备的访问地址保存在当前网络命名空间的ip_tunnel_net结构成员fb_tunnel_dev指针中,每个命名空间中仅有一个对应于指定ID(ip_tnl_net_id)的FB设备。GRE设备、GRETAP设备和ERSPAN设备具有不同的ID。devname为指定的FB设备名称。为设备结构的features成员添加仅限本地网络命名空间(NETIF_F_NETNS_LOCAL表明不可移动)。

int ip_tunnel_init_net(struct net *net, unsigned int ip_tnl_net_id, struct rtnl_link_ops *ops, char *devname)
{struct ip_tunnel_net *itn = net_generic(net, ip_tnl_net_id);struct ip_tunnel_parm parms;if (devname)strlcpy(parms.name, devname, IFNAMSIZ);itn->fb_tunnel_dev = __ip_tunnel_create(net, ops, &parms);if (!IS_ERR(itn->fb_tunnel_dev)) {itn->fb_tunnel_dev->features |= NETIF_F_NETNS_LOCAL;itn->fb_tunnel_dev->mtu = ip_tunnel_bind_dev(itn->fb_tunnel_dev);ip_tunnel_add(itn, netdev_priv(itn->fb_tunnel_dev));}
}

函数ip_tunnel_add将FB设备对应的ip_tunnel隧道结构按照哈希值挂载到网络命名空间的全局隧道链表中。其中FB设备对应的IP隧道结构在以上函数__ip_tunnel_create中分配网络设备时作为私有结构一并分配出来,哈希值的计算是按照隧道参数ip_tunnel_parm结构的成员:输入秘钥(i_key)和隧道远端IP(remote)的值计算而来。最终得到的hash值作为索引,对应到ip_tunnel_net类型itn的数据成员tunnels[hash]而得到链表的头部指针,即ip_bucket的返回结果。将FB设备对应的隧道结构链接到此链表中。由于FB设备的隧道参数parms仅初始化了name成员的值,i_key与remote都为空,计算的hash值为零,所以FB设备的隧道结构链接在itn->tunnel[0]所指向的链表上。

static struct hlist_head *ip_bucket(struct ip_tunnel_net *itn, struct ip_tunnel_parm *parms)
{__be32 i_key = parms->i_key;if (parms->iph.daddr && !ipv4_is_multicast(parms->iph.daddr))remote = parms->iph.daddr;elseremote = 0;if (!(parms->i_flags & TUNNEL_KEY) && (parms->i_flags & VTI_ISVTI))i_key = 0;h = ip_tunnel_hash(i_key, remote);return &itn->tunnels[h];
}
static void ip_tunnel_add(struct ip_tunnel_net *itn, struct ip_tunnel *t)
{struct hlist_head *head = ip_bucket(itn, &t->parms);if (t->collect_md)rcu_assign_pointer(itn->collect_md_tun, t);hlist_add_head_rcu(&t->hash_node, head);
}

FB设备在__ip_tunnel_create函数中被分配和注册。如果名字参数为空,使用kind值与%d索引组成的字符串作为设备名字,GRE隧道就是如此,未指定name,其kind值为gre,由于FB设备是第一个注册的gre设备,系统为其分配了设备名称gre0。

static struct net_device *__ip_tunnel_create(struct net *net, const struct rtnl_link_ops *ops, struct ip_tunnel_parm *parms)
{struct ip_tunnel *tunnel;if (parms->name[0])strlcpy(name, parms->name, IFNAMSIZ);else {strlcpy(name, ops->kind, IFNAMSIZ);strncat(name, "%d", 2);}dev = alloc_netdev(ops->priv_size, name, NET_NAME_UNKNOWN, ops->setup);err = register_netdevice(dev);
}

以下GRE三种类型的隧道初始化程序,其中不同的三个ID值ipgre_net_id、gre_tap_net_id和erspan_net_id分别在网络命名空间中对应着三个不同的隧道结构,每个命名空间隧道结构(ip_tunnel_net)对应一个FB设备。如前所述GRE隧道未指定FB设备名(最后一个参数为NULL),其设备名由ipgre_link_ops的kind成员指定。GRETAP隧道和ERSPAN隧道明确指定了FB设备名。如果后两个隧道不明确指定设备名,ip_tunnel_init_net函数也可以由ipgre_tap_ops和erspan_link_ops结构的kind成员值(分别为gretap和erspan)和索引正确的推导出。

    ipgre_init_net(struct net *net)|--- ip_tunnel_init_net(net, ipgre_net_id, &ipgre_link_ops, NULL);ipgre_tap_init_net(struct net *net)|--- ip_tunnel_init_net(net, gre_tap_net_id, &ipgre_tap_ops, "gretap0");erspan_init_net(struct net *net)|--- ip_tunnel_init_net(net, erspan_net_id, &erspan_link_ops, "erspan0");

以下为IPIP隧道和VTI隧道的初始化,可见二者指定的FB隧道设备名称为tunnl0和ip_vti0:

    ipip_init_net(struct net *net)|--- ip_tunnel_init_net(net, ipip_net_id, &ipip_link_ops, "tunl0");vti_init_net(struct net *net)|--- ip_tunnel_init_net(net, vti_net_id, &vti_link_ops, "ip_vti0");

FB隧道设备接收

对于接收到的隧道封装数据包,在处理之前,内核需要知道其属于哪一个隧道。ip_tunnel_lookup函数查找合适的隧道,如果内核中并没有相对应的隧道,查找函数将使用FB设备对应的隧道作为返回值。因为FB隧道没有源和目的地址等信息,理论上和所有数据包都匹配。

struct ip_tunnel *ip_tunnel_lookup(struct ip_tunnel_net *itn,int link, __be16 flags, __be32 remote, __be32 local, __be32 key)
{if (itn->fb_tunnel_dev && itn->fb_tunnel_dev->flags & IFF_UP)return netdev_priv(itn->fb_tunnel_dev);
}

在找到隧道之后,__iptunnel_pull_header函数剥掉隧道的头部数据,
隧道内层的数据包交由ip_tunnel_rcv函数处理。由于此处的隧道为FB隧道,其不具有校验的功能,所以如果数据包设置了校验位,FB隧道将丢弃此数据包。

static int __ipgre_rcv(struct sk_buff *skb, const struct tnl_ptk_info *tpi, ...)
{  struct ip_tunnel *tunnel;tunnel = ip_tunnel_lookup(itn, skb->dev->ifindex, tpi->flags, iph->saddr, iph->daddr, tpi->key);if (tunnel) {__iptunnel_pull_header(skb, hdr_len, tpi->proto, raw_proto, false);ip_tunnel_rcv(tunnel, skb, tpi, tun_dst, log_ecn_error);return PACKET_RCVD;}
}

函数skb_reset_network_header将网络头指针重新指向内层数据的IP头部,skb_scrub_packet消除skb中的一些数据包记录信息,将skb结构的成员pkt_type设置为PACKET_HOST,随后由函数gro_cells_receive将数据包送往内核协议栈。此时作为一个普通的IP数据包,协议栈根据IP头部信息查询路由表,决定数据包时本地接收或者进行转发。

int ip_tunnel_rcv(struct ip_tunnel *tunnel, struct sk_buff *skb, const struct tnl_ptk_info *tpi, ...)
{if ((!(tpi->flags&TUNNEL_CSUM) &&  (tunnel->parms.i_flags&TUNNEL_CSUM)) ||((tpi->flags&TUNNEL_CSUM) && !(tunnel->parms.i_flags&TUNNEL_CSUM))) {tunnel->dev->stats.rx_crc_errors++;tunnel->dev->stats.rx_errors++;goto drop;}skb_reset_network_header(skb);skb_scrub_packet(skb, !net_eq(tunnel->net, dev_net(tunnel->dev)));gro_cells_receive(&tunnel->gro_cells, skb);
}

FB隧道设备必须配置有与内存数据包IP地址相同网段的地址,否者数据包将被协议栈路由系统的(rp_filter)反向路径检查所丢弃。

内核版本

Linux-4.15

 

更多推荐

内核IP隧道的FallBack设备与隧道

本文发布于:2024-02-14 09:34:36,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1762756.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:隧道   内核   设备   IP   FallBack

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!