添加一个flow,调用的命令为
ovs-ofctl add-flow hello "hard_timeout=0 idle_timeout=0 priority=1 table=21 pkt_mark=0x55 tun_id=0x55 actions=mod_nw_dst:192.168.56.101,output:2"
这里调用的是调用ovs/utilities/ovs-ofctl.c的命令行工具
这个命令行工具支持的所有的命令及处理函数定义如下:
staticconststruct ovs_cmdl_command all_commands[] = { { "show", "switch", 1, 1, ofctl_show }, { "monitor", "switch [misslen] [invalid_ttl] [watch:[...]]", 1, 3, ofctl_monitor }, { "snoop", "switch", 1, 1, ofctl_snoop }, { "dump-desc", "switch", 1, 1, ofctl_dump_desc }, { "dump-tables", "switch", 1, 1, ofctl_dump_tables }, { "dump-table-features", "switch", 1, 1, ofctl_dump_table_features }, { "dump-table-desc", "switch", 1, 1, ofctl_dump_table_desc }, { "dump-flows", "switch", 1, 2, ofctl_dump_flows }, { "dump-aggregate", "switch", 1, 2, ofctl_dump_aggregate }, { "queue-stats", "switch [port [queue]]", 1, 3, ofctl_queue_stats }, { "queue-get-config", "switch port", 2, 2, ofctl_queue_get_config }, { "add-flow", "switch flow", 2, 2, ofctl_add_flow }, { "add-flows", "switch file", 2, 2, ofctl_add_flows }, { "mod-flows", "switch flow", 2, 2, ofctl_mod_flows }, { "del-flows", "switch [flow]", 1, 2, ofctl_del_flows }, { "replace-flows", "switch file", 2, 2, ofctl_replace_flows }, { "diff-flows", "source1 source2", 2, 2, ofctl_diff_flows }, { "add-meter", "switch meter", 2, 2, ofctl_add_meter }, { "mod-meter", "switch meter", 2, 2, ofctl_mod_meter }, { "del-meter", "switch meter", 2, 2, ofctl_del_meters }, { "del-meters", "switch", 1, 1, ofctl_del_meters }, { "dump-meter", "switch meter", 2, 2, ofctl_dump_meters }, { "dump-meters", "switch", 1, 1, ofctl_dump_meters }, { "meter-stats", "switch [meter]", 1, 2, ofctl_meter_stats }, { "meter-features", "switch", 1, 1, ofctl_meter_features }, { "packet-out", "switch in_port actions packet...", 4, INT_MAX, ofctl_packet_out }, { "dump-ports", "switch [port]", 1, 2, ofctl_dump_ports }, { "dump-ports-desc", "switch [port]", 1, 2, ofctl_dump_ports_desc }, { "mod-port", "switch iface act", 3, 3, ofctl_mod_port }, { "mod-table", "switch mod", 3, 3, ofctl_mod_table }, { "get-frags", "switch", 1, 1, ofctl_get_frags }, { "set-frags", "switch frag_mode", 2, 2, ofctl_set_frags }, { "probe", "target", 1, 1, ofctl_probe }, { "ping", "target [n]", 1, 2, ofctl_ping }, { "benchmark", "target n count", 3, 3, ofctl_benchmark }, { "ofp-parse", "file", 1, 1, ofctl_ofp_parse }, { "ofp-parse-pcap", "pcap", 1, INT_MAX, ofctl_ofp_parse_pcap }, { "add-group", "switch group", 1, 2, ofctl_add_group }, { "add-groups", "switch file", 1, 2, ofctl_add_groups }, { "mod-group", "switch group", 1, 2, ofctl_mod_group }, { "del-groups", "switch [group]", 1, 2, ofctl_del_groups }, { "insert-buckets", "switch [group]", 1, 2, ofctl_insert_bucket }, { "remove-buckets", "switch [group]", 1, 2, ofctl_remove_bucket }, { "dump-groups", "switch [group]", 1, 2, ofctl_dump_group_desc }, { "dump-group-stats", "switch [group]", 1, 2, ofctl_dump_group_stats }, { "dump-group-features", "switch", 1, 1, ofctl_dump_group_features }, { "add-tlv-map", "switch map", 2, 2, ofctl_add_tlv_map }, { "del-tlv-map", "switch [map]", 1, 2, ofctl_del_tlv_map }, { "dump-tlv-map", "switch", 1, 1, ofctl_dump_tlv_map }, { "help", NULL, 0, INT_MAX, ofctl_help }, { "list-commands", NULL, 0, INT_MAX, ofctl_list_commands }, /* Undocumented commands for testing. */ { "parse-flow", NULL, 1, 1, ofctl_parse_flow }, { "parse-flows", NULL, 1, 1, ofctl_parse_flows }, { "parse-nx-match", NULL, 0, 0, ofctl_parse_nxm }, { "parse-nxm", NULL, 0, 0, ofctl_parse_nxm }, { "parse-oxm", NULL, 1, 1, ofctl_parse_oxm }, { "parse-actions", NULL, 1, 1, ofctl_parse_actions }, { "parse-instructions", NULL, 1, 1, ofctl_parse_instructions }, { "parse-ofp10-match", NULL, 0, 0, ofctl_parse_ofp10_match }, { "parse-ofp11-match", NULL, 0, 0, ofctl_parse_ofp11_match }, { "parse-pcap", NULL, 1, 1, ofctl_parse_pcap }, { "check-vlan", NULL, 2, 2, ofctl_check_vlan }, { "print-error", NULL, 1, 1, ofctl_print_error }, { "encode-error-reply", NULL, 2, 2, ofctl_encode_error_reply }, { "ofp-print", NULL, 1, 2, ofctl_ofp_print }, { "encode-hello", NULL, 1, 1, ofctl_encode_hello }, { NULL, NULL, 0, 0, NULL }, };
根据这个数据结构的定义,"add-flow"调用的函数为
staticvoid ofctl_add_flow(struct ovs_cmdl_context *ctx) { ofctl_flow_mod(ctx->argc, ctx->argv, OFPFC_ADD); }
调用ofctl_flow_mod,parse_ofp_flow_mod_str将字符串解析为ofputil_flow_mod fm
ofputil_flow_mod包含两个最重要的成员变量:
struct match match,所谓match就是一个key。
struct ofpact *ofpacts; /* Series of "struct ofpact"s. */
staticvoid 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; error = parse_ofp_flow_mod_str(&fm, argc > 2 ? argv[2] : "", command, &usable_protocols); if (error) { ovs_fatal(0, "%s", error); } ofctl_flow_mod__(argv[1], &fm, 1, usable_protocols); } }
ofctl_flow_mod__会打开一个指向ovs-vswitchd的socket,将flow match变成openflow的协议,发出去transact_noreply
staticvoid ofctl_flow_mod__(constchar *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]; transact_noreply(vconn, ofputil_encode_flow_mod(fm, protocol)); free(CONST_CAST(struct ofpact *, fm->ofpacts)); } vconn_close(vconn); }
Ovs-vswitchd会监听socket,在ovs-vswitchd.c中bridge_run每个bridge会监听消息,ofproto_run监听openflow的调用,connmgr_run网络连接管理,ofconn_run管理socket连接。
connmgr_run(p->connmgr, handle_openflow);会设置当有openflow调用的时候,handle_openflow会被调用。
staticvoid handle_openflow(struct ofconn *ofconn, conststruct ofpbuf *ofp_msg) OVS_EXCLUDED(ofproto_mutex) { enum ofperr error = handle_openflow__(ofconn, ofp_msg); if (error) { ofconn_send_error(ofconn, ofp_msg->data, error); } COVERAGE_INC(ofproto_recv_openflow); }
handle_openflow__会做如下的调用:
case OFPTYPE_FLOW_MOD: return handle_flow_mod(ofconn, oh);
handle_flow_mod首先将openflow协议解析为fm和ofpacts
error = ofputil_decode_flow_mod(&ofm.fm, oh, ofconn_get_protocol(ofconn), &ofpacts, u16_to_ofp(ofproto->max_ports), ofproto->n_tables);
然后调用static enum ofperr handle_flow_mod__(struct ofproto *ofproto, struct ofproto_flow_mod *ofm, const struct flow_mod_requester *req)
会调用static enum ofperr ofproto_flow_mod_start(struct ofproto *ofproto, struct ofproto_flow_mod *ofm) OVS_REQUIRES(ofproto_mutex)
staticenum ofperr ofproto_flow_mod_start(struct ofproto *ofproto, struct ofproto_flow_mod *ofm) OVS_REQUIRES(ofproto_mutex) { switch (ofm->fm.command) { case OFPFC_ADD: return add_flow_start(ofproto, ofm); /* , &be->old_rules.stub[0], &be->new_rules.stub[0]); */ case OFPFC_MODIFY: return modify_flows_start_loose(ofproto, ofm); case OFPFC_MODIFY_STRICT: return modify_flow_start_strict(ofproto, ofm); case OFPFC_DELETE: return delete_flows_start_loose(ofproto, ofm); case OFPFC_DELETE_STRICT: return delete_flow_start_strict(ofproto, ofm); } return OFPERR_OFPFMFC_BAD_COMMAND; }
在函数add_flow_start中,首先cls_rule_init(&cr, &fm->match, fm->priority); 将match也即key变成一个cls_rule,cls_rule是一个压缩版本的match,match是一个整个数据结构保存整个package,从L1一直到L4全都有,比较大,如果保存在内存太浪费,cls_rule中有一个minimatch,是用压缩的方式保存match,也即如果match中为0的部分不保存,采取稀疏矩阵的方式。
接下来创建一个新的rule,error = replace_rule_create(ofproto, fm, &cr, table - ofproto->tables, rule, new_rule);
最后replace_rule_start(ofproto, ofm->version, rule, *new_rule, conjs, n_conjs); 将rule替换现在的rule,有则替换,没有则插入。
staticvoid replace_rule_start(struct ofproto *ofproto, cls_version_t version, struct rule *old_rule, struct rule *new_rule, struct cls_conjunction *conjs, size_t n_conjs) { struct oftable *table = &ofproto->tables[new_rule->table_id]; /* 'old_rule' may be either an evicted rule or replaced rule. */ if (old_rule) { /* Mark the old rule for removal in the next version. */ cls_rule_make_invisible_in_version(&old_rule->cr, version); } else { table->n_flows++; } /* Insert flow to the classifier, 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); /* Make the new rule visible for classifier lookups only from the next * version. */ classifier_insert(&table->cls, &new_rule->cr, version, conjs, n_conjs); }