我没有使用 XDP_TX 接收数据包

I'm not receiving packets using XDP_TX

// SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
// Copyright (c) 2018 Netronome Systems, Inc.
#define BPF_NO_GLOBAL_DATA

#include <stdbool.h>
#include <stddef.h>
#include <string.h>
#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>
#include <linux/ipv6.h>
#include <endian.h>

#include "bpf_endian.h"
#include "bpf_helpers.h"
#include "jhash.h"
#include "common.h"
#include "parsing_helpers.h"

#include <stdint.h>

__attribute__((__always_inline__))
static inline __u16 csum_fold_helper(__u64 csum) {
    int i;
#pragma unroll
    for (i = 0; i < 4; i++) {
        if (csum >> 16)
            csum = (csum & 0xffff) + (csum >> 16);
    }
    return ~csum;
}

__attribute__((__always_inline__))
static inline void ipv4_csum(void* data_start, int data_size, __u64* csum) {
    *csum = bpf_csum_diff(0, 0, data_start, data_size, *csum);
    *csum = csum_fold_helper(*csum);
}

__attribute__((__always_inline__))
static inline void ipv4_l4_csum(void* data_start, __u32 data_size,
    __u64* csum, struct iphdr* iph) {
    __u32 tmp = 0;
    *csum = bpf_csum_diff(0, 0, &iph->saddr, sizeof(__be32), *csum);
    *csum = bpf_csum_diff(0, 0, &iph->daddr, sizeof(__be32), *csum);
    tmp = __builtin_bswap32((__u32)(iph->protocol));
    *csum = bpf_csum_diff(0, 0, &tmp, sizeof(__u32), *csum);
    tmp = __builtin_bswap32((__u32)(data_size));
    *csum = bpf_csum_diff(0, 0, &tmp, sizeof(__u32), *csum);
    *csum = bpf_csum_diff(0, 0, data_start, data_size, *csum);
    *csum = csum_fold_helper(*csum);
}

SEC("prog")
int xdp_drop_benchmark_traffic(struct xdp_md* ctx)
{
    void* data_end = (void*)(long)ctx->data_end;
    void* data = (void*)(long)ctx->data;
    struct ethhdr* eth = data;

    if (data + sizeof(*eth) > data_end) {
        return XDP_PASS;
    }

    uint16_t h_proto = eth->h_proto;

    if (h_proto == htons(ETH_P_IP)) {
        struct iphdr* iph = data + sizeof(*eth);
        if (data + sizeof(*eth) + sizeof(*iph) > data_end) {
            return XDP_PASS;
        }

        if (iph->protocol != IPPROTO_TCP) {
            return XDP_PASS;
        }

        struct tcphdr* tcph = data + sizeof(*eth) + sizeof(*iph);
        if (data + sizeof(*eth) + sizeof(*iph) + sizeof(*tcph) > data_end) {
            return XDP_PASS;
        }

        __u16 tcp_len = htons(iph->tot_len) - (iph->ihl << 2);

        if (tcp_len > 2000) {
            return XDP_DROP;
        }

        if (tcph->dest == htons(65535)) {
            unsigned char gateway[ETH_ALEN];
            gateway[0] = 0x92; gateway[1] = 0x10;
            gateway[2] = 0x95; gateway[3] = 0x86;
            gateway[4] = 0x26; gateway[5] = 0xbf;
            __builtin_memcpy(eth->h_source, eth->h_dest, ETH_ALEN);
            __builtin_memcpy(eth->h_dest, gateway, ETH_ALEN);

            bpf_debug("MAC Source: %i:%i:%i", eth->h_source[0], eth->h_source[1], eth->h_source[2]);
            bpf_debug("%i:%i:%i\n", eth->h_source[3], eth->h_source[4], eth->h_source[5]);

            bpf_debug("MAC Destin: %i:%i:%i", eth->h_dest[0], eth->h_dest[1], eth->h_dest[2]);
            bpf_debug("%i:%i:%i\n", eth->h_dest[3], eth->h_dest[4], eth->h_dest[5]);

            iph->saddr = iph->daddr;
            iph->daddr = htonl(4266428307);

            __u64 csum = 0;
            iph->check = 0;
            ipv4_csum(iph, sizeof(struct iphdr), &csum);
            iph->check = csum;

            csum = 0;
            tcph->check = 0;
            ipv4_l4_csum(tcph, tcp_len, &csum, iph);
            tcph->check = csum;

            bpf_debug("Checksum New: %i | %i\n", iph->check, tcph->check);

            return XDP_TX;
        }
    }

    return XDP_PASS;
}

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

