本文简介DPDK中ACL链表的几个主要函数,以及数据结构。

ACL初始化

函数rte_acl_init如下,主要初始化工作是选择使用的分类算法。对于AVX2(Advanced Vector Extensions),仅当编译DPDK代码的编译器支持AVX2指令集,并且运行DPDK程序的处理器支持AVX2指令集时,才启用RTE_ACL_CLASSIFY_AVX2算法。否则,退而求其次,选用RTE_ACL_CLASSIFY_SSE算法。如果处理器不支持SSE4_1(Streaming SIMD Extensions)指令集,选用默认的算法为RTE_ACL_CLASSIFY_DEFAULT,

RTE_INIT(rte_acl_init)
{  enum rte_acl_classify_alg alg = RTE_ACL_CLASSIFY_DEFAULT;#ifdef CC_AVX2_SUPPORTif (rte_cpu_get_flag_enabled(RTE_CPUFLAG_AVX2))alg = RTE_ACL_CLASSIFY_AVX2;else if (rte_cpu_get_flag_enabled(RTE_CPUFLAG_SSE4_1))
#elseif (rte_cpu_get_flag_enabled(RTE_CPUFLAG_SSE4_1))
#endifalg = RTE_ACL_CLASSIFY_SSE;rte_acl_set_default_classify(alg);
}

以下指令检查当前的gcc编译器是否支持AVX2指令集。

$ gcc  -march=core-avx2 -dM -E - </dev/null 2>&1

从输出结果可以看到(输出省略不相关部分),__AVX2__关键字,表明支持AVX2指令集。另外,可见gcc同时支持SSE4_1指令集。

 #define __SSE4_1__ 1#define __SSE4_2__ 1#define __AVX__ 1#define __AVX2__ 1

DPDK中使用如下的代码赋值宏CC_AVX2_SUPPORT,如果grep找到AVX2关键字,echo数字1即赋值给宏CC_AVX2_SUPPORT值1。

CC_AVX2_SUPPORT=$(shell $(CC) -march=core-avx2 -dM -E - </dev/null 2>&1 | grep -q AVX2 && echo 1)

由以下的ACL分类函数定义可见,默认的类型RTE_ACL_CLASSIFY_DEFAULT与RTE_ACL_CLASSIFY_SCALAR相同,都是使用rte_acl_classify_scalar函数作为分类处理函数。

 static const rte_acl_classify_t classify_fns[] = {[RTE_ACL_CLASSIFY_DEFAULT] = rte_acl_classify_scalar,[RTE_ACL_CLASSIFY_SCALAR] = rte_acl_classify_scalar,[RTE_ACL_CLASSIFY_SSE] = rte_acl_classify_sse,[RTE_ACL_CLASSIFY_AVX2] = rte_acl_classify_avx2,[RTE_ACL_CLASSIFY_NEON] = rte_acl_classify_neon,[RTE_ACL_CLASSIFY_ALTIVEC] = rte_acl_classify_altivec,};

全局ACL链表

宏TAILQ_HEAD定义tailq链表结构rte_acl_list,以及指定链表中元素的类型rte_tailq_entry。宏EAL_REGISTER_TAILQ定义了RTE初始化函数,用于注册名称为“RTE_ACL”类型为rte_tailq_elem的rte_acl_tailq变量。

