基于 DPDK 实现简易 UDP 收发包

📅 2026/6/16 16:53:35
基于 DPDK 实现简易 UDP 收发包
本文将介绍如何利用dpdk实现udp的发包和收包。 从代码层次来看一共分为四部分——初始化收包整合发包。下面将按照这个顺序进行介绍。本文均为个人观点如有错误请纠正。一.初始化。1.参数初始化if(rte_eal_init(argc,argv)0){rte_exit(EXIT_FAILURE,Error);//dpdk中失败退出的函数}//参数初始化这个函数成功的返回值是大于等于0。之后创建用于存放网络数据包的内存池。函数是structrte_mempool*mbuf_poolrte_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_portsrte_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_lenRTE_ETHER_MAX_LEN}//最大能接收的包长度 标准以太网最大帧长};(2).然后把接收和发送队列全部设置只有一列。constintnum_rx_queues1;constintnum_tx_queues1;(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_confdev_info.default_txconf;txq_conf.offloadsport_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_recvdrte_eth_rx_burst(globle_portid,0,mbufs,BURST_SIZE);if(num_recvdBURST_SIZE){rte_exit(EXIT_FAILURE,recv error\n);}2.判断。先判断以太网头里是不是ipv4协议不是跳出循环。接着看ip头下一个是不是udp头。是则把数据拷贝便于后续发包。inti0;for(i0;inum_recvd;i){structrte_ether_hdr*ethhdrrte_pktmbuf_mtod(mbufs[i],structrte_ether_hdr*);if(ethhdr-ether_type!rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4)){continue;}structrte_ipv4_hdr*iphdrrte_pktmbuf_mtod_offset(mbufs[i],structrte_ipv4_hdr*,sizeof(structrte_ether_hdr));if(iphdr-next_proto_idIPPROTO_UDP){structrte_udp_hdr*udphdr(structrte_udp_hdr*)(iphdr1);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_tlengthntohs(udphdr-dgram_len);uint16_ttotal_lengthlengthsizeof(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_typehtons(RTE_ETHER_TYPE_IPV4);//ip header// struct rte_ipv4_hdr *ip msg sizeof(struct rte_ether_hdr);structrte_ipv4_hdr*ip(structrte_ipv4_hdr*)(eth1);ip-version_ihl0x45;ip-type_of_service0;ip-total_lengthhtons(total_len-sizeof(structrte_ether_hdr));ip-packet_id0;ip-fragment_offset0;ip-time_to_live64;ip-next_proto_idIPPROTO_UDP;ip-src_addrglobal_sip;ip-dst_addrglobal_dip;ip-hdr_checksum0;ip-hdr_checksumrte_ipv4_cksum(ip);// udp headerstructrte_udp_hdr*udp(structrte_udp_hdr*)(ip1);udp-src_portglobal_sport;udp-dst_portglobal_dport;uint16_tudplentotal_len-sizeof(structrte_ether_hdr)-sizeof(structrte_ipv4_hdr);udp-dgram_lenhtons(udplen);rte_memcpy((uint8_t*)(udp1),data,udplen);udp-dgram_cksum0;udp-dgram_cksumrte_ipv4_udptcp_cksum(ip,udp);return0;}四.发包先要在内存池中开一个内存区用来存放整合包的位置pkt_len 是整个包的总长度data_len 是当前这段数据的长度。拿到 mbuf 数据区的起始地址转成 uint8_t *后续往这里写数据。把以太网头、IP头、UDP头、payload 整合成一个完整的 UDP 包写进 msg 指向的内存。udphdr 1 指向的是 UDP 头之后的 payload 数据。structrte_mbuf*mbufrte_pktmbuf_alloc(mbuf_pool);if(!mbuf){rte_exit(EXIT_FAILURE,alloc\n);}mbuf-pkt_lentotal_length;mbuf-data_lentotal_length;uint8_t*msgrte_pktmbuf_mtod(mbuf,uint8_t*);ustack_encode_udp_pkt(msg,(uint8_t*)(udphdr1),total_length);rte_eth_tx_burst(globle_portid,0,mbuf,1);五.整体代码和运行结果#includestdio.h#includerte_eal.h#includerte_ethdev.h#includearpa/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_portid0;//网口从0开始staticconststructrte_eth_confport_conf_default{.rxmode{.max_rx_pkt_lenRTE_ETHER_MAX_LEN}};staticintustack_init_port(structrte_mempool*mbuf_pool){uint16_tnb_sys_portsrte_eth_dev_count_avail();//有几个网卡可用// printf(nb_sys_ports: %d\n,nb_sys_ports);if(nb_sys_ports0){rte_exit(EXIT_FAILURE,No support eth\n);}structrte_eth_dev_infodev_info;rte_eth_dev_info_get(globle_portid,dev_info);constintnum_rx_queues1;constintnum_tx_queues1;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_confdev_info.default_txconf;txq_conf.offloadsport_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_typehtons(RTE_ETHER_TYPE_IPV4);//ip header// struct rte_ipv4_hdr *ip msg sizeof(struct rte_ether_hdr);structrte_ipv4_hdr*ip(structrte_ipv4_hdr*)(eth1);ip-version_ihl0x45;ip-type_of_service0;ip-total_lengthhtons(total_len-sizeof(structrte_ether_hdr));ip-packet_id0;ip-fragment_offset0;ip-time_to_live64;ip-next_proto_idIPPROTO_UDP;ip-src_addrglobal_sip;ip-dst_addrglobal_dip;ip-hdr_checksum0;ip-hdr_checksumrte_ipv4_cksum(ip);// udp headerstructrte_udp_hdr*udp(structrte_udp_hdr*)(ip1);udp-src_portglobal_sport;udp-dst_portglobal_dport;uint16_tudplentotal_len-sizeof(structrte_ether_hdr)-sizeof(structrte_ipv4_hdr);udp-dgram_lenhtons(udplen);rte_memcpy((uint8_t*)(udp1),data,udplen);udp-dgram_cksum0;udp-dgram_cksumrte_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_poolrte_pktmbuf_pool_create(mbuf pool,NUM_MBUFS,0,0,RTE_MBUF_DEFAULT_BUF_SIZE,rte_socket_id());if(mbuf_poolNULL){rte_exit(EXIT_FAILURE,mbuf\n);}ustack_init_port(mbuf_pool);while(1){structrte_mbuf*mbufs[BURST_SIZE]{0};uint16_tnum_recvdrte_eth_rx_burst(globle_portid,0,mbufs,BURST_SIZE);if(num_recvdBURST_SIZE){rte_exit(EXIT_FAILURE,recv error\n);}inti0;for(i0;inum_recvd;i){structrte_ether_hdr*ethhdrrte_pktmbuf_mtod(mbufs[i],structrte_ether_hdr*);if(ethhdr-ether_type!rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4)){continue;}structrte_ipv4_hdr*iphdrrte_pktmbuf_mtod_offset(mbufs[i],structrte_ipv4_hdr*,sizeof(structrte_ether_hdr));if(iphdr-next_proto_idIPPROTO_UDP){structrte_udp_hdr*udphdr(structrte_udp_hdr*)(iphdr1);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_tlengthntohs(udphdr-dgram_len);uint16_ttotal_lengthlengthsizeof(structrte_ipv4_hdr)sizeof(structrte_ether_hdr);structrte_mbuf*mbufrte_pktmbuf_alloc(mbuf_pool);if(!mbuf){rte_exit(EXIT_FAILURE,alloc\n);}mbuf-pkt_lentotal_length;mbuf-data_lentotal_length;uint8_t*msgrte_pktmbuf_mtod(mbuf,uint8_t*);ustack_encode_udp_pkt(msg,(uint8_t*)(udphdr1),total_length);rte_eth_tx_burst(globle_portid,0,mbuf,1);printf(udp recv:%s\n,(char*)(udphdr1));}}}return0;}零声社区资源链接https:github.com/0voice