diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index e0dcf31..618ca4d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -14,7 +14,9 @@ jobs: contents: write pull-requests: write steps: - - uses: actions/checkout@v3.3.0 + - uses: actions/checkout@v3 + with: + submodules: 'recursive' - uses: actions-rs/toolchain@v1 with: toolchain: stable diff --git a/.github/workflows/test-libbpf.yml b/.github/workflows/test-libbpf.yml index acc884f..0c738df 100644 --- a/.github/workflows/test-libbpf.yml +++ b/.github/workflows/test-libbpf.yml @@ -68,4 +68,16 @@ jobs: - name: test 35-user-ringbuf run: | make -C src/35-user-ringbuf - sudo timeout -s 2 3 src/35-user-ringbuf/user_ringbuf || if [ $? = 124 ]; then exit 0; else exit $?; fi \ No newline at end of file + sudo timeout -s 2 3 src/35-user-ringbuf/user_ringbuf || if [ $? = 124 ]; then exit 0; else exit $?; fi + + - name: test 41 xdp + run: | + make -C src/41-xdp-tcpdump + + - name: test 42 xdp + run: | + make -C src/42-xdp-loadbalancer + + - name: test 43 kfuncs + run: | + make -C src/43-kfuncs diff --git a/.gitmodules b/.gitmodules index 63e8883..a15a392 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,9 +1,6 @@ [submodule "src/third_party/blazesym"] path = src/third_party/blazesym url = https://github.com/libbpf/blazesym -[submodule "src/third_party/libbpf"] - path = src/third_party/libbpf - url = https://github.com/libbpf/libbpf.git [submodule "src/third_party/bpftool"] path = src/third_party/bpftool url = https://github.com/libbpf/bpftool diff --git a/src/.mdbookignore b/src/.mdbookignore new file mode 100644 index 0000000..912eacc --- /dev/null +++ b/src/.mdbookignore @@ -0,0 +1 @@ +third_party diff --git a/src/43-kfuncs/.gitignore b/src/43-kfuncs/.gitignore new file mode 100644 index 0000000..c71a9ec --- /dev/null +++ b/src/43-kfuncs/.gitignore @@ -0,0 +1,10 @@ +.vscode +package.json +*.o +*.skel.json +*.skel.yaml +package.yaml +ecli +ecc +.output +kfunc diff --git a/src/43-kfuncs/Makefile b/src/43-kfuncs/Makefile new file mode 100644 index 0000000..71649c3 --- /dev/null +++ b/src/43-kfuncs/Makefile @@ -0,0 +1,141 @@ +# SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) +OUTPUT := .output +CLANG ?= clang +LIBBPF_SRC := $(abspath ../third_party/libbpf/src) +BPFTOOL_SRC := $(abspath ../third_party/bpftool/src) +LIBBPF_OBJ := $(abspath $(OUTPUT)/libbpf.a) +BPFTOOL_OUTPUT ?= $(abspath $(OUTPUT)/bpftool) +BPFTOOL ?= $(BPFTOOL_OUTPUT)/bootstrap/bpftool +LIBBLAZESYM_SRC := $(abspath ../third_party/blazesym/) +LIBBLAZESYM_OBJ := $(abspath $(OUTPUT)/libblazesym.a) +LIBBLAZESYM_HEADER := $(abspath $(OUTPUT)/blazesym.h) +ARCH ?= $(shell uname -m | sed 's/x86_64/x86/' \ + | sed 's/arm.*/arm/' \ + | sed 's/aarch64/arm64/' \ + | sed 's/ppc64le/powerpc/' \ + | sed 's/mips.*/mips/' \ + | sed 's/riscv64/riscv/' \ + | sed 's/loongarch64/loongarch/') +VMLINUX := ../third_party/vmlinux/$(ARCH)/vmlinux.h +# Use our own libbpf API headers and Linux UAPI headers distributed with +# libbpf to avoid dependency on system-wide headers, which could be missing or +# outdated +INCLUDES := -I$(OUTPUT) -I../third_party/libbpf/include/uapi -I$(dir $(VMLINUX)) +CFLAGS := -g -Wall +ALL_LDFLAGS := $(LDFLAGS) $(EXTRA_LDFLAGS) + +APPS = kfunc # minimal minimal_legacy uprobe kprobe fentry usdt sockfilter tc ksyscall + +CARGO ?= $(shell which cargo) +ifeq ($(strip $(CARGO)),) +BZS_APPS := +else +BZS_APPS := # profile +APPS += $(BZS_APPS) +# Required by libblazesym +ALL_LDFLAGS += -lrt -ldl -lpthread -lm +endif + +# Get Clang's default includes on this system. We'll explicitly add these dirs +# to the includes list when compiling with `-target bpf` because otherwise some +# architecture-specific dirs will be "missing" on some architectures/distros - +# headers such as asm/types.h, asm/byteorder.h, asm/socket.h, asm/sockios.h, +# sys/cdefs.h etc. might be missing. +# +# Use '-idirafter': Don't interfere with include mechanics except where the +# build would have failed anyways. +CLANG_BPF_SYS_INCLUDES ?= $(shell $(CLANG) -v -E - </dev/null 2>&1 \ + | sed -n '/<...> search starts here:/,/End of search list./{ s| \(/.*\)|-idirafter \1|p }') + +ifeq ($(V),1) + Q = + msg = +else + Q = @ + msg = @printf ' %-8s %s%s\n' \ + "$(1)" \ + "$(patsubst $(abspath $(OUTPUT))/%,%,$(2))" \ + "$(if $(3), $(3))"; + MAKEFLAGS += --no-print-directory +endif + +define allow-override + $(if $(or $(findstring environment,$(origin $(1))),\ + $(findstring command line,$(origin $(1)))),,\ + $(eval $(1) = $(2))) +endef + +$(call allow-override,CC,$(CROSS_COMPILE)cc) +$(call allow-override,LD,$(CROSS_COMPILE)ld) + +.PHONY: all +all: $(APPS) + +.PHONY: clean +clean: + $(call msg,CLEAN) + $(Q)rm -rf $(OUTPUT) $(APPS) + +$(OUTPUT) $(OUTPUT)/libbpf $(BPFTOOL_OUTPUT): + $(call msg,MKDIR,$@) + $(Q)mkdir -p $@ + +# Build libbpf +$(LIBBPF_OBJ): $(wildcard $(LIBBPF_SRC)/*.[ch] $(LIBBPF_SRC)/Makefile) | $(OUTPUT)/libbpf + $(call msg,LIB,$@) + $(Q)$(MAKE) -C $(LIBBPF_SRC) BUILD_STATIC_ONLY=1 \ + OBJDIR=$(dir $@)/libbpf DESTDIR=$(dir $@) \ + INCLUDEDIR= LIBDIR= UAPIDIR= \ + install + +# Build bpftool +$(BPFTOOL): | $(BPFTOOL_OUTPUT) + $(call msg,BPFTOOL,$@) + $(Q)$(MAKE) ARCH= CROSS_COMPILE= OUTPUT=$(BPFTOOL_OUTPUT)/ -C $(BPFTOOL_SRC) bootstrap + + +$(LIBBLAZESYM_SRC)/target/release/libblazesym.a:: + $(Q)cd $(LIBBLAZESYM_SRC) && $(CARGO) build --features=cheader,dont-generate-test-files --release + +$(LIBBLAZESYM_OBJ): $(LIBBLAZESYM_SRC)/target/release/libblazesym.a | $(OUTPUT) + $(call msg,LIB, $@) + $(Q)cp $(LIBBLAZESYM_SRC)/target/release/libblazesym.a $@ + +$(LIBBLAZESYM_HEADER): $(LIBBLAZESYM_SRC)/target/release/libblazesym.a | $(OUTPUT) + $(call msg,LIB,$@) + $(Q)cp $(LIBBLAZESYM_SRC)/target/release/blazesym.h $@ + +# Build BPF code +$(OUTPUT)/%.bpf.o: %.bpf.c $(LIBBPF_OBJ) $(wildcard %.h) $(VMLINUX) | $(OUTPUT) $(BPFTOOL) + $(call msg,BPF,$@) + $(Q)$(CLANG) -g -O2 -target bpf -D__TARGET_ARCH_$(ARCH) \ + $(INCLUDES) $(CLANG_BPF_SYS_INCLUDES) \ + -c $(filter %.c,$^) -o $(patsubst %.bpf.o,%.tmp.bpf.o,$@) + $(Q)$(BPFTOOL) gen object $@ $(patsubst %.bpf.o,%.tmp.bpf.o,$@) + +# Generate BPF skeletons +$(OUTPUT)/%.skel.h: $(OUTPUT)/%.bpf.o | $(OUTPUT) $(BPFTOOL) + $(call msg,GEN-SKEL,$@) + $(Q)$(BPFTOOL) gen skeleton $< > $@ + +# Build user-space code +$(patsubst %,$(OUTPUT)/%.o,$(APPS)): %.o: %.skel.h + +$(OUTPUT)/%.o: %.c $(wildcard %.h) | $(OUTPUT) + $(call msg,CC,$@) + $(Q)$(CC) $(CFLAGS) $(INCLUDES) -c $(filter %.c,$^) -o $@ + +$(patsubst %,$(OUTPUT)/%.o,$(BZS_APPS)): $(LIBBLAZESYM_HEADER) + +$(BZS_APPS): $(LIBBLAZESYM_OBJ) + +# Build application binary +$(APPS): %: $(OUTPUT)/%.o $(LIBBPF_OBJ) | $(OUTPUT) + $(call msg,BINARY,$@) + $(Q)$(CC) $(CFLAGS) $^ $(ALL_LDFLAGS) -lelf -lz -o $@ + +# delete failed targets +.DELETE_ON_ERROR: + +# keep intermediate (.skel.h, .bpf.o, etc) targets +.SECONDARY: diff --git a/src/43-kfuncs/README.md b/src/43-kfuncs/README.md new file mode 100644 index 0000000..1c09513 --- /dev/null +++ b/src/43-kfuncs/README.md @@ -0,0 +1,378 @@ +# 将 eBPF 的边界拓展:内核模块中的自定义 kfuncs + +你是否曾感到 eBPF 的功能受限?也许你遇到过现有的 eBPF 功能无法完成你的目标的情况。可能你需要与内核进行更深入的交互,或者面临标准 eBPF 运行时无法解决的性能问题。如果你曾希望在 eBPF 程序中拥有更多的灵活性和强大功能,本教程将为你提供帮助。 + +## 引言:通过 kfuncs 打破 eBPF 运行时的限制 + +**eBPF(扩展伯克利数据包过滤器)** 通过允许开发者在内核中运行受限的程序,彻底改变了 Linux 系统编程。它在网络、安全和可观察性方面带来了革命性的变化,使得无需修改内核源代码或加载传统的内核模块即可实现强大的功能。 + +然而,尽管 eBPF 非常强大,它也有其局限性: + +- **功能差距:** 有时,eBPF 运行时现有的功能无法提供你所需的特定能力。 +- **复杂需求:** 某些任务需要更复杂的内核交互,而 eBPF 无法开箱即用地处理这些需求。 +- **性能问题:** 在某些情况下,eBPF 运行时的开销会引入延迟,或者在高性能需求下效率不够。 + +这些挑战源于 **整个 eBPF 运行时** 的限制,而不仅仅是其辅助函数。那么,如何在不更改内核本身的情况下克服这些障碍呢? + +引入 **kfuncs(BPF 内核函数)**。通过在内核模块中定义你自己的 kfuncs,你可以将 eBPF 的功能扩展到其默认限制之外。这种方法让你能够: + +- **增强功能:** 引入标准 eBPF 运行时中不可用的新操作。 +- **自定义行为:** 根据你的具体需求调整内核交互。 +- **提升性能:** 通过在内核上下文中直接执行自定义代码来优化关键路径。 + +最重要的是,你无需修改核心内核,从而保持系统稳定性和代码安全性。 + +在本教程中,我们将向你展示如何定义自定义 kfuncs,以填补 eBPF 功能中的任何空白。我们将逐步创建一个引入新 kfuncs 的内核模块,并演示如何在你的 eBPF 程序中使用它们。无论你是希望克服性能瓶颈,还是需要 eBPF 运行时未提供的功能,自定义 kfuncs 都能为你的项目解锁新的可能性。 + +## 理解 kfuncs:将 eBPF 拓展到辅助函数之外 + +### 什么是 kfuncs? + +**BPF 内核函数(kfuncs)** 是 Linux 内核中的专用函数,供 eBPF 程序使用。与标准 eBPF 辅助函数不同,kfuncs 没有稳定的接口,且可能在内核版本之间有所不同。这种可变性意味着使用 kfuncs 的 BPF 程序需要与内核更新同步更新,以保持兼容性和稳定性。 + +### 为什么使用 kfuncs? + +1. **扩展功能:** kfuncs 允许执行标准 eBPF 辅助函数无法完成的操作。 +2. **自定义:** 定义针对特定用例的逻辑,增强 eBPF 程序的灵活性。 +3. **安全性和稳定性:** 通过将 kfuncs 封装在内核模块中,避免对核心内核的直接修改,保持系统完整性。 + +### kfuncs 在 eBPF 中的作用 + +kfuncs 作为 eBPF 程序与更深层内核功能之间的桥梁。它们允许 eBPF 程序执行更复杂的操作,方法是暴露现有内核函数或引入专门为 eBPF 交互设计的新封装。这种集成促进了更深入的内核交互,同时确保 eBPF 程序保持安全和可维护。 + +需要注意的是,Linux 内核已经包含了大量的 kfuncs。这些内置的 kfuncs 覆盖了广泛的功能,使大多数开发者无需定义新的 kfuncs 就能完成任务。然而,在现有 kfuncs 无法满足特定需求的情况下,定义自定义 kfuncs 就变得必要。本教程将演示如何定义新的 kfuncs 以填补任何空白,确保你的 eBPF 程序能够利用你所需的确切功能。eBPF 还可以扩展到用户空间。在用户空间 eBPF 运行时 [bpftime](https://github.com/eunomia-bpf/bpftime) 中,我们还在实现 ufuncs,它们类似于 kfuncs,但用于扩展用户空间应用程序。 + +## kfuncs 的概述及其演变 + +要理解 kfuncs 的重要性,必须了解它们与 eBPF 辅助函数之间的演变关系。 + +![累计的 Helper 和 kfunc 时间线](https://raw.githubusercontent.com/eunomia-bpf/code-survey/main/imgs/cumulative_helper_kfunc_timeline.png) + +**关键要点:** + +- **辅助函数的稳定性:** eBPF 辅助函数基本保持稳定,新增内容较少。 +- **kfuncs 的快速增长:** kfuncs 的采用和创建显著增加,表明社区对通过 kfuncs 扩展内核交互的兴趣。 +- **向更深层内核集成的转变:** 自 2023 年以来,新用例主要利用 kfuncs 影响内核行为,标志着通过 eBPF 进行更深层内核集成的趋势。 + +这一趋势凸显了社区通过 kfuncs 更深入地与内核集成,推动 eBPF 能力的边界的动力。 + +## 定义你自己的 kfunc:逐步指南 + +要利用 kfuncs 的强大功能,你需要在内核模块中定义它们。此过程确保你的自定义函数能够安全地暴露给 eBPF 程序,而无需更改核心内核。 + +### 编写内核模块 + +让我们从创建一个定义 kfunc 的简单内核模块开始。这个 kfunc 将执行一个基本的算术操作,作为理解机制的基础。 + +#### **文件:`hello.c`** + +```c +#include <linux/init.h> // 模块初始化的宏 +#include <linux/module.h> // 加载模块的核心头文件 +#include <linux/kernel.h> // 内核日志宏 +#include <linux/bpf.h> +#include <linux/btf.h> +#include <linux/btf_ids.h> + +// 声明 kfunc +__bpf_kfunc u64 bpf_kfunc_call_test(u32 a, u64 b, u32 c, u64 d); + +/* 定义 kfunc 函数 */ +__bpf_kfunc_start_defs(); + +__bpf_kfunc u64 bpf_kfunc_call_test(u32 a, u64 b, u32 c, u64 d) +{ + return a + b + c + d; +} + +__bpf_kfunc_end_defs(); + +// 定义 BTF kfunc ID 集合 +BTF_KFUNCS_START(bpf_kfunc_example_ids_set) +BTF_ID_FLAGS(func, bpf_kfunc_call_test) +BTF_KFUNCS_END(bpf_kfunc_example_ids_set) + +// 注册 kfunc ID 集合 +static const struct btf_kfunc_id_set bpf_kfunc_example_set = { + .owner = THIS_MODULE, + .set = &bpf_kfunc_example_ids_set, +}; + +// 模块初始化 +static int __init hello_init(void) +{ + int ret; + + printk(KERN_INFO "Hello, world!\n"); + // 注册 BTF kfunc ID 集合 + ret = register_btf_kfunc_id_set(BPF_PROG_TYPE_KPROBE, &bpf_kfunc_example_set); + if (ret) { + pr_err("bpf_kfunc_example: 注册 BTF kfunc ID 集合失败\n"); + return ret; + } + printk(KERN_INFO "bpf_kfunc_example: 模块加载成功\n"); + return 0; // 成功 +} + +// 模块清理 +static void __exit hello_exit(void) +{ + // 注销 BTF kfunc ID 集合(根据内核版本可选) + // unregister_btf_kfunc_id_set(BPF_PROG_TYPE_KPROBE, &bpf_kfunc_example_set); + printk(KERN_INFO "Goodbye, world!\n"); +} + +// 定义模块的入口和出口 +module_init(hello_init); +module_exit(hello_exit); + +MODULE_LICENSE("GPL"); // 许可类型 +MODULE_AUTHOR("Your Name"); // 模块作者 +MODULE_DESCRIPTION("一个简单的模块"); // 模块描述 +MODULE_VERSION("1.0"); // 模块版本 +``` + +**代码解释:** + +- **声明 kfunc:** `__bpf_kfunc` 宏声明了一个 eBPF 程序可以调用的函数。在这里,`bpf_kfunc_call_test` 接受四个参数(`a`、`b`、`c`、`d`),并返回它们的和。 + +- **BTF 定义:** `__bpf_kfunc_start_defs` 和 `__bpf_kfunc_end_defs` 宏标志着 kfunc 定义的开始和结束。`BTF_KFUNCS_START` 及相关宏有助于将 kfuncs 注册到 BPF 类型格式(BTF)中。 + +- **模块初始化:** `hello_init` 函数注册 kfunc ID 集合,使 `bpf_kfunc_call_test` 对 `BPF_PROG_TYPE_KPROBE` 类型的 eBPF 程序可用。 + +- **模块清理:** `hello_exit` 函数确保在模块移除时注销 kfunc ID 集合,保持系统的清洁。 + +#### **文件:`Makefile`** + +```makefile +obj-m += hello.o # hello.o 是目标 + +# 启用 BTF 生成 +KBUILD_CFLAGS += -g -O2 + +all: + make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules + +clean: + make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean +``` + +**Makefile 解释:** + +- **目标定义:** `obj-m += hello.o` 指定 `hello.o` 是要构建的模块。 + +- **BTF 生成标志:** `KBUILD_CFLAGS += -g -O2` 启用调试信息和优化,以促进 BTF 生成。 + +- **构建命令:** + - **`all`:** 通过调用内核构建系统编译内核模块。 + - **`clean`:** 清理构建产物。 + +**注意:** 提供的代码已在 **Linux 内核版本 6.11** 上进行了测试。如果你使用的是较早的版本,可能需要实现一些变通方法,例如引用 `compact.h`。 + +### 编译内核模块 + +有了内核模块的源代码和 Makefile 后,按照以下步骤编译模块: + +1. **导航到模块目录:** + + ```bash + cd /path/to/bpf-developer-tutorial/src/43-kfuncs/module/ + ``` + +2. **编译模块:** + + ```bash + make + ``` + + 该命令将生成一个名为 `hello.ko` 的文件,即编译后的内核模块。 + +### 加载内核模块 + +要将编译好的模块插入内核,使用 `insmod` 命令: + +```bash +sudo insmod hello.ko +``` + +### 验证模块加载 + +加载模块后,通过检查内核日志来验证是否成功插入: + +```bash +dmesg | tail +``` + +**预期输出:** + +```txt +[ 1234.5678] Hello, world! +[ 1234.5679] bpf_kfunc_example: 模块加载成功 +``` + +### 移除内核模块 + +当不再需要该模块时,使用 `rmmod` 命令卸载它: + +```bash +sudo rmmod hello +``` + +**验证移除:** + +```bash +dmesg | tail +``` + +**预期输出:** + +```txt +[ 1234.9876] Goodbye, world! +``` + +## 处理编译错误 + +在编译过程中,你可能会遇到以下错误: + +```txt +Skipping BTF generation for /root/bpf-developer-tutorial/src/43-kfuncs/module/hello.ko due to unavailability of vmlinux +``` + +**解决方案:** + +1. **安装 `dwarves` 包:** + + `dwarves` 包提供了 BTF 生成所需的工具。 + + ```sh + sudo apt install dwarves + ``` + +2. **复制 `vmlinux` 文件:** + + 确保包含 BTF 信息的 `vmlinux` 文件在构建目录中可用。 + + ```sh + sudo cp /sys/kernel/btf/vmlinux /usr/lib/modules/$(uname -r)/build/ + ``` + + 该命令将 `vmlinux` 文件复制到适当的构建目录中,确保 BTF 生成成功。 + +本教程的完整代码可以在 GitHub 的链接 <https://github.com/eunomia-bpf/bpf-developer-tutorial/tree/main/src/43-kfuncs> 中找到。这个代码在 Linux 内核版本 6.11 上进行了测试,其他低版本可能需要一些修改,参考 `compact.h`。 + +## 在 eBPF 程序中使用自定义 kfunc + +有了定义自定义 kfunc 的内核模块后,下一步是创建一个利用该函数的 eBPF 程序。这种交互展示了 kfuncs 引入的增强功能。 + +### 编写 eBPF 程序 + +创建一个 eBPF 程序,该程序附加到 `do_unlinkat` 内核函数,并使用自定义的 `bpf_kfunc_call_test` kfunc。 + +#### **文件:`kfunc.c`** + +```c +/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ +#define BPF_NO_GLOBAL_DATA +#include <linux/bpf.h> +#include <bpf/bpf_helpers.h> +#include <bpf/bpf_tracing.h> + +typedef unsigned int u32; +typedef unsigned long long u64; +typedef int pid_t; + +// 声明外部 kfunc +extern u64 bpf_kfunc_call_test(u32 a, u64 b, u32 c, u64 d) __ksym; + +// 许可信息 +char LICENSE[] SEC("license") = "Dual BSD/GPL"; + +// 附加到 do_unlinkat 内核函数 +SEC("kprobe/do_unlinkat") +int handle_kprobe(void *ctx) +{ + pid_t pid = bpf_get_current_pid_tgid() >> 32; + u64 result = bpf_kfunc_call_test(1, 2, 3, 4); + bpf_printk("BPF 触发了 do_unlinkat,PID: %d。结果: %lld\n", pid, result); + return 0; +} +``` + +**eBPF 代码解释:** + +- **外部 kfunc 声明:** `extern` 关键字声明了 `bpf_kfunc_call_test` 函数,使其在 eBPF 程序中可用。 + +- **Kprobe 附加:** `SEC("kprobe/do_unlinkat")` 宏将 eBPF 程序附加到 `do_unlinkat` 内核函数。每当调用 `do_unlinkat` 时,`handle_kprobe` 函数就会执行。 + +- **使用 kfunc:** 在 `handle_kprobe` 中,eBPF 程序调用 `bpf_kfunc_call_test`,传入四个参数(`1, 2, 3, 4`)。结果,即这些数字的和,通过 `bpf_printk` 打印出来,显示 PID 和结果。 + +### 编译 eBPF 程序 + +要编译 eBPF 程序,确保你已安装必要的工具,如 `clang` 和 `llvm`。以下是编译程序的方法: + +1. **导航到 eBPF 程序目录:** + + ```bash + cd /path/to/bpf-developer-tutorial/src/43-kfuncs/ + ``` + +2. **编译 eBPF 程序:** + + ```bash + make + ``` + +### 运行 eBPF 程序 + +假设你有一个用户空间应用程序或工具来加载和附加 eBPF 程序,你可以执行它以观察 eBPF 程序与自定义 kfunc 之间的交互。 + +**示例输出:** + +```bash +$ sudo ./kfunc +BPF 程序加载并成功附加。按 Ctrl-C 退出。 + node-9523 [004] ...21 7520.587718: bpf_trace_printk: BPF 触发了 do_unlinkat,PID: 9523。结果: 10 + + cpptools-11242 [003] ...21 7859.613060: bpf_trace_printk: BPF 触发了 do_unlinkat,PID: 11235。结果: 10 + +^C +cpptools-11242 [002] ...21 7865.831074: bpf_trace_printk: BPF 触发了 do_unlinkat,PID: 11235。结果: 10 +``` + +**输出解释:** + +每当内核中调用 `do_unlinkat` 函数时,eBPF 程序都会打印一条消息,指示进程的 PID 和 kfunc 调用的结果。在此示例中,`1 + 2 + 3 + 4` 的和为 `10`,这在输出中得到了体现。 + +## 总结与结论 + +在本教程中,我们深入探讨了通过定义和使用自定义内核函数(kfuncs)来扩展 eBPF 功能。以下是我们涵盖的内容回顾: + +- **理解 kfuncs:** 掌握了 kfuncs 的概念及其在增强 eBPF 标准辅助函数方面的作用。 +- **定义 kfuncs:** 创建了一个定义自定义 kfunc 的内核模块,确保其能够安全地暴露给 eBPF 程序,而无需更改核心内核。 +- **编写使用 kfuncs 的 eBPF 程序:** 开发了一个利用自定义 kfunc 执行特定操作的 eBPF 程序,展示了增强的功能。 +- **编译与执行:** 提供了逐步指南,编译、加载和运行内核模块及 eBPF 程序,确保你能在自己的系统上复现设置。 +- **错误处理:** 解决了潜在的编译问题,并提供了解决方案,以确保顺利的开发体验。 + +**关键要点:** + +- **克服辅助函数的限制:** kfuncs 填补了标准 eBPF 辅助函数留下的空白,提供了针对特定需求的扩展功能。 +- **维护系统稳定性:** 通过将 kfuncs 封装在内核模块中,确保在不进行侵入性内核更改的情况下保持系统稳定性。 +- **社区驱动的演变:** kfuncs 的快速增长和采用凸显了 eBPF 社区致力于通过内核级编程推动 eBPF 潜力的决心。 +- **利用现有 kfuncs:** 在定义新的 kfuncs 之前,探索内核提供的现有 kfuncs。它们涵盖了广泛的功能,除非绝对必要,否则无需创建自定义函数。 + +**准备好进一步提升你的 eBPF 技能了吗?** [访问我们的教程仓库](https://github.com/eunomia-bpf/bpf-developer-tutorial)并[探索我们网站上的更多教程](https://eunomia.dev/tutorials/)。深入了解大量示例,深化你的理解,并为动态发展的 eBPF 世界做出贡献! + +祝你 eBPF 编程愉快! + +## 参考资料 + +- [BPF 内核函数文档](https://docs.kernel.org/bpf/kfuncs.html) +- [eBPF kfuncs 指南](https://docs.ebpf.io/linux/kfuncs/) + +## 额外资源 + +如果你想了解更多关于 eBPF 的知识和实践,可以访问我们的开源教程代码仓库 <https://github.com/eunomia-bpf/bpf-developer-tutorial> 或者我们的网站 <https://eunomia.dev/tutorials/>,获取更多示例和完整代码。 + +## 结论 + +通过遵循本详尽的教程,你已经掌握了使用自定义 kfuncs 扩展 eBPF 功能的知识。无论你是旨在执行高级内核交互、克服辅助函数的限制,还是增强你的可观察性工具,kfuncs 都提供了你所需的灵活性和强大功能。继续尝试,保持好奇,并为不断发展的 eBPF 领域做出贡献! diff --git a/src/43-kfuncs/README_en.md b/src/43-kfuncs/README_en.md new file mode 100644 index 0000000..4c9d385 --- /dev/null +++ b/src/43-kfuncs/README_en.md @@ -0,0 +1,378 @@ +# Extending eBPF Beyond Its Limits: Custom kfuncs in Kernel Modules + +Have you ever felt constrained by eBPF's capabilities? Maybe you've run into situations where the existing eBPF features just aren't enough to accomplish your goals. Perhaps you need deeper interactions with the kernel, or you're facing performance issues that the standard eBPF runtime can't solve. If you've ever wished for more flexibility and power in your eBPF programs, this tutorial is for you. + +## Introduction: Breaking Free from eBPF Runtime Limitations with kfuncs + +**eBPF (extended Berkeley Packet Filter)** has revolutionized Linux system programming by allowing developers to run sandboxed programs inside the kernel. It's a game-changer for networking, security, and observability, enabling powerful functionalities without the need to modify kernel source code or load traditional kernel modules. + +But as amazing as eBPF is, it isn't without its limitations: + +- **Functionality Gaps:** Sometimes, the existing features of the eBPF runtime don't provide the specific capabilities you need. +- **Complex Requirements:** Certain tasks demand more intricate kernel interactions that eBPF can't handle out of the box. +- **Performance Issues:** In some cases, the overhead of the eBPF runtime introduces latency or isn't efficient enough for high-performance requirements. + +These challenges stem from the limitations of the **entire eBPF runtime**, not just its helper functions. So how do you overcome these hurdles without altering the kernel itself? + +Enter **kfuncs (BPF Kernel Functions)**. By defining your own kfuncs within kernel modules, you can extend eBPF's capabilities beyond its default limitations. This approach lets you: + +- **Enhance Functionality:** Introduce new operations that aren't available in the standard eBPF runtime. +- **Customize Behavior:** Tailor kernel interactions to fit your specific needs. +- **Boost Performance:** Optimize critical paths by executing custom code directly in the kernel context. + +Best of all, you achieve this without modifying the core kernel, keeping your system stable and your code safe. + +In this tutorial, we'll show you how to define custom kfuncs to fill any gaps in eBPF's capabilities. We'll walk through creating a kernel module that introduces new kfuncs and demonstrate how to use them in your eBPF programs. Whether you're looking to overcome performance bottlenecks or need features the eBPF runtime doesn't offer, custom kfuncs can unlock new possibilities for your projects. + +## Understanding kfuncs: Extending eBPF Beyond Helpers + +### What Are kfuncs? + +**BPF Kernel Functions (kfuncs)** are specialized functions within the Linux kernel that are exposed for use by eBPF programs. Unlike standard eBPF helpers, kfuncs do not have a stable interface and can vary between kernel releases. This variability means that BPF programs utilizing kfuncs need to be updated in tandem with kernel updates to maintain compatibility and stability. + +### Why Use kfuncs? + +1. **Extended Functionality:** kfuncs enable operations that standard eBPF helpers cannot perform. +2. **Customization:** Define logic tailored to specific use cases, enhancing the flexibility of eBPF programs. +3. **Safety and Stability:** By encapsulating kfuncs within kernel modules, you avoid direct modifications to the core kernel, preserving system integrity. + +### How kfuncs Fit into eBPF + +kfuncs serve as bridges between eBPF programs and deeper kernel functionalities. They allow eBPF programs to perform more intricate operations by either exposing existing kernel functions or introducing new wrappers specifically designed for eBPF interactions. This integration facilitates deeper kernel interactions while ensuring that eBPF programs remain safe and maintainable. + +It's important to note that the Linux kernel already includes a plethora of kfuncs. These built-in kfuncs cover a wide range of functionalities, allowing most developers to accomplish their tasks without the need to define new ones. However, in cases where the existing kfuncs do not meet specific requirements, defining custom kfuncs becomes necessary. This tutorial demonstrates how to define new kfuncs to fill any gaps, ensuring that your eBPF programs can leverage the exact functionality you need. eBPF can also be extended to userspace. In the userspace eBPF runtime [bpftime](https://github.com/eunomia-bpf/bpftime), we are also implementing ufuncs, which are similar to kfuncs but extending userspace applications. + +## Overview of kfuncs and Their Evolution + +To appreciate the significance of kfuncs, it's essential to understand their evolution in relation to eBPF helper functions. + +![Cumulative Helper and kfunc Timeline](https://raw.githubusercontent.com/eunomia-bpf/code-survey/main/imgs/cumulative_helper_kfunc_timeline.png) + +**Key Takeaways:** + +- **Stability of Helper Functions:** eBPF helper functions have remained largely stable, with minimal new additions. +- **Rapid Growth of kfuncs:** There's been a significant increase in the adoption and creation of kfuncs, indicating the community's interest in expanding kernel interactions via kfuncs. +- **Shift Towards Deeper Kernel Integrations:** Since 2023, new use cases predominantly leverage kfuncs to influence kernel behavior, signaling a trend towards more profound kernel integrations through eBPF. + +This trend underscores the community's drive to push the boundaries of what eBPF can achieve by integrating more deeply with the kernel through kfuncs. + +## Defining Your Own kfunc: A Step-by-Step Guide + +To harness the power of kfuncs, you'll need to define them within a kernel module. This process ensures that your custom functions are safely exposed to eBPF programs without altering the core kernel. + +### Writing the Kernel Module + +Let's start by creating a simple kernel module that defines a kfunc. This kfunc will perform a basic arithmetic operation, serving as a foundation for understanding the mechanics. + +#### **File: `hello.c`** + +```c +#include <linux/init.h> // Macros for module initialization +#include <linux/module.h> // Core header for loading modules +#include <linux/kernel.h> // Kernel logging macros +#include <linux/bpf.h> +#include <linux/btf.h> +#include <linux/btf_ids.h> + +// Declare the kfunc +__bpf_kfunc u64 bpf_kfunc_call_test(u32 a, u64 b, u32 c, u64 d); + +/* Define the kfunc functions */ +__bpf_kfunc_start_defs(); + +__bpf_kfunc u64 bpf_kfunc_call_test(u32 a, u64 b, u32 c, u64 d) +{ + return a + b + c + d; +} + +__bpf_kfunc_end_defs(); + +// Define the BTF kfunc ID set +BTF_KFUNCS_START(bpf_kfunc_example_ids_set) +BTF_ID_FLAGS(func, bpf_kfunc_call_test) +BTF_KFUNCS_END(bpf_kfunc_example_ids_set) + +// Register the kfunc ID set +static const struct btf_kfunc_id_set bpf_kfunc_example_set = { + .owner = THIS_MODULE, + .set = &bpf_kfunc_example_ids_set, +}; + +// Module initialization +static int __init hello_init(void) +{ + int ret; + + printk(KERN_INFO "Hello, world!\n"); + // Register the BTF kfunc ID set + ret = register_btf_kfunc_id_set(BPF_PROG_TYPE_KPROBE, &bpf_kfunc_example_set); + if (ret) { + pr_err("bpf_kfunc_example: Failed to register BTF kfunc ID set\n"); + return ret; + } + printk(KERN_INFO "bpf_kfunc_example: Module loaded successfully\n"); + return 0; // Success +} + +// Module cleanup +static void __exit hello_exit(void) +{ + // Unregister the BTF kfunc ID set (optional based on kernel version) + // unregister_btf_kfunc_id_set(BPF_PROG_TYPE_KPROBE, &bpf_kfunc_example_set); + printk(KERN_INFO "Goodbye, world!\n"); +} + +// Define module entry and exit points +module_init(hello_init); +module_exit(hello_exit); + +MODULE_LICENSE("GPL"); // License type +MODULE_AUTHOR("Your Name"); // Module author +MODULE_DESCRIPTION("A simple module"); // Module description +MODULE_VERSION("1.0"); // Module version +``` + +**Explanation of the Code:** + +- **Declaring the kfunc:** The `__bpf_kfunc` macro declares a function that eBPF programs can invoke. Here, `bpf_kfunc_call_test` takes four parameters (`a`, `b`, `c`, `d`) and returns their sum. + +- **BTF Definitions:** The `__bpf_kfunc_start_defs` and `__bpf_kfunc_end_defs` macros demarcate the beginning and end of kfunc definitions. The `BTF_KFUNCS_START` and related macros assist in registering the kfuncs with the BPF Type Format (BTF). + +- **Module Initialization:** The `hello_init` function registers the kfunc ID set, making `bpf_kfunc_call_test` available to eBPF programs of type `BPF_PROG_TYPE_KPROBE`. + +- **Module Cleanup:** The `hello_exit` function ensures that the kfunc ID set is unregistered upon module removal, maintaining system cleanliness. + +#### **File: `Makefile`** + +```makefile +obj-m += hello.o # hello.o is the target + +# Enable BTF generation +KBUILD_CFLAGS += -g -O2 + +all: + make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules + +clean: + make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean +``` + +**Explanation of the Makefile:** + +- **Target Definition:** `obj-m += hello.o` specifies that `hello.o` is the module to be built. + +- **BTF Generation Flags:** `KBUILD_CFLAGS += -g -O2` enables debug information and optimization, facilitating BTF generation. + +- **Build Commands:** + - **`all`:** Compiles the kernel module by invoking the kernel build system. + - **`clean`:** Cleans up the build artifacts. + +**Note:** The provided code has been tested on Linux kernel version **6.11**. If you're using an earlier version, you might need to implement workarounds, such as referencing `compact.h`. + +### Compiling the Kernel Module + +With the kernel module source and Makefile in place, follow these steps to compile the module: + +1. **Navigate to the Module Directory:** + + ```bash + cd /path/to/bpf-developer-tutorial/src/43-kfuncs/module/ + ``` + +2. **Compile the Module:** + + ```bash + make + ``` + + This command will generate a file named `hello.ko`, which is the compiled kernel module. + +### Loading the Kernel Module + +To insert the compiled module into the kernel, use the `insmod` command: + +```bash +sudo insmod hello.ko +``` + +### Verifying Module Loading + +After loading the module, verify its successful insertion by checking the kernel logs: + +```bash +dmesg | tail +``` + +**Expected Output:** + +```txt +[ 1234.5678] Hello, world! +[ 1234.5679] bpf_kfunc_example: Module loaded successfully +``` + +### Removing the Kernel Module + +When you no longer need the module, unload it using the `rmmod` command: + +```bash +sudo rmmod hello +``` + +**Verify Removal:** + +```bash +dmesg | tail +``` + +**Expected Output:** + +```txt +[ 1234.9876] Goodbye, world! +``` + +## Handling Compilation Errors + +During the compilation process, you might encounter the following error: + +```txt +Skipping BTF generation for /root/bpf-developer-tutorial/src/43-kfuncs/module/hello.ko due to unavailability of vmlinux +``` + +**Solution:** + +1. **Install the `dwarves` Package:** + + The `dwarves` package provides tools necessary for BTF generation. + + ```sh + sudo apt install dwarves + ``` + +2. **Copy the `vmlinux` File:** + + Ensure that the `vmlinux` file, which contains BTF information, is available in the build directory. + + ```sh + sudo cp /sys/kernel/btf/vmlinux /usr/lib/modules/$(uname -r)/build/ + ``` + + This command copies the `vmlinux` file to the appropriate build directory, enabling successful BTF generation. + +The complete code for this tutorial can be found in the link <https://github.com/eunomia-bpf/bpf-developer-tutorial/tree/main/src/43-kfuncs> on GitHub. This is tested on Linux kernel version 6.11, and some modifications may be required for lower versions, referring to `compact.h`. + +## Utilizing Your Custom kfunc in an eBPF Program + +With the kernel module defining your custom kfunc in place, the next step is to create an eBPF program that leverages this function. This interaction showcases the enhanced capabilities introduced by kfuncs. + +### Writing the eBPF Program + +Create an eBPF program that attaches to the `do_unlinkat` kernel function and uses the custom `bpf_kfunc_call_test` kfunc. + +#### **File: `kfunc.c`** + +```c +/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ +#define BPF_NO_GLOBAL_DATA +#include <linux/bpf.h> +#include <bpf/bpf_helpers.h> +#include <bpf/bpf_tracing.h> + +typedef unsigned int u32; +typedef unsigned long long u64; +typedef int pid_t; + +// Declare the external kfunc +extern u64 bpf_kfunc_call_test(u32 a, u64 b, u32 c, u64 d) __ksym; + +// License information +char LICENSE[] SEC("license") = "Dual BSD/GPL"; + +// Attach to the do_unlinkat kernel function +SEC("kprobe/do_unlinkat") +int handle_kprobe(void *ctx) +{ + pid_t pid = bpf_get_current_pid_tgid() >> 32; + u64 result = bpf_kfunc_call_test(1, 2, 3, 4); + bpf_printk("BPF triggered do_unlinkat from PID %d. Result: %lld\n", pid, result); + return 0; +} +``` + +**Explanation of the eBPF Code:** + +- **External kfunc Declaration:** The `extern` keyword declares the `bpf_kfunc_call_test` function, making it accessible within the eBPF program. + +- **Kprobe Attachment:** The `SEC("kprobe/do_unlinkat")` macro attaches the eBPF program to the `do_unlinkat` kernel function. Every time `do_unlinkat` is invoked, the `handle_kprobe` function executes. + +- **Using the kfunc:** Within `handle_kprobe`, the eBPF program calls `bpf_kfunc_call_test` with four arguments (`1, 2, 3, 4`). The result, which should be the sum of these numbers, is then printed using `bpf_printk`, displaying both the PID and the result. + +### Compiling the eBPF Program + +To compile the eBPF program, ensure you have the necessary tools installed, such as `clang` and `llvm`. Here's how you can compile the program: + +1. **Navigate to the eBPF Program Directory:** + + ```bash + cd /path/to/bpf-developer-tutorial/src/43-kfuncs/ + ``` + +2. **Compile the eBPF Program:** + + ```bash + make + ``` + +### Running the eBPF Program + +Assuming you have a user-space application or a tool to load and attach the eBPF program, you can execute it to observe the interaction between the eBPF program and the custom kfunc. + +**Sample Output:** + +```bash +$ sudo ./kfunc +BPF program loaded and attached successfully. Press Ctrl-C to exit. + node-9523 [004] ...21 7520.587718: bpf_trace_printk: BPF triggered do_unlinkat from PID 9523. Result: 10 + + cpptools-11242 [003] ...21 7859.613060: bpf_trace_printk: BPF triggered do_unlinkat from PID 11235. Result: 10 + +^C +cpptools-11242 [002] ...21 7865.831074: bpf_trace_printk: BPF triggered do_unlinkat from PID 11235. Result: 10 +``` + +**Explanation of the Output:** + +Each time the `do_unlinkat` function is invoked in the kernel, the eBPF program prints a message indicating the PID of the process and the result of the kfunc call. In this example, the sum `1 + 2 + 3 + 4` results in `10`, which is reflected in the output. + +## Summary and Conclusion + +In this tutorial, we've delved deep into extending eBPF's capabilities by defining and utilizing custom kernel functions (kfuncs). Here's a recap of what we've covered: + +- **Understanding kfuncs:** Grasped the concept of kfuncs and their role in enhancing eBPF beyond standard helper functions. +- **Defining kfuncs:** Created a kernel module that defines a custom kfunc, ensuring it can be safely exposed to eBPF programs without altering the core kernel. +- **Writing eBPF Programs with kfuncs:** Developed an eBPF program that leverages the custom kfunc to perform specific operations, demonstrating the enhanced functionality. +- **Compilation and Execution:** Provided a step-by-step guide to compile, load, and run both the kernel module and the eBPF program, ensuring you can replicate the setup on your own system. +- **Error Handling:** Addressed potential compilation issues and offered solutions to ensure a smooth development experience. + +**Key Takeaways:** + +- **Overcoming Helper Limitations:** kfuncs bridge the gaps left by standard eBPF helpers, offering extended functionality tailored to specific needs. +- **Maintaining System Stability:** By encapsulating kfuncs within kernel modules, you ensure that system stability is maintained without making invasive changes to the kernel. +- **Community-Driven Evolution:** The rapid growth and adoption of kfuncs highlight the eBPF community's commitment to pushing the boundaries of what's possible with kernel-level programming. +- **Leveraging Existing kfuncs:** Before defining new kfuncs, explore the existing ones provided by the kernel. They cover a wide range of functionalities, reducing the need to create custom functions unless absolutely necessary. + +**Ready to elevate your eBPF skills even further?** [Visit our tutorial repository](https://github.com/eunomia-bpf/bpf-developer-tutorial) and [explore more tutorials on our website](https://eunomia.dev/tutorials/). Dive into a wealth of examples, deepen your understanding, and contribute to the dynamic world of eBPF! + +Happy eBPF-ing! + +## References + +- [BPF Kernel Functions Documentation](https://docs.kernel.org/bpf/kfuncs.html) +- [eBPF kfuncs Guide](https://docs.ebpf.io/linux/kfuncs/) + +## Additional Resources + +If you'd like to learn more about eBPF knowledge and practices, you can visit our open source tutorial code repository at <https://github.com/eunomia-bpf/bpf-developer-tutorial> or website <https://eunomia.dev/tutorials/> for more examples and complete code. + +## Conclusion + +By following this detailed tutorial, you've equipped yourself with the knowledge to extend eBPF's capabilities using custom kfuncs. Whether you're aiming to perform advanced kernel interactions, overcome helper limitations, or enhance your observability tools, kfuncs provide the flexibility and power you need. Continue experimenting, stay curious, and contribute to the ever-evolving landscape of eBPF! diff --git a/src/43-kfuncs/kfunc.bpf.c b/src/43-kfuncs/kfunc.bpf.c new file mode 100644 index 0000000..f282bc4 --- /dev/null +++ b/src/43-kfuncs/kfunc.bpf.c @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ +#define BPF_NO_GLOBAL_DATA +#include <linux/bpf.h> +#include <bpf/bpf_helpers.h> +#include <bpf/bpf_tracing.h> + +typedef unsigned int u32; +typedef unsigned long long u64; +typedef int pid_t; + +extern u64 bpf_kfunc_call_test(u32 a, u64 b, u32 c, u64 d) __ksym; + +char LICENSE[] SEC("license") = "Dual BSD/GPL"; + +SEC("kprobe/do_unlinkat") +int handle_kprobe(void *ctx) +{ + pid_t pid = bpf_get_current_pid_tgid() >> 32; + u64 result = bpf_kfunc_call_test(1, 2, 3, 4); + bpf_printk("BPF triggered do_unlinkat from PID %d. Result: %lld\n", pid, result); + return 0; +} diff --git a/src/43-kfuncs/kfunc.c b/src/43-kfuncs/kfunc.c new file mode 100644 index 0000000..d7bb134 --- /dev/null +++ b/src/43-kfuncs/kfunc.c @@ -0,0 +1,76 @@ +#include <stdio.h> +#include <stdlib.h> +#include <signal.h> +#include <unistd.h> +#include <errno.h> + +#include "kfunc.skel.h" // Include the generated skeleton header + +static volatile bool exiting = false; + +// Signal handler for graceful termination +void handle_signal(int sig) { + exiting = true; +} + +int main(int argc, char **argv) { + struct kfunc_bpf *skel; + int err; + + // Handle SIGINT and SIGTERM for graceful shutdown + signal(SIGINT, handle_signal); + + // Open the BPF application + skel = kfunc_bpf__open(); + if (!skel) { + fprintf(stderr, "Failed to open BPF skeleton\n"); + return 1; + } + + // Load & verify the BPF program + err = kfunc_bpf__load(skel); + if (err) { + fprintf(stderr, "Failed to load and verify BPF skeleton: %d\n", err); + goto cleanup; + } + + // Attach the BPF program (e.g., attach kprobe) + err = kfunc_bpf__attach(skel); + if (err) { + fprintf(stderr, "Failed to attach BPF skeleton: %d\n", err); + goto cleanup; + } + + printf("BPF program loaded and attached successfully. Press Ctrl-C to exit.\n"); + + // Optionally, read the trace_pipe to see bpf_printk outputs + FILE *trace_pipe = fopen("/sys/kernel/debug/tracing/trace_pipe", "r"); + if (!trace_pipe) { + perror("fopen trace_pipe"); + // Continue without reading trace_pipe + } + + // Main loop + while (!exiting) { + if (trace_pipe) { + char buffer[256]; + if (fgets(buffer, sizeof(buffer), trace_pipe)) { + printf("%s", buffer); + } else { + if (errno == EINTR) + break; + } + } else { + // If trace_pipe is not available, just sleep + sleep(1); + } + } + + if (trace_pipe) + fclose(trace_pipe); + +cleanup: + // Clean up and destroy the BPF program + kfunc_bpf__destroy(skel); + return err < 0 ? -err : 0; +} diff --git a/src/43-kfuncs/module/.gitignore b/src/43-kfuncs/module/.gitignore new file mode 100644 index 0000000..9523173 --- /dev/null +++ b/src/43-kfuncs/module/.gitignore @@ -0,0 +1,23 @@ +# Ignore object files and kernel modules +*.o +*.ko +*.mod +*.mod.c +*.symvers +*.order + +# Ignore temporary and backup files +*~ +*.bak +*.tmp +*.swp + +# Ignore build directory if generated +/Module.symvers +/Modules.markers +/Module.markers +/modules.order + +# Ignore other automatically generated files +*.cmd +.tmp_versions/ diff --git a/src/43-kfuncs/module/Makefile b/src/43-kfuncs/module/Makefile new file mode 100644 index 0000000..44afa31 --- /dev/null +++ b/src/43-kfuncs/module/Makefile @@ -0,0 +1,11 @@ +obj-m += hello.o # hello.o is the target + +# Enable BTF generation +KBUILD_CFLAGS += -g -O2 + +all: + # Compile the module with BTF information + make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules + +clean: + make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean diff --git a/src/43-kfuncs/module/README.md b/src/43-kfuncs/module/README.md new file mode 100644 index 0000000..d2dfada --- /dev/null +++ b/src/43-kfuncs/module/README.md @@ -0,0 +1,134 @@ +# write a basic kernel module + +## hello world + +Writing a Linux kernel module involves creating code that can be loaded into and unloaded from the kernel dynamically, without rebooting the system. Here’s a simple step-by-step guide to help you write a basic kernel module: + +### 1. Set Up Your Environment + +Make sure you have the Linux kernel headers installed and a suitable development environment ready. For Ubuntu or Debian, install them with: + +```bash +sudo apt-get install linux-headers-$(uname -r) build-essential +``` + +### 2. Write the Kernel Module Code + +Here’s an example of a very basic Linux kernel module: + +```c +// hello.c: A simple Linux kernel module +#include <linux/init.h> // Macros for module initialization +#include <linux/module.h> // Core header for loading modules +#include <linux/kernel.h> // Kernel logging macros + +// Function executed when the module is loaded +static int __init hello_init(void) +{ + printk(KERN_INFO "Hello, world!\n"); + return 0; // Return 0 if successful +} + +// Function executed when the module is removed +static void __exit hello_exit(void) +{ + printk(KERN_INFO "Goodbye, world!\n"); +} + +// Macros to define the module’s init and exit points +module_init(hello_init); +module_exit(hello_exit); + +MODULE_LICENSE("GPL"); // License type (GPL) +MODULE_AUTHOR("Your Name"); // Module author +MODULE_DESCRIPTION("A simple module"); // Module description +MODULE_VERSION("1.0"); // Module version +``` + +### 3. Create a Makefile + +To compile the kernel module, you’ll need a `Makefile`. Here's a simple one: + +```makefile +obj-m += hello.o # hello.o is the target + +all: + make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules + +clean: + make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean +``` + +### 4. Compile the Module + +Run the following command in the directory where your `hello.c` and `Makefile` are located: + +```bash +make +``` + +This will generate a file called `hello.ko`, which is the compiled kernel module. + +### 5. Load the Module + +To insert the module into the kernel, use `insmod`: + +```bash +sudo insmod hello.ko +``` + +### 6. Check the Logs + +To see the output from the `printk` statements, use the `dmesg` command: + +```bash +dmesg | tail +``` + +You should see something like: + +```txt +[ 1234.5678] Hello, world! +``` + +### 7. Remove the Module + +To unload the module, use `rmmod`: + +```bash +sudo rmmod hello +``` + +Again, check the logs using `dmesg`: + +```bash +sudo dmesg | tail +``` + +You should see: + +```txt +[ 1234.9876] Goodbye, world! +``` + +### 8. Clean Up + +To clean up the build files, run: + +```bash +make clean +``` + +### Notes + +- **License**: The `MODULE_LICENSE("GPL")` ensures the module is GPL-compliant, which allows it to use symbols (functions) exported by the kernel. +- **Debugging**: Use `printk` for logging within the module. It behaves similarly to `printf` but is designed for kernel space. +- **Module Parameters**: You can add parameters to modules using `module_param()` to pass arguments when the module is loaded. + +### Next Steps + +Once you are familiar with this basic example, you can explore: + +- Writing more advanced modules that interact with hardware or the filesystem. +- Using kernel-specific APIs like work queues, kthreads, or handling interrupts. +- Diving into eBPF or loadable kernel module techniques for debugging and tracing kernel events. diff --git a/src/43-kfuncs/module/compact.h b/src/43-kfuncs/module/compact.h new file mode 100644 index 0000000..5ff2115 --- /dev/null +++ b/src/43-kfuncs/module/compact.h @@ -0,0 +1,11 @@ +// Compatible for lower kernel versions. No need in 6.11. +#ifndef BTF_SET8_KFUNCS +/* This flag implies BTF_SET8 holds kfunc(s) */ +#define BTF_SET8_KFUNCS (1 << 0) +#endif +#ifndef BTF_KFUNCS_START +#define BTF_KFUNCS_START(name) static struct btf_id_set8 __maybe_unused name = { .flags = BTF_SET8_KFUNCS }; +#endif +#ifndef BTF_KFUNCS_END +#define BTF_KFUNCS_END(name) +#endif diff --git a/src/43-kfuncs/module/hello.c b/src/43-kfuncs/module/hello.c new file mode 100644 index 0000000..9552e1d --- /dev/null +++ b/src/43-kfuncs/module/hello.c @@ -0,0 +1,61 @@ +#include <linux/init.h> // Macros for module initialization +#include <linux/module.h> // Core header for loading modules +#include <linux/kernel.h> // Kernel logging macros +#include <linux/bpf.h> +#include <linux/btf.h> +#include <linux/btf_ids.h> + +__bpf_kfunc u64 bpf_kfunc_call_test(u32 a, u64 b, u32 c, u64 d); + +/* Define a kfunc function */ +__bpf_kfunc_start_defs(); + +__bpf_kfunc u64 bpf_kfunc_call_test(u32 a, u64 b, u32 c, u64 d) +{ + return a + b + c + d; +} + +__bpf_kfunc_end_defs(); + +BTF_KFUNCS_START(bpf_kfunc_example_ids_set) +BTF_ID_FLAGS(func, bpf_kfunc_call_test) +BTF_KFUNCS_END(bpf_kfunc_example_ids_set) + +// Register the kfunc ID set +static const struct btf_kfunc_id_set bpf_kfunc_example_set = { + .owner = THIS_MODULE, + .set = &bpf_kfunc_example_ids_set, +}; + +// Function executed when the module is loaded +static int __init hello_init(void) +{ + int ret; + + printk(KERN_INFO "Hello, world!\n"); + // Register the BTF kfunc ID set + ret = register_btf_kfunc_id_set(BPF_PROG_TYPE_KPROBE, &bpf_kfunc_example_set); + if (ret) { + pr_err("bpf_kfunc_example: Failed to register BTF kfunc ID set\n"); + return ret; + } + printk(KERN_INFO "bpf_kfunc_example: Module loaded successfully\n"); + return 0; // Return 0 if successful +} + +// Function executed when the module is removed +static void __exit hello_exit(void) +{ + // Unregister the BTF kfunc ID set + // unregister_btf_kfunc_id_set(BPF_PROG_TYPE_UNSPEC, &bpf_kfunc_example_set); + printk(KERN_INFO "Goodbye, world!\n"); +} + +// Macros to define the module’s init and exit points +module_init(hello_init); +module_exit(hello_exit); + +MODULE_LICENSE("GPL"); // License type (GPL) +MODULE_AUTHOR("Your Name"); // Module author +MODULE_DESCRIPTION("A simple module"); // Module description +MODULE_VERSION("1.0"); // Module version diff --git a/src/guideline.md b/src/guideline.md index 7e2ea4e..0f378c1 100644 --- a/src/guideline.md +++ b/src/guideline.md @@ -109,7 +109,9 @@ You need to have **Call to Action** in Summary and Conclusion - **Proofreading:** Check for grammatical errors and ensure technical accuracy. - **Accessibility:** Make sure that the tutorials are accessible to readers with varying levels of expertise in eBPF. -Also, do not just list points, try to make it using paragraph unless points list is clear. using oral English, clear and simple words and short sentence, make it attractive and easy to read, do not make it like a paper. +Also, do not just list points, try to make it using paragraph unless points list is clear. + +**The key point in tone: Using oral English, clear and simple words and short sentence, make it attractive and easy to read, do not make it like a paper. Not too much fancy words, try to be attracive** ## Template Summary diff --git a/src/third_party/libbpf b/src/third_party/libbpf deleted file mode 160000 index 56069cd..0000000 --- a/src/third_party/libbpf +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 56069cda7897afdd0ae2478825845c7a7308c878 diff --git a/src/third_party/libbpf b/src/third_party/libbpf new file mode 120000 index 0000000..b56801b --- /dev/null +++ b/src/third_party/libbpf @@ -0,0 +1 @@ +bpftool/libbpf \ No newline at end of file