可以通过controller或者ovs-ofctl命令给网桥添加流表,这篇文章以ovs-ofctl添加流表为例,看一下如何解析匹配域和action,如何发送openflow消息给网桥及ovs-vswitchd后台进程如何处理openflow消息。
添加流表格式
###添加流表时,都会调用 ofputil_parse_key_value 解析匹配域和动作域,格式如下
a. 可以只指定key,如下命令,tcp 指定协议,actions 中的1表示出端口为1
ovs-ofctl add-flow br0 "table=0 , priority=50, tcp, actions=1"
b. 指定key-value对,key-value对中间可以使用"=",":"或者使用小括号将value括起来key(value)
ovs-ofctl add-flow br0 "table=0 , priority=50, ct_state=-trk, tcp, in_port:1, actions=output:2"
ovs-ofctl add-flow br0 "table=0 , priority=50, ct_state=-trk, tcp, in_port=1, actions=output=2"
ovs-ofctl add-flow br0 "table=0 , priority=50, ct_state=-trk, tcp, in_port(1), actions=output(2)"
ovs支持的匹配字段
//初始化时,将支持的字段,按照name计算hash值后,保存到全局变量 mf_by_name。
//解析匹配字段时,可以通过name查找是否支持此字段
enum OVS_PACKED_ENUM mf_field_id {
/* ## -------- ## */
/* ## Metadata ## */
/* ## -------- ## */
/* "dp_hash".
*
* Flow hash computed in the datapath. Internal use only, not programmable
* from controller.
*
* The OXM code point for this is an attempt to test OXM experimenter
* support, which is otherwise difficult to test due to the dearth of use
* out in the wild. Because controllers can't add flows that match on
* dp_hash, this doesn't commit OVS to supporting this OXM experimenter
* code point in the future.
*
* Type: be32.
* Maskable: bitwise.
* Formatting: hexadecimal.
* Prerequisites: none.
* Access: read-only.
* NXM: NXM_NX_DP_HASH(35) since v2.2.
* OXM: NXOXM_ET_DP_HASH(0) since OF1.5 and v2.4.
*/
MFF_DP_HASH,
...
MFF_N_IDS
};
nxm_init() -> nxm_do_init
shash_init(&mf_by_name);
for (i = 0; i < MFF_N_IDS; i++) {
//mf_fields 全局变量,包含所有支持的匹配字段
const struct mf_field *mf = &mf_fields[i];
ovs_assert(mf->id == i); /* Fields must be in the enum order. */
shash_add_once(&mf_by_name, mf->name, mf);
if (mf->extra_name) {
shash_add_once(&mf_by_name, mf->extra_name, mf);
}
}
//mf_fields 全局变量的定义,包含了文件 meta-flow.inc,此文件是在编译时通过python脚本自动生成,
//其中定义了支持的所有匹配字段
const struct mf_field mf_fields[MFF_N_IDS] = {
#include "meta-flow.inc"
};
//meta-flow.inc 如下,摘取此文件
{
MFF_DP_HASH,
"dp_hash", NULL,
4, 32, false,
MFM_FULLY, MFS_HEXADECIMAL, MFP_NONE, false, false,
OFPUTIL_P_NXM_OXM_ANY,
OFPUTIL_P_NXM_OXM_ANY,
OFPUTIL_P_NXM_OXM_ANY,
-1, /* not usable for prefix lookup */
},
{
MFF_RECIRC_ID,
"recirc_id", NULL,
4, 32, false,
MFM_NONE, MFS_DECIMAL, MFP_NONE, false, false,
OFPUTIL_P_NXM_OXM_ANY,
OFPUTIL_P_NONE,
OFPUTIL_P_NONE,
-1, /* not usable for prefix lookup */
},
{
MFF_PACKET_TYPE,
"packet_type", NULL,
4, 32, false,
MFM_NONE, MFS_PACKET_TYPE, MFP_NONE, false, false,
OFPUTIL_P_NXM_OXM_ANY,
OFPUTIL_P_NONE,
OFPUTIL_P_NONE,
-1, /* not usable for prefix lookup */
},
ovs支持的action
支持的大部分action都定义在宏 OFPACTS 中,它引用的另一个宏 OFPACT 在不同函数中定义不同。
宏 OFPACT 的四个参数分别表示:
ENUM -- 用来定义 type OFPACT_ENUM,
STRUCT -- 结构体定义,用来保存 action 字段,
MEMBER -- 结构体中的一个字段,
NAME -- action的名字。
#define OFPACTS \
/* Output. */ \
OFPACT(OUTPUT, ofpact_output, ofpact, "output") \
OFPACT(GROUP, ofpact_group, ofpact, "group") \
OFPACT(CONTROLLER, ofpact_controller, userdata, "controller") \
OFPACT(ENQUEUE, ofpact_enqueue, ofpact, "enqueue") \
OFPACT(OUTPUT_REG, ofpact_output_reg, ofpact, "output_reg") \
OFPACT(BUNDLE, ofpact_bundle, slaves, "bundle") \
...
//比如下面的函数,这里的宏 OFPACT 用来定义 type
/* enum ofpact_type, with a member OFPACT_ for each action. */
enum OVS_PACKED_ENUM ofpact_type {
#define OFPACT(ENUM, STRUCT, MEMBER, NAME) OFPACT_##ENUM,
OFPACTS
#undef OFPACT
};
展开后 ofpact_type 定义如下
enum OVS_PACKED_ENUM ofpact_type {
OFPACT_OUTPUT,
OFPACT_GROUP,
OFPACT_CONTROLLER,
OFPACT_ENQUEUE,
...
OFPACT_CT
...
};
//比如下面的函数,这里的宏 OFPACT 用来根据 name 判断是否有匹配的action,如果有则设置type,并返回true
static bool
ofpact_type_from_name(const char *name, enum ofpact_type *type)
{
#define OFPACT(ENUM, STRUCT, MEMBER, NAME) \
if (!strcasecmp(name, NAME)) { \
*type = OFPACT_##ENUM; \
return true; \
}
OFPACTS
#undef OFPACT
return false;
}
//比如下面的函数,这里的宏 OFPACT 用来根据 type 返回对应的name
const char *
ofpact_name(enum ofpact_type type)
{
switch (type) {
#define OFPACT(ENUM, STRUCT, MEMBER, NAME) case OFPACT_##ENUM: return NAME;
OFPACTS
#undef OFPACT
}
return "";
}
//比如下面的函数,这里的宏 OFPACT 用来根据 type 执行对应的函数,解析action
static char * OVS_WARN_UNUSED_RESULT
ofpact_parse(enum ofpact_type type, char *value,
const struct ofputil_port_map *port_map, struct ofpbuf *ofpacts,
enum ofputil_protocol *usable_protocols)
{
switch (type) {
#define OFPACT(ENUM, STRUCT, MEMBER, NAME) \
case OFPACT_##ENUM: \
return parse_##ENUM(value, port_map, ofpacts, usable_protocols);
OFPACTS
#undef OFPACT
default:
OVS_NOT_REACHED();
}
}
ovs-ofctl add-flow 添加流表流程
ofctl_add_flow -> ofctl_flow_mod
static void
ofctl_flow_mod(int argc, char *argv[], uint16_t command)
{
if (argc > 2 && !strcmp(argv[2], "-")) {
ofctl_flow_mod_file(argc, argv, command);
} else {
struct ofputil_flow_mod fm;
char *error;
enum ofputil_protocol usable_protocols;
//解析命令行指定的流表信息,保存到 struct ofputil_flow_mod fm 中,主要包含匹配字段和action。
//根据匹配字段和action,得出最低支持的协议版本号
error = parse_ofp_flow_mod_str(&fm, argc > 2 ? argv[2] : "",
ports_to_accept(argv[1]), command,
&usable_protocols);
if (error) {
ovs_fatal(0, "%s", error);
}
//将 struct ofputil_flow_mod fm 中的信息转换成 openflow 消息,并发送
ofctl_flow_mod__(argv[1], &fm, 1, usable_protocols);
}
}
下面分别看一下函数parse_ofp_flow_mod_str和ofctl_flow_mod__的实现。
parse_ofp_flow_mod_str
parse_ofp_flow_mod_str -> parse_ofp_str -> parse_ofp_str__解析命令行指定的匹配域和action
static char * OVS_WARN_UNUSED_RESULT
parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string,
const struct ofputil_port_map *port_map,
enum ofputil_protocol *usable_protocols)
*fm = (struct ofputil_flow_mod) {
.match = MATCH_CATCHALL_INITIALIZER,
.priority = OFP_DEFAULT_PRIORITY,
.table_id = 0xff,
.command = command,
.buffer_id = UINT32_MAX,
.out_port = OFPP_ANY,
.out_group = OFPG_ANY,
};
//将 action= 后面的字符串保存在 act_str 中
act_str = extract_actions(string);
//解析匹配域,保存到 fm->match
while (ofputil_parse_key_value(&string, &name, &value)) {
const struct protocol *p;
const struct mf_field *mf;
//解析协议相关的字段
if (parse_protocol(name, &p)) {
match_set_dl_type(&fm->match, htons(p->dl_type));
if (p->nw_proto) {
match_set_nw_proto(&fm->match, p->nw_proto);
}
match_set_default_packet_type(&fm->match);
} else if (!strcmp(name, "eth")) {
match_set_packet_type(&fm->match, htonl(PT_ETH));
//到hash表 mf_by_name 查找是否支持name指定的字段
} else if ((mf = mf_from_name(name)) != NULL) {
parse_field(mf, value, port_map, &fm->match, usable_protocols);
union mf_value value, mask;
//解析 value,并设置相应的 mask
mf_parse(mf, s, port_map, &value, &mask);
//将value保存到 match 中
mf_set(mf, &value, &mask, match, &error);
if (!mask || is_all_ones(mask, mf->n_bytes)) {
mf_set_value(mf, value, match, err_str);
return mf->usable_protocols_exact;
} else if (is_all_zeros(mask, mf->n_bytes) && !mf_is_tun_metadata(mf)) {
/* Tunnel metadata matches on the existence of the field itself, so
* it still needs to be encoded even if the value is wildcarded. */
mf_set_wild(mf, match, err_str);
return OFPUTIL_P_ANY;
}
switch (mf->id) {
case MFF_CT_ZONE:
case MFF_CT_NW_PROTO:
return OFPUTIL_P_NONE;
...
case MFF_CT_STATE:
match_set_ct_state_masked(match, ntohl(value->be32), ntohl(mask->be32));
break;
}
}
...
}
//解析action,保存到 fm->ofpacts
enum ofputil_protocol action_usable_protocols;
struct ofpbuf ofpacts;
ofpbuf_init(&ofpacts, 32);
ofpacts_parse_instructions(act_str, port_map, &ofpacts, &action_usable_protocols);
ofpacts_parse_copy(s, port_map, ofpacts, usable_protocols, true, 0);
*usable_protocols = OFPUTIL_P_ANY;
ofpacts_parse(s, port_map, ofpacts, usable_protocols, allow_instructions, outer_action);
ofpacts_parse__(str, port_map, ofpacts, usable_protocols, allow_instructions, outer_action);
pos = str;
//解析key value
while (ofputil_parse_key_value(&pos, &key, &value)) {
enum ovs_instruction_type inst = OVSINST_OFPIT11_APPLY_ACTIONS;
enum ofpact_type type;
char *error = NULL;
ofp_port_t port;
//根据key查找是否支持此action,并返回 type
if (ofpact_type_from_name(key, &type)) {
//根据 type,解析action,如果 type 为 OFPACT_CT,则执行 parse_CT
ofpact_parse(type, value, port_map, ofpacts, usable_protocols);
parse_CT(value, port_map, ofpacts, usable_protocols);
inst = ovs_instruction_type_from_ofpact_type(type);
} else if (!strcasecmp(key, "mod_vlan_vid")) {
error = parse_set_vlan_vid(value, ofpacts, true);
} else if (!strcasecmp(key, "mod_vlan_pcp")) {
error = parse_set_vlan_pcp(value, ofpacts, true);
} else if (!strcasecmp(key, "set_nw_ttl")) {
error = parse_SET_IP_TTL(value, port_map,
ofpacts, usable_protocols);
} else if (!strcasecmp(key, "pop_vlan")) {
error = parse_pop_vlan(ofpacts);
} else if (!strcasecmp(key, "set_tunnel64")) {
error = parse_set_tunnel(value, ofpacts,
NXAST_RAW_SET_TUNNEL64);
} else if (!strcasecmp(key, "load")) {
error = parse_reg_load(value, ofpacts);
} else if (!strcasecmp(key, "bundle_load")) {
error = parse_bundle_load(value, port_map, ofpacts);
} else if (!strcasecmp(key, "drop")) {
drop = true;
} else if (!strcasecmp(key, "apply_actions")) {
return xstrdup("apply_actions is the default instruction");
//解析出端口,可以指定端口号,也可以指定端口名字
} else if (ofputil_port_from_string(key, port_map, &port)) {
ofpact_put_OUTPUT(ofpacts)->port = port;
} else {
return xasprintf("unknown action %s", key);
}
...
}
//将解析的action保存到 fm->ofpacts
fm->ofpacts_len = ofpacts.size;
fm->ofpacts = ofpbuf_steal_data(&ofpacts);
ofctl_flow_mod__
ofctl_flow_mod__ 将 struct ofputil_flow_mod fm 中的信息转换成 openflow 消息,并将消息发送出去
static void
ofctl_flow_mod__(const char *remote, struct ofputil_flow_mod *fms,
size_t n_fms, enum ofputil_protocol usable_protocols)
{
enum ofputil_protocol protocol;
struct vconn *vconn;
size_t i;
if (bundle) {
bundle_flow_mod__(remote, fms, n_fms, usable_protocols);
return;
}
protocol = open_vconn_for_flow_mod(remote, &vconn, usable_protocols);
for (i = 0; i < n_fms; i++) {
struct ofputil_flow_mod *fm = &fms[i];
//ofputil_encode_flow_mod 封装openflow消息,transact_noreply将消息发送出去
transact_noreply(vconn, ofputil_encode_flow_mod(fm, protocol));
free(CONST_CAST(struct ofpact *, fm->ofpacts));
}
vconn_close(vconn);
}
//将 struct ofputil_flow_mod fm 中的信息转换成 openflow 消息
struct ofpbuf *
ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm, enum ofputil_protocol protocol)
//不同的协议版本有不同的处理
switch (protocol) {
case OFPUTIL_P_OF11_STD:
case OFPUTIL_P_OF12_OXM:
case OFPUTIL_P_OF13_OXM:
case OFPUTIL_P_OF14_OXM:
case OFPUTIL_P_OF15_OXM:
case OFPUTIL_P_OF16_OXM: {
struct ofp11_flow_mod *ofm;
...
break;
}
case OFPUTIL_P_OF10_STD:
case OFPUTIL_P_OF10_STD_TID: {
struct ofp10_flow_mod *ofm;
...
break;
}
case OFPUTIL_P_OF10_NXM:
case OFPUTIL_P_OF10_NXM_TID: {
struct nx_flow_mod *nfm;
int match_len;
//分配内存,将openflow信息保存到 struct nx_flow_mod
msg = ofpraw_alloc(OFPRAW_NXT_FLOW_MOD, OFP10_VERSION,
NXM_TYPICAL_LEN + fm->ofpacts_len);
ofpraw_alloc_xid(raw, version, alloc_xid(), extra_tailroom);
struct ofpbuf *buf = ofpbuf_new(0);
ofpraw_put__(raw, version, xid, extra_tailroom, buf);
//这里用到另一个自动生成的全局变量 raw_infos
const struct raw_info *info = raw_info_get(raw);
const struct raw_instance *instance = raw_instance_get(info, version);
const struct ofphdrs *hdrs = &instance->hdrs;
struct ofp_header *oh;
buf->header = ofpbuf_put_uninit(buf, instance->hdrs_len);
buf->msg = ofpbuf_tail(buf);
//填充 struct ofphdrs
oh = buf->header;
oh->version = version;
oh->type = hdrs->type;
oh->length = htons(buf->size);
oh->xid = xid;
#define OF_VENDOR_ID 0
#define HPL_VENDOR_ID 0x000004EA /* HP Labs. */
#define NTR_VENDOR_ID 0x0000154d /* Netronome. */
#define NTR_COMPAT_VENDOR_ID 0x00001540 /* Incorrect value used in v2.4. */
#define NX_VENDOR_ID 0x00002320 /* Nicira. */
#define ONF_VENDOR_ID 0x4f4e4600 /* Open Networking Foundation. */
#define INTEL_VENDOR_ID 0x0000AA01 /* Intel */
//如果 hdrs->type 为 OFPT_VENDOR,说明是厂商自定义的类型,
//需要通过 vendor 指定厂商id(上面的宏定义),并通过 subtype 指定真正的消息类型
if (hdrs->type == OFPT_VENDOR) {
struct ofp_vendor_header *ovh = buf->header;
ovh->vendor = htonl(hdrs->vendor);
ovh->subtype = htonl(hdrs->subtype);
}
nfm = ofpbuf_put_zeros(msg, sizeof *nfm);
nfm->command = ofputil_tid_command(fm, protocol);
nfm->cookie = fm->new_cookie;
//处理 match 匹配域
match_len = nx_put_match(msg, &fm->match, fm->cookie, fm->cookie_mask);
nfm = msg->msg;
nfm->idle_timeout = htons(fm->idle_timeout);
nfm->hard_timeout = htons(fm->hard_timeout);
nfm->priority = htons(fm->priority);
nfm->buffer_id = htonl(fm->buffer_id);
nfm->out_port = htons(ofp_to_u16(fm->out_port));
nfm->flags = raw_flags;
nfm->match_len = htons(match_len);
//处理 action
ofpacts_put_openflow_actions(fm->ofpacts, fm->ofpacts_len, msg, version);
break;
}
default:
OVS_NOT_REACHED();
}
ofpmsg_update_length(msg);
struct ofp_header *oh = ofpbuf_at_assert(buf, 0, sizeof *oh);
oh->length = htons(buf->size);
return msg;
ovs-vswitchd 接收openflow消息,添加流表流程
接收到openflow消息后会调用handle_openflow__进行处理,其中参数msg->data 指向openflow消息,包括ofp_header和消息体。
openflow消息格式如下(以ct消息为例)
struct ofp_vendor_header(struct ofp_header+vendor+subtype) + struct nx_flow_mod + match field(header+payload) +
match field(header+payload) + struct ofp_action_header + struct nx_action_conntrack
static enum ofperr
handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg)
OVS_EXCLUDED(ofproto_mutex)
{
const struct ofp_header *oh = msg->data;
enum ofptype type;
enum ofperr error;
//解析openflow消息类型,不是直接用 oh->type,oh->type不是真正的type
error = ofptype_decode(&type, oh);
if (error) {
return error;
}
switch (type) {
//添加流表消息类型为 OFPTYPE_FLOW_MOD,调用handle_flow_mod处理
case OFPTYPE_FLOW_MOD:
return handle_flow_mod(ofconn, oh);
}
}
handle_flow_mod首先解码openflow消息到struct ofputil_flow_mod,再添加流表。
static enum ofperr
handle_flow_mod(struct ofconn *ofconn, const struct ofp_header *oh)
OVS_EXCLUDED(ofproto_mutex)
{
struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
struct ofputil_flow_mod fm;
uint64_t ofpacts_stub[1024 / 8];
struct ofpbuf ofpacts;
enum ofperr error;
error = reject_slave_controller(ofconn);
if (error) {
return error;
}
ofpbuf_use_stub(&ofpacts, ofpacts_stub, sizeof ofpacts_stub);
//解码openflow消息,保存到 struct ofputil_flow_mod fm
error = ofputil_decode_flow_mod(&fm, oh, ofconn_get_protocol(ofconn),
ofproto_get_tun_tab(ofproto),
&ofproto->vl_mff_map, &ofpacts,
u16_to_ofp(ofproto->max_ports),
ofproto->n_tables);
if (!error) {
struct openflow_mod_requester req = { ofconn, oh };
//处理fm
error = handle_flow_mod__(ofproto, &fm, &req);
}
ofpbuf_uninit(&ofpacts);
return error;
}
下面分别看一下ofputil_decode_flow_mod和handle_flow_mod__
ofputil_decode_flow_mod
enum ofperr
ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
const struct ofp_header *oh,
enum ofputil_protocol protocol,
const struct tun_table *tun_table,
const struct vl_mff_map *vl_mff_map,
struct ofpbuf *ofpacts,
ofp_port_t max_port, uint8_t max_table)
struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
//pull后,跳过 struct ofp_vendor_header,b->data 指向消息体
//header中 raw 表示类型
enum ofpraw raw = ofpraw_pull_assert(&b);
if (raw == OFPRAW_OFPT11_FLOW_MOD) {
...
}
else {
if (raw == OFPRAW_OFPT10_FLOW_MOD) {
...
} else if (raw == OFPRAW_NXT_FLOW_MOD) {
/* Nicira extended flow_mod. */
const struct nx_flow_mod *nfm;
/* Dissect the message. */
//返回 nfm。b->data 向后偏移 sizeof *nfm 个字节,指向match field
nfm = ofpbuf_pull(&b, sizeof *nfm);
nx_pull_match(&b, ntohs(nfm->match_len), &fm->match, &fm->cookie, &fm->cookie_mask, false, tun_table, vl_mff_map);
nx_pull_match__(b, match_len, true, pipeline_fields_only, match, cookie, cookie_mask, tun_table, vl_mff_map);
uint8_t *p = NULL;
//返回p,指向 match field 开始位置,b->data 向后偏移 match_len
if (match_len) {
p = ofpbuf_try_pull(b, ROUND_UP(match_len, 8));
}
//遍历p指向的match field,设置到对应的 match 字段中
nx_pull_raw(p, match_len, strict, pipeline_fields_only, match, cookie, cookie_mask, tun_table, vl_mff_map);
struct ofpbuf b = ofpbuf_const_initializer(p, match_len);
while (b.size) {
const uint8_t *pos = b.data;
const struct mf_field *field;
union mf_value value;
union mf_value mask;
nx_pull_match_entry(&b, cookie != NULL, vl_mff_map, &field, &value, &mask);
//header 格式 oxm_class+oxm_field+hm+oxm_length+payload,其中hm全称为hasmaks,表示是否有掩码
//没有掩码时:oxm_class+oxm_field+0+oxm_length+payload
//有掩码时: oxm_class+oxm_field+1+oxm_length+payload+mask
//如果 oxm_class 为 oxffff,则说明是 experimenter,格式变成
//oxm_class+oxm_field+hm+oxm_length+experimenter ID+payload
uint64_t header;
nx_pull_entry__(b, allow_cookie, vl_mff_map, &header, field, value, mask);
const struct mf_field *field;
enum ofperr header_error;
unsigned int payload_len;
const uint8_t *payload;
int width;
nx_pull_header__(b, allow_cookie, vl_mff_map, header, &field);
//先判断是否是 experimenter_oxm,如果是,则取64位值
*header = ((uint64_t) ntohl(get_unaligned_be32(b->data))) << 32;
if (is_experimenter_oxm(*header)) {
*header = ntohll(get_unaligned_be64(b->data));
}
ofpbuf_pull(b, nxm_header_len(*header));
*field = mf_from_oxm_header(*header, vl_mff_map);
//遍历 nxm_header_map 找到 struct nxm_field
const struct nxm_field *f = nxm_field_by_header(header);
const struct mf_field *mff = mf_from_id(f->id);
//返回 mf_field
return &mf_fields[id];
const struct mf_field *vl_mff = mf_get_vl_mff(mff, vl_mff_map);
return vl_mff ? vl_mff : mff;
payload_len = nxm_payload_len(*header);
payload = ofpbuf_try_pull(b, payload_len);
width = nxm_field_bytes(*header);
//将 payload 拷贝到 value
copy_entry_value(field, value, payload, width);
if (mask) {
if (nxm_hasmask(*header)) {
//将 mask 拷贝到 mask
copy_entry_value(field, mask, payload + width, width);
} else {
//如果没有指定mask,则设置mask全f
memset(mask, 0xff, sizeof *mask);
}
}
if (field_) {
*field_ = field;
return header_error;
}
mf_set(field, &value, &mask, match, &err_str);
switch (mf->id) {
case MFF_CT_STATE:
match_set_ct_state_masked(match, ntohl(value->be32), ntohl(mask->be32));
match->flow.ct_state = ct_state & mask & UINT8_MAX;
match->wc.masks.ct_state = mask & UINT8_MAX;
break;
}
}
fm->priority = ntohs(nfm->priority);
fm->new_cookie = nfm->cookie;
fm->idle_timeout = ntohs(nfm->idle_timeout);
fm->hard_timeout = ntohs(nfm->hard_timeout);
fm->importance = 0;
fm->buffer_id = ntohl(nfm->buffer_id);
fm->out_port = u16_to_ofp(ntohs(nfm->out_port));
fm->out_group = OFPG_ANY;
raw_flags = nfm->flags;
}
}
//此时 b->data 指向了 instruction 字段,即action域
//解析的action存放在 ofpacts 中
ofpacts_pull_openflow_instructions(&b, b.size, oh->version, vl_mff_map, &fm->ofpacts_tlv_bitmap, ofpacts);
ofpbuf_clear(ofpacts);
if (version == OFP10_VERSION) {
return ofpacts_pull_openflow_actions__(openflow, instructions_len,
version,
(1u << N_OVS_INSTRUCTIONS) - 1,
ofpacts, 0, vl_mff_map,
ofpacts_tlv_bitmap);
const struct ofp_action_header *actions;
size_t orig_size = ofpacts->size;
actions = ofpbuf_try_pull(openflow, actions_len);
ofpacts_decode(actions, actions_len, version, vl_mff_map, ofpacts_tlv_bitmap, ofpacts);
struct ofpbuf openflow = ofpbuf_const_initializer(actions, actions_len);
while (openflow.size) {
const struct ofp_action_header *action = openflow.data;
enum ofp_raw_action_type raw;
uint64_t arg;
ofpact_pull_raw(&openflow, ofp_version, &raw, &arg);
//ofpact_decode 是自动生成的代码,位于文件 lib/ofp-actions.inc2
ofpact_decode(action, raw, ofp_version, arg, vl_mff_map, ofpacts_tlv_bitmap, ofpacts);
switch (raw) {
case NXAST_RAW_CT:
//decode_NXAST_RAW_CT 在 lib/ofp-actions.c 文件中
return decode_NXAST_RAW_CT(ALIGNED_CAST(const struct nx_action_conntrack *, a), version, vl_mff_map, tlv_bitmap, out);
const size_t ct_offset = ofpacts_pull(out);
//out 是保存action的 ofpacts
struct ofpact_conntrack *conntrack = ofpact_put_CT(out);
conntrack->flags = ntohs(nac->flags);
decode_ct_zone(nac, conntrack, vl_mff_map, tlv_bitmap);
conntrack->recirc_table = nac->recirc_table;
conntrack->alg = ntohs(nac->alg);
ofpbuf_pull(out, sizeof(*conntrack));
struct ofpbuf openflow = ofpbuf_const_initializer(nac + 1, ntohs(nac->len) - sizeof(*nac));
ofpacts_pull_openflow_actions__(&openflow, openflow.size, ofp_version,
1u << OVSINST_OFPIT11_APPLY_ACTIONS,
out, OFPACT_CT, vl_mff_map,
tlv_bitmap);
}
}
}
}
//保存 action
fm->ofpacts = ofpacts->data;
fm->ofpacts_len = ofpacts->size;
ofputil_decode_flow_mod_flags(raw_flags, fm->command, oh->version, &fm->flags);
handle_flow_mod__
将 struct ofputil_flow_mod fm 转换成 struct ofproto_flow_mod ofm
static enum ofperr
handle_flow_mod__(struct ofproto *ofproto, const struct ofputil_flow_mod *fm,
const struct openflow_mod_requester *req)
struct ofproto_flow_mod ofm;
ofproto_flow_mod_init(ofproto, &ofm, fm, NULL);
/* Forward flow mod fields we need later. */
ofm->command = fm->command;
ofm->modify_cookie = fm->modify_cookie;
switch (ofm->command) {
case OFPFC_ADD:
check_buffer_id = true;
//创建 rule
add_flow_init(ofproto, ofm, fm);
if (fm->table_id == 0xff) {
ofproto->ofproto_class->rule_choose_table(ofproto, &fm->match, &table_id);
} else if (fm->table_id < ofproto->n_tables) {
table_id = fm->table_id;
}
struct oftable *table;
table = &ofproto->tables[table_id];
if (!ofm->temp_rule) {
struct cls_rule cr;
cls_rule_init(&cr, &fm->match, fm->priority);
cls_rule_init__(rule, priority);
rculist_init(&rule->node);
*CONST_CAST(int *, &rule->priority) = priority;
ovsrcu_init(&rule->cls_match, NULL);
minimatch_init(CONST_CAST(struct minimatch *, &rule->match), match);
struct miniflow tmp;
miniflow_map_init(&tmp, &src->wc.masks);
/* Allocate two consecutive miniflows. */
miniflow_alloc(dst->flows, 2, &tmp);
miniflow_init(dst->flow, &src->flow);
minimask_init(dst->mask, &src->wc);
/* Allocate new rule. Destroys 'cr'. */
ofproto_rule_create(ofproto, &cr, table - ofproto->tables,
fm->new_cookie, fm->idle_timeout,
fm->hard_timeout, fm->flags,
fm->importance, fm->ofpacts,
fm->ofpacts_len,
fm->match.flow.tunnel.metadata.present.map,
fm->ofpacts_tlv_bitmap, &ofm->temp_rule);
struct rule *rule;
/* Allocate new rule. */
rule = ofproto->ofproto_class->rule_alloc();
struct rule_dpif *rule = xzalloc(sizeof *rule);
return &rule->up;
/* Initialize base state. */
*CONST_CAST(struct ofproto **, &rule->ofproto) = ofproto;
//匹配域
cls_rule_move(CONST_CAST(struct cls_rule *, &rule->cr), cr);
ovs_refcount_init(&rule->ref_count);
rule->created = rule->modified = time_msec();
rule->idle_timeout = idle_timeout;
rule->hard_timeout = hard_timeout;
*CONST_CAST(uint16_t *, &rule->importance) = importance;
rule->removed_reason = OVS_OFPRR_NONE;
*CONST_CAST(uint8_t *, &rule->table_id) = table_id;
rule->flags = flags & OFPUTIL_FF_STATE;
//action
*CONST_CAST(const struct rule_actions **, &rule->actions) = rule_actions_create(ofpacts, ofpacts_len);
ofproto->ofproto_class->rule_construct(rule);
struct rule_dpif *rule = rule_dpif_cast(rule_);
ovs_mutex_init_adaptive(&rule->stats_mutex);
rule->stats.n_packets = 0;
rule->stats.n_bytes = 0;
rule->stats.used = rule->up.modified;
rule->recirc_id = 0;
rule->new_rule = NULL;
rule->forward_counts = false;
rule->state = RULE_INITIALIZED;
//new_rule 为 ofm->temp_rule
*new_rule = rule;
}
break;
}
ovs_mutex_lock(&ofproto_mutex);
ofm.version = ofproto->tables_version + 1;
error = ofproto_flow_mod_start(ofproto, &ofm);
rule_collection_init(&ofm->old_rules);
rule_collection_init(&ofm->new_rules);
switch (ofm->command) {
case OFPFC_ADD:
add_flow_start(ofproto, ofm);
struct rule *new_rule = ofm->temp_rule;
const struct rule_actions *actions = rule_get_actions(new_rule);
struct oftable *table = &ofproto->tables[new_rule->table_id];
/* Check for the existence of an identical rule.
* This will not return rules earlier marked for removal. */
old_rule = rule_from_cls_rule(classifier_find_rule_exactly(&table->cls, &new_rule->cr, ofm->version));
replace_rule_start(ofproto, ofm, old_rule, new_rule);
//获取 table
struct oftable *table = &ofproto->tables[new_rule->table_id];
//流个数加1
table->n_flows++;
/* Insert flow to ofproto data structures, so that later flow_mods may
* relate to it. This is reversible, in case later errors require this to
* be reverted. */
ofproto_rule_insert__(ofproto, new_rule);
const struct rule_actions *actions = rule_get_actions(rule);
if (rule->hard_timeout || rule->idle_timeout) {
ovs_list_insert(&ofproto->expirable, &rule->expirable);
}
cookies_insert(ofproto, rule);
eviction_group_add_rule(rule);
if (actions->has_meter) {
meter_insert_rule(rule);
}
/* Make the new rule visible for classifier lookups only from the next
* version. */
//classifier_insert(struct classifier *cls, const struct cls_rule *rule, ovs_version_t version,
// const struct cls_conjunction conj[],size_t n_conj)
classifier_insert(&table->cls, &new_rule->cr, ofm->version, ofm->conjs, ofm->n_conjs);
classifier_replace(cls, rule, version, conj, n_conj);
struct cls_match *new;
struct cls_subtable *subtable;
struct cls_match *head;
new = cls_match_alloc(rule, version, conjs, n_conjs);
subtable = find_subtable(cls, rule->match.mask);
struct cls_subtable *subtable;
CMAP_FOR_EACH_WITH_HASH (subtable, cmap_node, minimask_hash(mask, 0),
&cls->subtables_map) {
if (minimask_equal(mask, &subtable->mask)) {
return subtable;
}
}
if (!subtable) {
subtable = insert_subtable(cls, rule->match.mask);
size_t count = miniflow_n_values(&mask->masks);
subtable = xzalloc(sizeof *subtable + MINIFLOW_VALUES_SIZE(count));
cmap_init(&subtable->rules);
miniflow_clone(CONST_CAST(struct miniflow *, &subtable->mask.masks), &mask->masks, count);
}
head = find_equal(subtable, rule->match.flow, hash);
if (!head) {
for (i = 0; i < cls->n_tries; i++) {
if (subtable->trie_plen[i]) {
trie_insert(&cls->tries[i], rule, subtable->trie_plen[i]);
}
}
/* Add rule to ports trie. */
if (subtable->ports_mask_len) {
/* We mask the value to be inserted to always have the wildcarded
* bits in known (zero) state, so we can include them in comparison
* and they will always match (== their original value does not
* matter). */
ovs_be32 masked_ports = minimatch_get_ports(&rule->match);
trie_insert_prefix(&subtable->ports_trie, &masked_ports,
subtable->ports_mask_len);
}
n_rules = cmap_insert(&subtable->rules, &new->cmap_node, hash);
}
/* Make rule visible to iterators (immediately). */
rculist_push_back(&subtable->rules_list, CONST_CAST(struct rculist *, &rule->node));
cls->n_rules++;
if (cls->publish) {
pvector_publish(&cls->subtables);
}
break;
}
总结
在ovs-ofctl端,首先解析命令行参数,将匹配字段和action保存到 struct ofputil_flow_mod,再将struct ofputil_flow_mod中的信息根据openflow protocol转换到不同的结构体中,比如对于OFPUTIL_P_OF10_NXM和OFPUTIL_P_OF10_NXM_TID协议类型,会转换成struct nx_flow_mod,更详细的可参考函数ofputil_encode_flow_mod。
在ovs-vswitchd端,接收openflow消息,将其解码保存到 struct ofputil_flow_mod,再转换成 struct ofproto_flow_mod。