云计算百科
云计算领域专业知识百科平台

dpdk之arp回显代码案例

一、dpdk之arp回显代码案例

1.arp.c

#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdint.h>
#include <stddef.h>
#include <string.h>

// DPDK使用的头文件
#include <rte_eal.h>
#include <rte_mbuf.h>
#include <rte_ethdev.h>
#include <rte_mempool.h>
#include <rte_ether.h>
#include <rte_ip.h>
#include <rte_udp.h>
#include <rte_arp.h>

#define ENABLE_ARP 1

#define NUM_MBUFS (4096 – 1)
#define BURST_SIZE 32
#define UDP_PAYLOAD "Hello DPDK! This is a response packet."
// 新增:定义负载打印缓冲区大小(防止超长负载溢出)
#define MAX_PAYLOAD_PRINT 1024

int gDpdkPortId = 0;
//全局变量:存储当前窗口的MAC地址和IP地址(用于arp响应)
static struct rte_ether_addr gPortMacAddr;
static uint32_t gPortIpAddr = 0;

static const struct rte_eth_conf port_conf_default = {
.rxmode = {},
.txmode = {}
};

static void ng_init_port(struct rte_mempool *mbuf_pool){
uint16_t nb_sys_ports = rte_eth_dev_count_avail();
if(nb_sys_ports == 0){
rte_exit(EXIT_FAILURE,"No Support eth found\\n");
}

struct rte_eth_dev_info dev_info;
rte_eth_dev_info_get(gDpdkPortId,&dev_info);

const int num_rx_queues = 1;
const int num_tx_queues = 1;
struct rte_eth_conf port_conf = port_conf_default;
rte_eth_dev_configure(gDpdkPortId,num_rx_queues,num_tx_queues,&port_conf);

if(rte_eth_rx_queue_setup(gDpdkPortId,0,512,rte_eth_dev_socket_id(gDpdkPortId),NULL,mbuf_pool) < 0){
rte_exit(EXIT_FAILURE,"Could not setup RX queue\\n");
}

struct rte_eth_txconf txq_conf = dev_info.default_txconf;
if(rte_eth_tx_queue_setup(gDpdkPortId,0,512,rte_eth_dev_socket_id(gDpdkPortId),
&txq_conf) < 0){
rte_exit(EXIT_FAILURE,"Could not setup TX queue\\n");
}

if(rte_eth_dev_start(gDpdkPortId) < 0){
rte_exit(EXIT_FAILURE,"Could not start Ethernet port\\n");
}

// 获取当前网口的MAC地址(DPDK 19.08.2:rte_eth_macaddr_get为void类型,无返回值)
rte_eth_macaddr_get(gDpdkPortId,&gPortMacAddr);

//设置当前网口的ip地址,也就是自己网卡ip
gPortIpAddr = inet_addr("192.168.3.100");
//启用混杂模式
rte_eth_promiscuous_enable(gDpdkPortId);

printf("Port %d initialized successfully, promiscuous mode enabled\\n", gDpdkPortId);
printf("Port MAC address: %02x:%02x:%02x:%02x:%02x:%02x\\n",
gPortMacAddr.addr_bytes[0], gPortMacAddr.addr_bytes[1],
gPortMacAddr.addr_bytes[2], gPortMacAddr.addr_bytes[3],
gPortMacAddr.addr_bytes[4], gPortMacAddr.addr_bytes[5]);
printf("Port IP address: %s\\n", inet_ntoa(*(struct in_addr *)&gPortIpAddr));
}

