鲲鹏社区首页
中文
注册
开发者
我要评分
获取效率
正确性
完整性
易理解
在线提单
论坛求助

control

在DSL语言中,control是用于定义数据包处理逻辑的核心组件。control块负责在解析器解析出数据包的头部之后,根据表匹配的结果执行相应的动作,从而实现数据包的转发、修改或丢弃等功能。

control的功能

  • 连接parser和转发逻辑

    在数据包头部完成解析后,control块会对数据包进行处理,根据头部字段信息,通过匹配表查找来决定如何操作数据包。

  • 表的匹配

    包含了匹配表的定义和应用逻辑,匹配表通过查找键(key)与数据包头部的字段进行匹配。

  • action执行

    根据查找结果执行action,如果流表未命中则执行miss action。

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所示。

表1 架构文件支持DPDK枚举列表

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();
          }
    }
表2 参数说明

参数

说明

key集合

用于进行流表匹配的信息集合,包含内置key和用户自定义key。

actions集合

流表命中后可以执行的action,包含内置action和用户自定义action。

default_action

key没有匹配项时执行的action。

  • default_action不能自定义,只能为架构文件中提供的flexda_no_action、flexda_upcall、flexda_drop三者之一。
  • 如果不指定default_action,则默认为flexda_no_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();
        }
    }
}