suricata中DPDK收发包源码分析2

news2024/11/26 20:45:20

《suricata中DPDK收发包源码分析1》中分析了整体的DPDK收发包框架代码,今天我们继续来深入了解一下一些细节方面的问题。

目录

Q1:收发包线程模式在代码中是怎样确定的?

Q2: DPDK库的初始化rte_eal_init在哪里调用的?

Q3: 对网卡进行DPDK相关接口的配置操作的代码在哪里?

Q4: DPDK mbfu和suricata Packet是怎么转换的?

Q5: 网卡的RSS对哈希是在哪里配置的?


Q1:收发包线程模式在代码中是怎样确定的?

前面的文章《suricata中DPDK收发包线程模型和配置说明》中说的收包线程模型如下,那么这个收包模型在代码中是怎么确定的呢?

前面讲的DPDK的网口配置文件时,每个网口会指定收包线程数,我们指定的是2,即每个网口有两个收包线程,在RunModeSetLiveCaptureWorkers函数中会获取网卡总数,遍历所有网卡,然后遍历所有网卡配置的线程个数,所以总共启动的收包线程数就是网卡数*每个网卡配置的收包线程数,那怎么确定第一个收包线程只收取第一个网卡的0号队列的报文,第二个收包线程只收取第一个网卡的1号队列的报文,以及第三个线程,第四个线程以此类推呢?

前面讲到,DPDK调用rte_eth_rx_burst进行循环收包的函数是ReceiveDPDKLoop,rte_eth_rx_burst函数的前两个参数是port_id和queue_id,所以要控制每个线程收哪个网卡哪个队列的报文控制这两个参数就行了,这里的port_id和queue_id来自于DPDK的线程变量DPDKThreadVars,每个收包线程都有一个这样的变量:

typedef struct DPDKThreadVars_ {

/* counters */

uint64_t pkts;

ThreadVars *tv;

TmSlot *slot;

LiveDevice *livedev;

ChecksumValidationMode checksum_mode;

/* references to packet and drop counters */

uint16_t capture_dpdk_packets;

uint16_t capture_dpdk_rx_errs;

uint16_t capture_dpdk_imissed;

uint16_t capture_dpdk_rx_no_mbufs;

uint16_t capture_dpdk_ierrors;

uint16_t capture_dpdk_tx_errs;

unsigned int flags;

int threads;

/* for IPS */

DpdkCopyModeEnum copy_mode;

uint16_t out_port_id; //发送网口id

/* Entry in the peers_list */

uint64_t bytes;

uint64_t accepted;

uint64_t dropped;

uint16_t port_id; //收包网口id

uint16_t queue_id; //网口队列id

struct rte_mempool *pkt_mempool;

struct rte_mbuf *received_mbufs[BURST_SIZE];

} DPDKThreadVars;

DPDKThreadVars变量的值来自于slot->slot_data,slot->slot_data是TmThreadsSlotPktAcqLoop函数中:

for (slot = s; slot != NULL; slot = slot->slot_next) {

if (slot->SlotThreadInit != NULL) {

void *slot_data = NULL;

r = slot->SlotThreadInit(tv, slot->slot_initdata, &slot_data); //ReceiveDPDKThreadInit 或 DecodeDPDKThreadInit

if (r != TM_ECODE_OK) {

if (r == TM_ECODE_DONE) {

EngineDone();

TmThreadsSetFlag(tv, THV_CLOSED | THV_INIT_DONE | THV_RUNNING_DONE);

goto error;

} else {

TmThreadsSetFlag(tv, THV_CLOSED | THV_RUNNING_DONE);

goto error;

}

}

(void)SC_ATOMIC_SET(slot->slot_data, slot_data); //局部变量slot_data复制给slot->slot_data

}

局部变量slot_data是slot->SlotThreadInit(指向ReceiveDPDKThreadInit函数时)中将slot->slot_initdata(也就是DPDKIfaceConfig)的内容赋值给它的,那slot->slot_initdata(也就是DPDKIfaceConfig)又是哪里来的呢?其实slot->slot_initdata是TmSlotSetFuncAppend函数中将第三个参数data赋值给它的,这个data就是在RunModeSetLiveCaptureWorkersForDevice函数中注册ReceiveDPDK时通过TmSlotSetFuncAppend(tv, tm_module, aconf)传递的这个aconf变量,而conf变量就是ParseDpdkConfigAndConfigureDevice函数根据pcie地址读取到的网口的信息,所有绕了一大圈,才把配置里面的port_id和queue_id给到了DPDK线程变量DPDKThreadVars,用来决定每个线程收哪个网卡哪个队列的报文。

另外,发送报文都是调用Packet变量的p->ReleasePacket函数指针,也就是DPDKReleasePacket函数,通过DPDK的rte_eth_tx_burst接口进行发送的。

Q2: DPDK库的初始化rte_eal_init在哪里调用的?

rte_eal_init是在InitEal函数中调用的,调用栈如下:

SuricataMain

    -->RunModeDispatch

        -->RunModeIdsDpdkWorkers

            -->InitEal

                -->rte_eal_init

rte_eal_init传入的参数很简单,就是配置中的proc-type,指定的是Primary进程,其实suricata文档中说明的是DPDK收包的话就应该是Primary进程。

Q3: 对网卡进行DPDK相关接口的配置操作的代码在哪里?

对网卡进行配置操作的函数调用栈如下:

SuricataMain
  -->RunModeDispatch
      -->RunModeIdsDpdkWorkers
        -->RunModeSetLiveCaptureWorkers
          -->ParseDpdkConfigAndConfigureDevice
            -->DeviceConfigure
              -->DeviceInitPortConf(设置网卡RSS)
              -->rte_eth_dev_configure(配置网卡队列等参数)
              -->rte_eth_dev_adjust_nb_rx_tx_desc(配置网卡收包文件描述符)
              -->rte_eth_dev_set_mtu(设置MTU)
              -->DeviceConfigureQueues(按网口申请收包mbuf内存池)
                -->rte_eth_rx_queue_setup(配置网口收包队列)
                -->rte_eth_tx_queue_setup(配置网口发包队列)

需要注意的是启动网卡的接口rte_eth_dev_start,是在ReceiveDPDKThreadInit函数,也就是在收包线程入口TmThreadsSlotPktAcqLoop中调用slot->SlotThreadInit时进行的,由于多个一个网口只用启动一次,但是一个网口对应多个收包线程,所以加了一个判断,只在每个网口最后一个收包线程时启动网卡,部分代码如下:

// the last thread starts the device

if (queue_id == dpdk_config->threads - 1) {

retval = rte_eth_dev_start(ptv->port_id);

if (retval < 0) {

SCLogError("Error (%s) during device startup of %s", rte_strerror(-retval),

dpdk_config->iface);

goto fail;

}

Q4: DPDK mbfu和suricata Packet是怎么转换的?

在ReceiveDPDKLoop函数中,通过rte_eth_rx_burst收包的每一个mbuf与Packet对应,部分代码如下:

//DPDK开始收包

nb_rx = rte_eth_rx_burst(ptv->port_id, ptv->queue_id, ptv->received_mbufs, BURST_SIZE);

if (unlikely(nb_rx == 0)) {

continue;

}

ptv->pkts += (uint64_t)nb_rx;

for (uint16_t i = 0; i < nb_rx; i++) {

p = PacketGetFromQueueOrAlloc(); //Packet与dpdk mbuf一一对应

if (unlikely(p == NULL)) {

continue;

}

PKT_SET_SRC(p, PKT_SRC_WIRE);

p->datalink = LINKTYPE_ETHERNET;

if (ptv->checksum_mode == CHECKSUM_VALIDATION_DISABLE) {

p->flags |= PKT_IGNORE_CHECKSUM;

}

p->ts = DPDKSetTimevalReal(&machine_start_time);

p->dpdk_v.mbuf = ptv->received_mbufs[i]; //记录mbuf,后面用来发送报文时指定mbuf

p->ReleasePacket = DPDKReleasePacket; //释放mbuf或者发送报文

p->dpdk_v.copy_mode = ptv->copy_mode; //转发模式 DpdkCopyModeEnum

p->dpdk_v.out_port_id = ptv->out_port_id; //出网口

p->dpdk_v.out_queue_id = ptv->queue_id; //出网口的队列

p->livedev = ptv->livedev; //出网口信息指针

if (ptv->checksum_mode == CHECKSUM_VALIDATION_DISABLE) {

p->flags |= PKT_IGNORE_CHECKSUM;

} else if (ptv->checksum_mode == CHECKSUM_VALIDATION_OFFLOAD) {

uint64_t ol_flags = ptv->received_mbufs[i]->ol_flags;

if ((ol_flags & RTE_MBUF_F_RX_IP_CKSUM_MASK) == RTE_MBUF_F_RX_IP_CKSUM_GOOD &&

(ol_flags & RTE_MBUF_F_RX_L4_CKSUM_MASK) == RTE_MBUF_F_RX_L4_CKSUM_GOOD) {

SCLogDebug("HW detected GOOD IP and L4 chsum, ignoring validation");

p->flags |= PKT_IGNORE_CHECKSUM;

} else {

if ((ol_flags & RTE_MBUF_F_RX_IP_CKSUM_MASK) == RTE_MBUF_F_RX_IP_CKSUM_BAD) {

SCLogDebug("HW detected BAD IP checksum");

// chsum recalc will not be triggered but rule keyword check will be

p->level3_comp_csum = 0;

}

if ((ol_flags & RTE_MBUF_F_RX_L4_CKSUM_MASK) == RTE_MBUF_F_RX_L4_CKSUM_BAD) {

SCLogDebug("HW detected BAD L4 chsum");

p->level4_comp_csum = 0;

}

}

}

//构造Packet结构体

PacketSetData(p, rte_pktmbuf_mtod(p->dpdk_v.mbuf, uint8_t *),

rte_pktmbuf_pkt_len(p->dpdk_v.mbuf)); //Packet中记录了包的首地址和长度

if (TmThreadsSlotProcessPkt(ptv->tv, ptv->slot, p) != TM_ECODE_OK) {

TmqhOutputPacketpool(ptv->tv, p);

DPDKFreeMbufArray(ptv->received_mbufs, nb_rx - i - 1, i + 1);

SCReturnInt(EXIT_FAILURE);

}

}

Q5: 网卡的RSS对哈希是在哪里配置的?

是在DeviceInitPortConf函数中,部分代码如下:

port_conf->rx_adv_conf.rss_conf = (struct rte_eth_rss_conf){

.rss_key = rss_hkey,

.rss_key_len = RSS_HKEY_LEN,

.rss_hf = iconf->rss_hf,

};

其中rss_hkey是40个字节的数组,设置的是对称哈希,可以将相同五元组的报文都放入同一个网卡队列中:

// General purpose RSS key for symmetric bidirectional flow distribution

uint8_t rss_hkey[] = { 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D,

0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D,

0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A };

这个RSS最终是调用DPDK的网卡配置函数rte_eth_dev_configure来进行配置的。

好了,关于suricata中DPDK进行收发包的全部实现逻辑就讲到这里了。有问题的朋友,可以进网络技术开发交流群提问(先加我微信,备注加群)。喜欢文章内容的朋友,记得加个关注哦~~

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

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

相关文章

Linux中LV Status的状态为NOT available

今天下午有现场反馈备份磁盘找不到了&#xff0c;使用lvm方式的。提供了todesk帮忙看下&#xff0c; 首先使用 blkid查看&#xff0c;确实看不到备份磁盘的UUID&#xff0c;使用lvdisplay查看状态&#xff0c;状态不对了 [rootdb1 ~]# lvdisplay --- Logical volume --- …

.Vue3项目初始化

文章目录 1.Vue3项目初始化1.1 创建vue项目1.2 vue 初始化1.3 git 项目管理1.4 配置iconfig.json1.5 element 按需引入1.6 element 主题色的定制1.7 axios的基础配置1.8 router路由的配置 1.Vue3项目初始化 1.1 创建vue项目 npm init vuelatest1.2 vue 初始化 npm install1.…

【2023/05/16】MonteCarlo

Hello&#xff01;大家好&#xff0c;我是霜淮子&#xff0c;2023倒计时第11天。 Share O Beauty,find theyself in love,not in the flattery of thymirror. 译文&#xff1a; 啊&#xff0c;美啊&#xff0c;在爱中找你自己吧&#xff0c;不要到你镜子的诌谀中去寻找。 M…

[遗传学]转座因子的结构与功能

本篇文章主要带你了解:转座因子的发现和分类;原核生物以及真核生物种的转座子;转座作用的分子机制以及转座因子的遗传学效应和应用. &#x1f9ec;转座因子的发现和分类 &#x1f9ec;转座因子的概念 转座因子(transposable element)是在转座酶&#xff08;transposase&#xf…

Class 03 - R语言的 Vectors(向量) 与 lists(列表)

Class 03 - R语言的 Vector与 列表 list R语言语法脚本文件的创建、保存、和修改名称第一个函数使用帮助功能查看函数详细说明语法问题变量与赋值定义变量名称格式调用变量 R中的数据结构Vectors (向量)创建向量查看向量的性质查看数据类型 typeof()查看数据长度 length()检查…

Elasticsearch 核心技术(十):GEO 地理查询(geo_bounding_box、geo_distance、geo_shape)

❤️ 博客主页&#xff1a;水滴技术 &#x1f680; 支持水滴&#xff1a;点赞&#x1f44d; 收藏⭐ 留言&#x1f4ac; &#x1f338; 订阅专栏&#xff1a;大数据核心技术从入门到精通 文章目录 一、地理数据类型1.1、geo_point 地理点类型1.1.1、创建一个含有 geo_point 字…

opencv_c++学习(八)

一、两张图像的像素比较 比较最大最小 最小&#xff1a; min(lnputArray src1, InputArray src2, outputArray dst)最大&#xff1a; max(lnputArray src1, InputArray src2, outputArray dst)src1 :第一个图像矩阵&#xff0c;可以是任意通道数的矩阵。 src2:第二个图像矩…

电源电压过冲

前言&#xff1a; 前段时间突然想起来以前的一个问题&#xff0c;这个问题相信大家也都遇到过&#xff0c;甚至是解决过&#xff0c;或者没解决&#xff0c;也就不了了之&#xff0c;今天这篇文章&#xff0c;主要来讲下这个问题&#xff0c;看完喜欢的欢迎给我留言或者点赞&a…

【Linux】常见指令

&#x1f307;个人主页&#xff1a;平凡的小苏 &#x1f4da;学习格言&#xff1a;别人可以拷贝我的模式&#xff0c;但不能拷贝我不断往前的激情 &#x1f6f8;C专栏&#xff1a;Linux修炼内功基地 家人们更新不易&#xff0c;你们的&#x1f44d;点赞&#x1f44d;和⭐关注⭐…

EasyYapi插件—快速生成API接口文档

EasyYapi插件—快速生成API接口文档 1. 功能 导出http到&#xff08;Controller注解类&#xff09; YapiPostmanmarkdown导出RPC到 YapimarkdownCall api调用API接口 注意点&#xff1a; 注释中可以使用module标注模块所属的模块。表示接口会发布到yapi模块下&#xff1b;只…

生存分析利器:Python 中的 Kaplan-Meier Fitter 类详解

KaplanMeierFitter是lifelines库中用于计算生存分析的一个类。使用KaplanMeierFitter类&#xff0c;我们可以对我们的数据进行不同组之间的生存分析&#xff0c;比如根据年龄、性别、治疗手段等分类变量进行分组分析。 该类包含许多方法和属性&#xff0c;其中最常用的是fit()…

FPGA复位信号设计讨论

复位概述 复位作为电子系统中最常见的信号同时也是最重要的信号&#xff0c;它对工程师整体的设计表现有着极大的影响。复位信号可能深刻影响设计的性能表现&#xff0c;功耗&#xff0c;面积等等。对于一个优秀的系统设计&#xff0c;很难不把复位信号当成一个关键信号来设计。…

【车载基础软件 |ASF中间件 系列二】— 国产汽车生态平台ASF的生态框架

本文主要介绍国产基础软件开发平台架构下基于ASF的生态框架。 背景信息 随着E/E架构演进,从最初传统的分布式架构,由独立功能的ECU通过CANLIN等高实时性 总线通讯。诼渐演进到当前分域集中式结合车联网功能的结构,车载以大网逐渐步入了整车电子电器架构之中,除了高实时性…

【AIGC使用教程】论文阅读神器 SciSpace 从注册到体验

欢迎关注【AIGC使用教程】 专栏 【AIGC使用教程】论文阅读神器 SciSpace 从注册到体验 【AIGC使用教程】Microsoft Edge/Bing Chat 注册使用完全指南 【AIGC使用教程】GitHub Copilot 免费注册及在 VS Code 中的安装使用 【AIGC使用教程】GitHub Copilot 免费注册及在 PyCharm …

golang第一个简单项目实战+源码(gin+gorm)

创建项目并新建包 点击file->settings->GOPATH&#xff0c;把当前项目加进GOPATH目录中&#xff0c;然后点击apply&#xff0c; 点击加号完成之后&#xff0c;在左边选择go build 新建数据库 编写配置信息 这里将数据的信息都写在了配置文件里面 dns的内容为&#x…

二叉树概念结构,以及画图加代码分析二叉树遍历,创建,销毁,层序遍历,判断是否完全二叉树等等

二叉树 树的概念及结构树的概念树的相关概念树的表示 二叉树的概念及结构概念特殊的二叉树二叉树性质二叉树的存储结构 二叉树的实现二叉树顺序结构的实现二叉树链式结构的实现二叉树的遍历前序遍历中序遍历后序遍历 二叉树结点数量叶子结点数量求树高求k层结点数量 二叉树创建…

SpringMVC第一阶段:SpringMVC的介绍和使用

1、SpringMVC的概述 Spring MVC框架是一个开源的Java平台&#xff0c;为开发强大的基于JavaWeb应用程序提供全面的基础架构支持&#xff0c;并且使用起来非常简单容易。 Spring web MVC框架提供了MVC(模型 - 视图 - 控制器)架构&#xff0c;用于开发灵活和松散耦合的Web应用程…

【原创】创建Vue3 ts项目

文章目录 创建Vue3 ts项目工作空间创建vue 项目进入项目目录启动项目 & 停止项目访问成功 创建Vue3 ts项目 工作空间 进入工作空间目录&#xff1a;D:\workspace\vue 创建vue 项目 vue create vt #创建vt项目vue test 选择手动进行配置&#xff1a; 选中下面几个核心…

利用 Mybatis-Plus 的动态数据源实现多数据源配置

目录 一、导入依赖 二、Application.yaml配置文件 三、切换数据源 四、其他方法 4.1 配置多个数据源 4.2 定义Datasource和EntityManager 4.3 在需要使用数据源的地方注入不同的EntityManager 官网&#xff1a;https://baomidou.com/pages/a61e1b/#dynamic-datasource …

vue2实现路由跳转后隐藏底部固定导航栏Tabber的一种方式

在使用vue路由的时候&#xff0c;跳转到某些页面上是不需要展示底部固定的导航栏的&#xff0c;所以在某些特定的页面跳转时候&#xff0c;就需要隐藏底部的导航栏 这里用了一种方式去解决这个问题 1、前提 这里我把底部导航栏做了一个组件的封装&#xff0c;然后在App.vue里…