nevermosby / linux-bpf-learning

learn how to use BPF/eBPF
MIT License
488 stars 96 forks source link

eBPF TC 程序获取 payload #13

Open cheneytianx opened 2 years ago

cheneytianx commented 2 years ago

大佬你好,

不知道你有没有尝试过在 tc 程序中获取网络数据包的 payload。

一般根据 skb 的长度和各个协议包头的长度可以计算出有效的 payload 长度,现在我想利用 bpf_skb_load_bytes() 这个函数将 payload 拷贝到自己定义的 buffer 中,想着后续匹配下关键词什么的。

结果一直绕不过 eBPF verifier 的校验,不知道大佬是否有相关经验可以分享?

nevermosby commented 2 years ago

Hello @cheneytianx,一看便知是高端玩家。几个问题先确认下: 1、你的eBPF代码是怎样的?verifier报什么错? 2、当前获取skb数据,可以直接"direct packet access",即skb->dataskb->data_end,这个方式尝试过了吗?

cheneytianx commented 2 years ago

这大概是一个数据包的结构信息。

skb->data  -->[ ethhdr | iphdr | tcphdr | payload ]  <-- skb->data_end

首先解析各种协议包头,计算 payload 的 offset 和 长度 data_len:

u16 offset = sizeof(*eth) + sizeof(*iph) + (tcph->doff << 2);
u32 data_len = skb->len - offset;

然后想使用 bpf_skb_load_bytes() 一次性把 payload 拷到一个 buffer 中:

// 这里做过一些逻辑判断,确保 data_len < MAX_LEN,offset + data_len < skb->data_end
char buf[MAX_LEN] = {0};
bpf_skb_load_bytes(skb, offset, buf, data_len);

于是验证器报错:

85: (85) call bpf_skb_load_bytes#26
invalid stack type R3 off=-136 access_size=0

在这里找到一些相关资料:link

image

上面验证器报的是 R3 的错,不过把 data_len(R4) 换成一个字面值常量程序就正常了。

可能,第四个参数就是没办法传一个变量吧。


感谢大佬,看您的博客学到了不少 >_<

zaoying commented 1 year ago

我成功把tcp payload打印到std,tls协议还得解密。

let start = Ipv4Hdr::LEN + TcpHdr::LEN;
        let end = ctx.len() as usize;
        if end - start <= 0 {
            info!(&ctx, "no payload");
            return Ok(1);
        }

有个地方需要注意:根据我观察,只有XDP程序才需要把offset加上eth的报文长度,其他钩子例如cgroups skb,只需要ipv4 + tcp 就足够了