1 简介
Nic网卡有流分类处理功能,是指网卡在收包时,将符合某种规则的包放入指定的队列。网卡一般支持一种或多种流分类能,而且不同的网卡可能支持不同种类的filter,例如intel 82599系列网卡支持n-tuple、L2_tunnel、flow director等。即使intel 82599系列网卡和intel 700系列网卡都支持flow director,它们支持的方式也不一样。
dpdk要支持各种类型网卡,就要为所有网卡抽象出统一的属性,但是某些属性只对一种网卡有意义,比如并不是所有的网卡都关注flow_type 和is_vf,比如intel 82599系列网卡不需要flow_type及is_vf。还有那些可选或者可缺省的属性容易让用户产生疑惑。经常在某种filter类型中随意插入一些某个网卡特有的属性。其次,随着DPDK支持的网卡越来越多,DPDK需要定义的filter类型要增加,网卡filter功能升级也需要DPDK作相应修改,这样很容易导致API/ABI的破坏。
鉴于上述原因,一个generic flow API必不可少,rte_flow由此产生。
rte_flow主要是用来配置流量,主要就是match和action。match包括了报文信息(比如协议头、payload)和属性(物理端口、虚拟设备ID等);action包括了drop、转发到指定的queues或者物理设备或者虚拟设备、执行tunnel offload、添加标记等。此类API提供了一种通用的方式来配置硬件以匹配特定的 Ingress 或 Egress 流量,根据用户的任何配置规则更改其操作及查询相关计数器。所有API带有前缀 rte_flow ,在文件 rte_flow.h 中定义。
在OVS-DPDK应用场景里OVS可以通过rte_flow将部分网卡支持的flow pattern(eth+vlan+ip+tcp/udp)以及相应的action(如queue/mark/drop/rss等)下发至网卡,这样可以在网卡收包方向“部分”卸载掉OVS 解析封包,查找表以及enqueue/drop等action的执行动作,从而提升转发性能。pmd帮助在其设备上安装流程规则,如下图所示
2 RTE_FLOW流规则
rte_flow流规则是具有匹配模式的属性和动作列表的组合。 流规则构成了rte_flow API的基础。
一个流规则可以具有几个不同的动作(如在将数据重定向到特定队列之前执行计数,封装,解封装等操作), 而不是依靠几个规则来实现这些动作,应用程序操作具体的硬件实现细节来顺序执行。
rte_flowAPI提供了基于规则的不同优先级支持,例如,当报文匹配两个规则时,强制先执行特定规则。 然而,对于支持多个优先级的硬件,这一条不能保证。 当支持时,可用优先级的数量通常较低,这也是为什么还可以通过PMD 在软件中实现(如通过重新排序规则可以模拟缺失的优先级)。
为了尽可能保持与硬件无关,默认情况下所有规则都被认为具有相同的优先级,这意味着重叠规则(当数据包被多个过滤器匹配时)之间的顺序是不确定的。
流规则主要有:
- 属性:流规则的属性,例如其方向(Ingress或Egress)和优先级。
- 模式条目:匹配模式的一部分,匹配特定的数据包数据或流量属
性。也可以描述模式本身属性,如反向匹配。 - 匹配条目:查找的属性,组合任意的模式。
- 动作:每当数据包被匹配时执行的操作(如丢弃流量/转移流量等)
2.1 属性
struct rte_flow_attr {
uint32_t group; /**< Priority group. */
/*流规则可以通过为其分配一个公共的组号来分组。
较低的值具有较高的优先级。组0具有最高优先级。
Group 0是默认,进入匹配后可以通过JUMP action到达其他的Group。*/
uint32_t priority; /**< Rule priority level within group. */
/*该流规则在组内的优先级*/
uint32_t ingress:1; /**< Rule applies to ingress traffic. */
uint32_t egress:1; /**< Rule applies to egress traffic. */
/*流量方向的指定,必须至少指定一个方向,流量规则可以应用于入站和/或出站流量(Ingress/Egress)*/
uint32_t transfer:1;
/*将rule转移到模式中能找到的任何设备,所以可以重新路由别人的流量。*/
uint32_t reserved:29; /**< Reserved, must be zero. */
};
2.2 模式条目
struct rte_flow_item {
enum rte_flow_item_type type; /**< Item type. */
/*匹配的类型*/
const void *spec; /**< Pointer to item specification structure. */
/*要匹配的数值(如IPv4地址)。*/
const void *last; /**< Defines an inclusive range (spec to last). */
/*规格中的相应字段的范围上限。*/
const void *mask; /**< Bit-mask applied to spec and last. */
/*应用于spec和last的位掩码(如匹配IPv4地址的前缀)*/
};
对于模式条目中的type,可以匹配的模式有两种:
- 匹配协议头部及报文数据(ANY,RAW,ETH,VLAN,IPV4,IPV6,ICMP,UDP,TCP,SCTP,VXLAN,MPLS,GRE等等)。
- 匹配元数据或影响模式处理(END,VOID,INVERT,PF,VF,PORT等等)。
条目规范结构用于匹配协议字段(或项目属性)中的特定值。文档描述每个条目是否与一个条目及其类型名称相关联。
可以为给定的条目最多设置三个相同类型的结构:
- spec: 要匹配的数值(如IPv4地址)。
- last: 规格中的相应字段的范围上限。
- mask: 应用于spec和last的位掩码(如匹配IPv4地址的前缀)。
使用限制:
- 没有spec 就设置 mask 或 last 是错误的。
- 错误的last 值如0或者等于 spec 将被忽略,他们不能产生范围。不支持低于 spec 的非0值。
- 设置spce 和可选的 last ,而不设置 mask 会导致PMD使用该条目定义的默认`` mask``(定义 为 rte_flow_item_{name}_mask 常量)。不设置他们相当于提供空掩码匹配。
- 不设置他们相当于提供空掩码匹配。
- 掩码是用于spec 和 last 的简单位掩码,如果不小心使用,可能会产生意想不到的结果。例如,对于IPv4地 址字段,spec提供10.1.2.3,last提供10.3.4.5,掩码为255.255.0.0,有效范围为10.1.0.0~10.3.255.255。
匹配以太网头部的条目示例:
Table 8.1 Ethernet item |
||
Field |
Subfield |
Value |
spec |
src |
00:01:02:03:04 |
dst |
00:2a:66:00:01 |
|
type |
0x22aa |
|
last |
unspecified |
|
mask |
src |
00:ff:ff:ff:00 |
dst |
00:00:00:00:ff |
|
type |
0x0000 |
无掩码的位可以匹配任意的值(显示为 ? ), 以太头部具有如下的属性匹配信息:
- src: ??:01:02:03:??
- dst: ??:??:??:??:01
- type: 0x????
2.3 匹配模式
根据dpdk org 资料,从最低的协议开始,以END items终结。
例子如下:
TCPv4 as L4 |
|
Index |
Item |
0 |
Ethernet |
1 |
IPv4 |
2 |
TCP |
3 |
END |
TCPv6 in VXLAN |
|
Index |
Item |
0 |
Ethernet |
1 |
IPv4 |
2 |
UDP |
3 |
VXLAN |
4 |
Ethernet |
5 |
IPv6 |
6 |
TCP |
7 |
END |
TCPv4 as L4 with meta items |
|
Index |
Item |
0 |
VOID |
1 |
Ethernet |
2 |
VOID |
3 |
IPv4 |
4 |
TCP |
5 |
VOID |
6 |
VOID |
7 |
END |
上面的例子显示了一个元条目,如何实现不影响报文数据匹配结果,只要他们保持堆叠正确。结果匹配与 “TCPv4 as L4” 条目相同。
UDPv6 anywhere |
|
Index |
Item |
0 |
IPv6 |
1 |
UDP |
2 |
END |
2.4 ITEM
END,PMD强制支持,spec、last、mask都忽略
VOID,其他同上
INVERT,反向匹配,spec、last、mask都忽略
PF,VF匹配PF和VF的流量,spec、last、mask都不能设置
VF,能被多次指定;匹配PF和VF流量;默认的mask匹配任意VF ID
PHY_PORT,默认的mask匹配任意端口号
item types
大多数基本上都具有bit-mask的协议头定义。必须从最低协议层到最高协议层的匹配模式。
ANY,匹配任意协议,默认的mask代表任意层
RAW,直接匹配给定长度、指定偏移的字符;不支持last字段的范围配置;默认mask所有字段精确匹配。
ETH,匹配Ethernet协议头;dst是目的MAC;src是源MAC;type是类型;默认mask只匹配源和目的地址。
VLAN,匹配VLAN协议头;tci表示tag control information;inner_type表示inner EtherType;默认的mask只匹配TCI的VID部分。
IPV4,hdr表示IPv4协议头;默认的mask只匹配源目的地址。
IPV6、ICMP、UDP、TCP、SCTP类似。
VXLAN,flags通常是0x08;rsvd0缺省,通常0x000000;vni;rsvd1缺省,通常是0x00;默认的mask只匹配vni。
PORT_ID,默认的mask匹配指定的端口号
MARK,匹配之前用MARKaction打上标记的报文,默认的mask匹配任意值
3 rte_flow下发流程
OVS在做partial offload时, rte_flow下发流程如下图所示(以dpdk i40e driver为例):
1) OVS在处理数据包时会根据 key值进行datapath cache流表匹配,如果匹配成功则执行流表对应的 action;如果失败则通过upcall送入ofproto层处理,之后会将flow下发至datapath cache。
2) OVS在下发flow至datapath cache的同时,会检查下发的flow是否带mark值,如果flow带mark值,则说明是之前下发过的,flow put action会调整为modify,如果flow不带mark值,则生成新的mark值,flow put action为add。(注意mark id一般是按序递增,注意mark值是global的,即不会出现若干pmd线程下发同一条flow时产生mark值冲突问题)
3) 根据netdev类型调用对应的flow_put。(注意目前只支持dpdk类型的port做网卡收方向的flow_put)
4) 在产生rte_flow之前要先做两步检查,一是检查是否是做flow modify,如果是则要通过i40e driver把已存在的rte_flow删除。二是检查flow的有效性:这一步只是在OVS层面做检查,即检查是否有OVS自定义的封包解析栏位,如metadata等。如果包含,则意味着dpdk rte_flow无法解析,这时会直接退出。 5)检查通过,则进入dpdk_add_rte_flow_offload,构造rte_flow pattern。(attr里的ingress为1,action目前固定为mark+rss+end )
6)接下来的处理动作是在dpdk i40e driver中完成的,即dpdk i40e driver要做进一步的flow pattern检查(parse_fdir_pattern/action/attr),根据网卡具体支持的解析规则检查flow spec和flow mask是否支持,如果不支持则返回错误代码。之后调用flow_add_fdir,将flow pattern/action写入网卡。
7)在网卡收到包时,会解析包头信息,当发现有匹配则网卡会打上flow pattern对应的mark值(这个值会带在mbuf.hash.fdir.hi,同时mbuf.ol_flag对应rx_fdir_id的bit会置1)。OVS在做datapath flow cache查找时,如果发现这笔pkt带mark值,则跳过解析数据包和datapath cache查找,直接根据mark值找到对应的flow entry,提取并执行action。
4 参考资料
https://doc.dpdk.org/guides/prog_guide/rte_flow.html
https://www.sdnlab.com/23216.html