奇怪的是,我没有在目标服务器上接收到 TCP 数据包 *(147.135.76.254 | 4266428307),而且在本地服务器上,数据包也没有出现在 tcpdump -i eth0 dst port 65535

我做错了什么吗?如果是,我该如何修复代码。

目的是:在服务器端口 LOCAL:65535 上接收数据包,并重定向到 DESTINATION:65535


更新:

  1. 程序似乎运行正常,我收到消息“收到!”在猫 /sys/kernel/debug/tracing/trace_pipe

  2. 数据包没有到达目标服务器

  3. 在目标服务器 (Windows) 中,我是 运行 Wireshark,禁用了 TCP 校验和验证,这意味着中间没有阻止数据包的过滤器校验和无效,例如:

    在源服务器 (linux) 上,我发送 hping3 147.135.76.254 -p 65535 -- badcksum 并在 wireshark(目标服务器)中接收所有数据包(具有无效校验和)

  4. 在源服务器 (XDP) 中,当我执行:tcpdump -i eth0 tcp 端口 65535 时,我没有收到任何数据包。

    注意:我使用 ethtool -K eth0 tx off 来禁用卸载/chksum

  5. 如果我将 XDP_TX 更改为 XDP_PASS 我可以在 tcpdump 中接收此数据包(上面的命令):
    14:38:10.964195 IP d2-2-us-east-va-1.39698 > 147.135.76.254.65535: Flags [S], seq 3962643128, win 14600, options [mss 1460,sackOK,TS val 2414158903 ecr 0, nop,wscale 9], 长度 0

  6. 在 ethtool -S eth0 中正确显示 rx/tx

  7. 如果我从“XDP_TX”更改为“XDP_PASS”,我会在 tcpdump 中收到此响应:135.148.232.155.58062 > 147.135.76.254.65535:Flags [ S]、cksum 0x41c3(正确)、seq 2996104997、win 14600、options [mss 1460、sackOK、TS val 2614058020 ecr 0、nop、wscale 9]、长度 0,这意味着我的 TCP 校验和和 IP 计算正常工作

  8. 我更新了正确通知 MAC 地址,但问题仍然存在。


执行的命令:

[root@d2-2-us-east-va-1 ~]# arp -a _gateway (135.148.232.1) at 92:10:95:86:26:bf [ether] on eth0

[root@d2-2-us-east-va-1 ~]#猫/sys/class/net/eth0/address fa:16:3e:0e:cf:a4

[root@d2-2-us-east-va-1 ~]# ifconfig ...(确认 mac 地址)

您没有更新以太网层的源和目标 MAC 地址。如果源 MAC 与配置的 NIC MAC 不匹配,某些 NIC,尤其是虚拟化的 NIC 将丢弃传出数据包。

如果您将数据包反射回本地网络上的同一台物理机器(本地机器或 router/default 网关),您可以简单地交换 src 和 dst 地址。

我更新了您的代码以添加此内容:

// SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
// Copyright (c) 2018 Netronome Systems, Inc.
#define BPF_NO_GLOBAL_DATA

#include <stdbool.h>
#include <stddef.h>
#include <string.h>
#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>
#include <linux/ipv6.h>
#include <endian.h>

#include "bpf_endian.h"
#include "bpf_helpers.h"
#include "jhash.h"
#include "common.h"
#include "parsing_helpers.h"

#include <stdint.h>

