直接用libbpf-go而非自己封装系统调用,因其已封装libbpf c库,复用内核兼容逻辑,避免手动处理btf、co-re、map fd传递及perf buffer解析等易错细节。

为什么直接用 libbpf-go 而不是自己封装系统调用
Go 原生不支持 eBPF 程序加载和 map 交互,硬写 syscall 容易出错,且要手动处理 BTF、CO-RE、map fd 传递、perf event ring buffer 解析等细节。社区成熟的 libbpf-go 封装了 libbpf C 库,能复用内核兼容逻辑,避免重复踩坑。
常见错误现象:failed to load program: permission denied(没开 CAP_SYS_ADMIN 或 seccomp 限制)、invalid argument(BPF 指令校验失败,常因 Go struct 字段对齐与内核不一致)。
- 必须用
go build -buildmode=c-shared或确保运行时有libbpf.so可用(Ubuntu/Debian 安装libbpf-dev) - struct 字段需显式加
//go:binary-only-package注释并使用binary.Read+unsafe.Offsetof避免 padding 差异 - 推荐从
libbpf-go/examples的tcplife或tcprtt示例起步,它们已处理 perf buffer 消费和事件解析
如何让 eBPF 程序捕获 TCP 连接建立事件
eBPF 不能直接 hook accept() 或 connect() 的返回路径(因涉及用户栈),稳妥做法是 trace inet_csk_accept(服务端)和 tcp_v4_connect(客户端)内核函数。
使用场景:监控新建连接的源/目的 IP、端口、耗时,但要注意这些函数在不同内核版本符号可能变化(如 tcp_v6_connect 替代 tcp_v4_connect)。
- 用
bpf_program__attach_kprobe()绑定,attach_kprobe("tcp_v4_connect")即可 - 在 eBPF C 代码中用
bpf_get_current_comm()和bpf_get_current_pid_tgid()关联进程上下文 - 务必检查
ret == 0后再读取skb或sock参数,否则可能访问空指针导致程序被拒绝加载
如何安全地把网络事件从 eBPF map 传到 Go 用户态
别用 bpf_map_lookup_elem 轮询——性能差且容易丢事件。正确方式是用 perf buffer(perf_buffer)或 ring buffer(libbpf 1.0+ 推荐),由内核在事件触发时主动唤醒用户态。
性能影响:perf buffer 默认页大小为 4KB,高吞吐下建议调大(如 8 或 16 页),但过大会增加内存占用;ring buffer 更轻量,但需要 Go 端用 libbpf-go.PerfBuffer 的 Read 方法及时消费。
- Go 中初始化:
pb, _ := libbpf.NewPerfBuffer("events", func(data []byte) { /* 解析结构体 */ }) - eBPF C 端发送前必须用
bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &event, sizeof(event)) - 事件结构体字段顺序、对齐必须和 Go 中
unsafe.Sizeof计算结果严格一致,建议用github.com/cilium/ebpf/rlimit提升RLIMIT_MEMLOCK
为什么 tcplife 示例里总看不到短连接事件
因为 eBPF 程序默认只 attach 到当前命名空间的 socket,容器环境或非 root 用户启动时,netns 不匹配会导致事件丢失。另外,close() 路径比 connect() 复杂,需同时 trace tcp_close 和 inet_csk_destroy_sock 才能覆盖所有关闭场景。
- 确认是否在目标 netns 中运行:用
nsenter -t <pid> -n ./your-program</pid>进入容器 netns - 检查 eBPF 程序 attach 点是否被 kprobe 黑名单拦截(如某些云厂商内核禁用
tcp_close) - 短连接若在 eBPF 程序加载前就完成,事件自然不会被捕获——没有“回溯”能力,这是 eBPF 的根本限制
真正难的不是写通第一个包,而是让事件解析不出错、不丢、不卡死;尤其当结构体字段类型在不同内核版本间变动时,靠硬编码 offset 几乎必崩。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











