Linux 新技術基石 eBPF and XDP
hi,大家好,今天給大家分享的是 Linux 最近特別火的新技術,當前最流行 Linux 內核技術是什麼?那就是 eBPF 技術,著名開源的 Cilium 把 eBPF 技術帶飛,國內 Linux,容器雲原生等領域愛好者也燃起一股學習 eBPF 的熱潮。
新技術出現的歷史原因
廉頗老矣,尚能飯否
iptables/netfilter
iptables/netfilter 是上個時代 Linux 網絡提供的優秀的防火牆技術,擴展性強,能夠滿足當時大部分網絡應用需求,如果不知道 iptables/netfilter 是什麼,請參考之前文章:一個奇葩的網絡問題,把技術磚家 "搞蒙了" ,裏面對 iptables/netfilter 技術有詳細介紹。
但該框架也存在很多明顯問題:
- 路徑太長
netfilter 框架在 IP 層,報文需要經過鏈路層,IP 層才能被處理,如果是需要丟棄報文,會白白浪費很多 CPU 資源,影響整體性能;
- O(N) 匹配
如上圖所示,極端情況下,報文需要依次遍歷所有規則,才能匹配中,極大影響報文處理性能;
- 規則太多
netfilter 框架類似一套可以自由添加策略規則專家系統,並沒有對添加規則進行合併優化,這些都嚴重依賴操作人員技術水平,隨着規模的增大,規則數量 n 成指數級增長,而報文處理又是 0(n)複雜度,最終性能會直線下降。
內核協議棧
隨着互聯網流量越來愈大, 網卡性能越來越強,Linux 內核協議棧在 10Mbps/100Mbps 網卡的慢速時代是沒有任何問題的,那個時候應用程序大部分時間在等網卡送上來數據。
現在到了 1000Mbps/10Gbps/40Gbps 網卡的時代,數據被很快地收入,協議棧複雜處理邏輯,效率捉襟見肘,把大量報文堵在內核裏。
各類鏈表在多 CPU 環境下的同步開銷。
不可睡眠的軟中斷路徑過長。
sk_buff 的分配和釋放。
內存拷貝的開銷。
上下文切換造成的 cache miss。
…
於是,內核協議棧各種優化措施應着需求而來:
網卡 RSS,多隊列。
中斷線程化。
分割鎖粒度。
Busypoll。
…
但卻都是見招拆招,治標不治本。問題的根源不是這些機制需要優化,而是這些機制需要推倒重構。蒸汽機車剛出來的時候,馬車伕爲了保持競爭優勢,不是去換一匹昂貴的快馬,而是賣掉馬去買一臺蒸汽機裝上。基本就是這個意思。
重構的思路很顯然有兩個:
upload 方法:別讓應用程序等內核了,讓應用程序自己去網卡直接拉數據。
offload 方法:別讓內核處理網絡邏輯了,讓網卡自己處理。
總之,繞過內核就對了,內核協議棧揹負太多歷史包袱。
DPDK 讓用戶態程序直接處理網絡流,bypass 掉內核,使用獨立的 CPU 專門幹這個事。
XDP 讓灌入網卡的 eBPF 程序直接處理網絡流,bypass 掉內核,使用網卡 NPU 專門幹這個事。
如此一來,內核協議棧就不再參與數據平面的事了,留下來專門處理諸如路由協議,遠程登錄等控制平面和管理平面的數據流。
改善 iptables/netfilter 的規模瓶頸,提高 Linux 內核協議棧 IO 性能,內核需要提供新解決方案,那就是 eBPF/XDP 框架,讓我們來看一看,這套框架是如何解決問題的。
eBPF 到底是什麼?
eBPF 的歷史
BPF 是 Linux 內核中高度靈活和高效的類似虛擬機的技術,允許以安全的方式在各個掛鉤點執行字節碼。它用於許多 Linux 內核子系統,最突出的是網絡、跟蹤和安全(例如沙箱)。
BPF 架構
BPF 是一個通用目的 RISC 指令集,其最初的設計目標是:用 C 語言的一個子集編 寫程序,然後用一個編譯器後端(例如 LLVM)將其編譯成 BPF 指令,稍後內核再通 過一個位於內核中的(in-kernel)即時編譯器(JIT Compiler)將 BPF 指令映射成處理器的原生指令(opcode ),以取得在內核中的最佳執行性能。
BPF 指令
儘管 BPF 自 1992 年就存在,擴展的 Berkeley Packet Filter (eBPF) 版本首次出現在 Kernel3.18 中,如今被稱爲 “經典”BPF (cBPF) 的版本已過時。許多人都知道 cBPF 是 tcpdump 使用的數據包過濾語言。現在 Linux 內核只運行 eBPF,並且加載的 cBPF 字節碼在程序執行之前被透明地轉換爲內核中的 eBPF 表示。除非指出 eBPF 和 cBPF 之間的明確區別,一般現在說的 BPF 就是指 eBPF。
eBPF 總體設計
-
BPF 不僅通過提供其指令集來定義自己,而且還通過提供圍繞它的進一步基礎設施,例如充當高效鍵 / 值存儲的映射、與內核功能交互並利用內核功能的輔助函數、調用其他 BPF 程序的尾調用、安全加固原語、用於固定對象(地圖、程序)的僞文件系統,以及允許將 BPF 卸載到網卡的基礎設施。
-
LLVM 提供了一個 BPF 後端,因此可以使用像 clang 這樣的工具將 C 編譯成 BPF 目標文件,然後可以將其加載到內核中。BPF 與 Linux 內核緊密相連,允許在不犧牲本機內核性能的情況下實現完全可編程。
eBPF 總體設計包括以下幾個部分:
eBPF Runtime
-
安全保障 : eBPF 的 verifier 將拒絕任何不安全的程序並提供沙箱運行環境
-
持續交付: 程序可以更新在不中斷工作負載的情況下
-
高性能:JIT 編譯器可以保證運行性能
eBPF Hooks
- 內核函數 (kprobes)、用戶空間函數 (uprobes)、系統調用、fentry/fexit、跟蹤點、網絡設備 (tc/xdp)、網絡路由、TCP 擁塞算法、套接字(數據面)
eBPF Maps
Map 類型
-
Hash tables, Arrays
-
LRU (Least Recently Used)
-
Ring Buffer
-
Stack Trace
-
LPM (Longest Prefix match)
作用
-
程序狀態
-
程序配置
-
程序間共享數據
-
和用戶空間共享狀態、指標和統計
eBPF Helpers
有哪些 Helpers?
-
隨機數
-
獲取當前時間
-
map 訪問
-
獲取進程 / cgroup 上下文
-
處理網絡數據包和轉發
-
訪問套接字數據
-
執行尾調用
-
訪問進程棧
-
訪問系統調用參數
-
...
eBPF Tail and Function Calls
尾調用有什麼用?
● 將程序鏈接在一起
● 將程序拆分爲獨立的邏輯組件
● 使 BPF 程序可組合
函數調用有什麼用?
● 重用內部的功能程序
● 減少程序大小(避免內聯)
eBPF JIT Compiler
-
確保本地執行性能而不需要了解 CPU
-
將 BPF 字節碼編譯到 CPU 架構特定指令集
eBPF 可以做什麼?
eBPF 開源 Projects
Cilium
-
Cilium 是開源軟件,用於 Linux 容器管理平臺(如 Docker 和 Kubernetes)部署的服務之間的透明通信和提供安全隔離保護。
-
Cilium 基於微服務的應用,使用 HTTP、gRPC、Kafka 等輕量級協議 API 相互通信。
- Cilium 的基於 eBPF 的新 Linux 內核技術,它能夠在 Linux 本身中動態插入強大的安全可見性和控制邏輯。由於 eBPF 在 Linux 內核中運行,因此可以在不更改應用程序代碼或容器配置的情況下應用和更新 Cilium 安全策略。
Cilium 在它的 datapath 中重度使用了 BPF 技術
-
Cilium 是位於 Linux kernel 與容器編排系統的中間層。向上可以爲容器配置網絡,向下可以向 Linux 內核生成 BPF 程序來控制容器的安全性和轉發行爲。
-
利用 Linux BPF,Cilium 保留了透明地插入安全可視性 + 強制執行的能力,但這種方式基於服務 /pod/ 容器標識(與傳統系統中的 IP 地址識別相反),並且可以根據應用層進行過濾 (例如 HTTP)。因此,通過將安全性與尋址分離,Cilium 不僅可以在高度動態的環境中應用安全策略,而且除了提供傳統的第 3 層和第 4 層分割之外,還可以通過在 HTTP 層運行來提供更強的安全隔離。
-
BPF 的使用使得 Cilium 能夠以高度可擴展的方式實現以上功能,即使對於大規模環境也不例外。
對比傳統容器網絡(採用 iptables/netfilter):
-
eBPF 主機路由允許繞過主機命名空間中所有的 iptables 和上層網絡棧,以及穿過 Veth 對時的一些上下文切換,以節省資源開銷。網絡數據包到達網絡接口設備時就被儘早捕獲,並直接傳送到 Kubernetes Pod 的網絡命名空間中。在流量出口側,數據包同樣穿過 Veth 對,被 eBPF 捕獲後,直接被傳送到外部網絡接口上。eBPF 直接查詢路由表,因此這種優化完全透明。
-
基於 eBPF 中的 kube-proxy 網絡技術正在替換基於 iptables 的 kube-proxy 技術,與 Kubernetes 中的原始 kube-proxy 相比,eBPF 中的 kuber-proxy 替代方案具有一系列重要優勢,例如更出色的性能、可靠性以及可調試性等等。
BCC(BPF Compiler Collection)
BCC 是一個框架,它使用戶能夠編寫嵌入其中的 eBPF 程序的 Python 程序。該框架主要針對涉及應用程序和系統分析 / 跟蹤的用例,其中 eBPF 程序用於收集統計信息或生成事件,用戶空間中的對應部分收集數據並以人類可讀的形式顯示。運行 python 程序將生成 eBPF 字節碼並將其加載到內核中。
bpftrace
bpftrace 是一種用於 Linux eBPF 的高級跟蹤語言,可在最近的 Linux 內核 (4.x) 中使用。bpftrace 使用 LLVM 作爲後端將腳本編譯爲 eBPF 字節碼,並利用 BCC 與 Linux eBPF 子系統以及現有的 Linux 跟蹤功能進行交互:內核動態跟蹤 (kprobes)、用戶級動態跟蹤 (uprobes) 和跟蹤點. bpftrace 語言的靈感來自 awk、C 和前身跟蹤器,例如 DTrace 和 SystemTap。
eBPF Go 庫
eBPF Go 庫提供了一個通用的 eBPF 庫,它將獲取 eBPF 字節碼的過程與 eBPF 程序的加載和管理解耦。eBPF 程序通常是通過編寫高級語言創建的,然後使用 clang/LLVM 編譯器編譯爲 eBPF 字節碼。
libbpf C/C++ 庫
libbpf 庫是一個基於 C/C++ 的通用 eBPF 庫,它有助於解耦從 clang/LLVM 編譯器生成的 eBPF 目標文件加載到內核中,並通過提供易於使用的庫 API 來抽象與 BPF 系統調用的交互應用程序。
那 XDP 又是什麼?
XDP 的全稱是:eXpress Data Path
XDP 是 Linux 內核中提供高性能、可編程的網絡數據包處理框架。
XDP 整體框架
-
直接接管網卡的 RX 數據包(類似 DPDK 用戶態驅動)處理;
-
通過運行 BPF 指令快速處理報文;
-
和 Linux 協議棧無縫對接;
XDP 總體設計
XDP 總體設計包括以下幾個部分:
XDP 驅動
網卡驅動中 XDP 程序的一個掛載點,每當網卡接收到一個數據包就會執行這個 XDP 程序;XDP 程序可以對數據包進行逐層解析、按規則進行過濾,或者對數據包進行封裝或者解封裝,修改字段對數據包進行轉發等;
BPF 虛擬機
並沒有在圖裏畫出來,一個 XDP 程序首先是由用戶編寫用受限制的 C 語言編寫的,然後通過 clang 前端編譯生成 BPF 字節碼,字節碼加載到內核之後運行在 eBPF 虛擬機上,虛擬機通過即時編譯將 XDP 字節碼編譯成底層二進制指令;eBPF 虛擬機支持 XDP 程序的動態加載和卸載;
BPF maps
存儲鍵值對,作爲用戶態程序和內核態 XDP 程序、內核態 XDP 程序之間的通信媒介,類似於進程間通信的共享內存訪問;用戶態程序可以在 BPF 映射中預定義規則,XDP 程序匹配映射中的規則對數據包進行過濾等;XDP 程序將數據包統計信息存入 BPF 映射,用戶態程序可訪問 BPF 映射獲取數據包統計信息;
BPF 程序校驗器
XDP 程序肯定是我們自己編寫的,那麼如何確保 XDP 程序加載到內核之後不會導致內核崩潰或者帶來其他的安全問題呢?程序校驗器就是在將 XDP 字節碼加載到內核之前對字節碼進行安全檢查,比如判斷是否有循環,程序長度是否超過限制,程序內存訪問是否越界,程序是否包含不可達的指令;
XDP Action
XDP 用於報文的處理,支持如下 action:
enum xdp_action {
XDP_ABORTED = 0,
XDP_DROP,
XDP_PASS,
XDP_TX,
XDP_REDIRECT,
};
-
XDP_DROP:在驅動層丟棄報文,通常用於實現 DDos 或防火牆
-
XDP_PASS:允許報文上送到內核網絡棧,同時處理該報文的 CPU 會分配並填充一個 skb,將其傳遞到 GRO 引擎。之後的處理與沒有 XDP 程序的過程相同。
-
XDP_TX:從當前網卡發送出去。
-
XDP_REDIRECT:從其他網卡發送出去。
-
XDP_ABORTED:表示程序產生了異常,其行爲和 XDP_DROP 相同,但 XDP_ABORTED 會經過 trace_xdp_exception tracepoint,因此可以通過 tracing 工具來監控這種非正常行爲。
AF_XDP
AF_XDP 是爲高性能數據包處理而優化的地址族,AF_XDP 套接字使 XDP 程序可以將幀重定向到用戶空間應用程序中的內存緩衝區。
XDP 設計原則
-
XDP 專爲高性能而設計。它使用已知技術並應用選擇性約束來實現性能目標
-
XDP 還具有可編程性。無需修改內核即可即時實現新功能
-
XDP 不是內核旁路。它是內核協議棧的快速路徑
-
XDP 不替代 TCP/IP 協議棧。與協議棧協同工作
-
XDP 不需要任何專門的硬件。它支持網絡硬件的少即是多原則
XDP 技術優勢
及時處理
-
在網絡協議棧前處理,由於 XDP 位於整個 Linux 內核網絡軟件棧的底部,能夠非常早地識別並丟棄攻擊報文,具有很高的性能。可以改善 iptables 協議棧丟包的性能瓶頸
-
DDIO
-
Packeting steering
-
輪詢式
高性能優化
-
無鎖設計
-
批量 I/O 操作
-
不需要分配 skbuff
-
支持網絡卸載
-
支持網卡 RSS
指令虛擬機
-
規則優化,編譯成精簡指令,快速執行
-
支持熱更新,可以動態擴展內核功能
-
易編程 - 高級語言也可以間接在內核運行
-
安全可靠,BPF 程序先校驗後執行,XDP 程序沒有循環
可擴展模型
-
支持應用處理(如應用層協議 GRO)
-
支持將 BPF 程序卸載到網卡
-
BPF 程序可以移植到用戶空間或其他操作系統
可編程性
-
包檢測,BPF 程序發現的動作
-
靈活(無循環)協議頭解析
-
可能由於流查找而有狀態
-
簡單的包字段重寫(encap/decap)
XDP 工作模式
XDP 有三種工作模式,默認是 native
(原生)模式,當討論 XDP 時通常隱含的都是指這 種模式。
-
Native XDP
默認模式,在這種模式中,XDP BPF 程序直接運行在網絡驅動的早期接收路徑上( early receive path)。
-
Offloaded XDP
在這種模式中,XDP BPF 程序直接 offload 到網卡。
-
Generic XDP
對於還沒有實現 native 或 offloaded XDP 的驅動,內核提供了一個 generic XDP 選 項,這種設置主要面向的是用內核的 XDP API 來編寫和測試程序的開發者,對於在生產環境使用 XDP,推薦要麼選擇 native 要麼選擇 offloaded 模式。
XDP vs DPDK
相對於 DPDK,XDP:
優點
-
無需第三方代碼庫和許可
-
同時支持輪詢式和中斷式網絡
-
無需分配大頁
-
無需專用的 CPU
-
無需定義新的安全網絡模型
缺點
注意 XDP 的性能提升是有代價的,它犧牲了通用型和公平性
-
XDP 不提供緩存隊列(qdisc),TX 設備太慢時直接丟包,因而不要在 RX 比 TX 快的設備上使用 XDP
-
XDP 程序是專用的,不具備網絡協議棧的通用性
如何選擇?
-
內核延伸項目,不想 bypass 內核的下一代高性能方案;
-
想直接重用內核代碼;
-
不支持 DPDK 程序環境;
XDP 適合場景
-
DDoS 防禦
-
防火牆
-
基於 XDP_TX 的負載均衡
-
網絡統計
-
流量監控
-
棧前過濾 / 處理
-
...
XDP 例子
下面是一個最小的完整 XDP 程序,實現丟棄包的功能(xdp-example.c
):
#include <linux/bpf.h>
#ifndef __section
# define __section(NAME) \
__attribute__((section(NAME), used))
#endif
__section("prog")
int xdp_drop(struct xdp_md *ctx)
{
return XDP_DROP;
}
char __license[] __section("license") = "GPL";
用下面的命令編譯並加載到內核:
$ clang -O2 -Wall -target bpf -c xdp-example.c -o xdp-example.o
$ ip link set dev em1 xdp obj xdp-example.o
以上命令將一個 XDP 程序 attach 到一個網絡設備,需要是 Linux 4.11 內核中支持 XDP 的設備,或者 4.12+ 版本的內核。
最後
eBPF/XDP 作爲 Linux 革新技術正在悄悄改變着 Linux 網絡發展模式。
eBPF 正在將 Linux 內核轉變爲微內核,越來越多的新內核功能採用 eBPF 實現,讓新增內核功能更加快捷高效。
總體而言,基於業界基準測試結果,eBPF 顯然是解決具有挑戰性的雲原生需求的最佳技術。
參考 & 延伸閱讀
The eXpress Data Path:
Fast Programmable Packet Processing in the Operating System Kernel
https://docs.cilium.io/en/v1.6/bpf/
bpf-rethinkingthelinuxkernel-200303183208
https://ebpf.io/what-is-ebpf/
https://www.kernel.org/doc/html/latest/networking/af_xdp.html
https://cilium.io/blog/2021/05/11/cni-benchmark
https://blog.csdn.net/dog250/article/details/107243696
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/VmDfYDVlz7PVN6sz6HrIgg