static int create_eth_ip_udp(struct rte_mbuf *mbuf,
uint8_t *dst_mac, uint8_t *src_mac,
uint32_t src_ip, uint32_t dst_ip,
uint16_t src_port, uint16_t dst_port,
const uint8_t *payload, uint32_t payload_len){
if(mbuf == NULL || dst_mac == NULL || src_mac == NULL || payload == NULL || payload_len == 0){
printf("Invalid parameters for packet construction\\n");
return -1;
}

struct rte_ether_hdr *ehdr = NULL;
struct rte_ipv4_hdr *iphdr = NULL;
struct rte_udp_hdr *udphdr = NULL;
uint8_t *udp_payload = NULL;

const uint32_t eth_hdr_len = sizeof(struct rte_ether_hdr);
const uint32_t ip_hdr_len = sizeof(struct rte_ipv4_hdr);
const uint32_t udp_hdr_len = sizeof(struct rte_udp_hdr);
const uint32_t total_pkt_len = eth_hdr_len + ip_hdr_len + udp_hdr_len + payload_len;

ehdr = rte_pktmbuf_mtod_offset(mbuf, struct rte_ether_hdr *, 0);
iphdr = rte_pktmbuf_mtod_offset(mbuf, struct rte_ipv4_hdr *, eth_hdr_len);
udphdr = rte_pktmbuf_mtod_offset(mbuf, struct rte_udp_hdr *, eth_hdr_len + ip_hdr_len);
udp_payload = rte_pktmbuf_mtod_offset(mbuf, uint8_t *, eth_hdr_len + ip_hdr_len + udp_hdr_len);

memcpy((uint8_t *)&ehdr->d_addr, dst_mac, RTE_ETHER_ADDR_LEN); // 目标MAC
memcpy((uint8_t *)&ehdr->s_addr, src_mac, RTE_ETHER_ADDR_LEN); // 源MAC
ehdr->ether_type = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4);

iphdr->version_ihl = RTE_IPV4_VHL_DEF;
iphdr->type_of_service = 0;
iphdr->total_length = rte_cpu_to_be_16(ip_hdr_len + udp_hdr_len + payload_len);
iphdr->packet_id = 0;
iphdr->fragment_offset = 0;
iphdr->time_to_live = 64;
iphdr->next_proto_id = IPPROTO_UDP;
iphdr->src_addr = src_ip;
iphdr->dst_addr = dst_ip;
iphdr->hdr_checksum = 0;
iphdr->hdr_checksum = rte_ipv4_cksum(iphdr);

udphdr->src_port = rte_cpu_to_be_16(src_port);
udphdr->dst_port = rte_cpu_to_be_16(dst_port);
udphdr->dgram_len = rte_cpu_to_be_16(udp_hdr_len + payload_len);
udphdr->dgram_cksum = 0;

memcpy(udp_payload, payload, payload_len);

mbuf->pkt_len = total_pkt_len;
mbuf->data_len = total_pkt_len;

return 0;
}

static int send_udp_response(struct rte_mempool *mbuf_pool,
struct rte_ether_hdr *recv_ehdr,
struct rte_ipv4_hdr *recv_iphdr,
struct rte_udp_hdr *recv_udphdr){
struct rte_mbuf *tx_mbuf = rte_pktmbuf_alloc(mbuf_pool);
if(tx_mbuf == NULL){
printf("Failed to allocate mbuf for TX packet\\n");
return -1;
}

uint8_t src_mac[RTE_ETHER_ADDR_LEN] = {0};
uint8_t dst_mac[RTE_ETHER_ADDR_LEN] = {0};

memcpy(src_mac, (uint8_t *)&recv_ehdr->d_addr, RTE_ETHER_ADDR_LEN);
memcpy(dst_mac, (uint8_t *)&recv_ehdr->s_addr, RTE_ETHER_ADDR_LEN);

uint32_t src_ip = recv_iphdr->dst_addr;
uint32_t dst_ip = recv_iphdr->src_addr;

uint16_t src_port = ntohs(recv_udphdr->dst_port);
uint16_t dst_port = ntohs(recv_udphdr->src_port);

const uint8_t *payload = (const uint8_t *)UDP_PAYLOAD;
uint32_t payload_len = strlen(UDP_PAYLOAD);

if(create_eth_ip_udp(tx_mbuf,dst_mac,src_mac,src_ip,dst_ip,src_port,dst_port,
payload,payload_len) < 0){
rte_pktmbuf_free(tx_mbuf);
return -1;
}

struct rte_mbuf *tx_mbufs[1] = {tx_mbuf};
uint16_t num_sent = rte_eth_tx_burst(gDpdkPortId,0,tx_mbufs,1);

if(num_sent < 1){
printf("Failed to send response packet, num_sent: %u\\n", num_sent);
rte_pktmbuf_free(tx_mbuf);
return -1;
}

struct in_addr src_addr, dst_addr;
src_addr.s_addr = src_ip;
dst_addr.s_addr = dst_ip;
printf("Response sent: %s:%d -> %s:%d, payload: %s\\n",
inet_ntoa(src_addr), src_port,
inet_ntoa(dst_addr), dst_port,
UDP_PAYLOAD);

return 0;
}

