eBPF:修改UDP负载并响应客户端

问题描述

我正在尝试修改从客户端接收到的 UDP 有效负载,以克隆和重定向数据包以响应它。交换MAC和IP地址已经完成,以及克隆,但我不知道如何修改UDP负载

我想将包含以下 3 个字节的负载发送回客户端:0x1a 0x31 0x0f,通过创建新的负载(更好)并在数据包中替换它或替换。我该怎么做?

这是我目前的代码

根据评论更新(现在只需要重新计算校验和):

int pingpong(struct __sk_buff *skb)
{
    void *data = (void *)(long)skb->data;
    void *data_end = (void *)(long)skb->data_end;
    struct ethhdr *eth  = data;
    struct iphdr  *ip;
    struct udphdr *udpdata;

    if ((void *)eth + sizeof(*eth) > data_end) {
            return TC_ACT_UNSPEC;
    }

    ip   = data + sizeof(*eth);
    if ((void *)ip + sizeof(*ip) > data_end) {
            return TC_ACT_UNSPEC;
    }

    udpdata = (void *)ip + sizeof(*ip);
    if ((void *)udpdata + sizeof(*udpdata) > data_end) {
            return TC_ACT_UNSPEC;
    }

    if (eth->h_proto != htons(ETH_P_IP)) {
            return TC_ACT_UNSPEC;
    }
    
    if (ip->protocol != IPPROTO_UDP) {
            return TC_ACT_UNSPEC;
    }

    unsigned int payload_size;
    unsigned char *payload;
    payload_size = ntohs(udpdata->len) - sizeof(*udpdata);
    payload = (unsigned char *)udpdata + sizeof(*udpdata);
    
    if ((void *)payload + payload_size > data_end) {
            return TC_ACT_UNSPEC;
    }
    
    // 1. Swap the MACs
    __u8 tmp_mac[ETH_ALEN];
    memcpy(tmp_mac,eth->h_dest,ETH_ALEN);
    memcpy(eth->h_dest,eth->h_source,ETH_ALEN);
    memcpy(eth->h_source,tmp_mac,ETH_ALEN);

    // 2. Swap the IPs
    if (eth->h_proto == htons(ETH_P_IP)) {
        __u32 tmp_ip = ip->saddr;
        ip->saddr = ip->daddr;
        ip->daddr = tmp_ip;
    }

    // 3. Swap the ports
    udpdata->source = port;
    udpdata->dest = srcport;

    // 4. Change the payload to be 0x1a 0x31 0x0f
    bpf_skb_adjust_room(skb,-1,BPF_ADJ_ROOM_NET,0);
    uint8_t byte1 = 0x1a;
    uint8_t byte2 = 0x31;
    uint8_t byte3 = 0x0f;
    int ret = bpf_skb_store_bytes(skb,payload_offset,&byte1,sizeof(byte1),0);
    ret = bpf_skb_store_bytes(skb,payload_offset+1,&byte2,sizeof(byte2),payload_offset+2,&byte3,sizeof(byte3),0);    

    // Re-calculate the checksum
    bpf_l4_csum_replace(skb,L4_CSUM_OFF,datap[0],byte1,0);
    bpf_l4_csum_replace(skb,datap[1],byte2,datap[2],byte3,0);
    // Not working!

    // Final: Redirect to be sent
    bpf_clone_redirect(skb,skb->ifindex,0);   
}

如果我只是更改负载而不使用 adjust_room 函数删除 1 个字节,它会被发送,但最后一个字节是 00。我想删除它。

请问有什么建议吗?谢谢!

解决方法

根据@Qeole 的建议,您可以使用以下内容来更改 UDP 负载的大小和内容:

// Remove 1 byte after IP header.
bpf_skb_adjust_room(ctx,-1,BPF_ADJ_ROOM_NET,0);

... re-check packet pointers or verifier will complain ...

// Need to rewrite the UDP header as the extra space was added before it.
*new_udphdr = *old_udphdr;

// Write payload.
udp_payload[0] = 0x1a
udp_payload[1] = 0x31
udp_payload[2] = 0x0f