我目前正在尝试构建一个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 参数,计算为这些字节的确切数量?
如果是 MAX_BUF_SIZE 并且有效负载大小恰好小于 MAX_BUF_SIZE,则 bpf_skb_load_bytes 返回错误 -14;并且没有读取任何信息。 (我还在这个存储库上使用 sockfilter 示例进行了尝试:https://github.com/libbpf/libbpf-bootstrap,如果仅具有整数的有效负载被拦截,这正是发生的情况)。
这让我们需要计算有效负载的大小,以便从有效负载中读取正确的字节数。
如果我尝试从 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);
}
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
时可能需要投射它。