DPDK源码分析之l2fwd

news2024/11/28 0:44:18

什么是L2转发

2层转发,即对应OSI模型中的数据链路层,该层以Mac帧进行传输,运行在2层的比较有代表性的设备就是交换机了。

当交换机收到数据时,它会检查它的目的MAC地址,然后把数据从目的主机所在的接口转发出去。

交换机之所以能实现这一功能,是因为交换机内部有一个MAC地址表,MAC地址表记录了网络中所有MAC地址与该交换机各端口的对应信息。某一数据帧需要转发时,交换机根据该数据帧的目的MAC地址来查找MAC地址表,从而得到该地址对应的端口,即知道具有该MAC地址的设备是连接在交换机的哪个端口上,然后交换机把数据帧从该端口转发出去。

1.交换机根据收到数据帧中的源MAC地址建立该地址同交换机端口的映射,并将其写入MAC地址表中。
2.交换机将数据帧中的目的MAC地址同已建立的MAC地址表进行比较,以决定由哪个端口进行转发。
3.如数据帧中的目的MAC地址不在MAC地址表中,则向所有端口转发。这一过程称为泛洪(flood)。
4.接到广播帧或组播帧的时候,它立即转发到除接收端口之外的所有其他端口。

DPDK-l2fwd做了什么

该实例中代码写死的网卡为promiscuous混杂模式,我的虚拟机的两个网卡是直连的,拓扑如下:

因此,dpdk-l2fwd中,port 0收到包会转发给port 1,port 1收到包也会转发给相邻端口port 0,下图port 0混杂模式收到29694508个包然后会把这些包都sent给port 1,port 1同样收到其他包后也会转发给port 0。

因此,dpdk l2 fwd这个例子展示了两个网卡在mac层成功的转发了数据包,后续我们会阅读源码并调试程序来看,dpdk是如何实现这一功能的。

Pktgen安装

pktgen-dpdk是用于对DPDK进行高速数据包测试的工具

使用的命令行参数如下:

-c  : 用于指定运行程序的CPU内核掩码。
-n  : 用来指定内存通道。
-s  : 如果你想用pktgen发送pcap文件  例如 [-s P:PCAP_file]  -s 0 : 1.pcap    0表示在第0个网卡,1.pcap及文件名
-P  : 启动所有网卡,并进入混杂模式,想指定特定网卡 用 -p mask
-m  : 指定lcore和port的映射关系 [1].0, [2].1 core1->port0 core2->port2

由于pktgen是基于dpdk进行开发的,因此选取的pktgen的版本和dpdk的版本一定是相互兼容的,我这边版本如下:

pktgen version:
pktgen-dpdk-pktgen-21.03.1

dpdk version:
20.11.4-rc1

安装过程严格按照install.md即可:

//1. 先升级一下gcc版本
sudo yum install centos-release-scl
sudo yum install devtoolset-7-gcc*
scl enable devtoolset-7 bash
which gcc 
gcc -version
//2. 安装libpcap
yum install libpcap
yum install dnf-plugins-core
yum install libpcap-devel
//3. 环境变量设置
export RTE_SDK=<DPDKinstallDir>
export RTE_TARGET=x86_64-native-linux-gcc
//4. make

这样就可以通过参数配置,指定port去发包了,下面我通过port0发包10000pkts/s,由于port0和port1直连,可以看到port 1收包10000pkts/s。

//core0为主控负责命令行接受,流量显示,消息调度
//core1->port0 core2->port1
./pktgen -l 0-2 -n 3 -- -P -m "[1].0, [2].1"
set 0 dst mac 00:0c:29:93:e6:be
set 0 count 10000
start 0

源码阅读

无情GDB

gdb并打印一些关键信息,加深l2fwd源码理解

l2fwd_parse_args

解析l2fwd转发的一些命令行配置,我这边配置的是set args -- -q 1 -p 0x3即:

l2fwd_rx_queue_per_lcore:每个逻辑核负责处理一个rx队列,后续可以看到网卡配置时一个网卡配置一个rx队列和tx队列,因此这个参数可以理解为一个逻辑核负责处理一个网卡。

l2fwd_enabled_port_mask:可用的的网卡port的掩码

