日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

万字长文|深入理解XDP全景指南

發(fā)布時間:2024/4/11 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 万字长文|深入理解XDP全景指南 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

譯者序

本文翻譯自 2018 年 ACM?CoNEXT?大會上的一篇文章:

The eXpress Data Path: Fast Programmable Packet Processing in the Operating System Kernel

作者陣容豪華,包括來自 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://doi.org/10.1145/3281411.3281443

由于譯者水平有限,本文不免存在遺漏或錯誤之處。如有疑問,請查閱原文。

以下是譯文。

  • 譯者序

  • 摘要

  • 1 引言

    • 1.1 現(xiàn)有方案(kernel bypass)存在的問題

    • 1.2 新方案:給內(nèi)核網(wǎng)絡(luò)棧添加可編程能力

    • 1.3 新方案(XDP)的優(yōu)點

    • 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è)計

    • 唯一加載入口:bpf()?系統(tǒng)調(diào)用

    • 校驗器工作原理:two-pass DAG

    • 內(nèi)存越界和空指針檢查:職責(zé)上移到程序自身/開發(fā)者

    • 跟蹤數(shù)據(jù)訪問操作和值范圍

    • 不同類型數(shù)據(jù)的校驗信息來源(source of truth)

    • 校驗器的目的

    • 在設(shè)備驅(qū)動中執(zhí)行,無需上下文切換

    • 在軟件最早能處理包的位置執(zhí)行,性能最優(yōu)

    • XDP 程序典型執(zhí)行流

    • 3.0 XDP 系統(tǒng)架構(gòu)

    • 3.1 XDP driver hook

    • 3.2 eBPF 虛擬機

    • 3.3 BPF maps

    • 3.4 eBPF verifier

    • 3.5 XDP 程序示例

    • 3.6 小結(jié)

  • 4 性能評估

    • XDP 未做底層代碼優(yōu)化

    • 通用目的操作系統(tǒng),首要目標:更好的擴展和配置,而非極致性能

    • 轉(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 真實場景使用案例

    • Facebook Katran

    • 性能

    • 模擬 Cloudflare 防御架構(gòu)

    • 程序邏輯

    • 性能

    • 內(nèi)核數(shù)據(jù)平面 & 控制平面(BIRD/FRR)

    • XDP:直接查詢內(nèi)核路由表并轉(zhuǎn)發(fā)

    • 測試:XDP routing + 全球 BGP 路由表

    • 性能:2.5x

    • 5.1 案例一:軟件路由(software routing)

    • 5.2 案例二:Inline DoS Mitigation

    • 5.3 案例三:負載均衡(load balancing)

  • 6 XDP 的未來方向

    • 校驗邏輯偏保守

    • 缺少標準庫

    • 一個網(wǎng)卡接口只能 attach 一個 XDP 程序

    • 6.1 eBPF 程序的限制

    • 6.2 用戶體驗和調(diào)試

    • 6.3 驅(qū)動支持

    • 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é)

  • 致謝

  • 參考文獻

摘要

近些年業(yè)界流行通過內(nèi)核旁路(kernel bypass)的方式實現(xiàn)?可編程的包處理過程(programmable packet processing)。實現(xiàn)方式是 將網(wǎng)絡(luò)硬件完全交由某個專門的用戶空間應(yīng)用(userspace application) 接管,從而避免內(nèi)核和用戶態(tài)上下文切換的昂貴性能開銷。

但是,操作系統(tǒng)被旁路(繞過)之后,它的應(yīng)用隔離(application isolation) 和安全機制(security mechanisms)就都失效了;一起失效的還有各種經(jīng)過已經(jīng) 充分測試的配置、部署和管理工具。

為解決這個問題,我們提出一種新的可編程包處理方式:eXpress Data Path (XDP)。

  • XDP 提供了一個仍然基于操作系統(tǒng)內(nèi)核的安全執(zhí)行環(huán)境,在設(shè)備驅(qū)動上下文?(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)通過 C 等高層語言編寫,然后編譯成特定字節(jié)碼;出于安 全考慮,內(nèi)核會首先對這些字節(jié)碼執(zhí)行靜態(tài)分析,然后再將它們翻譯成?處理器原生指令(native instructions)。

  • 測試結(jié)果顯示,XDP 能達到?24Mpps/core?的處理性能。

