1. 程式人生 > >Linux協議棧對vlan的處理

Linux協議棧對vlan的處理

(基於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相關資訊。
   驅動再將這些資訊寫到晶片中。

   因此,網絡卡驅動從網絡卡接收佇列中收到的報文,已經不帶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)相同的路線上去了:)