前言
- 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
5struct 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
10struct 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和modify_cookie
- 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 修改严格匹配的流表
- OFPFC_ADD 添加
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目录下)