You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在eBPF/libbpf中获取完整skb数据包内容而非地址?

解决libbpf中bpf_probe_read_kernel读取skb数据包内容的问题

你遇到的问题核心是BPF验证器的内存访问限制:skb->data是指向内核空间的指针,BPF程序处于沙箱环境,不能直接访问该指针指向的地址,直接传skb->data会触发验证器的内存访问错误;而&skb->data是取指针变量本身的地址,读取到的只是指针的值(数据包的内核地址),不是实际内容。

正确的做法是分两步操作:

  1. 先读取skb->data的指针值
    用bpf_probe_read_kernel读取skb->data成员的实际值(即数据包所在的内核地址),存到临时变量中:

    void *skb_data;
    bpf_probe_read_kernel(&skb_data, sizeof(skb_data), &skb->data);
    

    这里&skb->data是struct sk_buff结构体成员的地址,属于BPF程序允许访问的范围,不会触发验证器错误。

  2. 用读取到的指针值获取数据包内容
    以第一步得到的skb_data作为源地址,再次调用bpf_probe_read_kernel读取实际的数据包内容到你的结构体中:

    bpf_probe_read_kernel(my_struct, 512, skb_data);
    

完整示例代码

#include <linux/skbuff.h>
#include <bpf/bpf_helpers.h>
#include <linux/if_ether.h>

// 自定义数据包结构体,按需定义字段
struct my_packet {
    __u8  eth_dst[ETH_ALEN];
    __u8  eth_src[ETH_ALEN];
    __u16 eth_type;
    // 更多字段...
};

SEC("kprobe/ip_rcv")
int BPF_KPROBE(ip_rcv, struct sk_buff *skb)
{
    struct my_packet pkt = {};
    void *skb_data;
    __u32 skb_len;

    // 读取skb->data的指针值
    bpf_probe_read_kernel(&skb_data, sizeof(skb_data), &skb->data);
    // 读取数据包实际长度,避免越界读取
    bpf_probe_read_kernel(&skb_len, sizeof(skb_len), &skb->len);

    // 计算安全读取长度:取数据包实际长度和结构体大小的较小值
    __u32 read_len = (skb_len > sizeof(pkt)) ? sizeof(pkt) : skb_len;
    // 读取数据包内容
    bpf_probe_read_kernel(&pkt, read_len, skb_data);

    // 后续处理,比如打印以太网类型
    bpf_printk("ETH type: %u", ntohs(pkt.eth_type));

    return 0;
}

char _license[] SEC("license") = "GPL";

额外注意事项

  • 确保my_struct的内存合法(如栈上分配或bpf_map分配的内存),避免非法内存访问。
  • 结合skb->len控制读取长度,既符合BPF验证器的安全要求,也能避免读取超出数据包范围的无效内存。
  • 若内核版本在5.10及以上,可考虑使用更高效的bpf_copy_from_user类函数,但核心逻辑一致:先获取合法的内核地址,再通过安全读取函数获取内容。

内容的提问来源于stack exchange,提问作者Vic

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 06:10:19