1

我是 XDP eBPF 的新手。我有一个旨在丢弃 UDP 数据包的 BPF 程序,但它无法加载,因为它被内核验证程序拒绝。下面是代码:

#include <linux/bpf.h>
#include <linux/icmp.h>
#include <linux/if_ether.h>
#include <linux/if_vlan.h>
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/udp.h>

#define SEC(NAME) __attribute__((section(NAME), used))
SEC("xdp")
int dropper(struct xdp_md *ctx) {

    unsigned int ipsize = 0;
    void *data = (void *)(unsigned long)ctx->data;
    void *data_end = (void *)(long)ctx->data_end;

    struct ethhdr *eth = data;
    ipsize = sizeof(*eth);
    struct iphdr *ip = (struct iphdr *)((char*)ctx->data + ipsize);
    if (ip->protocol == IPPROTO_UDP) {
             return XDP_DROP;
    }

    return XDP_PASS;
}

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

这是加载程序时出现的内容:

Prog section 'xdp' rejected: Permission denied (13)!
 - Type:         6
 - Instructions: 30 (0 over limit)
 - License:      GPL

Verifier analysis:

0: (7b) *(u64 *)(r10 -16) = r1
1: (b7) r1 = 0
2: (63) *(u32 *)(r10 -20) = r1
last_idx 2 first_idx 0
regs=2 stack=0 before 1: (b7) r1 = 0
3: (79) r1 = *(u64 *)(r10 -16)
4: (61) r1 = *(u32 *)(r1 +0)
5: (7b) *(u64 *)(r10 -32) = r1
6: (79) r1 = *(u64 *)(r10 -16)
7: (61) r1 = *(u32 *)(r1 +4)
8: (7b) *(u64 *)(r10 -40) = r1
9: (79) r1 = *(u64 *)(r10 -32)
10: (7b) *(u64 *)(r10 -48) = r1
11: (b7) r1 = 14
12: (63) *(u32 *)(r10 -20) = r1
13: (79) r1 = *(u64 *)(r10 -16)
14: (61) r1 = *(u32 *)(r1 +0)
15: (61) r2 = *(u32 *)(r10 -20)
16: (0f) r1 += r2
last_idx 16 first_idx 0
regs=4 stack=0 before 15: (61) r2 = *(u32 *)(r10 -20)
17: (7b) *(u64 *)(r10 -56) = r1
18: (79) r1 = *(u64 *)(r10 -56)
19: (71) r1 = *(u8 *)(r1 +9)
invalid access to packet, off=9 size=1, R1(id=1,off=0,r=0)
R1 offset is outside of the packet
processed 20 insns (limit 1000000) max_states_per_insn 0 total_states 0 peak_states 0 mark_read 0

Error fetching program/map!

我发现错误出现在 UDP 检查语句 if 上(ip->protocol == IPPROTO_UDP),因为当我评论检查时,它加载时没有错误。

所以,我需要知道的是,为什么内核验证程序拒绝 UDP 检查语句(如前所述),以及可以采取什么措施来解决它以解决错误。

谢谢。

4

1 回答 1

1

其中一些错误在 eBPF 的内核文档中有描述。在您的情况下,请特别参阅直接数据包访问部分。

在 Linux 内核中强制检查您的程序的内核验证程序确保不会尝试越界访问。您的程序被拒绝,因为它可能会触发这种越界访问。

您需要检查数据包的长度是否至少等于 IPv4 标头的长度,以便程序在读取时ip->protocol不会尝试加载超出数据包的边界。像这样的东西(未经测试):

/*
 * If packet is shorter than a IPv4 header, we cannot
 * dereference and read ip->protocol, and it cannot be
 * UDP anyway: pass the packet to the stack.
 *
 * "ip + 1" is the address at "ip + the length of one
 * additional struct iphdr", in other words, it points
 * to the byte right after the IP header.
 */
if (ip + 1 > data_end)
        return XDP_PASS;

if (ip->protocol == IPPROTO_UDP) { ...

请参阅此其他答案以获取更多详细信息。

于 2021-08-12T08:39:26.707 回答