由于Linux系統強大的網絡管理功能,很多網絡設備都是基于Linux開發的,通過Linux自帶的netfilter模塊,以及豐富的開源軟件可以打造出功能完善的網絡接入設備。其中典型的運用就是虛擬子網VLAN,在交換機上劃分多個VLAN子網,并且匯聚到中繼口Trunk上,同時網關啟用VLAN模塊,通過配置對應的VLAN虛擬網卡實現與交換機的互聯通訊。目前大多數網卡為了提高數據包的處理能力,直接對報文的VLAN Tag進行移除和插入操作,因此到達內核中的數據包已經不包含VLAN Tag。為了實現對數據包的分析審計等需求,需要知道每個數據包是屬于哪個VLAN的。基于這個目的本文通過對數據包的收發流程,抓包原理的分析,提出了一種通過直接修改內核讓數據包帶上VLAN ID的方法。
現在使用最廣泛的VLAN協議標準是 IEEE 802.1Q,許多廠家的交換機,路由器產品都支持IEEE 802.1Q標準。802.1Q Tag的長度是4 bytes,它位于以太網幀中源MAC地址和長度類型之間。802.1Q Tag包含4個字段。
(1)Type:長度為2 bytes,表示幀類型,802.1Q tag幀中Type字段取固定值 0x8100,如果不支持 802.1Q的設備收到802.1Q幀,則將其丟棄。
(2)PRI:priority字段,長度為3 bit,表示 以太網幀的優先級,取值范圍是0~7,數值越大,優先級越高。當交換機,路由器發生傳輸擁塞時,優先發送優先級高的數據幀。
(3)CFI:Canonical Format Indicator,長度為 1bit,表示MAC地址是否是經典格式。CFI為0說明是經典格式,CFI為1表示為非經典格式。該字段用于區分以太網幀、FDDI幀和令牌環網幀,在以太網幀中,CFI取值為0。
(4)VID:VLAN ID,長度為12 bit,取值范圍是0~4095,其中0和4095是保留值,不能給用戶使用。

圖1 VLAN Tag報文格式
數據包到達網卡以后,網卡判斷數據包是否為802.1Q報文,如果是的話則移除VLAN Tag,修改以太網幀,同時把VLAN信息儲存在 sk_buff中。網卡驅動從網卡收到的報文,已經不帶VLAN tag了。接著通過 netif_rx進入協議棧,netif_rx調用napi_schedule調度,發送 NET_RX_SOFTIRQ軟中斷,觸發net_rx_action執行其 poll操作 process_backlog,接著調用netif_receive_skb來處理收到的skb數據。VLAN模塊通過調用dev_add_pack注冊了802.1Q的協議類型,將自己的prot_hook 掛到了ptype_all鏈表中。在netif_receive_skb遍歷ptype_all找到對應的VLAN鉤子,于是進入了vlan的接收函數vlan_skb_recv,這個函數會移除 VLAN Tag重置以太網層的 proto,再調用netif_rx重新進入協議棧處理流程。由于VLAN Tag已經在網卡上被移除了,實際上并不會進入vlan_skb_recv。不過網卡只會移除第一層VLAN Tag,如果你的報文是QINQ這種雙層VLAN的協議,那么第二層VLAN Tag將由vlan_skb_recv來移除。
數據包進入ip_crv以后,根據目的IP進行路由判斷,如果是發往本地的報文則進入ip_local_deliver后發往本地應用。如果目的IP不是本機則根據默認路由進入轉發模塊ip_forward,如果需要 NAT則內核修改數據包的源 IP等信息,接著協議棧通過dev_queue_xmit調用 dev_hard_start_xmit,關聯到了具體的網絡設備處理函數,從而調用了vlan_dev_hard_start_xmit,在這里插入VLAN Tag頭,并壓入sk_buffer中,找到真實的網絡設備,再次調用dev_queue_xmit,進入網卡驅動,這里只有雙層VLAN協議才會由內核插入內層的VLAN TAG,最外層的VLAN TAG由網卡完成插入。
Libpcap通過創建 AF_PACKET類型的套接字,把自己的prot_hook掛到了ptype_all鏈表上,AF_PACKET套接字注冊的鉤子函數是packet_rcv。可以看出無論是接收還是發送的報文,內核都會對ptype_all鏈表進行遍歷,最終會調用到packet_rcv函數,把數據包加入到了 AF_PACKET套接字的接收隊列。當應用程式調用recv接收數據包時內核最終會調用packet_recvmsg從接收隊列中取出skb,將數據包內容skb->data拷貝到用戶空間。
通過上面的分析,我們只要在packet_recvmsg函數里在將數據包拷貝到用戶空間的時候將VLAN ID插入到報文的結尾,再通過libpcap等AF_PACKET套接字接口獲取數據的時候就可以得到VLAN ID。代碼修改如下。
修改前:

修改后:

以發送帶有VLAN ID為100的報文為例,下圖是交換機發出的報文:

圖2 VLAN報文
Linux網關接收到數據包以后將VLAN ID插入結尾:

圖3 修改后的VLAN報文
通過修改內核的方式使得 AF_PACKET套接字獲取的報文攜帶VLAN ID,為數據包的分析審計提供了基礎。