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] 之间的报文会匹配上的规则。