伯克利包過濾器(英語:Berkeley Packet Filter,縮寫 BPF),是類Unix系統上數據鏈路層的一種原始接口,提供原始鏈路層封包的收發。除此之外,如果網卡驅動支持混雜模式,那麼它可以讓網卡處於此種模式,這樣可以收到網絡上的所有包,不管他們的目的地是不是所在主機

另外,BPF支持過濾數據包——用戶態的進程可以提供一個過濾程序來聲明它想收到哪些數據包。通過這種過濾可以避免從操作系統內核向用戶態複製其他對用戶態程序無用的數據包,從而極大地提高性能。

BPF有時也只表示過濾機制,而不是整個接口。一些系統,比如Linux和Tru64 Unix,提供了數據鏈路層的原始接口,而不是BPF的接口,但使用了BPF的過濾機制。

BSD 內核實現例程如 bpf_mtap()bpf_tap(),以BPF_MTAP()BPF_TAP()宏定義的形式進行包裹由網卡驅動(以及偽驅動pseudo-drivers) 向BPF機製發送進出的封包。

歷史

原始的論文由Steven McCanne 和 Van Jacobson於1992年在勞倫斯伯克利國家實驗室工作時編寫,於1993年在San Diego舉辦的USENIX冬季會議上正式發表。

背景

許多版本的Unix操作系統提供了用於捕獲數據包的設施,使得監控當前網絡情況成為了可能。但是,因為網絡監控程序運行在用戶態,數據包必須被拷貝來通過內核與用戶態之間的邊界。可以通過使用一種被稱為「數據包過濾器」的內核代理來減少拷貝的數量,它會儘量早地丟棄不想要的數據包。早先的數據包過濾器被實現為基於棧的虛擬機,在RISC CPU上性能不佳。BPF使用了一種新的基於寄存器(Register)的虛擬機,在性能上有顯著提升。[1]

過濾

BPF的過濾功能是以對於BPF虛擬機機器語言的一種解釋器的形式實現的,使用這種語言編寫的程序可以抓取數據包,對數據包中的數據採取算術操作,並將結果與常量或數據包中的數據或結果中的測試位比較,根據比較的結果決定接受還是拒絕封包。

傳統的Unix BPF實現能夠被用於用戶態,儘管它是為內核態編寫。這是通過編譯時的條件預處理完成的。

優化

一些項目使用了不同以往的BPF指令集或者執行方法。

包括FreeBSD和WinPcap在內的一些平台,使用即時編譯(JIT)編譯器來把BPF指令轉換為原始字節碼,以進一步提高性能。Linux有一個BPF JIT編譯器,但被默認禁用。

此虛擬機語言的內核態解釋器則被用於其他操作系統的原始數據鏈路機制,例如Tru64 Unix系統,以及Linux內核中的套接字過濾器,和WinPcap數據包抓取機制。

用戶態解釋器由實現了pcap API的libpcap/WinPcap提供,因此,在對此過濾機制沒有內核態支持的系統上抓取數據包時,數據包可以在內核態過濾,使用pcap API的代碼可以工作於此兩種模式;在使用用戶態過濾的系統上,所有數據包由內核態複製到用戶態,包括將被過濾出去的封包。這種解釋器也可以用於包含由pcap抓取的封包的文件。

2007年,Robert Watson英語Robert Watson (computer scientist)與Christian Peron為FreeBSD操作系統中BPF的實現加入了zero-copy buffer extension,使得驅動程序中斷處理器中的內核封包抓取能直接向用戶內存寫,以避免BPF設備收到的所有封包數據的兩次複製需要,一份副本存在於用戶進程的接收路徑中,這保證了不同BPF設備調用者的獨立性,同時使得只把封包頭部放入BPF緩衝區,而不是複製整個封包數據。

Will Drewry為seccomp(安全計算)系統調用策略添加了BPF過濾器,這也是BPF第一次在網絡領域之外的使用。[2]

