ebpf 開發(fā)入門之核心概念篇

我是 LEE涌矢,老李扁达,一個在 IT 行業(yè)摸爬滾打 16 年的技術老兵叔汁。

事件背景

在完成了第一章的編寫《ebpf 開發(fā)入門之 helloworld》后叹坦,繼續(xù)往下寫多少怎么寫都是我最近思考的問題。跟周邊的小伙伴一起溝通后扇雕,認為 epbf 還在繼續(xù)發(fā)展拓售,重點應該關注它的核心概念,而不是重點關注它底層的內部實現(xiàn)镶奉,畢竟我的環(huán)境是對 epbf 最大程度的使用础淤,而不是深入開發(fā)崭放。所以經過一段時間的思考,覺得為了使用 cilium 深入 ebpf 研究無可厚非鸽凶,但是過分深入則可能不太合適币砂。

有了上面的觀點,那么我就再寫一個文章輸出 epbf 的核心概念玻侥,后續(xù)深入的小伙伴可以根據(jù)自己實際需要找資料深入决摧。

世界觀

在講解具體概念之前,我們先科普下 epbf 的整體世界觀凑兰。

eBPF 世界觀

Hook

中文名

鉤子

大白話

在 epbf 的世界里看 Linux 內核所有核心調用都可以 Hook掌桩,可以理解成為萬物皆可掛鉤子做 Callback。

具體解釋

eBPF 程序都是事件驅動的姑食,它們會在內核或者應用程序經過某個確定的 Hook 點的時候運行波岛,這些 Hook 點都是提前定義的,包括系統(tǒng)調用音半、函數(shù)進入/退出则拷、內核 tracepoints、網絡事件等祟剔。

eBPF 鉤子

如果針對某個特定需求的 Hook 點不存在隔躲,可以通過 kprobe 或者 uprobe 來在內核或者用戶程序的幾乎所有地方掛載 eBPF 程序。

隨意的鉤子

Verifier

中文名

驗證器

大白話

生成應用內核層的 bytescode 要想進入到內核中去運行物延,必然要有個“安全檢查員”對這個 bytescode 的安全和合法性進行檢測宣旱。

具體解釋

每一個 eBPF 程序加載到內核都要經過 Verification,用來保證 eBPF 程序的安全性叛薯,主要包括:

  • 要保證 加載 eBPF 程序的進程有必要的特權級浑吟,除非節(jié)點開啟了 unpriviledged 特性,只有特權級的程序才能夠加載 eBPF 程序

    1. 內核提供了一個配置項 /proc/sys/kernel/unprivileged_bpf_disabled 來禁止非 特權用戶使用 bpf(2) 系統(tǒng)調用耗溜,可以通過 sysctl 命令修改

    2. 比較特殊的一點是组力,這個配置項特意設計為一次性開關(one-time kill switch), 這 意味著一旦將它設為 1抖拴,就沒有辦法再改為 0 了燎字,除非重啟內核

    3. 一旦設置為 1 之后,只有初始命名空間中有 CAP_SYS_ADMIN 特權的進程才可以調用 bpf(2) 系統(tǒng)調用 阿宅。Cilium 啟動后也會將這個配置項設為 1

       # echo 1 > /proc/sys/kernel/unprivileged_bpf_disabled
      
  • 要保證 eBPF 程序不會崩潰或者使得系統(tǒng)出故障

  • 要保證 eBPF 程序不能陷入死循環(huán)候衍,能夠 runs to completion

  • 要保證 eBPF 程序必須滿足系統(tǒng)要求的大小,過大的 eBPF 程序不允許被加載進內核

  • 要保證 eBPF 程序的復雜度有限洒放,Verifier 將會評估 eBPF 程序所有可能的執(zhí)行路徑蛉鹿,必須能夠在有限時間內完成 eBPF 程序復雜度分析

JIT Compiler

中文名

JIT 編譯器

大白話

跟 java 的 JVM 有點類似,就是把 bytescode 編譯成本機能夠運行的二進制代碼往湿。

具體解釋

