control
在DSL语言中,control是用于定义数据包处理逻辑的核心组件。control块负责在解析器解析出数据包的头部之后,根据表匹配的结果执行相应的动作,从而实现数据包的转发、修改或丢弃等功能。
control的功能
control的基本语法
control <control_name> (param1, param2, ...) {
@<pre/main/post>
table <table_name> {
key = {
<field_name>: <match_type>;
...
}
actions = {
<action_name0>;
<action_name1>;
...
}
default_action = <default_action>
size = <val>
timer_en = <true/false>
aging_time = <val>
}
apply {
//apply_result res = xxx.apply();
if(<table_name>.apply().hit){
...
}
}
}
table
control中table用于定义流表结构,描述了一个match-action集合。
- table前需标注@pre、@main、@post注解说明表的类型。不同表类型的规格约束请参见OVS规格。
- table前可标注@rss_fastpath以使能RSS快路径。
key列表表示匹配字段的构造规则以及匹配方式,此版本仅支持exact匹配方式。用户可以使用基础类型、header类型和header_union类型组key。当用户使用hydra_builtin.hdr架构文件中提供的带@dpdk注解的header组key时,使用的是DPDK的标准枚举。架构文件支持dpdk的列表如表1所示。
header |
包含字段 |
对应DPDK枚举 |
|---|---|---|
@dpdk header rte_eth_hdr_t |
bit<16> eth_type |
RTE_FLOW_ITEM_TYPE_ETH |
macAddr src |
||
macAddr dst |
||
@dpdk header rte_vlan_hdr_t |
bit<16> vlan_tci |
RTE_FLOW_ITEM_TYPE_VLAN |
@dpdk header rte_ipv4_hdr_t |
bit<8> protocol |
RTE_FLOW_ITEM_TYPE_IPV4 |
bit<32> src_ip |
||
bit<32> dst_ip |
||
@dpdk header rte_ipv6_hdr_t |
bit<8> protocol |
RTE_FLOW_ITEM_TYPE_IPV6 |
bit<128> src_ip |
||
bit<128> dst_ip |
||
@dpdk header rte_tcp_hdr_t |
bit<16> src_port |
RTE_FLOW_ITEM_TYPE_TCP |
bit<16> dst_port |
||
@dpdk header rte_udp_hdr_t |
bit<16> src_port |
RTE_FLOW_ITEM_TYPE_UDP |
bit<16> dst_port |
||
@dpdk header rte_icmp_hdr_t |
bit<8> type |
RTE_FLOW_ITEM_TYPE_ICMP |
bit<8> code |
||
bit<16> ident |
||
@dpdk header rte_icmp6_hdr_t |
bit<8> type |
RTE_FLOW_ITEM_TYPE_ICMP |
bit<8> code |
||
@dpdk header rte_vxlan_hdr_t |
bit<24> vni |
RTE_FLOW_ITEM_TYPE_VXLAN |
@dpdk header rte_port_id_hdr_t |
bit<16> port_id |
RTE_FLOW_ITEM_TYPE_PORT_ID |
@dpdk header rte_geneve_hdr_t |
bit<16> ver_opt_len_o_c_rsvd0 |
RTE_FLOW_ITEM_TYPE_GENEVE |
bit<16> protocol |
||
bit<24> vni |
||
bit<8> rsvd1 |
- 当前版本不支持使用表达式组key,例如:a + b : exact是不被支持的。
- 当前版本不支持使用header的isValid()方法组key。
- 当前版本不支持使用枚举类型组key。
- 当前版本自定义key不可超过64个。
- 使用header_union类型组key时,其header成员对应的协议不应同时出现,例如IPv4和IPv6只会出现其中一种协议。
- 当前版本组key只支持L3层的IPv4/IPv6复用,L4层的tcp/udp/icmp/icmp6复用,提供的默认header_union结构如下。
header_union ip_union { rte_ipv4_hdr_t dpdk_ipv4; rte_ipv6_hdr_t dpdk_ipv6; } header_union port_icmp_union { rte_tcp_hdr_t dpdk_tcp; rte_udp_hdr_t dpdk_udp; rte_icmp_hdr_t dpdk_icmp; rte_icmp6_hdr_t dpdk_icmp6; } header_union port_union { rte_tcp_hdr_t dpdk_tcp; rte_udp_hdr_t dpdk_udp; } header_union icmp_union { rte_icmp_hdr_t dpdk_icmp; rte_icmp6_hdr_t dpdk_icmp6; } - 使用header_union类型组key时,需要在apply中显式调用setValid(),标记header_union中生效的协议类型。如下代码以ip_union为例。
control Pipe(in headers hdr){ ip_union l3_union; table t{ ... key = { l3_union : exact; ... } ... } apply{ if (hdr.l3.ipv4.isValid()) { l3_union.dpdk_ipv4.setValid(); l3_union.dpdk_ipv4.protocol = hdr.l3.ipv4.protocol; l3_union.dpdk_ipv4.sip = hdr.l3.ipv4.sip; l3_union.dpdk_ipv4.dip = hdr.l3.ipv4.dip; } else if (hdr.l3.ipv6.isValid()) { l3_union.dpdk_ipv6.setValid(); l3_union.dpdk_ipv6.next_header = hdr.l3.ipv6.next_header; l3_union.dpdk_ipv6.sip = hdr.l3.ipv6.sip; l3_union.dpdk_ipv6.dip = hdr.l3.ipv6.dip; } ... t.apply(); } }
参数 |
说明 |
|---|---|
key集合 |
用于进行流表匹配的信息集合,包含内置key和用户自定义key。 |
actions集合 |
流表命中后可以执行的action,包含内置action和用户自定义action。 |
default_action |
key没有匹配项时执行的action。
|
size |
table的表项条数。非必选项,默认值为1024。所有table的size总和不可超过32MB。 最大支持2M流表。 size仅可为字面量,不支持变量和表达式。示例如下所示。
size = 0x1000000; // legal size = 16 * 1024 * 1024; // illegal |
aging_time |
老化时间,单位秒。非必选项,默认值为30。若不需要开启老化则需将aging_time设置为0。aging_time取值范围为[0,1098]。 |
- 因编程框架支持使用aging_time自定义硬件流表老化时间,所以原DPAK硬件流表老化配置项flow_max_idle在编程框架下配置不再生效。
- 对于承诺的约束范围外的数据,编译时编译器内部将自动截断,可能导致不可预期的错误。
apply
control中的apply方法是control的入口。在apply块中,可以调用table的apply方法进行该table的组key操作和查表操作,apply方法返回类型为一个包含{bool hit; bool miss; bool error;}的结构体。该结构体类型在架构文件中会声明。
下述MyPrePipe control定义了一张名为fullnat_exact的主表,使用标准DPDK枚举IPv4和IPv6、以及自定义src_port和dst_port作为key,流表条目为32M。在control的apply方法中根据报文解析的结果对变量进行填充,如果IPv4有效且ttl>1,则去匹配fullnat_exact流表,若命中则执行table actionsList中的actions,若未命中则执行default_action,即上送。示例代码如下所示。
control MyPrePipe(in headers hdr) {
rte_ipv4_hdr_t dpdk_ipv4;
rte_ipv6_hdr_t dpdk_ipv6;
bit<16> src_port;
bit<16> dst_port;
@main
table fullnat_exact {
key = {
dpdk_ipv4 : exact;
dpdk_ipv6 : exact;
src_port : exact;
dst_port : exact;
}
actions = {
ovs_action_mod_tos(hdr);
ovs_action_mod_ttl(hdr);
rte_port_id;
rte_set_ipv4_src;
rte_set_ipv4_dst;
rte_set_ipv6_src;
rte_set_ipv6_dst;
rte_set_tp_src;
rte_set_tp_dst;
rte_set_mac_dst;
rte_set_mac_src;
}
size = 33554432;
default_action = flexda_upcall;
}
apply {
if (hdr.l3.ipv4.isValid()) {
dpdk_ipv4.protocol = hdr.l3.ipv4.protocol;
dpdk_ipv4.sip = hdr.l3.ipv4.sip;
dpdk_ipv4.dip = hdr.l3.ipv4.dip;
}
else if (hdr.l3.ipv6.isValid()) {
dpdk_ipv6.next_header = hdr.l3.ipv6.next_header;
dpdk_ipv6.sip = hdr.l3.ipv6.sip;
dpdk_ipv6.dip = hdr.l3.ipv6.dip;
}
if(hdr.l4.tcp.isValid()) {
src_port = hdr.l4.tcp.src_port;
dst_port = hdr.l4.tcp.dst_port;
}
else if (hdr.l4.udp.isValid()) {
src_port = hdr.l4.udp.src_port;
dst_port = hdr.l4.udp.dst_port;
}
if (hdr.l3.ipv4.isValid() && hdr.l3.ipv4.ttl > 1) {
fullnat_exact.apply();
}
}
}