news 2026/6/16 16:53:32

基于 DPDK 实现简易 UDP 收发包

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于 DPDK 实现简易 UDP 收发包

本文将介绍如何利用dpdk实现udp的发包和收包。 从代码层次来看一共分为四部分——初始化,收包,整合,发包。下面将按照这个顺序进行介绍。本文均为个人观点,如有错误请纠正。
一.初始化。
1.参数初始化:

if(rte_eal_init(argc,argv)<0){rte_exit(EXIT_FAILURE,"Error");//dpdk中失败退出的函数}//参数初始化

这个函数成功的返回值是大于等于0。之后创建用于存放网络数据包的内存池。函数是

structrte_mempool*mbuf_pool=rte_pktmbuf_pool_create("mbuf pool",NUM_MBUFS,0,0,RTE_MBUF_DEFAULT_BUF_SIZE,rte_socket_id());

这个函数一共有6个参数。依次是名称;内存池内部有多少内存格;每个CPU核的缓存大小;每个内存格的私有大小;每个内存格的数据区大小;内存分配在哪个 NUMA 节点上。举个例子:这个内存池就好比一个柜子,它的名字是什么?内部有多少个收纳区(在dpdk中每个收纳区大小都一样)?每个 CPU 核可以私自偷偷缓存几个格子(0 表示不私藏,用一个取一个)?每个收纳格除了放东西之外,还能贴多大的私人便利贴(0 表示不贴)?每个收纳区最多能放多少东西?这个柜子在当前 CPU 所在的哪个房间?
之后进行硬件初始化:
1.先查看有几个网卡可用

uint16_tnb_sys_ports=rte_eth_dev_count_avail();

2.之后拿到这个对应网卡的设备信息

structrte_eth_dev_infodev_info;rte_eth_dev_info_get(globle_portid,&dev_info);// globle_portid = 0 代表第1个网卡 1-->2 以此类推

3.初始化网卡:
(1).这里首先配置接收模式(rxmode)中,最大能接收的包长度 = 标准以太网最大帧长(1518 字节)。其余配置项没写,全部用默认值。

staticconststructrte_eth_confport_conf_default={.rxmode={.max_rx_pkt_len=RTE_ETHER_MAX_LEN}//最大能接收的包长度 = 标准以太网最大帧长};

(2).然后把接收和发送队列全部设置只有一列。

constintnum_rx_queues=1;constintnum_tx_queues=1;

(3).配置网口。四个参数分别是哪个网卡?开几个接收队列?开几个发送队列?具体怎么配置?

rte_eth_dev_configure(globle_portid,num_rx_queues,num_tx_queues,&port_conf_default);

(4).队列初始化:
接收队列——rte_eth_rx_queue_setup六个参数依次是:哪个网卡?初始化几号队列?这个队列能放多少描述符?内存分配在哪个 NUMA 节点?接收队列的额外配置(NULL是默认)?收到的包往哪个内存池里放?
发送队列——rte_eth_tx_queue_setup五个参数依次是:哪个网卡?初始化几号队列?这个队列能放多少描述符?内存分配在哪个 NUMA 节点?发送队列的额外配置?这里之所以单独设置,是因为在前面设置了rxmode,为了让收发两侧的 offload 配置保持一致,满足 DPDK 网卡驱动的要求,避免初始化失败或运行异常。

if(rte_eth_rx_queue_setup(globle_portid,0,128,rte_eth_dev_socket_id(globle_portid),NULL,mbuf_pool)<0){rte_exit(EXIT_FAILURE,"could not setup RX queue\n");}structrte_eth_txconftxq_conf=dev_info.default_txconf;txq_conf.offloads=port_conf_default.rxmode.offloads;if(rte_eth_tx_queue_setup(globle_portid,0,512,rte_eth_dev_socket_id(globle_portid),&txq_conf)<0){rte_exit(EXIT_FAILURE,"could not setup TX queue\n");}

(5).启动

if(rte_eth_dev_start(globle_portid)<0){rte_exit(EXIT_FAILURE,"could not start\n");}

二.收包
1.接收。
四个参数依次是:哪个网卡?从第几个接收队列收?收到的包存到哪里?最多收几个包?

uint16_tnum_recvd=rte_eth_rx_burst(globle_portid,0,mbufs,BURST_SIZE);if(num_recvd>BURST_SIZE){rte_exit(EXIT_FAILURE,"recv error\n");}

2.判断。

先判断以太网头里是不是ipv4协议,不是跳出循环。接着看ip头下一个是不是udp头。是则把数据拷贝便于后续发包。

inti=0;for(i=0;i<num_recvd;i++){structrte_ether_hdr*ethhdr=rte_pktmbuf_mtod(mbufs[i],structrte_ether_hdr*);if(ethhdr->ether_type!=rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4)){continue;}structrte_ipv4_hdr*iphdr=rte_pktmbuf_mtod_offset(mbufs[i],structrte_ipv4_hdr*,sizeof(structrte_ether_hdr));if(iphdr->next_proto_id==IPPROTO_UDP){structrte_udp_hdr*udphdr=(structrte_udp_hdr*)(iphdr+1);rte_memcpy(global_smac,ethhdr->d_addr.addr_bytes,RTE_ETHER_ADDR_LEN);rte_memcpy(global_dmac,ethhdr->s_addr.addr_bytes,RTE_ETHER_ADDR_LEN);rte_memcpy(&global_sip,&iphdr->dst_addr,sizeof(uint32_t));rte_memcpy(&global_dip,&iphdr->src_addr,sizeof(uint32_t));rte_memcpy(&global_sport,&udphdr->dst_port,sizeof(uint16_t));rte_memcpy(&global_dport,&udphdr->src_port,sizeof(uint16_t));uint16_tlength=ntohs(udphdr->dgram_len);uint16_ttotal_length=length+sizeof(structrte_ipv4_hdr)+sizeof(structrte_ether_hdr);}

rte_pktmbuf_mtod:作用是从 mbuf 里取出数据包的起始地址,并转成你指定的类型指针。rte_pktmbuf_mtod_offset就是带偏移量版本的。ntohs(udphdr->dgram_len);dgram_len这个长度是udp头+数据的总长。
三.整合
以太网头

ip头

udp头

只需要将上图每种包的内容,按照顺序(以太网头,ip头,udp头+数据)粘贴进去就行了

staticintustack_encode_udp_pkt(uint8_t*msg,uint8_t*data,uint16_ttotal_len){//ether headerstructrte_ether_hdr*eth=(structrte_ether_hdr*)msg;rte_memcpy(eth->d_addr.addr_bytes,global_dmac,RTE_ETHER_ADDR_LEN);rte_memcpy(eth->s_addr.addr_bytes,global_smac,RTE_ETHER_ADDR_LEN);eth->ether_type=htons(RTE_ETHER_TYPE_IPV4);//ip header// struct rte_ipv4_hdr *ip = msg + sizeof(struct rte_ether_hdr);structrte_ipv4_hdr*ip=(structrte_ipv4_hdr*)(eth+1);ip->version_ihl=0x45;ip->type_of_service=0;ip->total_length=htons(total_len-sizeof(structrte_ether_hdr));ip->packet_id=0;ip->fragment_offset=0;ip->time_to_live=64;ip->next_proto_id=IPPROTO_UDP;ip->src_addr=global_sip;ip->dst_addr=global_dip;ip->hdr_checksum=0;ip->hdr_checksum=rte_ipv4_cksum(ip);// udp headerstructrte_udp_hdr*udp=(structrte_udp_hdr*)(ip+1);udp->src_port=global_sport;udp->dst_port=global_dport;uint16_tudplen=total_len-sizeof(structrte_ether_hdr)-sizeof(structrte_ipv4_hdr);udp->dgram_len=htons(udplen);rte_memcpy((uint8_t*)(udp+1),data,udplen);udp->dgram_cksum=0;udp->dgram_cksum=rte_ipv4_udptcp_cksum(ip,udp);return0;}

四.发包
先要在内存池中开一个内存区用来存放整合包的位置,pkt_len 是整个包的总长度,data_len 是当前这段数据的长度。拿到 mbuf 数据区的起始地址,转成 uint8_t *,后续往这里写数据。把以太网头、IP头、UDP头、payload 整合成一个完整的 UDP 包,写进 msg 指向的内存。udphdr + 1 指向的是 UDP 头之后的 payload 数据。

structrte_mbuf*mbuf=rte_pktmbuf_alloc(mbuf_pool);if(!mbuf){rte_exit(EXIT_FAILURE,"alloc\n");}mbuf->pkt_len=total_length;mbuf->data_len=total_length;uint8_t*msg=rte_pktmbuf_mtod(mbuf,uint8_t*);ustack_encode_udp_pkt(msg,(uint8_t*)(udphdr+1),total_length);rte_eth_tx_burst(globle_portid,0,&mbuf,1);

五.整体代码和运行结果

#include<stdio.h>#include<rte_eal.h>#include<rte_ethdev.h>#include<arpa/inet.h>#defineNUM_MBUFS4096#defineBURST_SIZE128uint8_tglobal_smac[RTE_ETHER_ADDR_LEN];uint8_tglobal_dmac[RTE_ETHER_ADDR_LEN];uint32_tglobal_sip;uint32_tglobal_dip;uint16_tglobal_sport;uint16_tglobal_dport;intgloble_portid=0;//网口从0开始staticconststructrte_eth_confport_conf_default={.rxmode={.max_rx_pkt_len=RTE_ETHER_MAX_LEN}};staticintustack_init_port(structrte_mempool*mbuf_pool){uint16_tnb_sys_ports=rte_eth_dev_count_avail();//有几个网卡可用// printf("nb_sys_ports: %d\n",nb_sys_ports);if(nb_sys_ports==0){rte_exit(EXIT_FAILURE,"No support eth\n");}structrte_eth_dev_infodev_info;rte_eth_dev_info_get(globle_portid,&dev_info);constintnum_rx_queues=1;constintnum_tx_queues=1;rte_eth_dev_configure(globle_portid,num_rx_queues,num_tx_queues,&port_conf_default);if(rte_eth_rx_queue_setup(globle_portid,0,128,rte_eth_dev_socket_id(globle_portid),NULL,mbuf_pool)<0){rte_exit(EXIT_FAILURE,"could not setup RX queue\n");}#ifENABLE_SENDstructrte_eth_txconftxq_conf=dev_info.default_txconf;txq_conf.offloads=port_conf_default.rxmode.offloads;if(rte_eth_tx_queue_setup(globle_portid,0,512,rte_eth_dev_socket_id(globle_portid),&txq_conf)<0){rte_exit(EXIT_FAILURE,"could not setup TX queue\n");}#endifif(rte_eth_dev_start(globle_portid)<0){rte_exit(EXIT_FAILURE,"could not start\n");}return0;}staticintustack_encode_udp_pkt(uint8_t*msg,uint8_t*data,uint16_ttotal_len){//ether headerstructrte_ether_hdr*eth=(structrte_ether_hdr*)msg;rte_memcpy(eth->d_addr.addr_bytes,global_dmac,RTE_ETHER_ADDR_LEN);rte_memcpy(eth->s_addr.addr_bytes,global_smac,RTE_ETHER_ADDR_LEN);eth->ether_type=htons(RTE_ETHER_TYPE_IPV4);//ip header// struct rte_ipv4_hdr *ip = msg + sizeof(struct rte_ether_hdr);structrte_ipv4_hdr*ip=(structrte_ipv4_hdr*)(eth+1);ip->version_ihl=0x45;ip->type_of_service=0;ip->total_length=htons(total_len-sizeof(structrte_ether_hdr));ip->packet_id=0;ip->fragment_offset=0;ip->time_to_live=64;ip->next_proto_id=IPPROTO_UDP;ip->src_addr=global_sip;ip->dst_addr=global_dip;ip->hdr_checksum=0;ip->hdr_checksum=rte_ipv4_cksum(ip);// udp headerstructrte_udp_hdr*udp=(structrte_udp_hdr*)(ip+1);udp->src_port=global_sport;udp->dst_port=global_dport;uint16_tudplen=total_len-sizeof(structrte_ether_hdr)-sizeof(structrte_ipv4_hdr);udp->dgram_len=htons(udplen);rte_memcpy((uint8_t*)(udp+1),data,udplen);udp->dgram_cksum=0;udp->dgram_cksum=rte_ipv4_udptcp_cksum(ip,udp);return0;}intmain(intargc,charconst*argv[]){if(rte_eal_init(argc,argv)<0){rte_exit(EXIT_FAILURE,"Error");}//参数初始化structrte_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\n");}ustack_init_port(mbuf_pool);while(1){structrte_mbuf*mbufs[BURST_SIZE]={0};uint16_tnum_recvd=rte_eth_rx_burst(globle_portid,0,mbufs,BURST_SIZE);if(num_recvd>BURST_SIZE){rte_exit(EXIT_FAILURE,"recv error\n");}inti=0;for(i=0;i<num_recvd;i++){structrte_ether_hdr*ethhdr=rte_pktmbuf_mtod(mbufs[i],structrte_ether_hdr*);if(ethhdr->ether_type!=rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4)){continue;}structrte_ipv4_hdr*iphdr=rte_pktmbuf_mtod_offset(mbufs[i],structrte_ipv4_hdr*,sizeof(structrte_ether_hdr));if(iphdr->next_proto_id==IPPROTO_UDP){structrte_udp_hdr*udphdr=(structrte_udp_hdr*)(iphdr+1);rte_memcpy(global_smac,ethhdr->d_addr.addr_bytes,RTE_ETHER_ADDR_LEN);rte_memcpy(global_dmac,ethhdr->s_addr.addr_bytes,RTE_ETHER_ADDR_LEN);rte_memcpy(&global_sip,&iphdr->dst_addr,sizeof(uint32_t));rte_memcpy(&global_dip,&iphdr->src_addr,sizeof(uint32_t));rte_memcpy(&global_sport,&udphdr->dst_port,sizeof(uint16_t));rte_memcpy(&global_dport,&udphdr->src_port,sizeof(uint16_t));uint16_tlength=ntohs(udphdr->dgram_len);uint16_ttotal_length=length+sizeof(structrte_ipv4_hdr)+sizeof(structrte_ether_hdr);structrte_mbuf*mbuf=rte_pktmbuf_alloc(mbuf_pool);if(!mbuf){rte_exit(EXIT_FAILURE,"alloc\n");}mbuf->pkt_len=total_length;mbuf->data_len=total_length;uint8_t*msg=rte_pktmbuf_mtod(mbuf,uint8_t*);ustack_encode_udp_pkt(msg,(uint8_t*)(udphdr+1),total_length);rte_eth_tx_burst(globle_portid,0,&mbuf,1);printf("udp recv:%s\n",(char*)(udphdr+1));}}}return0;}


零声社区资源链接:https:github.com/0voice

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/16 16:52:29

告别.NET升级噩梦:AI驱动的现代化工具让你轻松迁移到.NET 10

告别.NET升级噩梦&#xff1a;AI驱动的现代化工具让你轻松迁移到.NET 10 【免费下载链接】modernize-dotnet A tool to assist developers in upgrading .NET Framework applications to .NET 6 and beyond 项目地址: https://gitcode.com/gh_mirrors/up/modernize-dotnet …

作者头像 李华
网站建设 2026/6/16 16:51:20

Ubuntu语音录制全链路指南:从ALSA到Audacity的可控工作流

1. 这不是“装个软件就完事”的教程&#xff0c;而是一份能让你真正听清自己声音的Ubuntu语音工作流你刚装好Ubuntu&#xff0c;桌面清爽、终端顺手&#xff0c;但一想录段语音——比如给团队发个技术说明、做个小语种发音练习、或者剪辑播客初稿——立刻卡在第一步&#xff1a…

作者头像 李华
网站建设 2026/6/16 16:49:11

Java计算机毕设之基于 Spring Boot 的学生社团台账与资源管理系统设计 智慧校园视角下高校社团管理系统设计与实现(完整前后端代码+说明文档+LW,调试定制等)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华
网站建设 2026/6/16 16:49:00

深入解析iOS WebKit Debug Proxy安全通信机制:Base64与SHA1实战实现

深入解析iOS WebKit Debug Proxy安全通信机制&#xff1a;Base64与SHA1实战实现 【免费下载链接】ios-webkit-debug-proxy A DevTools proxy (Chrome Remote Debugging Protocol) for iOS devices (Safari Remote Web Inspector). 项目地址: https://gitcode.com/gh_mirrors/…

作者头像 李华
网站建设 2026/6/16 16:42:42

Centos7安装maven 3.9.11

Centos7安装maven 3.9.11&#x1f50d; 前置准备&#xff1a;先确认/安装JDK&#x1f4e6; 安装 Maven 3.9.11⚙️ 配置环境变量✅ 验证安装&#x1f680; 配置国内镜像&#xff08;必做&#xff0c;否则下依赖极慢&#xff09;❓ 常见问题排查CentOS7 安装 Maven 3.9.11 非常简…

作者头像 李华
网站建设 2026/6/16 16:41:22

2026中小艺培校长亲测:培训机构管理系统避坑指南,搞定排课家校

开画室、琴行、舞蹈班这几年&#xff0c;我最深的感受是&#xff1a;教学不难&#xff0c;管理太累。招生、排课、记课时、收退费、盘物料、回家长消息&#xff0c;每天被琐事绑住&#xff0c;根本没时间抓教学。身边同行都在找靠谱的教培机构教务软件&#xff0c;但要么太复杂…

作者头像 李华