#if ENABLE_ARP

//arp数据内存缓冲区起始指针 目的MAC地址 发送方ip 目的ip ARP操作码(arp包的类型) 发送方的MAC地址结构体指针
static int ng_encode_arp_pkt(uint8_t *msg, uint8_t *dst_mac, uint32_t sip, uint32_t dip,
uint16_t arp_opcode, struct rte_ether_addr *src_eth_addr) {
if (msg == NULL || dst_mac == NULL || src_eth_addr == NULL) {
printf("Invalid parameters for ARP packet encoding\\n");
return -1;
}

struct rte_ether_hdr *ehdr = (struct rte_ether_hdr *)msg;
struct rte_arp_hdr *ahdr = (struct rte_arp_hdr *)(msg + sizeof(struct rte_ether_hdr));

//1. 填充以太网头部
memcpy((uint8_t *)&ehdr->d_addr, dst_mac, RTE_ETHER_ADDR_LEN); // 目标MAC
memcpy((uint8_t *)&ehdr->s_addr, src_eth_addr, RTE_ETHER_ADDR_LEN); // 源MAC(网口MAC)
ehdr->ether_type = rte_cpu_to_be_16(RTE_ETHER_TYPE_ARP);

//2. 填充arp头部(精准适配 DPDK 19.08.2 真实字段名,来自命令行查询结果)
ahdr->arp_hardware = rte_cpu_to_be_16(RTE_ARP_HRD_ETHER); // 硬件类型:以太网
ahdr->arp_protocol = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4); // 协议类型:IPv4
ahdr->arp_hlen = RTE_ETHER_ADDR_LEN; // 硬件地址长度
ahdr->arp_plen = sizeof(uint32_t); // 协议地址长度
ahdr->arp_opcode = rte_cpu_to_be_16(arp_opcode); // ARP操作码:请求/响应

// 3. 填充ARP数据(发送方信息)
memcpy((uint8_t *)&ahdr->arp_data.arp_sha, src_eth_addr, RTE_ETHER_ADDR_LEN); // 发送方MAC
ahdr->arp_data.arp_sip = sip; // 发送方IP(网口IP)

// 4. 填充ARP数据(目标方信息)
memcpy((uint8_t *)&ahdr->arp_data.arp_tha, dst_mac, RTE_ETHER_ADDR_LEN); // 目标方MAC
ahdr->arp_data.arp_tip = dip; // 目标方IP

return 0;
}

