linux vlan

前段时间对新版Linux Kernel处理VLAN网络报文存在些疑惑,现对linux-4.4.4内核VLAN处理粗略的分析解除疑惑,即新版Linux内核在__netif_receive_skb_core函数中脱掉网络报文VLAN头部信息后,在没有对应的VLAN网络接口接收处理的情况下,VLAN网络报文将如何转发。



VLAN网络报文接收:

Linux网络协议栈接收处理物理网卡的网络报文,基本由netif_receive_skb函数开始,netif_receive_skb函数可以说是自底向上的网络协议栈的入口,在该入口函数实现不同网络协议报文的分发处理,例:ARP802.1QIP协议等。


函数int netif_receive_skb(struct sk_buff *skb)定义在net/core/dev.c文件内,该函数定义如下:




Linux网络报文在由netif_receive_skb函数接收处理函数调用过程如下:


int netif_receive_skb(struct sk_buff *skb)-> static int netif_receive_skb_internal(struct sk_buff *skb) -> staticint __netif_receive_skb(struct sk_buff *skb) -> static int__netif_receive_skb_core(struct sk_buff *skb, bool pfmemalloc),该网络报文最终由__netif_receive_skb_core函数处理,根据网络报文协议类型分发至不同的网络报文协议处理模块。


网络报文在static int __netif_receive_skb_core(struct sk_buff *skb, boolpfmemalloc)函数的处理流程如下:


1)检查该网络报文是否为ETH_P_8021Q/ETH_P_8021AD协议类型,若是则由skb_vlan_untag

  函数脱掉vlan头部信息


2)该网络报文进一步移交由ptype_allskb->dev->ptype_all协议列表处理,例如应用程序抓包比如tcpdump应用程序抓包,将由ptype_allskb->dev->ptype_all协议列表处理函数将网络报文移交至应用层tcpdump应用程序. 


3) skb_vlan_tag_present 检查该网络报文是否为vlan网络报文,若是则将网络报文移交vlan_do_receive函数处理, vlan_do_receive函数主要作用根据vlan_id查找更新接收网络报文的vlan网络接口,更新处理完成后返回__netif_receive_skb_core移交上层协议处理,其处理过程如下:






4)最后将根据网络报文协议类型由deliver_ptype_list_skb函数将网络报文移交至网络报文协议处理模块处理,IP网络报文协议类型为例,IP网络报文将由ip_rcv接口函数处理,IP协议类型的注册如下,最终由dev_add_pack协议注册接口函数将ip_packet_type注册至ptype_base协议列表处理内.



早期linux内核在处理VLAN协议时同样采用IP协议类似的处理方式,将ETH_P_8021Q协议注册至ptype_base协议列表处理内:



但这样做存在问题,例如将VLAN网络接口eth1.101的物理网络接口eth1添加至Linux网桥时VLANID=101的网络报文将直接被Linux网桥处理,必须通过额外的在ebtables BROUTE表里添加ebtables规则才能将VLANID=101的网络报文送至VLAN网络接口eth1.101进行接收处理,详细实现请参考Linux-2.6.32.11


而新版linux内核对该实现方式直接嵌入至__netif_receive_skb_core函数,统一脱掉VLAN头部信息移交vlan_do_receive函数处理根据vlan_id查找更新接收网络报文的vlan网络接口等相关信息.再移交更上层的网络协议处理模块处理,比如IP协议类型,完成对VLAN网络报文的接收处理工作。



VLAN网络报文发送:

Linux创建VLAN网络接口可通过vconfigiproute2命令工具创建,基本的命令格式如下:

vconfig add eth1 101

ip linkadd link eth1 name eth1.101type vlan id 101

上述命令注册VLAN网络接口eth1.101,接收处理来自eth1VLANID101的网络报文。

eth1.101 VLAN网络接口在注册时由static const struct net_device_ops vlan_netdev_ops初始化,因此所有经由eth1.101 VLAN网络接口发出的网络报文将由vlan_dev_hard_start_xmit函数处理



vlan_dev_hard_start_xmit函数处理过程,简述如下:


1) 所有经过桥接/路由转发经由例:eth1.101 VLAN网络接口发出的网络报文将由vlan_dev_hard_start_xmit函数处理

2)通过例:eth1.101 VLAN网络接口获取VLAN配置信息,涉及VLAN配置VLANIDVLAN优先级

3)检查网络报文是否符合添加VLAN头部信息,符合则在skb中更新VLAN头部信息,由__vlan_hwaccel_put_tag根据vlan_tci更新skb VLAN信息



4)更新网络报文skb->devskb->dev=vlan->real_dev,eth1,最后由dev_queue_xmit最终 由将网络报文由物理网络接口eth1发出,完成VLAN网络报文的发送工作。


5)至此在新版Linux内核VLAN发送中尚未真正看到在网络报文中插入vlan头部,早期linux内核在处理发送VLAN网络报文时网络报文中插入vlan头部是在vlan_dev_hard_start_xmit函数中完成插入vlan头部的.顺着dev_queue_xmit的处理逻辑: int dev_queue_xmit(struct sk_buff *skb) ->static int__dev_queue_xmit(struct sk_buff *skb, void *accel_priv)-> static struct sk_buff*validate_xmit_skb(struct sk_buff *skb, struct net_device *dev)最终调用

static struct sk_buff*validate_xmit_vlan(struct sk_buff *skb,

netdev_features_t features)完成在网络报文中插入VLAN头部,完成VLAN网络报文封装,最络由物理网络接口发出VLAN网络报文。

 

新版Linux内核在接收处理VLAN网络报文时在__netif_receive_skb_core调用skb_vlan_untag函数脱掉网络报文的VLAN头部信息,因此对于上层协议来说该网络报文转换成普通以太网网络报文。相关处理逻辑如下:

struct sk_buff *skb_vlan_untag(structsk_buff *skb)并由__vlan_hwaccel_put_tag将脱掉VLAN头部信息更新至skb中。


网络报文脱掉VLAN头部信息,若在vlan_do_receive函数根据vlan_id查找不到接收网络报文的vlan网络接口,该网络报文再由网络协议栈转发比如linux网桥/ovs网桥,经由dev_queue_xmit转发至其它网络接口时将由struct sk_buff *validate_xmit_skb(struct sk_buff *skb, structnet_device *dev) 完成己脱掉VLAN头部在网络报文中重新插入VLAN头部,完成VLAN报文转发。



猜你喜欢

转载自blog.csdn.net/qq_35782149/article/details/90034216