Just-In-Time(JIT) 編譯用來將通用的 eBPF 字節(jié)碼翻譯成與機器相關的指令集妖异,從而極大加速 BPF 程序的執(zhí)行:

  • 與解釋器相比惋戏,它們可以降低每個指令的開銷。通常他膳,指令可以 1:1 映射到底層架構的原生指令
  • 這也會減少生成的可執(zhí)行鏡像的大小响逢,因此對 CPU 的指令緩存更友好
  • 特別地,對于 CISC 指令集(例如 x86)矩乐,JIT 做了很多特殊優(yōu)化龄句,目的是為給定的指令產生可能的最短操作碼,以降低程序翻譯過程所需的空間

概念講解

有了世界觀的上的認識散罕,同時這篇文章是作為入門,核心概念不應該說的太細太深傀蓉,這樣容易勸退很多小伙伴欧漱。所以這里采用“一句話”說明白的方式解釋和介紹 epbf 的核心概念。

Helper Functions

中文名

輔助函數(shù)

大白話

應用在用戶層不能直接訪問內核層的數(shù)據(jù)葬燎,那么就需要一個代理人幫忙去執(zhí)行误甚,等待執(zhí)行完畢后獲得返回結果。

具體解釋

eBPF 程序不能夠隨意調用內核函數(shù)谱净,如果這么做的話會導致 eBPF 程序與特定的內核版本綁定窑邦,相反它內核定義的一系列 Helper functions。Helper functions 使得 BPF 能夠通過一組內核定義的穩(wěn)定的函數(shù)調用來從內核中查詢數(shù)據(jù)壕探,或者將數(shù)據(jù)推送到內核冈钦。所有的 BPF 輔助函數(shù)都是核心內核的一部分,無法通過內核模塊來擴展或添加李请。

輔助函數(shù)

不同類型的 BPF 程序能夠使用的輔助函數(shù)可能是不同的瞧筛,例如:

  • 與 attach 到 tc 層的 BPF 程序相比,attach 到 socket 的 BPF 程序只能夠調用前者可以調用的輔助函數(shù)的一個子集
  • lightweight tunneling 使用的封裝和解封裝輔助函數(shù)导盅,只能被更低的 tc 層使用较幌;而推送通知到用戶態(tài)所使用的事件輸出輔助函數(shù),既可以被 tc 程序使用也可以被 XDP 程序使用

Maps

中文名

映射存儲

大白話

ebpf Map 是駐留在內核空間中的高效 Key/Value store白翻,包含多種類型的 Map乍炉,由內核實現(xiàn)其功能。用來作為用戶層和內核層之間數(shù)據(jù)交換的媒介滤馍,同時可以在不同程序之間共享數(shù)據(jù)岛琼。

具體解釋

epbf Map

BPF Map 的交互場景有以下幾種:

  • BPF 程序和用戶態(tài)程序的交互:BPF 程序運行完,得到的結果存儲到 map 中纪蜒,供用戶態(tài)程序通過文件描述符訪問
  • BPF 程序和內核態(tài)程序的交互:和 BPF 程序以外的內核程序交互衷恭,也可以使用 map 作為中介
  • BPF 程序間交互:如果 BPF 程序內部需要用全局變量來交互,但是由于安全原因 BPF 程序不允許訪問全局變量纯续,可以使用 map 來充當全局變量
  • BPF Tail call:Tail call 是一個 BPF 程序跳轉到另一 BPF 程序随珠,BPF 程序首先通過 BPF_MAP_TYPE_PROG_ARRAY 類型的 map 來知道另一個 BPF 程序的指針灭袁,然后調用 tail_call() 的 helper function 來執(zhí)行 Tail call
  • 共享 map 的 BPF 程序不要求是相同的程序類型,例如 tracing 程序可以和網絡程序共享 map窗看,單個 BPF 程序目前最多可直接訪問 64 個不同 map茸歧。
ebpf Map 共享數(shù)據(jù)

內核中的 通用 map 有

  • BPF_MAP_TYPE_HASH
  • BPF_MAP_TYPE_ARRAY
  • BPF_MAP_TYPE_PERCPU_HASH
  • BPF_MAP_TYPE_PERCPU_ARRAY
  • BPF_MAP_TYPE_LRU_HASH
  • BPF_MAP_TYPE_LRU_PERCPU_HASH
  • BPF_MAP_TYPE_LPM_TRIE