//构建并返回ARP响应数据包
static struct rte_mbuf *send_arp_response(struct rte_mempool *mbuf_pool,
struct rte_arp_hdr *recv_ahdr,
struct rte_ether_hdr *recv_ehdr) {
if (mbuf_pool == NULL || recv_ahdr == NULL || recv_ehdr == NULL) {
printf("Invalid parameters for ARP response creation\\n");
return NULL;
}

//1. 计算ARP数据包的总长度
const unsigned total_length = sizeof(struct rte_ether_hdr) + sizeof(struct rte_arp_hdr);

//2. 分配mbuf缓冲区
struct rte_mbuf *mbuf = rte_pktmbuf_alloc(mbuf_pool);
if (!mbuf) {
printf("Failed to allocate mbuf for ARP response\\n");
return NULL;
}

// 3. 初始化mbuf长度信息
mbuf->pkt_len = total_length;
mbuf->data_len = total_length;

//4. 获取mbuf数据指针
uint8_t *pkt_data = rte_pktmbuf_mtod(mbuf,uint8_t *);
if (pkt_data == NULL) {
printf("Failed to get mbuf data pointer for ARP response\\n");
rte_pktmbuf_free(mbuf);
return NULL;
}

// 5. 提取接收的ARP请求中的关键信息
uint8_t dst_mac[RTE_ETHER_ADDR_LEN] = {0};
memcpy(dst_mac, (uint8_t *)&recv_ehdr->s_addr, RTE_ETHER_ADDR_LEN); // 目标MAC:请求方MAC
uint32_t src_ip = gPortIpAddr; // 源IP:当前网口IP
uint32_t dst_ip = recv_ahdr->arp_data.arp_sip; // 目标IP:请求方IP

// 6. 编码ARP响应数据包(操作码:ARP响应)
if (ng_encode_arp_pkt(pkt_data, dst_mac, src_ip, dst_ip,
RTE_ARP_OP_REPLY, &gPortMacAddr) < 0) {
printf("Failed to encode ARP response packet\\n");
rte_pktmbuf_free(mbuf);
return NULL;
}

// 7. 打印ARP响应信息
struct in_addr src_addr, dst_addr;
src_addr.s_addr = src_ip;
dst_addr.s_addr = dst_ip;
printf("ARP Response prepared: %s (%02x:%02x:%02x:%02x:%02x:%02x) -> %s (%02x:%02x:%02x:%02x:%02x:%02x)\\n",
inet_ntoa(src_addr), gPortMacAddr.addr_bytes[0], gPortMacAddr.addr_bytes[1],
gPortMacAddr.addr_bytes[2], gPortMacAddr.addr_bytes[3],
gPortMacAddr.addr_bytes[4], gPortMacAddr.addr_bytes[5],
inet_ntoa(dst_addr), dst_mac[0], dst_mac[1], dst_mac[2],
dst_mac[3], dst_mac[4], dst_mac[5]);

return mbuf;
}

#endif

