X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=ofproto%2Fofproto.c;h=5ab4d3a82097e40adb2a1607649093734bf98cd7;hb=47271d0d8d12c5689cd0103dc47b62913a515b36;hp=cead7f76fcb275be9f6454493743a884cd683878;hpb=5bf0e941d91cc1236b0fa9551a75a287ca31282f;p=openvswitch diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c index cead7f76..5ab4d3a8 100644 --- a/ofproto/ofproto.c +++ b/ofproto/ofproto.c @@ -19,85 +19,53 @@ #include "ofproto.h" #include #include -#include -#include -#include #include #include -#include "autopath.h" -#include "bitmap.h" -#include "bond.h" #include "byte-order.h" -#include "cfm.h" #include "classifier.h" #include "connmgr.h" #include "coverage.h" #include "dynamic-string.h" -#include "fail-open.h" #include "hash.h" #include "hmap.h" -#include "hmapx.h" -#include "in-band.h" -#include "lacp.h" -#include "mac-learning.h" -#include "multipath.h" #include "netdev.h" -#include "netflow.h" -#include "netlink.h" #include "nx-match.h" -#include "odp-util.h" #include "ofp-print.h" #include "ofp-util.h" -#include "ofproto-sflow.h" #include "ofpbuf.h" #include "openflow/nicira-ext.h" #include "openflow/openflow.h" -#include "openvswitch/datapath-protocol.h" #include "packets.h" #include "pinsched.h" #include "pktbuf.h" #include "poll-loop.h" #include "private.h" -#include "rconn.h" #include "shash.h" #include "sset.h" -#include "stream-ssl.h" -#include "tag.h" -#include "timer.h" #include "timeval.h" #include "unaligned.h" #include "unixctl.h" -#include "vconn.h" -#include "vlan-bitmap.h" #include "vlog.h" VLOG_DEFINE_THIS_MODULE(ofproto); -COVERAGE_DEFINE(odp_overflow); COVERAGE_DEFINE(ofproto_agg_request); -COVERAGE_DEFINE(ofproto_costly_flags); -COVERAGE_DEFINE(ofproto_ctlr_action); COVERAGE_DEFINE(ofproto_error); -COVERAGE_DEFINE(ofproto_expiration); -COVERAGE_DEFINE(ofproto_expired); COVERAGE_DEFINE(ofproto_flows_req); COVERAGE_DEFINE(ofproto_flush); -COVERAGE_DEFINE(ofproto_invalidated); COVERAGE_DEFINE(ofproto_no_packet_in); -COVERAGE_DEFINE(ofproto_ofp2odp); -COVERAGE_DEFINE(ofproto_packet_in); COVERAGE_DEFINE(ofproto_packet_out); COVERAGE_DEFINE(ofproto_queue_req); COVERAGE_DEFINE(ofproto_recv_openflow); COVERAGE_DEFINE(ofproto_reinit_ports); -COVERAGE_DEFINE(ofproto_unexpected_rule); COVERAGE_DEFINE(ofproto_uninstallable); 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, @@ -111,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 *); @@ -126,7 +93,7 @@ static const struct ofproto_class **ofproto_classes; static size_t n_ofproto_classes; static size_t allocated_ofproto_classes; -/* Map from dpif name to struct ofproto, for use by unixctl commands. */ +/* Map from datapath name to struct ofproto, for use by unixctl commands. */ static struct hmap all_ofprotos = HMAP_INITIALIZER(&all_ofprotos); static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); @@ -292,10 +259,10 @@ ofproto_create(const char *datapath_name, const char *datapath_type, ofproto->sw_desc = xstrdup(DEFAULT_SW_DESC); ofproto->serial_desc = xstrdup(DEFAULT_SERIAL_DESC); ofproto->dp_desc = xstrdup(DEFAULT_DP_DESC); - 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); @@ -305,6 +272,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); @@ -464,20 +432,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; @@ -489,9 +456,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)", @@ -500,22 +469,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 @@ -622,6 +575,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); @@ -631,10 +586,13 @@ ofproto_destroy__(struct ofproto *ofproto) free(ofproto->sw_desc); free(ofproto->serial_desc); free(ofproto->dp_desc); - 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); } @@ -650,7 +608,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); } @@ -687,6 +644,7 @@ process_port_change(struct ofproto *ofproto, int error, char *devname) int ofproto_run(struct ofproto *p) { + struct ofport *ofport; char *devname; int error; @@ -706,9 +664,13 @@ ofproto_run(struct ofproto *p) process_port_change(p, error, devname); } } - while ((error = netdev_monitor_poll(p->netdev_monitor, - &devname)) != EAGAIN) { - process_port_change(p, error, devname); + + HMAP_FOR_EACH (ofport, hmap_node, &p->ports) { + unsigned int change_seq = netdev_change_seq(ofport->netdev); + if (ofport->change_seq != change_seq) { + ofport->change_seq = change_seq; + update_port(p, netdev_get_name(ofport->netdev)); + } } connmgr_run(p->connmgr, handle_openflow); @@ -719,11 +681,18 @@ ofproto_run(struct ofproto *p) void ofproto_wait(struct ofproto *p) { + struct ofport *ofport; + p->ofproto_class->wait(p); if (p->ofproto_class->port_poll_wait) { p->ofproto_class->port_poll_wait(p); } - netdev_monitor_poll_wait(p->netdev_monitor); + + HMAP_FOR_EACH (ofport, hmap_node, &p->ports) { + if (ofport->change_seq != netdev_change_seq(ofport->netdev)) { + poll_immediate_wake(); + } + } connmgr_wait(p->connmgr); } @@ -897,7 +866,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. * @@ -905,32 +874,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); @@ -938,12 +910,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) { @@ -1052,11 +1031,11 @@ ofport_install(struct ofproto *p, } ofport->ofproto = p; ofport->netdev = netdev; + ofport->change_seq = netdev_change_seq(netdev); ofport->opp = *opp; ofport->ofp_port = ntohs(opp->port_no); /* Add port to 'p'. */ - netdev_monitor_add(p->netdev_monitor, ofport->netdev); hmap_insert(&p->ports, &ofport->hmap_node, hash_int(ofport->ofp_port, 0)); shash_add(&p->port_by_name, netdev_name, ofport); @@ -1123,7 +1102,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); @@ -1137,7 +1116,6 @@ ofport_destroy__(struct ofport *port) struct ofproto *ofproto = port->ofproto; const char *name = netdev_get_name(port->netdev); - netdev_monitor_remove(ofproto->netdev_monitor, port->netdev); hmap_remove(&ofproto->ports, &port->hmap_node); shash_delete(&ofproto->port_by_name, shash_find(&ofproto->port_by_name, name)); @@ -1186,21 +1164,24 @@ 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. */ - netdev_monitor_remove(ofproto->netdev_monitor, port->netdev); - netdev_monitor_add(ofproto->netdev_monitor, netdev); - - netdev_close(port->netdev); + /* 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.*/ port->netdev = netdev; + port->change_seq = netdev_change_seq(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' @@ -1250,7 +1231,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, @@ -1259,6 +1241,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; @@ -1267,6 +1263,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; @@ -1302,15 +1299,14 @@ ofproto_rule_destroy__(struct rule *rule) rule->ofproto->ofproto_class->rule_dealloc(rule); } -/* Destroys 'rule' and iterates through all of its facets and revalidates them, - * destroying any that no longer has a rule (which is probably all of them). - * - * 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 @@ -1334,15 +1330,9 @@ 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 (or the statistics for one of its facets) appropriately. - * 'packet' must have at least sizeof(struct ofp_packet_in) bytes of headroom. + * statistics appropriately. 'packet' must have at least sizeof(struct + * ofp_packet_in) bytes of headroom. * * 'packet' doesn't necessarily have to match 'rule'. 'rule' will be credited * with statistics for 'packet' either way. @@ -1359,20 +1349,6 @@ rule_execute(struct rule *rule, uint16_t in_port, struct ofpbuf *packet) return rule->ofproto->ofproto_class->rule_execute(rule, &flow, packet); } -/* Remove 'rule' from 'ofproto' and free up the associated memory: - * - * - Removes 'rule' from the classifier. - * - * - If 'rule' has facets, revalidates them (and possibly uninstalls and - * destroys them), via rule_destroy(). - */ -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 @@ -1409,25 +1385,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); @@ -1492,7 +1465,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) { @@ -1708,19 +1681,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 * 2); + msg = start_ofp_stats_reply(request, sizeof *ots * p->n_tables); + + 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; @@ -1805,7 +1779,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)) { @@ -1819,11 +1792,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); @@ -1836,38 +1808,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); } @@ -1922,6 +1924,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; @@ -1941,11 +1944,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); } @@ -1964,6 +1967,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); @@ -1984,12 +1990,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; - cls_cursor_init(&cursor, &p->cls, NULL); - CLS_CURSOR_FOR_EACH (rule, cr, &cursor) { - flow_stats_ds(rule, results); + for (cls = &p->tables[0]; cls < &p->tables[p->n_tables]; cls++) { + struct cls_cursor cursor; + struct rule *rule; + + cls_cursor_init(&cursor, cls, NULL); + CLS_CURSOR_FOR_EACH (rule, cr, &cursor) { + flow_stats_ds(rule, results); + } } } @@ -2002,6 +2012,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, @@ -2009,15 +2031,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; @@ -2049,8 +2072,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); @@ -2201,13 +2223,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) { @@ -2222,10 +2249,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 @@ -2268,19 +2320,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; + } } } } @@ -2307,15 +2363,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(); } } @@ -2360,12 +2426,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)); + } } } @@ -2373,8 +2443,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)); } } @@ -2399,7 +2469,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 @@ -2433,7 +2503,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 @@ -2448,7 +2518,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; } @@ -2480,23 +2551,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) { @@ -2528,6 +2590,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) { @@ -2537,7 +2610,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; @@ -2602,12 +2674,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);