You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

XDP-eBPF无法捕获多播IPv4数据包及MAC地址打印咨询

问题描述

sender程序可正常发送多播数据包,listener程序也能正常接收,但XDP内核代码无法正确识别该多播数据包。仅关注IPv4协议,XDP追踪日志显示多播包的以太网协议ID为0xC0A8,而普通IPv4数据包的以太网协议ID为0x800。需解答:

  1. 导致该问题的原因是什么?
  2. 如何在XDP代码中打印以太网头部的h_dest(MAC地址)?

问题1:原因分析

0xC0A8是IPv4地址段192.168.x.x的十六进制表示(C0=192,A8=168),这说明你在XDP代码中错误解析了以太网头部。

核心原因是:处理发送方向的多播包时,XDP挂载的网卡上,数据包的以太网头部可能未被硬件/内核完全组装,或者你混淆了数据包方向(发送/接收),导致解析时eth->h_proto读取到的是MAC地址的后两个字节,而非真正的以太网协议字段。

从日志数据看,多播包长度为45字节,普通IPv4包为60字节(包含14字节以太网头部),多播包的长度计算异常,进一步佐证了解析偏移错误的问题。

问题2:打印以太网目标MAC地址的方法

XDP中bpf_printk不直接支持数组格式化输出,需手动遍历h_dest数组,逐个打印每个字节的十六进制值。修改XDP代码的以太网头部处理部分,添加如下代码:

if ((eth + 1) <= data_end)
{
    // 打印目标MAC地址
    bpf_printk("Dest MAC: %02x:%02x:%02x:%02x:%02x:%02x",
                eth->h_dest[0], eth->h_dest[1], eth->h_dest[2],
                eth->h_dest[3], eth->h_dest[4], eth->h_dest[5]);
    
    // 原有协议打印逻辑
    bpf_printk("ETH %ld 0x%X",data_end-data,bpf_ntohs(eth->h_proto));
    // ... 后续原有代码
}

注意:bpf_printk的参数数量存在限制(通常最多10个),上述代码的参数数量符合要求。


附:相关代码

以太网头部结构示例

struct ethhdr {
    unsigned char   h_dest[ETH_ALEN];   /* 目标MAC地址 */
    unsigned char   h_source[ETH_ALEN]; /* 源MAC地址 */
    __be16          h_proto;            /* 协议类型ID字段 */
} __attribute__((packed));

xdp_kernel.c(修改后)

#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_endian.h>

#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/udp.h>
#include <linux/in.h>


SEC("xdp")
int xdp_test(struct xdp_md *ctx)
{
    bpf_printk("PACKET RECIEVED");
    void *data_end = (void *)(long)ctx->data_end;
    void *data = (void *)(long)ctx->data;

    void *pos = data;
    struct ethhdr *eth = (struct ethhdr *)(pos);

    if ((eth + 1) <= data_end)
    {
        // 新增打印MAC地址的代码
        bpf_printk("Dest MAC: %02x:%02x:%02x:%02x:%02x:%02x",
                    eth->h_dest[0], eth->h_dest[1], eth->h_dest[2],
                    eth->h_dest[3], eth->h_dest[4], eth->h_dest[5]);
        
        bpf_printk("ETH %ld 0x%X",data_end-data,bpf_ntohs(eth->h_proto));
        if (bpf_ntohs(eth->h_proto) == ETH_P_IP)
        {
            //bpf_printk("IP");
            pos += sizeof(struct ethhdr);
            struct iphdr *iph = (struct iphdr *)(pos);

            if ((iph + 1) <= data_end)
            {
                if (iph->protocol == IPPROTO_UDP)
                {
                    bpf_printk("UDP");
                    const __u16 iph_sz_in_bytes = iph->ihl * 4;

                    if (((void *)iph + iph_sz_in_bytes) <= data_end)
                    {
                        pos += iph_sz_in_bytes;

                        struct udphdr *udh = (struct udphdr *)(pos);

                        if ((udh + 1) <= data_end)
                        {
                        }
                    }
                }
            }
        }
    }
    return XDP_PASS;
}

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

sender.c

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>

#include <string.h>
#include <stdio.h>
#include <stdlib.h>

int main()
{
    const int delay_secs = 1;
    const char *message2 = "Hello, World!";

    unsigned long counter = 0;

    char message[8192] = {0};

    int fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (fd < 0)
    {
        perror("socket");
        return 1;
    }

    struct ip_mreq mreq;
    memset(&mreq, 0, sizeof(mreq));
    mreq.imr_interface.s_addr = inet_addr("192.168.2.6"); // 本地WiFi接口地址

    if (
        setsockopt(
            fd, IPPROTO_IP, IP_MULTICAST_IF, (char *)&mreq, sizeof(mreq)) < 0)
    {
        perror("setsockopt");
        return 1;
    }

    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = inet_addr("239.0.0.0");
    addr.sin_port = htons(1234);

    while (1)
    {
        char ch = 0;
        snprintf(message, 8192, "%s-%lu", message2, ++counter);
        int nbytes = sendto(
            fd,
            message,
            strlen(message),
            0,
            (struct sockaddr *)&addr,
            sizeof(addr));
        if (nbytes < 0)
        {
            perror("sendto");
            return 1;
        }
        sleep(delay_secs);
    }

    return 0;
}

listener.c

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <time.h>

#include <string.h>
#include <stdio.h>
#include <stdlib.h>

#define MSGBUFSIZE 8192

int main()
{
    int fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (fd < 0)
    {
        perror("socket");
        return 1;
    }

    u_int yes = 1;
    if (
        setsockopt(
            fd, SOL_SOCKET, SO_REUSEADDR, (char *)&yes, sizeof(yes)) < 0)
    {
        perror("Reusing ADDR failed");
        return 1;
    }

    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = inet_addr("239.0.0.0"); 
    addr.sin_port = htons(1234);

    if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
    {
        perror("bind");
        return 1;
    }

    struct ip_mreq mreq;
    mreq.imr_multiaddr.s_addr = inet_addr("239.0.0.0");
    mreq.imr_interface.s_addr = inet_addr("192.168.2.6"); // 本地WiFi地址
    if (
        setsockopt(
            fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&mreq, sizeof(mreq)) < 0)
    {
        perror("setsockopt");
        return 1;
    }

    while (1)
    {
        char msgbuf[MSGBUFSIZE];
        int addrlen = sizeof(addr);
        int nbytes = recvfrom(
            fd,
            msgbuf,
            MSGBUFSIZE,
            0,
            (struct sockaddr *)&addr,
            &addrlen);
        if (nbytes < 0)
        {
            perror("recvfrom");
            return 1;
        }
        msgbuf[nbytes] = '\0';
        puts(msgbuf);
    }

    return 0;
}

附:XDP追踪日志

多播包日志

sender-28811 [002] ..s21 11044.326101: bpf_trace_printk: PACKET RECIEVED
sender-28811 [002] ..s21 11044.326122: bpf_trace_printk: ETH 45 0xC0A8

普通IPv4包日志

irq/168-iwlwifi-596 [006] ..s21 11041.649288: bpf_trace_printk: PACKET RECIEVED
irq/168-iwlwifi-596 [006] ..s21 11041.649298: bpf_trace_printk: ETH 60 0x800


内容的提问来源于stack exchange,提问作者Nagaraj

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 05:05:54