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

eBPF tracepoint net_dev_queue无法获取正确IP地址求助

问题描述

我编写了一段eBPF代码,用于计算数据包在net_dev_queue与net_dev_xmit两个tracepoint之间的时间差。为生成流键,我需要通过trace_event_raw_net_dev_template结构体提取IP地址,但提取出的IP始终为0.0.0.0。以下是我使用的代码,请问我遗漏了什么?

struct flow_key
{
    __be32 src_ip;
    __be32 dst_ip;
    __u16 src_port;
    __u16 dst_port;
    __u8 protocol;
};

static __always_inline int parse_flow_key(struct sk_buff *skb, struct flow_key *key)
{
    struct iphdr iph;
    bpf_core_read(&iph, sizeof(iph), skb->head + skb->network_header);



    if (skb->protocol != bpf_htons(ETH_P_IP)) {
        // This is not an IP packet
        bpf_printk("Not an IP packet 0x%xn", skb->protocol);
        return -1;
    }

    key->src_ip = iph.saddr;
    key->dst_ip = iph.daddr;
    key->protocol = iph.protocol;
    bpf_printk(" %pI4 -> %pI4 ", iph.saddr, iph.daddr);
 

// More code 
}

SEC("tracepoint/net/net_dev_queue")
int tp_ingress_kernel(struct trace_event_raw_net_dev_template *ctx)
{
    struct sk_buff skb;
    bpf_probe_read(&skb, sizeof(skb), ctx->skbaddr);
    struct flow_key key = {};

    if (parse_flow_key(&skb, &key) < 0)
        return 0;
    bpf_printk("Packet received in kernel\n");

    return 0;
}
问题分析与解决

你的代码存在三个核心问题,导致无法正确提取IP地址:

  1. 错误拷贝整个struct sk_buff
    struct sk_buff是内核中体积庞大的结构体,直接用bpf_probe_read拷贝整个对象不仅低效,还会导致指针类型字段(如head)在eBPF程序中无法指向有效内存,后续读取IP头自然会得到无效值。正确做法是只读取需要的字段,而非整个结构体。

  2. IP协议检查顺序颠倒
    你先读取IP头,再判断数据包是否为IP类型。如果是非IP包,此时读取的IP头内容完全无效,甚至可能触发非法内存访问。应该先校验协议类型,再读取对应协议的头部数据。

  3. 直接访问skb->head指针无效
    skb->head是内核空间的内存指针,在eBPF程序中不能直接通过skb->head + skb->network_header计算IP头地址,必须使用eBPF提供的安全helper函数读取数据。

修改后的代码示例:

struct flow_key
{
    __be32 src_ip;
    __be32 dst_ip;
    __u16 src_port;
    __u16 dst_port;
    __u8 protocol;
};

static __always_inline int parse_flow_key(struct trace_event_raw_net_dev_template *ctx, struct flow_key *key)
{
    __be16 protocol;
    // 仅读取skb的protocol字段,无需拷贝整个结构体
    bpf_core_read(&protocol, sizeof(protocol), &ctx->skb->protocol);
    
    if (protocol != bpf_htons(ETH_P_IP)) {
        bpf_printk("Not an IP packet 0x%x\n", protocol);
        return -1;
    }

    struct iphdr iph;
    // 使用bpf_skb_load_bytes安全读取IP头数据
    if (bpf_skb_load_bytes(ctx->skb, ctx->skb->network_header, &iph, sizeof(iph)) != 0) {
        bpf_printk("Failed to read IP header\n");
        return -1;
    }

    key->src_ip = iph.saddr;
    key->dst_ip = iph.daddr;
    key->protocol = iph.protocol;
    // %pI4需要传入IP地址的指针,而非直接传入整数
    bpf_printk(" %pI4 -> %pI4 ", &iph.saddr, &iph.daddr);

    // 后续端口解析代码...
    return 0;
}

SEC("tracepoint/net/net_dev_queue")
int tp_ingress_kernel(struct trace_event_raw_net_dev_template *ctx)
{
    struct flow_key key = {};

    if (parse_flow_key(ctx, &key) < 0)
        return 0;
    bpf_printk("Packet received in kernel\n");

    return 0;
}

额外注意:

  • 访问ctx->skb的字段时必须用bpf_core_read,因为trace_event_raw_net_dev_template中的skb是内核结构体指针,需要安全读取。
  • bpf_skb_load_bytes是eBPF专门提供的skb数据读取helper,比直接操作指针更可靠。
  • 使用%pI4打印IP地址时,要传入地址指针,不能直接传__be32类型的整数。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 17:57:10