王余雷WANG Yu-lei;程鵬CHENG Peng;婁方亮LOU Fang-liang;謝海峰XIE Hai-feng
(中興通訊股份有限公司,南京210012)
通訊系統(tǒng)使用DPDK(Data Plane Development Kit,數(shù)據(jù)平面開發(fā)套件)接管網(wǎng)卡后,要實(shí)現(xiàn)對(duì)網(wǎng)卡進(jìn)行抓包的功能,一般來(lái)說(shuō),需要基于業(yè)務(wù)流程增加抓包函數(shù),且抓包函數(shù)是通過(guò)五元組(目的端口,源端口,目的地址,源地址,協(xié)議類型)等簡(jiǎn)單的特征字段或規(guī)則進(jìn)行報(bào)文過(guò)濾,其中特征字段或規(guī)則是固化在源代碼中;從而,當(dāng)增加特殊的特征字段或規(guī)則時(shí),則需要重新修改源代碼和制作版本等。為了更好地應(yīng)對(duì)大流量復(fù)雜場(chǎng)景的精準(zhǔn)報(bào)文過(guò)濾,本文實(shí)現(xiàn)了基于DPDK 的eBPF 報(bào)文過(guò)濾器進(jìn)行靈活的抓包功能,已在通訊系統(tǒng)中得到廣泛的應(yīng)用。
BPF (Berkeley Packet Filter),即伯克利數(shù)據(jù)包過(guò)濾器,最初構(gòu)想提出于1992 年,其目的是為了提供一種過(guò)濾包的方法,并且要避免從內(nèi)核空間到用戶空間的無(wú)用的數(shù)據(jù)包復(fù)制行為。在2013 年,Alexei Starovoitov 對(duì)BPF 進(jìn)行了徹底地改造,并增加了新的功能,該新版本被命名為eBPF,即extended BPF;為了后向兼容,傳統(tǒng)的BPF 仍被保留,該老版本被重命名為cBPF,即classical BPF。對(duì)比于cBPF,eBPF 帶來(lái)的改變是革命性的:其一是eBPF 可為內(nèi)核跟蹤、事件監(jiān)控、網(wǎng)絡(luò)性能、流量控制等領(lǐng)域帶來(lái)了激動(dòng)人心的變革;其二是在接口的設(shè)計(jì)以及易用性上有了較大的改進(jìn)。
Linux 內(nèi)核的BPF,是Linux 內(nèi)核提供的基于從用戶空間注入內(nèi)核的BPF 指令的動(dòng)態(tài)注入技術(shù),在內(nèi)核中實(shí)現(xiàn)了BPF 指令的虛擬機(jī),對(duì)收到的BPF 指令先用一個(gè)校驗(yàn)器進(jìn)行檢查,檢查指令無(wú)誤后附著到一個(gè)套接字上,接著對(duì)套接字上每個(gè)收到的包執(zhí)行BPF 指令。當(dāng)一個(gè)數(shù)據(jù)包到達(dá)網(wǎng)絡(luò)接口時(shí),數(shù)據(jù)鏈路層的驅(qū)動(dòng)會(huì)把它向系統(tǒng)的協(xié)議棧傳送。但如果BPF 監(jiān)聽接口,驅(qū)動(dòng)首先調(diào)用BPF。BPF 首先進(jìn)行過(guò)濾操作,然后把數(shù)據(jù)包存放在過(guò)濾器相關(guān)的緩沖區(qū)中,最后設(shè)備驅(qū)動(dòng)再次獲得控制。BPF 虛擬機(jī)是一個(gè)輕量級(jí)的,高效的狀態(tài)機(jī),對(duì)BPF 過(guò)濾代碼進(jìn)行解釋處理等。Linux 內(nèi)核的BPF 的總體架構(gòu)如圖1 所示。
DPDK 的eBPF 模塊與Linux 內(nèi)核的eBPF 類似,最大的區(qū)別在于DPDK 的eBPF 模塊在用戶態(tài)實(shí)現(xiàn)了eBPF虛擬機(jī)執(zhí)行引擎,用來(lái)執(zhí)行eBPF 程序?qū)?yīng)的字節(jié)碼,支持eBPF 規(guī)范的基本功能集,但未兼容cBPF 規(guī)范的指令集。其eBPF 提供以下的基本操作:創(chuàng)建一個(gè)新的eBPF執(zhí)行上下文,并將用戶提供的eBPF 代碼加載到其中;銷毀eBPF 執(zhí)行上下文及其運(yùn)行時(shí)結(jié)構(gòu),并釋放關(guān)聯(lián)的內(nèi)存;執(zhí)行與提供的輸入?yún)?shù)關(guān)聯(lián)的eBPF 字節(jié)碼;提供有關(guān)給定eBPF 上下文的本機(jī)編譯代碼的信息;從ELF 文件加載eBPF 程序并安裝回調(diào)以在給定的ethdev 端口/隊(duì)列上執(zhí)行它。
目前DPDK 的eBPF 模塊實(shí)現(xiàn)了兩個(gè)執(zhí)行eBPF 程序的回調(diào)入口,分別在rte_eth_tx_burst 函數(shù)和rte_eth_rx_burst函數(shù)中,在收到報(bào)文后和發(fā)送報(bào)文前,均可以執(zhí)行回調(diào)函數(shù)。而這些回調(diào)函數(shù)是由BPF 模塊在加載BPF 目標(biāo)文件時(shí)賦值。(圖2)
本文實(shí)現(xiàn)了類似通用的tcpdump 工具抓包功能,采用DPDK 的eBPF 虛擬機(jī)執(zhí)行引擎,不需要重新編譯軟件,通過(guò)人機(jī)界面方式,實(shí)現(xiàn)靈活地抓取滿足過(guò)濾條件的報(bào)文,且以pcap 格式文件保存至指定目錄下。即詳細(xì)的設(shè)計(jì)思路是:在協(xié)議棧的收發(fā)報(bào)文處,加入Hook 回調(diào)函數(shù),根據(jù)用戶輸入的抓包過(guò)濾條件,例如:指定報(bào)文的任意字段(外層頭/內(nèi)層頭/報(bào)文內(nèi)容等),對(duì)BPF 程序進(jìn)行了修改,從而就能實(shí)現(xiàn)對(duì)報(bào)文的不同操作,對(duì)于滿足抓包過(guò)濾條件的報(bào)文,則以pcap 格式文件進(jìn)行保存。(圖3)