timer_period:多长时间将统计信息输出到stdout中,缺省为10s

转发端口配置

两两一组互为转发,因为我这边就两个port0和port1:

port 0 转给port 1, port 1 转给port 0

这里面用到了rte_eth_devices[portid]->data.owner.id与RTE_ETH_DEV_NO_OWNER进行比较,代表该接管的网卡还没有被使用。

网卡接管

我这边是虚拟机网卡E1000,因此是在eal初始化时调用的是eth_em_devinit,内容很多后面用到哪个在详细看一下,这边就是dpdk通过igb_uio用户态驱动接管网卡,并对其进行一些参数的初始化。

//pmd驱动一系列函数,包括设备的启动,停止,混杂模式,广播模式,MTU设置以及Mac地址设置
eth_dev->dev_ops = &eth_em_ops;
eth_dev->rx_queue_count = eth_em_rx_queue_count;
//DD位(Descriptor Done Status)用于标志标识一个描述符buf是否可用。
eth_dev->rx_descriptor_done   = eth_em_rx_descriptor_done;
eth_dev->rx_descriptor_status = eth_em_rx_descriptor_status;
eth_dev->tx_descriptor_status = eth_em_tx_descriptor_status;
//收包函数
//1、网卡使用DMA写Rx FIFO中的Frame到Rx Ring Buffer中的mbuf,设置desc的DD为1
//2、网卡驱动取走mbuf后,设置desc的DD为0,更新RDT
eth_dev->rx_pkt_burst = (eth_rx_burst_t)&eth_em_recv_pkts;
//发包函数
eth_dev->tx_pkt_burst = (eth_tx_burst_t)&eth_em_xmit_pkts;
//发包准备函数,offload设置校验以及检验和
eth_dev->tx_pkt_prepare = (eth_tx_prep_t)&eth_em_prep_pkts;
//mac地址字符串
eth_dev->data->mac_addrs = rte_zmalloc("e1000", RTE_ETHER_ADDR_LEN * hw->mac.rar_entry_count, 0);

为逻辑核分配port

根据之前配置的core最大可以处理几个网卡port,我这边是1所以,每个core赋值一个网卡,可以看到n_rx_port都是1

rte_pktmbuf_pool_create

