本文主要介紹DPDK框架下進行報文捕獲的方法,并對各種方法的優劣進行簡單分析。
1.pdump庫的使用
在DPDK的16.07版本中,添加了Packet capture特性,通過pdump庫可以非常方便的進行報文的捕獲:
a) 在程序初始化過程中調用rte_pdump_init,啟動dump_thread進行消息的監聽:
#ifdef RTE_LIBRTE_PDUMP
/* initialize packet capture framework /
rte_pdump_init(NULL);
#endif
b) 在程序退出前調用rte_pdump_uninit進行資源的釋放:
#ifdef RTE_LIBRTE_PDUMP
/ uninitialize packet capture framework */
rte_pdump_uninit();
#endif
c) 啟動pdump程序,發送抓包命令,進行抓包。
基本流程與數據流如下所示:
步驟詳細說明:
- A采用rx-worker-tx的模型進行報文的處理,其中調用rte_pdump_init會啟動dump_thread,即圖中紅色的message線程;
- pdump采用secondary模式啟動,與A共享mmap映射的內存空間;
- pdump啟動過程中會創建mbuf_pool和ring,用于后續接收A中報文的拷貝;
- pdump會通過rte_eth_dev_attach方式創建vdev,且采用eth_pcap驅動進行初始化,留意init中的open_tx_pcap;
- pdump向A發送開啟抓包的消息(UDP方式),消息內容為前面創建的mbuf_pool、ring以及抓包的port和對應的queue;
- A中的dump_thread收到消息后,獲取相應信息,在port上注冊call_back函數;
- 對于開啟抓包的port,在rx_burst/tx_burst時會先調用call_back,這里對應pdump_rx/pdump_tx,它會由mbuf_pool中分配mbuf進行報文的復制,同時enqueue到ring中;(mbuf_pool和ring在步驟3中創建,在步驟5中傳遞給A)
- pdump進行ring的dequeue操作獲取拷貝報文;
- 拷貝報文通過rte_eth_tx_burst發送給vdev;
- vdev通過eth_pcap的tx_pkt_burst發送報文,即調用eth_pcap_tx_dumper完成報文的pcap存儲(pcap_dump)。
2.KNI方式
kNI,全稱 Kernel NIC Interface,下面是DPDK官方手冊對它的介紹:
The Kernel NIC Interface (KNI) is a DPDK control plane solution that allows userspace applications to exchange packets with the kernel networking stack.
這里對data plane和control plane進行簡單說明:
data plane專注于報文的轉發;control plane專注于協議處理,如ospf計算。如果采用DPDK架構,那么就會遇到如下問題:
不論是協議報文還是數據報文通過port接收后,都到了data plane,data plane如何將協議報文交給control plane呢?
control plane(往往不是DPDK程序)與網絡設備進行協議交互時,報文又如何投遞給port發送出去呢?
KNI就是control plane與data plane間的橋梁:加載rte_kni.ko驅動后,Linux內核響應DPDK程序中發送的IOCTL消息創建虛擬接口并轉換FIFO地址用于后續的報文交互。
在DPDK提供pdump特性前,對報文抓取主要就是采用KNI方式:
a) DPDK程序創建虛擬接口;
b) 將收到的報文發送給虛擬接口;
c) 啟用類似tcpdump的工具抓取虛擬接口上的報文。
基本流程和數據流如下:
步驟詳細說明:
- A啟動時候,創建mbuf_pool,指定kthread的綁定方式,然后通過調用rte_kni_alloc創建FIFO隊列,并發送IOCTL消息創建vEth;
- IOCTL消息處理時,既會創建vEth,同時也會啟動ktread_kni內核線程,這個線程用于將A中的報文發送給內核協議棧(在其它場景中,也可以將內核發送給vEth的報文通過tx_q傳遞給A:kni_net_tx);
- A可以實現一個message線程,用于接收類似PDUMP進程的消息來開啟抓包;
- 啟動自定義的PDUMP程序,發送抓包命令;
- 消息傳遞可以采用進程間通信機制,消息格式自行定義;
- A中判斷報文是否需要dump,如果需要dump,則將報文clone后通過rte_kni_tx_burst傳遞給內部的rx_q隊列;
- kthread_kni內核線程調用kni_net_rx,獲取rx_q中的報文;
- 報文獲取后,轉換為skb,設置skb的dev為vEth,然后通過netif_rx_ni交給內核協議棧。這時tcpdump就可抓獲傳遞到vEth的報文了。
圖中省略了操作前的insmod rte_kni.ko和最后的tcpdump工具使用。
3.用戶態實現報文捕獲和dump
一般DPDK程序作為data plane使用,在部分設計過程中,則將DPDK框架用于旁路部署,進行高性能抓包,相關設計可以參考:
github上的項目:https://github.com/Woutifier/dpdkcap
科來的專利:http://www.google.com/patents/CN105357151A?cl=zh
如果是串接或單臂模式這樣的data plane程序,在DPDK框架中進行報文的捕獲和dump操作是不合時宜的,因為它對程序自身的轉發性能影響非常大,而且增加了設計復雜度。
說明:上文中的A,指采用DPDK框架的primary進程,不同的轉發模型中它包含不同的線程。
4.優劣比較
a) pdump庫使用方便,性能高(僅有一次copy,步驟7);不過它在16.07才支持,低版本不支持,且16.07版本中還不支持filter;
b) KNI方式,需要創建thread,性能應該略低于pdump(KNI內部fifo處理有一定開銷),開發工作較多,但可以自行實現過濾功能,相比pdump的全copy在某些場景會更適用,且使用tcpdump抓取在運維上更方便;
c) pdump方式,拷貝由port的驅動完成,dump到文件則由pdump程序完成;KNI方式,拷貝在用戶態空間完成,發送到內核態后,dump到文件則有tcpdump完成;在DPDK框架中進行報文捕獲和dump,僅推薦用于旁路抓包。
本文對DPDK的抓包原理進行了簡單介紹,并比較了各種方式的優劣,對于抓包的具體設計和性能權衡則沒有做過多的探究,希望對大家能有所幫助。