DPDK ACL规则字段到tries树节点转换

2023-12-19 10:32

本文主要是介绍DPDK ACL规则字段到tries树节点转换,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

示例ACL规则:

@39.7.6.0/24  146.11.37.196/32    0 : 65535   514 : 614   0x6/0xFF


ACL库中定义了三种字段类型,如下:

enum {       RTE_ACL_FIELD_TYPE_MASK = 0,RTE_ACL_FIELD_TYPE_RANGE, RTE_ACL_FIELD_TYPE_BITMASK
};

其中RTE_ACL_FIELD_TYPE_BITMASK与类型RTE_ACL_FIELD_TYPE_MASK,分别对应单字节的诸如协议号规则字段,和4字节如IP地址规则字段,这两者在转换为tries树节点node时,处理逻辑基本相同。类型RTE_ACL_FIELD_TYPE_RANGE对应规则中的端口范围字段,与前两者处理逻辑不同。

协议字段转换


以下函数acl_gen_mask_trie负责mask类的ACL字段转换到trie树节点node。对于RTE_ACL_FIELD_TYPE_BITMASK类型,以TCP协议号 6/0xff 为例,并且假设level等于1(ACL系统要求第一个字段为单字节,所以通常情况下协议号为第一个字段)。其size为1即一个字节。

  |----------|      |----------||   root   |      |   node   ||----------|      |----------|level-1            level-2

函数中的for循环为每一个字节数据分配一个rte_acl_node结构,并且依次递增其深度level的值。函数acl_gen_mask创建rte_acl_bitset类型的位图结构,并依据数值和掩码初始化位图。

static struct rte_acl_node * acl_gen_mask_trie(struct acl_build_context *context,const void *value, const void *mask, int size, int level, struct rte_acl_node **pend)
{struct rte_acl_node *root, *node, *prev;struct rte_acl_bitset bits;const uint8_t *val = value;const uint8_t *msk = mask;root = acl_alloc_node(context, level++);prev = root;for (n = size - 1; n >= 0; n--) {node = acl_alloc_node(context, level++);acl_gen_mask(&bits, val[n] & msk[n], msk[n]);acl_add_ptr(context, prev, node, &bits);prev = node;}*pend = prev;return root;
}

对于TCP协议号6/0xff,位图生成函数acl_gen_mask,将位图bitset的第6位设置为1。由于掩码为0xff覆盖所有位,最终仅设置了第6位。由于acl_gen_mask函数处理的value和mask都是1个字节(ACL库实现步长为8bit的trie树),这里的参数定义为uint32_t感觉没有必要,函数返回值range并没有被使用到。

static int acl_gen_mask(struct rte_acl_bitset *bitset, uint32_t value, uint32_t mask)
{int range = 0;/* clear the bitset values */for (n = 0; n < RTE_ACL_BIT_SET_SIZE; n++) bitset->bits[n] = 0;/* for each bit in value/mask, add bit to set */for (n = 0; n < UINT8_MAX + 1; n++) {if ((n & mask) == value) {range++;bitset->bits[n / (sizeof(bits_t) * 8)] |= 1 << (n % (sizeof(bits_t) * 8));}}return range;
}

完成位图初始化后,增加node节点关联指针。对于协议号6/0xff节点,函数acl_add_ptr将root节点的prts[0]的ptr指向第二级的node节点;与此ptr关联的value值为以上计算而来的bitset位图。由于此为root节点的第一个下级节点,root节点自身的位图值同样为0100 0000,如果之后出现分支节点,root节点自身的位图将等于所有分支位图的合集。由于trie树为8bit步长,此处的位图共256个bits,所以理论上来说,root节点最多可有256个二级分支。