為展示 XDP 靈活的編程模型,本文還將給出三個程序示例,

  • layer-3 routing(三層路由轉(zhuǎn)發(fā))

  • inline DDoS protection(DDoS 防護)

  • layer-4 load balancing(四層負載均衡)

  • 1 引言

    軟件實現(xiàn)高性能包處理的場景,對每個包的處理耗時有著極高的要求。通用目的操作系統(tǒng)中 的網(wǎng)絡(luò)棧更多是針對靈活性的優(yōu)化,這意味著它們花在每個包上 的指令太多了,不適合網(wǎng)絡(luò)高吞吐的場景。

    因此,隨后出現(xiàn)了一些專門用于包處理的軟件開發(fā)工具,例如 Data Plane Development Kit (DPDK) [16]。這些工具一般都會完全繞過內(nèi)核,將網(wǎng)絡(luò)硬件直接交 給用戶態(tài)的網(wǎng)絡(luò)應(yīng)用,并需要獨占一個或多個 CPU。

    1.1 現(xiàn)有方案(kernel bypass)存在的問題

    內(nèi)核旁路方式可以顯著提升性能,但缺點也很明顯:

    • 很難與現(xiàn)有系統(tǒng)集成;

    • 上層應(yīng)用必須要將內(nèi)核中已經(jīng)非常成熟的模塊在用戶態(tài)重新實現(xiàn)一遍,例如路由表、高層協(xié)議棧等;

    • 最壞的情況下,這種包處理應(yīng)用只能工作在一個完全隔絕的環(huán)境,因為內(nèi)核提供的常見工具和部署方式在這種情況下都不可用了。

    • 導(dǎo)致系統(tǒng)越來越復(fù)雜,而且破壞了操作系統(tǒng)內(nèi)核在把控的安全邊界。?在基礎(chǔ)設(shè)施逐漸遷移到 Kubernetes/Docker 等容器環(huán)境的背景下,這一點顯得尤其嚴重, 因為在這種場景下,內(nèi)核擔(dān)負著資源抽象和隔離的重任。

    1.2 新方案:給內(nèi)核網(wǎng)絡(luò)棧添加可編程能力

    對此,本文提供了另一種解決方案:給內(nèi)核網(wǎng)絡(luò)棧添加可編程能力。這使得我們能在?兼容各種現(xiàn)有系統(tǒng)、復(fù)用已有網(wǎng)絡(luò)基礎(chǔ)設(shè)施的前提下,仍然實現(xiàn)高速包處理。?這個框架稱為 XDP,

    • XDP 定義了一個受限的執(zhí)行環(huán)境(a limited execution environment),運行在一個?eBPF 指令虛擬機中。eBPF 是 BSD Packet Filter (BPF) [37] 的擴展。

    • XDP 程序運行在內(nèi)核上下文中,此時內(nèi)核自身都還沒有接觸到包數(shù)據(jù)( before the kernel itself touches the packet data),這使得我們能在網(wǎng)卡收到包后?最早能處理包的位置,做一些自定義數(shù)據(jù)包處理(包括重定向)。

    • 內(nèi)核在加載(load)時執(zhí)行靜態(tài)校驗,以確保用戶提供的 XDP 程序的安全。

    • 之后,程序會被動態(tài)編譯成原生機器指令(native machine instructions),以獲得高性能。

    XDP 已經(jīng)在過去的幾個內(nèi)核 release 中逐漸合并到內(nèi)核,但在本文之前,還沒有關(guān)于 XDP 系統(tǒng)的 完整架構(gòu)介紹。本文將對 XDP 做一個高層介紹。

    1.3 新方案(XDP)的優(yōu)點

    我們的測試結(jié)果顯示 XDP 能取得?24Mpps/core?的處理性能,這雖然與 DPDK 還有差距, 但相比于后者這種 kernel bypass 的方式,XDP 有非常多的優(yōu)勢。

    具體地,XDP:

  • 與內(nèi)核網(wǎng)絡(luò)棧協(xié)同工作,將硬件的控制權(quán)完全留在內(nèi)核范圍內(nèi)。帶來的好處:

    • 保持了內(nèi)核的安全邊界

    • 無需對網(wǎng)絡(luò)配置或管理工具做任何修改

    無需任何特殊硬件特性,任何有 Linux 驅(qū)動的網(wǎng)卡都可以支持, 現(xiàn)有的驅(qū)動只需做一些修改,就能支持 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 層交互時,沒有從用戶態(tài)將包重新注入內(nèi)核的昂貴開銷。

    對應(yīng)用透明。這創(chuàng)造了一些新的部署場景/方式,例如直接在應(yīng)用所 在的服務(wù)器上部署 DoS 防御(而非中心式/網(wǎng)關(guān)式 DoS 防御)。

    服務(wù)不中斷的前提下動態(tài)重新編程(dynamically re-program), 這意味著可以按需加入或移除功能,而不會引起任何流量中斷,也能動態(tài)響應(yīng)系統(tǒng)其他部分的的變化。

    無需預(yù)留專門的 CPU 做包處理,這意味著 CPU 功耗與流量高低直接相關(guān),更節(jié)能。

    1.4 本文組織結(jié)構(gòu)

    接下來的內(nèi)容介紹 XDP 的設(shè)計,并做一些性能分析。結(jié)構(gòu)組織如下:

    • Section 2?介紹相關(guān)工作;

    • Section 3?介紹 XDP 系統(tǒng)的設(shè)計;

    • Section 4?做一些性能分析;

    • Section 5?提供了幾個真實 XDP 場景的程序例子;

    • Section 6?討論 XDP 的未來發(fā)展方向;

    • Section 7?總結(jié)。

    2 相關(guān)工作

    XDP當(dāng)然不是第一個支持可編程包處理的系統(tǒng) —— 這一領(lǐng)域在過去幾年發(fā)展勢頭良好, 并且趨勢還在持續(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]。

    • 更加通用、且高度可定制的包處理解決方案,能夠處理從多種源收來的數(shù)據(jù)包 [12, 20, 31, 34, 40, 44]。

    要基于通用(Common Off The Shelf,COTS)硬件實現(xiàn)高性能包處理,就必須解決?網(wǎng)卡(NIC)和包處理程序之間的所有瓶頸。由于性能瓶頸主要來源于內(nèi)核和用戶態(tài)應(yīng)用之間的接口(系統(tǒng)調(diào)用開銷非常大,另外,內(nèi)核功能豐富,但也非常復(fù)雜), 低層(low-level)框架必須通過這樣或那樣的方式來降低這些開銷。

    現(xiàn)有的一些框架通過幾種不同的方式實現(xiàn)了高性能,XDP構(gòu)建在其中一些技術(shù)之上。?接下來對 XDP 和它們的異同做一些比較分析。

    2.1 用戶態(tài)輪詢 vs. XDP

    • DPDK?[16] 可能是使用最廣泛的高性能包處理框架。它最初只支持 Intel 網(wǎng)卡,后來逐 步擴展到其他廠商的網(wǎng)卡。DPDK 也稱作內(nèi)核旁路框架(kernel bypass framework), 因為它將網(wǎng)絡(luò)硬件的控制權(quán)從內(nèi)核轉(zhuǎn)移到了用戶態(tài)的網(wǎng)絡(luò)應(yīng)用,完全避免了內(nèi)核-用戶態(tài) 之間的切換開銷。

    • 與DPDK 類似的還有?PF_RING?ZC module [45] 和 hardware-specific Solarflare OpenOnload [24]。

    在現(xiàn)有的所有框架中,內(nèi)核旁路方式性能是最高的?[18];但如引言中指 出,這種方式在管理、維護和安全方面都存在不足。

    XDP 采用了一種與內(nèi)核旁路截然相反的方式:相比于將網(wǎng)絡(luò)硬件的控制權(quán)上移到用戶空間, XDP 將性能攸關(guān)的包處理操作直接放在內(nèi)核中,在操作系統(tǒng)的網(wǎng)絡(luò)棧之前執(zhí)行。

    • 這種方式同樣避免了內(nèi)核-用戶態(tài)切換開銷(所有操作都在內(nèi)核);

    • 但仍然由內(nèi)核來管理硬件,因此保留了操作系統(tǒng)提供的管理接口和安全防護能力;

    這里的主要創(chuàng)新是:使用了一個虛擬執(zhí)行環(huán)境,它能對加載的 程序進行校驗,確保它們不會對內(nèi)核造成破壞。

    2.2 內(nèi)核模塊 vs. XDP

    在 XDP 之前,以內(nèi)核模塊(kernel module)方式實現(xiàn)包處理功能代價非常高, 因為程序執(zhí)行出錯時可能會導(dǎo)致整個系統(tǒng)崩潰,而且內(nèi)核的內(nèi)部 API 也會隨著時間發(fā)生變化。?因此也就不難理解為什么只有很少的系統(tǒng)采用了這種方式。其中做的比較好包括

    • 虛擬交換機?Open vSwitch [44]

    • Click [40]

    • 虛擬機路由器?Contrail [41]

    這幾個系統(tǒng)都支持靈活的配置,適用于多種場景,取得比較小的平攤代價。

    XDP通過:

  • 提供一個安全的執(zhí)行環(huán)境,以及內(nèi)核社區(qū)支持,提供與那些暴露到用戶空間一樣穩(wěn)定的內(nèi)核 API

    極大地降低了那些將處理過程下沉到內(nèi)核的應(yīng)用(applications of moving processing into the kernel)的成本。

  • 此外,XDP 程序也能夠完全繞過內(nèi)核網(wǎng)絡(luò)棧(completely bypass), 與在內(nèi)核網(wǎng)絡(luò)棧中做 hook 的傳統(tǒng)內(nèi)核模塊相比,性能也更高。

  • XDP 除了能將處理過程下沉到內(nèi)核以獲得最高性能之外,還支持在程序中執(zhí)行重定向?(redirection)操作,完全繞過內(nèi)核網(wǎng)絡(luò)棧,將包送到特殊類型的用戶空間 socket;?甚至能工作在 zero-copy 模式,進一步降低開銷。

    • 這種模式與 Netmap [46] 和 PF_RING [11] 方式類似,但后者是在沒有完全繞過內(nèi) 核的情況下,通過降低從網(wǎng)絡(luò)設(shè)備到用戶態(tài)應(yīng)用(network device to userspace application)之間的傳輸開銷,實現(xiàn)高性能包處理。

    內(nèi)核模塊方式的另一個例子是 Packet I/O engine,這是 PacketShader [19] 的組成部分, 后者專用于 Arrakis [43] and ClickOS [36] 之類的特殊目的操作系統(tǒng)。

    2.3 可編程硬件 vs. XDP

    可編程硬件設(shè)備也是一種實現(xiàn)高性能包處理的方式。

    • 一個例子是 NetFPGA [32],通過對它暴露的 API 進行編程,能夠在這種基于 FPGA 的專 用設(shè)備上運行任何包處理任務(wù)。

    • P4 編程語言 [7] 致力于將這種可編程能力擴展到更廣泛的包處理硬件上?(巧合的是,它還包括了一個 XDP backend [51])。

    某種意義上來說,XDP 可以認為是一種?offload?方式:

  • 性能敏感的處理邏輯下放到網(wǎng)卡驅(qū)動中,以提升性能;

  • 其他的處理邏輯仍然走內(nèi)核網(wǎng)絡(luò)棧;

  • 如果沒有用到內(nèi)核 helper 函數(shù),那整個 XDP 程序都可以 offload 到網(wǎng)卡(目前 Netronome smart-NICs [27] 已經(jīng)支持)。

  • 2.4 小結(jié)

    XDP 提供了一種高性能包處理方式,與已有方式相比,在性能、與現(xiàn)有系統(tǒng)的集成、靈活性 等方面取得了更好的平衡。接下來介紹 XDP 是如何取得這種平衡的。

    3 XDP 設(shè)計

    XDP 的設(shè)計理念:

    • 高性能包處理

    • 集成到操作系統(tǒng)內(nèi)核(kernel)并與之協(xié)同工作,同時

    • 確保系統(tǒng)其它部分的安全性(safety)完整性(integrity)

    這種與內(nèi)核的深度集成顯然會給設(shè)計帶來一些限制,在 XDP 組件合并到 Linux 的過程中,我們也收到了許多來自社區(qū)的反饋,促使我們不斷調(diào)整 XDP 的設(shè)計,但 這些設(shè)計反思不在本文討論范圍之內(nèi)。

    3.0 XDP 系統(tǒng)架構(gòu)

    圖 1 描繪了整個 XDP 系統(tǒng),四個主要組成部分:

  • XDP driver hook:XDP 程序的主入口,在網(wǎng)卡收到包執(zhí)行。

  • eBPF virtual machine:執(zhí)行 XDP 程序的字節(jié)碼,以及對字節(jié)碼執(zhí)行 JIT 以提升性能。

  • BPF maps:內(nèi)核中的 key/value 存儲,作為圖中各系統(tǒng)的主要通信通道。

  • eBPF verifier:加載程序時對其執(zhí)行靜態(tài)驗證,以確保它們不會導(dǎo)致內(nèi)核崩潰。

  • Fig 1. XDP 與 Linux 網(wǎng)絡(luò)棧的集成。這里只畫了 ingress 路徑,以免圖過于復(fù)雜。

    上圖是 ingress 流程。網(wǎng)卡收到包之后,在處理包數(shù)據(jù)(packet data)之前,會先執(zhí)行 main XDP hook 中的 eBPF 程序。?這段程序可以選擇:

  • 丟棄(drop)這個包;或者

  • 通過當(dāng)前網(wǎng)卡將包再發(fā)送(send)出去;或者

  • 將包重定向(redirect)到其他網(wǎng)絡(luò)接口(包括虛擬機的虛擬網(wǎng)卡),或者通過 AF_XDP socket 重定向到用戶空間;或者

  • 放行(allow)這個包,如果后面沒有其他原因?qū)е碌?drop,這個包就會進入常規(guī)的內(nèi)核網(wǎng)絡(luò)棧。如果是這種情況,也就是放行包進入內(nèi)核網(wǎng)絡(luò)棧,那接下來在將包放到發(fā)送隊列之前(before packets are queued for transmission), 還有一個能執(zhí)行 BPF 程序的地方:TC BPF hook。

  • 此外,圖 1 中還可以看出,不同的 eBPF 程序之間、eBPF 程序和用戶空間應(yīng)用之間,都能夠通過 BPF maps 進行通信。

    3.1 XDP driver hook

    在設(shè)備驅(qū)動中執(zhí)行,無需上下文切換

    XDP 程序在網(wǎng)絡(luò)設(shè)備驅(qū)動中執(zhí)行,網(wǎng)絡(luò)設(shè)備每收到一個包,程序就執(zhí)行一次。

    相關(guān)代碼實現(xiàn)為一個內(nèi)核庫函數(shù)(library function),因此程序直接 在設(shè)備驅(qū)動中執(zhí)行,無需切換到用戶空間上下文。

    在軟件最早能處理包的位置執(zhí)行,性能最優(yōu)

    回到上面圖 1 可以看到:程序在網(wǎng)卡收到包之后最早能處理包的位置?執(zhí)行 —— 此時內(nèi)核還沒有為包分配?struct sk_buff?結(jié)構(gòu)體, 也沒有執(zhí)行任何解析包的操作。

    XDP 程序典型執(zhí)行流

    下圖是一個典型的 XDP 程序執(zhí)行流:

    Fig 2. 典型 XDP 程序的執(zhí)行流。

    網(wǎng)卡收到一個包時,XDP程序依次執(zhí)行:

  • 提取包頭中的信息(例如 IP、MAC、Port、Proto 等),

    執(zhí)行到程序時,系統(tǒng)會傳遞給它一個上下文對象(context object)作為參賽?(即?struct xdp_md *ctx,后面有例子),其中包括了指向原 始包數(shù)據(jù)的指針,以及描述這個包是從哪個網(wǎng)卡的哪個接口接收上來的等元數(shù)據(jù)字段。

  • 讀取或更新一些資源的元信息(例如更新統(tǒng)計信息);

    解析包數(shù)據(jù)之后,XDP 程序可以讀取?ctx?中的包元數(shù)據(jù)(packet metadata) 字段,例如從哪個網(wǎng)卡的哪個接口收上來的(ifindex)。除此之外,ctx 對象還允許 程序訪問與包數(shù)據(jù)毗鄰的一塊特殊內(nèi)存區(qū)域(cb, control buffer), 在包穿越整個系統(tǒng)的過程中,可以將自定義的數(shù)據(jù)塞在這里。

    除了 per-packet metadata,XDP 程序還可以通過 BPF map 定義和訪問自己的持久數(shù)據(jù)?,以及通過各種 helper 函數(shù)訪問內(nèi)核基礎(chǔ)設(shè)施。

    • BPF map 使 BPF 程序能與系統(tǒng)的其他部分之間通信;

    • Helpers 使 BPF 程序能利用到某些已有的內(nèi)核功能(例如路由表), 而無需穿越整個內(nèi)核網(wǎng)絡(luò)棧。

    如果有需要,對這個包進行?rewrite header?操作,

    程序能修改包數(shù)據(jù)的任何部分,包括添加或刪除包頭。這使得 XDP 程序能執(zhí)行封裝/接封裝操作,以及重寫(rewrite)地址字段然后轉(zhuǎn)發(fā)等操作。

    內(nèi)核 helper 函數(shù)各有不同用途,例如修改一個包之后,計算新的校驗和(checksum)。

    進行最后的判決(verdict),確定接下來對這個包執(zhí)行什么操作;

    判決結(jié)果包括:

    重定向功能的用途:

    這些不同的路徑,在圖 1 對應(yīng)的是幾條實線。

    將重定向判決(verdict)與重定向目標(target)分開,使得重定向目標類型很容易擴展;?另外,由于重定向參數(shù)(目標)是通過 BPF map 查詢的,因此無需修 改 XDP 程序,就能動態(tài)修改重定向目標。

    • 三種簡單返回碼:丟棄這個包、通過接收時的網(wǎng)卡將包重新發(fā)送出去、允許這個包進入內(nèi)核網(wǎng)絡(luò)棧;

    • 第四種返回碼 redirect:允許 XDP 程序指定網(wǎng)卡、CPU、用戶態(tài) socket?等,將包重定向過去。

  • 將原始包通過另一個網(wǎng)卡(包括虛擬機的虛擬網(wǎng)卡)發(fā)送出去;

  • 轉(zhuǎn)發(fā)給指定?CPU?做進一步處理;

  • 轉(zhuǎn)發(fā)給?AF_XDP 類型的 socket?做進一步處理;

  • 程序還能通過尾調(diào)用(tail call),將控制權(quán)交給另一個 XDP 程序;?通過這種方式,可以將一個大程序拆分成幾個邏輯上的小程序(例如,根據(jù) IPv4/IPv6)。

    由于 XDP 程序可包含任意指令,因此前三步(讀取包數(shù)據(jù)、處理元數(shù)據(jù)、重寫包數(shù)據(jù))?順序可以是任意的,而且支持多層嵌套。?但實際中為了獲得高性能,大部分情況下還是將執(zhí)行結(jié)構(gòu)組織成這順序的三步。

    3.2 eBPF 虛擬機

    XDP 程序在 Extended BPF (eBPF) 虛擬機中執(zhí)行。eBPF 是早期 BSD packet filter (BPF) [37] 的擴展,后者在過去的幾十年中廣泛 應(yīng)用于各種包處理工具。

    BPF 使用?基于寄存器的(register-based) virtual machine 來描述?過濾動作(filtering actions)。

    eBPF 虛擬機支持動態(tài)加載(loading)和重加載(re-loading)程序,內(nèi)核管理所有 BPF 程序的生命周期。

    3.3 BPF maps

    eBPF 程序在觸發(fā)內(nèi)核事件時執(zhí)行(例如,觸發(fā) XDP 程序執(zhí)行的,是收包事件)。?程序每次執(zhí)行時,初始狀態(tài)都是相同的(即程序是無狀態(tài)的),它們無法直接訪問?內(nèi)核中的持久存儲(BPF map)。為此,內(nèi)核提供了訪問 BPF map 的 helper 函數(shù)。

    BPF map 是 key/value 存儲,在加載 eBPF 程序時定義(defined upon loading an eBPF program)。

    用途:

  • 持久存儲。例如一個 eBPF 程序每次執(zhí)行時,都會從里面獲取上一次的狀態(tài)。

  • 用于協(xié)調(diào)兩個或多個 eBPF 程序。例如一個往里面寫數(shù)據(jù),一個從里面讀數(shù)據(jù)。

  • 用于用戶態(tài)程序和內(nèi)核 eBPF 程序之間的通信。

  • 3.4 eBPF verifier

    唯一加載入口:bpf()?系統(tǒng)調(diào)用

    由于 eBPF 代碼直接運行在內(nèi)核地址空間,因此它能直接訪問 —— 也可 能是破壞 —— 任何內(nèi)存。為防止這種情況發(fā)生,內(nèi)核規(guī)定只能通過唯一入口(?bpf()?系統(tǒng)調(diào)用)加載 BPF 程序。

    加載 BPF 程序時,位于內(nèi)核中的校驗器首先會對字節(jié)碼程序進行靜態(tài)分析,以確保

    • 程序中沒有任何不安全的操作(例如訪問任意內(nèi)存),

    • 程序會終止(terminate)。通過下面這兩點來實現(xiàn):

      • 禁止循環(huán)操作

      • 限制程序最大指令數(shù)

    校驗器工作原理:two-pass DAG

    校驗器的工作原理:首先根據(jù)程序的控制流構(gòu)建一個有向無環(huán)圖(DAG), 然后對 DAG 執(zhí)行如下校驗:

    • 首先,對 DAG 進行一次深度優(yōu)先搜索(depth-first search),以 確保它是無環(huán)的(acyclic),例如,沒有循環(huán),也不包含不支持或無法執(zhí)行到的指令。

    • 然后,再掃描一遍,這次會遍歷 DAG 的所有可能路徑。這次掃描的目的是:

      程序執(zhí)行?load?或?call?指令時,如果參數(shù)不合法,就會在這里被拒絕。參數(shù)合法 性是通過在程序執(zhí)行期間跟蹤所有寄存器和棧變量的狀態(tài)(states of registers and stack variables)來實現(xiàn)的。

      • 確保程序的內(nèi)存訪問都是安全的,

      • 調(diào)用?helper 函數(shù)時傳的參數(shù)類型是對的。

    內(nèi)存越界和空指針檢查:職責(zé)上移到程序自身/開發(fā)者

    這種跟蹤寄存器狀態(tài)的機制是為了在無法預(yù)知內(nèi)存邊界的情況下,仍然確保程序 的內(nèi)存訪問不會越界。無法預(yù)知內(nèi)存邊界是因為:

    • 包的大小是不固定的;

    • map 的內(nèi)容也無法提前預(yù)知,因此也無法判斷一次 map 查找操作是否會成功。

    為解決這個問題,校驗器會檢查已加載的程序自身是否會做如下檢查:

  • 解引用指針前做了內(nèi)存邊界檢查,

  • 查詢 map 之前是檢查了 map 指針是否為空。

  • 這種方式將處理邏輯中的安全檢查和遇到錯誤時如何處理的控制權(quán)都?交給了 BPF 程序的編寫者。

    跟蹤數(shù)據(jù)訪問操作和值范圍

    為跟蹤數(shù)據(jù)訪問,校驗器會跟蹤

  • 數(shù)據(jù)類型

  • 指針偏置(pointer offsets)

  • 所有寄存器的可能值范圍

  • 程序開始時,

    • R1?寄存器中存儲的是指向 context metadata 的指針(struct xdp_md *ctx),

    • R10?是棧指針(stack pointer),

    • 其他所有寄存器都是未初始化狀態(tài)。

    接下來程序每執(zhí)行一步,寄存器狀態(tài)就會更新一次。當(dāng)寄存器中存入一個新值時,這個寄存器 還會繼承與這個值相關(guān)的狀態(tài)變量(inherits the state variables from the source of the value)。

    算術(shù)操作會影響標量類型的值的范圍(value ranges of scalar types),以及指針類型的 offset。?可能的最大范圍(max possible range)存儲在狀態(tài)變量中,例如往寄存器中 load 一個字節(jié)時, 這個寄存器的可能值范圍就設(shè)置為 0~255。指令圖(instruction graph)中的?各邏輯分支就會根據(jù)操作結(jié)果更新寄存器狀態(tài)。例如,比較操作?R1 > 10,

    • 校驗器在一個分支?if R1 > 10?中會將 R1 最小值設(shè)為 11,

    • 在另一個?else?分支中將其最大值設(shè)為 10。

    不同類型數(shù)據(jù)的校驗信息來源(source of truth)

    利用狀態(tài)變量中存儲的范圍信息,校驗器就能預(yù)測每個 load 指令能訪問的所有 內(nèi)存范圍,確保它執(zhí)行的都是合法內(nèi)存訪問。

  • 對于包數(shù)據(jù)(packet data)的訪問,會與 context 對象中的?data_end?變量做比較;

  • 對于?BPF map 中獲取的值,或用到 map 定義中聲明的 data size 信息;

  • 對于棧上存儲的值,會檢查狀態(tài)變量中記錄的值范圍;

  • 對于指針算術(shù)操作(pointer arithmetic)還會施加額外的限制,指針通常不能被轉(zhuǎn)換成整形值。

  • 只要校驗器無法證明某個操作是安全,該?BPF 程序在加載時(load time)就會被拒絕。?除此之外,校驗器還會利用范圍信息確保內(nèi)存的對齊訪問(enforce aligned memory access)。

    校驗器的目的

    需要說明的是,校驗器的目的是避免將內(nèi)核內(nèi)部(the internals of the kernel )暴露給惡意或有缺陷的 eBPF 程序,而非確保程序中函數(shù)的實現(xiàn)已經(jīng)是最高效的。

    換句話說,如果 XDP 程序中處理邏輯過多,也可能會導(dǎo)致機器變慢 ;如果代碼寫的有問題,也可能會破壞包數(shù)據(jù)。出于這些原因,加載 BPF 程序需要 管理員權(quán)限(root)。避免這些 bug 的責(zé)任在程序員,但選擇將哪些程序加載 到系統(tǒng)的權(quán)限在管理員。

    3.5 XDP 程序示例

    下面是一個簡單的 XDP 程序,展示了前面介紹的一些特性。?程序會解析包數(shù)據(jù),判斷如果是 UDP 包,直接交換源和目的 MAC 地址,然后將包從相同網(wǎng)卡再發(fā)送回去,

    雖然這是一個非常簡單的例子,但真實世界中的 XDP 程序用到的組件和特性,這里基本都具備了。

    // 從內(nèi)核 BPF 代碼示例 xdp2_kern.c 修改而來。1 // 用于統(tǒng)計包數(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 }

    具體地:

    • 定義了一個 BPF map 存儲統(tǒng)計信息。用戶態(tài)程序可以 poll 這個 map 來獲取統(tǒng)計信息。

    • context 對象?struct xdp_md *ctx?中有包數(shù)據(jù)的 start/end 指針,可用于直接訪問包數(shù)據(jù)。

    • 將數(shù)據(jù)指針和?data_end?比較,確保內(nèi)存訪問不會越界。

    • 程序必須自己解析包,包括 VLAN headers 等東西。

    • 直接通過指針(direct packet data access)修改包頭。

    • 內(nèi)核提供的 map lookup helper。這是程序中唯一的真實函數(shù)調(diào)用;其他函數(shù)都是內(nèi)聯(lián),包括?htons()。

    • 最終針對這個包的判決通過程序返回值傳遞給調(diào)用方。

    將這段程序安裝到網(wǎng)卡接口上時,它首先會被編譯成 eBPF 字節(jié)碼,然后經(jīng)受校驗器檢查。?這里的檢查項包括:

  • 無循環(huán)操作;程序大小(指令數(shù)量);

  • 訪問包數(shù)據(jù)之前,做了內(nèi)存邊界檢查;

  • 傳遞給 map lookup 函數(shù)的參數(shù),類型與 map 定義相匹配;

  • map lookup 的返回值(value 的內(nèi)存地址)在使用之前,檢查了是否為 NULL。

  • 3.6 小結(jié)

    XDP 系統(tǒng)由四個主要部分組成:

  • XDP device driver hook:網(wǎng)卡收到包之后直接運行;

  • eBPF虛擬機:執(zhí)行 XDP 程序(以及內(nèi)核其他模塊加載的 BPF 程序);

  • BPF maps:使不同 BPF 程序之間、BPF 程序與用戶空間應(yīng)用之間能夠通信;

  • eBPF verifier:確保程序不包含任何可能會破壞內(nèi)核的操作。

  • 這四部分加在一起,創(chuàng)造了一個編寫自定義包處理應(yīng)用的強大環(huán)境,它能加速包處理的關(guān)鍵 路徑,同時還與內(nèi)核及現(xiàn)有基礎(chǔ)設(shè)施密切集成。

    接下來看一下 XDP 應(yīng)用的性能。

    4 性能評估

    DPDK 是目前性能最高的包處理框架 [18],因此本文將 XDP 與 DPDK 及 Linux 內(nèi)核網(wǎng)絡(luò) 棧的性能做一個對比。測試機器環(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ò)硬件通過 DMA 能直接將包放到 CPU 緩存。

    • 關(guān)閉超線性(Hyperthreading)。

    • 網(wǎng)卡:兩塊?Mellanox ConnectX-5 Ex VPI dual-port 100Gbps,mlx5 驅(qū)動。

    • 內(nèi)核:Linux 4.18

    • 使用基于 DPDK 的 TRex packet generator [9] 生成測試流量。所有測試腳本位于 [22]。

    在測試中,我們主要關(guān)心三個 metric:

    • 直接棄包(packet drop)性能。

      為展示最高的包處理性能,我們將用最簡單的操作 ——?丟棄接收到的包?—— 來測試。?這個測試能有效測量系統(tǒng)的整體開銷,也是真正的包處理應(yīng)用能達到的性能上限。

    • CPU 使用量。

      如引言中指出,XDP 的優(yōu)點之一是 CPU 使用量與流量大小是正相關(guān)的,而無需預(yù)留專 門的 CPU 給它用。我們通過測量 CPU 利用率隨網(wǎng)絡(luò)負載的變化來量化這個指標。

    • 包轉(zhuǎn)發(fā)性能。

      轉(zhuǎn)發(fā)的復(fù)雜性要更高一些,例如,涉及到與多塊網(wǎng)卡的交互、重寫二層頭等等。?這里會將轉(zhuǎn)發(fā)延遲也考慮進去。

    我們已經(jīng)驗證,使用 MTU(1500 字節(jié))包時,我們的系統(tǒng)單核就能達到線速(100 Gbps), 而且 CPU 有 50% 是空閑的。顯然,真正的挑戰(zhàn)在于 PPS,而非帶寬,其他一些測試也已經(jīng)指出了這一點 [46]。?出于這個原因,我們用最小包(64 字節(jié))測試,衡量指標是 PPS。

    對于 XDP 和 Linux 內(nèi)核網(wǎng)絡(luò)棧的測試,由于它們沒有顯式指定某些 CPU 來處理網(wǎng)絡(luò)包的方式,因此我們通過配置硬件 RSS(Receive Side Scaling)來講流量定向到指定 CPU。

    對網(wǎng)卡、內(nèi)核的一些配置調(diào)優(yōu),見代碼倉庫 [22]。

    4.1 直接棄包(packet drop)性能

    Fig 3. 直接棄包(packet drop)性能。DPDK 需要預(yù)留一個 CPU 運行控制任務(wù),因此只剩下 5 個 CPU 做包處理。

    上圖是性能與 CPU 數(shù)量的關(guān)系。

    • XDP 基準性能是?24Mpps/core,DPDK 是?43.5Mpps/core。

    • 二者在分別達到各自的峰值之前,PPS 都是隨 CPU 數(shù)量線性增長的。

    • 最終全局性能受限于 PCI 總線,啟用 PCI descriptor compression(在 CPU cycles 和 PCI 總線帶寬之間取舍)之后,能達到 115Mpps。

    再看圖中 Linux 網(wǎng)絡(luò)棧在兩種配置下的性能:

  • 通過?iptables 的 raw table?丟棄流量,這是?Linux 網(wǎng)絡(luò)棧中最早能丟棄包的地方;

  • 通過?conntrack(連接跟蹤)模塊,這個模塊的開銷非常大,但在很多 Linux 發(fā)行版中都是默認開啟的。

  • conntrack 模式達到了 1.8Mpps/core,raw 模式是 4.8Mpps/core ;這兩種模式均未達到硬件瓶頸。?最終的性能,XDP 比常規(guī)網(wǎng)絡(luò)棧的最快方式快了 5 倍。

    Linux raw mode test 中,我們還測量了 XDP 程序不丟棄包,而是更新包數(shù)統(tǒng)計然后將包 送到內(nèi)核網(wǎng)絡(luò)棧的場景。?這種情況下,XDP 單核的處理性能會下降到 4.5Mpps/core,有 13.3ns 處理延遲。?圖中并未給出這個測試結(jié)果,因為這個開銷太小了。

    4.2 CPU Usage

    Fig 4. 直接棄包(packet drop)場景下的 CPU 利用率。

    用系統(tǒng)提供的?mpstat?命令測量 CPU 利用率。結(jié)果如圖 4 。

    • DPDK 是 busy poll 模式,因此 CPU 永遠是 100%。

    • XDP 和 Linux 內(nèi)核網(wǎng)絡(luò)棧都是隨流量平滑增長:前面一小段是非線性的,后面基本是線性的。

      前面非線性主要是硬中斷帶來的固定開銷,在流量很小時這一部分占比較大。

    4.3 包轉(zhuǎn)發(fā)性能

    這個測試中,轉(zhuǎn)發(fā)應(yīng)用執(zhí)行非常簡單的 MAC 地址重寫:直接交換源和目的 MAC 地址,然后轉(zhuǎn)發(fā)。?這是轉(zhuǎn)發(fā)場景下最精簡的步驟了,因此結(jié)果代表了所有真實轉(zhuǎn)發(fā)應(yīng)用的性能上限。

    • 圖中包括了同網(wǎng)卡轉(zhuǎn)發(fā)和不同網(wǎng)卡轉(zhuǎn)發(fā)(XDP?程序返回碼不同)的結(jié)果。

    • DPDK 示例程序只支持通過另一個網(wǎng)卡轉(zhuǎn)發(fā),因此這里只列出了這種情況下的性能。

    • Linux 網(wǎng)絡(luò)棧不支持這種極簡轉(zhuǎn)發(fā)模式(minimal forwarding mode),需要設(shè)置完整的 橋接或路由查找(bridging or routing lookup)才能轉(zhuǎn)發(fā)包;路由查找是非常耗時的 ,由于其他幾種應(yīng)用并沒有這一步,因此結(jié)果直接對比是沒意義的。因此這里略去了 Linux 網(wǎng)絡(luò)棧的結(jié)果。

    轉(zhuǎn)發(fā)吞吐(pps)

    Fig 5. 轉(zhuǎn)發(fā)性能。在同一網(wǎng)卡接口上收發(fā)會占用同一 PCI port 的帶寬, 這意味著在 70Mpps XDP same-nic 組就已經(jīng)達到了 PCI 總線的瓶頸

    如圖 5 所示,性能隨 CPU 數(shù)量線性擴展,直到達到全局性能瓶頸。XDP 在同網(wǎng)卡轉(zhuǎn)發(fā)的性能遠高于 DPDK 異網(wǎng)卡性能,原因是內(nèi)存處理方式不同:

    • packet buffer 是設(shè)備驅(qū)動分配的,與接收接口(receiving interface)相關(guān)聯(lián)。

    • 因此,異網(wǎng)卡場景下,當(dāng)包轉(zhuǎn)發(fā)到另一個接口時,memory buffer 需要還給與之關(guān)聯(lián)的接口。

    轉(zhuǎn)發(fā)延遲

    表 1. 轉(zhuǎn)發(fā)延遲。機器網(wǎng)卡的兩個接口直連,在轉(zhuǎn)發(fā)速率分別為 100pps 和 1Mpps 的條件下,持續(xù) 50s 測量端到端延遲

    高 pps 場景下,XDP 的延遲已經(jīng)接近 DPDK。但在低 pps 場景下,XDP 延遲比 DPDK 大的多,原因是?XDP 是基于中斷的,中斷處理時間( interrupt processing time)此時占大頭;而 DPDK 是輪詢模式,延遲相對比較固定。

    4.4 討論:XDP 性能與 DPDK 還有差距的原因

    XDP 未做底層代碼優(yōu)化

    上一節(jié)已經(jīng)看到,XDP 相比于常規(guī) Linux 網(wǎng)絡(luò)棧性能有了顯著提升。但對于大部分 XDP 場景來說,性能還是與 DPDK 有差距。我們認為,這是主要是因為 DPDK 做了相當(dāng)多的底層 代碼優(yōu)化。舉個例子來解釋,考慮 packet drop 例子:

    • XDP 24Mpps/core,對應(yīng)?41.6ns/packet

    • DPDK 43.5Mpps,對應(yīng)?22.9ns/packet

    多出來的 18.7ns 在我們的 3.6GHz 機器上對應(yīng) 67 個時鐘周期。因此,很顯然?每個很小的優(yōu)化在這里都會產(chǎn)生很大的影響。例如,我們測量出在測試 機器上,每次函數(shù)調(diào)用需要 1.3ns。mlx5 驅(qū)動處理每個包都有 10 次 函數(shù)調(diào)用,總計就是 13ns。

    通用目的操作系統(tǒng),首要目標:更好的擴展和配置,而非極致性能

    另外,在 Linux 這樣的通用目的操作系統(tǒng)中,某些開銷是不可避免的, 因為設(shè)備驅(qū)動或子系統(tǒng)的組織方式是為了實現(xiàn)更好的擴展和配置,而非極致性能。

    但是,我們認為有些優(yōu)化還是有必要的。例如,我們嘗試將內(nèi)核中與測試網(wǎng)卡無關(guān)的 DMA 函數(shù)調(diào)用刪掉, 這樣將前面提到的 10 個函數(shù)調(diào)用降低到了 6 個,測試結(jié)果顯示這將單核性能提升到了 29Mpps/core。?依此推測的話,將另外 6 個函數(shù)調(diào)用也優(yōu)化掉,能將 XDP 的性能提升到?37.6Mpps。?實際上我們不可能將 6 個全部去掉,但去掉其中幾個,再加上一些其他優(yōu)化,我 們相信 XDP 和 DPDK 的性能差距將越來越小。

    其他驅(qū)動的測試結(jié)果也是類似的,例如 i40e driver for 40 Gbps Intel cards。

    基于以上討論,我們相信未來 XDP 與 DPDK 的性能差距將越來越小。

    另一方面,考慮到 XDP 在靈活性和與內(nèi)核集成方面的優(yōu)勢, XDP 已經(jīng)是很多實際場景中的非常有競爭力的方式。下文給出幾個例子。

    5 真實場景使用案例

    本節(jié)給出三個例子來具體展示 XDP 在真實世界中的應(yīng)用。?這幾個案例都是已經(jīng)真實在用的,但本文出于解釋目的,將使用簡化的版本。?同時也建議讀者參考 [38],后者是獨立的文章,介紹使用 XDP 解決實際工作中網(wǎng)絡(luò)服務(wù)所面臨的一些挑戰(zhàn)。

    本節(jié)目的是展示真實 XDP 方案的可行性,因此不會將重點放在與業(yè)界最新的實現(xiàn)做詳盡性能對比上。?我們會拿常規(guī)的 Linux 內(nèi)核網(wǎng)絡(luò)棧的性能作為 baseline,來對比 XDP 應(yīng)用的性能。

    5.1 案例一:軟件路由(software routing)

    內(nèi)核數(shù)據(jù)平面 & 控制平面(BIRD/FRR)

    Linux 內(nèi)核實現(xiàn)了一個功能完整的路由表,作為數(shù)據(jù)平面,支持

    • policy routing

    • source-specific routing

    • multipath load balancing, and more.

    對于控制平面,Bird [10] 或 FRR [17] 這樣的路由守護進程( routing daemons)實現(xiàn)了多種路由控制平面協(xié)議。Linux 提供的這套生態(tài)系統(tǒng)功能如此豐富 ,因此再在另一個包處理框架中重新實現(xiàn)一套類似的路由棧代價將非常高, 更實際的方式是對 Linux 內(nèi)核的數(shù)據(jù)平面進行優(yōu)化。

    XDP:直接查詢內(nèi)核路由表并轉(zhuǎn)發(fā)

    XDP 非常適合做這件事情,尤其是它提供了一個 helper 函數(shù),能從 XDP 程序中直接查詢內(nèi)核路由表。

    • 如果查詢成功,會返回?egress interface 和下一跳 MAC 地址, XDP 程序利用這些信息足夠?qū)⒓崔D(zhuǎn)發(fā)出去。

    • 如果下一跳 MAC 還是未知的(因為之前還沒進行過 neighbour lookup),XDP 程序就 能將包傳給內(nèi)核網(wǎng)絡(luò)棧,后者會解析 neighbor 地址,這樣隨后的包 就能直接被 XDP 程序轉(zhuǎn)發(fā)了。

    測試:XDP routing + 全球 BGP 路由表

    為展示 XDP 路由的性能,我們用 Linux 內(nèi)核代碼中的 XDP routing 例子 [1],與常規(guī) Linux 內(nèi)核網(wǎng)絡(luò)棧的性能做對比。?兩組測試:

  • 路由表中只有一條路由;

  • 路由表中有從 routeviews.org 中 dump 而來的全球 BGP 路由表(global BGP routing table)。?包含 752,138 條路由。隨機生成 4000 個目的 IP 地址,以確保能充分利用到這種路由表。

    如果目的 IP 地址少于 4000 個,實際用到的路由表部分會較小,能夠保存在 CPU 緩存中,使得結(jié)果不準確。?增大 IP 數(shù)量至 4000 個以上,不會對轉(zhuǎn)發(fā)性能造成影響,但可以避免緩存導(dǎo)致的結(jié)果不準問題。

  • 對于兩組測試,下一跳 MAC 地址都是與我們的發(fā)送網(wǎng)卡直接相關(guān)的接口的地址。

    性能:2.5x

    Fig 6. 軟件路由的性能。由于性能隨核數(shù)線性增加,這里只給出單核的結(jié)果。

    測試結(jié)果如上圖所示。

    • full table lookup 性能提升了 2.5 倍;

    • smaller routing table 組,提升了 3 倍。

    這說明,XDP 路由程序 + 單核 + 10Gbps 網(wǎng)卡?的軟硬件配置,就能?處理整張全球 BGP 路由表(保守估計每個包平均 300 字節(jié))。

    5.2 案例二:Inline DoS Mitigation

    DoS 攻擊還是像瘟疫一樣糾纏著互聯(lián)網(wǎng),現(xiàn)在通常的方式是:通過已經(jīng)入侵的大量設(shè)備發(fā)起分布式(DDoS)攻擊。

    有了XDP 之后,我們能直接在應(yīng)用服務(wù)器(application servers)上?部署包過濾程序來防御此類攻擊(inline DoS mitigation),?無需修改應(yīng)用代碼。如果應(yīng)用是部署在虛擬機里,那 XDP 程序還可以 部署在宿主機(hypervisor)上,這樣單個程序就能保護機器上所有的虛擬機。

    模擬 Cloudflare 防御架構(gòu)

    為展示工作原理,我們用 XDP 作為過濾機制,模擬 Cloudflare 的 DDoS 防御架構(gòu)?[6]。?他們的 Gatebot architecture ,首先在各 PoP 點機器上采樣,然后統(tǒng)一收起來做分析, 根據(jù)分析結(jié)果生成防御規(guī)則。

    防御規(guī)則的形式是對包數(shù)據(jù)(payload)進行一系列簡單檢查, 能直接編譯成 eBPF 代碼然后分發(fā)到 PoP 點的所有服務(wù)器上。這里說的代碼是 XDP 程序 ,它會將匹配到規(guī)則的所有流量丟棄,同時將統(tǒng)計信息更新到 BPF map。

    程序邏輯

    為驗證這種方案的性能,我們編寫一個 XDP 程序,它

  • 解析包頭,執(zhí)行一些簡單驗證。對每個包:執(zhí)行四次讀取操作,以解析外層包頭。

  • 將符合攻擊特性的流量丟棄。具體:丟棄 UDP + 特定端口的流量。

  • 將其他流量通過 CPU redirect 方式重定向給另一個 CPU?做進一步處理;

  • 性能

    我們用 netperf 做性能壓測 [26]。

    • 用 netperf TCP round-trip benchmark,單個 TCP 連接來回小的 request/reply,統(tǒng)計 transactions/second。

      模擬的是交互式應(yīng)用,例如小的遠程過程調(diào)用(RPC)。

    • 實驗在單核上進行,模擬多個流量(正常流量+攻擊流量)競爭同一物理資源的場景。

    • 在?beseline 35K 業(yè)務(wù) TPS(transactions per second)基礎(chǔ)上,打少量 UDP 流量作為攻擊流量。逐漸加大攻擊流量,觀察 TPS 的變化。

    Fig 7. DDoS 性能。業(yè)務(wù)吞吐(TPS)隨攻擊流量的變化。

    結(jié)果如上圖所示,

    • 沒有 XDP 的一組,性能急劇下降:攻擊流量在 3Mpps 時性能減半,3.5Mpps 時基本跌零;

    • 有 XDP 程序的一組,攻擊流量達到 19.5Mpps 之前,業(yè)務(wù)吞吐保持在 28.5K TPS 以上,過了這個臨界點性能才開始急劇下降。

    以上結(jié)果表明,XDP 防御 DDoS 攻擊在實際中是完全可行的,單核就能輕松處理 10Gbps 的、都是最小包(minimum-packet)的 DoS 流量。?這種 DDoS 防御的部署更加靈活,無需硬件或應(yīng)用做任何改動。

    5.3 案例三:負載均衡(load balancing)

    Facebook Katran

    負載均衡的場景,我們用 Facebook 開源的 Katran 作為例子 [15]。Katran 的工作原理是對外通告服務(wù)的 IP,這樣目標是這個 IP 的流量就會被路由到 XDP 實現(xiàn)的負載均衡器。

    • 負載均衡器對包頭(source packet header)進行哈希,以此選擇目標應(yīng)用服務(wù)器。

    • 然后將對包進行封裝(encap),發(fā)送給應(yīng)用服務(wù)器;

    • 應(yīng)用服務(wù)器解封裝(decap),處理請求,然后直接將回包發(fā)給客戶端(DSR 模式)。

    在這個過程中,XDP 程序負責(zé)哈希、封裝以及將包從接收網(wǎng)卡再發(fā)出去的任務(wù)。?配置信息存儲在 BPF map 中,整個封裝邏輯是完全在 eBPF 中實現(xiàn)的。

    性能

    為測試性能,我們給 Katran XDP 程序配置幾個固定的目標機器。?對照組是 IPVS,它是 Linux 內(nèi)核的一部分。性能如表 2 所示,隨 CPU 數(shù)量線性增長, XDP 比 IPVS 性能高 4.3 倍。

    表 2. 負載均衡器性能(Mpps)

    配置:1 VIP/core, 100 DstIPs/VIP.

    6 XDP 的未來方向

    XDP已經(jīng)能用于解決真實問題,但作為Linux內(nèi)核的一部分,XDP 還在快速開發(fā)過程中。

    6.1 eBPF 程序的限制

    前面提到,加載到 eBPF 虛擬機的程序必須保證其安全性(不會破壞內(nèi)核),因此對 eBPF 程序作了一下限制,歸結(jié)為兩方面:

  • 確保程序會終止:在實現(xiàn)上是通過禁止循環(huán)和限制程序的最大指令數(shù)(max size of the program);

  • 確保內(nèi)存訪問的安全:通過 3.4 小結(jié)介紹的寄存器狀態(tài)跟蹤(register state tracking)來實現(xiàn)。

  • 校驗邏輯偏保守

    由于校驗器的首要職責(zé)是保證內(nèi)核的安全,因此其校驗邏輯比較保守, 凡是它不能證明為安全的,一律都拒絕。有時這會導(dǎo)致假陰性(false negatives), 即某些實際上是安全的程序被拒絕加載;這方面在持續(xù)改進。

    • 校驗器的錯誤提示也已經(jīng)更加友好,以幫助開發(fā)者更快定位問題。

    • 近期已經(jīng)支持了 BPF 函數(shù)調(diào)用(function calls)。

    • 正在計劃支持有限循環(huán)(bounded loops)。

    • 正在提升校驗器效率,以便處理更大的 BPF 程序。

    缺少標準庫

    相比于用戶空間 C 程序,eBPF 程序的另一個限制是缺少標準庫,包括?內(nèi)存分配、線程、鎖等等庫。

  • 內(nèi)核的生命周期和執(zhí)行上下文管理(life cycle and execution context management )部分地彌補了這一不足,(例如,加載的 XDP 程序會為每個收到的包執(zhí)行),

  • 內(nèi)核提供的 helper 函數(shù)也部分地彌補了一不足。

  • 一個網(wǎng)卡接口只能 attach 一個 XDP 程序

    這個限制其實也是可以繞過的:將 XDP 程序組織成程序數(shù)組,通過尾 調(diào)用,根據(jù)包上下文在程序之間跳轉(zhuǎn),或者是將幾個程序做 chaining。

    6.2 用戶體驗和調(diào)試

    XDP 程序運行在內(nèi)核,因此常規(guī)的用戶空間 debug 工具是用不了的,但內(nèi)核自帶的 debug 和 introspection 功能是可以用在 XDP (及其他 eBPF 程序)上的。?包括:

    • tracepoints and kprobes [13]

    • performance counters that are part of the perf subsystem [42]

    但不熟悉內(nèi)核生態(tài)系統(tǒng)的開發(fā)者可能會對這些工具感到非常陌生,難以使用。因此,也出 現(xiàn)了一些更方便普通開發(fā)者的工具,包括 BCC [50]、bpftool [8]、libbpf 函數(shù)庫 [30] 等等。

    6.3 驅(qū)動支持

    設(shè)備要支持 XDP,需要實現(xiàn)內(nèi)核核心網(wǎng)絡(luò)棧暴露出的一個 API。?寫作本文時 Linux 4.18 已經(jīng)有 12 種驅(qū)動支持 XDP,包括了大部分高速網(wǎng)卡。?最新列表見 [2]。

    隨著 XDP 系統(tǒng)的不斷成熟,核心代碼逐漸上移到內(nèi)核中,驅(qū)動需要維護的代碼越 來越少。例如,redirection action 支持新的 target 時,無需驅(qū)動做任何改動。

    最后,對于那些不支持 XDP 的驅(qū)動,內(nèi)核提供了?Generic XDP?feature [39],這是軟件實現(xiàn)的 XDP,性能會低一些, 在實現(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 之間還有一些性能差距,一些改進工作正在進行中:

    • 驅(qū)動代碼 micro-optimisations

    • 刪除核心 XDP 代碼中的非必要操作

    • 通過批處理平攤處理開銷

    6.5 QoS 和 Rate Transitions

    當(dāng)前,XDP 還沒有任何 QoS 機制。?尤其是,如果對端已經(jīng)過載(例如兩端的網(wǎng)絡(luò)速度或特性不匹配),XDP 程序是收不到任何背壓(back-pressure)的,

    雖然 XDP 中缺少 QoS,但 Linux 內(nèi)核網(wǎng)絡(luò)棧中卻有很多業(yè)界最佳的 Active Queue Management (AQM) 特性和 packet scheduling algorithms [23]。?這些特性中,部分并不適用于 XDP,但我們相信能夠 以一種對包處理應(yīng)用完全透明的方式,選擇其中部分集成到 XDP。?我們計劃對這一方向進行更深入研究。

    6.6 加速傳輸層協(xié)議

    我們已經(jīng)證明 XDP 能在保留操作系統(tǒng)原有功能的前提下,集成到操作系統(tǒng)中,實現(xiàn)高速包數(shù)據(jù)。

    目前的 XDP 還是用于無狀態(tài)包處理(stateless packet processing) ,如果將這個模型擴展到有狀態(tài)傳輸層協(xié)議(stateful transport protocols),例如 TCP,它能給依賴可靠/有狀態(tài)傳輸?shù)膽?yīng)用提供類似的性能提升。

    實際上,已經(jīng)有一些研究證明,相比于操作系統(tǒng)的協(xié)議棧,accelerated transport protocols 能顯著提升性能[5, 25, 35, 52]。其中的一個解決方案 [52] 表明,在保留內(nèi) 核 TCP 協(xié)議棧的的前提下,原始包處理性能(raw packet processing)存在巨大的提升 空間。

    XDP 非常適用于這種場景,目前也已經(jīng)有一些關(guān)于如何實現(xiàn)的初步討論 [21], 雖然離實際使用還很遠,但仍然是一個令人振奮的、擴展 XDP 系統(tǒng) scope 的方向。

    6.7 內(nèi)核-用戶空間零拷貝(zero-copy to userspace)

    3.1 小節(jié)提到,XDP 程序能將數(shù)據(jù)包重定向到用戶空間應(yīng)用(userspace application)打 開的特殊類型 socket。這可以用于加速客戶端和服務(wù)端在同一臺機器?的網(wǎng)絡(luò)密集型應(yīng)用(network-heavy applications running on the local machine)。

    更多信息可參考:?(譯) 利用 ebpf sockmap/redirection 提升 socket 性能(2020)。?這里使用的是 BPF 而非 XDP,但核心原理是一樣的,只是程序執(zhí)行的位置(hook)不同。?譯注。

    但在目前的實現(xiàn)中,這種方式在底層仍然需要拷貝包數(shù)據(jù),因此性能會打折扣。

    目前已經(jīng)有工作在進行,通過 AF_XDP 實現(xiàn)真正的數(shù)據(jù)零拷貝。但這項工作需要?對網(wǎng)絡(luò)設(shè)備的內(nèi)存處理過程有一些限制,因此需要設(shè)備驅(qū)動的顯式支持。?第一個支持這個功能的 patch 已經(jīng)合并到?4.19?內(nèi)核,更多驅(qū)動的支持 正在添加中。初步的性能測試結(jié)果還是很樂觀的,顯示能達到 20Mpps/core 的內(nèi)核到用戶 空間傳遞(transfer)速度。

    6.8 XDP 作為基礎(chǔ)構(gòu)建模塊(XDP as a building block)

    正如 DPDK 用于高層包處理框架的底層構(gòu)建模塊(例如 [31]),XDP 有望成為高層應(yīng)用的運行時環(huán)境 (runtime environment for higher-level applications)。

    實際上,我們看到一些基于 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ū)動 [53]。

    7 總結(jié)

    本文描述了 XDP,一個安全、快速、可編程、集成到操作系統(tǒng)內(nèi)核的包處理框架。?測試結(jié)果顯示,XDP 能提供 24Mpps/core 的高處理性能,這一數(shù)字雖然與基于 kernel bypass 的 DPDK 仍有差距,但提供了其他一些非常有競爭力的優(yōu)勢:

  • 兼容內(nèi)核安全和管理框架(kernel bypass 方式在 bypass 內(nèi)核網(wǎng)絡(luò)棧的同時,也將安全和設(shè)備管理等這些極其重要的基礎(chǔ)設(shè)施 bypass 了);

  • 兼容內(nèi)核網(wǎng)絡(luò)棧,可選擇性利用內(nèi)核已有的基礎(chǔ)設(shè)施和功能;

  • 提供與內(nèi)核 API 一樣穩(wěn)定的編程接口;

  • 對應(yīng)用完全透明;

  • 更新、替換程序的過程不會引起服務(wù)中斷;

  • 無需專門硬件,無需獨占 CPU 等資源。

  • 相比于 kernel bypass 這種非此即彼、完全繞開內(nèi)核的方式,我們相信 XDP 有更廣闊的的應(yīng)用前景。Facebook、Cloudflare 等公司實際落地的 XDP 應(yīng)用,更加增強了我們的這種信心。

    最后,XDP系統(tǒng)還在快速發(fā)展,前面也列出了一些正在未來可能會做的開發(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.

    參考文獻

    https://arthurchiao.art/blog/xdp-paper-acm-2018-zh/

    - END -


    看完一鍵三連在看轉(zhuǎn)發(fā)點贊

    是對文章最大的贊賞,極客重生感謝你

    推薦閱讀

    深入理解Linux內(nèi)核之內(nèi)存尋址


    2022新年重磅技術(shù)分享|深入理解Linux操作系統(tǒng)


    一些優(yōu)秀的后端開源項目!


    你好,這里是極客重生,我是阿榮,大家都叫我榮哥,從華為->外企->到互聯(lián)網(wǎng)大廠,目前是大廠資深工程師,多次獲得五星員工,多年職場經(jīng)驗,技術(shù)扎實,專業(yè)后端開發(fā)和后臺架構(gòu)設(shè)計,熱愛底層技術(shù),豐富的實戰(zhàn)經(jīng)驗,分享技術(shù)的本質(zhì)原理,希望幫助更多人蛻變重生,拿BAT大廠offer,培養(yǎng)高級工程師能力,成為技術(shù)專家,實現(xiàn)高薪夢想,期待你的關(guān)注!點擊藍字查看我的成長之路

    校招/社招/簡歷/面試技巧/大廠技術(shù)棧分析/后端開發(fā)進階/優(yōu)秀開源項目/直播分享/技術(shù)視野/實戰(zhàn)高手等,?極客星球希望成為最有技術(shù)價值星球,盡最大努力為星球的同學(xué)提供技術(shù)和成長幫助!詳情查看->極客星球

    ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 求點贊,在看,分享三連

    的昂貴性能開銷。

    但是,操作系統(tǒng)被旁路(繞過)之后,它的應(yīng)用隔離(application isolation) 和安全機制(security mechanisms)就都失效了;一起失效的還有各種經(jīng)過已經(jīng) 充分測試的配置、部署和管理工具。

    為解決這個問題,我們提出一種新的可編程包處理方式:eXpress Data Path (XDP)。

    • XDP 提供了一個仍然基于操作系統(tǒng)內(nèi)核的安全執(zhí)行環(huán)境,在設(shè)備驅(qū)動上下文?(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)通過 C 等高層語言編寫,然后編譯成特定字節(jié)碼;出于安 全考慮,內(nèi)核會首先對這些字節(jié)碼執(zhí)行靜態(tài)分析,然后再將它們翻譯成?處理器原生指令(native instructions)。

    • 測試結(jié)果顯示,XDP 能達到?24Mpps/core?的處理性能。

    為展示 XDP 靈活的編程模型,本文還將給出三個程序示例,

  • layer-3 routing(三層路由轉(zhuǎn)發(fā)

  • inline DDoS protection(DDoS 防護

  • layer-4 load balancing(四層負載均衡

  • 1 引言

    軟件實現(xiàn)高性能包處理的場景,對每個包的處理耗時有著極高的要求。通用目的操作系統(tǒng)中 的網(wǎng)絡(luò)棧更多是針對靈活性的優(yōu)化,這意味著它們花在每個包上 的指令太多了,不適合網(wǎng)絡(luò)高吞吐的場景。

    因此,隨后出現(xiàn)了一些專門用于包處理的軟件開發(fā)工具,例如 Data Plane Development Kit (DPDK) [16]。這些工具一般都會完全繞過內(nèi)核,將網(wǎng)絡(luò)硬件直接交 給用戶態(tài)的網(wǎng)絡(luò)應(yīng)用,并需要獨占一個或多個 CPU。

    1.1 現(xiàn)有方案(kernel bypass)存在的問題

    內(nèi)核旁路方式可以顯著提升性能,但缺點也很明顯:

    • 很難與現(xiàn)有系統(tǒng)集成;

    • 上層應(yīng)用必須要將內(nèi)核中已經(jīng)非常成熟的模塊在用戶態(tài)重新實現(xiàn)一遍,例如路由表、高層協(xié)議棧等;

    • 最壞的情況下,這種包處理應(yīng)用只能工作在一個完全隔絕的環(huán)境,因為內(nèi)核提供的常見工具和部署方式在這種情況下都不可用了。

    • 導(dǎo)致系統(tǒng)越來越復(fù)雜,而且破壞了操作系統(tǒng)內(nèi)核在把控的安全邊界。?在基礎(chǔ)設(shè)施逐漸遷移到 Kubernetes/Docker 等容器環(huán)境的背景下,這一點顯得尤其嚴重, 因為在這種場景下,內(nèi)核擔(dān)負著資源抽象和隔離的重任。

    1.2 新方案:給內(nèi)核網(wǎng)絡(luò)棧添加可編程能力

    對此,本文提供了另一種解決方案:給內(nèi)核網(wǎng)絡(luò)棧添加可編程能力。這使得我們能在?兼容各種現(xiàn)有系統(tǒng)、復(fù)用已有網(wǎng)絡(luò)基礎(chǔ)設(shè)施的前提下,仍然實現(xiàn)高速包處理。?這個框架稱為XDP,

    • XDP定義了一個受限的執(zhí)行環(huán)境(a limited execution environment),運行在一個?eBPF 指令虛擬機中。eBPF 是 BSD Packet Filter (BPF) [37] 的擴展。

    • XDP 程序運行在內(nèi)核上下文中,此時內(nèi)核自身都還沒有接觸到包數(shù)據(jù)( before the kernel itself touches the packet data),這使得我們能在網(wǎng)卡收到包后?最早能處理包的位置,做一些自定義數(shù)據(jù)包處理(包括重定向)。

    • 內(nèi)核在加載(load)時執(zhí)行靜態(tài)校驗,以確保用戶提供的 XDP 程序的安全。

    • 之后,程序會被動態(tài)編譯成原生機器指令(native machine instructions),以獲得高性能。

    XDP已經(jīng)在過去的幾個內(nèi)核 release 中逐漸合并到內(nèi)核,但在本文之前,還沒有關(guān)于 XDP 系統(tǒng)的 完整架構(gòu)介紹。本文將對 XDP 做一個高層介紹。

    1.3 新方案(XDP)的優(yōu)點

    我們的測試結(jié)果顯示 XDP 能取得?24Mpps/core?的處理性能,這雖然與 DPDK 還有差距, 但相比于后者這種 kernel bypass 的方式,XDP 有非常多的優(yōu)勢。

    本文是 2018 年的測試結(jié)果,更新的一些性能(及場景)對比可參考?(譯) 為容器時代設(shè)計的高級 eBPF 內(nèi)核特性(FOSDEM, 2021)。?譯注。

    具體地,XDP

  • 與內(nèi)核網(wǎng)絡(luò)棧協(xié)同工作,將硬件的控制權(quán)完全留在內(nèi)核范圍內(nèi)。帶來的好處:

    • 保持了內(nèi)核的安全邊界

    • 無需對網(wǎng)絡(luò)配置或管理工具做任何修改

    無需任何特殊硬件特性,任何有 Linux 驅(qū)動的網(wǎng)卡都可以支持, 現(xiàn)有的驅(qū)動只需做一些修改,就能支持 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 層交互時,沒有從用戶態(tài)將包重新注入內(nèi)核的昂貴開銷。

    對應(yīng)用透明。這創(chuàng)造了一些新的部署場景/方式,例如直接在應(yīng)用所 在的服務(wù)器上部署 DoS 防御(而非中心式/網(wǎng)關(guān)式 DoS 防御)。

    服務(wù)不中斷的前提下動態(tài)重新編程(dynamically re-program), 這意味著可以按需加入或移除功能,而不會引起任何流量中斷,也能動態(tài)響應(yīng)系統(tǒng)其他部分的的變化。

    無需預(yù)留專門的 CPU 做包處理,這意味著 CPU 功耗與流量高低直接相關(guān),更節(jié)能。

    1.4 本文組織結(jié)構(gòu)

    接下來的內(nèi)容介紹 XDP 的設(shè)計,并做一些性能分析。結(jié)構(gòu)組織如下:

    • Section 2?介紹相關(guān)工作;

    • Section 3?介紹 XDP 系統(tǒng)的設(shè)計;

    • Section 4?做一些性能分析;

    • Section 5?提供了幾個真實 XDP 場景的程序例子;

    • Section 6?討論 XDP 的未來發(fā)展方向;

    • Section 7?總結(jié)。

    2 相關(guān)工作

    XDP 當(dāng)然不是第一個支持可編程包處理的系統(tǒng) —— 這一領(lǐng)域在過去幾年發(fā)展勢頭良好, 并且趨勢還在持續(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]。

    • 更加通用、且高度可定制的包處理解決方案,能夠處理從多種源收來的數(shù)據(jù)包 [12, 20, 31, 34, 40, 44]。

    要基于通用(Common Off The Shelf,COTS)硬件實現(xiàn)高性能包處理,就必須解決?網(wǎng)卡(NIC)和包處理程序之間的所有瓶頸。由于性能瓶頸主要來源于內(nèi)核和用戶態(tài)應(yīng)用之 間的接口(系統(tǒng)調(diào)用開銷非常大,另外,內(nèi)核功能豐富,但也非常復(fù)雜), 低層(low-level)框架必須通過這樣或那樣的方式來降低這些開銷。

    現(xiàn)有的一些框架通過幾種不同的方式實現(xiàn)了高性能,XDP 構(gòu)建在其中一些技術(shù)之上。?接下來對 XDP 和它們的異同做一些比較分析。

    2.1 用戶態(tài)輪詢 vs. XDP

    • DPDK?[16] 可能是使用最廣泛的高性能包處理框架。它最初只支持 Intel 網(wǎng)卡,后來逐 步擴展到其他廠商的網(wǎng)卡。DPDK 也稱作內(nèi)核旁路框架(kernel bypass framework), 因為它將網(wǎng)絡(luò)硬件的控制權(quán)從內(nèi)核轉(zhuǎn)移到了用戶態(tài)的網(wǎng)絡(luò)應(yīng)用,完全避免了內(nèi)核-用戶態(tài) 之間的切換開銷。

    • 與DPDK類似的還有PF_RING ZC module [45] 和 hardware-specific Solarflare OpenOnload [24]。

    在現(xiàn)有的所有框架中,內(nèi)核旁路方式性能是最高的?[18];但如引言中指 出,這種方式在管理、維護和安全方面都存在不足。

    XDP 采用了一種與內(nèi)核旁路截然相反的方式:相比于將網(wǎng)絡(luò)硬件的控制權(quán)上移到用戶空間, XDP 將性能攸關(guān)的包處理操作直接放在內(nèi)核中,在操作系統(tǒng)的網(wǎng)絡(luò)棧之前執(zhí)行。

    • 這種方式同樣避免了內(nèi)核-用戶態(tài)切換開銷(所有操作都在內(nèi)核),

    • 但仍然由內(nèi)核來管理硬件,因此保留了操作系統(tǒng)提供的管理接口和安全防護能力。

      這里的主要創(chuàng)新是:使用了一個虛擬執(zhí)行環(huán)境,它能對加載的 程序進行校驗,確保它們不會對內(nèi)核造成破壞。

    2.2 內(nèi)核模塊 vs. XDP

    在 XDP 之前,以內(nèi)核模塊(kernel module)方式實現(xiàn)包處理功能代價非常高, 因為程序執(zhí)行出錯時可能會導(dǎo)致整個系統(tǒng)崩潰,而且內(nèi)核的內(nèi)部 API 也會隨著時間發(fā)生變化。?因此也就不難理解為什么只有很少的系統(tǒng)采用了這種方式。其中做的比較好包括

    • 虛擬交換機?Open vSwitch [44]

    • Click [40]

    • 虛擬機路由器?Contrail [41]

    這幾個系統(tǒng)都支持靈活的配置,適用于多種場景,取得比較小的平攤代價。

    XDP通過:

  • 提供一個安全的執(zhí)行環(huán)境,以及內(nèi)核社區(qū)支持,提供與那些暴露到用戶空間一樣穩(wěn)定的內(nèi)核 API

    極大地降低了那些將處理過程下沉到內(nèi)核的應(yīng)用(applications of moving processing into the kernel)的成本。

  • 此外,XDP 程序也能夠完全繞過內(nèi)核網(wǎng)絡(luò)棧(completely bypass), 與在內(nèi)核網(wǎng)絡(luò)棧中做 hook 的傳統(tǒng)內(nèi)核模塊相比,性能也更高。

  • XDP 除了能將處理過程下沉到內(nèi)核以獲得最高性能之外,還支持在程序中執(zhí)行重定向?(redirection)操作,完全繞過內(nèi)核網(wǎng)絡(luò)棧,將包送到特殊類型的用戶空間 socket;?甚至能工作在 zero-copy 模式,進一步降低開銷。

  • 這種模式與 Netmap [46] 和 PF_RING?[11] 方式類似,但后者是在沒有完全繞過內(nèi) 核的情況下,通過降低從網(wǎng)絡(luò)設(shè)備到用戶態(tài)應(yīng)用(network device to userspace application)之間的傳輸開銷,實現(xiàn)高性能包處理。

  • 內(nèi)核模塊方式的另一個例子是 Packet I/O engine,這是 PacketShader [19] 的組成部分, 后者專用于 Arrakis [43] and ClickOS [36] 之類的特殊目的操作系統(tǒng)。

    2.3 可編程硬件 vs. XDP

    可編程硬件設(shè)備也是一種實現(xiàn)高性能包處理的方式。

    • 一個例子是?NetFPGA?[32],通過對它暴露的 API 進行編程,能夠在這種基于 FPGA 的專 用設(shè)備上運行任何包處理任務(wù)。

    • P4 編程語言 [7] 致力于將這種可編程能力擴展到更廣泛的包處理硬件上?(巧合的是,它還包括了一個 XDP backend [51])。

    某種意義上來說,XDP 可以認為是一種?offload?方式:

  • 性能敏感的處理邏輯下放到網(wǎng)卡驅(qū)動中,以提升性能;

  • 其他的處理邏輯仍然走內(nèi)核網(wǎng)絡(luò)棧;

  • 如果沒有用到內(nèi)核 helper 函數(shù),那整個 XDP 程序都可以 offload 到網(wǎng)卡(目前 Netronome smart-NICs [27] 已經(jīng)支持)。

  • 2.4 小結(jié)

    XDP 提供了一種高性能包處理方式,與已有方式相比,在性能、與現(xiàn)有系統(tǒng)的集成、靈活性 等方面取得了更好的平衡。接下來介紹 XDP 是如何取得這種平衡的。

    3 XDP 設(shè)計

    XDP 的設(shè)計理念:

    • 高性能包處理

    • 集成到操作系統(tǒng)內(nèi)核(kernel)并與之協(xié)同工作,同時

    • 確保系統(tǒng)其它部分的安全性(safety)完整性(integrity)

    這種與內(nèi)核的深度集成顯然會給設(shè)計帶來一些限制,在 XDP 組件合并到 Linux 的過程中,我們也收到了許多來自社區(qū)的反饋,促使我們不斷調(diào)整 XDP 的設(shè)計,但 這些設(shè)計反思不在本文討論范圍之內(nèi)。

    3.0 XDP 系統(tǒng)架構(gòu)

    圖 1 描繪了整個 XDP 系統(tǒng),四個主要組成部分:

  • XDP driver hook:XDP 程序的主入口,在網(wǎng)卡收到包執(zhí)行。

  • eBPF virtual machine:執(zhí)行 XDP 程序的字節(jié)碼,以及對字節(jié)碼執(zhí)行 JIT 以提升性能。

  • BPF maps:內(nèi)核中的 key/value 存儲,作為圖中各系統(tǒng)的主要通信通道。

  • eBPF verifier:加載程序時對其執(zhí)行靜態(tài)驗證,以確保它們不會導(dǎo)致內(nèi)核崩潰。

  • Fig 1. XDP 與 Linux 網(wǎng)絡(luò)棧的集成。這里只畫了 ingress 路徑,以免圖過于復(fù)雜。

    上圖是 ingress 流程。網(wǎng)卡收到包之后,在處理包數(shù)據(jù)(packet data)之前,會先執(zhí)行 main XDP hook 中的 eBPF 程序。?這段程序可以選擇:

  • 丟棄(drop)這個包;或者

  • 通過當(dāng)前網(wǎng)卡將包再發(fā)送(send)出去;或者

  • 將包重定向(redirect)到其他網(wǎng)絡(luò)接口(包括虛擬機的虛擬網(wǎng)卡),或者通過 AF_XDP socket 重定向到用戶空間;或者

  • 放行(allow)這個包,如果后面沒有其他原因?qū)е碌?drop,這個包就會進入常規(guī)的內(nèi)核網(wǎng)絡(luò)棧。

    如果是這種情況,也就是放行包進入內(nèi)核網(wǎng)絡(luò)棧,那接下來在將包放到發(fā)送隊列之前(before packets are queued for transmission), 還有一個能執(zhí)行 BPF 程序的地方:TC BPF hook。

    更多信息,可參考?(譯) [論文] 邁向完全可編程 tc 分類器(cls_bpf)(NetdevConf,2016)。?譯注。

  • 此外,圖 1 中還可以看出,不同的 eBPF 程序之間、eBPF 程序和用戶空間應(yīng)用之間,都能夠通過 BPF maps 進行通信。

    3.1 XDP driver hook

    在設(shè)備驅(qū)動中執(zhí)行,無需上下文切換

    XDP 程序在網(wǎng)絡(luò)設(shè)備驅(qū)動中執(zhí)行,網(wǎng)絡(luò)設(shè)備每收到一個包,程序就執(zhí)行一次。

    相關(guān)代碼實現(xiàn)為一個內(nèi)核庫函數(shù)(library function),因此程序直接 在設(shè)備驅(qū)動中執(zhí)行,無需切換到用戶空間上下文。

    在軟件最早能處理包的位置執(zhí)行,性能最優(yōu)

    回到上面圖 1 可以看到:程序在網(wǎng)卡收到包之后最早能處理包的位置?執(zhí)行 —— 此時內(nèi)核還沒有為包分配?struct sk_buff?結(jié)構(gòu)體, 也沒有執(zhí)行任何解析包的操作。

    XDP 程序典型執(zhí)行流

    下圖是一個典型的 XDP 程序執(zhí)行流:

    Fig 2. 典型 XDP 程序的執(zhí)行流。

    網(wǎng)卡收到一個包時,XDP 程序依次執(zhí)行:

  • 提取包頭中的信息(例如 IP、MAC、Port、Proto 等),

    執(zhí)行到程序時,系統(tǒng)會傳遞給它一個上下文對象(context object)作為參賽?(即?struct xdp_md *ctx,后面有例子),其中包括了指向原 始包數(shù)據(jù)的指針,以及描述這個包是從哪個網(wǎng)卡的哪個接口接收上來的等元數(shù)據(jù)字段。

  • 讀取或更新一些資源的元信息(例如更新統(tǒng)計信息);

    解析包數(shù)據(jù)之后,XDP 程序可以讀取?ctx?中的包元數(shù)據(jù)(packet metadata) 字段,例如從哪個網(wǎng)卡的哪個接口收上來的(ifindex)。除此之外,ctx 對象還允許 程序訪問與包數(shù)據(jù)毗鄰的一塊特殊內(nèi)存區(qū)域(cb, control buffer), 在包穿越整個系統(tǒng)的過程中,可以將自定義的數(shù)據(jù)塞在這里。

    除了 per-packet metadata,XDP 程序還可以通過 BPF map 定義和訪問自己的持久數(shù)據(jù)?,以及通過各種 helper 函數(shù)訪問內(nèi)核基礎(chǔ)設(shè)施。

    • BPF map 使 BPF 程序能與系統(tǒng)的其他部分之間通信;

    • Helpers 使 BPF 程序能利用到某些已有的內(nèi)核功能(例如路由表), 而無需穿越整個內(nèi)核網(wǎng)絡(luò)棧。

    如果有需要,對這個包進行?rewrite header?操作,

    程序能修改包數(shù)據(jù)的任何部分,包括添加或刪除包頭。這使得 XDP 程序能執(zhí)行封裝/接封裝操作,以及重寫(rewrite)地址字段然后轉(zhuǎn)發(fā)等操作。

    內(nèi)核 helper 函數(shù)各有不同用途,例如修改一個包之后,計算新的校驗和(checksum)。

    進行最后的判決(verdict),確定接下來對這個包執(zhí)行什么操作;

    判決結(jié)果包括:

    重定向功能的用途:

    這些不同的路徑,在圖 1 對應(yīng)的是幾條實線。

    將重定向判決(verdict)與重定向目標(target)分開,使得重定向目標類型很容易擴展;?另外,由于重定向參數(shù)(目標)是通過 BPF map 查詢的,因此無需修 改 XDP 程序,就能動態(tài)修改重定向目標。

    • 三種簡單返回碼:丟棄這個包、通過接收時的網(wǎng)卡將包重新發(fā)送出去、允許這個包進入內(nèi)核網(wǎng)絡(luò)棧;

    • 第四種返回碼 redirect:允許?XDP?程序指定網(wǎng)卡、CPU、用戶態(tài) socket?等,將包重定向過去。

  • 將原始包通過另一個網(wǎng)卡(包括虛擬機的虛擬網(wǎng)卡)發(fā)送出去;

  • 轉(zhuǎn)發(fā)給指定?CPU?做進一步處理;

  • 轉(zhuǎn)發(fā)給?AF_XDP 類型的 socket?做進一步處理;

  • 程序還能通過尾調(diào)用(tail call),將控制權(quán)交給另一個 XDP 程序;?通過這種方式,可以將一個大程序拆分成幾個邏輯上的小程序(例如,根據(jù) IPv4/IPv6)。

    由于 XDP 程序可包含任意指令,因此前三步(讀取包數(shù)據(jù)、處理元數(shù)據(jù)、重寫包數(shù)據(jù))?順序可以是任意的,而且支持多層嵌套。?但實際中為了獲得高性能,大部分情況下還是將執(zhí)行結(jié)構(gòu)組織成這順序的三步。

    3.2 eBPF 虛擬機

    XDP 程序在 Extended BPF (eBPF) 虛擬機中執(zhí)行。eBPF 是早期 BSD packet filter (BPF) [37] 的擴展,后者在過去的幾十年中廣泛 應(yīng)用于各種包處理工具。

    BPF 使用?基于寄存器的(register-based) virtual machine 來描述?過濾動作(filtering actions)。

    eBPF 虛擬機支持動態(tài)加載(loading)和重加載(re-loading)程序,內(nèi)核管理所有 BPF 程序的生命周期。

    3.3 BPF maps

    eBPF 程序在觸發(fā)內(nèi)核事件時執(zhí)行(例如,觸發(fā) XDP 程序執(zhí)行的,是收包事件)。?程序每次執(zhí)行時,初始狀態(tài)都是相同的(即程序是無狀態(tài)的),它們無法直接訪問?內(nèi)核中的持久存儲(BPF map)。為此,內(nèi)核提供了訪問 BPF map 的?helper 函數(shù)。

    BPF map 是 key/value 存儲,在加載 eBPF 程序時定義(defined upon loading an eBPF program)。

    用途:

  • 持久存儲。例如一個 eBPF 程序每次執(zhí)行時,都會從里面獲取上一次的狀態(tài)。

  • 用于協(xié)調(diào)兩個或多個 eBPF 程序。例如一個往里面寫數(shù)據(jù),一個從里面讀數(shù)據(jù)。

  • 用于用戶態(tài)程序和內(nèi)核 eBPF 程序之間的通信。

  • 3.4 eBPF verifier

    唯一加載入口:bpf()?系統(tǒng)調(diào)用

    由于 eBPF 代碼直接運行在內(nèi)核地址空間,因此它能直接訪問 —— 也可 能是破壞 —— 任何內(nèi)存。為防止這種情況發(fā)生,內(nèi)核規(guī)定只能通過唯一入口(?bpf()?系統(tǒng)調(diào)用)加載 BPF 程序。

    加載 BPF 程序時,位于內(nèi)核中的校驗器首先會對字節(jié)碼程序進行靜態(tài)分析,以確保

    • 程序中沒有任何不安全的操作(例如訪問任意內(nèi)存),

    • 程序會終止(terminate)。通過下面這兩點來實現(xiàn):

      • 禁止循環(huán)操作

      • 限制程序最大指令數(shù)

    校驗器工作原理:two-pass DAG

    校驗器的工作原理:首先根據(jù)程序的控制流構(gòu)建一個有向無環(huán)圖(DAG), 然后對 DAG 執(zhí)行如下校驗:

    • 首先,對 DAG 進行一次深度優(yōu)先搜索(depth-first search),以 確保它是無環(huán)的(acyclic),例如,沒有循環(huán),也不包含不支持或無法執(zhí)行到的指令。

    • 然后,再掃描一遍,這次會遍歷 DAG 的所有可能路徑。這次掃描的目的是:

      程序執(zhí)行?load?或?call?指令時,如果參數(shù)不合法,就會在這里被拒絕。參數(shù)合法 性是通過在程序執(zhí)行期間跟蹤所有寄存器和棧變量的狀態(tài)(states of registers and stack variables)來實現(xiàn)的。

      • 確保程序的內(nèi)存訪問都是安全的,

      • 調(diào)用?helper 函數(shù)時傳的參數(shù)類型是對的。

    內(nèi)存越界和空指針檢查:職責(zé)上移到程序自身/開發(fā)者

    這種跟蹤寄存器狀態(tài)的機制是為了在無法預(yù)知內(nèi)存邊界的情況下,仍然確保程序 的內(nèi)存訪問不會越界。無法預(yù)知內(nèi)存邊界是因為:

    • 包的大小是不固定的;

    • map 的內(nèi)容也無法提前預(yù)知,因此也無法判斷一次 map 查找操作是否會成功。

    為解決這個問題,校驗器會檢查已加載的程序自身是否會做如下檢查:

  • 解引用指針前做了內(nèi)存邊界檢查,

  • 查詢 map 之前是檢查了 map 指針是否為空。

  • 這種方式將處理邏輯中的安全檢查和遇到錯誤時如何處理的控制權(quán)都?交給了 BPF 程序的編寫者。

    跟蹤數(shù)據(jù)訪問操作和值范圍

    為跟蹤數(shù)據(jù)訪問,校驗器會跟蹤

  • 數(shù)據(jù)類型

  • 指針偏置(pointer offsets)

  • 所有寄存器的可能值范圍

  • 程序開始時:

    • R1?寄存器中存儲的是指向 context metadata 的指針(struct xdp_md *ctx),

    • R10?是棧指針(stack pointer),

    • 其他所有寄存器都是未初始化狀態(tài)。

    接下來程序每執(zhí)行一步,寄存器狀態(tài)就會更新一次。當(dāng)寄存器中存入一個新值時,這個寄存器 還會繼承與這個值相關(guān)的狀態(tài)變量(inherits the state variables from the source of the value)。

    算術(shù)操作會影響標量類型的值的范圍(value ranges of scalar types),以及指針類型的 offset。?可能的最大范圍(max possible range)存儲在狀態(tài)變量中,例如往寄存器中 load?一個字節(jié)時, 這個寄存器的可能值范圍就設(shè)置為 0~255。指令圖(instruction graph)中的?各邏輯分支就會根據(jù)操作結(jié)果更新寄存器狀態(tài)。例如,比較操作 R1 > 10,

    • 校驗器在一個分支?if R1 > 10?中會將 R1 最小值設(shè)為 11,

    • 在另一個?else?分支中將其最大值設(shè)為 10。

    不同類型數(shù)據(jù)的校驗信息來源(source of truth)

    利用狀態(tài)變量中存儲的范圍信息,校驗器就能預(yù)測每個 load 指令能訪問的所有 內(nèi)存范圍,確保它執(zhí)行的都是合法內(nèi)存訪問。

  • 對于包數(shù)據(jù)(packet data)的訪問,會與 context 對象中的?data_end?變量做比較;

  • 對于?BPF?map 中獲取的值,或用到 map 定義中聲明的 data size 信息;

  • 對于棧上存儲的值,會檢查狀態(tài)變量中記錄的值范圍;

  • 對于指針算術(shù)操作(pointer arithmetic)還會施加額外的限制,指針通常不能被轉(zhuǎn)換成整形值。

  • 只要校驗器無法證明某個操作是安全,該?BPF 程序在加載時(load time)就會被拒絕。?除此之外,校驗器還會利用范圍信息確保內(nèi)存的對齊訪問(enforce aligned memory access)。

    校驗器的目的

    需要說明的是,校驗器的目的是避免將內(nèi)核內(nèi)部(the internals of the kernel )暴露給惡意或有缺陷的 eBPF 程序,而非確保程序中函數(shù)的實現(xiàn)已經(jīng)是最高效的。

    換句話說,如果 XDP 程序中處理邏輯過多,也可能會導(dǎo)致機器變慢 ;如果代碼寫的有問題,也可能會破壞包數(shù)據(jù)。出于這些原因,加載 BPF 程序需要 管理員權(quán)限(root)。避免這些 bug 的責(zé)任在程序員,但選擇將哪些程序加載 到系統(tǒng)的權(quán)限在管理員。

    3.5 XDP 程序示例

    下面是一個簡單的 XDP 程序,展示了前面介紹的一些特性。?程序會解析包數(shù)據(jù),判斷如果是 UDP 包,直接交換源和目的 MAC 地址,然后將包從相同網(wǎng)卡再發(fā)送回去,

    雖然這是一個非常簡單的例子,但真實世界中的 XDP 程序用到的組件和特性,這里基本都具備了。

    // 從內(nèi)核 BPF 代碼示例 xdp2_kern.c 修改而來。1 // 用于統(tǒng)計包數(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 }

    具體地:

    • 定義了一個 BPF map 存儲統(tǒng)計信息。用戶態(tài)程序可以 poll 這個 map 來獲取統(tǒng)計信息。

    • context 對象?struct xdp_md *ctx?中有包數(shù)據(jù)的 start/end 指針,可用于直接訪問包數(shù)據(jù)。

    • 將數(shù)據(jù)指針和?data_end?比較,確保內(nèi)存訪問不會越界。

    • 程序必須自己解析包,包括 VLAN headers 等東西。

    • 直接通過指針(direct packet data access)修改包頭。

    • 內(nèi)核提供的 map lookup helper。這是程序中唯一的真實函數(shù)調(diào)用;其他函數(shù)都是內(nèi)聯(lián),包括?htons()。

    • 最終針對這個包的判決通過程序返回值傳遞給調(diào)用方。

    將這段程序安裝到網(wǎng)卡接口上時,它首先會被編譯成 eBPF 字節(jié)碼,然后經(jīng)受校驗器檢查。?這里的檢查項包括:

  • 無循環(huán)操作;程序大小(指令數(shù)量);

  • 訪問包數(shù)據(jù)之前,做了內(nèi)存邊界檢查;

  • 傳遞給 map lookup 函數(shù)的參數(shù),類型與 map 定義相匹配;

  • map lookup 的返回值(value 的內(nèi)存地址)在使用之前,檢查了是否為 NULL。

  • 3.6 小結(jié)

    XDP 系統(tǒng)由四個主要部分組成:

  • XDP device driver hook:網(wǎng)卡收到包之后直接運行;

  • eBPF 虛擬機:執(zhí)行 XDP 程序(以及內(nèi)核其他模塊加載的 BPF 程序);

  • BPF maps:使不同 BPF 程序之間、BPF 程序與用戶空間應(yīng)用之間能夠通信;

  • eBPF verifier:確保程序不包含任何可能會破壞內(nèi)核的操作。

  • 這四部分加在一起,創(chuàng)造了一個編寫自定義包處理應(yīng)用的強大環(huán)境,它能加速包處理的關(guān)鍵 路徑,同時還與內(nèi)核及現(xiàn)有基礎(chǔ)設(shè)施密切集成。

    接下來看一下 XDP 應(yīng)用的性能。

    4 性能評估

    DPDK 是目前性能最高的包處理框架 [18],因此本文將 XDP 與 DPDK 及 Linux 內(nèi)核網(wǎng)絡(luò) 棧的性能做一個對比。測試機器環(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ò)硬件通過 DMA 能直接將包放到 CPU 緩存。

    • 關(guān)閉超線性(Hyperthreading)。

    • 網(wǎng)卡:兩塊?Mellanox ConnectX-5 Ex VPI dual-port 100Gbps,mlx5 驅(qū)動。

    • 內(nèi)核:Linux 4.18

    • 使用基于 DPDK 的 TRex packet generator [9] 生成測試流量。所有測試腳本位于 [22]。

    在測試中,我們主要關(guān)心三個 metric:

    • 直接棄包(packet drop)性能。

      為展示最高的包處理性能,我們將用最簡單的操作 ——?丟棄接收到的包?—— 來測試。?這個測試能有效測量系統(tǒng)的整體開銷,也是真正的包處理應(yīng)用能達到的性能上限。

    • CPU 使用量。

      如引言中指出,XDP 的優(yōu)點之一是 CPU 使用量與流量大小是正相關(guān)的,而無需預(yù)留專 門的 CPU 給它用。我們通過測量 CPU 利用率隨網(wǎng)絡(luò)負載的變化來量化這個指標。

    • 包轉(zhuǎn)發(fā)性能。

      轉(zhuǎn)發(fā)的復(fù)雜性要更高一些,例如,涉及到與多塊網(wǎng)卡的交互、重寫二層頭等等。?這里會將轉(zhuǎn)發(fā)延遲也考慮進去。

    我們已經(jīng)驗證,使用 MTU(1500 字節(jié))包時,我們的系統(tǒng)單核就能達到線速(100 Gbps), 而且 CPU 有 50% 是空閑的。顯然,真正的挑戰(zhàn)在于 PPS,而非帶寬,其他一些測試也已經(jīng)指出了這一點 [46]。?出于這個原因,我們用最小包(64 字節(jié))測試,衡量指標是 PPS。

    對于 XDP 和 Linux 內(nèi)核網(wǎng)絡(luò)棧的測試,由于它們沒有顯式指定某些 CPU 來處理網(wǎng)絡(luò)包的方式,因此我們通過配置硬件 RSS(Receive Side Scaling)來講流量定向到指定 CPU。

    對網(wǎng)卡、內(nèi)核的一些配置調(diào)優(yōu),見代碼倉庫 [22]。

    4.1 直接棄包(packet drop)性能

    Fig 3. 直接棄包(packet drop)性能。DPDK 需要預(yù)留一個 CPU 運行控制任務(wù),因此只剩下 5 個 CPU 做包處理。

    上圖是性能與 CPU 數(shù)量的關(guān)系。

    • XDP 基準性能是?24Mpps/core,DPDK 是?43.5Mpps/core。

    • 二者在分別達到各自的峰值之前,PPS 都是隨 CPU 數(shù)量線性增長的。

    • 最終全局性能受限于 PCI 總線,啟用 PCI descriptor compression(在 CPU cycles 和 PCI 總線帶寬之間取舍)之后,能達到 115Mpps。

    再看圖中 Linux 網(wǎng)絡(luò)棧在兩種配置下的性能:

  • 通過?iptables 的 raw table?丟棄流量,這是?Linux 網(wǎng)絡(luò)棧中最早能丟棄包的地方;

  • 通過?conntrack(連接跟蹤)模塊,這個模塊的開銷非常大,但在很多 Linux 發(fā)行版中都是默認開啟的。

  • conntrack 模式達到了 1.8Mpps/core,raw 模式是 4.8Mpps/core ;這兩種模式均未達到硬件瓶頸。?最終的性能,XDP 比常規(guī)網(wǎng)絡(luò)棧的最快方式快了 5 倍。

    Linux raw mode test 中,我們還測量了 XDP 程序不丟棄包,而是更新包數(shù)統(tǒng)計然后將包 送到內(nèi)核網(wǎng)絡(luò)棧的場景。?這種情況下,XDP 單核的處理性能會下降到 4.5Mpps/core,有 13.3ns 處理延遲。?圖中并未給出這個測試結(jié)果,因為這個開銷太小了。

    4.2 CPU Usage

    Fig 4. 直接棄包(packet drop)場景下的 CPU 利用率。

    用系統(tǒng)提供的?mpstat?命令測量 CPU 利用率。結(jié)果如圖 4 。

    • DPDK 是 busy poll 模式,因此 CPU 永遠是 100%。

    • XDP 和 Linux 內(nèi)核網(wǎng)絡(luò)棧都是隨流量平滑增長:前面一小段是非線性的,后面基本是線性的。

      前面非線性主要是硬中斷帶來的固定開銷,在流量很小時這一部分占比較大。

    4.3 包轉(zhuǎn)發(fā)性能

    這個測試中,轉(zhuǎn)發(fā)應(yīng)用執(zhí)行非常簡單的 MAC 地址重寫:直接交換源和目的 MAC 地址,然后轉(zhuǎn)發(fā)。?這是轉(zhuǎn)發(fā)場景下最精簡的步驟了,因此結(jié)果代表了所有真實轉(zhuǎn)發(fā)應(yīng)用的性能上限。

    • 圖中包括了同網(wǎng)卡轉(zhuǎn)發(fā)和不同網(wǎng)卡轉(zhuǎn)發(fā)(XDP?程序返回碼不同)的結(jié)果。

    • DPDK 示例程序只支持通過另一個網(wǎng)卡轉(zhuǎn)發(fā),因此這里只列出了這種情況下的性能。

    • Linux 網(wǎng)絡(luò)棧不支持這種極簡轉(zhuǎn)發(fā)模式(minimal forwarding mode),需要設(shè)置完整的 橋接或路由查找(bridging or routing lookup)才能轉(zhuǎn)發(fā)包;路由查找是非常耗時的 ,由于其他幾種應(yīng)用并沒有這一步,因此結(jié)果直接對比是沒意義的。因此這里略去了 Linux 網(wǎng)絡(luò)棧的結(jié)果。

    轉(zhuǎn)發(fā)吞吐(pps)

    Fig 5. 轉(zhuǎn)發(fā)性能。在同一網(wǎng)卡接口上收發(fā)會占用同一 PCI port 的帶寬, 這意味著在 70Mpps XDP same-nic 組就已經(jīng)達到了 PCI 總線的瓶頸

    如圖 5 所示,性能隨 CPU 數(shù)量線性擴展,直到達到全局性能瓶頸。XDP 在同網(wǎng)卡轉(zhuǎn)發(fā)的性能遠高于 DPDK 異網(wǎng)卡性能,原因是內(nèi)存處理方式不同:

    • packet buffer 是設(shè)備驅(qū)動分配的,與接收接口(receiving interface)相關(guān)聯(lián)。

    • 因此,異網(wǎng)卡場景下,當(dāng)包轉(zhuǎn)發(fā)到另一個接口時,memory buffer 需要還給與之關(guān)聯(lián)的接口。

    轉(zhuǎn)發(fā)延遲

    表 1. 轉(zhuǎn)發(fā)延遲。機器網(wǎng)卡的兩個接口直連,在轉(zhuǎn)發(fā)速率分別為 100pps 和 1Mpps 的條件下,持續(xù) 50s 測量端到端延遲

    高 pps 場景下,XDP 的延遲已經(jīng)接近 DPDK。但在低 pps 場景下,XDP 延遲比 DPDK 大的多,原因是?XDP 是基于中斷的,中斷處理時間( interrupt processing time)此時占大頭;而 DPDK 是輪詢模式,延遲相對比較固定。

    4.4 討論:XDP 性能與 DPDK 還有差距的原因

    XDP 未做底層代碼優(yōu)化

    上一節(jié)已經(jīng)看到,XDP 相比于常規(guī) Linux 網(wǎng)絡(luò)棧性能有了顯著提升。但對于大部分 XDP 場景來說,性能還是與 DPDK 有差距。我們認為,這是主要是因為 DPDK 做了相當(dāng)多的底層 代碼優(yōu)化。舉個例子來解釋,考慮 packet drop 例子:

    • XDP 24Mpps/core,對應(yīng)?41.6ns/packet

    • DPDK 43.5Mpps,對應(yīng)?22.9ns/packet

    多出來的 18.7ns 在我們的 3.6GHz 機器上對應(yīng) 67 個時鐘周期。因此,很顯然?每個很小的優(yōu)化在這里都會產(chǎn)生很大的影響。例如,我們測量出在測試 機器上,每次函數(shù)調(diào)用需要 1.3ns。mlx5 驅(qū)動處理每個包都有 10 次 函數(shù)調(diào)用,總計就是 13ns。

    通用目的操作系統(tǒng),首要目標:更好的擴展和配置,而非極致性能

    另外,在 Linux 這樣的通用目的操作系統(tǒng)中,某些開銷是不可避免的, 因為設(shè)備驅(qū)動或子系統(tǒng)的組織方式是為了實現(xiàn)更好的擴展和配置,而非極致性能。

    但是,我們認為有些優(yōu)化還是有必要的。例如,我們嘗試將內(nèi)核中與測試網(wǎng)卡無關(guān)的 DMA 函數(shù)調(diào)用刪掉, 這樣將前面提到的 10 個函數(shù)調(diào)用降低到了 6 個,測試結(jié)果顯示這將單核性能提升到了 29Mpps/core。?依此推測的話,將另外 6 個函數(shù)調(diào)用也優(yōu)化掉,能將 XDP 的性能提升到?37.6Mpps。?實際上我們不可能將 6 個全部去掉,但去掉其中幾個,再加上一些其他優(yōu)化,我 們相信 XDP 和 DPDK 的性能差距將越來越小。

    其他驅(qū)動的測試結(jié)果也是類似的,例如 i40e driver for 40 Gbps Intel cards。

    基于以上討論,我們相信未來 XDP 與 DPDK 的性能差距將越來越小。

    另一方面,考慮到 XDP 在靈活性和與內(nèi)核集成方面的優(yōu)勢, XDP 已經(jīng)是很多實際場景中的非常有競爭力的方式。下文給出幾個例子。

    5 真實場景使用案例

    本節(jié)給出三個例子來具體展示 XDP 在真實世界中的應(yīng)用。?這幾個案例都是已經(jīng)真實在用的,但本文出于解釋目的,將使用簡化的版本。?同時也建議讀者參考 [38],后者是獨立的文章,介紹使用 XDP 解決實際工作中網(wǎng)絡(luò)服務(wù)所面臨的一些挑戰(zhàn)。

    本節(jié)目的是展示真實 XDP 方案的可行性,因此不會將重點放在與業(yè)界最新的實現(xiàn)做詳盡性能對比上。?我們會拿常規(guī)的 Linux 內(nèi)核網(wǎng)絡(luò)棧的性能作為 baseline,來對比 XDP 應(yīng)用的性能。

    5.1 案例一:軟件路由(software routing)

    內(nèi)核數(shù)據(jù)平面 & 控制平面(BIRD/FRR)

    Linux 內(nèi)核實現(xiàn)了一個功能完整的路由表,作為數(shù)據(jù)平面,支持

    • policy routing

    • source-specific routing

    • multipath load balancing, and more.

    對于控制平面,Bird [10] 或 FRR [17] 這樣的路由守護進程( routing daemons)實現(xiàn)了多種路由控制平面協(xié)議。Linux 提供的這套生態(tài)系統(tǒng)功能如此豐富 ,因此再在另一個包處理框架中重新實現(xiàn)一套類似的路由棧代價將非常高, 更實際的方式是對 Linux 內(nèi)核的數(shù)據(jù)平面進行優(yōu)化。

    XDP:直接查詢內(nèi)核路由表并轉(zhuǎn)發(fā)

    XDP 非常適合做這件事情,尤其是它提供了一個 helper 函數(shù),能從 XDP 程序中直接查詢內(nèi)核路由表。

    • 如果查詢成功,會返回?egress interface 和下一跳 MAC 地址, XDP 程序利用這些信息足夠?qū)⒓崔D(zhuǎn)發(fā)出去。

    • 如果下一跳 MAC 還是未知的(因為之前還沒進行過 neighbour lookup),XDP 程序就 能將包傳給內(nèi)核網(wǎng)絡(luò)棧,后者會解析 neighbor 地址,這樣隨后的包 就能直接被 XDP 程序轉(zhuǎn)發(fā)了。

    測試:XDP routing + 全球 BGP 路由表

    為展示 XDP 路由的性能,我們用 Linux 內(nèi)核代碼中的 XDP routing 例子 [1],與常規(guī) Linux 內(nèi)核網(wǎng)絡(luò)棧的性能做對比。?兩組測試:

  • 路由表中只有一條路由;

  • 路由表中有從 routeviews.org 中 dump 而來的全球 BGP 路由表(global BGP routing table)。?包含 752,138 條路由。隨機生成 4000 個目的 IP 地址,以確保能充分利用到這種路由表。

    如果目的 IP 地址少于 4000 個,實際用到的路由表部分會較小,能夠保存在 CPU 緩存中,使得結(jié)果不準確。?增大 IP 數(shù)量至 4000 個以上,不會對轉(zhuǎn)發(fā)性能造成影響,但可以避免緩存導(dǎo)致的結(jié)果不準問題。

  • 對于兩組測試,下一跳 MAC 地址都是與我們的發(fā)送網(wǎng)卡直接相關(guān)的接口的地址。

    性能:2.5x

    Fig 6. 軟件路由的性能。由于性能隨核數(shù)線性增加,這里只給出單核的結(jié)果。

    測試結(jié)果如上圖所示。

    • full table lookup 性能提升了 2.5 倍;

    • smaller routing table 組,提升了 3 倍。

    這說明,XDP 路由程序 + 單核 + 10Gbps 網(wǎng)卡?的軟硬件配置,就能?處理整張全球 BGP 路由表(保守估計每個包平均 300 字節(jié))。

    5.2 案例二:Inline DoS Mitigation

    DoS 攻擊還是像瘟疫一樣糾纏著互聯(lián)網(wǎng),現(xiàn)在通常的方式是:通過已經(jīng)入侵的大量設(shè)備發(fā)起分布式(DDoS)攻擊。

    有了XDP 之后,我們能直接在應(yīng)用服務(wù)器(application servers)上?部署包過濾程序來防御此類攻擊(inline DoS mitigation),?無需修改應(yīng)用代碼。如果應(yīng)用是部署在虛擬機里,那 XDP 程序還可以 部署在宿主機(hypervisor)上,這樣單個程序就能保護機器上所有的虛擬機。

    模擬 Cloudflare 防御架構(gòu)

    為展示工作原理,我們用 XDP 作為過濾機制,模擬 Cloudflare 的 DDoS 防御架構(gòu)?[6]。?他們的 Gatebot architecture ,首先在各 PoP 點機器上采樣,然后統(tǒng)一收起來做分析, 根據(jù)分析結(jié)果生成防御規(guī)則。

    防御規(guī)則的形式是對包數(shù)據(jù)(payload)進行一系列簡單檢查, 能直接編譯成 eBPF 代碼然后分發(fā)到 PoP 點的所有服務(wù)器上。這里說的代碼是 XDP 程序 ,它會將匹配到規(guī)則的所有流量丟棄,同時將統(tǒng)計信息更新到 BPF map。

    程序邏輯

    為驗證這種方案的性能,我們編寫一個 XDP 程序,它

  • 解析包頭,執(zhí)行一些簡單驗證。對每個包:執(zhí)行四次讀取操作,以解析外層包頭。

  • 將符合攻擊特性的流量丟棄。具體:丟棄 UDP + 特定端口的流量。

  • 將其他流量通過 CPU redirect 方式重定向給另一個 CPU?做進一步處理;

  • 性能

    我們用 netperf 做性能壓測 [26]。

    • 用 netperf TCP round-trip benchmark,單個 TCP 連接來回小的 request/reply,統(tǒng)計 transactions/second。

      模擬的是交互式應(yīng)用,例如小的遠程過程調(diào)用(RPC)。

    • 實驗在單核上進行,模擬多個流量(正常流量+攻擊流量)競爭同一物理資源的場景。

    • 在?beseline 35K 業(yè)務(wù) TPS(transactions per second)基礎(chǔ)上,打少量 UDP 流量作為攻擊流量。逐漸加大攻擊流量,觀察 TPS 的變化。

    Fig 7. DDoS 性能。業(yè)務(wù)吞吐(TPS)隨攻擊流量的變化。

    結(jié)果如上圖所示,

    • 沒有 XDP 的一組,性能急劇下降:攻擊流量在 3Mpps 時性能減半,3.5Mpps 時基本跌零;

    • 有 XDP 程序的一組,攻擊流量達到 19.5Mpps 之前,業(yè)務(wù)吞吐保持在 28.5K TPS 以上,過了這個臨界點性能才開始急劇下降。

    以上結(jié)果表明,XDP 防御 DDoS 攻擊在實際中是完全可行的,單核就能輕松處理 10Gbps 的、都是最小包(minimum-packet)的 DoS 流量。?這種 DDoS 防御的部署更加靈活,無需硬件或應(yīng)用做任何改動。

    5.3 案例三:負載均衡(load balancing)

    Facebook Katran

    負載均衡的場景,我們用 Facebook 開源的 Katran 作為例子 [15]。Katran 的工作原理是對外通告服務(wù)的 IP,這樣目標是這個 IP 的流量就會被路由到 XDP 實現(xiàn)的負載均衡器。

    • 負載均衡器對包頭(source packet header)進行哈希,以此選擇目標應(yīng)用服務(wù)器。

    • 然后將對包進行封裝(encap),發(fā)送給應(yīng)用服務(wù)器;

    • 應(yīng)用服務(wù)器解封裝(decap),處理請求,然后直接將回包發(fā)給客戶端(DSR 模式)。

    在這個過程中,XDP 程序負責(zé)哈希、封裝以及將包從接收網(wǎng)卡再發(fā)出去的任務(wù)。?配置信息存儲在 BPF map 中,整個封裝邏輯是完全在 eBPF 中實現(xiàn)的。

    性能

    為測試性能,我們給 Katran XDP 程序配置幾個固定的目標機器。?對照組是 IPVS,它是 Linux 內(nèi)核的一部分。性能如表 2 所示,隨 CPU 數(shù)量線性增長, XDP 比 IPVS 性能高 4.3 倍。

    表 2. 負載均衡器性能(Mpps)

    配置:1 VIP/core, 100 DstIPs/VIP.

    6 XDP 的未來方向

    XDP 已經(jīng)能用于解決真實問題,但作為 Linux 內(nèi)核的一部分,XDP 還在快速開發(fā)過程中。

    6.1 eBPF 程序的限制

    前面提到,加載到 eBPF 虛擬機的程序必須保證其安全性(不會破壞內(nèi)核),因此對 eBPF 程序作了一下限制,歸結(jié)為兩方面:

  • 確保程序會終止:在實現(xiàn)上是通過禁止循環(huán)和限制程序的最大指令數(shù)(max size of the program);

  • 確保內(nèi)存訪問的安全:通過 3.4 小結(jié)介紹的寄存器狀態(tài)跟蹤(register state tracking)來實現(xiàn)。

  • 校驗邏輯偏保守

    由于校驗器的首要職責(zé)是保證內(nèi)核的安全,因此其校驗邏輯比較保守, 凡是它不能證明為安全的,一律都拒絕。有時這會導(dǎo)致假陰性(false negatives), 即某些實際上是安全的程序被拒絕加載;這方面在持續(xù)改進。

    • 校驗器的錯誤提示也已經(jīng)更加友好,以幫助開發(fā)者更快定位問題。

    • 近期已經(jīng)支持了 BPF 函數(shù)調(diào)用(function calls)。

    • 正在計劃支持有限循環(huán)(bounded loops)。

    • 正在提升校驗器效率,以便處理更大的 BPF 程序。

    缺少標準庫

    相比于用戶空間 C 程序,eBPF 程序的另一個限制是缺少標準庫,包括?內(nèi)存分配、線程、鎖等等庫。

  • 內(nèi)核的生命周期和執(zhí)行上下文管理(life cycle and execution context management )部分地彌補了這一不足,(例如,加載的 XDP 程序會為每個收到的包執(zhí)行),

  • 內(nèi)核提供的 helper 函數(shù)也部分地彌補了一不足。

  • 一個網(wǎng)卡接口只能 attach 一個 XDP 程序

    這個限制其實也是可以繞過的:將 XDP 程序組織成程序數(shù)組,通過尾 調(diào)用,根據(jù)包上下文在程序之間跳轉(zhuǎn),或者是將幾個程序做 chaining。

    6.2 用戶體驗和調(diào)試

    XDP 程序運行在內(nèi)核,因此常規(guī)的用戶空間 debug 工具是用不了的,但內(nèi)核自帶的 debug 和 introspection 功能是可以用在 XDP (及其他 eBPF 程序)上的。?包括:

    • tracepoints and kprobes [13]

    • performance counters that are part of the perf subsystem [42]

    但不熟悉內(nèi)核生態(tài)系統(tǒng)的開發(fā)者可能會對這些工具感到非常陌生,難以使用。因此,也出 現(xiàn)了一些更方便普通開發(fā)者的工具,包括 BCC [50]、bpftool [8]、libbpf 函數(shù)庫 [30] 等等。

    6.3 驅(qū)動支持

    設(shè)備要支持 XDP,需要實現(xiàn)內(nèi)核核心網(wǎng)絡(luò)棧暴露出的一個 API。?寫作本文時 Linux 4.18 已經(jīng)有 12 種驅(qū)動支持 XDP,包括了大部分高速網(wǎng)卡。?最新列表見 [2]。

    隨著 XDP 系統(tǒng)的不斷成熟,核心代碼逐漸上移到內(nèi)核中,驅(qū)動需要維護的代碼越 來越少。例如,redirection action 支持新的 target 時,無需驅(qū)動做任何改動。

    最后,對于那些不支持 XDP 的驅(qū)動,內(nèi)核提供了?Generic XDP?feature [39],這是軟件實現(xiàn)的 XDP,性能會低一些, 在實現(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 性能提升

    XDPDPDK 之間還有一些性能差距,一些改進工作正在進行中:

    • 驅(qū)動代碼 micro-optimisations

    • 刪除核心 XDP 代碼中的非必要操作

    • 通過批處理平攤處理開銷

    6.5 QoS 和 Rate Transitions

    當(dāng)前,XDP 還沒有任何 QoS 機制。?尤其是,如果對端已經(jīng)過載(例如兩端的網(wǎng)絡(luò)速度或特性不匹配),XDP 程序是收不到任何背壓(back-pressure)的,

    雖然 XDP 中缺少 QoS,但 Linux 內(nèi)核網(wǎng)絡(luò)棧中卻有很多業(yè)界最佳的 Active Queue Management (AQM) 特性和 packet scheduling algorithms [23]。?這些特性中,部分并不適用于 XDP,但我們相信能夠 以一種對包處理應(yīng)用完全透明的方式,選擇其中部分集成到 XDP。?我們計劃對這一方向進行更深入研究。

    6.6 加速傳輸層協(xié)議

    我們已經(jīng)證明 XDP 能在保留操作系統(tǒng)原有功能的前提下,集成到操作系統(tǒng)中,實現(xiàn)高速包數(shù)據(jù)。

    目前的 XDP 還是用于無狀態(tài)包處理(stateless packet processing) ,如果將這個模型擴展到有狀態(tài)傳輸層協(xié)議(stateful transport protocols),例如 TCP,它能給依賴可靠/有狀態(tài)傳輸?shù)膽?yīng)用提供類似的性能提升。

    實際上,已經(jīng)有一些研究證明,相比于操作系統(tǒng)的協(xié)議棧,accelerated transport protocols 能顯著提升性能[5, 25, 35, 52]。其中的一個解決方案 [52] 表明,在保留內(nèi) 核 TCP 協(xié)議棧的的前提下,原始包處理性能(raw packet processing)存在巨大的提升 空間。

    XDP 非常適用于這種場景,目前也已經(jīng)有一些關(guān)于如何實現(xiàn)的初步討論 [21], 雖然離實際使用還很遠,但仍然是一個令人振奮的、擴展 XDP 系統(tǒng) scope 的方向。

    6.7 內(nèi)核-用戶空間零拷貝(zero-copy to userspace)

    3.1 小節(jié)提到,XDP 程序能將數(shù)據(jù)包重定向到用戶空間應(yīng)用(userspace application)打 開的特殊類型 socket。這可以用于加速客戶端和服務(wù)端在同一臺機器?的網(wǎng)絡(luò)密集型應(yīng)用(network-heavy applications running on the local machine)。

    更多信息可參考:?(譯) 利用 ebpf sockmap/redirection 提升 socket 性能(2020)。?這里使用的是 BPF 而非 XDP,但核心原理是一樣的,只是程序執(zhí)行的位置(hook)不同。?譯注。

    但在目前的實現(xiàn)中,這種方式在底層仍然需要拷貝包數(shù)據(jù),因此性能會打折扣。

    目前已經(jīng)有工作在進行,通過 AF_XDP 實現(xiàn)真正的數(shù)據(jù)零拷貝。但這項工作需要?對網(wǎng)絡(luò)設(shè)備的內(nèi)存處理過程有一些限制,因此需要設(shè)備驅(qū)動的顯式支持。?第一個支持這個功能的 patch 已經(jīng)合并到?4.19?內(nèi)核,更多驅(qū)動的支持 正在添加中。初步的性能測試結(jié)果還是很樂觀的,顯示能達到 20Mpps/core 的內(nèi)核到用戶 空間傳遞(transfer)速度。

    6.8 XDP 作為基礎(chǔ)構(gòu)建模塊(XDP as a building block)

    正如 DPDK 用于高層包處理框架的底層構(gòu)建模塊(例如 [31]),XDP 有望成為高層應(yīng)用的運行時環(huán)境 (runtime environment for higher-level applications)。

    實際上,我們看到一些基于 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ū)動 [53]。

    7 總結(jié)

    本文描述了XDP,一個安全、快速、可編程、集成到操作系統(tǒng)內(nèi)核的包處理框架。?測試結(jié)果顯示,XDP能提供 24Mpps/core 的高處理性能,這一數(shù)字雖然與基于 kernel bypass 的DPDK仍有差距,但提供了其他一些非常有競爭力的優(yōu)勢:

  • 兼容內(nèi)核安全管理框架(kernel bypass 方式在bypass 內(nèi)核網(wǎng)絡(luò)棧的同時,也將安全和設(shè)備管理等這些極其重要的基礎(chǔ)設(shè)施 bypass 了);

  • 兼容內(nèi)核網(wǎng)絡(luò)棧,可選擇性利用內(nèi)核已有的基礎(chǔ)設(shè)施和功能;

  • 提供與內(nèi)核 API 一樣穩(wěn)定的編程接口;

  • 應(yīng)用完全透明

  • 更新、替換程序的過程不會引起服務(wù)中斷;

  • 無需專門硬件,無需獨占CPU等資源。

  • 相比于 kernel bypass 這種非此即彼、完全繞開內(nèi)核的方式,我們相信 XDP 有更廣闊的的應(yīng)用前景。Facebook、Cloudflare 等公司實際落地的 XDP 應(yīng)用,更加增強了我們的這種信心。

    最后,XDP系統(tǒng)還在快速發(fā)展,前面也列出了一些正在未來可能會做的開發(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.

    參考文獻

    https://arthurchiao.art/blog/xdp-paper-acm-2018-zh/

    - END -


    看完一鍵三連在看轉(zhuǎn)發(fā)點贊

    是對文章最大的贊賞,極客重生感謝你

    推薦閱讀

    深入理解Linux內(nèi)核之內(nèi)存尋址


    2022新年重磅技術(shù)分享|深入理解Linux操作系統(tǒng)


    一些優(yōu)秀的后端開源項目!


    你好,這里是極客重生,我是阿榮,大家都叫我榮哥,從華為->外企->到互聯(lián)網(wǎng)大廠,目前是大廠資深工程師,多次獲得五星員工,多年職場經(jīng)驗,技術(shù)扎實,專業(yè)后端開發(fā)和后臺架構(gòu)設(shè)計,熱愛底層技術(shù),豐富的實戰(zhàn)經(jīng)驗,分享技術(shù)的本質(zhì)原理,希望幫助更多人蛻變重生,拿BAT大廠offer,培養(yǎng)高級工程師能力,成為技術(shù)專家,實現(xiàn)高薪夢想,期待你的關(guān)注!點擊藍字查看我的成長之路

    校招/社招/簡歷/面試技巧/大廠技術(shù)棧分析/后端開發(fā)進階/優(yōu)秀開源項目/直播分享/技術(shù)視野/實戰(zhàn)高手等,?極客星球希望成為最有技術(shù)價值星球,盡最大努力為星球的同學(xué)提供技術(shù)和成長幫助!詳情查看->極客星球

    ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 求點贊,在看,分享三連

    總結(jié)

    以上是生活随笔為你收集整理的万字长文|深入理解XDP全景指南的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。