一、DPDK之UDP收包

本文记录了作者学习DPDK技术的第一天,主要介绍了如何使用DPDK官方1908版本代码搭建环境,配置以太网设备eth0,设置接收队列,并编写了一个简单的程序来接收和解析UDP数据包。程序通过网络调试助手发送数据,成功打印出源和目标地址及数据内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近对DPDK技术颇感兴趣,工作之余想要系统性学习一下。借此平台记录下学习心得,方便以后回顾。
DPDK环境搭建就不赘述了,学习采用DPDK官方代码的1908版本。

今天记录下第一天的收获。

#include<rte_eal.h>
#include<rte_ethdev.h>
#include<rte_mbuf.h>
#include<stdio.h>
#include<arpa/inet.h>

int gDpdkPortId = 0;//eth0
#define NUM_MBUFS (4096-1)

#define BURST_SIZE	32

static const struct rte_eth_conf port_conf_default = {
.rxmode = {.max_rx_pkt_len = RTE_ETHER_MAX_LEN} //RTE_ETHER_MAX_LEN 以太网数据中长度,一般为1518
}

static void ng_init_port(struct rte_mempool *mbuf_pool)
{
	//查询系统中可用的以太网设备数量,比如eth0,eth1等
	uint16_t nb_sys_ports = rte_eth_dev_count_avail();
	if(nb_sys_ports == 0)
	{
		rte_exit(EXIT,"no eth dev is availble\n");
	}
	struct rte_eth_dev_info dev_info;
	//查询以太网接口属性,此处的id = 0,代表查询eth0
	rte_eth_dev_info_get(gDpdkPortId,&dev_info);

	const int num_rx_queues = 1;//设置接受队列大小,通常每个队列与一个独立CPU关联
	const int num_tx_queues = 0;
	struct rte_eth_conf port_conf = port_conf_default;
	//配置eth0相关属性,用于后面接收发送数据包
	rte_eth_dev_configure(gDpdkPortId,num_rx_queues,num_tx_queues,&port_conf);

	//用于配置以太网设备的接收队列
	ifrte_eth_rx_queue_setup(gDpdkPortId,0,128,
		rte_eth_dev_socket_id(gDpdkPortId),NULL,mbuf_pool) < 0{
		rte_exit(EXIT,"could not setup RX queue\n");
	}

	//启动指定的网卡,使其能够接收和发送数据包
	//初始化指定的以太网设备,配置接收队列和设备属性,并启动该网卡,以便进行数据包的收发和处理操作
	if(rte_eth_dev_start(gDpdkPortId) < 0)
	{
		rte_exit(EXIT,"can not start\n");
	}
}

int main(int argc,char *argv[])
{
	//初始化EAL环境
	if(rte_eal_init(argc,argv) < 0 )
	{
		rte_exit(EXIT,"Error with EAL init\n");
	}
	//创建内存池
	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,"Could not create mbuf pool\n");
	}

	//mbuf_pool 是一个预先创建好的内存池,它将被用于接收队列来存储数据包的缓冲区
	ng_init_port(mbuf_pool);

	while(1)
	{
		struct rte_mbuf *mbufs[BURST_SIZE];
		//mbufs用于存储数据包的缓冲区结构体
		//BURST_SIZE表示每次从网卡接收数据包的最大数量
		unsigned num_recvd = rte_eth_rx_burst(gDpdkPortId,0,mbufs,BURST_SIZE);
		if(num_recvd > BURST_SIZE)
		{
			rte_exit(EXIT,"Error receiving from eth\n");
		}

		unsigned i = 0;
		for(i = 0;i < num_recvd;i++)
		{
			//rte_ether_hdr是DPDK 中用于表示以太网数据包头部的结构体
			//rte_pktmbuf_mtod用于将数据包缓冲区中的数据指针转换为特定类型的指针,以方便对数据包头部进行解析
			struct rte_ether_hdr ehdr = rte_pktmbuf_mtod(mbufs[i],struct rte_ether_hdr *);
			//rte_cpu_to_be_16用于将 16 位的数据从主机字节序(CPU 字节序)转换为网络字节序(大端字节序)
			if(ehdr->ether_type != rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4))
			{
				continue;
			}
			//rte_pktmbuf_mtod_offset来获取数据包缓冲区中 IPv4 头部的指针
			//将数据包偏移以太网数据包头部大小后,就是IPV4头部信息,再转换为struct rte_ipv4_hdr *
			struct rte_ipv4_hdr * iphdr = rte_pktmbuf_mtod_offset(mbufs[i],struct rte_ipv4_hdr *,
			sizeof(struct rte_ether_hdr));
			
			if(iphdr->next_proto_id == IPPROTO_UDP)
			{
				//(iphdr + 1) +1指的是偏移rte_ipv4_hdr大小
				struct rte_udp_hdr *udphdr = (struct rte_udp_hdr *)(iphdr + 1);
				uint16_t length = ntohs(udphdr->dgram_len);
				*((char *)udphdr + length) = '\0';

				struct in_addr addr;
				addr.s_addr = iphdr->src_addr;
				printf("src: %s:%d\n",inet_ntoa(addr),udphdr->src_port);

				addr.s_addr = iphdr->dst_addr;
				printf("dst: %s:%d data:%s\n",inet_ntoa(addr),udphdr->dst_port,(char *)(udphdr+1));

				rte_pktmbuf_free(mbufs[i]);
			}
		}
	}
}

接下来在配置好的DPDK环境中编译运行程序,通过网络调试助手发送数据。
在这里插入图片描述

最后可以看到程序将得到的UDP数据打印出来,至此,自己写的第一个关于DPDK的收包程序完毕,可以睡个好觉了!!明天再冲

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

写一封情书

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值