static int acl_add_ptr(struct acl_build_context *context, struct rte_acl_node *node, struct rte_acl_node *ptr, struct rte_acl_bitset *bits)
{uint32_t n, num_ptrs;/* Find available ptr and add a new pointer to this node */for (n = node->min_add; n < node->max_ptrs; n++) {if (node->ptrs[n].ptr == NULL) {node->ptrs[n].ptr = ptr;acl_include(&node->ptrs[n].values, bits, 0);acl_include(&node->values, bits, -1);
}

至此,协议字段proto已经完整的转换为了tries树的两个node节点,完成之后trie树节点图如下:

    level-1                     level-2|----------|               |----------||   root   |        |----->|   node   ||----------|        |      |----------||               ||- ptrs[0].ptr -||- ptrs[0].value = 0100 0000|- value = 0100 0000

IP地址字段装换


接下来,看一下IP地址字段的节点转换,IP地址字段属于RTE_ACL_FIELD_TYPE_MASK类型。以源IP地址39.7.6.0/24(0xffff ff00)为例,介绍其转换逻辑。与以上的协议字段相同,也是使用转换函数acl_gen_mask_trie实现,不同点在与此处size长度为4个字节。函数acl_gen_mask_trie的for循环每次处理一个字节,由最高字节开始(39)。由于之前的协议字段转换已经将node的level增加到了2,此处由level-3开始。


第一次循环创建最高字节(39)的node节点,level等于4。acl_gen_mask函数将39转换为bitset位图,由于对应的掩码为0xff,转换后位图的第39位设置为1。

  |----------|      |-------------||   root   |      |   node(39)  ||----------|      |-------------|level-3             level-4

函数acl_add_ptr,如上的介绍,建立两个node节点之间的联系,完成之后的trie树节点如下:

    level-3                     level-4|----------|               |----------||   root   |        |----->|   node   ||----------|        |      |----------||               ||- ptrs[0].ptr -||- ptrs[0].value = 0x01 << 39|- value = 0x01 << 39

之后的三次循环将创建剩余字节7.6.0/0xff ff00表示的node节点,如下。由于最后一个节点node-7,其值和掩码都为0,致使位图的256个位全部为1,表明查找时匹配任意值。IP地址最终的trie树节点图如下所示:

    level-3                       level-4                      level-5                       level-6                       level-7|----------|                  |----------|                 |----------|                  |----------|                  |----------||   root   |        |-------->|   node   |         |------>|   node   |          |------>|   node   |          |------>|   node   ||----------|        |         |----------|         |       |----------|          |       |----------|          |       |----------||               |              |               |             |               |             |               ||- ptrs[0].ptr -|              |- ptrs[0].ptr -|             |- ptrs[0].ptr -|             |- ptrs[0].ptr -||- ptrs[0].value = 0x01 << 39  |- ptrs[0].value = 1000 0000  |- ptrs[0].value = 0100 0000  |- ptrs[0].value = all 1 in 256 bits|- value = 0x01 << 39          |- value = 1000 0000          |- value = 0100 0000          |- value = all 1 in 256 bits

端口范围转换


最后,看一下类型RTE_ACL_FIELD_TYPE_RANGE字段转换trie树节点的处理。此类型使用函数acl_gen_range_trie转换trie树节点。以目的端口号范围字段514 : 614(0x202:0x266)为例,其size长度为2个字节。在此示例中端口范围的下限和上限值的高8位相等(都为0x2),意味着上限值的低8位一定大于下限值的低8位,在分配两个节点node之后,交由函数acl_alloc_node处理,属于比较简单的情况。由于在源IP地址处理完成后,level值增加到了7,在经过目的IP地址的node转换,level值又增加到了12,此处由level-13开始。

  |----------|      |-------------||   root   |      |     pend    ||----------|      |-------------|level-13           level-15

函数acl_gen_range_trie,在一开始就创建两个node节点,其中一个为头节点,另一个为尾节点,如上所示。对于此处讨论的端口范围(0x202:0x266),由于满足下限和上限值的高8位相等的条件,核心处理有函数acl_gen_range完成。

static struct rte_acl_node *
acl_gen_range_trie(struct acl_build_context *context, const void *min, const void *max, int size, int level, struct rte_acl_node **pend)
{struct rte_acl_node *root;const uint8_t *lo = min;const uint8_t *hi = max;*pend = acl_alloc_node(context, level+size);root = acl_alloc_node(context, level++);if (lo[size - 1] == hi[size - 1]) {acl_gen_range(context, hi, lo, size, level, root, *pend);}
}

函数acl_gen_range实际上创建了一个node节点level-14。调用两次acl_add_ptr_range函数来建立两个相邻level节点之间的联系。

    level-13           level-14             level-15|----------|      |-------------|      |-------------||   root   |      |    node     |      |    pend     ||----------|      |-------------|      |-------------|

由于端口字段的长度size为2个字节,函数中的for循环执行一次。

static void acl_gen_range(struct acl_build_context *context,const uint8_t *hi, const uint8_t *lo, int size, int level, struct rte_acl_node *root, struct rte_acl_node *end)
{struct rte_acl_node *node, *prev;prev = root;for (n = size - 1; n > 0; n--) {node = acl_alloc_node(context, level++);acl_add_ptr_range(context, prev, node, lo[n], hi[n]);prev = node;}acl_add_ptr_range(context, prev, end, lo[0], hi[0]);
}

函数acl_add_ptr_range完成两个功能:位图的初始化,以及ptrs指针的赋值操作。对于范围限值的高8位数值(0x02),位图赋值为0x04,即第二位置1。对于范围限值的低8位(0x02:0x66),其位图为比特位由第2位到第102(0x66)位全部设置为1。

    level-13                       level-14                    level-15       |----------|                  |----------|                 |----------|    |   root   |        |-------->|   node   |         |------>|   node   |    |----------|        |         |----------|         |       |----------|    |               |              |               |             |- ptrs[0].ptr -|              |- ptrs[0].ptr -|             |- ptrs[0].value = 0000 0100   |- ptrs[0].value = all 1 in (2 - 102)bits |- value = 0000 0100           |- value = all 1 in (2 - 102)bits       

函数acl_add_ptr_range如下,置位[low, high]直接的所有位。函数acl_add_ptr参见之前的介绍。

static int acl_add_ptr_range(struct acl_build_context *context,struct rte_acl_node *root, struct rte_acl_node *node, uint8_t low, uint8_t high)
{for (n = 0; n < UINT8_MAX + 1; n++)if (n >= low && n <= high)bitset.bits[n / (sizeof(bits_t) * 8)] |= 1 << (n % (sizeof(bits_t) * 8));return acl_add_ptr(context, root, node, &bitset);
}

以上的端口范围(0x202:0x266)仅是比较简单的一种情况。对于端口范围274:581(0x112:0x245),与以上的示例不同,此时高8位不相等,函数acl_gen_range_trie生成的node节点如下,为保证上限值的低8位大于下限值的低8位,将以上的端口范围拆分为以下两个范围:(0x112:0x01ff)和(0x0200:0x245),创建具有两个分支的trie树。

    level-13                       level-14                    level-15       |----------|                  |----------|                 |----------|    |   root   |        |-------->|   node   |         |------>|   node   |    |----------|        |         |----------|         |       |----------|    |               |              |               |             |- ptrs[0].ptr -|              |- ptrs[0].ptr -|             |- ptrs[0].value = 0000 0100   |- ptrs[0].value = 1 in (0 - 0x45)bits |- value = 0000 0100           |- value = 1 in (0 - 0x45)bits   |||                               level-14                    level-15       |                            |----------|                 |----------|    |               |----------->|   node   |         |------>|   node   |    |               |            |----------|         |       |----------|    |               |                 |               |             |- ptrs[1].ptr -|                 |- ptrs[0].ptr -|             |- ptrs[1].value = 0000 0010      |- ptrs[0].value = 1 in (12 - 0xff)bits |- value = 0000 0010              |- value = 1 in (12 - 0xff)bits    |- value = 0000 0110(最终root自身位图)

总结函数acl_gen_range_trie的处理逻辑如下,其中下限值表示为Low[1]与Low[0];上限值表示为Hi[1]与Hi[0]。索引0表示低位字节,1表示高位字节。每个端口范围生成一个trie树分支,例如对于c-1条件下,将生成具有3个分支的trie树。

a)端口范围上下限值的高位字节相等,即(Hi[1] == Low[1])。
    低位字节只有一种情况:Low[0] <= Hi[0]。生成的trie树只有一个分支,参见以上的端口范围(0x202:0x266)的介绍。

b)端口范围上下限值的高位字节差值等于1,即(Hi[1] - Low[1] == 1)。
    b-1)Low[0] != 0x00,Hi[0] != 0xff。如端口范围(0x202:0x366),拆分成2个范围(0x202:0x2ff)和(0x300:0x366)。
    b-2)Low[0] == 0x00,Hi[0] != 0xff。如端口范围(0x200:0x366),拆分成2个范围(0x200:0x2ff)和(0x300:0x366)。
    b-3)Low[0] != 0x00,Hi[0] == 0xff。如端口范围(0x202:0x3ff),拆分成2个范围(0x202:0x2ff)和(0x300:0x3ff)。
    b-4)Low[0] == 0x00,Hi[0] == 0xff。如端口范围(0x200:0x3ff),不拆分。

