5.4.0内核eBPF挂载consume_skb tracepoint无法访问__sk_buff
Linux 5.4.0 内核下 consume_skb tracepoint 访问 __sk_buff 失败问题解决方案
两类加载错误的根本原因
- 直接指针访问返回
Permission denied(-13)、校验器报R2 invalid mem access 'inv':
tracepoint类eBPF程序的上下文并非适配过eBPF校验规则的struct __sk_buff,而是对应tracepoint事件专属的原始结构体。tracepoint/skb/consume_skb触发时传入的上下文为struct trace_event_raw_consume_skb,仅存储内核原生struct sk_buff的地址、释放回调入口等基础字段,不支持直接读取data/data_end这类包数据指针。直接将上下文强转为__sk_buff做指针访问,会被校验器判定为非法内存访问。
仅TC、XDP、Socket Filter等原生以skb为运行上下文的eBPF程序类型,才支持直接通过__sk_buff的data/data_end指针做零拷贝包数据解析,tracepoint程序无该权限。 - 调用
bpf_skb_load_bytes返回Invalid argument(-22)、校验器报unknown func bpf_skb_load_bytes#26:
eBPF辅助函数的调用权限与程序类型强绑定,不存在跨所有程序类型通用的helper。bpf_skb_load_bytes(功能号26)仅开放给TC、Socket Filter、流解析、sk_skb类程序调用,5.4版本内核未将该helper加入tracepoint类程序的允许调用列表,校验器会直接拦截未授权的helper调用。
5.4版本正确读取skb内容的实现方法
tracepoint场景下读取skb数据无需依赖skb专属helper,按以下步骤实现即可:
- 从tracepoint上下文提取原生sk_buff指针
tracepoint/skb/consume_skb上下文的第一个字段即为struct sk_buff *skbaddr,直接读取该字段获取内核态原生skb结构的地址,禁止将整个上下文强转为__sk_buff类型。 - 通过通用probe helper读取包内容
5.4版本tracepoint程序已支持调用bpf_probe_read_kernel(旧版本libbpf可兼容使用bpf_probe_read),按结构体偏移逐层读取skb字段和包数据即可,参考实现代码如下:
// 引入必要头文件,tracepoint结构体定义可通过bpftool自动生成 SEC("tracepoint/skb/consume_skb") int trace_consume_skb(struct trace_event_raw_consume_skb *ctx) { struct sk_buff *native_skb = (struct sk_buff *)ctx->skbaddr; struct ethhdr eth_hdr; unsigned char *skb_data_ptr; __u16 eth_proto; // 先从原生skb结构中读取data指针 bpf_probe_read_kernel(&skb_data_ptr, sizeof(skb_data_ptr), &native_skb->data); // 从data指针指向的线性数据区读取以太网头 bpf_probe_read_kernel(ð_hdr, sizeof(eth_hdr), skb_data_ptr); // 转换字节序获取以太网协议类型 eth_proto = __bpf_ntohs(eth_hdr.h_proto); // 后续解析IP头、传输层头时,先读取skb->len校验偏移合法性,再通过bpf_probe_read_kernel读取对应内容即可 bpf_printk("consumed skb, ethernet protocol: 0x%x\n", eth_proto); return 0; }
实现注意事项
- 禁止直接对
bpf_probe_read_kernel读出的skb_data_ptr做直接解引用操作,tracepoint上下文下校验器不会对未经过helper校验的内核指针放行,所有内核内存读取必须走probe类helper。 - 若解析非线性格式的skb(存在frag_list/paged分片),需要先读取
skb->data_len判断分片偏移,再对应读取分片结构中的数据,仅读取skb线性区数据时无需处理该逻辑。 - 5.18及以上内核版本提供了
bpf_cast_to_kern_ctx、bpf_skb_pull_data等适配tracepoint场景的skb操作helper,但5.4版本无相关支持,使用probe_read是兼容性最高的方案。
内容的提问来源于stack exchange,提问作者marxlaml
相关产品推荐
相关产品推荐