eBPF

從3.18版本開始,Linux 內核提供了一種擴展的BPF虛擬機,被稱為「extended BPF」,簡稱為eBPF。它能夠被用於非網絡相關的功能,比如附在不同的tracepoints上,從而獲取當前內核運行的許多信息。[3]

傳統的BPF,現在被稱為cBPF(classical BPF)。

eBPF由Alexei Starovoitov在PluMgrid工作時設計,這家公司專注於研究新的方法來設計軟件定義網絡解決方案。在它只是一個提議時,Daniel Borkmann——Red Hat公司的內核工程師,幫助修改使得它能夠進入內核代碼並完全替代已有的BPF實現。這是二十年來BPF首次主要的更新,使得BPF成為了一個通用的虛擬機。[2]

eBPF被Linux內核合併的事件線如下[2]

  • 2014年3月。eBPF補丁被合併到Linux內核。
  • 2014年6月。JIT組件被合併到內核3.15版本。
  • 2014年12月。bpf系統調用被合併到內核3.18版本。
  • 在後來的Linux 4.x系列版本中又添加了對於kprobes、uprobes、tracepoints以及perf_events的支持。

因為eBPF虛擬機使用的是類似於匯編語言的指令,對於程序編寫來說直接使用難度非常大。和將C語言生成匯編語言類似,現在的編譯器正在逐步完善從更高級的語言生成BPF虛擬機使用的指令。LLVM在3.7版本開始支持BPF作為後端輸出。[4]GCC 10也將會支持BPF作為後端。[5][6]BCC是IOVisor項目下的編譯器工具集,用於創建內核跟蹤(tracing)工具。[7]bpftrace是為eBPF設計的高級跟蹤語言,在Linux內核(4.x)中提供。[8]

eBPF現在被應用於網絡、跟蹤、內核優化、硬件建模等領域。[9]

安全

Spectre攻擊可以利用Linux內核的eBPF JIT編譯器來從其它內核進程提取信息。[10]

參考文獻

  1. ^ McCanne, Steven; Jacobson, Van. The BSD Packet Filter: A New Architecture for User-level Packet Capture. Proceedings of the USENIX Winter 1993 Conference Proceedings on USENIX Winter 1993 Conference Proceedings. USENIX'93 (Berkeley, CA, USA: USENIX Association). 1993: 2–2. 
  2. ^ 2.0 2.1 2.2 Gregg, Brendan. Chapter 2. Technology Background. BPF Performance Tools. Addison Wesley. 2019-11-04 [2019-10-04]. ISBN 9780136554820. (原始內容存檔於2019-10-04). 
  3. ^ Extending extended BPF [LWN.net]. lwn.net. [2019-10-02]. (原始內容存檔於2019-04-24). 
  4. ^ BPF Backend Merged Into LLVM To Make Use Of New Kernel Functionality - Phoronix. www.phoronix.com. [2019-10-04]. (原始內容存檔於2020-01-24). 
  5. ^ Oracle Is Aiming To Contribute An eBPF Backend To The GCC 10 Compiler - Phoronix. www.phoronix.com. [2019-10-04]. (原始內容存檔於2019-06-22). 
  6. ^ [PATCH 0/8] eBPF support for GCC [LWN.net]. lwn.net. [2019-10-04]. 
  7. ^ BCC. IO Visor Project. [2019-10-04]. (原始內容存檔於2020-06-10) (美國英語). 
  8. ^ bfptrace(Github), IO Visor Project, 2019-10-04 [2019-10-05], (原始內容存檔於2019-10-02) 
  9. ^ Alexei Starovoitov. BPF - in-kernel virtual machine. 2015-02-23. 
  10. ^ Ben. Project Zero: Reading privileged memory with a side-channel. Project Zero. 2018-01-03 [2019-10-03]. (原始內容存檔於2019-10-01). 

外部連結