一、eBPF 是什么?
1. 简单理解
eBPF
就是一个让你安全地在 Linux
内核中运行自定义代码的机制。
它可以在内核中运行经过验证的“沙盒字节码”,而不需要修改内核源码,也不需要编写复杂的内核模块。
2. eBPF 的核心优势
二、eBPF 运行架构
下面这张图,可以帮你快速理解 eBPF 的工作原理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| + | 用户态 (User Space) | | | | eBPF 程序 (C / bpftrace) | | ↓ clang 编译 | | eBPF 字节码 (.o) | + | v + | 内核态 (Kernel Space) | | | | eBPF 验证器 (Verifier) | | - 检查安全性 | | - 防止死循环 | | - 检查内存访问范围 | | | | JIT 编译器 (JIT Compiler) | | - 转成本地机器码执行 | | | | 挂载到 Hook 点 | | - kprobe/tracepoint | | - XDP/tc | | - socket filter | +
|
三、eBPF 常用挂钩点
类型 |
说明 |
场景 |
Socket Filter |
套接字层数据包过滤 |
tcpdump、网络分析 |
kprobe/kretprobe |
内核函数入口/返回点插桩 |
系统调用跟踪 |
tracepoint |
预定义内核事件 |
性能分析 |
tc BPF |
网络流量控制 |
限速、QoS |
XDP |
网络驱动最底层数据包处理 |
DDoS 防护 |
四、eBPF 实战:追踪 TCP 连接
我们做一个小实验:当进程建立 TCP
连接时,打印 PID
、目标 IP
、端口。
1. 内核态 eBPF 程序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| #include "vmlinux.h" #include <bpf/bpf_helpers.h> #include <bpf/bpf_endian.h>
char LICENSE[] SEC("license") = "GPL";
SEC("kprobe/tcp_v4_connect") int tcp_connect(struct pt_regs *ctx) { struct sock *sk = (struct sock *)PT_REGS_PARM1(ctx); u32 pid = bpf_get_current_pid_tgid() >> 32;
__be32 daddr = 0; __be16 dport = 0;
bpf_probe_read_kernel(&daddr, sizeof(daddr), &sk->__sk_common.skc_daddr); bpf_probe_read_kernel(&dport, sizeof(dport), &sk->__sk_common.skc_dport);
bpf_printk("PID %d -> %pI4:%d\n", pid, &daddr, bpf_ntohs(dport)); return 0; }
|
2. 用户态加载器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| #include <stdio.h> #include <bpf/libbpf.h> #include <unistd.h>
static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) { return vfprintf(stderr, format, args); }
int main() { struct bpf_object *obj; struct bpf_program *prog; struct bpf_link *link; int err;
libbpf_set_print(libbpf_print_fn);
obj = bpf_object__open_file("tcpconnect.bpf.o", NULL); if (libbpf_get_error(obj)) { fprintf(stderr, "打开 eBPF 对象失败\n"); return 1; }
err = bpf_object__load(obj); if (err) { fprintf(stderr, "加载 eBPF 程序失败\n"); return 1; }
prog = bpf_object__find_program_by_name(obj, "tcp_connect"); if (!prog) { fprintf(stderr, "找不到 eBPF 程序\n"); return 1; }
link = bpf_program__attach_kprobe(prog, false, "tcp_v4_connect"); if (libbpf_get_error(link)) { fprintf(stderr, "附加 kprobe 失败\n"); return 1; }
printf("成功加载并附加 eBPF 程序。按 Ctrl+C 停止\n"); while (1) { sleep(1); }
bpf_link__destroy(link); bpf_object__close(obj); return 0; }
|
3. 编译运行
1 2 3 4 5 6 7 8
| clang -O2 -g -target bpf -D__TARGET_ARCH_x86 -c tcpconnect.bpf.c -o tcpconnect.bpf.o
gcc loader.c -o loader -lbpf
sudo ./loader
|
4. 查看日志
1
| sudo cat /sys/kernel/debug/tracing/trace_pipe
|
示例输出:
1 2
| PID 1234 -> 93.184.216.34:80 PID 5678 -> 142.250.72.238:443
|
五、eBPF 的实际应用
性能分析
网络安全
故障排查
容器可观测性
六、总结
eBPF 已经成为 Linux 内核可观测性和网络安全的核心技术。
它的强大在于:
对于开发者和运维工程师来说,掌握 eBPF
,不仅可以提高故障排查效率,还能为安全和性能优化带来新的可能性。