int main(int argc,char *argv[]){
int ret = rte_eal_init(argc,argv);
if(ret < 0){
fprintf(stderr, "DPDK EAL初始化失败!错误码:%d\\n", ret);
rte_exit(EXIT_FAILURE,"EAL init error!");
}

argc -= ret;
argv += ret;

struct rte_mempool *mbuf_pool = rte_pktmbuf_pool_create("mbuf_pool",NUM_MBUFS,0,0,
RTE_MBUF_DEFAULT_BUF_SIZE,rte_socket_id());
if(mbuf_pool == NULL){
rte_exit(EXIT_FAILURE,"mbuf pool create error!");
}
printf("mbuf pool created successfully\\n");

ng_init_port(mbuf_pool);

while(1){
struct rte_mbuf *mbufs[BURST_SIZE];

unsigned num_recv = rte_eth_rx_burst(gDpdkPortId,0,mbufs,BURST_SIZE);
if(num_recv > BURST_SIZE){
rte_exit(EXIT_FAILURE,"Invalid number of received packets");
}

for(unsigned int i = 0;i < num_recv;i++){
int need_process = 1;
struct rte_ether_hdr *ehdr = NULL;
struct rte_ipv4_hdr *iphdr = NULL;
struct rte_udp_hdr *udphdr = NULL;
uint8_t *recv_payload = NULL; // 新增:接收负载指针
uint32_t recv_payload_len = 0; // 新增:接收负载长度
char payload_buf[MAX_PAYLOAD_PRINT] = {0}; // 新增:负载打印缓冲区

ehdr = rte_pktmbuf_mtod(mbufs[i],struct rte_ether_hdr *);

#if ENABLE_ARP
// 补全:解析ARP数据包(仅处理ARP请求,精准适配 DPDK 19.08.2 真实字段)
if(ehdr->ether_type == rte_cpu_to_be_16(RTE_ETHER_TYPE_ARP)){
struct rte_arp_hdr *ahdr = rte_pktmbuf_mtod_offset(mbufs[i],struct rte_arp_hdr *,
sizeof(struct rte_ether_hdr));

// 验证ARP类型和操作码(仅处理IPv4 ARP请求,使用查询到的正确字段)
if (rte_cpu_to_be_16(RTE_ARP_HRD_ETHER) == ahdr->arp_hardware &&
rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4) == ahdr->arp_protocol &&
rte_cpu_to_be_16(RTE_ARP_OP_REQUEST) == ahdr->arp_opcode &&
ahdr->arp_data.arp_tip == gPortIpAddr) { // 目标IP是当前网口IP

//构建ARP响应数据包
struct rte_mbuf *arp_mbuf = send_arp_response(mbuf_pool,ahdr,ehdr);
if(arp_mbuf != NULL){
// 发送ARP响应(正确API:rte_eth_tx_burst)
uint16_t num_sent = rte_eth_tx_burst(gDpdkPortId, 0, &arp_mbuf, 1);
if (num_sent < 1) {
printf("Failed to send ARP response, num_sent: %u\\n", num_sent);
rte_pktmbuf_free(arp_mbuf);
}
}
}

need_process = 0; // ARP包处理完成,无需后续UDP处理
}
#endif
if(ehdr->ether_type != rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4)){
need_process = 0;
}

if(need_process){
iphdr = rte_pktmbuf_mtod_offset(mbufs[i], struct rte_ipv4_hdr *,
sizeof(struct rte_ether_hdr));
if(iphdr == NULL || iphdr->next_proto_id != IPPROTO_UDP){
need_process = 0;
}
}

if(need_process){
udphdr = rte_pktmbuf_mtod_offset(mbufs[i], struct rte_udp_hdr *,
sizeof(struct rte_ether_hdr) + sizeof(struct rte_ipv4_hdr));
if(udphdr == NULL){
need_process = 0;
} else {
// 新增:计算UDP负载起始地址和长度
const uint32_t hdr_total_len = sizeof(struct rte_ether_hdr) +
sizeof(struct rte_ipv4_hdr) +
sizeof(struct rte_udp_hdr);
// 1. 获取负载起始地址
recv_payload = rte_pktmbuf_mtod_offset(mbufs[i], uint8_t *, hdr_total_len);
// 2. 计算负载长度(网络字节序转主机字节序后,减去UDP头长度)
recv_payload_len = ntohs(udphdr->dgram_len) – sizeof(struct rte_udp_hdr);

// 3. 安全拷贝负载到打印缓冲区(防止超长负载溢出)
if(recv_payload_len > 0 && recv_payload != NULL){
uint32_t copy_len = (recv_payload_len > MAX_PAYLOAD_PRINT – 1) ?
(MAX_PAYLOAD_PRINT – 1) : recv_payload_len;
memcpy(payload_buf, recv_payload, copy_len);
payload_buf[copy_len] = '\\0'; // 字符串结尾符,保证正常打印
}

// 新增:打印接收到的UDP负载
struct in_addr src_addr, dst_addr;
src_addr.s_addr = iphdr->src_addr;
dst_addr.s_addr = iphdr->dst_addr;
printf("Received UDP packet: %s:%d -> %s:%d\\n",
inet_ntoa(src_addr), ntohs(udphdr->src_port),
inet_ntoa(dst_addr), ntohs(udphdr->dst_port));
printf("Received UDP payload length: %u bytes\\n", recv_payload_len);
printf("Received UDP payload content: %s\\n",
(recv_payload_len > 0) ? payload_buf : "Empty payload");
printf("—————————————————-\\n");

send_udp_response(mbuf_pool, ehdr, iphdr, udphdr);
}
}

rte_pktmbuf_free(mbufs[i]);
}
}

return 0;
}

2. Makefile

# SPDX-License-Identifier: BSD-3-Clause
# Copyright(c) 2010-2014 Intel Corporation

# binary name
APP = arp

# all source are stored in SRCS-y
SRCS-y := arp.c

# Build using pkg-config variables if possible
ifeq ($(shell pkg-config –exists libdpdk && echo 0),0)

all: shared
.PHONY: shared static
shared: build/$(APP)-shared
ln -sf $(APP)-shared build/$(APP)
static: build/$(APP)-static
ln -sf $(APP)-static build/$(APP)

PKGCONF=pkg-config –define-prefix

