譯者序
本文翻譯自 2018 年 ACM CoNEXT
大會(huì)上的一篇文章:
The eXpress Data Path: Fast Programmable Packet Processing in the Operating System Kernel
作者陣容豪華,,包括來(lái)自 Cilium 的 Daniel Borkmann,、John Fastabend 等。
論文引用信息:
Toke H?iland-J?rgensen, Jesper Dangaard Brouer, Daniel Borkmann, John Fastabend, Tom Herbert, David Ahern, and David Miller. 2018. The eXpress Data Path: Fast Programmable Packet Processing in the Operating System Kernel. In CoNEXT ’18: International Conference on emerging Networking EXperiments and Technologies, December 4–7, 2018, Heraklion, Greece. ACM, New York, NY, USA, 13 pages. https:///10.1145/3281411.3281443
由于譯者水平有限,,本文不免存在遺漏或錯(cuò)誤之處。如有疑問(wèn),,請(qǐng)查閱原文,。
以下是譯文,。
譯者序
摘要
1 引言
1.1 現(xiàn)有方案(kernel bypass)存在的問(wèn)題
1.2 新方案:給內(nèi)核網(wǎng)絡(luò)棧添加可編程能力
1.3 新方案(XDP)的優(yōu)點(diǎn)
1.4 本文組織結(jié)構(gòu)
2 相關(guān)工作
2.1 用戶態(tài)輪詢 vs. XDP
2.2 內(nèi)核模塊 vs. XDP
2.3 可編程硬件 vs. XDP
2.4 小結(jié)
3 XDP 設(shè)計(jì)
唯一加載入口:bpf()
系統(tǒng)調(diào)用
校驗(yàn)器工作原理:two-pass DAG
內(nèi)存越界和空指針檢查:職責(zé)上移到程序自身/開(kāi)發(fā)者
跟蹤數(shù)據(jù)訪問(wèn)操作和值范圍
不同類型數(shù)據(jù)的校驗(yàn)信息來(lái)源(source of truth)
校驗(yàn)器的目的
在設(shè)備驅(qū)動(dòng)中執(zhí)行,無(wú)需上下文切換
在軟件最早能處理包的位置執(zhí)行,,性能最優(yōu)
XDP 程序典型執(zhí)行流
3.0 XDP 系統(tǒng)架構(gòu)
3.1 XDP driver hook
3.2 eBPF 虛擬機(jī)
3.3 BPF maps
3.4 eBPF verifier
3.5 XDP 程序示例
3.6 小結(jié)
4 性能評(píng)估
XDP 未做底層代碼優(yōu)化
通用目的操作系統(tǒng),,首要目標(biāo):更好的擴(kuò)展和配置,,而非極致性能
轉(zhuǎn)發(fā)吞吐(pps)
轉(zhuǎn)發(fā)延遲
4.1 直接棄包(packet drop)性能
4.2 CPU Usage
4.3 包轉(zhuǎn)發(fā)性能
4.4 討論:XDP 性能與 DPDK 還有差距的原因
5 真實(shí)場(chǎng)景使用案例
Facebook Katran
性能
模擬 Cloudflare 防御架構(gòu)
程序邏輯
性能
內(nèi)核數(shù)據(jù)平面 & 控制平面(BIRD/FRR)
XDP:直接查詢內(nèi)核路由表并轉(zhuǎn)發(fā)
測(cè)試:XDP routing 全球 BGP 路由表
性能:2.5x
5.1 案例一:軟件路由(software routing)
5.2 案例二:Inline DoS Mitigation
5.3 案例三:負(fù)載均衡(load balancing)
6 XDP 的未來(lái)方向
校驗(yàn)邏輯偏保守
缺少標(biāo)準(zhǔn)庫(kù)
一個(gè)網(wǎng)卡接口只能 attach 一個(gè) XDP 程序
6.1 eBPF 程序的限制
6.2 用戶體驗(yàn)和調(diào)試
6.3 驅(qū)動(dòng)支持
6.4 性能提升
6.5 QoS 和 Rate Transitions
6.6 加速傳輸層協(xié)議
6.7 內(nèi)核-用戶空間零拷貝(zero-copy to userspace)
6.8 XDP 作為基礎(chǔ)構(gòu)建模塊(XDP as a building block)
7 總結(jié)
致謝
參考文獻(xiàn)
摘要
近些年業(yè)界流行通過(guò)內(nèi)核旁路(kernel bypass)的方式實(shí)現(xiàn) 可編程的包處理過(guò)程(programmable packet processing),。實(shí)現(xiàn)方式是 將網(wǎng)絡(luò)硬件完全交由某個(gè)專門(mén)的用戶空間應(yīng)用(userspace application) 接管,從而避免內(nèi)核和用戶態(tài)上下文切換的昂貴性能開(kāi)銷,。
但是,,操作系統(tǒng)被旁路(繞過(guò))之后,它的應(yīng)用隔離(application isolation) 和安全機(jī)制(security mechanisms)就都失效了,;一起失效的還有各種經(jīng)過(guò)已經(jīng) 充分測(cè)試的配置,、部署和管理工具。
為解決這個(gè)問(wèn)題,我們提出一種新的可編程包處理方式:eXpress Data Path (XDP),。
XDP 提供了一個(gè)仍然基于操作系統(tǒng)內(nèi)核的安全執(zhí)行環(huán)境,,在設(shè)備驅(qū)動(dòng)上下文(device driver context)中執(zhí)行,可用于定制各種包處理應(yīng)用,。
XDP 是主線內(nèi)核(mainline Linux kernel)的一部分,,與現(xiàn)有的內(nèi)核 網(wǎng)絡(luò)棧(kernel’s networking stack)完全兼容,二者協(xié)同工作,。
XDP 應(yīng)用(application)通過(guò) C 等高層語(yǔ)言編寫(xiě),,然后編譯成特定字節(jié)碼;出于安 全考慮,,內(nèi)核會(huì)首先對(duì)這些字節(jié)碼執(zhí)行靜態(tài)分析,,然后再將它們翻譯成 處理器原生指令(native instructions)。
測(cè)試結(jié)果顯示,,XDP 能達(dá)到 24Mpps/core的處理性能,。
為展示 XDP 靈活的編程模型,本文還將給出三個(gè)程序示例,,
layer-3 routing(三層路由轉(zhuǎn)發(fā))
inline DDoS protection(DDoS 防護(hù))
layer-4 load balancing(四層負(fù)載均衡)
1 引言
軟件實(shí)現(xiàn)高性能包處理的場(chǎng)景,,對(duì)每個(gè)包的處理耗時(shí)有著極高的要求。通用目的操作系統(tǒng)中 的網(wǎng)絡(luò)棧更多是針對(duì)靈活性的優(yōu)化,,這意味著它們花在每個(gè)包上 的指令太多了,,不適合網(wǎng)絡(luò)高吞吐的場(chǎng)景。
因此,,隨后出現(xiàn)了一些專門(mén)用于包處理的軟件開(kāi)發(fā)工具,例如 Data Plane Development Kit (DPDK) [16],。這些工具一般都會(huì)完全繞過(guò)內(nèi)核,,將網(wǎng)絡(luò)硬件直接交 給用戶態(tài)的網(wǎng)絡(luò)應(yīng)用,并需要獨(dú)占一個(gè)或多個(gè) CPU,。
1.1 現(xiàn)有方案(kernel bypass)存在的問(wèn)題
內(nèi)核旁路方式可以顯著提升性能,,但缺點(diǎn)也很明顯:
很難與現(xiàn)有系統(tǒng)集成;
上層應(yīng)用必須要將內(nèi)核中已經(jīng)非常成熟的模塊在用戶態(tài)重新實(shí)現(xiàn)一遍,,例如路由表,、高層協(xié)議棧等;
最壞的情況下,,這種包處理應(yīng)用只能工作在一個(gè)完全隔絕的環(huán)境,,因?yàn)閮?nèi)核提供的常見(jiàn)工具和部署方式在這種情況下都不可用了。
導(dǎo)致系統(tǒng)越來(lái)越復(fù)雜,,而且破壞了操作系統(tǒng)內(nèi)核在把控的安全邊界,。 在基礎(chǔ)設(shè)施逐漸遷移到 Kubernetes/Docker 等容器環(huán)境的背景下,這一點(diǎn)顯得尤其嚴(yán)重,, 因?yàn)樵谶@種場(chǎng)景下,,內(nèi)核擔(dān)負(fù)著資源抽象和隔離的重任,。
1.2 新方案:給內(nèi)核網(wǎng)絡(luò)棧添加可編程能力
對(duì)此,本文提供了另一種解決方案:給內(nèi)核網(wǎng)絡(luò)棧添加可編程能力,。這使得我們能在 兼容各種現(xiàn)有系統(tǒng),、復(fù)用已有網(wǎng)絡(luò)基礎(chǔ)設(shè)施的前提下,仍然實(shí)現(xiàn)高速包處理,。 這個(gè)框架稱為 XDP,,
XDP 定義了一個(gè)受限的執(zhí)行環(huán)境(a limited execution environment),運(yùn)行在一個(gè) eBPF 指令虛擬機(jī)中,。eBPF 是 BSD Packet Filter (BPF) [37] 的擴(kuò)展,。
XDP 程序運(yùn)行在內(nèi)核上下文中,此時(shí)內(nèi)核自身都還沒(méi)有接觸到包數(shù)據(jù)( before the kernel itself touches the packet data),,這使得我們能在網(wǎng)卡收到包后 最早能處理包的位置,,做一些自定義數(shù)據(jù)包處理(包括重定向)。
內(nèi)核在加載(load)時(shí)執(zhí)行靜態(tài)校驗(yàn),,以確保用戶提供的 XDP 程序的安全,。
之后,程序會(huì)被動(dòng)態(tài)編譯成原生機(jī)器指令(native machine instructions),,以獲得高性能,。
XDP 已經(jīng)在過(guò)去的幾個(gè)內(nèi)核 release 中逐漸合并到內(nèi)核,但在本文之前,,還沒(méi)有關(guān)于 XDP 系統(tǒng)的 完整架構(gòu)介紹,。本文將對(duì) XDP 做一個(gè)高層介紹。
1.3 新方案(XDP)的優(yōu)點(diǎn)
我們的測(cè)試結(jié)果顯示 XDP 能取得 24Mpps/core
的處理性能,,這雖然與 DPDK 還有差距,, 但相比于后者這種 kernel bypass 的方式,XDP 有非常多的優(yōu)勢(shì),。
具體地,,XDP:
與內(nèi)核網(wǎng)絡(luò)棧協(xié)同工作,將硬件的控制權(quán)完全留在內(nèi)核范圍內(nèi),。帶來(lái)的好處:
無(wú)需任何特殊硬件特性,,任何有 Linux 驅(qū)動(dòng)的網(wǎng)卡都可以支持, 現(xiàn)有的驅(qū)動(dòng)只需做一些修改,,就能支持 XDP hooks,。
可以選擇性地復(fù)用內(nèi)核網(wǎng)絡(luò)棧中的現(xiàn)有功能,例如路由表或 TCP/IP 協(xié)議棧,,在保持配置接口不變的前提下,,加速關(guān)鍵性能路徑(critical performance paths)。
保證 eBPF 指令集和 XDP 相關(guān)的編程接口(API)的穩(wěn)定性。
與常規(guī) socket 層交互時(shí),,沒(méi)有從用戶態(tài)將包重新注入內(nèi)核的昂貴開(kāi)銷,。
對(duì)應(yīng)用透明。這創(chuàng)造了一些新的部署場(chǎng)景/方式,,例如直接在應(yīng)用所 在的服務(wù)器上部署 DoS 防御(而非中心式/網(wǎng)關(guān)式 DoS 防御),。
服務(wù)不中斷的前提下動(dòng)態(tài)重新編程(dynamically re-program), 這意味著可以按需加入或移除功能,,而不會(huì)引起任何流量中斷,,也能動(dòng)態(tài)響應(yīng)系統(tǒng)其他部分的的變化。
無(wú)需預(yù)留專門(mén)的 CPU 做包處理,,這意味著 CPU 功耗與流量高低直接相關(guān),,更節(jié)能。
1.4 本文組織結(jié)構(gòu)
接下來(lái)的內(nèi)容介紹 XDP 的設(shè)計(jì),,并做一些性能分析,。結(jié)構(gòu)組織如下:
Section 2 介紹相關(guān)工作;
Section 3介紹 XDP 系統(tǒng)的設(shè)計(jì),;
Section 4做一些性能分析,;
Section 5提供了幾個(gè)真實(shí) XDP 場(chǎng)景的程序例子;
Section 6討論 XDP 的未來(lái)發(fā)展方向,;
Section 7總結(jié),。
2 相關(guān)工作
XDP當(dāng)然不是第一個(gè)支持可編程包處理的系統(tǒng) —— 這一領(lǐng)域在過(guò)去幾年發(fā)展勢(shì)頭良好, 并且趨勢(shì)還在持續(xù),。業(yè)內(nèi)已經(jīng)有了幾種可編程包處理框架,,以及基于這些框架的新型應(yīng)用,包括:
單一功能的應(yīng)用,,如 switching [47], routing [19], named-based forwarding [28], classification [48], caching [33] or traffic generation [14],。
更加通用、且高度可定制的包處理解決方案,,能夠處理從多種源收來(lái)的數(shù)據(jù)包 [12, 20, 31, 34, 40, 44],。
要基于通用(Common Off The Shelf,,COTS)硬件實(shí)現(xiàn)高性能包處理,,就必須解決 網(wǎng)卡(NIC)和包處理程序之間的所有瓶頸。由于性能瓶頸主要來(lái)源于內(nèi)核和用戶態(tài)應(yīng)用之間的接口(系統(tǒng)調(diào)用開(kāi)銷非常大,,另外,,內(nèi)核功能豐富,但也非常復(fù)雜),, 低層(low-level)框架必須通過(guò)這樣或那樣的方式來(lái)降低這些開(kāi)銷,。
現(xiàn)有的一些框架通過(guò)幾種不同的方式實(shí)現(xiàn)了高性能,XDP構(gòu)建在其中一些技術(shù)之上。 接下來(lái)對(duì) XDP 和它們的異同做一些比較分析,。
2.1 用戶態(tài)輪詢 vs. XDP
DPDK[16] 可能是使用最廣泛的高性能包處理框架,。它最初只支持 Intel 網(wǎng)卡,后來(lái)逐 步擴(kuò)展到其他廠商的網(wǎng)卡,。DPDK 也稱作內(nèi)核旁路框架(kernel bypass framework),, 因?yàn)樗鼘⒕W(wǎng)絡(luò)硬件的控制權(quán)從內(nèi)核轉(zhuǎn)移到了用戶態(tài)的網(wǎng)絡(luò)應(yīng)用,完全避免了內(nèi)核-用戶態(tài) 之間的切換開(kāi)銷,。
與DPDK 類似的還有 PF_RINGZC module [45] 和 hardware-specific Solarflare OpenOnload [24],。
在現(xiàn)有的所有框架中,內(nèi)核旁路方式性能是最高的[18],;但如引言中指 出,,這種方式在管理、維護(hù)和安全方面都存在不足,。
XDP 采用了一種與內(nèi)核旁路截然相反的方式:相比于將網(wǎng)絡(luò)硬件的控制權(quán)上移到用戶空間,, XDP 將性能攸關(guān)的包處理操作直接放在內(nèi)核中,在操作系統(tǒng)的網(wǎng)絡(luò)棧之前執(zhí)行,。
這種方式同樣避免了內(nèi)核-用戶態(tài)切換開(kāi)銷(所有操作都在內(nèi)核),;
但仍然由內(nèi)核來(lái)管理硬件,因此保留了操作系統(tǒng)提供的管理接口和安全防護(hù)能力,;
這里的主要?jiǎng)?chuàng)新是:使用了一個(gè)虛擬執(zhí)行環(huán)境,,它能對(duì)加載的 程序進(jìn)行校驗(yàn),確保它們不會(huì)對(duì)內(nèi)核造成破壞,。
2.2 內(nèi)核模塊 vs. XDP
在 XDP 之前,,以內(nèi)核模塊(kernel module)方式實(shí)現(xiàn)包處理功能代價(jià)非常高, 因?yàn)槌绦驁?zhí)行出錯(cuò)時(shí)可能會(huì)導(dǎo)致整個(gè)系統(tǒng)崩潰,,而且內(nèi)核的內(nèi)部 API 也會(huì)隨著時(shí)間發(fā)生變化,。 因此也就不難理解為什么只有很少的系統(tǒng)采用了這種方式。其中做的比較好包括
這幾個(gè)系統(tǒng)都支持靈活的配置,,適用于多種場(chǎng)景,,取得比較小的平攤代價(jià)。
XDP通過(guò):
提供一個(gè)安全的執(zhí)行環(huán)境,,以及內(nèi)核社區(qū)支持,,提供與那些暴露到用戶空間一樣穩(wěn)定的內(nèi)核 API
極大地降低了那些將處理過(guò)程下沉到內(nèi)核的應(yīng)用(applications of moving processing into the kernel)的成本。
此外,,XDP 程序也能夠完全繞過(guò)內(nèi)核網(wǎng)絡(luò)棧(completely bypass),, 與在內(nèi)核網(wǎng)絡(luò)棧中做 hook 的傳統(tǒng)內(nèi)核模塊相比,性能也更高,。
XDP 除了能將處理過(guò)程下沉到內(nèi)核以獲得最高性能之外,,還支持在程序中執(zhí)行重定向(redirection)操作,,完全繞過(guò)內(nèi)核網(wǎng)絡(luò)棧,將包送到特殊類型的用戶空間 socket,; 甚至能工作在 zero-copy 模式,,進(jìn)一步降低開(kāi)銷。
這種模式與 Netmap [46] 和 PF_RING [11] 方式類似,,但后者是在沒(méi)有完全繞過(guò)內(nèi) 核的情況下,,通過(guò)降低從網(wǎng)絡(luò)設(shè)備到用戶態(tài)應(yīng)用(network device to userspace application)之間的傳輸開(kāi)銷,實(shí)現(xiàn)高性能包處理,。
內(nèi)核模塊方式的另一個(gè)例子是 Packet I/O engine,,這是 PacketShader [19] 的組成部分, 后者專用于 Arrakis [43] and ClickOS [36] 之類的特殊目的操作系統(tǒng),。
2.3 可編程硬件 vs. XDP
可編程硬件設(shè)備也是一種實(shí)現(xiàn)高性能包處理的方式,。
一個(gè)例子是 NetFPGA [32],通過(guò)對(duì)它暴露的 API 進(jìn)行編程,,能夠在這種基于 FPGA 的專 用設(shè)備上運(yùn)行任何包處理任務(wù),。
P4 編程語(yǔ)言 [7] 致力于將這種可編程能力擴(kuò)展到更廣泛的包處理硬件上(巧合的是,它還包括了一個(gè) XDP backend [51]),。
某種意義上來(lái)說(shuō),,XDP 可以認(rèn)為是一種 offload方式:
性能敏感的處理邏輯下放到網(wǎng)卡驅(qū)動(dòng)中,以提升性能,;
其他的處理邏輯仍然走內(nèi)核網(wǎng)絡(luò)棧,;
如果沒(méi)有用到內(nèi)核 helper 函數(shù),那整個(gè) XDP 程序都可以 offload 到網(wǎng)卡(目前 Netronome smart-NICs [27] 已經(jīng)支持),。
2.4 小結(jié)
XDP 提供了一種高性能包處理方式,,與已有方式相比,在性能,、與現(xiàn)有系統(tǒng)的集成,、靈活性 等方面取得了更好的平衡。接下來(lái)介紹 XDP 是如何取得這種平衡的,。
3 XDP 設(shè)計(jì)
XDP 的設(shè)計(jì)理念:
這種與內(nèi)核的深度集成顯然會(huì)給設(shè)計(jì)帶來(lái)一些限制,在 XDP 組件合并到 Linux 的過(guò)程中,,我們也收到了許多來(lái)自社區(qū)的反饋,,促使我們不斷調(diào)整 XDP 的設(shè)計(jì),但 這些設(shè)計(jì)反思不在本文討論范圍之內(nèi),。
3.0 XDP 系統(tǒng)架構(gòu)
圖 1 描繪了整個(gè) XDP 系統(tǒng),,四個(gè)主要組成部分:
XDP driver hook:XDP 程序的主入口,,在網(wǎng)卡收到包執(zhí)行,。
eBPF virtual machine:執(zhí)行 XDP 程序的字節(jié)碼,,以及對(duì)字節(jié)碼執(zhí)行 JIT 以提升性能。
BPF maps:內(nèi)核中的 key/value 存儲(chǔ),,作為圖中各系統(tǒng)的主要通信通道,。
eBPF verifier:加載程序時(shí)對(duì)其執(zhí)行靜態(tài)驗(yàn)證,以確保它們不會(huì)導(dǎo)致內(nèi)核崩潰,。
Fig 1. XDP 與 Linux 網(wǎng)絡(luò)棧的集成,。這里只畫(huà)了 ingress 路徑,以免圖過(guò)于復(fù)雜,。
上圖是 ingress 流程,。網(wǎng)卡收到包之后,在處理包數(shù)據(jù)(packet data)之前,,會(huì)先執(zhí)行 main XDP hook 中的 eBPF 程序,。 這段程序可以選擇:
丟棄(drop)這個(gè)包;或者
通過(guò)當(dāng)前網(wǎng)卡將包再發(fā)送(send)出去,;或者
將包重定向(redirect)到其他網(wǎng)絡(luò)接口(包括虛擬機(jī)的虛擬網(wǎng)卡),,或者通過(guò) AF_XDP socket 重定向到用戶空間;或者
放行(allow)這個(gè)包,,如果后面沒(méi)有其他原因?qū)е碌?drop,,這個(gè)包就會(huì)進(jìn)入常規(guī)的內(nèi)核網(wǎng)絡(luò)棧。如果是這種情況,,也就是放行包進(jìn)入內(nèi)核網(wǎng)絡(luò)棧,,那接下來(lái)在將包放到發(fā)送隊(duì)列之前(before packets are queued for transmission), 還有一個(gè)能執(zhí)行 BPF 程序的地方:TC BPF hook,。
此外,,圖 1 中還可以看出,不同的 eBPF 程序之間,、eBPF 程序和用戶空間應(yīng)用之間,,都能夠通過(guò) BPF maps 進(jìn)行通信。
3.1 XDP driver hook
在設(shè)備驅(qū)動(dòng)中執(zhí)行,,無(wú)需上下文切換
XDP 程序在網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)中執(zhí)行,,網(wǎng)絡(luò)設(shè)備每收到一個(gè)包,程序就執(zhí)行一次,。
相關(guān)代碼實(shí)現(xiàn)為一個(gè)內(nèi)核庫(kù)函數(shù)(library function),,因此程序直接 在設(shè)備驅(qū)動(dòng)中執(zhí)行,無(wú)需切換到用戶空間上下文,。
在軟件最早能處理包的位置執(zhí)行,,性能最優(yōu)
回到上面圖 1 可以看到:程序在網(wǎng)卡收到包之后最早能處理包的位置執(zhí)行 —— 此時(shí)內(nèi)核還沒(méi)有為包分配 struct sk_buff 結(jié)構(gòu)體, 也沒(méi)有執(zhí)行任何解析包的操作,。
XDP 程序典型執(zhí)行流
下圖是一個(gè)典型的 XDP 程序執(zhí)行流:
Fig 2. 典型 XDP 程序的執(zhí)行流,。
網(wǎng)卡收到一個(gè)包時(shí),,XDP程序依次執(zhí)行:
提取包頭中的信息(例如 IP、MAC,、Port,、Proto 等),
執(zhí)行到程序時(shí),,系統(tǒng)會(huì)傳遞給它一個(gè)上下文對(duì)象(context object)作為參賽(即 struct xdp_md *ctx,,后面有例子),其中包括了指向原 始包數(shù)據(jù)的指針,,以及描述這個(gè)包是從哪個(gè)網(wǎng)卡的哪個(gè)接口接收上來(lái)的等元數(shù)據(jù)字段,。
讀取或更新一些資源的元信息(例如更新統(tǒng)計(jì)信息);
解析包數(shù)據(jù)之后,,XDP 程序可以讀取 ctx 中的包元數(shù)據(jù)(packet metadata) 字段,,例如從哪個(gè)網(wǎng)卡的哪個(gè)接口收上來(lái)的(ifindex)。除此之外,,ctx 對(duì)象還允許 程序訪問(wèn)與包數(shù)據(jù)毗鄰的一塊特殊內(nèi)存區(qū)域(cb, control buffer),, 在包穿越整個(gè)系統(tǒng)的過(guò)程中,可以將自定義的數(shù)據(jù)塞在這里,。
除了 per-packet metadata,,XDP 程序還可以通過(guò) BPF map 定義和訪問(wèn)自己的持久數(shù)據(jù),以及通過(guò)各種 helper 函數(shù)訪問(wèn)內(nèi)核基礎(chǔ)設(shè)施,。
BPF map 使 BPF 程序能與系統(tǒng)的其他部分之間通信,;
Helpers 使 BPF 程序能利用到某些已有的內(nèi)核功能(例如路由表), 而無(wú)需穿越整個(gè)內(nèi)核網(wǎng)絡(luò)棧,。
如果有需要,對(duì)這個(gè)包進(jìn)行 rewrite header操作,,
程序能修改包數(shù)據(jù)的任何部分,包括添加或刪除包頭,。這使得 XDP 程序能執(zhí)行封裝/接封裝操作,,以及重寫(xiě)(rewrite)地址字段然后轉(zhuǎn)發(fā)等操作,。
內(nèi)核 helper 函數(shù)各有不同用途,,例如修改一個(gè)包之后,計(jì)算新的校驗(yàn)和(checksum),。
進(jìn)行最后的判決(verdict),確定接下來(lái)對(duì)這個(gè)包執(zhí)行什么操作;
判決結(jié)果包括:
重定向功能的用途:
這些不同的路徑,,在圖 1 對(duì)應(yīng)的是幾條實(shí)線。
將重定向判決(verdict)與重定向目標(biāo)(target)分開(kāi),,使得重定向目標(biāo)類型很容易擴(kuò)展,; 另外,,由于重定向參數(shù)(目標(biāo))是通過(guò) BPF map 查詢的,,因此無(wú)需修 改 XDP 程序,,就能動(dòng)態(tài)修改重定向目標(biāo),。
三種簡(jiǎn)單返回碼:丟棄這個(gè)包、通過(guò)接收時(shí)的網(wǎng)卡將包重新發(fā)送出去、允許這個(gè)包進(jìn)入內(nèi)核網(wǎng)絡(luò)棧,;
第四種返回碼 redirect:允許 XDP 程序指定網(wǎng)卡,、CPU、用戶態(tài) socket等,,將包重定向過(guò)去,。
將原始包通過(guò)另一個(gè)網(wǎng)卡(包括虛擬機(jī)的虛擬網(wǎng)卡)發(fā)送出去;
轉(zhuǎn)發(fā)給指定 CPU做進(jìn)一步處理,;
轉(zhuǎn)發(fā)給 AF_XDP 類型的 socket做進(jìn)一步處理,;
程序還能通過(guò)尾調(diào)用(tail call),將控制權(quán)交給另一個(gè) XDP 程序,; 通過(guò)這種方式,,可以將一個(gè)大程序拆分成幾個(gè)邏輯上的小程序(例如,根據(jù) IPv4/IPv6),。
由于 XDP 程序可包含任意指令,,因此前三步(讀取包數(shù)據(jù)、處理元數(shù)據(jù),、重寫(xiě)包數(shù)據(jù)) 順序可以是任意的,,而且支持多層嵌套,。 但實(shí)際中為了獲得高性能,,大部分情況下還是將執(zhí)行結(jié)構(gòu)組織成這順序的三步。
3.2 eBPF 虛擬機(jī)
XDP 程序在 Extended BPF (eBPF) 虛擬機(jī)中執(zhí)行,。eBPF 是早期 BSD packet filter (BPF) [37] 的擴(kuò)展,,后者在過(guò)去的幾十年中廣泛 應(yīng)用于各種包處理工具。
BPF 使用 基于寄存器的(register-based) virtual machine 來(lái)描述 過(guò)濾動(dòng)作(filtering actions),。
eBPF 虛擬機(jī)支持動(dòng)態(tài)加載(loading)和重加載(re-loading)程序,,內(nèi)核管理所有 BPF 程序的生命周期。
3.3 BPF maps
eBPF 程序在觸發(fā)內(nèi)核事件時(shí)執(zhí)行(例如,,觸發(fā) XDP 程序執(zhí)行的,,是收包事件)。 程序每次執(zhí)行時(shí),,初始狀態(tài)都是相同的(即程序是無(wú)狀態(tài)的),,它們無(wú)法直接訪問(wèn)內(nèi)核中的持久存儲(chǔ)(BPF map)。為此,,內(nèi)核提供了訪問(wèn) BPF map 的 helper 函數(shù),。
BPF map 是 key/value 存儲(chǔ),在加載 eBPF 程序時(shí)定義(defined upon loading an eBPF program),。
用途:
持久存儲(chǔ),。例如一個(gè) eBPF 程序每次執(zhí)行時(shí),都會(huì)從里面獲取上一次的狀態(tài)。
用于協(xié)調(diào)兩個(gè)或多個(gè) eBPF 程序,。例如一個(gè)往里面寫(xiě)數(shù)據(jù),,一個(gè)從里面讀數(shù)據(jù)。
用于用戶態(tài)程序和內(nèi)核 eBPF 程序之間的通信,。
3.4 eBPF verifier
唯一加載入口:bpf()
系統(tǒng)調(diào)用
由于 eBPF 代碼直接運(yùn)行在內(nèi)核地址空間,,因此它能直接訪問(wèn) —— 也可 能是破壞 —— 任何內(nèi)存。為防止這種情況發(fā)生,,內(nèi)核規(guī)定只能通過(guò)唯一入口( bpf() 系統(tǒng)調(diào)用)加載 BPF 程序,。
加載 BPF 程序時(shí),位于內(nèi)核中的校驗(yàn)器首先會(huì)對(duì)字節(jié)碼程序進(jìn)行靜態(tài)分析,,以確保
程序中沒(méi)有任何不安全的操作(例如訪問(wèn)任意內(nèi)存),,
程序會(huì)終止(terminate)。通過(guò)下面這兩點(diǎn)來(lái)實(shí)現(xiàn):
禁止循環(huán)操作
限制程序最大指令數(shù)
校驗(yàn)器工作原理:two-pass DAG
校驗(yàn)器的工作原理:首先根據(jù)程序的控制流構(gòu)建一個(gè)有向無(wú)環(huán)圖(DAG),, 然后對(duì) DAG 執(zhí)行如下校驗(yàn):
首先,,對(duì) DAG 進(jìn)行一次深度優(yōu)先搜索(depth-first search),以 確保它是無(wú)環(huán)的(acyclic),,例如,,沒(méi)有循環(huán),也不包含不支持或無(wú)法執(zhí)行到的指令,。
然后,,再掃描一遍,這次會(huì)遍歷 DAG 的所有可能路徑,。這次掃描的目的是:
程序執(zhí)行 load
或 call
指令時(shí),,如果參數(shù)不合法,就會(huì)在這里被拒絕,。參數(shù)合法 性是通過(guò)在程序執(zhí)行期間跟蹤所有寄存器和棧變量的狀態(tài)(states of registers and stack variables)來(lái)實(shí)現(xiàn)的,。
內(nèi)存越界和空指針檢查:職責(zé)上移到程序自身/開(kāi)發(fā)者
這種跟蹤寄存器狀態(tài)的機(jī)制是為了在無(wú)法預(yù)知內(nèi)存邊界的情況下,,仍然確保程序 的內(nèi)存訪問(wèn)不會(huì)越界。無(wú)法預(yù)知內(nèi)存邊界是因?yàn)椋?/p>
為解決這個(gè)問(wèn)題,,校驗(yàn)器會(huì)檢查已加載的程序自身是否會(huì)做如下檢查:
解引用指針前做了內(nèi)存邊界檢查,,
查詢 map 之前是檢查了 map 指針是否為空。
這種方式將處理邏輯中的安全檢查和遇到錯(cuò)誤時(shí)如何處理的控制權(quán)都 交給了 BPF 程序的編寫(xiě)者,。
跟蹤數(shù)據(jù)訪問(wèn)操作和值范圍
為跟蹤數(shù)據(jù)訪問(wèn),,校驗(yàn)器會(huì)跟蹤
數(shù)據(jù)類型
指針偏置(pointer offsets)
所有寄存器的可能值范圍
程序開(kāi)始時(shí),,
接下來(lái)程序每執(zhí)行一步,,寄存器狀態(tài)就會(huì)更新一次。當(dāng)寄存器中存入一個(gè)新值時(shí),,這個(gè)寄存器 還會(huì)繼承與這個(gè)值相關(guān)的狀態(tài)變量(inherits the state variables from the source of the value),。
算術(shù)操作會(huì)影響標(biāo)量類型的值的范圍(value ranges of scalar types),以及指針類型的 offset,。 可能的最大范圍(max possible range)存儲(chǔ)在狀態(tài)變量中,,例如往寄存器中 load 一個(gè)字節(jié)時(shí), 這個(gè)寄存器的可能值范圍就設(shè)置為 0~255,。指令圖(instruction graph)中的 各邏輯分支就會(huì)根據(jù)操作結(jié)果更新寄存器狀態(tài),。例如,比較操作 R1 > 10
,,
不同類型數(shù)據(jù)的校驗(yàn)信息來(lái)源(source of truth)
利用狀態(tài)變量中存儲(chǔ)的范圍信息,,校驗(yàn)器就能預(yù)測(cè)每個(gè) load 指令能訪問(wèn)的所有 內(nèi)存范圍,,確保它執(zhí)行的都是合法內(nèi)存訪問(wèn)。
對(duì)于包數(shù)據(jù)(packet data)的訪問(wèn),,會(huì)與 context 對(duì)象中的 data_end
變量做比較,;
對(duì)于 BPF map 中獲取的值,或用到 map 定義中聲明的 data size 信息,;
對(duì)于棧上存儲(chǔ)的值,,會(huì)檢查狀態(tài)變量中記錄的值范圍,;
對(duì)于指針?biāo)阈g(shù)操作(pointer arithmetic)還會(huì)施加額外的限制,,指針通常不能被轉(zhuǎn)換成整形值。
只要校驗(yàn)器無(wú)法證明某個(gè)操作是安全,,該 BPF 程序在加載時(shí)(load time)就會(huì)被拒絕,。 除此之外,校驗(yàn)器還會(huì)利用范圍信息確保內(nèi)存的對(duì)齊訪問(wèn)(enforce aligned memory access),。
校驗(yàn)器的目的
需要說(shuō)明的是,,校驗(yàn)器的目的是避免將內(nèi)核內(nèi)部(the internals of the kernel )暴露給惡意或有缺陷的 eBPF 程序,而非確保程序中函數(shù)的實(shí)現(xiàn)已經(jīng)是最高效的,。
換句話說(shuō),,如果 XDP 程序中處理邏輯過(guò)多,也可能會(huì)導(dǎo)致機(jī)器變慢 ,;如果代碼寫(xiě)的有問(wèn)題,,也可能會(huì)破壞包數(shù)據(jù),。出于這些原因,加載 BPF 程序需要 管理員權(quán)限(root),。避免這些 bug 的責(zé)任在程序員,,但選擇將哪些程序加載 到系統(tǒng)的權(quán)限在管理員。
3.5 XDP 程序示例
下面是一個(gè)簡(jiǎn)單的 XDP 程序,,展示了前面介紹的一些特性,。 程序會(huì)解析包數(shù)據(jù),判斷如果是 UDP 包,,直接交換源和目的 MAC 地址,,然后將包從相同網(wǎng)卡再發(fā)送回去,
雖然這是一個(gè)非常簡(jiǎn)單的例子,,但真實(shí)世界中的 XDP 程序用到的組件和特性,,這里基本都具備了。
// 從內(nèi)核 BPF 代碼示例 xdp2_kern.c 修改而來(lái),。
1 // 用于統(tǒng)計(jì)包數(shù)
2 struct bpf_map_def SEC('maps') rxcnt = {
3 .type = BPF_MAP_TYPE_PERCPU_ARRAY,
4 .key_size = sizeof(u32), // IP 協(xié)議類型,,即 IPv4/IPv6
5 .value_size = sizeof(long), // 包數(shù)
6 .max_entries = 256,
7 };
8
9 // 直接操作包數(shù)據(jù)(direct packet data access),交換 MAC 地址
10 static void swap_src_dst_mac(void *data)
11 {
12 unsigned short *p = data;
13 unsigned short dst[3];
14 dst[0] = p[0]; dst[1] = p[1]; dst[2] = p[2];
15 p[0] = p[3]; p[1] = p[4]; p[2] = p[5];
16 p[3] = dst[0]; p[4] = dst[1]; p[5] = dst[2];
17 }
18
19 static int parse_ipv4(void *data, u64 nh_off, void *data_end)
20 {
21 struct iphdr *iph = data nh_off;
22 if (iph 1 > data_end)
23 return 0;
24 return iph->protocol;
25 }
26
27 SEC('xdp1') // marks main eBPF program entry point
28 int xdp_prog1(struct xdp_md *ctx)
29 {
30 void *data_end = (void *)(long)ctx->data_end;
31 void *data = (void *)(long)ctx->data;
32 struct ethhdr *eth = data; int rc = XDP_DROP;
33 long *value; u16 h_proto; u64 nh_off; u32 ipproto;
34
35 nh_off = sizeof(*eth);
36 if (data nh_off > data_end)
37 return rc;
38
39 h_proto = eth->h_proto;
40
41 /* check VLAN tag; could be repeated to support double-tagged VLAN */
42 if (h_proto == htons(ETH_P_8021Q) || h_proto == htons(ETH_P_8021AD)) {
43 struct vlan_hdr *vhdr;
44
45 vhdr = data nh_off;
46 nh_off = sizeof(struct vlan_hdr);
47 if (data nh_off > data_end)
48 return rc;
49 h_proto = vhdr->h_vlan_encapsulated_proto;
50 }
51
52 if (h_proto == htons(ETH_P_IP))
53 ipproto = parse_ipv4(data, nh_off, data_end);
54 else if (h_proto == htons(ETH_P_IPV6))
55 ipproto = parse_ipv6(data, nh_off, data_end);
56 else
57 ipproto = 0;
58
59 /* lookup map element for ip protocol, used for packet counter */
60 value = bpf_map_lookup_elem(&rxcnt, &ipproto);
61 if (value)
62 *value = 1;
63
64 /* swap MAC addrs for UDP packets, transmit out this interface */
65 if (ipproto == IPPROTO_UDP) {
66 swap_src_dst_mac(data);
67 rc = XDP_TX;
68 }
69 return rc;
70 }
具體地:
定義了一個(gè) BPF map 存儲(chǔ)統(tǒng)計(jì)信息,。用戶態(tài)程序可以 poll 這個(gè) map 來(lái)獲取統(tǒng)計(jì)信息,。
context 對(duì)象 struct xdp_md *ctx
中有包數(shù)據(jù)的 start/end 指針,可用于直接訪問(wèn)包數(shù)據(jù),。
將數(shù)據(jù)指針和 data_end
比較,,確保內(nèi)存訪問(wèn)不會(huì)越界。
程序必須自己解析包,,包括 VLAN headers 等東西,。
直接通過(guò)指針(direct packet data access)修改包頭。
內(nèi)核提供的 map lookup helper,。這是程序中唯一的真實(shí)函數(shù)調(diào)用,;其他函數(shù)都是內(nèi)聯(lián),包括 htons()
,。
最終針對(duì)這個(gè)包的判決通過(guò)程序返回值傳遞給調(diào)用方,。
將這段程序安裝到網(wǎng)卡接口上時(shí),它首先會(huì)被編譯成 eBPF 字節(jié)碼,,然后經(jīng)受校驗(yàn)器檢查,。 這里的檢查項(xiàng)包括:
無(wú)循環(huán)操作;程序大?。ㄖ噶顢?shù)量),;
訪問(wèn)包數(shù)據(jù)之前,做了內(nèi)存邊界檢查,;
傳遞給 map lookup 函數(shù)的參數(shù),,類型與 map 定義相匹配,;
map lookup 的返回值(value 的內(nèi)存地址)在使用之前,檢查了是否為 NULL,。
3.6 小結(jié)
XDP系統(tǒng)由四個(gè)主要部分組成:
XDP device driver hook:網(wǎng)卡收到包之后直接運(yùn)行,;
eBPF虛擬機(jī):執(zhí)行 XDP 程序(以及內(nèi)核其他模塊加載的 BPF 程序);
BPF maps:使不同 BPF 程序之間,、BPF 程序與用戶空間應(yīng)用之間能夠通信,;
eBPF verifier:確保程序不包含任何可能會(huì)破壞內(nèi)核的操作。
這四部分加在一起,,創(chuàng)造了一個(gè)編寫(xiě)自定義包處理應(yīng)用的強(qiáng)大環(huán)境,,它能加速包處理的關(guān)鍵 路徑,同時(shí)還與內(nèi)核及現(xiàn)有基礎(chǔ)設(shè)施密切集成,。
接下來(lái)看一下 XDP 應(yīng)用的性能,。
4 性能評(píng)估
DPDK 是目前性能最高的包處理框架 [18],因此本文將 XDP 與 DPDK 及 Linux 內(nèi)核網(wǎng)絡(luò) 棧的性能做一個(gè)對(duì)比,。測(cè)試機(jī)器環(huán)境:
CPU:一塊 hexa-core Intel Xeon E5-1650 v4 CPU running at 3.60GHz,, 支持 Intel Data Direct I/O (DDIO) 技術(shù),網(wǎng)絡(luò)硬件通過(guò) DMA 能直接將包放到 CPU 緩存,。
關(guān)閉超線性(Hyperthreading),。
網(wǎng)卡:兩塊 Mellanox ConnectX-5 Ex VPI dual-port 100Gbps,mlx5 驅(qū)動(dòng),。
內(nèi)核:Linux 4.18
使用基于 DPDK 的 TRex packet generator [9] 生成測(cè)試流量,。所有測(cè)試腳本位于 [22]。
在測(cè)試中,,我們主要關(guān)心三個(gè) metric:
直接棄包(packet drop)性能,。
為展示最高的包處理性能,我們將用最簡(jiǎn)單的操作 —— 丟棄接收到的包—— 來(lái)測(cè)試,。 這個(gè)測(cè)試能有效測(cè)量系統(tǒng)的整體開(kāi)銷,,也是真正的包處理應(yīng)用能達(dá)到的性能上限。
CPU 使用量,。
如引言中指出,,XDP 的優(yōu)點(diǎn)之一是 CPU 使用量與流量大小是正相關(guān)的,而無(wú)需預(yù)留專 門(mén)的 CPU 給它用,。我們通過(guò)測(cè)量 CPU 利用率隨網(wǎng)絡(luò)負(fù)載的變化來(lái)量化這個(gè)指標(biāo)。
包轉(zhuǎn)發(fā)性能,。
轉(zhuǎn)發(fā)的復(fù)雜性要更高一些,,例如,涉及到與多塊網(wǎng)卡的交互,、重寫(xiě)二層頭等等,。 這里會(huì)將轉(zhuǎn)發(fā)延遲也考慮進(jìn)去,。
我們已經(jīng)驗(yàn)證,使用 MTU(1500 字節(jié))包時(shí),,我們的系統(tǒng)單核就能達(dá)到線速(100 Gbps), 而且 CPU 有 50% 是空閑的,。顯然,真正的挑戰(zhàn)在于 PPS,,而非帶寬,,其他一些測(cè)試也已經(jīng)指出了這一點(diǎn) [46]。 出于這個(gè)原因,,我們用最小包(64 字節(jié))測(cè)試,,衡量指標(biāo)是 PPS。
對(duì)于 XDP 和 Linux 內(nèi)核網(wǎng)絡(luò)棧的測(cè)試,,由于它們沒(méi)有顯式指定某些 CPU 來(lái)處理網(wǎng)絡(luò)包的方式,,因此我們通過(guò)配置硬件 RSS(Receive Side Scaling)來(lái)講流量定向到指定 CPU。
對(duì)網(wǎng)卡,、內(nèi)核的一些配置調(diào)優(yōu),,見(jiàn)代碼倉(cāng)庫(kù) [22]。
4.1 直接棄包(packet drop)性能
Fig 3. 直接棄包(packet drop)性能,。DPDK 需要預(yù)留一個(gè) CPU 運(yùn)行控制任務(wù),,因此只剩下 5 個(gè) CPU 做包處理。
上圖是性能與 CPU 數(shù)量的關(guān)系,。
XDP 基準(zhǔn)性能是 24Mpps/core
,,DPDK 是 43.5Mpps/core
。
二者在分別達(dá)到各自的峰值之前,,PPS 都是隨 CPU 數(shù)量線性增長(zhǎng)的,。
最終全局性能受限于 PCI 總線,啟用 PCI descriptor compression(在 CPU cycles 和 PCI 總線帶寬之間取舍)之后,,能達(dá)到 115Mpps,。
再看圖中 Linux 網(wǎng)絡(luò)棧在兩種配置下的性能:
通過(guò) iptables 的 raw table丟棄流量,這是 Linux 網(wǎng)絡(luò)棧中最早能丟棄包的地方,;
通過(guò) conntrack(連接跟蹤)模塊,,這個(gè)模塊的開(kāi)銷非常大,但在很多 Linux 發(fā)行版中都是默認(rèn)開(kāi)啟的,。
conntrack 模式達(dá)到了 1.8Mpps/core,,raw 模式是 4.8Mpps/core ;這兩種模式均未達(dá)到硬件瓶頸,。 最終的性能,,XDP 比常規(guī)網(wǎng)絡(luò)棧的最快方式快了 5 倍。
Linux raw mode test 中,,我們還測(cè)量了 XDP 程序不丟棄包,,而是更新包數(shù)統(tǒng)計(jì)然后將包 送到內(nèi)核網(wǎng)絡(luò)棧的場(chǎng)景,。 這種情況下,XDP單核的處理性能會(huì)下降到 4.5Mpps/core,,有 13.3ns 處理延遲,。 圖中并未給出這個(gè)測(cè)試結(jié)果,因?yàn)檫@個(gè)開(kāi)銷太小了,。
4.2 CPU Usage
Fig 4. 直接棄包(packet drop)場(chǎng)景下的 CPU 利用率,。
用系統(tǒng)提供的 mpstat
命令測(cè)量 CPU 利用率。結(jié)果如圖 4 ,。
DPDK 是 busy poll 模式,,因此 CPU 永遠(yuǎn)是 100%。
XDP 和 Linux 內(nèi)核網(wǎng)絡(luò)棧都是隨流量平滑增長(zhǎng):前面一小段是非線性的,,后面基本是線性的,。
前面非線性主要是硬中斷帶來(lái)的固定開(kāi)銷,在流量很小時(shí)這一部分占比較大,。
4.3 包轉(zhuǎn)發(fā)性能
這個(gè)測(cè)試中,,轉(zhuǎn)發(fā)應(yīng)用執(zhí)行非常簡(jiǎn)單的 MAC 地址重寫(xiě):直接交換源和目的 MAC 地址,然后轉(zhuǎn)發(fā),。 這是轉(zhuǎn)發(fā)場(chǎng)景下最精簡(jiǎn)的步驟了,,因此結(jié)果代表了所有真實(shí)轉(zhuǎn)發(fā)應(yīng)用的性能上限。
圖中包括了同網(wǎng)卡轉(zhuǎn)發(fā)和不同網(wǎng)卡轉(zhuǎn)發(fā)(XDP程序返回碼不同)的結(jié)果,。
DPDK 示例程序只支持通過(guò)另一個(gè)網(wǎng)卡轉(zhuǎn)發(fā),,因此這里只列出了這種情況下的性能。
Linux 網(wǎng)絡(luò)棧不支持這種極簡(jiǎn)轉(zhuǎn)發(fā)模式(minimal forwarding mode),,需要設(shè)置完整的 橋接或路由查找(bridging or routing lookup)才能轉(zhuǎn)發(fā)包,;路由查找是非常耗時(shí)的 ,由于其他幾種應(yīng)用并沒(méi)有這一步,,因此結(jié)果直接對(duì)比是沒(méi)意義的,。因此這里略去了 Linux 網(wǎng)絡(luò)棧的結(jié)果。
轉(zhuǎn)發(fā)吞吐(pps)
Fig 5. 轉(zhuǎn)發(fā)性能,。在同一網(wǎng)卡接口上收發(fā)會(huì)占用同一 PCI port 的帶寬,, 這意味著在 70Mpps XDP same-nic 組就已經(jīng)達(dá)到了 PCI 總線的瓶頸
如圖 5 所示,性能隨 CPU 數(shù)量線性擴(kuò)展,,直到達(dá)到全局性能瓶頸,。XDP 在同網(wǎng)卡轉(zhuǎn)發(fā)的性能遠(yuǎn)高于 DPDK 異網(wǎng)卡性能,原因是內(nèi)存處理方式不同:
packet buffer 是設(shè)備驅(qū)動(dòng)分配的,,與接收接口(receiving interface)相關(guān)聯(lián),。
因此,異網(wǎng)卡場(chǎng)景下,,當(dāng)包轉(zhuǎn)發(fā)到另一個(gè)接口時(shí),,memory buffer 需要還給與之關(guān)聯(lián)的接口。
轉(zhuǎn)發(fā)延遲
表 1. 轉(zhuǎn)發(fā)延遲,。機(jī)器網(wǎng)卡的兩個(gè)接口直連,,在轉(zhuǎn)發(fā)速率分別為 100pps 和 1Mpps 的條件下,持續(xù) 50s 測(cè)量端到端延遲
高 pps 場(chǎng)景下,,XDP 的延遲已經(jīng)接近 DPDK,。但在低 pps 場(chǎng)景下,XDP 延遲比 DPDK 大的多,,原因是 XDP 是基于中斷的,,中斷處理時(shí)間( interrupt processing time)此時(shí)占大頭;而 DPDK 是輪詢模式,,延遲相對(duì)比較固定,。
4.4 討論:XDP 性能與 DPDK 還有差距的原因
XDP 未做底層代碼優(yōu)化
上一節(jié)已經(jīng)看到,XDP 相比于常規(guī) Linux 網(wǎng)絡(luò)棧性能有了顯著提升,。但對(duì)于大部分 XDP 場(chǎng)景來(lái)說(shuō),,性能還是與 DPDK 有差距。我們認(rèn)為,,這是主要是因?yàn)?DPDK 做了相當(dāng)多的底層 代碼優(yōu)化,。舉個(gè)例子來(lái)解釋,考慮 packet drop 例子:
XDP 24Mpps/core,,對(duì)應(yīng) 41.6ns/packet
DPDK 43.5Mpps,,對(duì)應(yīng) 22.9ns/packet
多出來(lái)的 18.7ns 在我們的 3.6GHz 機(jī)器上對(duì)應(yīng) 67 個(gè)時(shí)鐘周期。因此,,很顯然 每個(gè)很小的優(yōu)化在這里都會(huì)產(chǎn)生很大的影響,。例如,我們測(cè)量出在測(cè)試 機(jī)器上,,每次函數(shù)調(diào)用需要 1.3ns,。mlx5 驅(qū)動(dòng)處理每個(gè)包都有 10 次 函數(shù)調(diào)用,總計(jì)就是 13ns,。
通用目的操作系統(tǒng),,首要目標(biāo):更好的擴(kuò)展和配置,而非極致性能
另外,,在 Linux 這樣的通用目的操作系統(tǒng)中,,某些開(kāi)銷是不可避免的, 因?yàn)樵O(shè)備驅(qū)動(dòng)或子系統(tǒng)的組織方式是為了實(shí)現(xiàn)更好的擴(kuò)展和配置,,而非極致性能,。
但是,我們認(rèn)為有些優(yōu)化還是有必要的。例如,,我們嘗試將內(nèi)核中與測(cè)試網(wǎng)卡無(wú)關(guān)的 DMA 函數(shù)調(diào)用刪掉,, 這樣將前面提到的 10 個(gè)函數(shù)調(diào)用降低到了 6 個(gè),測(cè)試結(jié)果顯示這將單核性能提升到了 29Mpps/core,。 依此推測(cè)的話,,將另外 6 個(gè)函數(shù)調(diào)用也優(yōu)化掉,能將 XDP 的性能提升到 37.6Mpps
,。 實(shí)際上我們不可能將 6 個(gè)全部去掉,,但去掉其中幾個(gè),再加上一些其他優(yōu)化,,我 們相信 XDP 和 DPDK 的性能差距將越來(lái)越小,。
其他驅(qū)動(dòng)的測(cè)試結(jié)果也是類似的,例如 i40e driver for 40 Gbps Intel cards,。
基于以上討論,,我們相信未來(lái) XDP 與 DPDK 的性能差距將越來(lái)越小。
另一方面,,考慮到 XDP 在靈活性和與內(nèi)核集成方面的優(yōu)勢(shì),, XDP 已經(jīng)是很多實(shí)際場(chǎng)景中的非常有競(jìng)爭(zhēng)力的方式。下文給出幾個(gè)例子,。
5 真實(shí)場(chǎng)景使用案例
本節(jié)給出三個(gè)例子來(lái)具體展示 XDP 在真實(shí)世界中的應(yīng)用,。 這幾個(gè)案例都是已經(jīng)真實(shí)在用的,但本文出于解釋目的,,將使用簡(jiǎn)化的版本,。 同時(shí)也建議讀者參考 [38],后者是獨(dú)立的文章,,介紹使用 XDP 解決實(shí)際工作中網(wǎng)絡(luò)服務(wù)所面臨的一些挑戰(zhàn),。
本節(jié)目的是展示真實(shí) XDP 方案的可行性,因此不會(huì)將重點(diǎn)放在與業(yè)界最新的實(shí)現(xiàn)做詳盡性能對(duì)比上,。 我們會(huì)拿常規(guī)的 Linux 內(nèi)核網(wǎng)絡(luò)棧的性能作為 baseline,,來(lái)對(duì)比 XDP 應(yīng)用的性能。
5.1 案例一:軟件路由(software routing)
內(nèi)核數(shù)據(jù)平面 & 控制平面(BIRD/FRR)
Linux 內(nèi)核實(shí)現(xiàn)了一個(gè)功能完整的路由表,,作為數(shù)據(jù)平面,,支持
對(duì)于控制平面,Bird [10] 或 FRR [17] 這樣的路由守護(hù)進(jìn)程( routing daemons)實(shí)現(xiàn)了多種路由控制平面協(xié)議,。Linux 提供的這套生態(tài)系統(tǒng)功能如此豐富 ,,因此再在另一個(gè)包處理框架中重新實(shí)現(xiàn)一套類似的路由棧代價(jià)將非常高, 更實(shí)際的方式是對(duì) Linux 內(nèi)核的數(shù)據(jù)平面進(jìn)行優(yōu)化,。
XDP:直接查詢內(nèi)核路由表并轉(zhuǎn)發(fā)
XDP 非常適合做這件事情,,尤其是它提供了一個(gè) helper 函數(shù),,能從 XDP 程序中直接查詢內(nèi)核路由表。
如果查詢成功,,會(huì)返回egress interface 和下一跳 MAC 地址,, XDP 程序利用這些信息足夠?qū)⒓崔D(zhuǎn)發(fā)出去。
如果下一跳 MAC 還是未知的(因?yàn)橹斑€沒(méi)進(jìn)行過(guò) neighbour lookup),,XDP 程序就 能將包傳給內(nèi)核網(wǎng)絡(luò)棧,,后者會(huì)解析 neighbor 地址,,這樣隨后的包 就能直接被 XDP 程序轉(zhuǎn)發(fā)了,。
測(cè)試:XDP routing 全球 BGP 路由表
為展示 XDP 路由的性能,我們用 Linux 內(nèi)核代碼中的 XDP routing 例子 [1],,與常規(guī) Linux 內(nèi)核網(wǎng)絡(luò)棧的性能做對(duì)比,。 兩組測(cè)試:
路由表中只有一條路由;
路由表中有從 routeviews.org 中 dump 而來(lái)的全球 BGP 路由表(global BGP routing table),。 包含 752,138 條路由,。隨機(jī)生成 4000 個(gè)目的 IP 地址,以確保能充分利用到這種路由表,。
如果目的 IP 地址少于 4000 個(gè),,實(shí)際用到的路由表部分會(huì)較小,能夠保存在 CPU 緩存中,,使得結(jié)果不準(zhǔn)確,。 增大 IP 數(shù)量至 4000 個(gè)以上,不會(huì)對(duì)轉(zhuǎn)發(fā)性能造成影響,,但可以避免緩存導(dǎo)致的結(jié)果不準(zhǔn)問(wèn)題,。
對(duì)于兩組測(cè)試,下一跳 MAC 地址都是與我們的發(fā)送網(wǎng)卡直接相關(guān)的接口的地址,。
性能:2.5x
Fig 6. 軟件路由的性能,。由于性能隨核數(shù)線性增加,這里只給出單核的結(jié)果,。
測(cè)試結(jié)果如上圖所示,。
full table lookup 性能提升了 2.5 倍;
smaller routing table 組,,提升了 3 倍,。
這說(shuō)明,XDP 路由程序 單核 10Gbps 網(wǎng)卡的軟硬件配置,,就能 處理整張全球 BGP 路由表(保守估計(jì)每個(gè)包平均 300 字節(jié)),。
5.2 案例二:Inline DoS Mitigation
DoS 攻擊還是像瘟疫一樣糾纏著互聯(lián)網(wǎng),現(xiàn)在通常的方式是:通過(guò)已經(jīng)入侵的大量設(shè)備發(fā)起分布式(DDoS)攻擊,。
有了XDP 之后,,我們能直接在應(yīng)用服務(wù)器(application servers)上 部署包過(guò)濾程序來(lái)防御此類攻擊(inline DoS mitigation), 無(wú)需修改應(yīng)用代碼。如果應(yīng)用是部署在虛擬機(jī)里,,那 XDP 程序還可以 部署在宿主機(jī)(hypervisor)上,,這樣單個(gè)程序就能保護(hù)機(jī)器上所有的虛擬機(jī)。
模擬 Cloudflare 防御架構(gòu)
為展示工作原理,,我們用 XDP 作為過(guò)濾機(jī)制,,模擬 Cloudflare 的 DDoS 防御架構(gòu)[6]。 他們的 Gatebot architecture ,,首先在各 PoP 點(diǎn)機(jī)器上采樣,,然后統(tǒng)一收起來(lái)做分析, 根據(jù)分析結(jié)果生成防御規(guī)則,。
防御規(guī)則的形式是對(duì)包數(shù)據(jù)(payload)進(jìn)行一系列簡(jiǎn)單檢查,, 能直接編譯成 eBPF 代碼然后分發(fā)到 PoP 點(diǎn)的所有服務(wù)器上。這里說(shuō)的代碼是 XDP 程序 ,,它會(huì)將匹配到規(guī)則的所有流量丟棄,,同時(shí)將統(tǒng)計(jì)信息更新到 BPF map。
程序邏輯
為驗(yàn)證這種方案的性能,,我們編寫(xiě)一個(gè) XDP 程序,,它
解析包頭,執(zhí)行一些簡(jiǎn)單驗(yàn)證,。對(duì)每個(gè)包:執(zhí)行四次讀取操作,,以解析外層包頭。
將符合攻擊特性的流量丟棄,。具體:丟棄 UDP 特定端口的流量,。
將其他流量通過(guò) CPU redirect 方式重定向給另一個(gè) CPU做進(jìn)一步處理;
性能
我們用 netperf 做性能壓測(cè) [26],。
用 netperf TCP round-trip benchmark,,單個(gè) TCP 連接來(lái)回小的 request/reply,統(tǒng)計(jì) transactions/second,。
模擬的是交互式應(yīng)用,,例如小的遠(yuǎn)程過(guò)程調(diào)用(RPC)。
實(shí)驗(yàn)在單核上進(jìn)行,,模擬多個(gè)流量(正常流量 攻擊流量)競(jìng)爭(zhēng)同一物理資源的場(chǎng)景,。
在 beseline 35K 業(yè)務(wù) TPS(transactions per second)基礎(chǔ)上,打少量 UDP 流量作為攻擊流量,。逐漸加大攻擊流量,,觀察 TPS 的變化。
Fig 7. DDoS 性能,。業(yè)務(wù)吞吐(TPS)隨攻擊流量的變化,。
結(jié)果如上圖所示,,
沒(méi)有 XDP 的一組,性能急劇下降:攻擊流量在 3Mpps 時(shí)性能減半,,3.5Mpps 時(shí)基本跌零,;
有 XDP 程序的一組,攻擊流量達(dá)到 19.5Mpps 之前,,業(yè)務(wù)吞吐保持在 28.5K TPS 以上,,過(guò)了這個(gè)臨界點(diǎn)性能才開(kāi)始急劇下降。
以上結(jié)果表明,,XDP 防御 DDoS 攻擊在實(shí)際中是完全可行的,,單核就能輕松處理 10Gbps 的、都是最小包(minimum-packet)的 DoS 流量,。 這種 DDoS 防御的部署更加靈活,,無(wú)需硬件或應(yīng)用做任何改動(dòng)。
5.3 案例三:負(fù)載均衡(load balancing)
Facebook Katran
負(fù)載均衡的場(chǎng)景,,我們用 Facebook 開(kāi)源的 Katran 作為例子 [15]。Katran 的工作原理是對(duì)外通告服務(wù)的 IP,,這樣目標(biāo)是這個(gè) IP 的流量就會(huì)被路由到 XDP 實(shí)現(xiàn)的負(fù)載均衡器,。
負(fù)載均衡器對(duì)包頭(source packet header)進(jìn)行哈希,以此選擇目標(biāo)應(yīng)用服務(wù)器,。
然后將對(duì)包進(jìn)行封裝(encap),,發(fā)送給應(yīng)用服務(wù)器;
應(yīng)用服務(wù)器解封裝(decap),,處理請(qǐng)求,,然后直接將回包發(fā)給客戶端(DSR 模式)。
在這個(gè)過(guò)程中,,XDP 程序負(fù)責(zé)哈希,、封裝以及將包從接收網(wǎng)卡再發(fā)出去的任務(wù)。 配置信息存儲(chǔ)在 BPF map 中,,整個(gè)封裝邏輯是完全在 eBPF 中實(shí)現(xiàn)的,。
性能
為測(cè)試性能,我們給 Katran XDP 程序配置幾個(gè)固定的目標(biāo)機(jī)器,。 對(duì)照組是 IPVS,,它是 Linux 內(nèi)核的一部分。性能如表 2 所示,,隨 CPU 數(shù)量線性增長(zhǎng),, XDP 比 IPVS 性能高 4.3 倍。
表 2. 負(fù)載均衡器性能(Mpps)
配置:1 VIP/core, 100 DstIPs/VIP.
6 XDP 的未來(lái)方向
XDP已經(jīng)能用于解決真實(shí)問(wèn)題,,但作為L(zhǎng)inux內(nèi)核的一部分,,XDP 還在快速開(kāi)發(fā)過(guò)程中,。
6.1 eBPF 程序的限制
前面提到,加載到 eBPF 虛擬機(jī)的程序必須保證其安全性(不會(huì)破壞內(nèi)核),,因此對(duì) eBPF 程序作了一下限制,,歸結(jié)為兩方面:
確保程序會(huì)終止:在實(shí)現(xiàn)上是通過(guò)禁止循環(huán)和限制程序的最大指令數(shù)(max size of the program);
確保內(nèi)存訪問(wèn)的安全:通過(guò) 3.4 小結(jié)介紹的寄存器狀態(tài)跟蹤(register state tracking)來(lái)實(shí)現(xiàn),。
校驗(yàn)邏輯偏保守
由于校驗(yàn)器的首要職責(zé)是保證內(nèi)核的安全,,因此其校驗(yàn)邏輯比較保守, 凡是它不能證明為安全的,,一律都拒絕,。有時(shí)這會(huì)導(dǎo)致假陰性(false negatives), 即某些實(shí)際上是安全的程序被拒絕加載,;這方面在持續(xù)改進(jìn),。
校驗(yàn)器的錯(cuò)誤提示也已經(jīng)更加友好,以幫助開(kāi)發(fā)者更快定位問(wèn)題,。
近期已經(jīng)支持了 BPF 函數(shù)調(diào)用(function calls),。
正在計(jì)劃支持有限循環(huán)(bounded loops)。
正在提升校驗(yàn)器效率,,以便處理更大的 BPF 程序,。
缺少標(biāo)準(zhǔn)庫(kù)
相比于用戶空間 C 程序,eBPF 程序的另一個(gè)限制是缺少標(biāo)準(zhǔn)庫(kù),,包括 內(nèi)存分配,、線程、鎖等等庫(kù),。
內(nèi)核的生命周期和執(zhí)行上下文管理(life cycle and execution context management )部分地彌補(bǔ)了這一不足,,(例如,加載的 XDP 程序會(huì)為每個(gè)收到的包執(zhí)行),,
內(nèi)核提供的 helper 函數(shù)也部分地彌補(bǔ)了一不足,。
一個(gè)網(wǎng)卡接口只能 attach 一個(gè) XDP 程序
這個(gè)限制其實(shí)也是可以繞過(guò)的:將 XDP 程序組織成程序數(shù)組,通過(guò)尾 調(diào)用,,根據(jù)包上下文在程序之間跳轉(zhuǎn),,或者是將幾個(gè)程序做 chaining。
6.2 用戶體驗(yàn)和調(diào)試
XDP 程序運(yùn)行在內(nèi)核,,因此常規(guī)的用戶空間 debug 工具是用不了的,,但內(nèi)核自帶的 debug 和 introspection 功能是可以用在 XDP (及其他 eBPF 程序)上的。 包括:
但不熟悉內(nèi)核生態(tài)系統(tǒng)的開(kāi)發(fā)者可能會(huì)對(duì)這些工具感到非常陌生,,難以使用,。因此,也出 現(xiàn)了一些更方便普通開(kāi)發(fā)者的工具,,包括 BCC [50],、bpftool [8],、libbpf 函數(shù)庫(kù) [30] 等等。
6.3 驅(qū)動(dòng)支持
設(shè)備要支持 XDP,,需要實(shí)現(xiàn)內(nèi)核核心網(wǎng)絡(luò)棧暴露出的一個(gè) API,。 寫(xiě)作本文時(shí) Linux 4.18 已經(jīng)有 12 種驅(qū)動(dòng)支持 XDP,包括了大部分高速網(wǎng)卡,。 最新列表見(jiàn) [2],。
隨著 XDP 系統(tǒng)的不斷成熟,核心代碼逐漸上移到內(nèi)核中,,驅(qū)動(dòng)需要維護(hù)的代碼越 來(lái)越少,。例如,redirection action 支持新的 target 時(shí),,無(wú)需驅(qū)動(dòng)做任何改動(dòng),。
最后,對(duì)于那些不支持 XDP 的驅(qū)動(dòng),,內(nèi)核提供了 Generic XDPfeature [39],,這是軟件實(shí)現(xiàn)的 XDP,性能會(huì)低一些,, 在實(shí)現(xiàn)上就是將 XDP 的執(zhí)行上移到了核心網(wǎng)絡(luò)棧(core networking stack),。
XDP 在內(nèi)核收包函數(shù) receive_skb() 之前,
Generic XDP在 receive_skb() 之后,,
更多關(guān)于 Generic XDP,可參考參考:容器網(wǎng)絡(luò)|深入理解Cilium
6.4 性能提升
XDP 和 DPDK 之間還有一些性能差距,,一些改進(jìn)工作正在進(jìn)行中:
6.5 QoS 和 Rate Transitions
當(dāng)前,,XDP 還沒(méi)有任何 QoS 機(jī)制。 尤其是,,如果對(duì)端已經(jīng)過(guò)載(例如兩端的網(wǎng)絡(luò)速度或特性不匹配),,XDP 程序是收不到任何背壓(back-pressure)的,
雖然 XDP 中缺少 QoS,,但 Linux 內(nèi)核網(wǎng)絡(luò)棧中卻有很多業(yè)界最佳的 Active Queue Management (AQM) 特性和 packet scheduling algorithms [23],。 這些特性中,部分并不適用于 XDP,,但我們相信能夠 以一種對(duì)包處理應(yīng)用完全透明的方式,,選擇其中部分集成到 XDP。 我們計(jì)劃對(duì)這一方向進(jìn)行更深入研究,。
6.6 加速傳輸層協(xié)議
我們已經(jīng)證明 XDP 能在保留操作系統(tǒng)原有功能的前提下,,集成到操作系統(tǒng)中,實(shí)現(xiàn)高速包數(shù)據(jù),。
目前的 XDP 還是用于無(wú)狀態(tài)包處理(stateless packet processing) ,,如果將這個(gè)模型擴(kuò)展到有狀態(tài)傳輸層協(xié)議(stateful transport protocols),,例如 TCP,它能給依賴可靠/有狀態(tài)傳輸?shù)膽?yīng)用提供類似的性能提升,。
實(shí)際上,,已經(jīng)有一些研究證明,相比于操作系統(tǒng)的協(xié)議棧,,accelerated transport protocols 能顯著提升性能[5, 25, 35, 52],。其中的一個(gè)解決方案 [52] 表明,在保留內(nèi) 核 TCP 協(xié)議棧的的前提下,,原始包處理性能(raw packet processing)存在巨大的提升 空間,。
XDP 非常適用于這種場(chǎng)景,目前也已經(jīng)有一些關(guān)于如何實(shí)現(xiàn)的初步討論 [21],, 雖然離實(shí)際使用還很遠(yuǎn),,但仍然是一個(gè)令人振奮的、擴(kuò)展 XDP 系統(tǒng) scope 的方向,。
6.7 內(nèi)核-用戶空間零拷貝(zero-copy to userspace)
3.1 小節(jié)提到,,XDP 程序能將數(shù)據(jù)包重定向到用戶空間應(yīng)用(userspace application)打 開(kāi)的特殊類型 socket。這可以用于加速客戶端和服務(wù)端在同一臺(tái)機(jī)器的網(wǎng)絡(luò)密集型應(yīng)用(network-heavy applications running on the local machine),。
更多信息可參考: (譯) 利用 ebpf sockmap/redirection 提升 socket 性能(2020),。 這里使用的是 BPF 而非 XDP,但核心原理是一樣的,,只是程序執(zhí)行的位置(hook)不同,。 譯注。
但在目前的實(shí)現(xiàn)中,,這種方式在底層仍然需要拷貝包數(shù)據(jù),,因此性能會(huì)打折扣。
目前已經(jīng)有工作在進(jìn)行,,通過(guò) AF_XDP 實(shí)現(xiàn)真正的數(shù)據(jù)零拷貝,。但這項(xiàng)工作需要 對(duì)網(wǎng)絡(luò)設(shè)備的內(nèi)存處理過(guò)程有一些限制,因此需要設(shè)備驅(qū)動(dòng)的顯式支持,。 第一個(gè)支持這個(gè)功能的 patch 已經(jīng)合并到 4.19內(nèi)核,,更多驅(qū)動(dòng)的支持 正在添加中。初步的性能測(cè)試結(jié)果還是很樂(lè)觀的,,顯示能達(dá)到 20Mpps/core 的內(nèi)核到用戶 空間傳遞(transfer)速度,。
6.8 XDP 作為基礎(chǔ)構(gòu)建模塊(XDP as a building block)
正如 DPDK 用于高層包處理框架的底層構(gòu)建模塊(例如 [31]),XDP 有望成為高層應(yīng)用的運(yùn)行時(shí)環(huán)境 (runtime environment for higher-level applications),。
實(shí)際上,,我們看到一些基于 XDP 的應(yīng)用和框架已經(jīng)出現(xiàn)了。包括
Cilium security middle-ware [3]
Suricata network monitor [4]
Open vSwitch [49]
P4-to-XDP compiler project [51]
甚至還有人嘗試將 XDP 作為 DPDK 的一種底層驅(qū)動(dòng) [53],。
7 總結(jié)
本文描述了 XDP,,一個(gè)安全,、快速、可編程,、集成到操作系統(tǒng)內(nèi)核的包處理框架,。 測(cè)試結(jié)果顯示,XDP 能提供 24Mpps/core 的高處理性能,,這一數(shù)字雖然與基于 kernel bypass 的 DPDK 仍有差距,,但提供了其他一些非常有競(jìng)爭(zhēng)力的優(yōu)勢(shì):
兼容內(nèi)核安全和管理框架(kernel bypass 方式在 bypass 內(nèi)核網(wǎng)絡(luò)棧的同時(shí),也將安全和設(shè)備管理等這些極其重要的基礎(chǔ)設(shè)施 bypass 了),;
兼容內(nèi)核網(wǎng)絡(luò)棧,,可選擇性利用內(nèi)核已有的基礎(chǔ)設(shè)施和功能;
提供與內(nèi)核 API 一樣穩(wěn)定的編程接口,;
對(duì)應(yīng)用完全透明,;
更新、替換程序的過(guò)程不會(huì)引起服務(wù)中斷,;
無(wú)需專門(mén)硬件,,無(wú)需獨(dú)占 CPU 等資源。
相比于 kernel bypass 這種非此即彼,、完全繞開(kāi)內(nèi)核的方式,,我們相信 XDP 有更廣闊的的應(yīng)用前景。Facebook,、Cloudflare 等公司實(shí)際落地的 XDP 應(yīng)用,,更加增強(qiáng)了我們的這種信心。
最后,,XDP系統(tǒng)還在快速發(fā)展,,前面也列出了一些正在未來(lái)可能會(huì)做的開(kāi)發(fā)/優(yōu)化工作。
致謝
XDP has been developed by the Linux networking community for a number of years, and the authors would like to thank everyone who has been involved. In particular,
Alexei Starovoitov has been instrumental in the development of the eBPF VM and verifier;
Jakub Kicinski has been a driving force behind XDP hardware offloading and the bpftool utility;
Bj?rn T?pel and Magnus Karlsson have been leading the AF_XDP and userspace zero-copy efforts.
We also wish to extend our thanks to the anonymous reviewers, and to our shepherd Srinivas Narayana, for their helpful comments.
參考文獻(xiàn)
https:///blog/xdp-paper-acm-2018-zh/
- END -
看完一鍵三連在看,,轉(zhuǎn)發(fā),,點(diǎn)贊
是對(duì)文章最大的贊賞,極客重生感謝你
深入理解Linux內(nèi)核之內(nèi)存尋址
2022新年重磅技術(shù)分享|深入理解Linux操作系統(tǒng)
一些優(yōu)秀的后端開(kāi)源項(xiàng)目!
你好,,這里是極客重生,,我是阿榮,,大家都叫我榮哥,,從華為->外企->到互聯(lián)網(wǎng)大廠,,目前是大廠資深工程師,多次獲得五星員工,,多年職場(chǎng)經(jīng)驗(yàn),,技術(shù)扎實(shí),專業(yè)后端開(kāi)發(fā)和后臺(tái)架構(gòu)設(shè)計(jì),,熱愛(ài)底層技術(shù),,豐富的實(shí)戰(zhàn)經(jīng)驗(yàn),分享技術(shù)的本質(zhì)原理,,希望幫助更多人蛻變重生,,拿BAT大廠offer,培養(yǎng)高級(jí)工程師能力,,成為技術(shù)專家,,實(shí)現(xiàn)高薪夢(mèng)想,期待你的關(guān)注,!點(diǎn)擊藍(lán)字查看我的成長(zhǎng)之路,。
校招/社招/簡(jiǎn)歷/面試技巧/大廠技術(shù)棧分析/后端開(kāi)發(fā)進(jìn)階/優(yōu)秀開(kāi)源項(xiàng)目/直播分享/技術(shù)視野/實(shí)戰(zhàn)高手等, 極客星球希望成為最有技術(shù)價(jià)值星球,盡最大努力為星球的同學(xué)提供技術(shù)和成長(zhǎng)幫助,!詳情查看->極客星球
求點(diǎn)贊,,在看,分享三連的昂貴性能開(kāi)銷。
但是,,操作系統(tǒng)被旁路(繞過(guò))之后,,它的應(yīng)用隔離(application isolation) 和安全機(jī)制(security mechanisms)就都失效了;一起失效的還有各種經(jīng)過(guò)已經(jīng) 充分測(cè)試的配置,、部署和管理工具,。
為解決這個(gè)問(wèn)題,我們提出一種新的可編程包處理方式:eXpress Data Path (XDP),。
XDP 提供了一個(gè)仍然基于操作系統(tǒng)內(nèi)核的安全執(zhí)行環(huán)境,,在設(shè)備驅(qū)動(dòng)上下文(device driver context)中執(zhí)行,可用于定制各種包處理應(yīng)用,。
XDP 是主線內(nèi)核(mainline Linux kernel)的一部分,,與現(xiàn)有的內(nèi)核 網(wǎng)絡(luò)棧(kernel’s networking stack)完全兼容,二者協(xié)同工作,。
XDP 應(yīng)用(application)通過(guò) C 等高層語(yǔ)言編寫(xiě),,然后編譯成特定字節(jié)碼;出于安 全考慮,,內(nèi)核會(huì)首先對(duì)這些字節(jié)碼執(zhí)行靜態(tài)分析,,然后再將它們翻譯成 處理器原生指令(native instructions)。
測(cè)試結(jié)果顯示,,XDP 能達(dá)到 24Mpps/core的處理性能,。
為展示 XDP 靈活的編程模型,本文還將給出三個(gè)程序示例,,
layer-3 routing(三層路由轉(zhuǎn)發(fā))
inline DDoS protection(DDoS 防護(hù))
layer-4 load balancing(四層負(fù)載均衡)
1 引言
軟件實(shí)現(xiàn)高性能包處理的場(chǎng)景,,對(duì)每個(gè)包的處理耗時(shí)有著極高的要求。通用目的操作系統(tǒng)中 的網(wǎng)絡(luò)棧更多是針對(duì)靈活性的優(yōu)化,,這意味著它們花在每個(gè)包上 的指令太多了,,不適合網(wǎng)絡(luò)高吞吐的場(chǎng)景。
因此,,隨后出現(xiàn)了一些專門(mén)用于包處理的軟件開(kāi)發(fā)工具,,例如 Data Plane Development Kit (DPDK) [16]。這些工具一般都會(huì)完全繞過(guò)內(nèi)核,,將網(wǎng)絡(luò)硬件直接交 給用戶態(tài)的網(wǎng)絡(luò)應(yīng)用,,并需要獨(dú)占一個(gè)或多個(gè) CPU。
1.1 現(xiàn)有方案(kernel bypass)存在的問(wèn)題
內(nèi)核旁路方式可以顯著提升性能,,但缺點(diǎn)也很明顯:
很難與現(xiàn)有系統(tǒng)集成;
上層應(yīng)用必須要將內(nèi)核中已經(jīng)非常成熟的模塊在用戶態(tài)重新實(shí)現(xiàn)一遍,,例如路由表,、高層協(xié)議棧等;
最壞的情況下,這種包處理應(yīng)用只能工作在一個(gè)完全隔絕的環(huán)境,,因?yàn)閮?nèi)核提供的常見(jiàn)工具和部署方式在這種情況下都不可用了,。
導(dǎo)致系統(tǒng)越來(lái)越復(fù)雜,而且破壞了操作系統(tǒng)內(nèi)核在把控的安全邊界,。 在基礎(chǔ)設(shè)施逐漸遷移到 Kubernetes/Docker 等容器環(huán)境的背景下,,這一點(diǎn)顯得尤其嚴(yán)重, 因?yàn)樵谶@種場(chǎng)景下,,內(nèi)核擔(dān)負(fù)著資源抽象和隔離的重任,。
1.2 新方案:給內(nèi)核網(wǎng)絡(luò)棧添加可編程能力
對(duì)此,本文提供了另一種解決方案:給內(nèi)核網(wǎng)絡(luò)棧添加可編程能力,。這使得我們能在 兼容各種現(xiàn)有系統(tǒng),、復(fù)用已有網(wǎng)絡(luò)基礎(chǔ)設(shè)施的前提下,仍然實(shí)現(xiàn)高速包處理,。 這個(gè)框架稱為XDP,,
XDP定義了一個(gè)受限的執(zhí)行環(huán)境(a limited execution environment),運(yùn)行在一個(gè) eBPF 指令虛擬機(jī)中,。eBPF 是 BSD Packet Filter (BPF) [37] 的擴(kuò)展,。
XDP 程序運(yùn)行在內(nèi)核上下文中,此時(shí)內(nèi)核自身都還沒(méi)有接觸到包數(shù)據(jù)( before the kernel itself touches the packet data),,這使得我們能在網(wǎng)卡收到包后 最早能處理包的位置,,做一些自定義數(shù)據(jù)包處理(包括重定向)。
內(nèi)核在加載(load)時(shí)執(zhí)行靜態(tài)校驗(yàn),,以確保用戶提供的 XDP 程序的安全,。
之后,程序會(huì)被動(dòng)態(tài)編譯成原生機(jī)器指令(native machine instructions),,以獲得高性能,。
XDP已經(jīng)在過(guò)去的幾個(gè)內(nèi)核 release 中逐漸合并到內(nèi)核,但在本文之前,,還沒(méi)有關(guān)于 XDP 系統(tǒng)的 完整架構(gòu)介紹,。本文將對(duì) XDP 做一個(gè)高層介紹。
1.3 新方案(XDP)的優(yōu)點(diǎn)
我們的測(cè)試結(jié)果顯示 XDP 能取得 24Mpps/core
的處理性能,,這雖然與 DPDK 還有差距,, 但相比于后者這種 kernel bypass 的方式,XDP 有非常多的優(yōu)勢(shì),。
本文是 2018 年的測(cè)試結(jié)果,,更新的一些性能(及場(chǎng)景)對(duì)比可參考 (譯) 為容器時(shí)代設(shè)計(jì)的高級(jí) eBPF 內(nèi)核特性(FOSDEM, 2021)。 譯注,。
具體地,,XDP:
與內(nèi)核網(wǎng)絡(luò)棧協(xié)同工作,,將硬件的控制權(quán)完全留在內(nèi)核范圍內(nèi)。帶來(lái)的好處:
無(wú)需任何特殊硬件特性,,任何有 Linux 驅(qū)動(dòng)的網(wǎng)卡都可以支持,, 現(xiàn)有的驅(qū)動(dòng)只需做一些修改,就能支持 XDP hooks,。
可以選擇性地復(fù)用內(nèi)核網(wǎng)絡(luò)棧中的現(xiàn)有功能,,例如路由表或 TCP/IP 協(xié)議棧,在保持配置接口不變的前提下,,加速關(guān)鍵性能路徑(critical performance paths),。
保證 eBPF 指令集和 XDP 相關(guān)的編程接口(API)的穩(wěn)定性。
與常規(guī) socket 層交互時(shí),,沒(méi)有從用戶態(tài)將包重新注入內(nèi)核的昂貴開(kāi)銷,。
對(duì)應(yīng)用透明。這創(chuàng)造了一些新的部署場(chǎng)景/方式,,例如直接在應(yīng)用所 在的服務(wù)器上部署 DoS 防御(而非中心式/網(wǎng)關(guān)式 DoS 防御),。
服務(wù)不中斷的前提下動(dòng)態(tài)重新編程(dynamically re-program), 這意味著可以按需加入或移除功能,,而不會(huì)引起任何流量中斷,,也能動(dòng)態(tài)響應(yīng)系統(tǒng)其他部分的的變化。
無(wú)需預(yù)留專門(mén)的 CPU 做包處理,,這意味著 CPU 功耗與流量高低直接相關(guān),,更節(jié)能。
1.4 本文組織結(jié)構(gòu)
接下來(lái)的內(nèi)容介紹 XDP 的設(shè)計(jì),,并做一些性能分析,。結(jié)構(gòu)組織如下:
Section 2介紹相關(guān)工作;
Section 3 介紹 XDP 系統(tǒng)的設(shè)計(jì),;
Section 4做一些性能分析,;
Section 5提供了幾個(gè)真實(shí) XDP 場(chǎng)景的程序例子;
Section 6討論 XDP 的未來(lái)發(fā)展方向,;
Section 7總結(jié),。
2 相關(guān)工作
XDP 當(dāng)然不是第一個(gè)支持可編程包處理的系統(tǒng) —— 這一領(lǐng)域在過(guò)去幾年發(fā)展勢(shì)頭良好, 并且趨勢(shì)還在持續(xù),。業(yè)內(nèi)已經(jīng)有了幾種可編程包處理框架,,以及基于這些框架的新型應(yīng)用,包括:
單一功能的應(yīng)用,,如 switching [47], routing [19], named-based forwarding [28], classification [48], caching [33] or traffic generation [14],。
更加通用、且高度可定制的包處理解決方案,,能夠處理從多種源收來(lái)的數(shù)據(jù)包 [12, 20, 31, 34, 40, 44],。
要基于通用(Common Off The Shelf,,COTS)硬件實(shí)現(xiàn)高性能包處理,就必須解決 網(wǎng)卡(NIC)和包處理程序之間的所有瓶頸,。由于性能瓶頸主要來(lái)源于內(nèi)核和用戶態(tài)應(yīng)用之 間的接口(系統(tǒng)調(diào)用開(kāi)銷非常大,另外,,內(nèi)核功能豐富,,但也非常復(fù)雜), 低層(low-level)框架必須通過(guò)這樣或那樣的方式來(lái)降低這些開(kāi)銷,。
現(xiàn)有的一些框架通過(guò)幾種不同的方式實(shí)現(xiàn)了高性能,,XDP 構(gòu)建在其中一些技術(shù)之上。 接下來(lái)對(duì) XDP 和它們的異同做一些比較分析,。
2.1 用戶態(tài)輪詢 vs. XDP
DPDK[16] 可能是使用最廣泛的高性能包處理框架,。它最初只支持 Intel 網(wǎng)卡,后來(lái)逐 步擴(kuò)展到其他廠商的網(wǎng)卡,。DPDK 也稱作內(nèi)核旁路框架(kernel bypass framework),, 因?yàn)樗鼘⒕W(wǎng)絡(luò)硬件的控制權(quán)從內(nèi)核轉(zhuǎn)移到了用戶態(tài)的網(wǎng)絡(luò)應(yīng)用,完全避免了內(nèi)核-用戶態(tài) 之間的切換開(kāi)銷,。
與DPDK類似的還有PF_RING ZC module [45] 和 hardware-specific Solarflare OpenOnload [24],。
在現(xiàn)有的所有框架中,內(nèi)核旁路方式性能是最高的[18],;但如引言中指 出,,這種方式在管理、維護(hù)和安全方面都存在不足,。
XDP 采用了一種與內(nèi)核旁路截然相反的方式:相比于將網(wǎng)絡(luò)硬件的控制權(quán)上移到用戶空間,, XDP 將性能攸關(guān)的包處理操作直接放在內(nèi)核中,在操作系統(tǒng)的網(wǎng)絡(luò)棧之前執(zhí)行,。
這種方式同樣避免了內(nèi)核-用戶態(tài)切換開(kāi)銷(所有操作都在內(nèi)核),,
但仍然由內(nèi)核來(lái)管理硬件,因此保留了操作系統(tǒng)提供的管理接口和安全防護(hù)能力,。
這里的主要?jiǎng)?chuàng)新是:使用了一個(gè)虛擬執(zhí)行環(huán)境,,它能對(duì)加載的 程序進(jìn)行校驗(yàn),確保它們不會(huì)對(duì)內(nèi)核造成破壞,。
2.2 內(nèi)核模塊 vs. XDP
在 XDP 之前,,以內(nèi)核模塊(kernel module)方式實(shí)現(xiàn)包處理功能代價(jià)非常高, 因?yàn)槌绦驁?zhí)行出錯(cuò)時(shí)可能會(huì)導(dǎo)致整個(gè)系統(tǒng)崩潰,,而且內(nèi)核的內(nèi)部 API 也會(huì)隨著時(shí)間發(fā)生變化,。 因此也就不難理解為什么只有很少的系統(tǒng)采用了這種方式。其中做的比較好包括
這幾個(gè)系統(tǒng)都支持靈活的配置,,適用于多種場(chǎng)景,,取得比較小的平攤代價(jià),。
XDP通過(guò):
提供一個(gè)安全的執(zhí)行環(huán)境,以及內(nèi)核社區(qū)支持,,提供與那些暴露到用戶空間一樣穩(wěn)定的內(nèi)核 API
極大地降低了那些將處理過(guò)程下沉到內(nèi)核的應(yīng)用(applications of moving processing into the kernel)的成本,。
此外,XDP 程序也能夠完全繞過(guò)內(nèi)核網(wǎng)絡(luò)棧(completely bypass),, 與在內(nèi)核網(wǎng)絡(luò)棧中做 hook 的傳統(tǒng)內(nèi)核模塊相比,,性能也更高。
XDP 除了能將處理過(guò)程下沉到內(nèi)核以獲得最高性能之外,,還支持在程序中執(zhí)行重定向(redirection)操作,,完全繞過(guò)內(nèi)核網(wǎng)絡(luò)棧,將包送到特殊類型的用戶空間 socket,; 甚至能工作在 zero-copy 模式,,進(jìn)一步降低開(kāi)銷。
這種模式與 Netmap [46] 和 PF_RING [11] 方式類似,,但后者是在沒(méi)有完全繞過(guò)內(nèi) 核的情況下,,通過(guò)降低從網(wǎng)絡(luò)設(shè)備到用戶態(tài)應(yīng)用(network device to userspace application)之間的傳輸開(kāi)銷,實(shí)現(xiàn)高性能包處理,。
內(nèi)核模塊方式的另一個(gè)例子是 Packet I/O engine,,這是 PacketShader [19] 的組成部分, 后者專用于 Arrakis [43] and ClickOS [36] 之類的特殊目的操作系統(tǒng),。
2.3 可編程硬件 vs. XDP
可編程硬件設(shè)備也是一種實(shí)現(xiàn)高性能包處理的方式,。
一個(gè)例子是 NetFPGA[32],通過(guò)對(duì)它暴露的 API 進(jìn)行編程,,能夠在這種基于 FPGA 的專 用設(shè)備上運(yùn)行任何包處理任務(wù),。
P4 編程語(yǔ)言 [7] 致力于將這種可編程能力擴(kuò)展到更廣泛的包處理硬件上(巧合的是,它還包括了一個(gè) XDP backend [51]),。
某種意義上來(lái)說(shuō),,XDP 可以認(rèn)為是一種 offload方式:
性能敏感的處理邏輯下放到網(wǎng)卡驅(qū)動(dòng)中,以提升性能,;
其他的處理邏輯仍然走內(nèi)核網(wǎng)絡(luò)棧,;
如果沒(méi)有用到內(nèi)核 helper 函數(shù),那整個(gè) XDP 程序都可以 offload 到網(wǎng)卡(目前 Netronome smart-NICs [27] 已經(jīng)支持),。
2.4 小結(jié)
XDP 提供了一種高性能包處理方式,,與已有方式相比,在性能,、與現(xiàn)有系統(tǒng)的集成,、靈活性 等方面取得了更好的平衡。接下來(lái)介紹 XDP 是如何取得這種平衡的,。
3 XDP 設(shè)計(jì)
XDP 的設(shè)計(jì)理念:
這種與內(nèi)核的深度集成顯然會(huì)給設(shè)計(jì)帶來(lái)一些限制,,在 XDP 組件合并到 Linux 的過(guò)程中,我們也收到了許多來(lái)自社區(qū)的反饋,,促使我們不斷調(diào)整 XDP 的設(shè)計(jì),,但 這些設(shè)計(jì)反思不在本文討論范圍之內(nèi)。
3.0 XDP 系統(tǒng)架構(gòu)
圖 1 描繪了整個(gè) XDP 系統(tǒng),,四個(gè)主要組成部分:
XDP driver hook:XDP 程序的主入口,,在網(wǎng)卡收到包執(zhí)行。
eBPF virtual machine:執(zhí)行 XDP 程序的字節(jié)碼,,以及對(duì)字節(jié)碼執(zhí)行 JIT 以提升性能。
BPF maps:內(nèi)核中的 key/value 存儲(chǔ),,作為圖中各系統(tǒng)的主要通信通道,。
eBPF verifier:加載程序時(shí)對(duì)其執(zhí)行靜態(tài)驗(yàn)證,以確保它們不會(huì)導(dǎo)致內(nèi)核崩潰,。
Fig 1. XDP 與 Linux 網(wǎng)絡(luò)棧的集成,。這里只畫(huà)了 ingress 路徑,以免圖過(guò)于復(fù)雜,。
上圖是 ingress 流程,。網(wǎng)卡收到包之后,在處理包數(shù)據(jù)(packet data)之前,,會(huì)先執(zhí)行 main XDP hook 中的 eBPF 程序,。 這段程序可以選擇:
丟棄(drop)這個(gè)包;或者
通過(guò)當(dāng)前網(wǎng)卡將包再發(fā)送(send)出去,;或者
將包重定向(redirect)到其他網(wǎng)絡(luò)接口(包括虛擬機(jī)的虛擬網(wǎng)卡),,或者通過(guò) AF_XDP socket 重定向到用戶空間;或者
放行(allow)這個(gè)包,,如果后面沒(méi)有其他原因?qū)е碌?drop,,這個(gè)包就會(huì)進(jìn)入常規(guī)的內(nèi)核網(wǎng)絡(luò)棧。
如果是這種情況,,也就是放行包進(jìn)入內(nèi)核網(wǎng)絡(luò)棧,,那接下來(lái)在將包放到發(fā)送隊(duì)列之前(before packets are queued for transmission), 還有一個(gè)能執(zhí)行 BPF 程序的地方:TC BPF hook,。
更多信息,,可參考 (譯) [論文] 邁向完全可編程 tc 分類器(cls_bpf)(NetdevConf,2016),。 譯注,。
此外,圖 1 中還可以看出,,不同的 eBPF 程序之間,、eBPF 程序和用戶空間應(yīng)用之間,,都能夠通過(guò) BPF maps 進(jìn)行通信。
3.1 XDP driver hook
在設(shè)備驅(qū)動(dòng)中執(zhí)行,,無(wú)需上下文切換
XDP 程序在網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)中執(zhí)行,,網(wǎng)絡(luò)設(shè)備每收到一個(gè)包,程序就執(zhí)行一次,。
相關(guān)代碼實(shí)現(xiàn)為一個(gè)內(nèi)核庫(kù)函數(shù)(library function),,因此程序直接 在設(shè)備驅(qū)動(dòng)中執(zhí)行,無(wú)需切換到用戶空間上下文,。
在軟件最早能處理包的位置執(zhí)行,,性能最優(yōu)
回到上面圖 1 可以看到:程序在網(wǎng)卡收到包之后最早能處理包的位置執(zhí)行 —— 此時(shí)內(nèi)核還沒(méi)有為包分配 struct sk_buff 結(jié)構(gòu)體, 也沒(méi)有執(zhí)行任何解析包的操作,。
XDP 程序典型執(zhí)行流
下圖是一個(gè)典型的 XDP 程序執(zhí)行流:
Fig 2. 典型 XDP 程序的執(zhí)行流,。
網(wǎng)卡收到一個(gè)包時(shí),XDP 程序依次執(zhí)行:
提取包頭中的信息(例如 IP,、MAC,、Port、Proto 等),,
執(zhí)行到程序時(shí),,系統(tǒng)會(huì)傳遞給它一個(gè)上下文對(duì)象(context object)作為參賽(即 struct xdp_md *ctx,后面有例子),,其中包括了指向原 始包數(shù)據(jù)的指針,,以及描述這個(gè)包是從哪個(gè)網(wǎng)卡的哪個(gè)接口接收上來(lái)的等元數(shù)據(jù)字段。
讀取或更新一些資源的元信息(例如更新統(tǒng)計(jì)信息),;
解析包數(shù)據(jù)之后,,XDP 程序可以讀取 ctx 中的包元數(shù)據(jù)(packet metadata) 字段,例如從哪個(gè)網(wǎng)卡的哪個(gè)接口收上來(lái)的(ifindex),。除此之外,,ctx 對(duì)象還允許 程序訪問(wèn)與包數(shù)據(jù)毗鄰的一塊特殊內(nèi)存區(qū)域(cb, control buffer), 在包穿越整個(gè)系統(tǒng)的過(guò)程中,,可以將自定義的數(shù)據(jù)塞在這里,。
除了 per-packet metadata,XDP 程序還可以通過(guò) BPF map 定義和訪問(wèn)自己的持久數(shù)據(jù),,以及通過(guò)各種 helper 函數(shù)訪問(wèn)內(nèi)核基礎(chǔ)設(shè)施,。
BPF map 使 BPF 程序能與系統(tǒng)的其他部分之間通信;
Helpers 使 BPF 程序能利用到某些已有的內(nèi)核功能(例如路由表),, 而無(wú)需穿越整個(gè)內(nèi)核網(wǎng)絡(luò)棧,。
如果有需要,對(duì)這個(gè)包進(jìn)行 rewrite header操作,
程序能修改包數(shù)據(jù)的任何部分,,包括添加或刪除包頭,。這使得 XDP 程序能執(zhí)行封裝/接封裝操作,以及重寫(xiě)(rewrite)地址字段然后轉(zhuǎn)發(fā)等操作,。
內(nèi)核 helper 函數(shù)各有不同用途,,例如修改一個(gè)包之后,計(jì)算新的校驗(yàn)和(checksum),。
進(jìn)行最后的判決(verdict),,確定接下來(lái)對(duì)這個(gè)包執(zhí)行什么操作;
判決結(jié)果包括:
重定向功能的用途:
這些不同的路徑,,在圖 1 對(duì)應(yīng)的是幾條實(shí)線,。
將重定向判決(verdict)與重定向目標(biāo)(target)分開(kāi),使得重定向目標(biāo)類型很容易擴(kuò)展,; 另外,,由于重定向參數(shù)(目標(biāo))是通過(guò) BPF map 查詢的,因此無(wú)需修 改 XDP 程序,,就能動(dòng)態(tài)修改重定向目標(biāo)。
三種簡(jiǎn)單返回碼:丟棄這個(gè)包,、通過(guò)接收時(shí)的網(wǎng)卡將包重新發(fā)送出去,、允許這個(gè)包進(jìn)入內(nèi)核網(wǎng)絡(luò)棧;
第四種返回碼 redirect:允許 XDP 程序指定網(wǎng)卡,、CPU,、用戶態(tài) socket等,將包重定向過(guò)去,。
將原始包通過(guò)另一個(gè)網(wǎng)卡(包括虛擬機(jī)的虛擬網(wǎng)卡)發(fā)送出去,;
轉(zhuǎn)發(fā)給指定 CPU做進(jìn)一步處理,;
轉(zhuǎn)發(fā)給 AF_XDP 類型的 socket做進(jìn)一步處理,;
程序還能通過(guò)尾調(diào)用(tail call),,將控制權(quán)交給另一個(gè) XDP 程序,; 通過(guò)這種方式,,可以將一個(gè)大程序拆分成幾個(gè)邏輯上的小程序(例如,,根據(jù) IPv4/IPv6),。
由于 XDP 程序可包含任意指令,,因此前三步(讀取包數(shù)據(jù),、處理元數(shù)據(jù),、重寫(xiě)包數(shù)據(jù)) 順序可以是任意的,而且支持多層嵌套,。 但實(shí)際中為了獲得高性能,,大部分情況下還是將執(zhí)行結(jié)構(gòu)組織成這順序的三步。
3.2 eBPF 虛擬機(jī)
XDP 程序在 Extended BPF (eBPF) 虛擬機(jī)中執(zhí)行,。eBPF 是早期 BSD packet filter (BPF) [37] 的擴(kuò)展,,后者在過(guò)去的幾十年中廣泛 應(yīng)用于各種包處理工具,。
BPF 使用 基于寄存器的(register-based) virtual machine 來(lái)描述 過(guò)濾動(dòng)作(filtering actions)。
eBPF 虛擬機(jī)支持動(dòng)態(tài)加載(loading)和重加載(re-loading)程序,,內(nèi)核管理所有 BPF 程序的生命周期,。
3.3 BPF maps
eBPF 程序在觸發(fā)內(nèi)核事件時(shí)執(zhí)行(例如,觸發(fā) XDP 程序執(zhí)行的,,是收包事件),。 程序每次執(zhí)行時(shí),初始狀態(tài)都是相同的(即程序是無(wú)狀態(tài)的),,它們無(wú)法直接訪問(wèn)內(nèi)核中的持久存儲(chǔ)(BPF map),。為此,內(nèi)核提供了訪問(wèn) BPF map 的 helper 函數(shù),。
BPF map 是 key/value 存儲(chǔ),,在加載 eBPF 程序時(shí)定義(defined upon loading an eBPF program)。
用途:
持久存儲(chǔ),。例如一個(gè) eBPF 程序每次執(zhí)行時(shí),,都會(huì)從里面獲取上一次的狀態(tài)。
用于協(xié)調(diào)兩個(gè)或多個(gè) eBPF 程序,。例如一個(gè)往里面寫(xiě)數(shù)據(jù),,一個(gè)從里面讀數(shù)據(jù)。
用于用戶態(tài)程序和內(nèi)核 eBPF 程序之間的通信,。
3.4 eBPF verifier
唯一加載入口:bpf()
系統(tǒng)調(diào)用
由于 eBPF 代碼直接運(yùn)行在內(nèi)核地址空間,,因此它能直接訪問(wèn) —— 也可 能是破壞 —— 任何內(nèi)存。為防止這種情況發(fā)生,,內(nèi)核規(guī)定只能通過(guò)唯一入口( bpf() 系統(tǒng)調(diào)用)加載 BPF 程序,。
加載 BPF 程序時(shí),位于內(nèi)核中的校驗(yàn)器首先會(huì)對(duì)字節(jié)碼程序進(jìn)行靜態(tài)分析,,以確保
程序中沒(méi)有任何不安全的操作(例如訪問(wèn)任意內(nèi)存),,
程序會(huì)終止(terminate)。通過(guò)下面這兩點(diǎn)來(lái)實(shí)現(xiàn):
禁止循環(huán)操作
限制程序最大指令數(shù)
校驗(yàn)器工作原理:two-pass DAG
校驗(yàn)器的工作原理:首先根據(jù)程序的控制流構(gòu)建一個(gè)有向無(wú)環(huán)圖(DAG),, 然后對(duì) DAG 執(zhí)行如下校驗(yàn):
首先,,對(duì) DAG 進(jìn)行一次深度優(yōu)先搜索(depth-first search),以 確保它是無(wú)環(huán)的(acyclic),,例如,,沒(méi)有循環(huán),也不包含不支持或無(wú)法執(zhí)行到的指令,。
然后,,再掃描一遍,這次會(huì)遍歷 DAG 的所有可能路徑。這次掃描的目的是:
程序執(zhí)行 load
或 call
指令時(shí),,如果參數(shù)不合法,,就會(huì)在這里被拒絕。參數(shù)合法 性是通過(guò)在程序執(zhí)行期間跟蹤所有寄存器和棧變量的狀態(tài)(states of registers and stack variables)來(lái)實(shí)現(xiàn)的,。
內(nèi)存越界和空指針檢查:職責(zé)上移到程序自身/開(kāi)發(fā)者
這種跟蹤寄存器狀態(tài)的機(jī)制是為了在無(wú)法預(yù)知內(nèi)存邊界的情況下,,仍然確保程序 的內(nèi)存訪問(wèn)不會(huì)越界,。無(wú)法預(yù)知內(nèi)存邊界是因?yàn)椋?/p>
為解決這個(gè)問(wèn)題,校驗(yàn)器會(huì)檢查已加載的程序自身是否會(huì)做如下檢查:
解引用指針前做了內(nèi)存邊界檢查,,
查詢 map 之前是檢查了 map 指針是否為空,。
這種方式將處理邏輯中的安全檢查和遇到錯(cuò)誤時(shí)如何處理的控制權(quán)都 交給了 BPF 程序的編寫(xiě)者。
跟蹤數(shù)據(jù)訪問(wèn)操作和值范圍
為跟蹤數(shù)據(jù)訪問(wèn),,校驗(yàn)器會(huì)跟蹤
數(shù)據(jù)類型
指針偏置(pointer offsets)
所有寄存器的可能值范圍
程序開(kāi)始時(shí):
接下來(lái)程序每執(zhí)行一步,,寄存器狀態(tài)就會(huì)更新一次,。當(dāng)寄存器中存入一個(gè)新值時(shí),這個(gè)寄存器 還會(huì)繼承與這個(gè)值相關(guān)的狀態(tài)變量(inherits the state variables from the source of the value),。
算術(shù)操作會(huì)影響標(biāo)量類型的值的范圍(value ranges of scalar types),以及指針類型的 offset,。 可能的最大范圍(max possible range)存儲(chǔ)在狀態(tài)變量中,,例如往寄存器中 load 一個(gè)字節(jié)時(shí), 這個(gè)寄存器的可能值范圍就設(shè)置為 0~255,。指令圖(instruction graph)中的 各邏輯分支就會(huì)根據(jù)操作結(jié)果更新寄存器狀態(tài),。例如,比較操作 R1 > 10,,
不同類型數(shù)據(jù)的校驗(yàn)信息來(lái)源(source of truth)
利用狀態(tài)變量中存儲(chǔ)的范圍信息,,校驗(yàn)器就能預(yù)測(cè)每個(gè) load 指令能訪問(wèn)的所有 內(nèi)存范圍,,確保它執(zhí)行的都是合法內(nèi)存訪問(wèn)。
對(duì)于包數(shù)據(jù)(packet data)的訪問(wèn),,會(huì)與 context 對(duì)象中的 data_end
變量做比較,;
對(duì)于 BPFmap 中獲取的值,或用到 map 定義中聲明的 data size 信息;
對(duì)于棧上存儲(chǔ)的值,,會(huì)檢查狀態(tài)變量中記錄的值范圍,;
對(duì)于指針?biāo)阈g(shù)操作(pointer arithmetic)還會(huì)施加額外的限制,指針通常不能被轉(zhuǎn)換成整形值,。
只要校驗(yàn)器無(wú)法證明某個(gè)操作是安全,,該 BPF 程序在加載時(shí)(load time)就會(huì)被拒絕。 除此之外,,校驗(yàn)器還會(huì)利用范圍信息確保內(nèi)存的對(duì)齊訪問(wèn)(enforce aligned memory access),。
校驗(yàn)器的目的
需要說(shuō)明的是,校驗(yàn)器的目的是避免將內(nèi)核內(nèi)部(the internals of the kernel )暴露給惡意或有缺陷的 eBPF 程序,,而非確保程序中函數(shù)的實(shí)現(xiàn)已經(jīng)是最高效的,。
換句話說(shuō),如果 XDP 程序中處理邏輯過(guò)多,,也可能會(huì)導(dǎo)致機(jī)器變慢 ,;如果代碼寫(xiě)的有問(wèn)題,也可能會(huì)破壞包數(shù)據(jù),。出于這些原因,,加載 BPF 程序需要 管理員權(quán)限(root)。避免這些 bug 的責(zé)任在程序員,,但選擇將哪些程序加載 到系統(tǒng)的權(quán)限在管理員,。
3.5 XDP 程序示例
下面是一個(gè)簡(jiǎn)單的 XDP 程序,展示了前面介紹的一些特性,。 程序會(huì)解析包數(shù)據(jù),,判斷如果是 UDP 包,直接交換源和目的 MAC 地址,,然后將包從相同網(wǎng)卡再發(fā)送回去,,
雖然這是一個(gè)非常簡(jiǎn)單的例子,但真實(shí)世界中的 XDP 程序用到的組件和特性,,這里基本都具備了,。
// 從內(nèi)核 BPF 代碼示例 xdp2_kern.c 修改而來(lái)。
1 // 用于統(tǒng)計(jì)包數(shù)
2 struct bpf_map_def SEC('maps') rxcnt = {
3 .type = BPF_MAP_TYPE_PERCPU_ARRAY,
4 .key_size = sizeof(u32), // IP 協(xié)議類型,,即 IPv4/IPv6
5 .value_size = sizeof(long), // 包數(shù)
6 .max_entries = 256,
7 };
8
9 // 直接操作包數(shù)據(jù)(direct packet data access),,交換 MAC 地址
10 static void swap_src_dst_mac(void *data)
11 {
12 unsigned short *p = data;
13 unsigned short dst[3];
14 dst[0] = p[0]; dst[1] = p[1]; dst[2] = p[2];
15 p[0] = p[3]; p[1] = p[4]; p[2] = p[5];
16 p[3] = dst[0]; p[4] = dst[1]; p[5] = dst[2];
17 }
18
19 static int parse_ipv4(void *data, u64 nh_off, void *data_end)
20 {
21 struct iphdr *iph = data nh_off;
22 if (iph 1 > data_end)
23 return 0;
24 return iph->protocol;
25 }
26
27 SEC('xdp1') // marks main eBPF program entry point
28 int xdp_prog1(struct xdp_md *ctx)
29 {
30 void *data_end = (void *)(long)ctx->data_end;
31 void *data = (void *)(long)ctx->data;
32 struct ethhdr *eth = data; int rc = XDP_DROP;
33 long *value; u16 h_proto; u64 nh_off; u32 ipproto;
34
35 nh_off = sizeof(*eth);
36 if (data nh_off > data_end)
37 return rc;
38
39 h_proto = eth->h_proto;
40
41 /* check VLAN tag; could be repeated to support double-tagged VLAN */
42 if (h_proto == htons(ETH_P_8021Q) || h_proto == htons(ETH_P_8021AD)) {
43 struct vlan_hdr *vhdr;
44
45 vhdr = data nh_off;
46 nh_off = sizeof(struct vlan_hdr);
47 if (data nh_off > data_end)
48 return rc;
49 h_proto = vhdr->h_vlan_encapsulated_proto;
50 }
51
52 if (h_proto == htons(ETH_P_IP))
53 ipproto = parse_ipv4(data, nh_off, data_end);
54 else if (h_proto == htons(ETH_P_IPV6))
55 ipproto = parse_ipv6(data, nh_off, data_end);
56 else
57 ipproto = 0;
58
59 /* lookup map element for ip protocol, used for packet counter */
60 value = bpf_map_lookup_elem(&rxcnt, &ipproto);
61 if (value)
62 *value = 1;
63
64 /* swap MAC addrs for UDP packets, transmit out this interface */
65 if (ipproto == IPPROTO_UDP) {
66 swap_src_dst_mac(data);
67 rc = XDP_TX;
68 }
69 return rc;
70 }
具體地:
定義了一個(gè) BPF map 存儲(chǔ)統(tǒng)計(jì)信息。用戶態(tài)程序可以 poll 這個(gè) map 來(lái)獲取統(tǒng)計(jì)信息,。
context 對(duì)象 struct xdp_md *ctx
中有包數(shù)據(jù)的 start/end 指針,,可用于直接訪問(wèn)包數(shù)據(jù)。
將數(shù)據(jù)指針和 data_end
比較,,確保內(nèi)存訪問(wèn)不會(huì)越界,。
程序必須自己解析包,,包括 VLAN headers 等東西。
直接通過(guò)指針(direct packet data access)修改包頭,。
內(nèi)核提供的 map lookup helper,。這是程序中唯一的真實(shí)函數(shù)調(diào)用;其他函數(shù)都是內(nèi)聯(lián),,包括 htons()
,。
最終針對(duì)這個(gè)包的判決通過(guò)程序返回值傳遞給調(diào)用方。
將這段程序安裝到網(wǎng)卡接口上時(shí),,它首先會(huì)被編譯成 eBPF 字節(jié)碼,,然后經(jīng)受校驗(yàn)器檢查。 這里的檢查項(xiàng)包括:
無(wú)循環(huán)操作,;程序大?。ㄖ噶顢?shù)量);
訪問(wèn)包數(shù)據(jù)之前,,做了內(nèi)存邊界檢查,;
傳遞給 map lookup 函數(shù)的參數(shù),類型與 map 定義相匹配,;
map lookup 的返回值(value 的內(nèi)存地址)在使用之前,,檢查了是否為 NULL。
3.6 小結(jié)
XDP 系統(tǒng)由四個(gè)主要部分組成:
XDP device driver hook:網(wǎng)卡收到包之后直接運(yùn)行,;
eBPF 虛擬機(jī):執(zhí)行 XDP 程序(以及內(nèi)核其他模塊加載的 BPF 程序),;
BPF maps:使不同 BPF 程序之間、BPF 程序與用戶空間應(yīng)用之間能夠通信,;
eBPF verifier:確保程序不包含任何可能會(huì)破壞內(nèi)核的操作,。
這四部分加在一起,創(chuàng)造了一個(gè)編寫(xiě)自定義包處理應(yīng)用的強(qiáng)大環(huán)境,,它能加速包處理的關(guān)鍵 路徑,,同時(shí)還與內(nèi)核及現(xiàn)有基礎(chǔ)設(shè)施密切集成。
接下來(lái)看一下 XDP 應(yīng)用的性能,。
4 性能評(píng)估
DPDK 是目前性能最高的包處理框架 [18],因此本文將 XDP 與 DPDK 及 Linux 內(nèi)核網(wǎng)絡(luò) 棧的性能做一個(gè)對(duì)比,。測(cè)試機(jī)器環(huán)境:
CPU:一塊 hexa-core Intel Xeon E5-1650 v4 CPU running at 3.60GHz,, 支持 Intel Data Direct I/O (DDIO) 技術(shù),網(wǎng)絡(luò)硬件通過(guò) DMA 能直接將包放到 CPU 緩存,。
關(guān)閉超線性(Hyperthreading),。
網(wǎng)卡:兩塊 Mellanox ConnectX-5 Ex VPI dual-port 100Gbps,mlx5 驅(qū)動(dòng),。
內(nèi)核:Linux 4.18
使用基于 DPDK 的 TRex packet generator [9] 生成測(cè)試流量,。所有測(cè)試腳本位于 [22],。
在測(cè)試中,我們主要關(guān)心三個(gè) metric:
直接棄包(packet drop)性能,。
為展示最高的包處理性能,,我們將用最簡(jiǎn)單的操作 —— 丟棄接收到的包—— 來(lái)測(cè)試。 這個(gè)測(cè)試能有效測(cè)量系統(tǒng)的整體開(kāi)銷,,也是真正的包處理應(yīng)用能達(dá)到的性能上限,。
CPU 使用量。
如引言中指出,,XDP 的優(yōu)點(diǎn)之一是 CPU 使用量與流量大小是正相關(guān)的,,而無(wú)需預(yù)留專 門(mén)的 CPU 給它用。我們通過(guò)測(cè)量 CPU 利用率隨網(wǎng)絡(luò)負(fù)載的變化來(lái)量化這個(gè)指標(biāo),。
包轉(zhuǎn)發(fā)性能,。
轉(zhuǎn)發(fā)的復(fù)雜性要更高一些,例如,,涉及到與多塊網(wǎng)卡的交互,、重寫(xiě)二層頭等等。 這里會(huì)將轉(zhuǎn)發(fā)延遲也考慮進(jìn)去,。
我們已經(jīng)驗(yàn)證,,使用 MTU(1500 字節(jié))包時(shí),我們的系統(tǒng)單核就能達(dá)到線速(100 Gbps), 而且 CPU 有 50% 是空閑的,。顯然,,真正的挑戰(zhàn)在于 PPS,而非帶寬,,其他一些測(cè)試也已經(jīng)指出了這一點(diǎn) [46],。 出于這個(gè)原因,我們用最小包(64 字節(jié))測(cè)試,,衡量指標(biāo)是 PPS,。
對(duì)于 XDP 和 Linux 內(nèi)核網(wǎng)絡(luò)棧的測(cè)試,由于它們沒(méi)有顯式指定某些 CPU 來(lái)處理網(wǎng)絡(luò)包的方式,,因此我們通過(guò)配置硬件 RSS(Receive Side Scaling)來(lái)講流量定向到指定 CPU,。
對(duì)網(wǎng)卡、內(nèi)核的一些配置調(diào)優(yōu),,見(jiàn)代碼倉(cāng)庫(kù) [22],。
4.1 直接棄包(packet drop)性能
Fig 3. 直接棄包(packet drop)性能。DPDK 需要預(yù)留一個(gè) CPU 運(yùn)行控制任務(wù),,因此只剩下 5 個(gè) CPU 做包處理,。
上圖是性能與 CPU 數(shù)量的關(guān)系。
XDP 基準(zhǔn)性能是 24Mpps/core
,,DPDK 是 43.5Mpps/core
,。
二者在分別達(dá)到各自的峰值之前,,PPS 都是隨 CPU 數(shù)量線性增長(zhǎng)的。
最終全局性能受限于 PCI 總線,,啟用 PCI descriptor compression(在 CPU cycles 和 PCI 總線帶寬之間取舍)之后,,能達(dá)到 115Mpps。
再看圖中 Linux 網(wǎng)絡(luò)棧在兩種配置下的性能:
通過(guò) iptables 的 raw table丟棄流量,,這是 Linux 網(wǎng)絡(luò)棧中最早能丟棄包的地方,;
通過(guò) conntrack(連接跟蹤)模塊,這個(gè)模塊的開(kāi)銷非常大,,但在很多 Linux 發(fā)行版中都是默認(rèn)開(kāi)啟的,。
conntrack 模式達(dá)到了 1.8Mpps/core,raw 模式是 4.8Mpps/core ,;這兩種模式均未達(dá)到硬件瓶頸,。 最終的性能,XDP 比常規(guī)網(wǎng)絡(luò)棧的最快方式快了 5 倍,。
Linux raw mode test 中,,我們還測(cè)量了 XDP 程序不丟棄包,而是更新包數(shù)統(tǒng)計(jì)然后將包 送到內(nèi)核網(wǎng)絡(luò)棧的場(chǎng)景,。 這種情況下,,XDP單核的處理性能會(huì)下降到 4.5Mpps/core,有 13.3ns 處理延遲,。 圖中并未給出這個(gè)測(cè)試結(jié)果,,因?yàn)檫@個(gè)開(kāi)銷太小了。
4.2 CPU Usage
Fig 4. 直接棄包(packet drop)場(chǎng)景下的 CPU 利用率,。
用系統(tǒng)提供的 mpstat
命令測(cè)量 CPU 利用率,。結(jié)果如圖 4 。
DPDK 是 busy poll 模式,,因此 CPU 永遠(yuǎn)是 100%,。
XDP 和 Linux 內(nèi)核網(wǎng)絡(luò)棧都是隨流量平滑增長(zhǎng):前面一小段是非線性的,后面基本是線性的,。
前面非線性主要是硬中斷帶來(lái)的固定開(kāi)銷,,在流量很小時(shí)這一部分占比較大。
4.3 包轉(zhuǎn)發(fā)性能
這個(gè)測(cè)試中,,轉(zhuǎn)發(fā)應(yīng)用執(zhí)行非常簡(jiǎn)單的 MAC 地址重寫(xiě):直接交換源和目的 MAC 地址,,然后轉(zhuǎn)發(fā)。 這是轉(zhuǎn)發(fā)場(chǎng)景下最精簡(jiǎn)的步驟了,,因此結(jié)果代表了所有真實(shí)轉(zhuǎn)發(fā)應(yīng)用的性能上限,。
圖中包括了同網(wǎng)卡轉(zhuǎn)發(fā)和不同網(wǎng)卡轉(zhuǎn)發(fā)(XDP程序返回碼不同)的結(jié)果,。
DPDK 示例程序只支持通過(guò)另一個(gè)網(wǎng)卡轉(zhuǎn)發(fā),,因此這里只列出了這種情況下的性能,。
Linux 網(wǎng)絡(luò)棧不支持這種極簡(jiǎn)轉(zhuǎn)發(fā)模式(minimal forwarding mode),需要設(shè)置完整的 橋接或路由查找(bridging or routing lookup)才能轉(zhuǎn)發(fā)包,;路由查找是非常耗時(shí)的 ,,由于其他幾種應(yīng)用并沒(méi)有這一步,因此結(jié)果直接對(duì)比是沒(méi)意義的,。因此這里略去了 Linux 網(wǎng)絡(luò)棧的結(jié)果,。
轉(zhuǎn)發(fā)吞吐(pps)
Fig 5. 轉(zhuǎn)發(fā)性能。在同一網(wǎng)卡接口上收發(fā)會(huì)占用同一 PCI port 的帶寬,, 這意味著在 70Mpps XDP same-nic 組就已經(jīng)達(dá)到了 PCI 總線的瓶頸
如圖 5 所示,,性能隨 CPU 數(shù)量線性擴(kuò)展,直到達(dá)到全局性能瓶頸,。XDP 在同網(wǎng)卡轉(zhuǎn)發(fā)的性能遠(yuǎn)高于 DPDK 異網(wǎng)卡性能,,原因是內(nèi)存處理方式不同:
packet buffer 是設(shè)備驅(qū)動(dòng)分配的,與接收接口(receiving interface)相關(guān)聯(lián),。
因此,,異網(wǎng)卡場(chǎng)景下,當(dāng)包轉(zhuǎn)發(fā)到另一個(gè)接口時(shí),,memory buffer 需要還給與之關(guān)聯(lián)的接口,。
轉(zhuǎn)發(fā)延遲
表 1. 轉(zhuǎn)發(fā)延遲。機(jī)器網(wǎng)卡的兩個(gè)接口直連,,在轉(zhuǎn)發(fā)速率分別為 100pps 和 1Mpps 的條件下,,持續(xù) 50s 測(cè)量端到端延遲
高 pps 場(chǎng)景下,XDP 的延遲已經(jīng)接近 DPDK,。但在低 pps 場(chǎng)景下,,XDP 延遲比 DPDK 大的多,原因是 XDP 是基于中斷的,,中斷處理時(shí)間( interrupt processing time)此時(shí)占大頭,;而 DPDK 是輪詢模式,延遲相對(duì)比較固定,。
4.4 討論:XDP 性能與 DPDK 還有差距的原因
XDP 未做底層代碼優(yōu)化
上一節(jié)已經(jīng)看到,,XDP 相比于常規(guī) Linux 網(wǎng)絡(luò)棧性能有了顯著提升。但對(duì)于大部分 XDP 場(chǎng)景來(lái)說(shuō),,性能還是與 DPDK 有差距,。我們認(rèn)為,這是主要是因?yàn)?DPDK 做了相當(dāng)多的底層 代碼優(yōu)化,。舉個(gè)例子來(lái)解釋,,考慮 packet drop 例子:
多出來(lái)的 18.7ns 在我們的 3.6GHz 機(jī)器上對(duì)應(yīng) 67 個(gè)時(shí)鐘周期,。因此,,很顯然 每個(gè)很小的優(yōu)化在這里都會(huì)產(chǎn)生很大的影響。例如,,我們測(cè)量出在測(cè)試 機(jī)器上,,每次函數(shù)調(diào)用需要 1.3ns。mlx5 驅(qū)動(dòng)處理每個(gè)包都有 10 次 函數(shù)調(diào)用,,總計(jì)就是 13ns,。
通用目的操作系統(tǒng),首要目標(biāo):更好的擴(kuò)展和配置,,而非極致性能
另外,,在 Linux 這樣的通用目的操作系統(tǒng)中,某些開(kāi)銷是不可避免的,, 因?yàn)樵O(shè)備驅(qū)動(dòng)或子系統(tǒng)的組織方式是為了實(shí)現(xiàn)更好的擴(kuò)展和配置,,而非極致性能。
但是,,我們認(rèn)為有些優(yōu)化還是有必要的,。例如,我們嘗試將內(nèi)核中與測(cè)試網(wǎng)卡無(wú)關(guān)的 DMA 函數(shù)調(diào)用刪掉,, 這樣將前面提到的 10 個(gè)函數(shù)調(diào)用降低到了 6 個(gè),,測(cè)試結(jié)果顯示這將單核性能提升到了 29Mpps/core。 依此推測(cè)的話,,將另外 6 個(gè)函數(shù)調(diào)用也優(yōu)化掉,,能將 XDP 的性能提升到 37.6Mpps
。 實(shí)際上我們不可能將 6 個(gè)全部去掉,,但去掉其中幾個(gè),,再加上一些其他優(yōu)化,我 們相信 XDP 和 DPDK 的性能差距將越來(lái)越小,。
其他驅(qū)動(dòng)的測(cè)試結(jié)果也是類似的,,例如 i40e driver for 40 Gbps Intel cards。
基于以上討論,,我們相信未來(lái) XDP 與 DPDK 的性能差距將越來(lái)越小,。
另一方面,考慮到 XDP 在靈活性和與內(nèi)核集成方面的優(yōu)勢(shì),, XDP 已經(jīng)是很多實(shí)際場(chǎng)景中的非常有競(jìng)爭(zhēng)力的方式,。下文給出幾個(gè)例子。
5 真實(shí)場(chǎng)景使用案例
本節(jié)給出三個(gè)例子來(lái)具體展示 XDP 在真實(shí)世界中的應(yīng)用,。 這幾個(gè)案例都是已經(jīng)真實(shí)在用的,,但本文出于解釋目的,將使用簡(jiǎn)化的版本。 同時(shí)也建議讀者參考 [38],,后者是獨(dú)立的文章,,介紹使用 XDP 解決實(shí)際工作中網(wǎng)絡(luò)服務(wù)所面臨的一些挑戰(zhàn)。
本節(jié)目的是展示真實(shí) XDP 方案的可行性,,因此不會(huì)將重點(diǎn)放在與業(yè)界最新的實(shí)現(xiàn)做詳盡性能對(duì)比上。 我們會(huì)拿常規(guī)的 Linux 內(nèi)核網(wǎng)絡(luò)棧的性能作為 baseline,,來(lái)對(duì)比 XDP 應(yīng)用的性能,。
5.1 案例一:軟件路由(software routing)
內(nèi)核數(shù)據(jù)平面 & 控制平面(BIRD/FRR)
Linux 內(nèi)核實(shí)現(xiàn)了一個(gè)功能完整的路由表,作為數(shù)據(jù)平面,,支持
對(duì)于控制平面,,Bird [10] 或 FRR [17] 這樣的路由守護(hù)進(jìn)程( routing daemons)實(shí)現(xiàn)了多種路由控制平面協(xié)議。Linux 提供的這套生態(tài)系統(tǒng)功能如此豐富 ,,因此再在另一個(gè)包處理框架中重新實(shí)現(xiàn)一套類似的路由棧代價(jià)將非常高,, 更實(shí)際的方式是對(duì) Linux 內(nèi)核的數(shù)據(jù)平面進(jìn)行優(yōu)化。
XDP:直接查詢內(nèi)核路由表并轉(zhuǎn)發(fā)
XDP 非常適合做這件事情,,尤其是它提供了一個(gè) helper 函數(shù),,能從 XDP 程序中直接查詢內(nèi)核路由表。
如果查詢成功,,會(huì)返回egress interface 和下一跳 MAC 地址,, XDP 程序利用這些信息足夠?qū)⒓崔D(zhuǎn)發(fā)出去。
如果下一跳 MAC 還是未知的(因?yàn)橹斑€沒(méi)進(jìn)行過(guò) neighbour lookup),,XDP 程序就 能將包傳給內(nèi)核網(wǎng)絡(luò)棧,,后者會(huì)解析 neighbor 地址,這樣隨后的包 就能直接被 XDP 程序轉(zhuǎn)發(fā)了,。
測(cè)試:XDP routing 全球 BGP 路由表
為展示 XDP 路由的性能,,我們用 Linux 內(nèi)核代碼中的 XDP routing 例子 [1],與常規(guī) Linux 內(nèi)核網(wǎng)絡(luò)棧的性能做對(duì)比,。 兩組測(cè)試:
路由表中只有一條路由,;
路由表中有從 routeviews.org 中 dump 而來(lái)的全球 BGP 路由表(global BGP routing table)。 包含 752,138 條路由,。隨機(jī)生成 4000 個(gè)目的 IP 地址,,以確保能充分利用到這種路由表。
如果目的 IP 地址少于 4000 個(gè),,實(shí)際用到的路由表部分會(huì)較小,,能夠保存在 CPU 緩存中,使得結(jié)果不準(zhǔn)確,。 增大 IP 數(shù)量至 4000 個(gè)以上,,不會(huì)對(duì)轉(zhuǎn)發(fā)性能造成影響,但可以避免緩存導(dǎo)致的結(jié)果不準(zhǔn)問(wèn)題。
對(duì)于兩組測(cè)試,,下一跳 MAC 地址都是與我們的發(fā)送網(wǎng)卡直接相關(guān)的接口的地址,。
性能:2.5x
Fig 6. 軟件路由的性能。由于性能隨核數(shù)線性增加,,這里只給出單核的結(jié)果,。
測(cè)試結(jié)果如上圖所示。
full table lookup 性能提升了 2.5 倍,;
smaller routing table 組,,提升了 3 倍。
這說(shuō)明,,XDP 路由程序 單核 10Gbps 網(wǎng)卡的軟硬件配置,,就能 處理整張全球 BGP 路由表(保守估計(jì)每個(gè)包平均 300 字節(jié))。
5.2 案例二:Inline DoS Mitigation
DoS 攻擊還是像瘟疫一樣糾纏著互聯(lián)網(wǎng),,現(xiàn)在通常的方式是:通過(guò)已經(jīng)入侵的大量設(shè)備發(fā)起分布式(DDoS)攻擊,。
有了XDP 之后,我們能直接在應(yīng)用服務(wù)器(application servers)上 部署包過(guò)濾程序來(lái)防御此類攻擊(inline DoS mitigation),, 無(wú)需修改應(yīng)用代碼,。如果應(yīng)用是部署在虛擬機(jī)里,那 XDP 程序還可以 部署在宿主機(jī)(hypervisor)上,,這樣單個(gè)程序就能保護(hù)機(jī)器上所有的虛擬機(jī),。
模擬 Cloudflare 防御架構(gòu)
為展示工作原理,我們用 XDP 作為過(guò)濾機(jī)制,,模擬 Cloudflare 的 DDoS 防御架構(gòu)[6],。 他們的 Gatebot architecture ,首先在各 PoP 點(diǎn)機(jī)器上采樣,,然后統(tǒng)一收起來(lái)做分析,, 根據(jù)分析結(jié)果生成防御規(guī)則。
防御規(guī)則的形式是對(duì)包數(shù)據(jù)(payload)進(jìn)行一系列簡(jiǎn)單檢查,, 能直接編譯成 eBPF 代碼然后分發(fā)到 PoP 點(diǎn)的所有服務(wù)器上,。這里說(shuō)的代碼是 XDP 程序 ,它會(huì)將匹配到規(guī)則的所有流量丟棄,,同時(shí)將統(tǒng)計(jì)信息更新到 BPF map,。
程序邏輯
為驗(yàn)證這種方案的性能,我們編寫(xiě)一個(gè) XDP 程序,,它
解析包頭,,執(zhí)行一些簡(jiǎn)單驗(yàn)證。對(duì)每個(gè)包:執(zhí)行四次讀取操作,,以解析外層包頭,。
將符合攻擊特性的流量丟棄。具體:丟棄 UDP 特定端口的流量。
將其他流量通過(guò) CPU redirect 方式重定向給另一個(gè) CPU做進(jìn)一步處理,;
性能
我們用 netperf 做性能壓測(cè) [26],。
用 netperf TCP round-trip benchmark,單個(gè) TCP 連接來(lái)回小的 request/reply,,統(tǒng)計(jì) transactions/second,。
模擬的是交互式應(yīng)用,例如小的遠(yuǎn)程過(guò)程調(diào)用(RPC),。
實(shí)驗(yàn)在單核上進(jìn)行,,模擬多個(gè)流量(正常流量 攻擊流量)競(jìng)爭(zhēng)同一物理資源的場(chǎng)景。
在 beseline 35K 業(yè)務(wù) TPS(transactions per second)基礎(chǔ)上,,打少量 UDP 流量作為攻擊流量。逐漸加大攻擊流量,,觀察 TPS 的變化,。
Fig 7. DDoS 性能。業(yè)務(wù)吞吐(TPS)隨攻擊流量的變化,。
結(jié)果如上圖所示,,
沒(méi)有 XDP 的一組,性能急劇下降:攻擊流量在 3Mpps 時(shí)性能減半,,3.5Mpps 時(shí)基本跌零,;
有 XDP 程序的一組,攻擊流量達(dá)到 19.5Mpps 之前,,業(yè)務(wù)吞吐保持在 28.5K TPS 以上,,過(guò)了這個(gè)臨界點(diǎn)性能才開(kāi)始急劇下降。
以上結(jié)果表明,,XDP 防御 DDoS 攻擊在實(shí)際中是完全可行的,,單核就能輕松處理 10Gbps 的、都是最小包(minimum-packet)的 DoS 流量,。 這種 DDoS 防御的部署更加靈活,,無(wú)需硬件或應(yīng)用做任何改動(dòng)。
5.3 案例三:負(fù)載均衡(load balancing)
Facebook Katran
負(fù)載均衡的場(chǎng)景,,我們用 Facebook 開(kāi)源的 Katran 作為例子 [15],。Katran 的工作原理是對(duì)外通告服務(wù)的 IP,這樣目標(biāo)是這個(gè) IP 的流量就會(huì)被路由到 XDP 實(shí)現(xiàn)的負(fù)載均衡器,。
負(fù)載均衡器對(duì)包頭(source packet header)進(jìn)行哈希,,以此選擇目標(biāo)應(yīng)用服務(wù)器。
然后將對(duì)包進(jìn)行封裝(encap),,發(fā)送給應(yīng)用服務(wù)器,;
應(yīng)用服務(wù)器解封裝(decap),處理請(qǐng)求,然后直接將回包發(fā)給客戶端(DSR 模式),。
在這個(gè)過(guò)程中,,XDP 程序負(fù)責(zé)哈希、封裝以及將包從接收網(wǎng)卡再發(fā)出去的任務(wù),。 配置信息存儲(chǔ)在 BPF map 中,,整個(gè)封裝邏輯是完全在 eBPF 中實(shí)現(xiàn)的。
性能
為測(cè)試性能,,我們給 Katran XDP 程序配置幾個(gè)固定的目標(biāo)機(jī)器,。 對(duì)照組是 IPVS,它是 Linux 內(nèi)核的一部分,。性能如表 2 所示,,隨 CPU 數(shù)量線性增長(zhǎng), XDP 比 IPVS 性能高 4.3 倍,。
表 2. 負(fù)載均衡器性能(Mpps)
配置:1 VIP/core, 100 DstIPs/VIP.
6 XDP 的未來(lái)方向
XDP 已經(jīng)能用于解決真實(shí)問(wèn)題,,但作為 Linux 內(nèi)核的一部分,XDP 還在快速開(kāi)發(fā)過(guò)程中,。
6.1 eBPF 程序的限制
前面提到,,加載到 eBPF 虛擬機(jī)的程序必須保證其安全性(不會(huì)破壞內(nèi)核),因此對(duì) eBPF 程序作了一下限制,,歸結(jié)為兩方面:
確保程序會(huì)終止:在實(shí)現(xiàn)上是通過(guò)禁止循環(huán)和限制程序的最大指令數(shù)(max size of the program),;
確保內(nèi)存訪問(wèn)的安全:通過(guò) 3.4 小結(jié)介紹的寄存器狀態(tài)跟蹤(register state tracking)來(lái)實(shí)現(xiàn)。
校驗(yàn)邏輯偏保守
由于校驗(yàn)器的首要職責(zé)是保證內(nèi)核的安全,,因此其校驗(yàn)邏輯比較保守,, 凡是它不能證明為安全的,一律都拒絕,。有時(shí)這會(huì)導(dǎo)致假陰性(false negatives),, 即某些實(shí)際上是安全的程序被拒絕加載;這方面在持續(xù)改進(jìn),。
校驗(yàn)器的錯(cuò)誤提示也已經(jīng)更加友好,,以幫助開(kāi)發(fā)者更快定位問(wèn)題。
近期已經(jīng)支持了 BPF 函數(shù)調(diào)用(function calls),。
正在計(jì)劃支持有限循環(huán)(bounded loops),。
正在提升校驗(yàn)器效率,以便處理更大的 BPF 程序,。
缺少標(biāo)準(zhǔn)庫(kù)
相比于用戶空間 C 程序,,eBPF 程序的另一個(gè)限制是缺少標(biāo)準(zhǔn)庫(kù),包括 內(nèi)存分配,、線程,、鎖等等庫(kù),。
內(nèi)核的生命周期和執(zhí)行上下文管理(life cycle and execution context management )部分地彌補(bǔ)了這一不足,(例如,,加載的 XDP 程序會(huì)為每個(gè)收到的包執(zhí)行),,
內(nèi)核提供的 helper 函數(shù)也部分地彌補(bǔ)了一不足。
一個(gè)網(wǎng)卡接口只能 attach 一個(gè) XDP 程序
這個(gè)限制其實(shí)也是可以繞過(guò)的:將 XDP 程序組織成程序數(shù)組,,通過(guò)尾 調(diào)用,,根據(jù)包上下文在程序之間跳轉(zhuǎn),或者是將幾個(gè)程序做 chaining,。
6.2 用戶體驗(yàn)和調(diào)試
XDP 程序運(yùn)行在內(nèi)核,,因此常規(guī)的用戶空間 debug 工具是用不了的,但內(nèi)核自帶的 debug 和 introspection 功能是可以用在 XDP (及其他 eBPF 程序)上的,。 包括:
但不熟悉內(nèi)核生態(tài)系統(tǒng)的開(kāi)發(fā)者可能會(huì)對(duì)這些工具感到非常陌生,,難以使用。因此,,也出 現(xiàn)了一些更方便普通開(kāi)發(fā)者的工具,,包括 BCC [50]、bpftool [8],、libbpf 函數(shù)庫(kù) [30] 等等,。
6.3 驅(qū)動(dòng)支持
設(shè)備要支持 XDP,,需要實(shí)現(xiàn)內(nèi)核核心網(wǎng)絡(luò)棧暴露出的一個(gè) API,。 寫(xiě)作本文時(shí) Linux 4.18 已經(jīng)有 12 種驅(qū)動(dòng)支持 XDP,包括了大部分高速網(wǎng)卡,。 最新列表見(jiàn) [2],。
隨著 XDP 系統(tǒng)的不斷成熟,核心代碼逐漸上移到內(nèi)核中,,驅(qū)動(dòng)需要維護(hù)的代碼越 來(lái)越少,。例如,redirection action 支持新的 target 時(shí),,無(wú)需驅(qū)動(dòng)做任何改動(dòng),。
最后,對(duì)于那些不支持 XDP 的驅(qū)動(dòng),,內(nèi)核提供了 Generic XDPfeature [39],,這是軟件實(shí)現(xiàn)的 XDP,性能會(huì)低一些,, 在實(shí)現(xiàn)上就是將 XDP 的執(zhí)行上移到了核心網(wǎng)絡(luò)棧(core networking stack),。
XDP 在內(nèi)核收包函數(shù) receive_skb() 之前,
Generic XDP在 receive_skb() 之后,,
更多關(guān)于 Generic XDP,,可參考參考:容器網(wǎng)絡(luò)|深入理解Cilium
6.4 性能提升
XDP和 DPDK之間還有一些性能差距,,一些改進(jìn)工作正在進(jìn)行中:
6.5 QoS 和 Rate Transitions
當(dāng)前,XDP 還沒(méi)有任何 QoS 機(jī)制,。 尤其是,,如果對(duì)端已經(jīng)過(guò)載(例如兩端的網(wǎng)絡(luò)速度或特性不匹配),XDP 程序是收不到任何背壓(back-pressure)的,,
雖然 XDP 中缺少 QoS,,但 Linux 內(nèi)核網(wǎng)絡(luò)棧中卻有很多業(yè)界最佳的 Active Queue Management (AQM) 特性和 packet scheduling algorithms [23]。 這些特性中,,部分并不適用于 XDP,,但我們相信能夠 以一種對(duì)包處理應(yīng)用完全透明的方式,選擇其中部分集成到 XDP,。 我們計(jì)劃對(duì)這一方向進(jìn)行更深入研究,。
6.6 加速傳輸層協(xié)議
我們已經(jīng)證明 XDP 能在保留操作系統(tǒng)原有功能的前提下,集成到操作系統(tǒng)中,,實(shí)現(xiàn)高速包數(shù)據(jù),。
目前的 XDP 還是用于無(wú)狀態(tài)包處理(stateless packet processing) ,如果將這個(gè)模型擴(kuò)展到有狀態(tài)傳輸層協(xié)議(stateful transport protocols),,例如 TCP,,它能給依賴可靠/有狀態(tài)傳輸?shù)膽?yīng)用提供類似的性能提升。
實(shí)際上,,已經(jīng)有一些研究證明,,相比于操作系統(tǒng)的協(xié)議棧,accelerated transport protocols 能顯著提升性能[5, 25, 35, 52],。其中的一個(gè)解決方案 [52] 表明,,在保留內(nèi) 核 TCP 協(xié)議棧的的前提下,原始包處理性能(raw packet processing)存在巨大的提升 空間,。
XDP 非常適用于這種場(chǎng)景,,目前也已經(jīng)有一些關(guān)于如何實(shí)現(xiàn)的初步討論 [21], 雖然離實(shí)際使用還很遠(yuǎn),,但仍然是一個(gè)令人振奮的,、擴(kuò)展 XDP 系統(tǒng) scope 的方向。
6.7 內(nèi)核-用戶空間零拷貝(zero-copy to userspace)
3.1 小節(jié)提到,,XDP 程序能將數(shù)據(jù)包重定向到用戶空間應(yīng)用(userspace application)打 開(kāi)的特殊類型 socket,。這可以用于加速客戶端和服務(wù)端在同一臺(tái)機(jī)器的網(wǎng)絡(luò)密集型應(yīng)用(network-heavy applications running on the local machine)。
更多信息可參考: (譯) 利用 ebpf sockmap/redirection 提升 socket 性能(2020),。 這里使用的是 BPF 而非 XDP,,但核心原理是一樣的,只是程序執(zhí)行的位置(hook)不同,。 譯注,。
但在目前的實(shí)現(xiàn)中,,這種方式在底層仍然需要拷貝包數(shù)據(jù),因此性能會(huì)打折扣,。
目前已經(jīng)有工作在進(jìn)行,,通過(guò) AF_XDP 實(shí)現(xiàn)真正的數(shù)據(jù)零拷貝。但這項(xiàng)工作需要 對(duì)網(wǎng)絡(luò)設(shè)備的內(nèi)存處理過(guò)程有一些限制,,因此需要設(shè)備驅(qū)動(dòng)的顯式支持,。 第一個(gè)支持這個(gè)功能的 patch 已經(jīng)合并到 4.19內(nèi)核,更多驅(qū)動(dòng)的支持 正在添加中,。初步的性能測(cè)試結(jié)果還是很樂(lè)觀的,,顯示能達(dá)到 20Mpps/core 的內(nèi)核到用戶 空間傳遞(transfer)速度。
6.8 XDP 作為基礎(chǔ)構(gòu)建模塊(XDP as a building block)
正如 DPDK 用于高層包處理框架的底層構(gòu)建模塊(例如 [31]),,XDP 有望成為高層應(yīng)用的運(yùn)行時(shí)環(huán)境 (runtime environment for higher-level applications),。
實(shí)際上,我們看到一些基于 XDP 的應(yīng)用和框架已經(jīng)出現(xiàn)了,。包括
Cilium security middle-ware [3]
Suricata network monitor [4]
Open vSwitch [49]
P4-to-XDP compiler project [51]
甚至還有人嘗試將 XDP 作為 DPDK 的一種底層驅(qū)動(dòng) [53],。
7 總結(jié)
本文描述了XDP,一個(gè)安全,、快速,、可編程、集成到操作系統(tǒng)內(nèi)核的包處理框架,。 測(cè)試結(jié)果顯示,,XDP能提供 24Mpps/core 的高處理性能,這一數(shù)字雖然與基于 kernel bypass 的DPDK仍有差距,,但提供了其他一些非常有競(jìng)爭(zhēng)力的優(yōu)勢(shì):
兼容內(nèi)核安全和管理框架(kernel bypass 方式在bypass 內(nèi)核網(wǎng)絡(luò)棧的同時(shí),,也將安全和設(shè)備管理等這些極其重要的基礎(chǔ)設(shè)施 bypass 了),;
兼容內(nèi)核網(wǎng)絡(luò)棧,,可選擇性利用內(nèi)核已有的基礎(chǔ)設(shè)施和功能;
提供與內(nèi)核 API 一樣穩(wěn)定的編程接口,;
對(duì)應(yīng)用完全透明,;
更新、替換程序的過(guò)程不會(huì)引起服務(wù)中斷,;
無(wú)需專門(mén)硬件,,無(wú)需獨(dú)占CPU等資源。
相比于kernel bypass 這種非此即彼,、完全繞開(kāi)內(nèi)核的方式,,我們相信 XDP 有更廣闊的的應(yīng)用前景。Facebook,、Cloudflare 等公司實(shí)際落地的 XDP 應(yīng)用,,更加增強(qiáng)了我們的這種信心,。
最后,XDP系統(tǒng)還在快速發(fā)展,,前面也列出了一些正在未來(lái)可能會(huì)做的開(kāi)發(fā)/優(yōu)化工作,。
致謝
XDP has been developed by the Linux networking community for a number of years, and the authors would like to thank everyone who has been involved. In particular,
Alexei Starovoitov has been instrumental in the development of the eBPF VM and verifier;
Jakub Kicinski has been a driving force behind XDP hardware offloading and the bpftool utility;
Bj?rn T?pel and Magnus Karlsson have been leading the AF_XDP and userspace zero-copy efforts.
We also wish to extend our thanks to the anonymous reviewers, and to our shepherd Srinivas Narayana, for their helpful comments.
參考文獻(xiàn)
https:///blog/xdp-paper-acm-2018-zh/