圖1 Linux 的BPF 總體框圖

圖2 DPDK 的eBPF 總體框圖

表1 cBPF 指令轉(zhuǎn)換至eBPF 指令關(guān)系表

圖3 方案設(shè)計(jì)示意圖
根據(jù)實(shí)際需求,當(dāng)前所面臨的問題是采用DPDK 的eBPF 模塊方式來(lái)實(shí)現(xiàn),DPDK 的eBPF 虛擬機(jī)執(zhí)行引擎只支持eBPF 規(guī)范的指令集,而不支持cBPF 規(guī)范的指令集,故需要實(shí)現(xiàn)了將cBPF 指令集轉(zhuǎn)換成兼容eBPF 規(guī)范的指令集;同時(shí)考慮到所實(shí)現(xiàn)的功能的易用性和簡(jiǎn)捷性,不僅要采用符合用戶習(xí)慣的方式,即采用類似于tcpdump 的方式,而且要實(shí)現(xiàn)以pcap 文件格式保存至SFTP 目錄下的功能,更利于用戶的離線分析或調(diào)試等,本文提出了具體的解決方案。
為了將用戶輸入的報(bào)文過(guò)濾條件生成的cBPF 指令集轉(zhuǎn)換成eBPF 指令集,本文根據(jù)cBPF 和eBPF 的指令格式給出了每一條cBPF 指令轉(zhuǎn)換成eBPF 指令的關(guān)系表及進(jìn)行相應(yīng)的程序設(shè)計(jì),其中每一條cBPF 指令可能對(duì)應(yīng)一條或者多條eBPF 指令,主要的指令之間的轉(zhuǎn)換參見表1。
cBPF 指令格式的結(jié)構(gòu)體:
struct sock_filter { /* Filter block */
__u16 code; /* Actual filter code */
__u8 jt; /* Jump true */
__u8 jf; /* Jump false */
__u32 k; /* Generic multiuse field */
};
eBPF 指令格式的結(jié)構(gòu)體:
struct bpf_insn {
__u8 code; /* opcode */
__u8 dst_reg:4; /* dest register */
__u8 src_reg:4; /* source register */
__s16 off; /* signed offset */
__s32 imm; /* signed immediate constant */
};
根據(jù)前文的總體設(shè)計(jì),本文提出的具體解決方案是:當(dāng)用戶啟動(dòng)抓包后,首先,用戶輸入報(bào)文過(guò)濾條件后生成cBPF 指令,再通過(guò)轉(zhuǎn)換程序變換為eBPF 指令。然后由檢驗(yàn)器檢查eBPF 指令的正確性,若不正確,則返回錯(cuò)誤;若正確,則創(chuàng)建一個(gè)新的BPF 執(zhí)行上下文,并將eBPF 指令加載其中。接著,將BPF 執(zhí)行上下文關(guān)聯(lián)到回調(diào)函數(shù),安裝回調(diào)函數(shù)到輸入?yún)?shù)給定的端口隊(duì)列回調(diào)入口。最后,當(dāng)程序執(zhí)行到端口隊(duì)列的回調(diào)入口時(shí),若回調(diào)函數(shù)存在,則執(zhí)行回調(diào)函數(shù),即執(zhí)行BPF 執(zhí)行上下文的eBPF 指令。從而,可實(shí)現(xiàn)靈活的抓包功能。當(dāng)用戶停止抓包后,卸載回調(diào)函數(shù),銷毀BPF 執(zhí)行上下文及運(yùn)行時(shí)結(jié)構(gòu),并且釋放關(guān)聯(lián)的內(nèi)存。
根據(jù)實(shí)際需求,本文對(duì)eBPF 報(bào)文過(guò)濾器的架構(gòu)、原理和當(dāng)前所面臨的問題等進(jìn)行了研究,實(shí)現(xiàn)了基于DPDK的eBPF 報(bào)文過(guò)濾器實(shí)現(xiàn)了靈活的抓包功能,更好地解決大流量復(fù)雜場(chǎng)景的精準(zhǔn)報(bào)文過(guò)濾的問題。