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

相关文章

使用Python实现网页表格转换为markdown

《使用Python实现网页表格转换为markdown》在日常工作中,我们经常需要从网页上复制表格数据,并将其转换成Markdown格式,本文将使用Python编写一个网页表格转Markdown工具,需... 在日常工作中,我们经常需要从网页上复制表格数据,并将其转换成Markdown格式,以便在文档、邮件或

Python将字符串转换为小写字母的几种常用方法

《Python将字符串转换为小写字母的几种常用方法》:本文主要介绍Python中将字符串大写字母转小写的四种方法:lower()方法简洁高效,手动ASCII转换灵活可控,str.translate... 目录一、使用内置方法 lower()(最简单)二、手动遍历 + ASCII 码转换三、使用 str.tr

Java如何将文件内容转换为MD5哈希值

《Java如何将文件内容转换为MD5哈希值》:本文主要介绍Java如何将文件内容转换为MD5哈希值的实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Java文件内容转换为MD5哈希值一个完整的Java示例代码代码解释注意事项总结Java文件内容转换为MD5

使用Java将实体类转换为JSON并输出到控制台的完整过程

《使用Java将实体类转换为JSON并输出到控制台的完整过程》在软件开发的过程中,Java是一种广泛使用的编程语言,而在众多应用中,数据的传输和存储经常需要使用JSON格式,用Java将实体类转换为J... 在软件开发的过程中,Java是一种广泛使用的编程语言,而在众多应用中,数据的传输和存储经常需要使用j

Java实现视频格式转换的完整指南

《Java实现视频格式转换的完整指南》在Java中实现视频格式的转换,通常需要借助第三方工具或库,因为视频的编解码操作复杂且性能需求较高,以下是实现视频格式转换的常用方法和步骤,需要的朋友可以参考下... 目录核心思路方法一:通过调用 FFmpeg 命令步骤示例代码说明优点方法二:使用 Jaffree(FF

Nginx路由匹配规则及优先级详解

《Nginx路由匹配规则及优先级详解》Nginx作为一个高性能的Web服务器和反向代理服务器,广泛用于负载均衡、请求转发等场景,在配置Nginx时,路由匹配规则是非常重要的概念,本文将详细介绍Ngin... 目录引言一、 Nginx的路由匹配规则概述二、 Nginx的路由匹配规则类型2.1 精确匹配(=)2

C语言中的常见进制转换详解(从二进制到十六进制)

《C语言中的常见进制转换详解(从二进制到十六进制)》进制转换是计算机编程中的一个常见任务,特别是在处理低级别的数据操作时,C语言作为一门底层编程语言,在进制转换方面提供了灵活的操作方式,今天,我们将深... 目录1、进制基础2、C语言中的进制转换2.1 从十进制转换为其他进制十进制转二进制十进制转八进制十进

Pandas进行周期与时间戳转换的方法

《Pandas进行周期与时间戳转换的方法》本教程将深入讲解如何在pandas中使用to_period()和to_timestamp()方法,完成时间戳与周期之间的转换,并结合实际应用场景展示这些方法的... 目录to_period() 时间戳转周期基本操作应用示例to_timestamp() 周期转时间戳基

使用Python开发Markdown兼容公式格式转换工具

《使用Python开发Markdown兼容公式格式转换工具》在技术写作中我们经常遇到公式格式问题,例如MathML无法显示,LaTeX格式错乱等,所以本文我们将使用Python开发Markdown兼容... 目录一、工具背景二、环境配置(Windows 10/11)1. 创建conda环境2. 获取XSLT

Java controller接口出入参时间序列化转换操作方法(两种)

《Javacontroller接口出入参时间序列化转换操作方法(两种)》:本文主要介绍Javacontroller接口出入参时间序列化转换操作方法,本文给大家列举两种简单方法,感兴趣的朋友一起看... 目录方式一、使用注解方式二、统一配置场景:在controller编写的接口,在前后端交互过程中一般都会涉及