Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
eBPFを使ったオレオレカーネル拡張入門
Search
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
Keisuke Nishimura
July 22, 2023
Programming
1
1k
eBPFを使ったオレオレカーネル拡張入門
Kernel/VM探検隊@東京 No16
Keisuke Nishimura
July 22, 2023
Tweet
Share
More Decks by Keisuke Nishimura
See All by Keisuke Nishimura
Xenのスケジューラがぜんぜんわからん
mu_mu_mu
0
1.3k
C言語プログラムの構造とほんの少し解釈
mu_mu_mu
3
1.7k
Intel MPK入門
mu_mu_mu
0
1.1k
Other Decks in Programming
See All in Programming
Oxlint JS plugins
kazupon
1
1.1k
2026/02/04 AIキャラクター人格の実装論 口 調の模倣から、コンテキスト制御による 『思想』と『行動』の創発へ
sr2mg4
0
640
Raku Raku Notion 20260128
hareyakayuruyaka
0
420
AHC061解説
shun_pi
0
260
AIエージェントのキホンから学ぶ「エージェンティックコーディング」実践入門
masahiro_nishimi
7
1.2k
AIとペアプロして処理時間を97%削減した話 #pyconshizu
kashewnuts
1
170
Rで始めるML・LLM活用入門
wakamatsu_takumu
0
110
株式会社 Sun terras カンパニーデック
sunterras
0
1.9k
AIプロダクト時代のQAエンジニアに求められること
imtnd
1
500
AI活用のコスパを最大化する方法
ochtum
0
120
Rails Girls Tokyo 18th GMO Pepabo Sponsor Talk
yutokyokutyo
0
180
Go1.26 go fixをプロダクトに適用して困ったこと
kurakura0916
0
310
Featured
See All Featured
Visualizing Your Data: Incorporating Mongo into Loggly Infrastructure
mongodb
49
9.9k
Making the Leap to Tech Lead
cromwellryan
135
9.7k
HU Berlin: Industrial-Strength Natural Language Processing with spaCy and Prodigy
inesmontani
PRO
0
250
Groundhog Day: Seeking Process in Gaming for Health
codingconduct
0
110
Avoiding the “Bad Training, Faster” Trap in the Age of AI
tmiket
0
95
Leveraging LLMs for student feedback in introductory data science courses - posit::conf(2025)
minecr
1
180
State of Search Keynote: SEO is Dead Long Live SEO
ryanjones
0
140
Build your cross-platform service in a week with App Engine
jlugia
234
18k
Building Experiences: Design Systems, User Experience, and Full Site Editing
marktimemedia
0
420
職位にかかわらず全員がリーダーシップを発揮するチーム作り / Building a team where everyone can demonstrate leadership regardless of position
madoxten
59
50k
Building AI with AI
inesmontani
PRO
1
750
Large-scale JavaScript Application Architecture
addyosmani
515
110k
Transcript
オレオレ カーネル拡張 入門 Kernel/VM 探検隊 Tokyo #16 mumumu / 西村
啓佑 (Keisuke Nishimura) ← Twitter: @mumumu_vm eBPFを使った 2023/07/22
発表内容 - eBPF基礎 - eBPFを使ったカーネルの拡張 - オレオレ カーネル 拡張 2
eBPF 基礎:概要 - カーネル内で動作する VM (In-Kernel VM) - 実行前に検証 =
安全 - 基本的に 特定のイベントをフックして実行 - BPF_PROG_TYPE_XDP - BPF_PROG_TYPE_SK_SKB - BPF_PROG_TYPE_KPROBE - … 3 それぞれを Program Type と呼称
eBPF 基礎:ユースケース例 - モニタリング: カーネル内データを柔軟に参照可能 - 高速パケット処理:コンテキストスイッチ無しで - システムコールのフィルタ:安全かつ効率的な 4
eBPF 基礎:ユースケース例 - モニタリング: カーネル内データを柔軟に参照可能 - 高速パケット処理:コンテキストスイッチ無しで - システムコールのフィルタ:安全かつ効率的な 5
これだけじゃない!
いま、eBPFを使ったカーネルの拡張が熱い! MemcachedのキャッシュをeBPFでカーネルに移植 [NSDI 2021] 6
いま、eBPFを使ったカーネルの拡張が熱い! eBPFでスケジューラのカスタマイズ [SOSP 2021] 7
いま、eBPFを使ったカーネルの拡張が熱い! eBPFを用いたストレージ処理高速化 (e.g. 高速なファイルトラバースとか) [OSDI 2022] 8
いま、eBPFを使ったカーネルの拡張が熱い! eBFPでロック機構をApp. 毎にカスタマイズ [OSDI 2022] 9
10 オレオレ カーネル 拡張 作りたくない? eBPFを使って
eBPFでカーネルを拡張できるおもちゃ作ってみた Program Type: BPF_PROG_OREORE プログラム仕様: - eBPF: u32 を受け取って計算した答えを返却 -
イベント: /sys/oreore/bpf への書き込み - 結果の出力: /sys/oreore/bpf の 読み込み - エラー処理やまともなVerificationは省略 11 ※Linux Kernel: 6.1.38 (最新のLTS)を使用
動作目標 12 sudo ./oreore_loader # bpfプログラムのロード
eBPF の機能追加レシピ 1. 新しい eBPF Program Type を定義 2. アタッチする機能を用意
3. プログラムを実行する関数定義 4. ユーザランドから呼び出し 13
#1 新しい eBPF Program Type を定義 ゴール:BPF_PROG_TYPEマクロで宣言 @ /include/linux/bpf_types.h 14
BPF_PROG_TYPE(BPF_PROG_OREORE, bpf_oreore, __u32, u32); 今回の例:
#1 新しい eBPF Program Type を定義 ゴール:BPF_PROG_TYPEマクロで宣言 @ /include/linux/bpf_types.h 15
BPF_PROG_TYPEいろんな使われ方をする(8回 def/undef)
#1 新しい eBPF Program Type を定義 ゴール:BPF_PROG_TYPEマクロで宣言 @ /include/linux/bpf_types.h 16
BPF_PROG_TYPE(BPF_PROG_OREORE, bpf_oreore, __u32, u32); 今回の例:
#1 新しい eBPF Program Type を定義 ゴール:BPF_PROG_TYPEマクロで宣言 @ /include/linux/bpf_types.h 17
BPF_PROG_TYPE(BPF_PROG_OREORE, bpf_oreore, __u32, u32); 今回の例: 追加したいProgram Type @/include/uapi/linux/bpf.h enum bpf_prog_type
#1 新しい eBPF Program Type を定義 ゴール:BPF_PROG_TYPEマクロで宣言 @ /include/linux/bpf_types.h 18
BPF_PROG_TYPE(BPF_PROG_OREORE, bpf_oreore, __u32, u32); 今回の例: 予め作った次の2つの構造体: struct bpf_verifier_ops <このarg> ## _verifier_ops; struct bpf_prog_ops <このarg> ## _prog_ops; を参照する.(詳しくは後述)
#1 新しい eBPF Program Type を定義 ゴール:BPF_PROG_TYPEマクロで宣言 @ /include/linux/bpf_types.h 19
BPF_PROG_TYPE(BPF_PROG_OREORE, bpf_oreore, __u32, u32); 今回の例: prog_ctx_type kern_ctx_type = eBPFからみた = カーネルからみた eBPFプログラムの引数の型 kern_ctx_type prog_ctx_type 変換
#1 新しい eBPF Program Type を定義 ゴール:BPF_PROG_TYPEマクロで宣言 @ /include/linux/bpf_types.h 20
BPF_PROG_TYPE(BPF_PROG_OREORE, bpf_oreore, __u32, u32); 今回の例: prog_ctx_type kern_ctx_type = eBPFからみた = カーネルからみた eBPFプログラムの引数の型 kern_ctx_type prog_ctx_type 変換 今回は自動の変換で事足りるが, 複雑な場合 bpf_verifier_opsの convert_ctx_access() で変換
#1 新しい eBPF Program Type を定義 ゴール:BPF_PROG_TYPEマクロで宣言 21 bpf_verifier_ops 構造体の設定
(とりあえずこの2つを登録) さっきのマクロ第二引数
#1 新しい eBPF Program Type を定義 ゴール:BPF_PROG_TYPEマクロで宣言 22 bpf_verifier_ops 構造体の設定
(とりあえずこの2つを登録) 引数addrから4 bytesのみ アクセス可能
#1 新しい eBPF Program Type を定義 ゴール:BPF_PROG_TYPEマクロで宣言 23 bpf_prog_ops 構造体の設定
(test_run メンバを登録するとデバッグし易い)
#1 新しい eBPF Program Type を定義 ゴール:BPF_PROG_TYPEマクロで宣言 @ /include/linux/bpf_types.h 24
1. enum bpf_prog_type にProg Type 追加 2. bpf_{prog|verifier}_ops 構造体 を定義 3. 適切な引数型で BPF_PROG_TYPE マクロ #1 まとめ:
#2 アタッチする機能を用意 25 ゴール:新しいProgram Type に対応するフラグを設定 1.BPF_PROG_TYPEに対応するフラグ BPF_OREORE_ATTACH を追加 2.対応する
bpf システムコールに当該機能を追加 今回の例: ※ 本来 attach は fd に対して動作するが, 今回は簡単のため,fd 非依存にプログラムを適当なコンテキストに保存
#2 アタッチする機能を用意 26 ゴール:新しいProgram Type に対応するフラグを設定 1.BPF_PROG_TYPEに対応するフラグ BPF_OREORE_ATTACH を追加 2.対応する
bpf システムコールに当該機能を追加 今回の例: enum bpf_attach_type (@/include/uapi/linux/bpf.h) に BPF_OREORE_ATTACH を追加 ※ 本来 attach は fd に対して動作するが, 今回は簡単のため,fd 非依存にプログラムを適当なコンテキストに保存
#2 アタッチする機能を用意 27 ゴール:新しいProgram Type に対応するフラグを設定 1.BPF_PROG_TYPEに対応するフラグ BPF_OREORE_ATTACH を追加 2.対応する
bpf システムコールに当該機能を追加 今回の例: /kernel/bpf/syscall.c の以下の関数に追加 - bpf_prog_load_check_attach - attach_type_to_prog_type - bpf_prog_at(de)tach
#2 アタッチする機能を用意 28 ゴール:新しいProgram Type に対応するフラグを設定 bpf_prog_load_check_attach(...) ... + case
BPF_PROG_TYPE_OREORE: + if (expected_attach_type == BPF_OREORE_ATTACH) + return 0; + return -EINVAL; ... attach_type_to_prog_type(...) ... + case BPF_OREORE_ATTACH: + return BPF_PROG_TYPE_OREORE; ...
#2 アタッチする機能を用意 29 ゴール:新しいProgram Type に対応するフラグを設定 +struct bpf_prog __rcu *oreore_prog;
+EXPORT_SYMBOL_GPL(oreore_prog); +int oreore_bpf_prog_attach(struct bpf_prog *prog) +{ + rcu_assign_pointer(oreore_prog, prog); + return 0; +} ... bpf_prog_attach(...) ... + case BPF_PROG_TYPE_OREORE: + ret = oreore_bpf_prog_attach(prog); + break; ...
#2 アタッチする機能を用意 30 ゴール:新しいProgram Type に対応するフラグを設定 +struct bpf_prog __rcu *oreore_prog;
+EXPORT_SYMBOL_GPL(oreore_prog); +int oreore_bpf_prog_attach(struct bpf_prog *prog) +{ + rcu_assign_pointer(oreore_prog, prog); + return 0; +} ... bpf_prog_attach(...) ... + case BPF_PROG_TYPE_OREORE: + ret = oreore_bpf_prog_attach(prog); + break; ... この変数をLKM から参照し, progを実行する予定 (本来はfdからfilp を引いて 処理内容を追加)
#2 アタッチする機能を用意 31 ゴール:新しいProgram Type に対応するフラグを設定 +int oreore_bpf_prog_detach() +{ +
rcu_assign_pointer(oreore_prog, NULL); + return 0; +} ... bpf_prog_detach(...) ... + case BPF_PROG_TYPE_OREORE: + ret = oreore_bpf_prog_detach(); + break; ...
#2 アタッチする機能を用意 32 ゴール:新しいProgram Type に対応するフラグを設定 1. enum bpf_attach_type にattach
type を追加 2. bpf システムコールで使われる4つの関数に 1で追加したフラグの設定を追加 #2 まとめ: #1-2 の変更を反映済カーネルをコンパイル・実行
#3 プログラムを実行する関数定義 33 ゴール: bpf_prog_run()の呼び出し (#1-2で作ったカーネル上で実行) LKMを書いて /sys/oreore/bpf への write
から bpf_prog_run() を呼び出し 今回の例: u32 bpf_prog_run(const struct bpf_prog *prog, const void *ctx); • bpf_prog を実行し,その返り値を返却 • その際ctx(#1で定義したkern_ctx_type)が引数
#3 プログラムを実行する関数定義 34 ゴール:bpf_prog_run()の呼び出し sysfs の show/store の ops に登録
(storeの方) attach されているか等 確認・エラー処理必要
#3 プログラムを実行する関数定義 35 ゴール:bpf_prog_run()の呼び出し sysfs の show/store の ops に登録
(show の方)
#3 プログラムを実行する関数定義 36 1. kern_ctx_typeの引数を作成 2. bpf_prog_run()をコール #1-2 の変更を行ったカーネルで実行 #3
まとめ: ゴール:bpf_prog_run()の呼び出し
#4 ユーザランドから登録 ゴール: bpfプログラムのロード & アタッチ 37 1. 入力(u32)に42を足すbpf 命令を錬成(気合い)
2. libbpfの bpf_prog_load() でロード 3. libbpfのbpf_prog_attach() でアタッチ 今回の例:
#4 ユーザランドから登録 ゴール: bpfプログラムのロード & アタッチ 38 入力(u32)に42を足すbpf 命令を錬成(気合い) static
struct bpf_insn insns[] = { // R0 = *(r1+0) BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, 0), // R0 += 42 BPF_ALU32_IMM(BPF_ADD, BPF_REG_0, 42), // Exit BPF_EXIT_INSN(), };
#4 ユーザランドから登録 ゴール: bpfプログラムのロード & アタッチ 39 入力(u32)に42を足すbpf 命令を錬成(気合い) static
struct bpf_insn insns[] = { // R0 = *(r1+0) BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, 0), // R0 += 42 BPF_ALU32_IMM(BPF_ADD, BPF_REG_0, 42), // Exit BPF_EXIT_INSN(), }; 本来はtools/bpf/以下を編集し llvm でバイトコード生成 + libbpfのインフラを使用
#4 ユーザランドから登録 ゴール: bpfプログラムのロード & アタッチ 40 libbpf の bpf_prog_load()
でロード
#4 ユーザランドから登録 ゴール: bpfプログラムのロード & アタッチ 41 bpf_prog_load() でロード オプションの指定が必要
#4 ユーザランドから登録 ゴール: bpfプログラムのロード & アタッチ 42 bpf_prog_attach() でアタッチ 本来は,ターゲットの
fd を指定
#4 ユーザランドから登録 ゴール: bpfプログラムのロード & アタッチ 43 1. 対象 bpf
プログラムを錬成 2. bpf_prog_{run|attach}()をコール #1-2 の変更を行ったカーネル上で実行 #4 まとめ:
動作例 44 sudo insmod oreore_mod.ko # #3 /sys/oreore sudo ./oreore_loader
# #4 load/attach
ここから先は... - エラーハンドリング(今回はほぼ無視) - libbpfからのサポートやBTF機構関連の追加 - ヘルパー関数の定義 - アタッチ動作の改善 etc...
45
まとめ - eBPF = In-Kernel VM - eBPF を使ったカーネルの拡張が熱い! -
意外と簡単にeBPFでカーネルを拡張可能! 46
47
Verifier の設定:メモリアクセス 自分の理解が正しければ... check_mem_access()がメモリ系の検証を担当 - is_valid_access() は ctx からのオフセットにへの制約 -
check_mem_region_access() - その他... 48
bpf_verifier_ops
bpf_prog_ops