跳轉到

eBPF:動態注入核心的沙盒引擎#

一九九二年,勞倫斯伯克利實驗室的 Steven McCanne 與 Van Jacobson 發表一篇論文,題目平實——《The BSD Packet Filter》。當時的封包過濾靠堆疊式過濾器,每個封包要經過層層判斷,開銷驚人。他們的洞見是:把過濾條件編譯成一個小型虛擬機的位元組碼,在核心內以一條 register-based 機器直接執行,只把匹配的封包複製到使用者態。這就是 BPF(Berkeley Packet Filter)。它的設計哲學有兩條鐵律:程式必須在有限步驟內終止,且絕不能讀寫核心記憶體之外的地方。 tcpdump 的 -tcp port 80 背後,就是一段 BPF 位元組碼。這兩條鐵律在二十多年後,成為一場核心革命的憲法基礎——只是當時無人預見。

革命的主角是 Alexei Starovoitov,Red Hat 出身的核心網路開發者。二〇一三年前後,他與网络副手 Daniel Borkmann 著手擴展 BPF:新的 eBPF 是一個 64 位元、十個暫存器的虛擬機,擁有 JIT 編譯(位元組碼直接編成原生 x86 指令,性能接近原生程式)、map 機制(核心與使用者態共享的鍵值儲存)、以及可擴充的輔助函式介面。二〇一四年,eBPF 隨 Linux 3.18 合入主線。最初的用途仍是封包過濾,但 Starovoitov 的野心顯而易見:這不是封包過濾器,這是「核心內的 JVM」。此後數年,掛載點如枝蔓生長:kprobe 可攔截任意核心函式、tracepoint 可訂閱核心事件、XDP 讓封包在網卡驅動層就被 eBPF 程式處理、tc 掛在流量控制層、後來連系統呼叫與 cgroup 都能掛載。核心第一次變成「可編程的」——不重啟、不重編、不載入危險模組。

讓這一切安全的是 verifier(驗證器),eBPF 最精巧也最具爭議的發明。每支 eBPF 程式載入前,verifier 會靜態模擬執行:檢查每一條路徑上記憶體存取的邊界、證明程式必然終止(禁止無界迴圈,直到二〇二一年引入有界迴圈)、追蹤指標與純值的型別、禁止未初始化的讀取。verifier 本質上是把形式驗證的簡化版嵌入核心載入路徑——它不是完美的證明器,卻把「寫核心程式」的門檻從核心開發者下放到普通系統工程師。代價是 verifier 的程式碼日益龐雜,錯誤與繞過手法層出不窮(CVE 與 security 論戰不斷),甚至二〇二一年前後 Google 與核心社群就 eBPF 安全性爆發公開爭執。這是典範轉移的必然陣痛:核心的可程式化,意味著核心攻擊面的擴張。

可觀測性是 eBPF 第一個征服的疆域。二〇一五年,IO Visor 專案推出 BCC,讓工程師用 Python 前端撰寫 C 語言 eBPF 程式;Netflix 的效能教父 Brendan Gregg 隨即成為最响亮的布道者——他在 Netflix 的生產叢集上,用 eBPF 工具即時回答「延遲消耗在哪個核心函式」這類以前需要重新編譯核心才能回答的問題。二〇二〇年問世的 bpftrace 更把核心追蹤變成一行腳本。Gregg 的著作《BPF Performance Tools》與他那張廣為流傳的全景圖,把「動態追蹤」從學術玩具變成 SRE 的日常武器。這是文化層面的變革:核心不再是黑盒,生產系統的除錯從「重現問題」轉向「直接觀測問題」。

網路是第二個戰場。二〇一七年,前 Cisco 工程師 Thomas Graf 創立的 Cilium 專案用 eBPF 重寫容器網路:連線追蹤、負載平衡、安全策略全部在核心內以 eBPF 實作,繞過了 iptables 的 O(n) 規則鏈。更戲劇性的故事在 Cloudflare:二〇一八年,他們用 XDP 對抗史上最大的 DDoS 攻擊之一——eBPF 程式直接在網卡驅動層丟棄惡意封包,單機每秒處理數千萬封包,垃圾流量根本沒機會觸及核心協定堆疊。傳統上這需要專用硬體,如今一段五百行的 eBPF 程式就足夠。meta 的 Katran、Google 的負載平衡、AWS 的 NAT 邏輯,先後改用 eBPF。網路資料平面的主導權,正在從「核心內建協定堆疊」轉向「可編程的 eBPF 管線」。

eBPF 的成功也製造了核心文化的張力。核心社群信奉「一切都該走 upstream 主線」,但 eBPF 的爆炸性成長讓人擔心核心淪為「eBPF 執行時」:功能不再以 C 程式碼進主線,而是以 eBPF 程式漂浮在使用者態。Linus Torvalds 本人態度曖昧——他痛恨不穩定的介面,但 eBPF 維持了「程式可載入、核心 ABI 不變」的承諾。二〇二一年,eBPF 的直接網路存取甚至促使核心開發者重新檢討 netfilter 的未來。爭論的本質是制度性的:誰有權定義核心的能力邊界?是 Linus 與 maintainer 們的品味,還是驗證器的演算法?eBPF 給出的答案是第三條路:把邊界的裁判權交給機器(verifier),人類只需遵守它。從 McCanne 與 Jacobson 為了省一次記憶體複製而發明的封包過濾器,到今天重新定義核心可程式性的沙盒引擎,eBPF 證明了一件事:最有力的系統抽象,是讓人們安全地做以前需要特權才能做的事——而「安全」二字的工程實現,正是 verifier 那幾萬行程式碼。