OpenvSwitch结构体ofputil_flow_mod详解

前言

  • ofputil_flow_mod是一个很重要的结构体,ovs在初始化时添加默认流表项以及使用ovs-ofctl操作ovs的流表项时都涉及到该结构体。
  • 本文涉及的代码是截至到2018年6月1日,ovs主干树的代码。

struct ofputil_flow_mod详解

list_node

  • 类型 struct ovs_list 描述双向链表节点的结构体。与linux内核的实现基本一样,为了兼容MSVS,初始化部分有所不同。

match

  • 类型struct minimatch 采用稀疏编码的方式对struct match压缩。struct minimatch和sttuct match定义如下:

    include/openvswitch/match.h:40
    1
    2
    3
    4
    5
    struct match {
    struct flow flow;
    struct flow_wildcards wc;
    struct tun_metadata_allocation tun_md;
    };
    include/openvswitch/match.h:249
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    struct minimatch {
    union {
    struct {
    struct miniflow *flow;
    struct minimask *mask;
    };
    struct miniflow *flows[2];
    };
    struct tun_metadata_allocation *tun_md;
    };

    struct minimatch由两个struct miniflow和一个struct tun_metadata_allocation组成;它们与struct match中的flow, wc和tun_md一一对应。OVS在只对struct match中的flow和wc字段使用稀疏编码的方式压缩,tun_md保持不变。实际上,minimatch只保存了match结构中的flow和ws字段中的非零部分(单位是8个字节)。tun_md字段保存的数据都与隧道(GRE、Vxlan等)相关,这部分数据保持不变。

  • ovs-ofctl中从字符串转换为minimatch的处理流程(ofp-flow.c:1474 parse_ofp_str__):
    流程说明以添加“dl_type=0x0800,dl_dst=11:22:33:44:55:66”规则为例
    • 1 初始化struct ofputil_flow_mod结构体。
    • 2 定义局部变量match并初始化(ofp-flow.c:1564 struct match match = MATCH_CATCHALL_INITIALIZER;)
    • 3 调用ofputil_parse_key_value将字符串拆解成键值对{key,value},例如示例的字符串被拆解成[{“dl_type”: “0x0800”},{“dl_dst”:”11:22:33:44:55:66}]
    • 4 根据key,将value添加到match对应的字段中。
      • 在ofp_parse_field中,调用mf_parse解析value。mf_parse函数主要是将value转换为对应的数据。例如将MAC地址字符串转换为struct eth_addr。mf_parse在解析数据时,会同时解析数据的掩码。
      • 数据转换完成后,调用mf_set函数设置match中对应的字段。mf_set调用match_set_*函数操作match中的数据。例如,在设置match的dl_dst字段时,最后调用match_set_dl_dst或match_set_dl_dst_masked.
    • 5 返回步骤3,直到所有的字符串解析完。
    • 6 调用minimatch_init将match转换为minimatch

priority

  • 类型int
  • OpenFlow协议流表规则的优先级,数值越大优先级越高。实际使用中,网络报文会与多个规则匹配,例如如下两条规则:

    • 1。“dl_dst=11:22:33:44:55:66, actions=output:x”
    • 2。“in_port=a, dl_dst=11:22:33:44:55:66, actions=output:y”

    从端口A发送的目标MAC地址为“11:22:33:44:55:66”数据报文,与上面的两条规则都匹配。如果这两条规则的优先级是一样的,则按其加入的先后顺序处理;否则,按优先级来处理。如果规则1的优先级高于2,则2永远都不会被匹配上。

cookie和cookie_mask

  • 类型ovs_be64
  • Cookie,有时也用其复数形式 Cookies,指某些网站为了辨别用户身份、进行 session 跟踪而储存在用户本地终端上的数据(通常经过加密)。cookie字段的作用是身份识别或session跟踪,OpenFlow协议本身支持多个控制器,但只有一个主控制(master)工作,其他的控制器(slave)只有在主控制器失效后,slave控制器才工作。cookie的作用更多的是与session跟踪相关的,例如在某一时段临时添加了一些特殊的处理,删除时可以通过该字段将其全部删除。
    cookie字段和cookie_mask字段在添加规则时被忽略,在删除和修改规则时被使用。
  • new_cookie的类型是ovs_be64, modify_cookie的类型是布尔型
  • 用于修改或添加cookie。
    • 添加流表规则时,该字段的值不能是UINT64_MAX. 可自行定义该值。modify_cookie的值为true。
    • 在修改流表规则(OFPFC_MODIFY and OFPFC_MODIFY_STRICT)时,包含以下两种情况:
      • 1。 如果一个或多个规则匹配,并且“modify_cookie”被设置为true,则cookies被替换为new_cookie,
      • 2。如果没有匹配的规则,则按照添加规则的方式处理。

table_id

  • 类型uint8_t
  • 规则被添加的表ID。openflow从1.1版本开始支持多级流表。在ovs-ofctl的命令中如果带有table参数,如:
    ovs-ofctl add-flow internal “priority=10,table=2,dl_type=0x08000,dl_dst=11:22:33:44:55:66,actions=output:1”
    则通信协议号被设置为OFPUTIL_P_OF10_STD_TID,通信的消息仍然是struct ofp10_flow_mod。表的id号被封装到command字段中。
    格式如下:
15—————–8 7—————0
table_id command
  • 相关代码:
    • ofputil_tid_command : ofp-flow.c:356
    • ofm->command = ofputil_tid_command(fm, protocol); ofp-flow.c:436和456

command

  • 类型uint16_t
  • 操作flow的命令。目前支持的命令如下:
    • OFPFC_ADD 添加
      • OFPFC_MODIFY 修改所有匹配的流表
      • OFPFC_MODIFY_STRICT 修改严格匹配的流表
      • OFPFC_DELETE 删除所有匹配的流表
      • OFPFC_DELETE_STRICT 修改严格匹配的流表

idle_timeout和hard_timeout

  • 类型uint16_t
  • idle_timeout和hard_timeout指定规则的有效期,单位秒。其中,idle_timeout指定流表规则失效前在多少秒中内是处于idle状态的,即没有任何报文与其匹配。而hard_timeout则是指流表在多少秒后失效,不管这期间是否有报文与该规则匹配。默认值为0,即永久生效。

buffer_id

  • 类型uint32_t
  • buffer_id指明被cache的数据报的ID,该字段是datapath设置的。默认值UINT32_MAX。

out_port和out_group

  • out_port的类型是ofp_port_t,out_group的类型为unit32_t
  • out_port和out_group被ovs-ofctl的replace-flow命令使用,默认值 OFPP_ANY 。

flags

  • 类型enum ofputil_flow_mod_flags
  • ovs扩展的功能的标记。默认值为0
  • 相关代码, ofp-flows.c: 1578-1590和ofp-flow.h:35-65

importance

  • 类型uint16_t
  • openflow一个表默认支持UINT_MAX个项,当添加表项时,如果此时没有剩余的存储空间则选择牺牲者。该值越高,被作为牺牲者的概率越大。默认值为0。
  • 相关代码:
    • rule_eviction_priority : ofproto.c:8493

ofpacts和ofpacts_len

  • ofpacts的类型为struct ofpact *; ofpacts_len的类型为size_t
  • ofpacks封装openflow的action。在ovs_ofctl中解析转换的流程如下(ofp-actions.c:8741 ofpacts_parse__):

    • 1。 调用ofputil_parse_key_value将字符串拆解成键值对{key,value}。例如“output:x”的key为“output”,值为x。
    • 2。 调用ofpact_type_from_name判断key定义的操作,如果是返回真,则调用ofpact_parse在ofpacts设置action及其值。
    • 3。ofpack_parse(ofp-actions.c:8709)中通过定义宏OFPACT和在ofp-actions.h中定义的宏OFPACTS调用相关函数设置action。例如被添加的操作是output,这里就调用parse_OUTPUT函数。
    • 4。parse_OUTPUT函数中,首先判断output的操作中是否有“port”和“max_len”字符串,如果有,则调用ofpact_put_OUTPUT_TRUNC处理(NXAST_OUTPUT_TRUNC是ovs扩展的操作);否则,调用ofputil_port_from_string将用字符串表示的端口号转换为ofp_port_t。下面看一下por_map参数是怎么得到的。

      • 在ofctl_flow_mod调用parse_ofp_flow_mod_str将字符串表示的flow转换为struct minimatch前,首先调用ports_to_accept函数来获得虚拟交换机所有的端口的描述信息,是否获得获取该信息是通过全局变量use_names(默认值-1)来控制的,use_names的值是通过ovs-ofctl工具的参数–names和–no-names改变的。如果use_names不为零,则调用get_port_map从ovs-vswitchd获得端口的描述信息。
      • get_port_map与ovs-vswitchd建立通信连接后,调用port_iterator_fetch_features函数读取当前虚拟交换机所有的端口信息。
      • port_iterator_fetch_features函数调用ofpraw_alloc生成类型为OFPTYPE_FEATURES_REQUEST的请求。注意调用参数是OFPRAW_OFPT_FEATURES_REQUEST,raw_info_get函数将其转换为OFPTYPE_FEATURES_REQUEST。
      • ovs-vswitchd收到请求后,调用handle_features_request将struct bridge结构体中的ofproto字段中的端口信息发送给请求者(ofproto.c:3309)。
    • 5。调用ofpact_put_OUTPUT(ofp-actions.h:1168)在ofpacts分配空间存储struct ofpact_output类型的数据。OFPP_CONTROLLER是指openflow的控制器,即将数据发送给openflow控制器。在我们的场景下,ovs不会设置外部的openflow控制器。所以ofpact_output的max_len字段为0.

  • 相关代码
    • ofp-port.c:149
    • ofp-actions.c:8709
    • ofp-actions.h:1168
    • ofproto.c:3309

ofpacts_tlv_bitmap

  • 类型uint64_t
  • 与特定的操作相关,如NXAST_RAW_CT, NXAST_RAW_MULTIPATH, ONFACT_RAW13_COPY_FIELD等。
  • 默认值为0.
  • 相关代码:
    • of-flow.c:316 fm->ofpacts_tlv_bitmap = 0;
    • ofp-actions.inc2(自动生成的文件,在编译根目录的lib目录下)