TAILQ_HEAD(rte_acl_list, rte_tailq_entry);static struct rte_tailq_elem rte_acl_tailq = {.name = "RTE_ACL",
};
EAL_REGISTER_TAILQ(rte_acl_tailq)#define EAL_REGISTER_TAILQ(t) \
RTE_INIT(tailqinitfn_ ##t) \
{ \         if (rte_eal_tailq_register(&t) < 0) \rte_panic("Cannot initialize tailq: %s\n", t.name); \
}  

如上所示,函数rte_eal_tailq_register将名为“RTE_ACL”的变量结构注册到全局rte_tailq_elem_head链表中。然而“RTE_ACL”结构自身亦包含一个tailq链表头成员head,由定义可见其类型与ACL链表rte_acl_list的类型一致,元素成员都为rte_tailq_entry结构,其正为ACL链表的头指针。

TAILQ_HEAD(rte_tailq_entry_head, rte_tailq_entry);struct rte_tailq_head {struct rte_tailq_entry_head tailq_head; /**< NOTE: must be first element */char name[RTE_TAILQ_NAMESIZE];
};
struct rte_tailq_elem {/* Reference to head in shared mem, updated at init time by rte_eal_tailqs_init() */struct rte_tailq_head *head;TAILQ_ENTRY(rte_tailq_elem) next;const char name[RTE_TAILQ_NAMESIZE];
};

在函数rte_acl_create中可见,正是通过将rte_acl_tailq的head成员的tailq_head子成员转换为rte_acl_list,而得到全局的ACL链表头部指针的。

struct rte_acl_ctx * rte_acl_create(const struct rte_acl_param *param)
{struct rte_acl_ctx *ctx;struct rte_acl_list *acl_list;struct rte_tailq_entry *te;char name[sizeof(ctx->name)];acl_list = RTE_TAILQ_CAST(rte_acl_tailq.head, rte_acl_list);
}#define RTE_TAILQ_CAST(tailq_entry, struct_name) \(struct struct_name *)&(tailq_entry)->tailq_head

ACL创建

主函数rte_acl_create,根据传入的rte_acl_param结构类型的参数,创建一个新的rte_acl_ctx结构,但是如果已经存在与参数中指定的名称相同的ACL上下文结构,直接返回其地址,不执行新建操作。以DPDK例程中的l3fwd-acl而言,其在调用ACL创建函数rte_acl_create时,传入的参数结构如下,参见文件/examples/l3fwd-acl/main.c中的函数setup_acl:

    int dim = ipv6 ? RTE_DIM(ipv6_defs) : RTE_DIM(ipv4_defs);/* Create ACL contexts */snprintf(name, sizeof(name), "%s%d", ipv6 ? L3FWD_ACL_IPV6_NAME : L3FWD_ACL_IPV4_NAME, socketid);acl_param.name = name;   acl_param.socket_id = socketid;acl_param.rule_size = RTE_ACL_RULE_SZ(dim);acl_param.max_rule_num = MAX_ACL_RULE_NUM;

对于IPv4而言,其ACL的名称为l3fwd-acl-ipv4(宏L3FWD_ACL_IPV4_NAME)与socketid拼接的字符串。对于IPv6,其名称为l3fwd-acl-ipv6+socketid的值。之后将会看到在函数rte_acl_create中,其将传入的ACL名称添加上ACL_的前缀。所以,实际的ACL名称为(以IPv4为例,假设socketid等于0):ACL_l3fwd-acl-ipv40。

在l3fwd-acl例程中,ACL单个规则项的大小(rule_size)设定为RTE_ACL_RULE_SZ(dim),其中dim的值为ACL规则的字段数量,对于IPv4来说,定义了5个字段,分别为协议proto、源IP、目的IP、源端口和目的端口。对于IPv6来说,同样是五元组,但是由于IPv6的地址长度为16个字节,所以,多出了6个字段,IPv6规则定义了11个字段。ACL规则大小等于rte_acl_rule结构本身的长度加上字段数量与字段结构体rte_acl_field的乘积。

#define RTE_ACL_RULE_SZ(fld_num)    \(sizeof(struct rte_acl_rule) + sizeof(struct rte_acl_field) * (fld_num))

最后一个参数最大规则数量(max_rule_num)设定为10万条(宏MAX_ACL_RULE_NUM)。

函数rte_acl_create实现如下,首先为传入参数结构中的ACL名称增加ACL_前缀。随后,在全局ACL链表中遍历查找,如果已经存在其名称与当前名称相同的ACL上下文结构,返回此结构即可。

struct rte_acl_ctx *rte_acl_create(const struct rte_acl_param *param)
{struct rte_acl_list *acl_list;struct rte_tailq_entry *te;acl_list = RTE_TAILQ_CAST(rte_acl_tailq.head, rte_acl_list);snprintf(name, sizeof(name), "ACL_%s", param->name);/* if we already have one with that name */TAILQ_FOREACH(te, acl_list, next) {ctx = (struct rte_acl_ctx *) te->data;if (strncmp(param->name, ctx->name, sizeof(ctx->name)) == 0)break;}

否则,创建新的ACL上下文结构(rte_acl_ctx),其占用空间大小(sz)为rte_acl_ctx上下文结构自身空间大小与所有的规则所占空间大小之和。另外,分配一个rte_tailq_entry链表项结构,以便向全局ACL链表上添加。在ACL上下文结构中规则链表开始的地址(rules成员)需要跳过rte_acl_ctx自身的长度。成员alg初始化为默认的rte_acl_default_classify。

    /* calculate amount of memory required for pattern set. */sz = sizeof(*ctx) + param->max_rule_num * param->rule_size;/* if ACL with such name doesn't exist, then create a new one. */if (te == NULL) {ctx = NULL;te = rte_zmalloc("ACL_TAILQ_ENTRY", sizeof(*te), 0);ctx = rte_zmalloc_socket(name, sz, RTE_CACHE_LINE_SIZE, param->socket_id);/* init new allocated context. */ctx->rules = ctx + 1;ctx->max_rules = param->max_rule_num;ctx->rule_sz = param->rule_size;ctx->socket_id = param->socket_id;ctx->alg = rte_acl_default_classify;snprintf(ctx->name, sizeof(ctx->name), "%s", param->name);te->data = (void *) ctx;TAILQ_INSERT_TAIL(acl_list, te, next);}

添加ACL规则

函数acl_add_rules向指定的ACL上下文中一次添加多条规则(包括单条),首先偏移到ACL上下文中已有规则的末尾,之后将要添加的规则拷贝进去,最后增加ACL上下文结构中的规则计数。

static int acl_add_rules(struct rte_acl_ctx *ctx, const void *rules, uint32_t num)
{uint8_t *pos;if (num + ctx->num_rules > ctx->max_rules)return -ENOMEM;pos = ctx->rules;pos += ctx->rule_sz * ctx->num_rules;memcpy(pos, rules, num * ctx->rule_sz);ctx->num_rules += num;

ACL规则树

以上创建的ACL上下文结构及其规则,如果要使用,需要对其进行build编译,生成运行时的trie树结构,以便进行高效的查询。由函数rte_acl_build完成,其参数为ACL上下文和rte_acl_config结构的编译控制参数。再次以示例程序l3fwd-acl来说明,初始化了ACL配置参数结构的三个成员,分别是类别数目num_categories,赋值为DEFAULT_MAX_CATEGORIES(1);字段数量num_fields,负责为dim,其值参见以上的说明;以及所有字段的定义成员defs,对于IPv4而言,将ipv4_defs结构的内容拷贝到其中。

    struct rte_acl_config acl_build_param;/* Perform builds */memset(&acl_build_param, 0, sizeof(acl_build_param));acl_build_param.num_categories = DEFAULT_MAX_CATEGORIES;acl_build_param.num_fields = dim;memcpy(&acl_build_param.defs, ipv6 ? ipv6_defs : ipv4_defs, ipv6 ? sizeof(ipv6_defs) : sizeof(ipv4_defs));if (rte_acl_build(context, &acl_build_param) != 0)rte_exit(EXIT_FAILURE, "Failed to build ACL trie\n");

最后,看一下IPv4的ACL字段定义ipv4_defs,如下列出了三种不同的ACL字段类型,由于目的IP地址、目的端口号分别与源IP地址和源端口号同类型,此处做了省略未列出。类型RTE_ACL_FIELD_TYPE_BITMASK对应于五元组的协议字段,长度为一个字节;类型RTE_ACL_FIELD_TYPE_MASK对应于源地址字段,长度为4个字节;类型RTE_ACL_FIELD_TYPE_RANGE对应于源端口字段,长度为2个字节。

struct rte_acl_field_def ipv4_defs[NUM_FIELDS_IPV4] = {{.type = RTE_ACL_FIELD_TYPE_BITMASK,.size = sizeof(uint8_t),.field_index = PROTO_FIELD_IPV4,.input_index = RTE_ACL_IPV4VLAN_PROTO,.offset = 0,},{.type = RTE_ACL_FIELD_TYPE_MASK,.size = sizeof(uint32_t),.field_index = SRC_FIELD_IPV4,.input_index = RTE_ACL_IPV4VLAN_SRC,.offset = offsetof(struct ipv4_hdr, src_addr) -offsetof(struct ipv4_hdr, next_proto_id),},...{.type = RTE_ACL_FIELD_TYPE_RANGE,.size = sizeof(uint16_t),.field_index = SRCP_FIELD_IPV4,.input_index = RTE_ACL_IPV4VLAN_PORTS,.offset = sizeof(struct ipv4_hdr) -offsetof(struct ipv4_hdr, next_proto_id),},...
};

DPDK-19.02

DPDK ACL链表相关推荐

  1. 《深入浅出DPDK》读书笔记(六):报文转发(run to completion、pipeline、精确匹配算法、最长前缀匹配LPM)

    本文内容为读书笔记,摘自<深入浅出DPDK> 65.网络报文的处理和转发主要分为硬件处理部分与软件处理部分,由以下模块构成: ❑Packet input:报文输入. ❑Pre-proces ...

  2. DPDK官方例程分析(4)-flow_classify

    前言 Flow Classify示例应用程序基于转发应用程序的简单框架示例. 它旨在演示使用Flow Classify库API的DPDK转发应用程序的基本组件 flow_classify例子对于DPD ...

  3. DPDK flow_classify 源码阅读

    代码部分 /* SPDX-License-Identifier: BSD-3-Clause* Copyright(c) 2017 Intel Corporation*/#include <std ...

  4. 2021年二月下旬文章导读与开源项目仓库 | scatter-gather DMA,SR-IOV,ARP欺骗,中断,Lockdep,virtio,vhost

    目录 文章目录 开源项目仓库 [转]浅谈scatter-gather DMA SR-IOV:网卡直通技术 [黑客入门] 连接公共WIFI有多危险(ARP欺骗) DPDK ACL算法介绍 文章目录 Li ...

  5. 借助英特尔® AVX-512 指令集进行数据包处理

    点击上方蓝字 关注我们吧 原文作者 Ray Kinsella:Chris MacNamara:Georgii Tkachuk 英特尔® AVX-512 为数据包处理工具包增添了强大的助力.近期即将发布 ...

  6. DPDK — 架构解析

    目录 文章目录 目录 前文列表 DPDK 架构 内核态模块 IGB_UIO KNI PMD DPDK Lib(核心部件库) 组件代码 平台相关模块 Classify 库 QoS 库 前文列表 < ...

  7. DPDK — RTE_LOG 日志模块

    目录 文章目录 目录 DPDK 的日志系统 RTE_LOG 宏 rte_log 和 rte_vlog 函数 日志模块初始化 第一阶段初始化 第二阶段初始化 注册新的日志类型 复用现有日志类型 参考文档 ...

  8. DPDK — EAL 环境抽象层

    目录 文章目录 目录 EAL 环境适配层 Linux 环境下的 EAL DPDK App 的初始化和运行 内存分配 Structure: malloc_heap Structure: malloc_e ...

  9. DPDK(Data Plane Development Kit)快速处理数据包 开发平台及接口 简介

    一.网络IO的处境和趋势 从我们用户的使用就可以感受到网速一直在提升,而网络技术的发展也从1GE/10GE/25GE/40GE/100GE的演变,从中可以得出单机的网络IO能力必须跟上时代的发展. 1 ...

最新文章

  1. JAVA实现从上往下打印二叉树(《剑指offer》)
  2. PHP识别电脑还是手机访问网站
  3. css 缩放_CSS 中 transform、animation、transition、translate的区别
  4. 谈谈AOP应用层切面设计
  5. Oracle数据库导入csv文件(sqlldr命令行)
  6. 一发就会被秒赞的句子
  7. sublime press key “escape” can't type anything
  8. python2和python3的区别 print-Python2与Python3中print用法总结
  9. 【FPGA-ASK调制解调】2-ASK调制和解调的Verilog代码实现
  10. Java实现智能对话机器人自动聊天+语音秒回
  11. 矩阵乘法 c/c++代码
  12. 关于NLSSORT和NLS_SORT的用法
  13. 删除WPS 遗留的qingnse64XXX.dll
  14. echarts的用法
  15. 软件开发的早期为何要进行可行性研究?该从哪几个方面研究目标系统的可行性?...
  16. 大数据技术之Flume
  17. 游资会带散户炒股吗?
  18. openlayers中使用rBush(R树)来存放要素等信息,本文修改了一点其中的rbush源码中的demo,使用canvas画出了insert和delete操作(建立树和删除树中数据)
  19. Linux那些事儿之我是EHCI(5) 2008年的这一场雪
  20. 什么是BFC、IFC、GFC、FFC

热门文章

  1. NAT(网络地址转换协议)
  2. Android 11.0 12.0USB鼠标右键改成返回键
  3. 小马哥spring编程核心思想_小马哥讲Spring核心编程思想
  4. MonkeyRunner 使用教程(基础教程)
  5. 多线程(十)多线程编程示例
  6. Prometheus监控容器并邮箱报警
  7. 计算机控制 电机调速实验,ACT-DT3直流电机转速控制实验指导书
  8. Node.js Websocket给指定的人发送信息
  9. 杨梅派 Waxberry Pi 全开源卡片式电脑
  10. 学习笔记--存储过程的创建和调用