內核中的 非通用 map 有

  • BPF_MAP_TYPE_PROG_ARRAY:一個數(shù)組 map,用于 hold 其他的 BPF 程序
  • BPF_MAP_TYPE_PERF_EVENT_ARRAY
  • BPF_MAP_TYPE_CGROUP_ARRAY:用于檢查 skb 中的 cgroup2 成員信息
  • BPF_MAP_TYPE_STACK_TRACE:用于存儲棧跟蹤的 MAP
  • BPF_MAP_TYPE_ARRAY_OF_MAPS:持有(hold) 其他 map 的指針显沈,這樣整個 map 就可以在運行時實現(xiàn)原子替換
  • BPF_MAP_TYPE_HASH_OF_MAPS:持有(hold) 其他 map 的指針软瞎,這樣整個 map 就可以在運行時實現(xiàn)原子替換

Object Pinning

中文名

釘住對象 (非常奇怪的翻譯,但是看源代碼拉讯,翻譯 pin 為固定和被釘在那里還是滿合適的)

大白話

ebpf map 和程序作為內核資源只能通過文件描述符訪問(fd)涤浇,這個映射實際就是 fd 到內存對象的屬性路徑的一個映射,這個映射過程叫 pin魔慷。

具體解釋

(★)ebpf map 和程序作為內核資源只能通過文件描述符訪問只锭,其背后是內核中的匿名 inode。 這個觀點很重要院尔,因為 pin 這個行為都是依據(jù)這個概念來的蜻展。

這樣做的優(yōu)點:

  • 用戶空間應用程序能夠使用大部分文件描述符相關的 API
  • 傳遞給 Unix socket 的文件描述符是透明工作等等

這樣做的缺點:

文件描述符受限于進程的生命周期,使得 map 共享之類的操作非常笨重邀摆,這給某些特定的場景帶來了很多復雜性纵顾。

解法

為了解決這個問題,內核實現(xiàn)了一個最小內核空間 BPF 文件系統(tǒng)栋盹,BPF map 和 BPF 程序 都可以 pin 到這個文件系統(tǒng)內施逾,這個過程稱為 object pinning。BPF 相關的文件系統(tǒng)不是單例模式(singleton)贞盯,它支持多掛載實例音念、硬鏈接、軟連接等等躏敢。

相應的闷愤,BPF 系統(tǒng)調用擴展了兩個新命令,如下圖所示:

  • BPF_OBJ_PIN:釘住一個對象
  • BPF_OBJ_GET:獲取一個被釘住的對象
ebpf Pinning

Tail Calls

中文名

尾調用

大白話

一個 BPF 程序可以調用另一個 BPF 程序件余,并且調用完成后不用返回到原來的程序讥脐。

具體解釋

尾調用的機制是指:一個 BPF 程序可以調用另一個 BPF 程序,并且調用完成后不用返回到原來的程序啼器。

  • 和普通函數(shù)調用相比旬渠,這種調用方式開銷最小,因為它是用長跳轉(long jump)實現(xiàn)的端壳,復用了原來的棧幀 (stack frame)
  • BPF 程序都是獨立驗證的告丢,因此要傳遞狀態(tài),要么使用 per-CPU map 作為 scratch 緩沖區(qū) 损谦,要么如果是 tc 程序的話岖免,還可以使用 skb 的某些字段(例如 cb[])
  • 相同類型的程序才可以尾調用岳颇,而且它們還要與 JIT 編譯器相匹配,因此要么是 JIT 編譯執(zhí)行颅湘,要么是解釋器執(zhí)行(invoke interpreted programs)话侧,但不能同時使用兩種方式
尾調用

Hardening

中文名

硬化 (明明說的就是安全,但是用 Hardening 這個單詞闯参,覺得有點奇怪)

大白話

硬化實際是對 epbf 運行狀態(tài)的值和數(shù)據(jù)進行保護瞻鹏,防止以外被篡改和破壞,是一種暗轉防護機制鹿寨。

具體解釋

在程序的生命周期內新博,BPF 將內核中的整個 BPF 解釋器映像(struct bpf_prog)以及 JIT 編譯映像(struct bpf_binary_header)鎖定為只讀,以防止代碼被破壞释移。例如叭披,由于某些內核 bug 而發(fā)生的任何損壞都會導致一般的保護故障,從而導致內核崩潰玩讳,而不是讓損壞靜靜地發(fā)生。

