consume_skb tracepoint中读取sk_buff全为0的异常问题咨询
问题背景
本问题为此前已删除提问的后续跟进:eBPF开发过程中遇到读取到的sk_buff结构内容全为0的问题,对应代码运行时会两次打印0:一次是读取操作的成功返回值ret,另一次是读取到的data_len字段值。需要确认是结构体读取方式有误,还是对应tracepoint入口处sk_buff本身就未填充有效数据。
复现代码
SEC("tracepoint/skb/consume_skb") int handle_skb(struct sk_buff *skb) { unsigned int data_len = 1; long ret; ret = bpf_probe_read_kernel(&data_len, sizeof(data_len), &(skb->data_len)); if (ret < 0) { bpf_printk("Error on probe read.\n"); } bpf_printk("ret: %d \n", ret); bpf_printk("len: %u \n", data_len); return 0; }
上述代码可稳定复现问题,尝试多种配置调整后结果均无变化。
补充测试现象
- 在
kfree_skbtracepoint做同类测试,存在完全相同的全0问题 - 打印tracepoint自带的
protocol字段时得到返回值1,不符合预期 - 初步怀疑问题出在环境配置而非代码逻辑本身
根因与修复方案
核心错误是对tracepoint传参规则理解有误:tracepoint/skb/consume_skb这类tracepoint的入口参数不是直接的sk_buff *指针,而是该tracepoint专属的上下文结构体,直接把入参强转成struct sk_buff *读取必然拿到错误数据。
按以下步骤修复即可:
- 确认目标tracepoint的实际入参结构:执行命令
sudo cat /sys/kernel/debug/tracing/events/skb/consume_skb/format查看该tracepoint的字段定义,第一个字段为void * skbaddr,存储的才是真正的sk_buff结构地址,上下文本身不是skb指针。 - 修正代码逻辑:先从tracepoint上下文中取出
skbaddr的值作为真实skb指针,再对这个地址调用bpf_probe_read_kernel读取sk_buff的对应字段,参考修正代码如下:// 定义对应tracepoint的上下文结构,未使用的字段无需定义,BPF按偏移读取字段 struct consume_skb_ctx { void *skbaddr; }; SEC("tracepoint/skb/consume_skb") int handle_skb(struct consume_skb_ctx *ctx) { unsigned int data_len = 1; long ret; struct sk_buff *skb = ctx->skbaddr; // 取出真实的sk_buff地址 ret = bpf_probe_read_kernel(&data_len, sizeof(data_len), &skb->data_len); if (ret < 0) { bpf_printk("Error on probe read.\n"); } bpf_printk("ret: %d \n", ret); bpf_printk("len: %u \n", data_len); return 0; } - 之前读取
protocol字段返回1的问题也是同一原因:直接读取上下文的内存偏移错误,拿到的不是真实的protocol字段值。kfree_skbtracepoint的处理逻辑完全一致,同样先从上下文取skbaddr字段再操作即可。
注意:不同内核版本的tracepoint字段偏移可能存在差异,优先以当前系统
/sys/kernel/debug/tracing/events/路径下的format文件输出为准,不要直接照搬其他版本的结构体定义。
内容的提问来源于stack exchange,提问作者marxlaml
相关产品推荐
相关产品推荐

