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

如何在DPDK报文中解析eDNS并提取Flags、Rdata等指定字段

DPDK DNS报文字段提取实现方案

现有代码问题

  • 你在外层定义了struct dnshdr *dns_hdr,但在if分支内重复定义了局部变量char *dns_hdr,外层指针未被赋值,后续操作会触发空指针访问
  • 未做报文长度校验,直接偏移取DNS头可能越界,DPDK场景下必须先确认payload长度大于UDP头+DNS头总长度再操作
  • DNS是变长结构,除固定头外还有查询、回答等可变段,不能硬编码偏移取Rdata、TTL字段,需要先处理域名压缩指针

C语言DPDK原生实现方案

1. 定义DNS核心结构

/* DNS固定头部 */
struct dnshdr {
    uint16_t id;
    uint16_t flags;      /* 待提取Flags字段 */
    uint16_t qdcount;    /* 查询数量 */
    uint16_t ancount;    /* 回答数量 */
    uint16_t nscount;
    uint16_t arcount;
} __attribute__((packed));

/* DNS回答段固定部分 */
struct dns_rr {
    uint16_t type;
    uint16_t class;      /* 待提取Class字段 */
    uint32_t ttl;        /* 待提取TTL字段 */
    uint16_t rdlength;   /* Rdata长度 */
    /* 后续为rdlength长度的Rdata字段 */
} __attribute__((packed));

2. 实现DNS解析逻辑

#include <rte_mbuf.h>
#include <rte_udp.h>
#include <rte_ip.h>

/* 辅助函数:跳过DNS域名(兼容压缩指针),返回下一个字段的指针,出错返回NULL */
static uint8_t* skip_dns_name(uint8_t *ptr, uint8_t *end) {
    while (ptr < end) {
        uint8_t len = *ptr;
        if (len == 0) return ptr + 1;
        /* 处理压缩指针:最高两位为1 */
        if ((len & 0xC0) == 0xC0) return ptr + 2;
        ptr += len + 1;
    }
    return NULL;
}

/**
 * 解析DNS报文,提取目标字段
 * 返回0为解析成功,负数为解析失败
 */
int parse_dns_pkt(struct rte_mbuf *pkt, struct rte_udp_hdr *udp_hdr,
                  uint16_t *out_flags, uint16_t *out_class, 
                  uint32_t *out_ttl, uint8_t **out_rdata, uint16_t *out_rdlen)
{
    /* 校验报文最小长度 */
    if (rte_pktmbuf_data_len(pkt) < (sizeof(struct rte_udp_hdr) + sizeof(struct dnshdr))) {
        return -1;
    }

    struct dnshdr *dns_h = (struct dnshdr *)((uint8_t *)udp_hdr + sizeof(struct rte_udp_hdr));
    *out_flags = rte_be_to_cpu_16(dns_h->flags);

    /* 无回答的查询包直接返回,可根据需求调整逻辑 */
    if (rte_be_to_cpu_16(dns_h->ancount) == 0) {
        return 0;
    }

    uint8_t *ptr = (uint8_t *)(dns_h + 1);
    uint8_t *pkt_end = (uint8_t *)udp_hdr + rte_be_to_cpu_16(udp_hdr->dgram_len);

    /* 跳过查询段 */
    ptr = skip_dns_name(ptr, pkt_end);
    if (!ptr || ptr + 4 > pkt_end) return -2;
    ptr += 4; // 跳过查询的type、class字段

    /* 解析第一个回答段 */
    ptr = skip_dns_name(ptr, pkt_end);
    if (!ptr || ptr + sizeof(struct dns_rr) > pkt_end) return -3;

    struct dns_rr *rr = (struct dns_rr *)ptr;
    *out_class = rte_be_to_cpu_16(rr->class);
    *out_ttl = rte_be_to_cpu_32(rr->ttl);
    *out_rdlen = rte_be_to_cpu_16(rr->rdlength);
    *out_rdata = ptr + sizeof(struct dns_rr);

    if (*out_rdata + *out_rdlen > pkt_end) return -4;
    return 0;
}

3. 业务侧调用

替换你原有的端口判断逻辑即可:

// 同时匹配DNS请求和响应包
if (rte_be_to_cpu_16(udp_hdr->dst_port) == 53 || rte_be_to_cpu_16(udp_hdr->src_port) == 53) {
    printf("DNS Packet detected\n");
    uint16_t dns_flags, dns_class, rd_len;
    uint32_t dns_ttl;
    uint8_t *rdata;
    if (parse_dns_pkt(pkt, udp_hdr, &dns_flags, &dns_class, &dns_ttl, &rdata, &rd_len) == 0) {
        // 提取到的字段可直接存入自定义结构体/数组独立保存
        printf("DNS Flags: 0x%x, Class: %d, TTL: %u\n", dns_flags, dns_class, dns_ttl);
        // Rdata可通过rte_memcpy拷贝到独立缓冲区存储
    }
}

C++封装实现方案

如果使用C++可以封装为类,自动管理字段生命周期:

#include <vector>
#include <cstdint>
#include <rte_mbuf.h>
#include <rte_udp.h>

class DnsParser {
public:
    uint16_t flags;
    uint16_t cls;
    uint32_t ttl;
    std::vector<uint8_t> rdata;

    bool parse(struct rte_mbuf* pkt, struct rte_udp_hdr* udp_hdr) {
        // 解析逻辑和C版本一致,解析成功后将Rdata拷贝到vector即可
        uint16_t rd_len;
        uint8_t* rdata_ptr;
        // ... 省略重复解析逻辑
        rdata.resize(rd_len);
        memcpy(rdata.data(), rdata_ptr, rd_len);
        return true;
    }
};

注意:所有网络字节序的字段必须通过rte_be_to_cpu_*系列接口转换为主机字节序后再存储使用

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 10:39:03