eBPF入门系列:学习资料及概念
学习资料
https://www.ebpf.top/
https://github.com/DavadDi/bpf_study
https://github.com/nevermosby/linux-bpf-learning
https://davidlovezoe.club/wordpress/archives/tag/bpf
https://github.com/Asphaltt/learn-by-example
cilium文档:BPF和XDP参考指南
iovisor 非官方BPF规范
XDP教程:https://github.com/xdp-project
eBPF和GO: https://networkop.co.uk/post/2021-03-ebpf-intro/
eBPF介绍
使用LLVM/clang编译bpf程序为bpf字节码;
可以通过bcc/bpftrace开发bpf程序;
BPF Maps
BPF Maps可以被内核和用户空间同时访问,可用于用户态程序读取内核的数据,也可以从用户态向内核态bpf程序传配置;
源码定义:https://elixir.bootlin.com/linux/v5.15.86/source/include/uapi/linux/bpf.h#L878
内核文档定义:https://docs.kernel.org/bpf/maps.html
BPF Program
bpf程序类型:kprobe(性能低、灵活性高)、tracepoints(性能高、灵活性低)
helper函数:https://man7.org/linux/man-pages/man7/bpf-helpers.7.html
bpf系统调用函数:https://man7.org/linux/man-pages/man2/bpf.2.html
编译
注意点
1、clang编译器可以通过 -g 参数生成CO-RE需要的内核数据结构BTF文件;
2、gcc 12版本编译 BPF 程序支持 CO-RE 特性;
检查 eBPF 对象文件
$ file hello.bpf.o
hello.bpf.o: ELF 64-bit LSB relocatable, eBPF, version 1 (SYSV), with debug_info, not stripped
$ llvm-objdump -S hello.bpf.o
hello.bpf.o: file format elf64-bpf
Disassembly of section xdp:
0000000000000000 <hello>:
; bpf_printk("Hello World %d", counter");
0: 18 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 r6 = 0
ll
2: 61 63 00 00 00 00 00 00 r3 = *(u32 *)(r6 + 0)
3: 18 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 r1 = 0
ll
5: b7 02 00 00 0f 00 00 00 r2 = 15
6: 85 00 00 00 06 00 00 00 call 6
; counter++;
7: 61 61 00 00 00 00 00 00 r1 = *(u32 *)(r6 + 0)
8: 07 01 00 00 01 00 00 00 r1 += 1
9: 63 16 00 00 00 00 00 00 *(u32 *)(r6 + 0) = r1
; return XDP_PASS;
10: b7 00 00 00 02 00 00 00 r0 = 2
11: 95 00 00 00 00 00 00 00 exit
加载并挂载程序
// xdp类型程序
$ ip link set dev eth0 xdp obj hello.bpf.o sec xdp
$ ip link set dev eth0 xdp off
BPF程序和挂载类型
BPF程序类型、挂载类型和ELF Sections 对应关系 libbpf 文档
Tracing(跟踪类型)
Kprobes 和 Kretprobes
kprobes 用于挂载到内核函数入口点,也可以通过指令挂载到入口点偏移量位置(不稳定);kretprobes 用于挂载到内核函数退出点。
bpf程序参数类型根据挂载点类型而不同,函数第一个参数为函数名。
# 挂载kprobe程序到execve系统调用入口 SEC("ksyscall/execve") int BPF_KPROBE_SYSCALL(kprobe_sys_execve, char *pathname) # 挂载kprobe程序到内核函数do_execve入口 SEC("kprobe/do_execve") int BPF_KPROBE(kprobe_do_execve, struct filename *filename)
Fentry 和 Fexit
内核版本5.5引入的更高效的挂载到内核函数入口点和出口点的程序类型。
kprobe类型程序代码可以无缝迁移到fentry。
fexit类型程序还可以访问函数入参,而kretprobes不能
# 挂载kentry程序到内核函数do_execve入口 SEC("fentry/do_execve") int BPF_PROG(fentry_execve, struct filename *filename) # kretprobe和fexit挂载相同内核函数do_unlinkat,fexit可以拿到函数入参 SEC("kretprobe/do_unlinkat") int BPF_KRETPROBE(do_unlinkat_exit, long ret) SEC("fexit/do_unlinkat") int BPF_PROG(do_unlinkat_exit, int dfd, struct filename *name, long ret)
Tracepoints
tracepoints 是跟踪内核中被标记的代码位置。tracepoints在不同内核版本相对稳定
查看当前版本内核可用的子系统跟踪点通过文件 /sys/kernel/tracing/available_events。
查看跟踪点函数参数方式,以execve函数入口点系统调用为例:
cat /sys/kernel/tracing/events/syscalls/sys_enter_execve/format
。Raw_tracepoints类型程序相对于tracepoints可以获得更好的性能,通过修改 raw_tp/raw_tracepoint代替tp。
# tracepoints类型程序SEC配置格式 SEC("tp/tracingsubsystem/tracepoint name") SEC("tp/syscalls/sys_enter_execve")
BTF-Enabled Tracepoints
启动BTD的tracepoints,解决内核数据接口在不同版本定义发生变化问题。
# SEC定义格式: SEC("tp_btf/*tracepoint name*") # 示例 SEC("tp_btf/sched_process_exec") int handle_exec(struct trace_event_raw_sched_process_exec *ctx)
函数第一个参数结构体名字格式:trace_event_raw_+跟踪点名
Uprobes and Uretprobes
uprobes和uretprobes挂载用户空间函数入口点和退出点,USDTs (user statically defined tracepoints)挂载特殊的tracepoints到用户空间的应用代码/库文件。
复用 kprobes 程序类型定义。
SEC("uprobe/usr/lib/aarch64-linux-gnu/libssl.so.3/SSL_write")
在检测⽤⼾空间代码时需要注意⼀些问题:...
LSM
BPF_PROG_TYPE_LSM类型程序用于挂载到Linux安全模块API,
Networking
需要的Linux capabilities: CAP_NET_ADMIN, CAP_BPF, CAP_SYS_ADMIN。
BPF程序类型在网络协议栈的钩子

使用场景:
1、通过 bpf 程序告诉内核如何处理数据包:重定向、丢弃、正常处理;
2、修改数据包或者socket参数;
Sockets
程序类型:
BPF_PROG_TYPE_SOCKET_FILTER:过滤复制到可观察工具(tcpdump)sockets。
BPF_PROG_TYPE_SOCK_OPS:允许拦截或者操作socket,例如设置socket各种参数
BPF_PROG_TYPE_SK_SKB:与sockmap一起使用,用于重定向socket流量
Traffic Control
eBPF程序可以挂载到自定义的filters或者classifiers,出入向都可以。
使用tc命令操作ebpf 程序
# 创建 clsact 类型的排队规则 sudo tc qdisc add dev eth0 clsact # 加载接收方向的 eBPF 程序 sudo tc filter add dev eth0 ingress bpf da obj tc-example.o sec ingress # 加载发送方向的 eBPF 程序 sudo tc filter add dev eth0 egress bpf da obj tc-example.o sec egress # 查看eBPF程序 tc filter show dev lxc17451654ad87 ingress tc filter show dev lxc17451654ad87 egress
XDP
可以使用ip link命令操作XDP程序
只能作用于ingress方向数据包,无法针对egress方向数据包
Flow Dissector(流解剖器)
BPF_PROG_TYPE_FLOW_DISSECTOR: 可以自定义解剖数据包
Lightweight Tunnels
BPF_PROG_TYPE_LWT_* 程序类型 : 实现数据包封装
Cgroups
用于控制一个Cgroup中的进程组的行为,比如控制一个Cgroup的socket是否被请求或者传输
程序类型:
BPF_CGROUP_SYSCTL:控制sysctl命令
BPF_PROG_TYPE_CGROUP_SOCK :
BPF_PROG_TYPE_CGROUP_SKB
Infrared Controllers
环境依赖
BTF(BPF Type Format)
是BPF程序一次编译随处运行(CO-RE:Compile Once-Run Everywhere)的关键,解决了 eBPF 程序在不同内核版本之间可移植的问题.
# cat /boot/config-`uname -r` | grep CONFIG_DEBUG_INFO_BTF
CONFIG_DEBUG_INFO_BTF=y
Last updated