DPDK报文分类与访问控制
原创翻译,转载请注明出处。
dpdk提供了一个访问控制库,提供了基于一系列分类规则对接收到的报文进行分类的能力。
ACL库用来在一系列规则上执行N元组查找,可以实现多个分类和对每个分类查找最佳匹配(最高优先级),ACL库的api提供如下基本操作:
- 创建一个新的访问控制(AC)环境实例(context)
- 添加规则到这个环境实例
- 为这个实例里所有的规则,创建必需的运行时结构体来指针报文分类
- 执行接收报文分类
- 删除AC环境实例和对应的运行时结构体,并释放内存
概述
1.规则定义
当前的实现允许用户对将要执行的报文分类需要的每一个context指定它独有规则(字段集合)。但这在规则字段上有一些限制条件:
规则定义的第一个字段必须是一个字节的长度
之后的字段必须以4个连续的字节分组
这主要是为性能考虑,查找函数处理第一个输入字节做为这个流的设置的一部分,然后这查找函数的内部循环被展开来同时处理4字节的输入。
要定义规则的每一个字段,需要使用如下的结构体:
1 struct rte_acl_field_def { 2 uint8_t type; /*< type - ACL_FIELD_TYPE. */ 3 uint8_t size; /*< size of field 1,2,4, or 8. */ 4 uint8_t field_index; /*< index of field inside the rule. */ 5 uint8_t input_index; /*< 0-N input index. */ 6 uint32_t offset; /*< offset to start of field. */ 7 };
type
字段的类型,有3种选项:
_MASK 表示有值和掩码的IP地址字段,定义相关的bit位
_RANGE 表示端口字段的低位和高位值
_BITMASK 表示协议标识字段的值和掩码位
size 这个参数定义了字段的字节数大小。允许的值范围有(1,2,4,8)bytes,注意,由于输入字节的分组,1或2字节的字段必须定义为连续的来组成4字节连续。通用,最好的做法是定义8或更多字节数的字段,这样构建进程会消除那些乱的字段。
field_index
一个0开始的值,用来指定字段在规则内部的位置,0~n-1表示n个字段。
input_index
上面提到过,所有输入字段,除了第一个,其他必须以4个连续字节分组,这个input_index就是来指定字段在那个组。
offset
这个定义了字段的偏移量,为查找指定了从缓冲区的起始位置的偏移。
举个栗子,定义一个IPv4的五元组的分类:
1 struct ipv4_5tuple { 2 uint8_t proto; 3 uint32_t ip_src; 4 uint32_t ip_dst; 5 uint16_t port_src; 6 uint16_t port_dst; 7 };
需要使用下面的字段定义数组:
1 struct rte_acl_field_def ipv4_defs[5] = { 2 /* first input field - always one byte long. */ 3 { 4 .type = RTE_ACL_FIELD_TYPE_BITMASK, 5 .size = sizeof (uint8_t), 6 .field_index = 0, 7 .input_index = 0, 8 .offset = offsetof (struct ipv4_5tuple, proto), 9 }, 10 /* next input field (IPv4 source address) - 4 consecutive bytes. */ 11 { 12 .type = RTE_ACL_FIELD_TYPE_MASK, 13 .size = sizeof (uint32_t), 14 .field_index = 1, 15 .input_index = 1, 16 .offset = offsetof (struct ipv4_5tuple, ip_src), 17 }, 18 /* next input field (IPv4 destination address) - 4 consecutive bytes. */ 19 { 20 .type = RTE_ACL_FIELD_TYPE_MASK, 21 .size = sizeof (uint32_t), 22 .field_index = 2, 23 .input_index = 2, 24 .offset = offsetof (struct ipv4_5tuple, ip_dst), 25 }, 26 /* 27 * Next 2 fields (src & dst ports) form 4 consecutive bytes. 28 * They share the same input index. 29 */ 30 { 31 .type = RTE_ACL_FIELD_TYPE_RANGE, 32 .size = sizeof (uint16_t), 33 .field_index = 3, 34 .input_index = 3, 35 .offset = offsetof (struct ipv4_5tuple, port_src), 36 }, 37 { 38 .type = RTE_ACL_FIELD_TYPE_RANGE, 39 .size = sizeof (uint16_t), 40 .field_index = 4, 41 .input_index = 3, 42 .offset = offsetof (struct ipv4_5tuple, port_dst), 43 }, 44 };
一个典型的IPv4五元组规则如下:
source addr/mask destination addr/mask source ports dest ports protocol/mask 192.168.1.0/24 192.168.2.31/32 0:65535 1234:1234 17/0xff
任何带有协议ID为17(UDP),源地址为192.168.1.[0-255],目的地址为192.168.2.31,源端口在[0-65535],目的端口为1234的ipv4报文将会匹配上面的规则。
定义IPv6 2元组: <protocol, IPv6 source address>的报文分类,
IPv6 头:
1 struct struct ipv6_hdr { 2 uint32_t vtc_flow; /* IP version, traffic class & flow label. */ 3 uint16_t payload_len; /* IP packet length - includes sizeof(ip_header). */ 4 uint8_t proto; /* Protocol, next header. */ 5 uint8_t hop_limits; /* Hop limits. */ 6 uint8_t src_addr[16]; /* IP address of source host. */ 7 uint8_t dst_addr[16]; /* IP address of destination host(s). */ 8 } __attribute__((__packed__));
需要使用下面的字段定义数组:
1 struct struct rte_acl_field_def ipv6_2tuple_defs[5] = { 2 { 3 .type = RTE_ACL_FIELD_TYPE_BITMASK, 4 .size = sizeof (uint8_t), 5 .field_index = 0, 6 .input_index = 0, 7 .offset = offsetof (struct ipv6_hdr, proto), 8 }, 9 { 10 .type = RTE_ACL_FIELD_TYPE_MASK, 11 .size = sizeof (uint32_t), 12 .field_index = 1, 13 .input_index = 1, 14 .offset = offsetof (struct ipv6_hdr, src_addr[0]), 15 }, 16 { 17 .type = RTE_ACL_FIELD_TYPE_MASK, 18 .size = sizeof (uint32_t), 19 .field_index = 2, 20 .input_index = 2, 21 .offset = offsetof (struct ipv6_hdr, src_addr[4]), 22 }, 23 { 24 .type = RTE_ACL_FIELD_TYPE_MASK, 25 .size = sizeof (uint32_t), 26 .field_index = 3, 27 .input_index = 3, 28 .offset = offsetof (struct ipv6_hdr, src_addr[8]), 29 }, 30 { 31 .type = RTE_ACL_FIELD_TYPE_MASK, 32 .size = sizeof (uint32_t), 33 .field_index = 4, 34 .input_index = 4, 35 .offset = offsetof (struct ipv6_hdr, src_addr[12]), 36 }, 37 };
一个典型的IPv4二元组规则如下:
source addr/mask protocol/mask 2001:db8:1234:0000:0000:0000:0000:0000/48 6/0xff
任何带有协议ID为6 (TCP),源地址在
[2001:db8:1234:0000:0000:0000:0000:0000 - 2001:db8:1234:ffff:ffff:ffff:ffff:ffff] 之间的报文会匹配上的规则。
下面的例子,查找Key最后的元素是8bit长,这样就会引起4字节连续的问题:
1 struct acl_key { 2 uint8_t ip_proto; 3 uint32_t ip_src; 4 uint32_t ip_dst; 5 uint8_t tos; /*< This is partially using a 32-bit input element */ 6 };
如下定义下面的字段定义数组:
1 struct rte_acl_field_def ipv4_defs[4] = { 2 /* first input field - always one byte long. */ 3 { 4 .type = RTE_ACL_FIELD_TYPE_BITMASK, 5 .size = sizeof (uint8_t), 6 .field_index = 0, 7 .input_index = 0, 8 .offset = offsetof (struct acl_key, ip_proto), 9 }, 10 /* next input field (IPv4 source address) - 4 consecutive bytes. */ 11 { 12 .type = RTE_ACL_FIELD_TYPE_MASK, 13 .size = sizeof (uint32_t), 14 .field_index = 1, 15 .input_index = 1, 16 .offset = offsetof (struct acl_key, ip_src), 17 }, 18 /* next input field (IPv4 destination address) - 4 consecutive bytes. */ 19 { 20 .type = RTE_ACL_FIELD_TYPE_MASK, 21 .size = sizeof (uint32_t), 22 .field_index = 2, 23 .input_index = 2, 24 .offset = offsetof (struct acl_key, ip_dst), 25 }, 26 /* 27 * Next element of search key (Type of Service) is indeed 1 byte long. 28 * Anyway we need to allocate all the 4 consecutive bytes for it. 29 */ 30 { 31 .type = RTE_ACL_FIELD_TYPE_BITMASK, 32 .size = sizeof (uint32_t), /* All the 4 consecutive bytes are allocated */ 33 .field_index = 3, 34 .input_index = 3, 35 .offset = offsetof (struct acl_key, tos), 36 }, 37 };
下面是一个典型的IPv4四元组规则:
source addr/mask destination addr/mask tos/mask protocol/mask 192.168.1.0/24 192.168.2.31/32 1/0xff 6/0xff
任何带有协议ID 6 (TCP), 源地址 192.168.1.[0-255], 目的地址 192.168.2.31, ToS 1的IPv4报文就会匹配这个规则.
当创建一个规则集合时,对每一个规则,也必须添加的附加信息:
priority
用来度量规则的权重,越高越好。如果输入的元组匹配到多个元组,那么就会使用权重最高的那条规则。如果匹配多个权重相同的规则,那么返回那条规则这个是未定义的。推荐的做法是,每个规则都设置一个唯一的权重。
category_mask
每个规则都使用的一个掩码位,用来选择规则的分类。当执行查找时,查找结果里包含了每一个分类。这样在使能了一个查找返回多个结果的情况,有效的支持了并行查找。举个栗子,有4个不同的ACL规则集合,一个用于访问控制,一个用于路由,等等。每一个集合有自己的分类,并组织在一个数据库里,一个查找就可以返回包含4个集合的结果。
userdata
一个用户定义的字段,该字段可以设置任何值除了0。对每一个分类,一个成功的查找返回匹配最高优先级的规则的用户数据。
注意:添加新规则到ACL环境中是,所有的字段都必须是主机字节序,当对输入的元组执行查找时,元组的所有字段都必须是网络字节序。
2.RT内存大小限制
rte_acl_build()创建了一个给定规则集合的内部数据结构,用来给将来的运行时遍历。当前的实现是一个multi-bit tries(字典树,步长等于8)。根据这个规则集合的规模,可能会消耗大量的内存。如果以固定的空间创建ACL字典树,同时把给定的规则集合分割成数个无关的子集并建立各自的字典树的话,同样取决于规则集合的规模,它会减少RT内存的大小要求但是会增加报文分类的时间。
在创建AC环境的时候,可以指定内部RT结构的最大内存数量限制。通过rte_acl_config的max_size字段来设置。设置一个大于0的值,在rte_acl_build()里:
尝试在RT表里最小化字典树的数目,but
确保RT表的大小不会超过给定的值。
设置为0的话,rte_acl_build()会默认是:尽量以最小大小来创建RT数据,但不做任何限制。
下面为用户提供了一个权衡性能与空间的方式,举个栗子:
1 struct rte_acl_ctx * acx; 2 struct rte_acl_config cfg; 3 int ret; 4 /* 5 * assuming that acx points to already created and 6 * populated with rules AC context and cfg filled properly. 7 */ 8 /* try to build AC context, with RT structures less then 8MB. */ 9 cfg.max_size = 0x800000; 10 ret = rte_acl_build(acx, &cfg); 11 /* 12 * RT structures can't fit into 8MB for given context. 13 * Try to build without exposing any hard limit. 14 */ 15 if (ret == -ERANGE) { 16 cfg.max_size = 0; 17 ret = rte_acl_build(acx, &cfg); 18 }
3.报文分类方法
rte_acl_build()成功之后,将开始执行报文分类,对输入的数据查找最高优先级的规则。下面有几个分类算法的实现:
RTE_ACL_CLASSIFY_SCALAR 通用实现,不需要任何硬件实现。
RTE_ACL_CLASSIFY_SSE 向量实现,能并行处理到8条流,要求SS4 4.1支持。
RTE_ACL_CLASSIFY_AVX2 向量实现,能并行处理到16条流,要求AVX2支持。
这个完全由运行时决定选择哪个算法,在创建的时候并没有什么不同。所有的算法都使用相同的RT数据结构和相同的原理。主要的不同是向量实现能手动利用IA SIMD指令和并行处理数据流。在启动ACL库之后会基于当前的平台来决定使用哪个最有效的分类方法,并把它当成默认设置。但用户可以重写这个默认的分类方法,这要求用户自己实现当前的平台选择的分类方法。
4.API使用举例:
温馨提示:如果想了解ACL API的更多细节,请参考DPDK API手册。
下面是一个IPV4五元组报文分类使用多个分类的规则的例子(Classify with Multiple Categories):
1 struct rte_acl_ctx * acx; 2 struct rte_acl_config cfg; 3 int ret; 4 /* define a structure for the rule with up to 5 fields. */ 5 RTE_ACL_RULE_DEF(acl_ipv4_rule, RTE_DIM(ipv4_defs)); 6 /* AC context creation parameters. */ 7 struct rte_acl_param prm = { 8 .name = "ACL_example", 9 .socket_id = SOCKET_ID_ANY, 10 .rule_size = RTE_ACL_RULE_SZ(RTE_DIM(ipv4_defs)), 11 /* number of fields per rule. */ 12 .max_rule_num = 8, /* maximum number of rules in the AC context. */ 13 }; 14 15 struct acl_ipv4_rule acl_rules[] = { 16 /* matches all packets traveling to 192.168.0.0/16, applies for categories: 0,1 * 17 { 18 .data = {.userdata = 1, .category_mask = 3, .priority = 1}, 19 /* destination IPv4 */ 20 .field[2] = {.value.u32 = IPv4(192,168,0,0),. mask_range.u32 = 16,}, 21 /* source port */ 22 .field[3] = {.value.u16 = 0, .mask_range.u16 = 0xffff,}, 23 /* destination port */ 24 .field[4] = {.value.u16 = 0, .mask_range.u16 = 0xffff,}, 25 }, 26 /* matches all packets traveling to 192.168.1.0/24, applies for categories: 0 */ 27 { 28 .data = {.userdata = 2, .category_mask = 1, .priority = 2}, 29 /* destination IPv4 */ 30 .field[2] = {.value.u32 = IPv4(192,168,1,0),. mask_range.u32 = 24,}, 31 /* source port */ 32 .field[3] = {.value.u16 = 0, .mask_range.u16 = 0xffff,}, 33 /* destination port */ 34 .field[4] = {.value.u16 = 0, .mask_range.u16 = 0xffff,}, 35 }, 36 /* matches all packets traveling from 10.1.1.1, applies for categories: 1 */ 37 { 38 .data = {.userdata = 3, .category_mask = 2, .priority = 3}, 39 /* source IPv4 */ 40 .field[1] = {.value.u32 = IPv4(10,1,1,1),. mask_range.u32 = 32,}, 41 /* source port */ 42 .field[3] = {.value.u16 = 0, .mask_range.u16 = 0xffff,}, 43 /* destination port */ 44 .field[4] = {.value.u16 = 0, .mask_range.u16 = 0xffff,}, 45 }, 46 }; 47 48 /* create an empty AC context */ 49 if ((acx = rte_acl_create(&prm)) == NULL) { 50 /* handle context create failure. */ 51 } 52 /* add rules to the context */ 53 ret = rte_acl_add_rules(acx, acl_rules, RTE_DIM(acl_rules)); 54 if (ret != 0) { 55 /* handle error at adding ACL rules. */ 56 } 57 /* prepare AC build config. */ 58 cfg.num_categories = 2; 59 cfg.num_fields = RTE_DIM(ipv4_defs); 60 memcpy(cfg.defs, ipv4_defs, sizeof (ipv4_defs)); 61 /* build the runtime structures for added rules, with 2 categories. */ 62 ret = rte_acl_build(acx, &cfg); 63 if (ret != 0) { 64 /* handle error at build runtime structures for ACL context. */ 65 }
对于如下的元组源IP地址:10.1.1.1,目的地址:192.168.1.15,一旦下面语句执行:
1 uint32_t results[4]; /* make classify for 4 categories. */ 2 rte_acl_classify(acx, data, results, 1, 4);
那么返回的结果数组里会如下:
results[4] = {2, 3, 0, 0};
对于分类0,规则1和规则2都匹配了,但是规则2有更高的优先级,因此result[0]包含了规则2的userdata。
对于分类1,规则1和规则3都匹配了,但是规则3有更高的优先级,因此result[1]包含了规则3的userdata。
对于分类2,3,都没有匹配,因此result[2],results[3]都是0,表示没有匹配到这些分类。
对于如下的元组源IP地址:192.168.1.1,目的地址:192.168.2.11,一旦下面语句执行:
1 uint32_t results[4]; /* make classify for 4 categories. */ 2 rte_acl_classify(acx, data, results, 1, 4);
那么返回的结果数组里会如下:
results[4] = {1, 1, 0, 0};
对于分类0,1,只有规则1匹配;
对于分类2,3,都没有匹配。
对于如下的元组源IP地址:10.1.1.1,目的地址:202.212.111.12,一旦下面语句执行:
1 uint32_t results[4]; /* make classify for 4 categories. */ 2 rte_acl_classify(acx, data, results, 1, 4);
那么返回的结果数组里会如下:
results[4] = {0, 3, 0, 0};
对于分类1,只有规则3匹配;
对于分类0,2,3,都没有匹配。
转载于:https://www.cnblogs.com/danxi/p/6650757.html
DPDK报文分类与访问控制相关推荐
- DPDK 报文收发流程(二十五)
一.报文的接收流程 传统方式接收报文时,当网卡接收到报文后会产生硬件中断,进而报文会通过协议栈,最后到达应用层,这个过程需要内核协议栈的处理. 和传统报文接收不同,当应用层想要接收来自网卡的报文时, ...
- dpdk报文收发流程--理解dma控制器、UIO驱动、描述符空间、mbuf空间、KNI
1. dpdk报文收发流程 1.1 报文接收流程 传统方式接收报文时,当网卡接收到报文后会产生硬件中断,进而报文会通过协议栈,最后到达应用层,这个过程需要内核协议栈的处理. 和传统报文接收不同,当应用 ...
- 六、QOS实现报文分类与标记
报文分类与标记 报文分类与标记 1.1.优先级映射是什么? 1.2.802.1P优先级 1.3.IP Precedence优先级 1.4.DSCP优先级 1.5.服务等级 1.6.服务等级规范定义 1 ...
- DPDK 报文调度/保序 终极解决方案 Event Dev 简介(硬件加速也很可能是鸿蒙微内核IPC性能的钥匙)...
01 背景介绍 网络报文的分发以及保序一直以来是让人头痛的问题, 为了完整的解释Event Dev的背景,我们可以从两个基本概念的定义开始: 报文分发是指针对网络通信报文的某种特征(例如 5 tupl ...
- DPDK报文转发(四)
基本的网络包处理主要包含: Packet input:报文输入. Pre-processing:对报文进行比较粗粒度的处理. Input classification:对报文进行较细粒度的分流. In ...
- 【计算机网络】网络层 : ICMP 协议 ( ICMP 差错报文 | 差错报文分类 | ICMP 询问报文 | ICMP 应用 | Ping | Traceroute )
文章目录 一.ICMP 协议 二.ICMP 协议 简介 三.ICMP 五种差错报告报文 四.ICMP 差错报文形成 五.ICMP 差错报文 不发送 情形 六.ICMP 询问报文 七.ICMP 应用 一 ...
- DPDK报文收发 run to completion, pipeline
摘自 深入浅出DPDK
- ICMP类型报文分类。
ICMP类型报文总的来说分为2类: (1)差错报告报文 类型值为3时:表示终点不可达 类型值为4时:表示源点抑制 类型值为5时:表示改变路由(Redirect) 类型值为11时:表示超时 类型值为12 ...
- 一款可编程的的串口调试工具 报文分类 脚本编程
下载链接链接: [https://download.csdn.net/download/weixin_41617063/85373149] 软件说明 一.软件基础功能: a.串口接收. b.串口发送. ...
- DPDK官方例程分析(4)-flow_classify
前言 Flow Classify示例应用程序基于转发应用程序的简单框架示例. 它旨在演示使用Flow Classify库API的DPDK转发应用程序的基本组件 flow_classify例子对于DPD ...
最新文章
- sql语句中having的作用是?
- Snapchat何以在Facebook包围下“杀出重围”?
- WIN7+wampserver2.4+zend stadio10.6.1配置Xdebug
- WORD 粘贴代码 不检查语法
- Spring Cloud Stream如何处理消息重复消费
- typescript或javascript深拷贝Object json
- html无需列表怎么打,如何以html无序列表形式而不是xml打印这些结果?
- linux每日命令(11):cat命令
- python mro c3_Python的MRO以及C3线性化算法
- Jquery获取web窗体关闭事件,排除刷新页面
- wps怎么把两张图片组合_如何对word/wps中的多个图片进行组合。 专家详解
- 一个WinForm程序的生与死
- 计算机专业英语中常见的前缀和后缀,专业英语词根与前后缀.ppt
- maven dependency problem 问题
- [CC2642r1] ble5 stacks 蓝牙协议栈 介绍和理解 TI协议栈下载
- web前端网页制作课作业:用DIV+CSS技术设计的静态网站【四大名著】中国传统文化主题题材设计
- 我是这样克服拖延症的,你也可以试试
- 涨姿势|小众建模软件MakeHuman,人物角色建模基础入门教程(1)
- 如何解决mui-scroll-wrapper组件在动态获取数据时滚动或者滑动失效的问题(亲测有效)
- 线性分类器良恶性乳腺癌肿瘤预测
热门文章
- java 运动_java web 运动前端
- java 获取麦克_AudioKit - 如何从麦克风获取实时floatChannelData?
- SQL Server之旅:(二)Could not load the DLL xpstar90.dll
- DOM中cloneNode的使用之旅
- C# 重绘tabControl,添加关闭按钮(页签)
- (转)程序员如何快速准备面试中的算法
- OpenCart支付宝付款接口(直接到账、担保交易、双接口)
- 控制总线上发送的控制信息
- Debian系、红帽系、Arch Linux系如何选择安装包
- 【TDA4系列】芯片资源:处理器Processor Subsystems 与 加速器和协处理器Accelerators and Coprocessors