DPDK报文分类与访问控制
原创翻译,转载请注明出处。
dpdk提供了一个访问控制库,提供了基于一系列分类规则对接收到的报文进行分类的能力。
ACL库用来在一系列规则上执行N元组查找,可以实现多个分类和对每个分类查找最佳匹配(最高优先级),ACL库的api提供如下基本操作:
- 创建一个新的访问控制(AC)环境实例(context)
- 添加规则到这个环境实例
- 为这个实例里所有的规则,创建必需的运行时结构体来指针报文分类
- 执行接收报文分类
- 删除AC环境实例和对应的运行时结构体,并释放内存
概述
1.规则定义
当前的实现允许用户对将要执行的报文分类需要的每一个context指定它独有规则(字段集合)。但这在规则字段上有一些限制条件:
规则定义的第一个字段必须是一个字节的长度
之后的字段必须以4个连续的字节分组
这主要是为性能考虑,查找函数处理第一个输入字节做为这个流的设置的一部分,然后这查找函数的内部循环被展开来同时处理4字节的输入。
要定义规则的每一个字段,需要使用如下的结构体:
struct rte_acl_field_def {
uint8_t type; /*< type - ACL_FIELD_TYPE. */
uint8_t size; /*< size of field 1,2,4, or 8. */
uint8_t field_index; /*< index of field inside the rule. */
uint8_t input_index; /*< 0-N input index. */
uint32_t offset; /*< offset to start of field. */
};
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的五元组的分类:
struct ipv4_5tuple {
uint8_t proto;
uint32_t ip_src;
uint32_t ip_dst;
uint16_t port_src;
uint16_t port_dst;
};
需要使用下面的字段定义数组:
struct rte_acl_field_def ipv4_defs[] = {
/* first input field - always one byte long. */
{
.type = RTE_ACL_FIELD_TYPE_BITMASK,
.size = sizeof (uint8_t),
.field_index = ,
.input_index = ,
.offset = offsetof (struct ipv4_5tuple, proto),
},
/* next input field (IPv4 source address) - 4 consecutive bytes. */
{
.type = RTE_ACL_FIELD_TYPE_MASK,
.size = sizeof (uint32_t),
.field_index = ,
.input_index = ,
.offset = offsetof (struct ipv4_5tuple, ip_src),
},
/* next input field (IPv4 destination address) - 4 consecutive bytes. */
{
.type = RTE_ACL_FIELD_TYPE_MASK,
.size = sizeof (uint32_t),
.field_index = ,
.input_index = ,
.offset = offsetof (struct ipv4_5tuple, ip_dst),
},
/*
* Next 2 fields (src & dst ports) form 4 consecutive bytes.
* They share the same input index.
*/
{
.type = RTE_ACL_FIELD_TYPE_RANGE,
.size = sizeof (uint16_t),
.field_index = ,
.input_index = ,
.offset = offsetof (struct ipv4_5tuple, port_src),
},
{
.type = RTE_ACL_FIELD_TYPE_RANGE,
.size = sizeof (uint16_t),
.field_index = ,
.input_index = ,
.offset = offsetof (struct ipv4_5tuple, port_dst),
},
};
一个典型的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 头:
struct struct ipv6_hdr {
uint32_t vtc_flow; /* IP version, traffic class & flow label. */
uint16_t payload_len; /* IP packet length - includes sizeof(ip_header). */
uint8_t proto; /* Protocol, next header. */
uint8_t hop_limits; /* Hop limits. */
uint8_t src_addr[]; /* IP address of source host. */
uint8_t dst_addr[]; /* IP address of destination host(s). */
} __attribute__((__packed__));
需要使用下面的字段定义数组:
struct struct rte_acl_field_def ipv6_2tuple_defs[] = {
{
.type = RTE_ACL_FIELD_TYPE_BITMASK,
.size = sizeof (uint8_t),
.field_index = ,
.input_index = ,
.offset = offsetof (struct ipv6_hdr, proto),
},
{
.type = RTE_ACL_FIELD_TYPE_MASK,
.size = sizeof (uint32_t),
.field_index = ,
.input_index = ,
.offset = offsetof (struct ipv6_hdr, src_addr[]),
},
{
.type = RTE_ACL_FIELD_TYPE_MASK,
.size = sizeof (uint32_t),
.field_index = ,
.input_index = ,
.offset = offsetof (struct ipv6_hdr, src_addr[]),
},
{
.type = RTE_ACL_FIELD_TYPE_MASK,
.size = sizeof (uint32_t),
.field_index = ,
.input_index = ,
.offset = offsetof (struct ipv6_hdr, src_addr[]),
},
{
.type = RTE_ACL_FIELD_TYPE_MASK,
.size = sizeof (uint32_t),
.field_index = ,
.input_index = ,
.offset = offsetof (struct ipv6_hdr, src_addr[]),
},
};
一个典型的IPv4二元组规则如下:
source addr/mask protocol/mask
:db8::::::/ /0xff
任何带有协议ID为6 (TCP),源地址在
[2001:db8:1234:0000:0000:0000:0000:0000 - 2001:db8:1234:ffff:ffff:ffff:ffff:ffff] 之间的报文会匹配上的规则。
下面的例子,查找Key最后的元素是8bit长,这样就会引起4字节连续的问题:
struct acl_key {
uint8_t ip_proto;
uint32_t ip_src;
uint32_t ip_dst;
uint8_t tos; /*< This is partially using a 32-bit input element */
};
如下定义下面的字段定义数组:
struct rte_acl_field_def ipv4_defs[] = {
/* first input field - always one byte long. */
{
.type = RTE_ACL_FIELD_TYPE_BITMASK,
.size = sizeof (uint8_t),
.field_index = ,
.input_index = ,
.offset = offsetof (struct acl_key, ip_proto),
},
/* next input field (IPv4 source address) - 4 consecutive bytes. */
{
.type = RTE_ACL_FIELD_TYPE_MASK,
.size = sizeof (uint32_t),
.field_index = ,
.input_index = ,
.offset = offsetof (struct acl_key, ip_src),
},
/* next input field (IPv4 destination address) - 4 consecutive bytes. */
{
.type = RTE_ACL_FIELD_TYPE_MASK,
.size = sizeof (uint32_t),
.field_index = ,
.input_index = ,
.offset = offsetof (struct acl_key, ip_dst),
},
/*
* Next element of search key (Type of Service) is indeed 1 byte long.
* Anyway we need to allocate all the 4 consecutive bytes for it.
*/
{
.type = RTE_ACL_FIELD_TYPE_BITMASK,
.size = sizeof (uint32_t), /* All the 4 consecutive bytes are allocated */
.field_index = ,
.input_index = ,
.offset = offsetof (struct acl_key, tos),
},
};
下面是一个典型的IPv4四元组规则:
source addr/mask destination addr/mask tos/mask protocol/mask
192.168.1.0/ 192.168.2.31/ /0xff /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数据,但不做任何限制。
下面为用户提供了一个权衡性能与空间的方式,举个栗子:
struct rte_acl_ctx * acx;
struct rte_acl_config cfg;
int ret;
/*
* assuming that acx points to already created and
* populated with rules AC context and cfg filled properly.
*/
/* try to build AC context, with RT structures less then 8MB. */
cfg.max_size = 0x800000;
ret = rte_acl_build(acx, &cfg);
/*
* RT structures can't fit into 8MB for given context.
* Try to build without exposing any hard limit.
*/
if (ret == -ERANGE) {
cfg.max_size = ;
ret = rte_acl_build(acx, &cfg);
}
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):
struct rte_acl_ctx * acx;
struct rte_acl_config cfg;
int ret;
/* define a structure for the rule with up to 5 fields. */
RTE_ACL_RULE_DEF(acl_ipv4_rule, RTE_DIM(ipv4_defs));
/* AC context creation parameters. */
struct rte_acl_param prm = {
.name = "ACL_example",
.socket_id = SOCKET_ID_ANY,
.rule_size = RTE_ACL_RULE_SZ(RTE_DIM(ipv4_defs)),
/* number of fields per rule. */
.max_rule_num = , /* maximum number of rules in the AC context. */
}; struct acl_ipv4_rule acl_rules[] = {
/* matches all packets traveling to 192.168.0.0/16, applies for categories: 0,1 *
{
.data = {.userdata = 1, .category_mask = 3, .priority = 1},
/* destination IPv4 */
.field[] = {.value.u32 = IPv4(,,,),. mask_range.u32 = ,},
/* source port */
.field[] = {.value.u16 = , .mask_range.u16 = 0xffff,},
/* destination port */
.field[] = {.value.u16 = , .mask_range.u16 = 0xffff,},
},
/* matches all packets traveling to 192.168.1.0/24, applies for categories: 0 */
{
.data = {.userdata = , .category_mask = , .priority = },
/* destination IPv4 */
.field[] = {.value.u32 = IPv4(,,,),. mask_range.u32 = ,},
/* source port */
.field[] = {.value.u16 = , .mask_range.u16 = 0xffff,},
/* destination port */
.field[] = {.value.u16 = , .mask_range.u16 = 0xffff,},
},
/* matches all packets traveling from 10.1.1.1, applies for categories: 1 */
{
.data = {.userdata = , .category_mask = , .priority = },
/* source IPv4 */
.field[] = {.value.u32 = IPv4(,,,),. mask_range.u32 = ,},
/* source port */
.field[] = {.value.u16 = , .mask_range.u16 = 0xffff,},
/* destination port */
.field[] = {.value.u16 = , .mask_range.u16 = 0xffff,},
},
}; /* create an empty AC context */
if ((acx = rte_acl_create(&prm)) == NULL) {
/* handle context create failure. */
}
/* add rules to the context */
ret = rte_acl_add_rules(acx, acl_rules, RTE_DIM(acl_rules));
if (ret != ) {
/* handle error at adding ACL rules. */
}
/* prepare AC build config. */
cfg.num_categories = ;
cfg.num_fields = RTE_DIM(ipv4_defs);
memcpy(cfg.defs, ipv4_defs, sizeof (ipv4_defs));
/* build the runtime structures for added rules, with 2 categories. */
ret = rte_acl_build(acx, &cfg);
if (ret != ) {
/* handle error at build runtime structures for ACL context. */
}
对于如下的元组源IP地址:10.1.1.1,目的地址:192.168.1.15,一旦下面语句执行:
uint32_t results[]; /* make classify for 4 categories. */
rte_acl_classify(acx, data, results, , );
那么返回的结果数组里会如下:
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,一旦下面语句执行:
uint32_t results[]; /* make classify for 4 categories. */
rte_acl_classify(acx, data, results, , );
那么返回的结果数组里会如下:
results[4] = {1, 1, 0, 0};
对于分类0,1,只有规则1匹配;
对于分类2,3,都没有匹配。
对于如下的元组源IP地址:10.1.1.1,目的地址:202.212.111.12,一旦下面语句执行:
uint32_t results[]; /* make classify for 4 categories. */
rte_acl_classify(acx, data, results, , );
那么返回的结果数组里会如下:
results[4] = {0, 3, 0, 0};
对于分类1,只有规则3匹配;
对于分类0,2,3,都没有匹配。
DPDK报文分类与访问控制的更多相关文章
- DPDK flow_classify 源码阅读
代码部分 /* SPDX-License-Identifier: BSD-3-Clause * Copyright(c) 2017 Intel Corporation */ #include < ...
- QoS专题-第3期-QoS实现之报文简单分类与标记
QoS实现之报文简单分类与标记 上一期专题我们讲到,MQC中的流分类可以实现报文的分类,流行为可以对报文进行重标记,从而实现对流量的精细化差分服务.而优先级映射则可以根据802.1p优先级.DSCP优 ...
- DPDK L3fwd 源码阅读
代码部分 整个L3fwd有三千多行代码,但总体思想就是在L2fwd的基础上,增加网络层的根据 IP 地址进行路由查找的内容. main.c 文件 int main(int argc, char **a ...
- HTTP报文语法/HTTP组成
一.HTTP报文分类:请求报文和响应报文 请求报文会向Web服务器请求一个动作,响应报文会将请求的结果返回给客户端 请求报文格式: <method> <request-UR ...
- 一种基于uCos-II操作系统和lwIP协议栈的IEEE-1588主站以及基于该主站的报文处理方法
主站以及应用于电力系统的支持IEEE‐1588协议的主时钟(IEEE‐1588主站)的实现方法.该方法是在一个低成本的硬件平台上,借助uCos‐II操作系统和TCP/IP的协议栈,对以太网数据进行了分 ...
- VNF网络性能提升解决方案及实践
VNF网络性能提升解决方案及实践 2016年7月 作者: 王智民 贡献者: 创建时间: 2016-7-20 稳定程度: 初稿 修改历史 版本 日期 修订人 说明 1.0 20 ...
- 以太网安全技术ACL原理+配置
一.以太网访问控制列表 主要作用:在整个网络中分布实施接入安全性 访问控制列表ACL(Access Control List)为网络设备提供了基本的服务安全性.对某个服务而言,安全管理员首先应该考虑的 ...
- H3C qos 简单配置
qos 有三种服务模型 Best-Effort service(尽力而为服务模型) Integrated service(综合服务模型,简称Int-Serv) Differentiated servi ...
- 计算机网络(5)-----ICMP协议和PING程序
控制报文协议(Internet Control Message Protocol) 定义 它是TCP/IP协议族的一个子协议,用于在IP主机.路由器之间传递控制消息.控制消息是指网络通不通.主机是否可 ...
随机推荐
- 一条SQL语句的千回百转
SQL语言相信大家都不陌生,从本质上来说,它是一种结构化查询语言,是用来数据库之间的通信的编程语言.作为一名Java程序员,我们从Java角度来看,SQL语言相当于Java接口,而数据库是实现这个接口 ...
- oracle相关操作,存储、临时表空间、用户操作、启动过程
表空间:此空间是用来进行数据存储的(表.function.存储过程等),所以是实际物理存储区域.临时表空间:主要用途是在数据库进行排序运算[如创建索引.order by及group by.distin ...
- 1.Hadoop集群安装部署
Hadoop集群安装部署 1.介绍 (1)架构模型 (2)使用工具 VMWARE cenos7 Xshell Xftp jdk-8u91-linux-x64.rpm hadoop-2.7.3.tar. ...
- Learning notes | Data Analysis: 1.1 data evaluation
| Data Evaluation | - Use Shift + Enter or Shift + Return to run the upper box so as to make it disp ...
- Go黑帽子
使用go语言来实现python黑帽子和绝技的代码 1.unix密码破解器 package main import( "bufio" "flag" "i ...
- 基于Doxygen_C语言代码文档一键生成的记录与规范(嵌入式适用)
下位机代码格式规范整合记录 注册 doxygen 账号获取doxygen 的 *.exe 执行文件 https://pan.baidu.com/s/1MF5v-Ts80BysmZtXSqONmg 提取 ...
- DP_最长公共子序列/动规入门
学自:https://open.163.com/movie/2010/12/L/4/M6UTT5U0I_M6V2U1HL4.html 最长公共子序列:(本文先谈如何求出最长公共子序列的长度,求出最长公 ...
- Oracle入门第一天(下)——数据库的管理
一.SQL Developer的使用 常用设置,参考:https://www.cnblogs.com/biGpython/archive/2012/03/30/2424739.html https:/ ...
- 20155204 实验3《敏捷开发与XP实践》实验报告
20155204 实验3<敏捷开发与XP实践>实验报告 一.实验内容与步骤 1.研究IDEA的code菜单. 老师给的任务的是把一串代码格式化,这个任务很简单.code菜单主要是关于编辑代 ...
- 20155317 《Java程序设计》0510上课考试博客
20155317 <Java程序设计>0510上课考试博客 二.Arrays和String单元测试 在IDEA中以TDD的方式对String类和Arrays类进行学习 测试相关方法的正常, ...