__attribute__((__always_inline__)) static inline __u16 csum_fold_helper(__u64 csum)
{
    int i;
#pragma unroll
    for (i = 0; i < 4; i++)
    {
        if (csum >> 16)
            csum = (csum & 0xffff) + (csum >> 16);
    }
    return ~csum;
}

__attribute__((__always_inline__)) static inline void ipv4_csum(void *data_start, int data_size, __u64 *csum)
{
    *csum = bpf_csum_diff(0, 0, data_start, data_size, *csum);
    *csum = csum_fold_helper(*csum);
}

__attribute__((__always_inline__)) static inline void ipv4_l4_csum(void *data_start, __u32 data_size,
                                                                   __u64 *csum, struct iphdr *iph)
{
    __u32 tmp = 0;
    *csum = bpf_csum_diff(0, 0, &iph->saddr, sizeof(__be32), *csum);
    *csum = bpf_csum_diff(0, 0, &iph->daddr, sizeof(__be32), *csum);
    tmp = __builtin_bswap32((__u32)(iph->protocol));
    *csum = bpf_csum_diff(0, 0, &tmp, sizeof(__u32), *csum);
    tmp = __builtin_bswap32((__u32)(data_size));
    *csum = bpf_csum_diff(0, 0, &tmp, sizeof(__u32), *csum);
    *csum = bpf_csum_diff(0, 0, data_start, data_size, *csum);
    *csum = csum_fold_helper(*csum);
}

SEC("prog")
int xdp_drop_benchmark_traffic(struct xdp_md *ctx)
{
    void *data_end = (void *)(long)ctx->data_end;
    void *data = (void *)(long)ctx->data;
    struct ethhdr *eth = data;

    if (data + sizeof(*eth) > data_end)
    {
        return XDP_PASS;
    }

    uint16_t h_proto = eth->h_proto;

    if (h_proto == htons(ETH_P_IP))
    {
        struct iphdr *iph = data + sizeof(*eth);
        if (data + sizeof(*eth) + sizeof(*iph) > data_end)
        {
            return XDP_PASS;
        }

        if (iph->protocol != IPPROTO_TCP)
        {
            return XDP_PASS;
        }

        struct tcphdr *tcph = data + sizeof(*eth) + sizeof(*iph);
        if (data + sizeof(*eth) + sizeof(*iph) + sizeof(*tcph) > data_end)
        {
            return XDP_PASS;
        }

        __u16 tcp_len = htons(iph->tot_len) - (iph->ihl << 2);

        if (tcp_len > 2000)
        {
            return XDP_DROP;
        }

        if (tcph->dest == htons(65535))
        {
            bpf_debug("Checksum Old: %i | %i\n", iph->check, tcph->check);

            unsigned char tmp[ETH_ALEN];
            __builtin_memcpy(tmp, eth->h_source, ETH_ALEN);
            __builtin_memcpy(eth->h_source, eth->h_dest ETH_ALEN);
            __builtin_memcpy(eth->h_dest, tmp);

            iph->saddr = iph->daddr;
            iph->daddr = htonl(4266428307);

            __u64 csum = 0;

            iph->check = 0;
            ipv4_csum(iph, sizeof(struct iphdr), &csum);
            iph->check = csum;

            csum = 0;
            tcph->check = 0;
            ipv4_l4_csum(tcph, tcp_len, &csum, iph);
            tcph->check = csum;

            bpf_debug("Checksum New: %i | %i\n", iph->check, tcph->check);

            return XDP_TX;
        }
    }

    return XDP_PASS;
}

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

更改的行是:

unsigned char tmp[ETH_ALEN];
__builtin_memcpy(tmp, eth->h_source, ETH_ALEN);
__builtin_memcpy(eth->h_source, eth->h_dest ETH_ALEN);
__builtin_memcpy(eth->h_dest, tmp);

请注意,如果您正在切换数据包(将其发送到不同的物理设备),这将不起作用,在这种情况下,您需要使用 ARP 来获得正确的 MAC 或对其进行硬编码。