如何使用助手 bpf_skb_load_bytes() 读取任意 len 字节?

问题描述 投票:0回答:1

背景

我目前正在尝试构建一个eBPF程序来读取通过两个进程传输的应用程序数据(任意长度)的内容。

我尝试使用 Linux TC 拦截数据包(就像此存储库的示例 tc.bpf.c 中所做的那样:https://github.com/libbpf/libbpf-bootstrap) 还尝试使用套接字过滤器拦截数据包(就像此存储库的示例 sockfilter.bpf.c 中所做的那样:https://github.com/libbpf/libbpf-bootstrap

问题

从 bpf_skb_load_bytes() 读取时,我需要提供一个参数 len (最后一个参数)。 给定可变长度消息,该消息可能小于 MAX_BUF_SIZE,为了 bpf_skb_load_bytes() 不返回错误,我需要指定应读取的有效负载的确切大小。

问题

任何人都可以帮助我了解如何读取数据包有效负载中的字节数吗? 向 bpf_skb_load_bytes 传递任意 len 参数,计算为这些字节的确切数量?

尝试1解决问题

如果是 MAX_BUF_SIZE 并且有效负载大小恰好小于 MAX_BUF_SIZE,则 bpf_skb_load_bytes 返回错误 -14;并且没有读取任何信息。 (我还在这个存储库上使用 sockfilter 示例进行了尝试:https://github.com/libbpf/libbpf-bootstrap,如果仅具有整数的有效负载被拦截,这正是发生的情况)。

这让我们需要计算有效负载的大小,以便从有效负载中读取正确的字节数。

尝试2解决问题

如果我尝试从 skb->len -total_hlen 计算有效负载大小,我的验证程序会出现问题,该验证程序指出有效负载_len 的最小值为负。 Total_hlen 是 eth 标头 (eth_hlen)、IP 标头 (ip_hlen) 和传输标头 (tp_hlen) 使用的总大小(以字节为单位)。 下面的程序示例:

    __u32 eth_hlen, ip_hlen, tp_hlen, data_len, total_hlen, payload_len;
    char payload[MAX_PAYLOAD_SIZE];
    ...
    total_hlen = eth_hlen + ip_hlen + tp_hlen;

    if (skb->len > total_hlen) {
        payload_len = skb->len - total_hlen;
        if (payload_len > sizeof(payload)) payload_len = sizeof(payload);

        long err = bpf_skb_load_bytes(skb, total_hlen, payload, payload_len);
        if (!err) {
            bpf_printk("New packet -> skb_len: %d, data_len: %d | Total_Hlen: %d ETH_Hlen: %d, IP_Hlen: %d, TP_Hlen: %d | payload_len: %d, payload: %s",
                skb->len, data_len, total_hlen, eth_hlen, ip_hlen, tp_hlen, payload_len, payload);
        } else bpf_printk("%d\n", err);
    }
c operating-system ebpf bpf libbpf
1个回答
0
投票

TL;DR. 验证者无法推断

payload_len
永远不会为负(以其签名表示形式)。您可能可以通过再一次检查来解决这个问题,告诉它
payload_len
是一个正值


解释

验证者无法追踪的代码是:

if (skb->len > total_hlen) {
    payload_len = skb->len - total_hlen;

验证者不能保持变量之间的约束,例如

a > b
,除了
skb->data_end
特殊情况。相反,验证者仅持有更简单的约束,例如
a > 0

验证器的限制意味着当它到达第二行时,它不记得

skb->len > total_hlen
。因此,它无法计算
payload_len
的正确最小界限,并最终认为负值是可能的。


建议的解决方案

__u32 eth_hlen, ip_hlen, tp_hlen, data_len, total_hlen;
__s32 payload_len;
char payload[MAX_PAYLOAD_SIZE];
...
total_hlen = eth_hlen + ip_hlen + tp_hlen;

if (skb->len > total_hlen) {
    payload_len = skb->len - total_hlen;
    if (payload_len > sizeof(payload)) payload_len = sizeof(payload);
    
    /* NEW CHECK */
    if (payload_len <= 0) return;

    long err = bpf_skb_load_bytes(skb, total_hlen, payload, payload_len);
    if (!err) {
        bpf_printk("New packet -> skb_len: %d, data_len: %d | Total_Hlen: %d ETH_Hlen: %d, IP_Hlen: %d, TP_Hlen: %d | payload_len: %d, payload: %s",
            skb->len, data_len, total_hlen, eth_hlen, ip_hlen, tp_hlen, payload_len, payload);
    } else bpf_printk("%d\n", err);
}

注意,我已对

payload_len
进行了签名,因此您在致电
bpf_skb_load_bytes
时可能需要投射它。

© www.soinside.com 2019 - 2024. All rights reserved.