(基于linux-2.6.x)
从代码上看,Linux对VLAN的处理方式如下。
1. vlan的处理,主要是依靠网卡本身。
有的网卡不支持vlan,如老的3com网卡3c501。
intel的ixgb(PRO/10GbE)和e1000(PRO/1000)网卡是支持vlan的。
其他的一些网卡驱动,从代码上来看,还未完整支持。
例如,有个网卡驱动(源码文件:drivers\net\spider_net.c),
vlan相关代码上的注释上说,/* further enhancement... yet to do */
2. 网卡对vlan的处理
VLAN tag插入/移除 都是由网卡完成的。
vlan报文的过滤也是由网卡完成的。
Linux协议栈通过驱动中的接口,注册vlan相关信息。
驱动再将这些信息写到芯片中。
rtl8169_rx_vlan_tag(desc, skb);
if (likely(polling))
napi_gro_receive(&tp->napi, skb);
else
netif_rx(skb);
dev->stats.rx_bytes += pkt_size;
dev->stats.rx_packets++;
从注释及代码可以看出,就是将vlan_tci记录到skb中,这样协议栈能知道这个报文的vlan信息,从而进行相应的处理。
3. Linux对vlan报文的处理(VLAN tag已经剥掉了)
上一步的工作完成后,接下来的工作有两种情况。
a)报文不是vlan报文,则驱动程序调用netif_receive_skb对报文进行处理。
其处理流程在《Linux协议栈代码阅读笔记(三)报文接收》中有描述。
b)报文是vlan报文(不过已经不带VLAN tag了),则驱动程序调用vlan_hwaccel_receive_skb对报文进行处理,vlan tag通过 参数传递给vlan_hwaccel_receive_skb。
vlan_hwaccel_receive_skb则简单的以如下形式调用__vlan_hwaccel_rx。
__vlan_hwaccel_rx(skb, grp, vlan_tag, 1);
__vlan_hwaccel_rx先根据vlan tag为报文做优先级赋值。
然后将低层代码误以为vlan报文的目的mac不是本机的错误纠正过来。
从代码上看,Linux对VLAN的处理方式如下。
1. vlan的处理,主要是依靠网卡本身。
有的网卡不支持vlan,如老的3com网卡3c501。
intel的ixgb(PRO/10GbE)和e1000(PRO/1000)网卡是支持vlan的。
其他的一些网卡驱动,从代码上来看,还未完整支持。
例如,有个网卡驱动(源码文件:drivers\net\spider_net.c),
vlan相关代码上的注释上说,/* further enhancement... yet to do */
2. 网卡对vlan的处理
VLAN tag插入/移除 都是由网卡完成的。
vlan报文的过滤也是由网卡完成的。
Linux协议栈通过驱动中的接口,注册vlan相关信息。
驱动再将这些信息写到芯片中。
因此,网卡驱动从网卡接收队列中收到的报文,已经不带VLAN tag了。
下面是r8169.c网卡驱动收到报文后,做的部分处理。
由于驱动收到的报文,已经不带vlan tag,因此skb->protocol的值就是普通的以太网类型。
skb->protocol = eth_type_trans(skb, dev);rtl8169_rx_vlan_tag(desc, skb);
if (likely(polling))
napi_gro_receive(&tp->napi, skb);
else
netif_rx(skb);
dev->stats.rx_bytes += pkt_size;
dev->stats.rx_packets++;
再看看rtl8169_rx_vlan_tag干了什么。下面贴出相关代码:
/**
* __vlan_hwaccel_put_tag - hardware accelerated VLAN inserting
* @skb: skbuff to tag
* @vlan_tci: VLAN TCI to insert
*
* Puts the VLAN TCI in @skb->vlan_tci and lets the device do the rest
*/
static inline struct sk_buff *__vlan_hwaccel_put_tag(struct sk_buff *skb,
u16 vlan_tci)
{
skb->vlan_tci = VLAN_TAG_PRESENT | vlan_tci;
return skb;
}
static void rtl8169_rx_vlan_tag(struct RxDesc *desc, struct sk_buff *skb)
{
u32 opts2 = le32_to_cpu(desc->opts2);
if (opts2 & RxVlanTag)
__vlan_hwaccel_put_tag(skb, swab16(opts2 & 0xffff));
desc->opts2 = 0;
}从注释及代码可以看出,就是将vlan_tci记录到skb中,这样协议栈能知道这个报文的vlan信息,从而进行相应的处理。
因为vlan tag被硬件剥掉了,软件不知道vlan信息,因此这里的代码将剥掉的vlan信息记录到SKB中,供协议栈使用。
3. Linux对vlan报文的处理(VLAN tag已经剥掉了)
上一步的工作完成后,接下来的工作有两种情况。
a)报文不是vlan报文,则驱动程序调用netif_receive_skb对报文进行处理。
其处理流程在《Linux协议栈代码阅读笔记(三)报文接收》中有描述。
b)报文是vlan报文(不过已经不带VLAN tag了),则驱动程序调用vlan_hwaccel_receive_skb对报文进行处理,vlan tag通过 参数传递给vlan_hwaccel_receive_skb。
vlan_hwaccel_receive_skb则简单的以如下形式调用__vlan_hwaccel_rx。
__vlan_hwaccel_rx(skb, grp, vlan_tag, 1);
__vlan_hwaccel_rx先根据vlan tag为报文做优先级赋值。
然后将低层代码误以为vlan报文的目的mac不是本机的错误纠正过来。
接下来,__vlan_hwaccel_rx调用netif_receive_skb。这就将处理流程收敛到与a)相同的路线上去了:)
Linux内核中vlan处理机制解析
这篇博客探讨了Linux协议栈如何处理vlan报文。网卡驱动在接收到不带VLAN tag的报文后,通过rtl8169_rx_vlan_tag函数将剥离的vlan信息记录到SKB中,以便协议栈后续处理。最终,报文通过__vlan_hwaccel_rx调用netif_receive_skb进入常规接收流程。
2071

被折叠的 条评论
为什么被折叠?



