BPF通過一種軟件定義的方式,將內(nèi)核的行為和數(shù)據(jù)暴露給用戶空間,開發(fā)者可以通過在用戶空間編寫B(tài)PF程序,加載到內(nèi)核空間執(zhí)行,進(jìn)而實現(xiàn)對內(nèi)核行為的靈活管理和控制
在計算機(jī)系統(tǒng)中,包過濾器通常有一個特定的用途,那就是提供給應(yīng)用程序來監(jiān)控系統(tǒng)的網(wǎng)絡(luò)與內(nèi)核運(yùn)行的相關(guān)信息。這些監(jiān)控程序?qū)τ谙到y(tǒng)的開發(fā)者、運(yùn)維者、或者是安全管理者,都有著重要的意義。
有了更加細(xì)粒度的網(wǎng)絡(luò)數(shù)據(jù)和內(nèi)核運(yùn)行數(shù)據(jù),對于開發(fā)者來說,就可以根據(jù)當(dāng)前系統(tǒng)的運(yùn)行情況,合理的優(yōu)化程序,提高程序的性能同時降低資源開銷;對于系統(tǒng)運(yùn)維者來說,能夠拿到精確全面的系統(tǒng)運(yùn)行數(shù)據(jù),可以更好的對系統(tǒng)進(jìn)行監(jiān)控,保證系統(tǒng)的可靠性與高可用性;對于安全管理者來說,可以從這些網(wǎng)絡(luò)和內(nèi)核行為中,發(fā)現(xiàn)異常,進(jìn)而在攻擊行為發(fā)生的早期,發(fā)現(xiàn)攻擊并且能夠快速的進(jìn)行響應(yīng)和修復(fù)。
BPF( )就是這樣的一種包過濾器,從其誕生之初,就引起了人們的廣泛關(guān)注與應(yīng)用,尤其是近年來,隨著微服務(wù)和云原生的發(fā)展和落地,BPF更是成為了內(nèi)核開發(fā)者最受追捧的技術(shù)之一。
一、BPF概述
BPF(BSD )是很早就有的Unix內(nèi)核特性,最早可以追溯到1992年發(fā)表在 上的一篇論文[1]。作者描述了他們?nèi)绾螢閁nix內(nèi)核實現(xiàn)一個網(wǎng)絡(luò)包過濾器,這種實現(xiàn)甚至比當(dāng)時最先進(jìn)的包過濾技術(shù)快20倍。
隨后,得益于如此強(qiáng)大的性能優(yōu)勢,所有Unix系統(tǒng)都將BPF作為網(wǎng)絡(luò)包過濾的首選技術(shù),拋棄了消耗更多內(nèi)存和性能更差的原有技術(shù)實現(xiàn)。后來由于BPF的理念逐漸成為主流,為各大操作系統(tǒng)所接受,這樣早期“B”所代表的BSD便漸漸淡去,最終演化成了今天我們眼中的BPF( )。比如我們熟知的,其底層就是依賴BPF實現(xiàn)的包過濾。
關(guān)于BPF的發(fā)展歷史,網(wǎng)上已經(jīng)有很多文章進(jìn)行了比較詳盡的解釋和描述,本文就不再過多的進(jìn)行介紹,感興趣的讀者可以自行搜索,或者參照參考文獻(xiàn)[2]。
本文重點要介紹的是自2014年,對傳統(tǒng)的BPF進(jìn)行擴(kuò)展進(jìn)化后的BPF。得益于BPF在包過濾上的良好表現(xiàn), 對BPF進(jìn)行徹底的改造,并增加了新的功能,改善了它的性能,這個新版本被命名為eBPF( BPF),新版本的BPF全面兼容并擴(kuò)充了原有BPF的功能。因此,將傳統(tǒng)的BPF重命名為cBPF( BPF),相對應(yīng)的,新版本的BPF則命名為eBPF或直接稱為BPF(后文所有的eBPF,均簡化描述為BPF)。Linux 3.15版本開始實現(xiàn)對eBPF的支持。
BPF針對現(xiàn)代硬件進(jìn)行了優(yōu)化和全新的設(shè)計,使其生成的指令集比cBPF解釋器生成的機(jī)器碼更快。這個擴(kuò)展版本還將BPF VM中的寄存器數(shù)量從兩個32位寄存器增加到10個64位寄存器。寄存器數(shù)量和寄存器寬度的增加為編寫更復(fù)雜的程序提供了可能性,開發(fā)人員可以自由的使用函數(shù)參數(shù)交換更多的信息。這些改進(jìn)使得BPF比原來的cBPF快四倍。這些改進(jìn),主要還是對網(wǎng)絡(luò)過濾器內(nèi)部處理的BPF指令集進(jìn)行優(yōu)化,仍然被限制在內(nèi)核空間中,只有少數(shù)用戶空間中的程序可以編寫B(tài)PF過濾器供內(nèi)核處理,比如和。
除了上述的優(yōu)化之外,BPF最讓人興奮的改進(jìn),是其向用戶空間的開放。開發(fā)者可以在用戶空間,編寫B(tài)PF程序,并將其加在到內(nèi)核空間執(zhí)行。雖然BPF程序看起來更像內(nèi)核模塊,但與內(nèi)核模塊不同的是,BPF程序不需要開發(fā)者重新編譯內(nèi)核,而且保證了在內(nèi)核不崩潰的情況下完成加載操作,著重強(qiáng)調(diào)了安全性和穩(wěn)定性。BPF代碼的貢獻(xiàn)單位主要包括、、Red Hat以及等。
圖l
BPF使得更多的內(nèi)核操作可以通過用戶空間的應(yīng)用程序來完成,這恰恰是與軟件定義的架構(gòu)和理念不謀而合。軟件定義強(qiáng)調(diào)將系統(tǒng)的數(shù)據(jù)平面和控制平面進(jìn)行分離,控制平面實現(xiàn)各種各樣的控制和管理邏輯,而數(shù)據(jù)平面則專注于高效快速的執(zhí)行,控制平面和數(shù)據(jù)平面通過特定的接口或協(xié)議進(jìn)行通信。
因此,筆者認(rèn)為,BPF正是設(shè)計和實現(xiàn)了一種對內(nèi)核進(jìn)行軟件定義( )的方式。控制平面是用戶空間的各種BPF程序,實現(xiàn)BPF程序在內(nèi)核的跟蹤點以及執(zhí)行邏輯;數(shù)據(jù)平面則是內(nèi)核各種操作的執(zhí)行單元,這些跟蹤點可以是一個系統(tǒng)調(diào)用,甚至是一段確定的實現(xiàn)代碼;控制平面和數(shù)據(jù)平面通過bpf()系統(tǒng)調(diào)用進(jìn)行通信,將用戶空間的控制平面邏輯,加在到內(nèi)核空間數(shù)據(jù)平面的準(zhǔn)確位置。
這種軟件定義內(nèi)核的設(shè)計和實現(xiàn),極大的提高了內(nèi)核行為分析與操作的靈活性、安全性和效率,降低了內(nèi)核操作的技術(shù)門檻。尤其在云原生環(huán)境中,對于云原生應(yīng)用的性能提升、可視化監(jiān)控以及安全檢測有著重要的意義。
二、 BPF原理與架構(gòu)
眾所周知,Linux內(nèi)核是一個事件驅(qū)動的系統(tǒng)設(shè)計,這意味著所有的操作都是基于事件來描述和執(zhí)行的。比如打開文件是一種事件、CPU執(zhí)行指令是一種事件、接收網(wǎng)絡(luò)數(shù)據(jù)包是一種事件等等。BPF作為內(nèi)核中的一個子系統(tǒng),可以檢查這些基于事件的信息源,并且允許開發(fā)者編寫并運(yùn)行在內(nèi)核觸發(fā)任何事件時安全執(zhí)行的BPF程序。
圖2BPF在Linux中掛載示例
圖3簡要描述了BPF的架構(gòu)及基本的工作流程。首先,開發(fā)者可以使用C語言(或者等其他高級程序語言)編寫自己的BPF程序,然后通過LLVM或者GNU、Clang等編譯器,將其編譯成BPF字節(jié)碼。Linux提供了一個bpf()系統(tǒng)調(diào)用,通過bpf()系統(tǒng)調(diào)用,將這段編譯之后的字節(jié)碼傳入內(nèi)核空間。
傳入內(nèi)核空間之后的BPF程序,并不是直接就在其指定的內(nèi)核跟蹤點上開始執(zhí)行,而是先通過這個組件,來保證我們傳入的這個BPF程序可以在內(nèi)核中安全的運(yùn)行。經(jīng)過安全檢測之后,Linux內(nèi)核還為BPF字節(jié)碼提供了一個實時的編譯器(Just-In-Time,JIT),JIT將確認(rèn)后的BPF字節(jié)碼編譯為對應(yīng)的機(jī)器碼。這樣就可以在BPF指定的跟蹤點上執(zhí)行我們的操作邏輯了。
圖3BPF架構(gòu)與流程圖那么,用戶空間的應(yīng)用程序怎么樣拿到我們插入到內(nèi)核中的BPF程序產(chǎn)生的數(shù)據(jù)呢?BPF是通過一種MAP的數(shù)據(jù)結(jié)構(gòu)來進(jìn)行數(shù)據(jù)的存儲和管理的,BPF將產(chǎn)生的數(shù)據(jù),通過指定的MAP數(shù)據(jù)類型進(jìn)行存儲,用戶空間的應(yīng)用程序,作為消費者,通過bpf()系統(tǒng)調(diào)用,從MAP數(shù)據(jù)結(jié)構(gòu)中讀取數(shù)據(jù)并進(jìn)行相應(yīng)的存儲和處理。這樣一個完整BPF程序的流程就完成了。三、
下面我們通過一個Hello World例子驅(qū)動級腳本能被檢測嗎,來對上述各個步驟進(jìn)行展開介紹。這個示例將完成下面的操作:當(dāng)內(nèi)核執(zhí)行某一系統(tǒng)調(diào)用時,打印“Hello, BPF World!”字符串。
首先我們先使用C語言編寫一段完成上述功能的BPF代碼.c:
1 #
2 # SEC(NAME) (((NAME), used))
3
4 SEC("http://")
5 int (void *ctx) {
6 char msg[] = "Hello, BPF World!";
7 (msg, (msg));
8 0;
9 }
10
11 char [] SEC("") = "GPL";
首先,我們需要聲明BPF程序什么時候執(zhí)行,這里有一個跟蹤點()的概念,跟蹤點是內(nèi)核二進(jìn)制代碼中的靜態(tài)標(biāo)記,允許開發(fā)人員注入代碼來檢查內(nèi)核的執(zhí)行。代碼的第4行就是指出我們這個BPF程序的跟蹤點是什么。在BPF的語法中,使用SEC標(biāo)識跟蹤點,在本例中,我們將在檢測到執(zhí)行系統(tǒng)調(diào)用時運(yùn)行這個BPF程序。
代碼的5—9行,定義了我們在這個追蹤點需要執(zhí)行的操作,也就是每當(dāng)內(nèi)核檢測到一個程序執(zhí)行另一個程序時,將打印消息“Hello, BPF World!”
然后我們將使用clang將這個程序編譯為成一個ELF二進(jìn)制文件,這是內(nèi)核能夠識別的一種文件格式。clang -O2 - bpf -c .c -o .o。
下面將這個已經(jīng)編譯好的BPF程序加載到內(nèi)核中,現(xiàn)在我們已經(jīng)編譯了第一個BPF程序,我們使用內(nèi)核提供的方法,將上述編譯好的.o加載到內(nèi)核。如下.c。
1 #
2 #
3 # ".h"
4 int main(int argc, char **argv) {
5 if ((".o") != 0) {
6 ("The didn't load the BPF \n");
7 -1;
8 }
9 ();
0;
11 }
使用如下方法編譯我們文件。
TOOLS=/-src//bpf
=/-src/tools/lib
=/-src/tools/perf
=/-src/tools//
clang -o -lelf\
-I${} \
-I${} \
-I${} \
-I${TOOLS} \
${TOOLS}/.c \
.c
然后運(yùn)行sudo ./,我們的BPF程序就已經(jīng)加載到內(nèi)核中了。當(dāng)我們停止這個程序時,上述BPF程序?qū)崿F(xiàn)自動從內(nèi)核中卸載。
四、BPF程序類型
通過上面的Hello World示例,我們已經(jīng)對BPF程序有了一個初步的認(rèn)識,那么接下來我們看一下驅(qū)動級腳本能被檢測嗎,我們都能夠用BPF來做什么?Linux內(nèi)核當(dāng)前提供了對哪些BPF程序類型的支持。
這里可以簡單的將BPF程序的類型分為兩個方面:內(nèi)核追蹤()和內(nèi)核網(wǎng)絡(luò)()。
1內(nèi)核追蹤()
第一類是內(nèi)核跟蹤。開發(fā)者可以通過BPF程序更清晰的了解系統(tǒng)中正在發(fā)生的事情。從前文中的介紹可以看出,BPF可以通過各種類型的追蹤點()訪問與特定程序相關(guān)的內(nèi)存區(qū)域,并從正在運(yùn)行的進(jìn)程中提取信息并執(zhí)行跟蹤。這樣開發(fā)者就可以獲取關(guān)于系統(tǒng)的行為及其所運(yùn)行的硬件的直接信息,甚至還可以直接訪問為每個特定進(jìn)程分配的資源,包括從文件描述符到CPU和內(nèi)存使用情況。
圖4BPF內(nèi)核行為追蹤
BPF對內(nèi)核行為的追蹤,可以通過靜態(tài)的追蹤點,或者是等動態(tài)的追蹤點,實現(xiàn)整個系統(tǒng)的可觀察性(),進(jìn)而可以進(jìn)行系統(tǒng)的性能分析、調(diào)試以及安全的檢測與發(fā)現(xiàn)。
圖
在安全檢測上,我們可以將BPF程序的追蹤點加載到一些關(guān)鍵并且不是很頻繁的內(nèi)核行為上,比如一個新的TCP/UDP會話的創(chuàng)建、啟動了新的進(jìn)程、特權(quán)提升等,這樣就可以通過對這些行為的監(jiān)控,進(jìn)行異常檢測。
圖6BPF實現(xiàn)主機(jī)入侵檢測
2內(nèi)核網(wǎng)絡(luò)()
第二類程序是對內(nèi)核網(wǎng)絡(luò)的操作。BPF程序允許開發(fā)者監(jiān)控并且操作計算機(jī)系統(tǒng)中的網(wǎng)絡(luò)流量,這也是BPF原始設(shè)計時的核心功能點。BPF允許過濾來自網(wǎng)絡(luò)接口的數(shù)據(jù)包,甚至完全拒絕這些數(shù)據(jù)包。不同類型的BPF程序可以加載到內(nèi)核網(wǎng)絡(luò)中不同的處理階段。
比如,開發(fā)者可以在網(wǎng)絡(luò)驅(qū)動程序收到包時立即將BPF程序附加到這一網(wǎng)絡(luò)事件上,并根據(jù)特定的過濾條件,對符合條件的數(shù)據(jù)包進(jìn)行處理。這種數(shù)據(jù)包的處理和過濾可以直接下沉到物理網(wǎng)卡上,利用網(wǎng)卡的處理單元( ),進(jìn)一步降低主機(jī)在數(shù)據(jù)包處理上的資源開銷。
當(dāng)然,這種靈活的數(shù)據(jù)包處理方式有優(yōu)點也有缺點。一方面,當(dāng)收到數(shù)據(jù)包之后,我們在越早的階段處理,可能在資源消耗上越有優(yōu)勢,但是這個時候,內(nèi)核還沒有將足夠的信息提供我們,我們對這個數(shù)據(jù)包的信息了解的就很少,這對下一步的處理決策有著一定的影響。另一方面,我們也可以在網(wǎng)絡(luò)事件傳遞到用戶空間之前將BPF程序加載到網(wǎng)絡(luò)事件上,這時,我們將擁有關(guān)于數(shù)據(jù)包的更多信息,并且有助于做出更明智的決策,但這就需要支付完全處理數(shù)據(jù)包的成本。
這里我們簡單舉個例子,如下圖所示,在容器等虛擬化環(huán)境中,我們可以將BPF程序附著在包括物理和虛擬的網(wǎng)絡(luò)設(shè)備上,這樣就能夠根據(jù)實際的業(yè)務(wù)場景以及網(wǎng)絡(luò)通信需求,實時動態(tài)的設(shè)置和更新網(wǎng)絡(luò)通信規(guī)則,實現(xiàn)對數(shù)據(jù)包的過濾。而這種包過濾,當(dāng)前容器網(wǎng)絡(luò)更多的是通過來實現(xiàn)的,那么一旦規(guī)模達(dá)到一定量級之后,不論是在規(guī)則管理上,還是在資源消耗上,都將帶來巨大的負(fù)擔(dān)和隱患。
圖7BPF實現(xiàn)容器網(wǎng)絡(luò)安全
BPF在網(wǎng)絡(luò)數(shù)據(jù)包的處理上,通常會與Linux內(nèi)核的另外一個重要功能XDP一起來實現(xiàn)。XDP( Data Path)是一個安全的、可編程的、高性能的、內(nèi)核集成的包處理器,它位于Linux網(wǎng)絡(luò)數(shù)據(jù)路徑中,當(dāng)網(wǎng)卡驅(qū)動程序收到包時,就會執(zhí)行BPF程序,XDP程序會在盡可能早的時間點對收到的包進(jìn)行刪除、修改或轉(zhuǎn)發(fā)到網(wǎng)絡(luò)堆棧等操作。XDP程序是通過bpf()系統(tǒng)調(diào)用控制的,使用BPF程序?qū)崿F(xiàn)相應(yīng)的控制邏輯。
圖8BPF+XDP實現(xiàn)網(wǎng)絡(luò)數(shù)據(jù)包過濾五、 BPF工具
當(dāng)前BPF貢獻(xiàn)者以及使用者,已經(jīng)開發(fā)并且開源了許多實用的BPF工具。這將給我們進(jìn)行BPF開發(fā)和使用帶來極大的便利性。
1BCC
前文的介紹中我們提到了,對于一個C語言實現(xiàn)的BPF程序,可以通過Clang、LLVM將其編譯成BPF字節(jié)碼,然后通過加載程序,將BPF字節(jié)碼通過bpf()系統(tǒng)調(diào)用加載到內(nèi)核中。這種用戶動態(tài)的編譯、加載比較麻煩,因此IO Visor開發(fā)實現(xiàn)了一個BPF程序工具包BCC[3]。
BCC(BPF )是高效創(chuàng)建BPF程序的工具包,BCC把上述BPF程序的編譯、加載等功能都集成了起來,提供友好的接口給用戶,進(jìn)而方便用戶的使用。它使用了( + Lua + C++)的混合架構(gòu),底層操作封裝到C++庫中,Lua提供一些輔助功能,對用戶的接口使用提供,和C++之間的調(diào)用使用連接。因為使用了,所有抓回來的數(shù)據(jù)分析和數(shù)據(jù)呈現(xiàn)都非常方便。
除此之外,BCC還提供了一套現(xiàn)成的工具和示例供開發(fā)者使用,下圖展示了當(dāng)前BCC提供的各種類型的工具,當(dāng)我們安裝完BCC之后,進(jìn)入"/usr/share/bcc/tools" 和"/usr/share/bcc//”目錄就可以使用這些工具。
圖9BCC工具集
/usr/share/bcc/tools# ./ -L
, top 10... Ctrl+C to quit.
^C[21:22:45]
TIME (us)
futex 1122 .331
673 .277
poll 219 .042
48 .875
33 .897
wait4 120 .613
read 4177 .764
fsync 4 .128
337 48387.145
2809 25358.704
2其他工具
是一個用于檢查BPF程序和MAP存儲的內(nèi)核實用程序。這個工具在默認(rèn)情況下不會安裝在任何Linux發(fā)行版上,而且它還處于開發(fā)階段,所以需要開發(fā)者編譯最支持Linux內(nèi)核的版本。將隨Linux內(nèi)核5.1版本一起發(fā)布版本。的一個重要功能就是可以掃描系統(tǒng),進(jìn)而了解系統(tǒng)支持了哪些BPF特性、系統(tǒng)中已經(jīng)加載了何種BPF程序等。比如可以查看內(nèi)核的哪個版本支持了哪種BPF程序,或者是否啟用了BPF JIT編譯器等。
[4]是BPF的高級跟蹤語言。它允許開發(fā)者用簡潔的DSL編寫B(tài)PF程序,并將它們保存為腳本,開發(fā)者可以執(zhí)行這些腳本,而不必在內(nèi)核中手動編譯和加載它們。它的靈感來自其他著名的Trace工具,比如awk和,將會是的一個很好的替代品。與直接使用BCC或其他BPF工具編寫程序相比,使用的一個優(yōu)點是,提供了許多不需要自己實現(xiàn)的內(nèi)置功能,比如聚合信息和創(chuàng)建直方圖等。
-trace [5]是命令行的一個非常棒的插件。它可以幫助開發(fā)者在集群中調(diào)度程序,而不必安裝任何附加的包或模塊。它通過使用trace-容器鏡像,通過作業(yè)調(diào)度來實現(xiàn),trace-鏡像中已經(jīng)安裝了運(yùn)行程序所需的所有東西,可以在中下載使用。
圖-trace架構(gòu)
六、總結(jié)
BPF機(jī)制通過在Linux內(nèi)核事件的處理流程上,插入用戶定義的BPF程序,實現(xiàn)對內(nèi)核的軟件定義,極大的提高了內(nèi)核行為分析與操作的靈活性、安全性和效率,降低了內(nèi)核操作的技術(shù)門檻。
Linux容器,作為云原生環(huán)境重要的支撐技術(shù),是Linux內(nèi)核上用于隔離和管理計算機(jī)進(jìn)程的一組特性的抽象,高度依賴了Linux內(nèi)核的底層功能。那么從內(nèi)核的角度來看:
(1)內(nèi)核知道所有的進(jìn)程/線程運(yùn)行情況;
(2)通過,內(nèi)核可以知道 配置的CPU/內(nèi)存/網(wǎng)絡(luò)等資源的配額以及使用情況;
(3)從的層面,內(nèi)核可以知道 配置的進(jìn)程隔離情況、網(wǎng)絡(luò)堆棧的情況、容器用戶等眾多的信息;
(4)還可以知道容器環(huán)境內(nèi)網(wǎng)絡(luò)的連接以及網(wǎng)絡(luò)流量的情況;
(5)容器對系統(tǒng)調(diào)用、內(nèi)核功能使用等信息。
因此,對于云原生環(huán)境來講,如果能夠拿到上述內(nèi)核所擁有的種種信息,對于云原生應(yīng)用的性能提升、可視化監(jiān)控以及安全檢測有著重要的意義。
參考文獻(xiàn):[1]. [2]. [3]. [4]. [5]. [6].
關(guān)于星云實驗室
星云實驗室專注于云計算安全、解決方案研究與虛擬化網(wǎng)絡(luò)安全問題研究。基于IaaS環(huán)境的安全防護(hù),利用SDN/NFV等新技術(shù)和新理念,提出了軟件定義安全的云安全防護(hù)體系。承擔(dān)并完成多個國家、省、市以及行業(yè)重點單位創(chuàng)新研究課題,已成功孵化落地綠盟科技云安全解決方案
添加好友,備注“進(jìn)群”,加入容器安全技術(shù)交流群,通過后會拉您入群。