在挂载kprobe的eBPF程序中读取非线性skb及线性化方法问询
一、kprobe场景下非线性skb的线性化方法
在挂载kprobe的eBPF程序中,有两种常见方式处理非线性skb:
使用
bpf_skb_linearize辅助函数
这是最直接的线性化方式,该函数会尝试将非线性skb的所有分片合并到线性区域中,返回0表示成功。注意需要确保skb具备写权限(比如在允许修改skb的内核路径上挂载kprobe),内核版本需4.18及以上支持。示例代码:if (bpf_skb_linearize(skb, 0) != 0) { // 线性化失败,可选择跳过或记录错误 return 0; } // 此时skb已线性化,可直接访问其数据区域手动遍历skb分片
如果无法获取skb的写权限,或者bpf_skb_linearize调用失败,可以手动遍历skb的分片结构读取数据。通过skb_shinfo(skb)->frags获取分片数组,逐个处理每个分片的地址和长度,使用bpf_probe_read_kernel(内核空间数据)或bpf_probe_read_user(用户空间数据)安全读取。示例代码:struct skb_shared_info *shinfo = skb_shinfo(skb); for (int i = 0; i < shinfo->nr_frags; i++) { struct skb_frag_struct *frag = &shinfo->frags[i]; void *frag_addr = skb_frag_address(frag); __u32 frag_len = skb_frag_size(frag); // 读取分片数据到本地缓冲区(示例读取前64字节) __u8 buf[64]; if (frag_len > sizeof(buf)) frag_len = sizeof(buf); bpf_probe_read_kernel(buf, frag_len, frag_addr); // 此处处理读取到的buf数据 }
二、Tracepoint中无bpf_skb_pull_data时的数据读取方法
bpf_skb_pull_data仅在XDP、TC等网络钩子中可用,Tracepoint场景下可通过以下方式读取skb数据:
使用
bpf_skb_load_bytes辅助函数
该函数会自动处理skb的非线性结构,从指定偏移读取指定长度的数据到缓冲区,无需关心分片细节。使用前需通过skb->len判断数据长度是否足够,避免越界。示例代码:// 读取以太网头部(前14字节) __u8 eth_hdr[14]; if (skb->len < sizeof(eth_hdr) || bpf_skb_load_bytes(skb, 0, eth_hdr, sizeof(eth_hdr)) != 0) { // 长度不足或读取失败,跳过处理 return 0; } // 处理以太网头部数据手动遍历分片(同kprobe场景)
如果bpf_skb_load_bytes无法满足复杂的读取需求(比如需要跨分片读取大段数据),可以复用kprobe场景中的分片遍历方法,逐个读取每个分片的数据并拼接处理。使用场景特定辅助函数
如果是读取特定协议或隧道相关的数据,可使用内核提供的专用辅助函数,比如bpf_skb_get_tunnel_key读取隧道密钥信息,避免直接处理原始skb数据。
内容的提问来源于stack exchange,提问作者still_learning

