X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=ofproto%2Fofproto.c;h=4c370d3b795361456a3c5e61a3acc23386b9b51c;hb=f740239011a9650ab4d63a336a4170e8f1983a63;hp=1c6b8a90f0eab91e075e28a8415be377aa4a2ff8;hpb=806cd5c7cc1adbb4696b96d1429650f2ade5c848;p=openvswitch diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c index 1c6b8a90..4c370d3b 100644 --- a/ofproto/ofproto.c +++ b/ofproto/ofproto.c @@ -64,7 +64,8 @@ COVERAGE_DEFINE(ofproto_update_port); static void ofport_destroy__(struct ofport *); static void ofport_destroy(struct ofport *); -static int rule_create(struct ofproto *, const struct cls_rule *, +static int rule_create(struct ofproto *, + const struct cls_rule *, uint8_t table_id, const union ofp_action *, size_t n_actions, uint16_t idle_timeout, uint16_t hard_timeout, ovs_be64 flow_cookie, bool send_flow_removed, @@ -78,7 +79,6 @@ static void ofproto_flush_flows__(struct ofproto *); static void ofproto_rule_destroy__(struct rule *); static void ofproto_rule_send_removed(struct rule *, uint8_t reason); -static void ofproto_rule_remove(struct rule *); static void handle_openflow(struct ofconn *, struct ofpbuf *); @@ -262,7 +262,8 @@ ofproto_create(const char *datapath_name, const char *datapath_type, ofproto->netdev_monitor = netdev_monitor_create(); hmap_init(&ofproto->ports); shash_init(&ofproto->port_by_name); - classifier_init(&ofproto->cls); + ofproto->tables = NULL; + ofproto->n_tables = 0; ofproto->connmgr = connmgr_create(ofproto, datapath_name, datapath_name); error = ofproto->ofproto_class->construct(ofproto); @@ -272,6 +273,7 @@ ofproto_create(const char *datapath_name, const char *datapath_type, ofproto_destroy__(ofproto); return error; } + assert(ofproto->n_tables > 0); ofproto->datapath_id = pick_datapath_id(ofproto); VLOG_INFO("using datapath ID %016"PRIx64, ofproto->datapath_id); @@ -431,20 +433,19 @@ ofproto_port_clear_cfm(struct ofproto *ofproto, uint16_t ofp_port) { struct ofport *ofport = ofproto_get_port(ofproto, ofp_port); if (ofport && ofproto->ofproto_class->set_cfm) { - ofproto->ofproto_class->set_cfm(ofport, NULL, NULL, 0); + ofproto->ofproto_class->set_cfm(ofport, NULL); } } /* Configures connectivity fault management on 'ofp_port' in 'ofproto'. Takes - * basic configuration from the configuration members in 'cfm', and the set of - * remote maintenance points from the 'n_remote_mps' elements in 'remote_mps'. - * Ignores the statistics members of 'cfm'. + * basic configuration from the configuration members in 'cfm', and the remote + * maintenance point ID from remote_mpid. Ignores the statistics members of + * 'cfm'. * * This function has no effect if 'ofproto' does not have a port 'ofp_port'. */ void ofproto_port_set_cfm(struct ofproto *ofproto, uint16_t ofp_port, - const struct cfm *cfm, - const uint16_t *remote_mps, size_t n_remote_mps) + const struct cfm_settings *s) { struct ofport *ofport; int error; @@ -456,9 +457,11 @@ ofproto_port_set_cfm(struct ofproto *ofproto, uint16_t ofp_port, return; } + /* XXX: For configuration simplicity, we only support one remote_mpid + * outside of the CFM module. It's not clear if this is the correct long + * term solution or not. */ error = (ofproto->ofproto_class->set_cfm - ? ofproto->ofproto_class->set_cfm(ofport, cfm, - remote_mps, n_remote_mps) + ? ofproto->ofproto_class->set_cfm(ofport, s) : EOPNOTSUPP); if (error) { VLOG_WARN("%s: CFM configuration on port %"PRIu16" (%s) failed (%s)", @@ -467,22 +470,6 @@ ofproto_port_set_cfm(struct ofproto *ofproto, uint16_t ofp_port, } } -/* Returns the connectivity fault management object associated with 'ofp_port' - * within 'ofproto', or a null pointer if 'ofproto' does not have a port - * 'ofp_port' or if that port does not have CFM configured. The caller must - * not modify or destroy the returned object. */ -const struct cfm * -ofproto_port_get_cfm(struct ofproto *ofproto, uint16_t ofp_port) -{ - struct ofport *ofport; - const struct cfm *cfm; - - ofport = ofproto_get_port(ofproto, ofp_port); - return (ofport - && ofproto->ofproto_class->get_cfm - && !ofproto->ofproto_class->get_cfm(ofport, &cfm)) ? cfm : NULL; -} - /* Checks the status of LACP negotiation for 'ofp_port' within ofproto. * Returns 1 if LACP partner information for 'ofp_port' is up-to-date, * 0 if LACP partner information is not current (generally indicating a @@ -589,6 +576,8 @@ ofproto_get_snoops(const struct ofproto *ofproto, struct sset *snoops) static void ofproto_destroy__(struct ofproto *ofproto) { + size_t i; + connmgr_destroy(ofproto->connmgr); hmap_remove(&all_ofprotos, &ofproto->hmap_node); @@ -601,7 +590,11 @@ ofproto_destroy__(struct ofproto *ofproto) netdev_monitor_destroy(ofproto->netdev_monitor); hmap_destroy(&ofproto->ports); shash_destroy(&ofproto->port_by_name); - classifier_destroy(&ofproto->cls); + + for (i = 0; i < ofproto->n_tables; i++) { + classifier_destroy(&ofproto->tables[i]); + } + free(ofproto->tables); ofproto->ofproto_class->dealloc(ofproto); } @@ -617,7 +610,6 @@ ofproto_destroy(struct ofproto *p) ofproto_flush_flows__(p); HMAP_FOR_EACH_SAFE (ofport, next_ofport, hmap_node, &p->ports) { - hmap_remove(&p->ports, &ofport->hmap_node); ofport_destroy(ofport); } @@ -864,7 +856,7 @@ ofproto_port_del(struct ofproto *ofproto, uint16_t ofp_port) return error; } -/* Adds a flow to the OpenFlow flow table in 'p' that matches 'cls_rule' and +/* Adds a flow to OpenFlow flow table 0 in 'p' that matches 'cls_rule' and * performs the 'n_actions' actions in 'actions'. The new flow will not * timeout. * @@ -872,32 +864,35 @@ ofproto_port_del(struct ofproto *ofproto, uint16_t ofp_port) * (0...65535, inclusive) then the flow will be visible to OpenFlow * controllers; otherwise, it will be hidden. * - * The caller retains ownership of 'cls_rule' and 'actions'. */ + * The caller retains ownership of 'cls_rule' and 'actions'. + * + * This is a helper function for in-band control and fail-open. */ void ofproto_add_flow(struct ofproto *p, const struct cls_rule *cls_rule, const union ofp_action *actions, size_t n_actions) { struct rule *rule; - rule_create(p, cls_rule, actions, n_actions, 0, 0, 0, false, &rule); + rule_create(p, cls_rule, 0, actions, n_actions, 0, 0, 0, false, &rule); } +/* Searches for a rule with matching criteria exactly equal to 'target' in + * ofproto's table 0 and, if it finds one, deletes it. + * + * This is a helper function for in-band control and fail-open. */ void ofproto_delete_flow(struct ofproto *ofproto, const struct cls_rule *target) { struct rule *rule; - rule = rule_from_cls_rule(classifier_find_rule_exactly(&ofproto->cls, - target)); - if (rule) { - ofproto_rule_remove(rule); - } + rule = rule_from_cls_rule(classifier_find_rule_exactly( + &ofproto->tables[0], target)); + ofproto_rule_destroy(rule); } static void ofproto_flush_flows__(struct ofproto *ofproto) { - struct rule *rule, *next_rule; - struct cls_cursor cursor; + size_t i; COVERAGE_INC(ofproto_flush); @@ -905,12 +900,19 @@ ofproto_flush_flows__(struct ofproto *ofproto) ofproto->ofproto_class->flush(ofproto); } - cls_cursor_init(&cursor, &ofproto->cls, NULL); - CLS_CURSOR_FOR_EACH_SAFE (rule, next_rule, cr, &cursor) { - ofproto_rule_remove(rule); + for (i = 0; i < ofproto->n_tables; i++) { + struct rule *rule, *next_rule; + struct cls_cursor cursor; + + cls_cursor_init(&cursor, &ofproto->tables[i], NULL); + CLS_CURSOR_FOR_EACH_SAFE (rule, next_rule, cr, &cursor) { + ofproto_rule_destroy(rule); + } } } +/* Deletes all of the flows from all of ofproto's flow tables, then + * reintroduces rules required by in-band control and fail open. */ void ofproto_flush_flows(struct ofproto *ofproto) { @@ -1090,7 +1092,7 @@ ofproto_port_unregister(struct ofproto *ofproto, uint16_t ofp_port) struct ofport *port = ofproto_get_port(ofproto, ofp_port); if (port) { if (port->ofproto->ofproto_class->set_cfm) { - port->ofproto->ofproto_class->set_cfm(port, NULL, NULL, 0); + port->ofproto->ofproto_class->set_cfm(port, NULL); } if (port->ofproto->ofproto_class->bundle_remove) { port->ofproto->ofproto_class->bundle_remove(port); @@ -1153,21 +1155,25 @@ update_port(struct ofproto *ofproto, const char *name) if (netdev) { port = ofproto_get_port(ofproto, ofproto_port.ofp_port); if (port && !strcmp(netdev_get_name(port->netdev), name)) { + struct netdev *old_netdev = port->netdev; + /* 'name' hasn't changed location. Any properties changed? */ if (!ofport_equal(&port->opp, &opp)) { ofport_modified(port, &opp); } - /* Install the newly opened netdev in case it has changed. */ + /* Install the newly opened netdev in case it has changed. + * Don't close the old netdev yet in case port_modified has to + * remove a retained reference to it.*/ netdev_monitor_remove(ofproto->netdev_monitor, port->netdev); netdev_monitor_add(ofproto->netdev_monitor, netdev); - - netdev_close(port->netdev); port->netdev = netdev; if (port->ofproto->ofproto_class->port_modified) { port->ofproto->ofproto_class->port_modified(port); } + + netdev_close(old_netdev); } else { /* If 'port' is nonnull then its name differs from 'name' and thus * we should delete it. If we think there's a port named 'name' @@ -1217,7 +1223,8 @@ init_ports(struct ofproto *p) * flow table, and stores the new rule into '*rulep'. Returns 0 on success, * otherwise a positive errno value or OpenFlow error code. */ static int -rule_create(struct ofproto *ofproto, const struct cls_rule *cls_rule, +rule_create(struct ofproto *ofproto, + const struct cls_rule *cls_rule, uint8_t table_id, const union ofp_action *actions, size_t n_actions, uint16_t idle_timeout, uint16_t hard_timeout, ovs_be64 flow_cookie, bool send_flow_removed, @@ -1226,6 +1233,20 @@ rule_create(struct ofproto *ofproto, const struct cls_rule *cls_rule, struct rule *rule; int error; + if (table_id == 0xff) { + if (ofproto->n_tables > 1) { + error = ofproto->ofproto_class->rule_choose_table(ofproto, + cls_rule, + &table_id); + if (error) { + return error; + } + assert(table_id < ofproto->n_tables); + } else { + table_id = 0; + } + } + rule = ofproto->ofproto_class->rule_alloc(); if (!rule) { error = ENOMEM; @@ -1234,6 +1255,7 @@ rule_create(struct ofproto *ofproto, const struct cls_rule *cls_rule, rule->ofproto = ofproto; rule->cr = *cls_rule; + rule->table_id = table_id; rule->flow_cookie = flow_cookie; rule->created = time_msec(); rule->idle_timeout = idle_timeout; @@ -1269,14 +1291,14 @@ ofproto_rule_destroy__(struct rule *rule) rule->ofproto->ofproto_class->rule_dealloc(rule); } -/* Destroys 'rule' and removes it from the datapath. - * - * The caller must have already removed 'rule' from the classifier. */ +/* Destroys 'rule' and removes it from the flow table and the datapath. */ void ofproto_rule_destroy(struct rule *rule) { - rule->ofproto->ofproto_class->rule_destruct(rule); - ofproto_rule_destroy__(rule); + if (rule) { + rule->ofproto->ofproto_class->rule_destruct(rule); + ofproto_rule_destroy__(rule); + } } /* Returns true if 'rule' has an OpenFlow OFPAT_OUTPUT or OFPAT_ENQUEUE action @@ -1300,12 +1322,6 @@ rule_has_out_port(const struct rule *rule, ovs_be16 out_port) return false; } -struct rule * -ofproto_rule_lookup(struct ofproto *ofproto, const struct flow *flow) -{ - return rule_from_cls_rule(classifier_lookup(&ofproto->cls, flow)); -} - /* Executes the actions indicated by 'rule' on 'packet' and credits 'rule''s * statistics appropriately. 'packet' must have at least sizeof(struct * ofp_packet_in) bytes of headroom. @@ -1325,15 +1341,6 @@ rule_execute(struct rule *rule, uint16_t in_port, struct ofpbuf *packet) return rule->ofproto->ofproto_class->rule_execute(rule, &flow, packet); } -/* Removes 'rule' from 'ofproto' and frees up the associated memory. Removes - * 'rule' from the classifier. */ -void -ofproto_rule_remove(struct rule *rule) -{ - rule->ofproto->ofproto_class->rule_remove(rule); - ofproto_rule_destroy(rule); -} - /* Returns true if 'rule' should be hidden from the controller. * * Rules with priority higher than UINT16_MAX are set up by ofproto itself @@ -1370,25 +1377,22 @@ handle_features_request(struct ofconn *ofconn, const struct ofp_header *oh) struct ofp_switch_features *osf; struct ofpbuf *buf; struct ofport *port; + bool arp_match_ip; + uint32_t actions; + + ofproto->ofproto_class->get_features(ofproto, &arp_match_ip, &actions); + assert(actions & (1 << OFPAT_OUTPUT)); /* sanity check */ osf = make_openflow_xid(sizeof *osf, OFPT_FEATURES_REPLY, oh->xid, &buf); osf->datapath_id = htonll(ofproto->datapath_id); osf->n_buffers = htonl(pktbuf_capacity()); - osf->n_tables = 2; + osf->n_tables = ofproto->n_tables; osf->capabilities = htonl(OFPC_FLOW_STATS | OFPC_TABLE_STATS | - OFPC_PORT_STATS | OFPC_ARP_MATCH_IP); - osf->actions = htonl((1u << OFPAT_OUTPUT) | - (1u << OFPAT_SET_VLAN_VID) | - (1u << OFPAT_SET_VLAN_PCP) | - (1u << OFPAT_STRIP_VLAN) | - (1u << OFPAT_SET_DL_SRC) | - (1u << OFPAT_SET_DL_DST) | - (1u << OFPAT_SET_NW_SRC) | - (1u << OFPAT_SET_NW_DST) | - (1u << OFPAT_SET_NW_TOS) | - (1u << OFPAT_SET_TP_SRC) | - (1u << OFPAT_SET_TP_DST) | - (1u << OFPAT_ENQUEUE)); + OFPC_PORT_STATS); + if (arp_match_ip) { + osf->capabilities |= htonl(OFPC_ARP_MATCH_IP); + } + osf->actions = htonl(actions); HMAP_FOR_EACH (port, hmap_node, &ofproto->ports) { ofpbuf_put(buf, &port->opp, sizeof port->opp); @@ -1453,7 +1457,7 @@ handle_set_config(struct ofconn *ofconn, const struct ofp_switch_config *osc) * * The log message mentions 'msg_type'. */ static int -reject_slave_controller(struct ofconn *ofconn, const const char *msg_type) +reject_slave_controller(struct ofconn *ofconn, const char *msg_type) { if (ofconn_get_type(ofconn) == OFCONN_PRIMARY && ofconn_get_role(ofconn) == NX_ROLE_SLAVE) { @@ -1669,19 +1673,20 @@ handle_table_stats_request(struct ofconn *ofconn, struct ofproto *p = ofconn_get_ofproto(ofconn); struct ofp_table_stats *ots; struct ofpbuf *msg; + size_t i; + + msg = start_ofp_stats_reply(request, sizeof *ots * p->n_tables); - msg = start_ofp_stats_reply(request, sizeof *ots * 2); + ots = ofpbuf_put_zeros(msg, sizeof *ots * p->n_tables); + for (i = 0; i < p->n_tables; i++) { + ots[i].table_id = i; + sprintf(ots[i].name, "table%zu", i); + ots[i].wildcards = htonl(OFPFW_ALL); + ots[i].max_entries = htonl(1000000); /* An arbitrary big number. */ + ots[i].active_count = htonl(classifier_count(&p->tables[i])); + } - /* Classifier table. */ - ots = append_ofp_stats_reply(sizeof *ots, ofconn, &msg); - memset(ots, 0, sizeof *ots); - strcpy(ots->name, "classifier"); - ots->wildcards = (ofconn_get_flow_format(ofconn) == NXFF_OPENFLOW10 - ? htonl(OFPFW_ALL) : htonl(OVSFW_ALL)); - ots->max_entries = htonl(1024 * 1024); /* An arbitrary big number. */ - ots->active_count = htonl(classifier_count(&p->cls)); - put_32aligned_be64(&ots->lookup_count, htonll(0)); /* XXX */ - put_32aligned_be64(&ots->matched_count, htonll(0)); /* XXX */ + p->ofproto_class->get_tables(p, ots); ofconn_send_reply(ofconn, msg); return 0; @@ -1766,7 +1771,6 @@ put_ofp_flow_stats(struct ofconn *ofconn, struct rule *rule, struct ofproto *ofproto = ofconn_get_ofproto(ofconn); struct ofp_flow_stats *ofs; uint64_t packet_count, byte_count; - ovs_be64 cookie; size_t act_len, len; if (rule_is_hidden(rule) || !rule_has_out_port(rule, out_port)) { @@ -1780,11 +1784,10 @@ put_ofp_flow_stats(struct ofconn *ofconn, struct rule *rule, ofs = append_ofp_stats_reply(len, ofconn, replyp); ofs->length = htons(len); - ofs->table_id = 0; + ofs->table_id = rule->table_id; ofs->pad = 0; - ofputil_cls_rule_to_match(&rule->cr, ofconn_get_flow_format(ofconn), - &ofs->match, rule->flow_cookie, &cookie); - put_32aligned_be64(&ofs->cookie, cookie); + ofputil_cls_rule_to_match(&rule->cr, &ofs->match); + put_32aligned_be64(&ofs->cookie, rule->flow_cookie); calc_flow_duration(rule->created, &ofs->duration_sec, &ofs->duration_nsec); ofs->priority = htons(rule->cr.priority); ofs->idle_timeout = htons(rule->idle_timeout); @@ -1797,38 +1800,68 @@ put_ofp_flow_stats(struct ofconn *ofconn, struct rule *rule, } } -static bool -is_valid_table(uint8_t table_id) +static struct classifier * +first_matching_table(struct ofproto *ofproto, uint8_t table_id) { - if (table_id == 0 || table_id == 0xff) { - return true; + if (table_id == 0xff) { + return &ofproto->tables[0]; + } else if (table_id < ofproto->n_tables) { + return &ofproto->tables[table_id]; } else { /* It would probably be better to reply with an error but there doesn't * seem to be any appropriate value, so that might just be * confusing. */ VLOG_WARN_RL(&rl, "controller asked for invalid table %"PRIu8, table_id); - return false; + return NULL; } } +static struct classifier * +next_matching_table(struct ofproto *ofproto, + struct classifier *cls, uint8_t table_id) +{ + return (table_id == 0xff && cls != &ofproto->tables[ofproto->n_tables - 1] + ? cls + 1 + : NULL); +} + +/* Assigns CLS to each classifier table, in turn, that matches TABLE_ID in + * OFPROTO: + * + * - If TABLE_ID is 0xff, this iterates over every classifier table in + * OFPROTO. + * + * - If TABLE_ID is the number of a table in OFPROTO, then the loop iterates + * only once, for that table. + * + * - Otherwise, TABLE_ID isn't valid for OFPROTO, so ofproto logs a warning + * and does not enter the loop at all. + * + * All parameters are evaluated multiple times. + */ +#define FOR_EACH_MATCHING_TABLE(CLS, TABLE_ID, OFPROTO) \ + for ((CLS) = first_matching_table(OFPROTO, TABLE_ID); \ + (CLS) != NULL; \ + (CLS) = next_matching_table(OFPROTO, CLS, TABLE_ID)) + static int handle_flow_stats_request(struct ofconn *ofconn, const struct ofp_header *oh) { const struct ofp_flow_stats_request *fsr = ofputil_stats_body(oh); struct ofproto *ofproto = ofconn_get_ofproto(ofconn); + struct classifier *cls; + struct cls_rule target; struct ofpbuf *reply; COVERAGE_INC(ofproto_flows_req); reply = start_ofp_stats_reply(oh, 1024); - if (is_valid_table(fsr->table_id)) { + ofputil_cls_rule_from_match(&fsr->match, 0, &target); + FOR_EACH_MATCHING_TABLE (cls, fsr->table_id, ofproto) { struct cls_cursor cursor; - struct cls_rule target; struct rule *rule; - ofputil_cls_rule_from_match(&fsr->match, 0, NXFF_OPENFLOW10, 0, - &target); - cls_cursor_init(&cursor, &ofproto->cls, &target); + cls_cursor_init(&cursor, cls, &target); CLS_CURSOR_FOR_EACH (rule, cr, &cursor) { put_ofp_flow_stats(ofconn, rule, fsr->out_port, &reply); } @@ -1883,6 +1916,7 @@ handle_nxst_flow(struct ofconn *ofconn, const struct ofp_header *oh) { struct ofproto *ofproto = ofconn_get_ofproto(ofconn); struct nx_flow_stats_request *nfsr; + struct classifier *cls; struct cls_rule target; struct ofpbuf *reply; struct ofpbuf b; @@ -1902,11 +1936,11 @@ handle_nxst_flow(struct ofconn *ofconn, const struct ofp_header *oh) COVERAGE_INC(ofproto_flows_req); reply = start_nxstats_reply(&nfsr->nsm, 1024); - if (is_valid_table(nfsr->table_id)) { + FOR_EACH_MATCHING_TABLE (cls, nfsr->table_id, ofproto) { struct cls_cursor cursor; struct rule *rule; - cls_cursor_init(&cursor, &ofproto->cls, &target); + cls_cursor_init(&cursor, cls, &target); CLS_CURSOR_FOR_EACH (rule, cr, &cursor) { put_nx_flow_stats(ofconn, rule, nfsr->out_port, &reply); } @@ -1925,6 +1959,9 @@ flow_stats_ds(struct rule *rule, struct ds *results) rule->ofproto->ofproto_class->rule_get_stats(rule, &packet_count, &byte_count); + if (rule->table_id != 0) { + ds_put_format(results, "table_id=%"PRIu8", ", rule->table_id); + } ds_put_format(results, "duration=%llds, ", (time_msec() - rule->created) / 1000); ds_put_format(results, "priority=%u, ", rule->cr.priority); @@ -1945,12 +1982,16 @@ flow_stats_ds(struct rule *rule, struct ds *results) void ofproto_get_all_flows(struct ofproto *p, struct ds *results) { - struct cls_cursor cursor; - struct rule *rule; + struct classifier *cls; + + for (cls = &p->tables[0]; cls < &p->tables[p->n_tables]; cls++) { + struct cls_cursor cursor; + struct rule *rule; - cls_cursor_init(&cursor, &p->cls, NULL); - CLS_CURSOR_FOR_EACH (rule, cr, &cursor) { - flow_stats_ds(rule, results); + cls_cursor_init(&cursor, cls, NULL); + CLS_CURSOR_FOR_EACH (rule, cr, &cursor) { + flow_stats_ds(rule, results); + } } } @@ -1963,6 +2004,18 @@ ofproto_get_netflow_ids(const struct ofproto *ofproto, ofproto->ofproto_class->get_netflow_ids(ofproto, engine_type, engine_id); } +/* Checks the fault status of CFM for 'ofp_port' within 'ofproto'. Returns 1 + * if CFM is faulted (generally indiciating a connectivity problem), 0 if CFM + * is not faulted, and -1 if CFM is not enabled on 'ofp_port'. */ +int +ofproto_port_get_cfm_fault(const struct ofproto *ofproto, uint16_t ofp_port) +{ + struct ofport *ofport = ofproto_get_port(ofproto, ofp_port); + return (ofport && ofproto->ofproto_class->get_cfm_fault + ? ofproto->ofproto_class->get_cfm_fault(ofport) + : -1); +} + static void query_aggregate_stats(struct ofproto *ofproto, struct cls_rule *target, ovs_be16 out_port, uint8_t table_id, @@ -1970,15 +2023,16 @@ query_aggregate_stats(struct ofproto *ofproto, struct cls_rule *target, { uint64_t total_packets = 0; uint64_t total_bytes = 0; + struct classifier *cls; int n_flows = 0; COVERAGE_INC(ofproto_agg_request); - if (is_valid_table(table_id)) { + FOR_EACH_MATCHING_TABLE (cls, table_id, ofproto) { struct cls_cursor cursor; struct rule *rule; - cls_cursor_init(&cursor, &ofproto->cls, target); + cls_cursor_init(&cursor, cls, target); CLS_CURSOR_FOR_EACH (rule, cr, &cursor) { if (!rule_is_hidden(rule) && rule_has_out_port(rule, out_port)) { uint64_t packet_count; @@ -2010,8 +2064,7 @@ handle_aggregate_stats_request(struct ofconn *ofconn, struct cls_rule target; struct ofpbuf *msg; - ofputil_cls_rule_from_match(&request->match, 0, NXFF_OPENFLOW10, 0, - &target); + ofputil_cls_rule_from_match(&request->match, 0, &target); msg = start_ofp_stats_reply(oh, sizeof *reply); reply = append_ofp_stats_reply(sizeof *reply, ofconn, &msg); @@ -2162,13 +2215,18 @@ add_flow(struct ofconn *ofconn, struct flow_mod *fm) int buf_err; int error; - if (fm->flags & OFPFF_CHECK_OVERLAP - && classifier_rule_overlaps(&p->cls, &fm->cr)) { - return ofp_mkerr(OFPET_FLOW_MOD_FAILED, OFPFMFC_OVERLAP); + if (fm->flags & OFPFF_CHECK_OVERLAP) { + struct classifier *cls; + + FOR_EACH_MATCHING_TABLE (cls, fm->table_id, p) { + if (classifier_rule_overlaps(cls, &fm->cr)) { + return ofp_mkerr(OFPET_FLOW_MOD_FAILED, OFPFMFC_OVERLAP); + } + } } buf_err = ofconn_pktbuf_retrieve(ofconn, fm->buffer_id, &packet, &in_port); - error = rule_create(p, &fm->cr, fm->actions, fm->n_actions, + error = rule_create(p, &fm->cr, fm->table_id, fm->actions, fm->n_actions, fm->idle_timeout, fm->hard_timeout, fm->cookie, fm->flags & OFPFF_SEND_FLOW_REM, &rule); if (error) { @@ -2183,10 +2241,35 @@ add_flow(struct ofconn *ofconn, struct flow_mod *fm) return buf_err; } -static struct rule * -find_flow_strict(struct ofproto *p, const struct flow_mod *fm) +/* Searches 'p' for an exact match for 'fm', in the table or tables indicated + * by fm->table_id. Returns 0 if no match was found, 1 if exactly one match + * was found, 2 if more than one match was found. If exactly one match is + * found, sets '*rulep' to the match, otherwise to NULL. + * + * This implements the rules for "strict" matching explained in the comment on + * struct nxt_flow_mod_table_id in nicira-ext.h. + * + * Ignores hidden rules. */ +static int +find_flow_strict(struct ofproto *p, const struct flow_mod *fm, + struct rule **rulep) { - return rule_from_cls_rule(classifier_find_rule_exactly(&p->cls, &fm->cr)); + struct classifier *cls; + + *rulep = NULL; + FOR_EACH_MATCHING_TABLE (cls, fm->table_id, p) { + struct rule *rule; + + rule = rule_from_cls_rule(classifier_find_rule_exactly(cls, &fm->cr)); + if (rule && !rule_is_hidden(rule)) { + if (*rulep) { + *rulep = NULL; + return 2; + } + *rulep = rule; + } + } + return *rulep != NULL; } static int @@ -2229,19 +2312,23 @@ modify_flows_loose(struct ofconn *ofconn, struct flow_mod *fm) { struct ofproto *p = ofconn_get_ofproto(ofconn); struct rule *match = NULL; - struct cls_cursor cursor; - struct rule *rule; + struct classifier *cls; int error; error = 0; - cls_cursor_init(&cursor, &p->cls, &fm->cr); - CLS_CURSOR_FOR_EACH (rule, cr, &cursor) { - if (!rule_is_hidden(rule)) { - int retval = modify_flow(fm, rule); - if (!retval) { - match = rule; - } else { - error = retval; + FOR_EACH_MATCHING_TABLE (cls, fm->table_id, p) { + struct cls_cursor cursor; + struct rule *rule; + + cls_cursor_init(&cursor, cls, &fm->cr); + CLS_CURSOR_FOR_EACH (rule, cr, &cursor) { + if (!rule_is_hidden(rule)) { + int retval = modify_flow(fm, rule); + if (!retval) { + match = rule; + } else { + error = retval; + } } } } @@ -2268,15 +2355,25 @@ static int modify_flow_strict(struct ofconn *ofconn, struct flow_mod *fm) { struct ofproto *p = ofconn_get_ofproto(ofconn); - struct rule *rule = find_flow_strict(p, fm); - if (rule && !rule_is_hidden(rule)) { - int error = modify_flow(fm, rule); + struct rule *rule; + int error; + + switch (find_flow_strict(p, fm, &rule)) { + case 0: + return add_flow(ofconn, fm); + + case 1: + error = modify_flow(fm, rule); if (!error) { error = send_buffered_packet(ofconn, rule, fm->buffer_id); } return error; - } else { - return add_flow(ofconn, fm); + + case 2: + return 0; + + default: + NOT_REACHED(); } } @@ -2321,12 +2418,16 @@ static void delete_flow(struct rule *, ovs_be16 out_port); static void delete_flows_loose(struct ofproto *p, const struct flow_mod *fm) { - struct rule *rule, *next_rule; - struct cls_cursor cursor; + struct classifier *cls; - cls_cursor_init(&cursor, &p->cls, &fm->cr); - CLS_CURSOR_FOR_EACH_SAFE (rule, next_rule, cr, &cursor) { - delete_flow(rule, htons(fm->out_port)); + FOR_EACH_MATCHING_TABLE (cls, fm->table_id, p) { + struct rule *rule, *next_rule; + struct cls_cursor cursor; + + cls_cursor_init(&cursor, cls, &fm->cr); + CLS_CURSOR_FOR_EACH_SAFE (rule, next_rule, cr, &cursor) { + delete_flow(rule, htons(fm->out_port)); + } } } @@ -2334,8 +2435,8 @@ delete_flows_loose(struct ofproto *p, const struct flow_mod *fm) static void delete_flow_strict(struct ofproto *p, struct flow_mod *fm) { - struct rule *rule = find_flow_strict(p, fm); - if (rule) { + struct rule *rule; + if (find_flow_strict(p, fm, &rule) == 1) { delete_flow(rule, htons(fm->out_port)); } } @@ -2360,7 +2461,7 @@ delete_flow(struct rule *rule, ovs_be16 out_port) } ofproto_rule_send_removed(rule, OFPRR_DELETE); - ofproto_rule_remove(rule); + ofproto_rule_destroy(rule); } static void @@ -2394,7 +2495,7 @@ ofproto_rule_expire(struct rule *rule, uint8_t reason) { assert(reason == OFPRR_HARD_TIMEOUT || reason == OFPRR_IDLE_TIMEOUT); ofproto_rule_send_removed(rule, reason); - ofproto_rule_remove(rule); + ofproto_rule_destroy(rule); } static int @@ -2409,7 +2510,8 @@ handle_flow_mod(struct ofconn *ofconn, const struct ofp_header *oh) return error; } - error = ofputil_decode_flow_mod(&fm, oh, ofconn_get_flow_format(ofconn)); + error = ofputil_decode_flow_mod(&fm, oh, + ofconn_get_flow_mod_table_id(ofconn)); if (error) { return error; } @@ -2441,23 +2543,14 @@ handle_flow_mod(struct ofconn *ofconn, const struct ofp_header *oh) return 0; default: + if (fm.command > 0xff) { + VLOG_WARN_RL(&rl, "flow_mod has explicit table_id but " + "flow_mod_table_id extension is not enabled"); + } return ofp_mkerr(OFPET_FLOW_MOD_FAILED, OFPFMFC_BAD_COMMAND); } } -static int -handle_tun_id_from_cookie(struct ofconn *ofconn, const struct ofp_header *oh) -{ - const struct nxt_tun_id_cookie *msg - = (const struct nxt_tun_id_cookie *) oh; - enum nx_flow_format flow_format; - - flow_format = msg->set ? NXFF_TUN_ID_FROM_COOKIE : NXFF_OPENFLOW10; - ofconn_set_flow_format(ofconn, flow_format); - - return 0; -} - static int handle_role_request(struct ofconn *ofconn, const struct ofp_header *oh) { @@ -2489,6 +2582,17 @@ handle_role_request(struct ofconn *ofconn, const struct ofp_header *oh) return 0; } +static int +handle_nxt_flow_mod_table_id(struct ofconn *ofconn, + const struct ofp_header *oh) +{ + const struct nxt_flow_mod_table_id *msg + = (const struct nxt_flow_mod_table_id *) oh; + + ofconn_set_flow_mod_table_id(ofconn, msg->set != 0); + return 0; +} + static int handle_nxt_set_flow_format(struct ofconn *ofconn, const struct ofp_header *oh) { @@ -2498,7 +2602,6 @@ handle_nxt_set_flow_format(struct ofconn *ofconn, const struct ofp_header *oh) format = ntohl(msg->format); if (format == NXFF_OPENFLOW10 - || format == NXFF_TUN_ID_FROM_COOKIE || format == NXFF_NXM) { ofconn_set_flow_format(ofconn, format); return 0; @@ -2563,12 +2666,12 @@ handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg) return 0; /* Nicira extension requests. */ - case OFPUTIL_NXT_TUN_ID_FROM_COOKIE: - return handle_tun_id_from_cookie(ofconn, oh); - case OFPUTIL_NXT_ROLE_REQUEST: return handle_role_request(ofconn, oh); + case OFPUTIL_NXT_FLOW_MOD_TABLE_ID: + return handle_nxt_flow_mod_table_id(ofconn, oh); + case OFPUTIL_NXT_SET_FLOW_FORMAT: return handle_nxt_set_flow_format(ofconn, oh);