这个mbuf pool主要是给网卡接收数据包提供mbuf的,换句话说网卡通过DMA收到数据需要把数据包通过DMA传送到一块内存,正是这个mbuf pool中的内存。这里会创建一个mbuf的内存池,每个muf的大小为( sizeof(struct rte_mbuf) + priv_size + data_room_size ,总共有nb_mbufs个mbuf。

//收队列数量+发队列数量+一次最大收报文数量+核数*它的cache中报文的数量
nb_mbufs = RTE_MAX(nb_ports * (nb_rxd + nb_txd + MAX_PKT_BURST + nb_lcores * MEMPOOL_CACHE_SIZE), 8192U);

pktbuf pool创建使用了下面几个函数,我们逐一debug:

rte_mempool_create_empty

->rte_mempool_populate_default

->rte_mempool_obj_iter

  • rte_mempool_create_empty

在memzone申请内存,这块内存包含sizeof(struct rte_mempool),每个逻辑核的cache size大小,以及私有数据的大小。然后会创建一个空的mempool头指针指向这块内存,并挂载到rte_mempool_tailq中,这个头包含sizeof(struct rte_mempool)以及每个逻辑核的cache大小,然后会在memzone申请所有pool内元素所需要的内存,这个内存地址赋给mp指针。总结如下:

debug如下:

  • rte_mempool_populate_default

ring队列默认为多生产者多消费者模式,eal初始化时会生成一个ring队列table,用于规定每种ring队列的一些函数操作,包括元素入队,出队,遍历等。

为mp创建一个ring队列,后续会存mbuf的指针。mp->flags |= MEMPOOL_F_POOL_CREATED

为mp每一个元素分配空间,这里面会做一个page-aligned address的操作如下:

然后寻找最大的连续页面进行分配元素,将这些元素指针加入到mp->elmlist中,分配的内存块信息记录在mp->memlist中,并把这些mbuf指针入队ring,然后会继续寻找连续页面分配元素直到elt_size。总结如下:

debug如下:

  • rte_mempool_obj_iter

初始化mbuf信息,包括所属内存池、缓存起始地址等。

网卡启动

rte_eth_dev_info_get获取对应port的网卡信息,包括网卡驱动,发送和接收队列个数,mtu值以及rx和tx的hw descriptor(后序给dma 用的, 里面包括了 内存搬运的起始地址 结束地址 什么的,dma 分析这个结构体完成数据传输。)

然后会通过eth_dev_rx_queue_config为dev->data->tx_queues和dev->data->rx_queues分配指向队列的指针,这里面代码写死了,1个接收队列1个发送队列,然后会调用setup函数对收发队列进行初始化。

收队列初始化:

网卡驱动的rx_queue_setup函数,由于我是虚拟网卡e1000,所以这边调用的是eth_igb_rx_queuesetup,这个函数主要分配了2个队列,sw_ring和rx_ring,以及网卡相关的寄存器设置。

rx_ring包含了E1000_MAX_RING_DESC个网卡描述符,里面含有dma传输的报文数据地址以及报文头部地址,rss hash值以及报文的校验和,长度等等。

sw_ring包含了 nb_desc个struct igb_rx_entry,也就是mbuf地址。

rx_ring主要存储报文数据的物理地址,物理地址供网卡DMA使用,也称为DMA地址(硬件使用物理地址,将报文copy到报文物理位置上)。sw_ring主要存储报文数据的虚拟地址,虚拟地址供应用使用(软件使用虚拟地址,读取报文)。

发队列初始化:

和接收队列类似,调用eth_em_tx_queue_setup。

启动网卡前:

设置报文发送失败的回调函数,这个实例是失败计数并free的操作,rte_eth_tx_buffer_count_callback.

启动网卡:

我这边调用的是e1000的pmd驱动,eth_em_start,函数极其复杂,原谅太菜的我过早的遇到了它,不过这边有个函数比较关键em_alloc_rx_queue_mbufs。它将sw_ring队列和用户的mbuf pool内存池关联,并设置rx_ring的dma地址。总结如下:

debug如下:

lcore_worker启动

主核负责统计各个网卡port收发包的数量

其他子核负责读取对应的网卡port的rx queue,然后如果有数据包的话就,就循环转发到配置的另一个网卡上。

代码极其简单,关键收发包api:

(*dev->tx_pkt_burst)(dev->data->tx_queues[queue_id], tx_pkts, nb_pkts);

(*dev->rx_pkt_burst)(dev->data->rx_queues[queue_id], rx_pkts, nb_pkts);

我这边就一个核,所以它接收后会自己发出去。

Reference

dpdk应用基础 (豆瓣)

理解物理网卡、网卡接口、内核、IP等属性的关系-CSDN博客

交换机-CSDN博客

dpdk多队列机制-CSDN博客

混杂模式和非混杂模式-CSDN博客

DPDK L2FWD使用 - 简书

在CentOS中升级gcc4.8到gcc5并修改默认设置 - it610.com

dpdk环境搭建+创建dpdk项目,并连接dpdk库_linggang_123的博客-CSDN博客

DPDK PKTGEN使用 - 简书

DPDK 示例之 L2FWD - 知乎

DPDK-Pktgen的使用

网卡基础概念扫盲-CSDN博客

交换机的工作原理-CSDN博客

c/c++ signal(信号)解析-CSDN博客

dpdk网卡收发包分析-ChinaUnix博客

DPDK总结网卡初始化-CSDN博客

网卡offload功能介绍-CSDN博客

dpdk 网卡队列初始化 + 收发包 - tycoon3 - 博客园 (cnblogs.com)

内存池之rte_mempool-CSDN博客

DPDK内存管理-mempool、mbuf-CSDN博客

DPDK数据包与内存专题-mempool内存池 - AISEED - 博客园 (cnblogs.com)

dpdk mbuf之概念理解_ych的专栏-CSDN博客_mbuf

DPDK 网卡收包流程_RToax-CSDN博客_dpdk多队列收包

DPDK源码分析之l2fwd

原文链接: https://zhuanlan.zhihu.com/p/454346807  原文作者:于顾而言

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/91937.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SSM 学习管理系统

SSM 学习管理系统 SSM 学习管理系统 功能介绍 首页 图片轮播展示 网站公告 学生注册 教师注册 课程资料 视频学习 友情链接 资料详情 学习进度 评论 收藏 后台管理 登录 管理员管理 修改密码 网站公告管理 友情链接管理 轮播图管理 学生管理 班级管理 我的班级管理 教师管理…

微信小程序 | 小程序的内置组件

&#x1f5a5;️ 微信小程序专栏&#xff1a;微信小程序 | 小程序的内置组件 &#x1f9d1;‍&#x1f4bc; 个人简介&#xff1a;一个不甘平庸的平凡人&#x1f36c; ✨ 个人主页&#xff1a;CoderHing的个人主页 &#x1f340; 格言: ☀️ 路漫漫其修远兮,吾将上下而求索☀️…

【轻量级开源ROS 的机器人设备(4)】--(3)通信实现

前文链接 【轻量级开源ROS 的机器人设备&#xff08;4&#xff09;】--&#xff08;2&#xff09;通信实现_无水先生的博客-CSDN博客 六、数据流 数据流 虽然 XML-RPC 为远程方法调用提供了一种简单而干净的协议&#xff0c;但其冗长和以文本为中心的编码使其不适合高带宽和低…

手把手分享:如何将小程序游戏引入自有APP?(Android篇)

上一期的为大家分享了&#xff1a;如何在iOS中引入FinClip SDK&#xff0c;并将小程序游戏运行到自有App 中。点击查看&#xff1a;&#x1f449;手把手系列&#xff1a;如何将小程序游戏引入自有APP&#xff1f;&#xff08;iOS篇&#xff09; 本周继续分享如何在Android系统…

小红书如何做推广增粉?怎样让小红书快速增加粉丝?

互联网时代&#xff0c;任何什么平台的推广都需要流量&#xff0c;没有流量的账号是做不起来的&#xff0c;也就没有宣传或是转化的效果。 小红书账号粉丝数量和权重是成正比的&#xff0c;涨粉越多的账号&#xff0c;说明越受到用户的喜欢&#xff0c;账号笔记的数据就会越好…

5G“新引擎”,助力矿山向无人化、智慧化转型

导语 | 5G 商用已过去三年&#xff0c;其发展已步入规模化应用的关键期。无论是在诸如矿山、港口远程驾驶的行业应用领域&#xff0c;还是在面向C端的智能汽车、自动驾驶方面&#xff0c;都得到了广泛应用。今天&#xff0c;我们特邀了三一智矿的董事长、腾讯云 TVP 行业大使马…

java面试强基(23)

什么是线程死锁?如何避免死锁? ​ 线程死锁描述的是这样一种情况&#xff1a;多个线程同时被阻塞&#xff0c;它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞&#xff0c;因此程序不可能正常终止。 上面的例子符合产生死锁的四个必要条件&#xff1…

提高短视频的收藏下载和转发率的方法,我们可以不做但不能不会

想要提高收藏和下载&#xff0c;那就要提高你内容的价值感和获得感。 我们要清晰的知道收获感不等同于真正的收获。那我们的做法就是给出冗余的有用&#xff0c;给出熟悉的陌生&#xff0c;给出精准的表达。那这是提高收藏转发的方法。 我们看到好看的视频&#xff0c;想分享视…

Hack the box -- Responder靶机

这个靶机记录一下。。感觉会用到。 任务1 当使用IP地址访问web服务时&#xff0c;我们被重定向到的域是什么? 这里我们访问一下ip&#xff0c;然后重定向了 FLAG:unika.htb 任务2 服务器上使用哪种脚本语言生成网页? 这里因为重定向域的问题&#xff0c;我们是访问不了的会报…

408 | 【2022年】计算机统考真题 自用回顾知识点整理

一、数据结构 T1:时间复杂度 ——直接求程序执行的次数 T5:哈夫曼树(最优二叉树)与哈夫曼编码 定义 结点带权路径长度:从根到任一节点的路径长度(经过的边数)与该结点权值的乘积树的带权路径长度WPL:所有叶节点的带权路径长度之和 哈夫曼树:WPL最小的二叉树哈夫曼树的…

PHPMailer发送邮件(PHP发送电子邮件)

很多网站注册时都会要求输入电子邮箱&#xff0c;其应用场景是比较广的&#xff0c;例如注册账号接收验证码、注册成功通知、登录通知、找回密码验证通知等。本文将介绍如何使用PHP实现发送邮件。 开源项目PHPMailer 使用了开源项目PHPMailer&#xff0c;本文使用163邮箱作为…

挑战一天速通python基础语法

挑战一天速通python基础语法 文章目录挑战一天速通python基础语法0. 防止某人健忘1. 一些小点2. 输入输出2.1 输出2.2 输入3. 一些基础语法3.1 条件语句3.2 循环语句3.3 空语句4. 函数5. 列表和元组5.1 列表5.2 元组6. 字典7. 文件操作再有一个月参加一个NTU的线上科研项目。。…

MicroPython-On-ESP8266——8x8LED点阵模块(5)自制贪吃蛇游戏

MicroPython-On-ESP8266——8x8LED点阵模块&#xff08;5&#xff09;自制贪吃蛇游戏 1. 背景知识 连续折腾了一段时间的8x8点阵屏模块&#xff0c;从基本原理到驱动它显示滚动图案效果&#xff0c;常用的功能都使用到了。系列如下&#xff1a; MicroPython-On-ESP8266——8…

文件下载漏洞详解

今天继续给大家介绍渗透测试相关知识&#xff0c;本文主要内容是文件下载漏洞详解。 免责声明&#xff1a; 本文所介绍的内容仅做学习交流使用&#xff0c;严禁利用文中技术进行非法行为&#xff0c;否则造成一切严重后果自负&#xff01; 再次强调&#xff1a;严禁对未授权设备…

速锐得视角—数字化能源新时代的影响挑战趋势

数字化正在改善能源系统的安全性、生产力、普及率和可持续性&#xff0c;但是同时数字化也引发了新的安全和隐私风险&#xff0c;此外&#xff0c;市场、企业和工作岗位也受数字化的影响&#xff0c;改变工种结构&#xff0c;在数字化新时代来临前&#xff0c;正在发生一些细微…

计算机毕业设计java+springboot+vue学生宿舍管理系统

项目介绍 通篇文章的撰写基础是实际的应用需要,然后在架构系统之前全面复习大学所修习的相关知识以及网络提供的技术应用教程,以学生宿舍管理系统的实际应用需要出发,架构系统来改善现学生宿舍管理系统及出入登记平台工作流程繁琐等问题。不仅如此以操作者的角度来说,该系统的…

数字IC验证快速入门,你想知道的干货都在这里

网上有很多人咨询关于数字IC验证的行情&#xff0c;下面大多数回答都说薪资高、机会多、发展好。 确实&#xff0c;一款芯片从立项到流片生产需要经过层层自测和验证&#xff0c;否则芯片注定是失败。可以说&#xff0c;IC验证是IC设计的关键所在。 验证的重要性 这些年来&a…

020 | 我国河长制运行中的公众协同参与机制研究 | 大学生创新训练项目申请书 | 极致技术工厂

研究目的营造全社会共同关心和保护河湖的良好氛围、拓宽公众参与渠道 公众参与河湖保护具有自身优势。公众是河湖保护的坚实力量&#xff0c;探求河长制中的公众参与机制&#xff0c;营造全社会关心保护环境的良好社会氛围是改善流域生态环境的根本举措。协同治理的发展以社会壮…

为什么要学习rust

一、rust的实现、优点 实现&#xff1a;明确/&#xff08;零成本&#xff09;抽象/赋能优点&#xff1a;兼具高性能/安全性/表达力 明确&#xff1a;rust不像其他的语言&#xff0c;这些编程语言为了照顾初学者&#xff0c;它会把很多基本概念隐藏在基本语法之后&#xff0c;它…

数图互通高校房产管理——公积金补贴管理

数图互通房产管理系统在这方面做得比较全面&#xff1b; 1、公积金管理 1.1 公积金开户申请、审核 新进入学校的职工、博士后(含特别研究助理)公积金开户申请老师登录平台进行公积金开户申请&#xff0c;申请页面先选择人员类型“在职、项目、博士后、特别研究助理”,由房产…