對于 x86_64 JIT 編譯器嚼贡,如果 CONFIG_RETPOLINE 已經設置(大多數(shù) Linux 發(fā)行版在編寫時都是默認設置)熏纯,則通過 retpoline 實現(xiàn)從使用尾部調用的間接跳轉的 JIT。

在/proc/sys/net/core/bpf_jit_harden 設置為 1 的情況下粤策,JIT 編譯的額外加固步驟將對非特權用戶生效樟澜。在不受信任的用戶對系統(tǒng)進行操作的情況下,通過減少(潛在的)攻擊面叮盘,可以有效地略微權衡它們的性能秩贰。與完全切換到解釋器相比,程序執(zhí)行時間的減少仍然會帶來更好的性能柔吼。

通過將實際指令隨機化毒费,這意味著通過將值的實際負載分成兩個步驟來重寫指令,將操作從基于即時的源操作數(shù)轉換為基于寄存器的操作數(shù):

  1. 加載一個盲化后的(blinded)立即數(shù) rnd ^ imm 到寄存器
  2. 將寄存器和 rnd 進行異或操作(xor)

Offloads

中文名

卸載

大白話

就是把 eBPF 的網絡程序內核層 bytescode 從 CPU 運行改為由網卡的 MPU 來執(zhí)行愈魏。

具體解釋

eBPF 網絡程序觅玻,尤其是 tc 和 XDP BPF 程序在內核中都有一個 offload 到硬件的接口,這樣就可以直接在網卡上執(zhí)行 BPF 程序培漏。

Offloads

參考文檔

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末溪厘,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子牌柄,更是在濱河造成了極大的恐慌畸悬,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,888評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件珊佣,死亡現(xiàn)場離奇詭異蹋宦,居然都是意外死亡披粟,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,677評論 3 399
  • 文/潘曉璐 我一進店門妆档,熙熙樓的掌柜王于貴愁眉苦臉地迎上來僻爽,“玉大人,你說我怎么就攤上這事贾惦⌒匕穑” “怎么了?”我有些...
    開封第一講書人閱讀 168,386評論 0 360
  • 文/不壞的土叔 我叫張陵须板,是天一觀的道長碰镜。 經常有香客問我,道長习瑰,這世上最難降的妖魔是什么绪颖? 我笑而不...
    開封第一講書人閱讀 59,726評論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮甜奄,結果婚禮上柠横,老公的妹妹穿的比我還像新娘。我一直安慰自己课兄,他們只是感情好牍氛,可當我...
    茶點故事閱讀 68,729評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著烟阐,像睡著了一般搬俊。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蜒茄,一...
    開封第一講書人閱讀 52,337評論 1 310
  • 那天唉擂,我揣著相機與錄音,去河邊找鬼檀葛。 笑死玩祟,一個胖子當著我的面吹牛,可吹牛的內容都是我干的驻谆。 我是一名探鬼主播卵凑,決...
    沈念sama閱讀 40,902評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼胜臊!你這毒婦竟也來了勺卢?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,807評論 0 276
  • 序言:老撾萬榮一對情侶失蹤象对,失蹤者是張志新(化名)和其女友劉穎黑忱,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 46,349評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡甫煞,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,439評論 3 340
  • 正文 我和宋清朗相戀三年菇曲,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片抚吠。...
    茶點故事閱讀 40,567評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡常潮,死狀恐怖,靈堂內的尸體忽然破棺而出楷力,到底是詐尸還是另有隱情喊式,我是刑警寧澤,帶...
    沈念sama閱讀 36,242評論 5 350
  • 正文 年R本政府宣布萧朝,位于F島的核電站岔留,受9級特大地震影響,放射性物質發(fā)生泄漏检柬。R本人自食惡果不足惜献联,卻給世界環(huán)境...
    茶點故事閱讀 41,933評論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望何址。 院中可真熱鬧里逆,春花似錦、人聲如沸用爪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,420評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽项钮。三九已至,卻和暖如春希停,著一層夾襖步出監(jiān)牢的瞬間烁巫,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,531評論 1 272
  • 我被黑心中介騙來泰國打工宠能, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留亚隙,地道東北人。 一個月前我還...
    沈念sama閱讀 48,995評論 3 377
  • 正文 我出身青樓违崇,卻偏偏與公主長得像阿弃,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子羞延,可洞房花燭夜當晚...
    茶點故事閱讀 45,585評論 2 359

推薦閱讀更多精彩內容