PC_FILE := $(shell $(PKGCONF) –path libdpdk)
CFLAGS += -O3 $(shell $(PKGCONF) –cflags libdpdk)
CFLAGS += -DALLOW_EXPERIMENTAL_API
LDFLAGS_SHARED = $(shell $(PKGCONF) –libs libdpdk)
LDFLAGS_STATIC = -Wl,-Bstatic $(shell $(PKGCONF) –static –libs libdpdk)

build/$(APP)-shared: $(SRCS-y) Makefile $(PC_FILE) | build
$(CC) $(CFLAGS) $(SRCS-y) -o $@ $(LDFLAGS) $(LDFLAGS_SHARED)

build/$(APP)-static: $(SRCS-y) Makefile $(PC_FILE) | build
$(CC) $(CFLAGS) $(SRCS-y) -o $@ $(LDFLAGS) $(LDFLAGS_STATIC)

build:
@mkdir -p $@

.PHONY: clean
clean:
rm -f build/$(APP) build/$(APP)-static build/$(APP)-shared
test -d build && rmdir -p build || true

else # Build using legacy build system

ifeq ($(RTE_SDK),)
$(error "Please define RTE_SDK environment variable")
endif

# Default target, detect a build directory, by looking for a path with a .config
RTE_TARGET ?= $(notdir $(abspath $(dir $(firstword $(wildcard $(RTE_SDK)/*/.config)))))

include $(RTE_SDK)/mk/rte.vars.mk

ifneq ($(CONFIG_RTE_EXEC_ENV_LINUX),y)
$(error This application can only operate in a linux environment, \\
please change the definition of the RTE_TARGET environment variable)
endif

CFLAGS += -O3
CFLAGS += -DALLOW_EXPERIMENTAL_API
CFLAGS += $(WERROR_FLAGS)

include $(RTE_SDK)/mk/rte.extapp.mk
endif

3.代码讲解

这段代码是一个支持 ARP 协议响应 + UDP 数据包回显的轻量级 DPDK 网络程序,相比上一版新增了关键的 ARP 协议处理能力:

  • 初始化 DPDK 环境和网卡后,程序以轮询方式批量接收网卡数据包;
  • 优先识别 ARP 数据包:若收到针对本机 IP 的 ARP 请求,构造并发送 ARP 响应(返回本机 MAC-IP 映射);
  • 其次处理 UDP 数据包:解析 IPv4 UDP 包的源 / 目地址、端口和负载,构造反向的 UDP 响应包并发送;
  • 全程采用 DPDK 的 “批量收发包 + 预分配 mbuf 内存池” 模式,保证高性能,适配 DPDK 19.08.2 版本。
  • main() // 程序入口
    ├── rte_eal_init() // 1. 初始化DPDK EAL环境
    ├── rte_pktmbuf_pool_create()// 2. 创建mbuf内存池(存储数据包)
    ├── ng_init_port() // 3. 初始化网卡端口(新增获取本机MAC/IP)
    │ ├── rte_eth_dev_count_avail() // 检测可用网卡数量
    │ ├── rte_eth_dev_info_get() // 获取网卡设备信息
    │ ├── rte_eth_dev_configure() // 配置网卡RX/TX队列数
    │ ├── rte_eth_rx_queue_setup() // 初始化RX队列
    │ ├── rte_eth_tx_queue_setup() // 初始化TX队列
    │ ├── rte_eth_dev_start() // 启动网卡
    │ ├── rte_eth_macaddr_get() // 新增:获取网卡MAC地址
    │ └── rte_eth_promiscuous_enable() // 开启混杂模式
    └── 无限循环(收包处理)
    ├── rte_eth_rx_burst() // 批量接收数据包
    ├── 数据包类型判断(ARP/UDP分支)
    │ ├── ARP分支(新增)
    │ │ ├── 解析ARP头(rte_arp_hdr)
    │ │ ├── send_arp_response() // 构造ARP响应包
    │ │ │ ├── rte_pktmbuf_alloc() // 分配mbuf
    │ │ │ ├── ng_encode_arp_pkt() // 编码ARP数据包
    │ │ │ │ ├── rte_cpu_to_be_16() // 主机序转网络大端序
    │ │ │ │ └── memcpy() // 填充MAC/IP字段
    │ │ │ └── rte_pktmbuf_mtod() // 获取mbuf数据指针
    │ │ ├── rte_eth_tx_burst() // 发送ARP响应包
    │ │ └── rte_pktmbuf_free() // 发送失败时释放mbuf
    │ └── UDP分支(原有)
    │ ├── 解析以太网/IP/UDP头
    │ ├── send_udp_response() // 构造UDP响应包
    │ │ ├── rte_pktmbuf_alloc() // 分配mbuf
    │ │ ├── create_eth_ip_udp() // 编码UDP数据包
    │ │ │ ├── rte_pktmbuf_mtod_offset() // 获取指定偏移指针
    │ │ │ ├── rte_cpu_to_be_16() // 主机序转网络大端序
    │ │ │ └── rte_ipv4_cksum() // 计算IP校验和
    │ │ └── rte_eth_tx_burst() // 发送UDP响应包
    │ └── rte_pktmbuf_free() // 发送失败时释放mbuf
    └── rte_pktmbuf_free() // 释放接收的mbuf

    阶段 1:初始化阶段(程序启动时)

    这一阶段完成 DPDK 环境、内存池、网卡的初始化,并保存本机 MAC/IP(为 ARP 响应做准备):

  • EAL 环境初始化调用rte_eal_init()完成 DPDK 底层环境初始化(CPU 绑定、内存划分、设备探测等),失败则直接退出。
  • mbuf 内存池创建调用rte_pktmbuf_pool_create()创建 4095 个 mbuf 的内存池,预分配数据包存储结构,避免运行时动态内存分配。
  • 网卡端口初始化(ng_init_port 函数)
    • 检测可用网卡数量,无则退出;
    • 获取网卡设备信息,配置 1 个 RX 队列 + 1 个 TX 队列;
    • 初始化 RX/TX 队列并启动网卡,开启混杂模式;
    • 新增关键步骤:
      • 调用rte_eth_macaddr_get()获取网卡的 MAC 地址,存入全局变量gPortMacAddr;
      • 手动设置网卡 IP 地址(192.168.3.100),存入全局变量gPortIpAddr;
      • 打印本机 MAC/IP,方便调试。
  • 阶段 2:运行阶段(无限循环收发包)

    初始化完成后,程序进入无限循环,优先处理 ARP 包,再处理 UDP 包,流程如下:

  • 批量收包调用rte_eth_rx_burst()批量接收数据包(每次最多 32 个),返回实际接收的包数量。
  • 逐个处理数据包对每个接收的数据包,按 “ARP→UDP” 的优先级处理:
    • 步骤 1:判断是否为 ARP 包(新增)解析以太网头(rte_ether_hdr),若ether_type为RTE_ETHER_TYPE_ARP(ARP 协议):① 解析 ARP 头(rte_arp_hdr),验证是否为 “IPv4 ARP 请求” 且目标 IP 是本机 IP;② 调用send_arp_response()构造 ARP 响应包:
      • 分配新的 mbuf,设置包长度;
      • 调用ng_encode_arp_pkt()填充以太网头(源 MAC = 本机、目的 MAC = 请求方)和 ARP 头(操作码 = 响应、发送方 IP/MAC = 本机、目标方 IP/MAC = 请求方);③ 调用rte_eth_tx_burst()发送 ARP 响应包,发送失败则释放 mbuf;④ 标记need_process=0,跳过后续 UDP 处理。
    • 步骤 2:判断是否为 UDP 包(原有)若不是 ARP 包,检查是否为 IPv4 UDP 包:① 解析以太网头,验证是否为 IPv4 协议;② 解析 IPv4 头,验证上层协议是否为 UDP;③ 解析 UDP 头,计算并打印 UDP 负载(安全拷贝到缓冲区,防止溢出);④ 调用send_udp_response()构造反向 UDP 响应包(源 / 目 MAC/IP/ 端口互换),发送后打印日志;
    • 步骤 3:释放 mbuf无论是否处理成功,都调用rte_pktmbuf_free()释放接收的 mbuf,归还到内存池复用。
  • 4.编译运行

    成功收到数据:

    同时宿主机arp表自动更新:

    0voice · GitHub

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » dpdk之arp回显代码案例
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!