为什么我的ebpf程序使用bpf_skb_store_bytes修改包后校验和没有改变?

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

我尝试通过 ebpf (tc egress) 在 ipv6 消息后添加扩展标头

我成功添加了扩展头。在初步测试中,我发现扩展头的添加并没有导致设备通信异常。

直到我的ebpf程序在openwrt(23.05.3 ramips/mt7621)路由器上运行并且传出的数据包由于校验和而被对方丢失时,我才发现这个问题。

我第一次在x86虚拟机(Ubuntu 22.04)上测试时没有出现这个问题。我发现虚拟机的ebpf程序打印的校验和与我实际发送的校验和不一致。事实上,发送的校验和是正确的。虚拟机好像自动修正了

而且,即使使用BPF_F_RECOMPUTE_CSUM标记调用bpf_skb_store_bytes,前后的校验和也不会改变。

这是我添加ipv6扩展头的代码的一部分,调用bpf_skb_store_bytes之前和之后的校验和是一致的

    protocol = ip6->nexthdr;   // store original protocol
    ip6->nexthdr = ip6nexthdr; // update protocol

    // off is the offset of the last nexthdr field (off = sizeof(struct ipv6hdr) + sizeof(struct ethhdr))
    // bytes_len is the length of the new bytes
    ip6->payload_len = bpf_htons(skb->len - off + bytes_len); // update payload_len

    // Print the checksum before bpf_skb_store_bytes
    if (protocol == IPPROTO_TCP) {
        struct tcphdr *tcp_hdr = (struct tcphdr *)((char *)ip6 + sizeof(*ip6));
        if ((char *)(tcp_hdr + 1) > (char *)(long)skb->data_end)
            return TC_ACT_SHOT;
        BPF_PRINT("TCP checksum before bpf_skb_store_bytes: %x", tcp_hdr->check);
    } else if (protocol == IPPROTO_UDP) {
        struct udphdr *udp_hdr = (struct udphdr *)((char *)ip6 + sizeof(*ip6));
        if ((char *)(udp_hdr + 1) > (char *)(long)skb->data_end)
            return TC_ACT_SHOT;
        BPF_PRINT("UDP checksum before bpf_skb_store_bytes: %x", udp_hdr->check);
    }
    /* Make room for new bytes and insert them.
     */
    if (bpf_skb_adjust_room(skb, bytes_len, BPF_ADJ_ROOM_NET, 0)) {
        BPF_PRINT("Failed to adjust room");
        return TC_ACT_SHOT;
    }
    /* Store the new bytes.
     */
    if (bpf_skb_store_bytes(skb, off, exthdr->bytes, bytes_len, BPF_F_RECOMPUTE_CSUM)) {
        BPF_PRINT("Failed to store bytes");
        return TC_ACT_SHOT;
    }
    /* Update last Extension Header's nexthdr field.
     */
    if (bpf_skb_store_bytes(skb, off + off_last_nexthdr, &protocol, sizeof(protocol), BPF_F_RECOMPUTE_CSUM)) {
        BPF_PRINT("Failed to store last nexthdr");
        return TC_ACT_SHOT;
    }

    // Print the checksum after bpf_skb_store_bytes
    if (protocol == IPPROTO_TCP) {
        ip6 = ipv6_header(skb, &off);
        if (!ip6) {
            BPF_PRINT("Failed to check IPv6 header");
            return TC_ACT_SHOT;
        }
        struct tcphdr *tcp_hdr = (struct tcphdr *)((char *)ip6 + sizeof(*ip6) + bytes_len);
        if ((char *)(tcp_hdr + 1) > (char *)(long)skb->data_end)
            return TC_ACT_SHOT;
        BPF_PRINT("TCP checksum after bpf_skb_store_bytes: %x", tcp_hdr->check);
    } else if (protocol == IPPROTO_UDP) {
        ip6 = ipv6_header(skb, &off);
        if (!ip6) {
            BPF_PRINT("Failed to check IPv6 header");
            return TC_ACT_SHOT;
        }
        struct udphdr *udp_hdr = (struct udphdr *)((char *)ip6 + sizeof(*ip6) + bytes_len);
        if ((char *)(udp_hdr + 1) > (char *)(long)skb->data_end)
            return TC_ACT_SHOT;
        BPF_PRINT("UDP checksum after bpf_skb_store_bytes: %x", udp_hdr->check);
    }

为什么我的 ebpf 程序在调用 bpf_skb_store_bytes 时设置了 BPF_F_RECOMPUTE_CSUM 标志,但不更新校验和?我是不是写错了什么地方?

为什么虚拟机(Ubuntu 22.04 x86_64)发送的数据包的校验和会自动修正?

checksum ebpf bpf xdp-bpf egress
1个回答
0
投票

而且,即使使用BPF_F_RECOMPUTE_CSUM标记调用bpf_skb_store_bytes,前后的校验和也不会改变。

BPF_F_RECOMPUTE_CSUM
告诉
bpf_skb_store_bytes
更新数据包校验和,而不是 TCP/UDP 校验和。因此,鉴于您使用的是 IPv6,此处无需更新。

IPv6 没有校验和,并且 TCP/UDP 校验和不覆盖 IPv6 扩展标头。除非您更改了 IPv6 地址或 IPv6 有效负载,否则您的 TCP/UDP 校验和不应更改。

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