【DPDK】基于dpdk实现用户态UDP网络协议栈
文章目录
- 一.背景及导言
- 二.协议栈架构设计
- 1. 数据包接收和发送引擎
- 2. 协议解析
- 3. 数据包处理逻辑
- 三.网络函数编写
- 1.socket
- 2.bind
- 3.recvfrom
- 4.sendto
- 5.close
- 四.总结
一.背景及导言
在当今数字化的世界中,网络通信的高性能和低延迟对于许多应用至关重要。而用户态网络协议栈通过摆脱传统内核态协议栈的限制,为实现更快速、灵活的数据包处理提供了新的可能性。本文将深入探讨基于DPDK的用户态UDP网络协议栈的设计、实现。
传统的内核态协议栈在处理网络通信时通常伴随着较大的性能开销,而用户态网络协议栈的崛起为高性能应用带来了全新的解决方案。DPDK,作为一款用于高性能数据平面应用的工具包,为用户态网络协议栈的实现提供了强大的支持。通过将网络协议栈移植到用户态,我们可以更灵活地优化数据包处理、提高吞吐量,并有效降低处理延迟。
二.协议栈架构设计
网络协议栈整体大致架构如下图所示:
1. 数据包接收和发送引擎
数据包接收和发送引擎负责从网络接口接收数据包,并将数据包发送到目标地址。通过DPDK提供的高性能数据包I/O接口,实现对多队列的支持,以提高并行性和吞吐量。
从网卡接收原始数据放入in_ring
:
rte_eth_rx_burst();
从out_ring
中取出数据通过网卡发送:
rte_eth_tx_burst();
while(1) {// rxstruct rte_mbuf *rx[BURST_SIZE];// 内存池//接收unsigned num_recvd = rte_eth_rx_burst(gDpdkPortId, 0, rx, BURST_SIZE);if(num_recvd > BURST_SIZE) {rte_exit(EXIT_FAILURE, "Error receiving from eth\n");} else if(num_recvd > 0) {//入队列rte_ring_sp_enqueue_burst(ring->in, (void**)rx, num_recvd, NULL);}// txstruct rte_mbuf *tx[BURST_SIZE];//出队列unsigned nb_tx = rte_ring_sc_dequeue_burst(ring->out, (void**)tx, BURST_SIZE,NULL);if(nb_tx > 0) {//发送rte_eth_tx_burst(gDpdkPortId, 0, tx, nb_tx);unsigned i = 0;for(;i < nb_tx; i++) {rte_pktmbuf_free(tx[i]);}}static uint64_t prev_tsc = 0, cur_tsc;uint64_t diff_tsc;cur_tsc = rte_rdtsc();diff_tsc = cur_tsc - prev_tsc;if(diff_tsc > TIMER_RESOLUTION_CYCLES) {rte_timer_manage();prev_tsc = cur_tsc;}}
2. 协议解析
协议解析模块负责对接收到的UDP数据包进行解析,提取出源和目标端口号、校验和等关键信息。采用高效的解析算法,确保对数据包的处理不成为性能瓶颈。
从原始数据包中解析以太网头
:
struct rte_ether_hdr *ehdr = rte_pktmbuf_mtod(mbufs[i],struct rte_ether_hdr*);
从原始数据包中(偏移以太网头)解析arp头
:
struct rte_arp_hdr *ahdr = rte_pktmbuf_mtod_offset(mbufs[i],struct rte_arp_hdr *,sizeof(struct rte_ether_hdr));
从原始数据包中解析IP头
:
struct rte_ipv4_hdr *iphdr = rte_pktmbuf_mtod_offset(mbufs[i], struct rte_ipv4_hdr *, sizeof(struct rte_ether_hdr));
通过IP头中的网络类型协议可以得知该数据包是UDP,TCP或ICMP包
,通过类型强制转换可以得到相对应的数据包协议头。
通过IP头偏移1位强转可得到UDP/TCP头
:
struct rte_udp_hdr *udphdr = (struct rte_udp_hdr *)(iphdr + 1);
通过IP头偏移1位强转可得到ICMP头
:
struct rte_icmp_hdr *icmphdr = (struct rte_icmp_hdr *)(iphdr + 1);
不同的数据包调用不同的函数处理,通过对数据包的解析可以得到我们想要的IP地址,端口号,以太网地址,数据等。
3. 数据包处理逻辑
数据包处理逻辑包括各种应用层的逻辑,如数据包过滤、路由决策等。这一部分需要具体根据应用场景进行定制,以满足不同需求。
当用户接收并处理完数据包后得到新的用户数据需要发送,此时我们只需要逆向操作接收数据包的过程
即可。
一个UDP数据帧组成结构如图所示,在用户数据上添加UDP头,在此基础上再添加IP头,最后再添加以太网头,一个UDP数据帧就组装完毕,就可直接通过网卡发送。
按UDP数据帧结构从用户数据从上往下依次组包。
!](https://img-blog.csdnimg.cn/direct/ede89757233f4dca8eff2eec63826075.png)
//1 etherstruct rte_ether_hdr *eth = (struct rte_ether_hdr*)msg;rte_memcpy(eth->s_addr.addr_bytes, src_mac, RTE_ETHER_ADDR_LEN);//源Mac地址rte_memcpy(eth->d_addr.addr_bytes, dst_mac, RTE_ETHER_ADDR_LEN);//目的Mac地址eth->ether_type = htons(RTE_ETHER_TYPE_IPV4);//类型
//2 iphdrstruct rte_ipv4_hdr *ip = (struct rte_ipv4_hdr*)(msg + sizeof(struct rte_ether_hdr));ip->version_ihl = 0x45; //4位版本,4位首部长度ip->type_of_service = 0;//服务类型ip->total_length = htons(length - sizeof(struct rte_ether_hdr));//总长度ip->packet_id = 0;//16位标识ip->fragment_offset = 0;//偏移ip->time_to_live = 64; //TTLip->next_proto_id = IPPROTO_UDP;//8位协议ip->src_addr = sip;ip->dst_addr = dip;ip->hdr_checksum = 0;ip->hdr_checksum = rte_ipv4_cksum(ip);//首部校验和
//3 udpstruct rte_udp_hdr *udp = (struct rte_udp_hdr*)(msg + sizeof(struct rte_ether_hdr) + sizeof(struct rte_ipv4_hdr));udp->src_port = sport;//源端口udp->dst_port = dport;//目的端口uint16_t udplen = length - sizeof(struct rte_ether_hdr) - sizeof(struct rte_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);//校验和
所有数据包都有以太网头
,IP头
和arp头
为第二层,TCP
UDP
ICMP
为第三次,数据组包的时候只需根据需求选择不同的协议填空
即可。
三.网络函数编写
定义主机,包括:唯一标识符,IP地址,Mac地址,协议,recvbuf,senfbuf,互斥锁,条件变量,链表结构。
struct localhost {int fd;uint32_t localip;uint8_t localmac[RTE_ETHER_ADDR_LEN];uint16_t localport;uint8_t protocol;struct rte_ring *recvbuf;struct rte_ring *sendbuf;struct localhost *prev;struct localhost *next;pthread_cond_t cond;pthread_mutex_t mutex;
};static struct localhost *lhost = NULL;
使用Hook自定义网络编程函数,或自定义网络函数名。
1.socket
static int
socket(__attribute__((unused))int domain, int type, __attribute__((unused))int protocol) {int fd = get_fd_frombitmap();struct localhost *host = rte_malloc("localhost", sizeof(struct localhost), 0);if(host == NULL) {return -1;}memset(host, 0, sizeof(struct localhost));host->fd = fd;if(type == SOCK_DGRAM) {host->protocol = IPPROTO_UDP;} host->recvbuf = rte_ring_create("recv buf",RING_SIZE,rte_socket_id(),RING_F_SP_ENQ | RING_F_SC_DEQ);if(host->recvbuf == NULL) {rte_free(host);return -1;}host->sendbuf = rte_ring_create("send buf",RING_SIZE,rte_socket_id(),RING_F_SP_ENQ | RING_F_SC_DEQ);if(host->sendbuf == NULL) {rte_ring_free(host->recvbuf);rte_free(host);return -1;}pthread_cond_t blank_cond = PTHREAD_COND_INITIALIZER;rte_memcpy(&host->cond, &blank_cond, sizeof(pthread_cond_t));pthread_mutex_t blank_mutex = PTHREAD_MUTEX_INITIALIZER;rte_memcpy(&host->mutex, &blank_mutex, sizeof(pthread_mutex_t));LL_ADD(host, lhost);return fd;
}
2.bind
static int bind(int sockfd, const struct sockaddr *addr,__attribute__((unused))socklen_t addrlen) {struct localhost *host = get_hostinfo_fromfd(sockfd);if(host == NULL) {return -1;}const struct sockaddr_in *laddr = (const struct sockaddr_in*)addr;host->localport = laddr->sin_port;rte_memcpy(&host->localip, &laddr->sin_addr.s_addr, sizeof(uint32_t));rte_memcpy(host->localmac, gSrcMac, RTE_ETHER_ADDR_LEN);return 0;
}
3.recvfrom
static ssize_t recvfrom(int sockfd, void *buf, size_t len, __attribute__((unused))int flags,struct sockaddr *src_addr, __attribute__((unused))socklen_t *addrlen){struct localhost *host = get_hostinfo_fromfd(sockfd);if(host == NULL) return -1;struct sockaddr_in *saddr = (struct sockaddr_in*)src_addr;//dequeuestruct offload *ol = NULL;unsigned char *ptr = NULL;int nb = -1;//阻塞pthread_mutex_lock(&host->mutex);while((nb = rte_ring_mc_dequeue(host->recvbuf,(void**)&ol)) < 0) {pthread_cond_wait(&host->cond, &host->mutex);}pthread_mutex_unlock(&host->mutex);saddr->sin_port = ol->sport;rte_memcpy(&saddr->sin_addr.s_addr, &ol->sip, sizeof(uint32_t));struct in_addr addr;addr.s_addr = ol->dip;printf("nrecvto ---> src: %s:%d \n", inet_ntoa(addr), ntohs(ol->dport));if(len < ol->length) { //一次无法接收全部数据rte_memcpy(buf, ol->data, len);ptr = rte_malloc("unsigned char *", ol->length - len, 0);rte_memcpy(ptr, ol->data + len, ol->length - len);ol->length -= len;rte_free(ol->data);ol->data = ptr;rte_ring_mp_enqueue(host->recvbuf, ol);return len;} else {rte_memcpy(buf, ol->data, ol->length);rte_free(ol->data);rte_free(ol);return ol->length;}
}
4.sendto
static ssize_t sendto(int sockfd, const void *buf, size_t len, __attribute__((unused))int flags,const struct sockaddr *dest_addr, __attribute__((unused))socklen_t addrlen){struct localhost *host = get_hostinfo_fromfd(sockfd);if(host == NULL) return -1;const struct sockaddr_in *daddr = (const struct sockaddr_in*)dest_addr;struct offload *ol = rte_malloc("offload", sizeof(struct offload), 0);if(ol == NULL) {return -1;}ol->dip = daddr->sin_addr.s_addr;ol->dport = daddr->sin_port;ol->sip = host->localip;ol->sport = host->localport;ol->length = len;struct in_addr addr;addr.s_addr = ol->dip;printf("nsendto ---> src: %s:%d \n", inet_ntoa(addr), ntohs(ol->dport));ol->data = rte_malloc("ol data", len, 0);if(ol->data == NULL) {rte_free(ol);return -1;}rte_memcpy(ol->data, buf, len);rte_ring_mp_enqueue(host->sendbuf, ol);return len;
}
5.close
static int nclose(int fd) {struct localhost *host = get_hostinfo_fromfd(fd);if(host == NULL) {return -1;}LL_REMOVE(host, lhost);if(host->recvbuf){rte_ring_free(host->recvbuf);}if(host->sendbuf){rte_ring_free(host->sendbuf);}rte_free(host);return 0;
}
四.总结
通过本文,我们深入研究了基于DPDK的用户态UDP网络协议栈的设计、实现。在整体设计思路上,我们采用了用户态网络协议栈的理念,通过将核心功能移至用户空间,结合DPDK的强大支持,实现了一个高性能、低延迟的数据包处理方案。
关键组成部分中,我们详细介绍了数据包接收和发送引擎、协议解析、数据包处理逻辑等模块。这些组成部分共同协作,使得用户态UDP网络协议栈能够在不同应用场景下发挥其优势。
整体架构图清晰展示了各个模块之间的关系,以及数据在协议栈中的流动路径。这有助于读者更好地理解我们设计的用户态UDP网络协议栈的整体结构。
通过对用户态UDP网络协议栈的研究,我们不仅深刻理解了其设计和实现,也为构建更高性能、更灵活的网络通信系统奠定了基础。未来,我们期待在这一基础上进一步优化和扩展,以满足不断发展的网络应用需求。
链接: 基于DPDK实现的UDP用户态网络协议栈完整代码
相关文章:

【DPDK】基于dpdk实现用户态UDP网络协议栈
文章目录 一.背景及导言二.协议栈架构设计1. 数据包接收和发送引擎2. 协议解析3. 数据包处理逻辑 三.网络函数编写1.socket2.bind3.recvfrom4.sendto5.close 四.总结 一.背景及导言 在当今数字化的世界中,网络通信的高性能和低延迟对于许多应用至关重要。而用户态网…...

开源好用的所见即所得(WYSIWYG)编辑器:Editor.js
文章目录 特点基于区块干净的数据 界面与交互插件标题和文本图片列表Todo表格 使用安装创建编辑器实例配置工具本地化自定义样式 今天介绍一个开源好用的Web所见即所得(WYSIWYG)编辑器: Editor.js Editor.js 是一个基于 Web 的所见即所得富文本编辑器,它…...
sqlite 损坏 修复
步骤1 SQLite Download Page下载sqlite3 对应的系统版本 2.参考怎么恢复sqlite 数据库文件✅ - 有乐数据恢复网 sqlite3 dbname > .mode insert > .output dbdump.sql > .dump > .exit 恢复方法1 1.创建一个新的数据库 例如名字叫 test.db 2sqlite3 test.…...

初学Vue——Vue路由
0 什么是Vue路由 类似于Html中的超链接(<a>)一样,可以跳转页面的一种方式。 前端路由:URL中hash(#号之后的内容)与组件之间的对应关系,如下图: 当我们点击左侧导航栏时,浏览器的地址栏会发生变化,路…...

如何使用宝塔面板搭建Discuz并结合cpolar实现远程访问本地论坛
文章目录 前言1.安装基础环境2.一键部署Discuz3.安装cpolar工具4.配置域名访问Discuz5.固定域名公网地址6.配置Discuz论坛 前言 Crossday Discuz! Board(以下简称 Discuz!)是一套通用的社区论坛软件系统,用户可以在不需要任何编程的基础上&a…...

llc的基波分析法
对于我们之前分析的 LLC等效谐振电路的分析,其实我们发现分析的并不是完整的方波输入,而是用正弦波来分的 那么为何用基波来分析呢,因为对于方波而言,根据傅里叶级数它是可以分解成基波、 1次、3次、5次.......等各种奇次谐波的入…...

一键清除JavaScript代码中的注释:使用正则表达式实现
这个正则表达式可以有效地匹配 JavaScript 代码中的各种注释,并且跳过了以 http: 或 https: 开头的链接。 /\/\*[\s\S]*?\*\/|\/\/[^\n]*|<!--[\s\S]*?-->|(?<!http:|https:)\/\/[^\n]*/gvscode 实战,ctrlF 调出查找替换工具,点…...

第七次作业
IPSEC VPPN实验配置 目标:在FW5和FW3之间建立一条IPSEC通道,保证10.0.2.0/24网段可以正常访问到192.168.1.0/24 1.FW1和FW2进行双机热备(之前实验没保存,可看上个实验) 还有一些配置前面实验有。 2.场景选择点对点…...
在jupyternotebook中,如何解决作图时负号无法显示的问题?
输入以下代码即可。 import matplotlib as mpl mpl.rcParams[axes.unicode_minus] False 原文链接:在jupyternotebook中,如何解决作图时负号无法显示的问题?...

ConnectionResetError: [WinError 10054] 远程主机强迫关闭了一个现有的连接。
发生的错误信息: File "C:\Users\malongqiang\.conda\envs\ObjectDetection\lib\ssl.py", line 1309, in do_handshakeself._sslobj.do_handshake() ConnectionResetError: [WinError 10054] 远程主机强迫关闭了一个现有的连接。 分析原因: …...
主流开发语言与环境介绍
主流开发语言与环境介绍 1. 引言 随着计算机科学的不断发展,各种编程语言和开发环境层出不穷。选择一种适合自己的主流开发语言和环境是每个开发者都必须面临的问题。本文将为大家介绍几种目前最为流行的主流开发语言和环境,帮助读者选择合适的工具进行…...

Unity性能优化篇(九) 模型优化之LOD技术概述以及操作方法
LOD模型优化技术概述: 1.LOD技术可以根据摄像头远近来显示不同精度的模型(例如吃鸡游戏 随着跳伞高度 来显示下面树木以及建筑的模型精度) LOD模型优化技术操作方法: 可使用Unity自带的LOD Group组件,并根据项目的情况来调整该组件的属性。Untiy资源商店也有一些其…...

就业班 2401--3.11 Linux Day15--ftp数据传输测试server和client+谷歌验证码登录远程连接
文件服务器 路漫漫其修远兮,吾将上下而求索.构建NFS远程共享存储 一、NFS介绍 文件系统级别共享(是NAS存储) --------- 已经做好了格式化,可以直接用。 速度慢比如:nfs,sambaNFS NFS:Networ…...

有点NB的免费wordpress主题模板
一个不错的黄色模板,用WP免费主题模板搭建家政服务公司网站。 https://www.wpniu.com/themes/15.html...

安全防御第七次作业
拓扑图如图所示: 问题:在FW7和FW8之间建立一条IPSEC通道保证10.0.2.0/24网段 可以正常访问到192.168.1.0/24 注:基础配置我在此省略了 一、NAT配置 FW4: FW6: 二、在FW4上做服务器映射 三、配置IPSEC FW5ÿ…...
解决跨域问题的FastAPI应用及常见报错解析
介绍: 跨域问题在前后端分离的Web应用中经常会遇到。FastAPI作为一个快速、现代化的Python Web框架,在处理跨域问题上也提供了一些解决方案。本文将介绍如何使用FastAPI来解决跨域问题,并分析一些常见的报错及解决方法。 正文: …...

NXP iMX8MM Cortex-M4 核心 GPT Capture 测试
By Toradex秦海 1). 简介 NXP i.MX8 系列处理器均为异构多核架构 SoC,除了可以运行 Linux 等复杂操作系统的 Cortax-A 核心,还包含了可以运行实时操作系统比如 FreeRTOS 的 Cortex-M 核心,本文就演示通过 NXP i.MX8MM 处理器集成的 Cortex-…...

2步破解官方sublime4
sublime简要破解流程 1.下载sublime官方最新版2. 破解流程 1.下载sublime官方最新版 打开 官方网站下载 portable version 版,省的安装。。解压到任意位置,备份 sublime_text.exe 文件 2. 破解流程 打开网址把文件 sublime_text.exe 拖入网页搜索替换…...

【智能家居】东胜物联ODM定制ZigBee网关,助力能源管理解决方案商,提升市场占有率
背景 本文案例服务的客户是专业从事智能家居能源管理的解决方案商,其产品与服务旨在帮助用户监测、管理和优化能源消耗,以提高能源使用效率。 随着公司的扩张,为了增加市场占有率,他们希望找到更好的硬件服务支持,以…...

Python爬虫实战第三例【三】【上】
零.实现目标 爬取视频网站视频 视频网站你们随意,在这里我选择飞某速(狗头保命)。 例如,作者上半年看过的“铃芽之旅”,突然想看了,但是在正版网站看要VIP,在盗版网站看又太卡了,…...
云计算——弹性云计算器(ECS)
弹性云服务器:ECS 概述 云计算重构了ICT系统,云计算平台厂商推出使得厂家能够主要关注应用管理而非平台管理的云平台,包含如下主要概念。 ECS(Elastic Cloud Server):即弹性云服务器,是云计算…...
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以?
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以? 在 Golang 的面试中,map 类型的使用是一个常见的考点,其中对 key 类型的合法性 是一道常被提及的基础却很容易被忽视的问题。本文将带你深入理解 Golang 中…...

解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八
现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet,点击确认后如下提示 最终上报fail 解决方法 内核升级导致,需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...

iPhone密码忘记了办?iPhoneUnlocker,iPhone解锁工具Aiseesoft iPhone Unlocker 高级注册版分享
平时用 iPhone 的时候,难免会碰到解锁的麻烦事。比如密码忘了、人脸识别 / 指纹识别突然不灵,或者买了二手 iPhone 却被原来的 iCloud 账号锁住,这时候就需要靠谱的解锁工具来帮忙了。Aiseesoft iPhone Unlocker 就是专门解决这些问题的软件&…...

聊聊 Pulsar:Producer 源码解析
一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台,以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中,Producer(生产者) 是连接客户端应用与消息队列的第一步。生产者…...

全球首个30米分辨率湿地数据集(2000—2022)
数据简介 今天我们分享的数据是全球30米分辨率湿地数据集,包含8种湿地亚类,该数据以0.5X0.5的瓦片存储,我们整理了所有属于中国的瓦片名称与其对应省份,方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...

用机器学习破解新能源领域的“弃风”难题
音乐发烧友深有体会,玩音乐的本质就是玩电网。火电声音偏暖,水电偏冷,风电偏空旷。至于太阳能发的电,则略显朦胧和单薄。 不知你是否有感觉,近两年家里的音响声音越来越冷,听起来越来越单薄? —…...
省略号和可变参数模板
本文主要介绍如何展开可变参数的参数包 1.C语言的va_list展开可变参数 #include <iostream> #include <cstdarg>void printNumbers(int count, ...) {// 声明va_list类型的变量va_list args;// 使用va_start将可变参数写入变量argsva_start(args, count);for (in…...
Python网页自动化Selenium中文文档
1. 安装 1.1. 安装 Selenium Python bindings 提供了一个简单的API,让你使用Selenium WebDriver来编写功能/校验测试。 通过Selenium Python的API,你可以非常直观的使用Selenium WebDriver的所有功能。 Selenium Python bindings 使用非常简洁方便的A…...

沙箱虚拟化技术虚拟机容器之间的关系详解
问题 沙箱、虚拟化、容器三者分开一一介绍的话我知道他们各自都是什么东西,但是如果把三者放在一起,它们之间到底什么关系?又有什么联系呢?我不是很明白!!! 就比如说: 沙箱&#…...