c)端口范围上下限值的高位字节差值大于1,即(Hi[1] - Low[1] > 1)。
    c-1)Low[0] != 0x00,Hi[0] != 0xff。如端口范围(0x202:0x566),拆分成3个范围(0x202:0x2ff)和(0x300:0x4ff)和(0x500:0x566)。
    c-2)Low[0] == 0x00,Hi[0] != 0xff。如端口范围(0x200:0x566),拆分成3个范围(0x200:0x2ff)和(0x500:0x566)。
    c-3)Low[0] != 0x00,Hi[0] == 0xff。如端口范围(0x202:0x5ff),拆分成2个范围(0x202:0x2ff)和(0x300:0x5ff)。
    c-4)Low[0] == 0x00,Hi[0] == 0xff。如端口范围(0x200:0x5ff),不拆分。

 

END

 

这篇关于DPDK ACL规则字段到tries树节点转换的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/511849

相关文章

使用Java解析JSON数据并提取特定字段的实现步骤(以提取mailNo为例)

《使用Java解析JSON数据并提取特定字段的实现步骤(以提取mailNo为例)》在现代软件开发中,处理JSON数据是一项非常常见的任务,无论是从API接口获取数据,还是将数据存储为JSON格式,解析... 目录1. 背景介绍1.1 jsON简介1.2 实际案例2. 准备工作2.1 环境搭建2.1.1 添加

