伯克利包過濾器(英語: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]

從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). 

外部連結

編輯