使用 eBPF 实现数据包的克隆和(解封装)封装

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

我正在尝试创建一个 TC 程序,该程序将克隆数据包,用修改后的 L3 标头封装它,然后 将克隆发送到不同的主机(“监控主机”) - 我可以使用

bpf_skb_adjust_room
的组合来做到这一点与
bpf_clone_redirect
? 内核示例并未在此用例中透露太多细节(例如,here。) 我当前的尝试似乎正在改变原始数据包:

// Represents the redirect destination.
struct destination {
    __u32 destination_ip;
    __u8 destination_mac[ETH_ALEN];
};

// Contains the destination to redirect traffic to.
struct bpf_map_def SEC("maps") destinations = {
    .type        = BPF_MAP_TYPE_HASH,
    .key_size    = sizeof(__u32),
    .value_size  = sizeof(struct destination),
    .max_entries = 1,
    .map_flags   = BPF_F_NO_PREALLOC,
};

SEC("tc")
int tc_ingress(struct __sk_buff *skb) {
    __u32 key = 0;
    struct destination *dest = bpf_map_lookup_elem(&destinations, &key);

    if (dest != NULL) {
        void *data_end = (void *)(long)skb->data_end;
        void *data = (void *)(long)skb->data;
        // Necessary validation: if L3 layer does not exist, ignore and continue.
        if (data + sizeof(struct ethhdr) > data_end) {
            return TC_ACT_OK;
        }

        struct ethhdr *eth = data;
        struct iphdr encapsulate_iphdr = {};
        struct iphdr *original_iphdr = data + sizeof(struct ethhdr);
         if ((void*) original_iphdr + sizeof(struct iphdr) > data_end) {
            return TC_ACT_OK;
        }

        // Change the L2 destination to the provided MAC destination
        // and the source to the MAC addr of the recieving host.
        memcpy(&eth->h_source, &eth->h_dest, ETH_ALEN);
        memcpy(&eth->h_dest, dest->destination_mac, ETH_ALEN);

        // Change the L3 destination to the provided destination IP
        // and the source to the ip addr of the recieving host.
        memcpy(&encapsulate_iphdr.daddr, &dest->destination_ip, IPV4_ADDR_LEN);
        memcpy(&encapsulate_iphdr.saddr, &original_iphdr->daddr, IPV4_ADDR_LEN);

        // Adjust room for another iphdr after the L2 layer.
        if (bpf_skb_adjust_room(skb, sizeof(struct iphdr), BPF_ADJ_ROOM_NET, 0)) {
            return TC_ACT_OK;
        }

        // Store the headers at after L2 headers at the original headers offset.
        unsigned long offset = (unsigned long) original_iphdr;
        if (bpf_skb_store_bytes(skb, (int)offset, &encapsulate_iphdr, sizeof(struct iphdr), 0)) {
            return TC_ACT_OK;
        }

        // route back the to egress path.
        // Zero flag means that the socket buffer is
        // cloned to the iface egress path.
        bpf_clone_redirect(skb, skb->ifindex, 0);
    }
    return TC_ACT_OK;
}
networking network-programming ebpf bpf
1个回答
0
投票

我相信这在今天运行的同一个 BPF 程序中是不可能的,因为

bpf_clone_redirect
一旦调用克隆就会重定向它,并且没有克隆助手不会重定向。

但是,您可以通过再循环到同一接口来实现此目的。伪代码看起来像:

if (skb->mark == ORIGINAL_PACKET) {
    skb->mark = 0;
    return TC_ACT_OK;
}

skb->mark = ORIGINAL_PACKET;
bpf_clone_redirect(skb, skb->ifindex, BPF_F_INGRESS);
skb->mark = 0;

... implement changes ...

return bpf_redirect(skb, skb->ifindex, 0);
© www.soinside.com 2019 - 2024. All rights reserved.