JAVA中整型数组、字符串数组、整型数和字符串 的创建与转换的方法

《JAVA中整型数组、字符串数组、整型数和字符串的创建与转换的方法》本文介绍了Java中字符串、字符数组和整型数组的创建方法,以及它们之间的转换方法,还详细讲解了字符串中的一些常用方法,如index... 目录一、字符串、字符数组和整型数组的创建1、字符串的创建方法1.1 通过引用字符数组来创建字符串1.2

Java将时间戳转换为Date对象的方法小结

《Java将时间戳转换为Date对象的方法小结》在Java编程中,处理日期和时间是一个常见需求,特别是在处理网络通信或者数据库操作时,本文主要为大家整理了Java中将时间戳转换为Date对象的方法... 目录1. 理解时间戳2. Date 类的构造函数3. 转换示例4. 处理可能的异常5. 考虑时区问题6.

基于C#实现将图片转换为PDF文档

《基于C#实现将图片转换为PDF文档》将图片(JPG、PNG)转换为PDF文件可以帮助我们更好地保存和分享图片,所以本文将介绍如何使用C#将JPG/PNG图片转换为PDF文档,需要的可以参考下... 目录介绍C# 将单张图片转换为PDF文档C# 将多张图片转换到一个PDF文档介绍将图片(JPG、PNG)转

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

day-51 合并零之间的节点

思路 直接遍历链表即可,遇到val=0跳过,val非零则加在一起,最后返回即可 解题过程 返回链表可以有头结点,方便插入,返回head.next Code /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode() {}*

【每日一题】LeetCode 2181.合并零之间的节点(链表、模拟)

【每日一题】LeetCode 2181.合并零之间的节点(链表、模拟) 题目描述 给定一个链表,链表中的每个节点代表一个整数。链表中的整数由 0 分隔开,表示不同的区间。链表的开始和结束节点的值都为 0。任务是将每两个相邻的 0 之间的所有节点合并成一个节点,新节点的值为原区间内所有节点值的和。合并后,需要移除所有的 0,并返回修改后的链表头节点。 思路分析 初始化:创建一个虚拟头节点

PDF 软件如何帮助您编辑、转换和保护文件。

如何找到最好的 PDF 编辑器。 无论您是在为您的企业寻找更高效的 PDF 解决方案,还是尝试组织和编辑主文档,PDF 编辑器都可以在一个地方提供您需要的所有工具。市面上有很多 PDF 编辑器 — 在决定哪个最适合您时,请考虑这些因素。 1. 确定您的 PDF 文档软件需求。 不同的 PDF 文档软件程序可以具有不同的功能,因此在决定哪个是最适合您的 PDF 软件之前,请花点时间评估您的

C# double[] 和Matlab数组MWArray[]转换

C# double[] 转换成MWArray[], 直接赋值就行             MWNumericArray[] ma = new MWNumericArray[4];             double[] dT = new double[] { 0 };             double[] dT1 = new double[] { 0,2 };

Adblock Plus官方规则Easylist China说明与反馈贴(2015.12.15)

-------------------------------特别说明--------------------------------------- 视频广告问题:因Adblock Plus的局限,存在以下现象,优酷、搜狐、17173黑屏并倒数;乐视、爱奇艺播放广告。因为这些视频网站的Flash播放器被植入了检测代码,而Adblock Plus无法修改播放器。 如需同时使用ads