X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=ofproto%2Fofproto.c;h=91ff02388c3b09d72d8c0a45a7317437c01558f9;hb=62471e59fccf3c48d75becfa12690e23bb683407;hp=9a91c1cea27df8574fcc467a31a12a2dba7467b1;hpb=1e82e503c5358f8dce9eb2105448f0ec894d57bc;p=openvswitch diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c index 9a91c1ce..91ff0238 100644 --- a/ofproto/ofproto.c +++ b/ofproto/ofproto.c @@ -36,6 +36,7 @@ #include "netflow.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" @@ -49,17 +50,16 @@ #include "rconn.h" #include "shash.h" #include "status.h" -#include "stp.h" #include "stream-ssl.h" #include "svec.h" #include "tag.h" #include "timeval.h" #include "unixctl.h" #include "vconn.h" +#include "vlog.h" #include "xtoxll.h" -#define THIS_MODULE VLM_ofproto -#include "vlog.h" +VLOG_DEFINE_THIS_MODULE(ofproto) #include "sflow_api.h" @@ -169,19 +169,44 @@ static void send_flow_removed(struct ofproto *p, struct rule *rule, /* ofproto supports two kinds of OpenFlow connections: * - * - "Controller connections": Connections to ordinary OpenFlow controllers. - * ofproto maintains persistent connections to these controllers and by - * default sends them asynchronous messages such as packet-ins. + * - "Primary" connections to ordinary OpenFlow controllers. ofproto + * maintains persistent connections to these controllers and by default + * sends them asynchronous messages such as packet-ins. * - * - "Transient connections", e.g. from ovs-ofctl. When these connections + * - "Service" connections, e.g. from ovs-ofctl. When these connections * drop, it is the other side's responsibility to reconnect them if * necessary. ofproto does not send them asynchronous messages by default. + * + * Currently, active (tcp, ssl, unix) connections are always "primary" + * connections and passive (ptcp, pssl, punix) connections are always "service" + * connections. There is no inherent reason for this, but it reflects the + * common case. */ enum ofconn_type { - OFCONN_CONTROLLER, /* An OpenFlow controller. */ - OFCONN_TRANSIENT /* A transient connection. */ + OFCONN_PRIMARY, /* An ordinary OpenFlow controller. */ + OFCONN_SERVICE /* A service connection, e.g. "ovs-ofctl". */ +}; + +/* A listener for incoming OpenFlow "service" connections. */ +struct ofservice { + struct hmap_node node; /* In struct ofproto's "services" hmap. */ + struct pvconn *pvconn; /* OpenFlow connection listener. */ + + /* These are not used by ofservice directly. They are settings for + * accepted "struct ofconn"s from the pvconn. */ + int probe_interval; /* Max idle time before probing, in seconds. */ + int rate_limit; /* Max packet-in rate in packets per second. */ + int burst_limit; /* Limit on accumulating packet credits. */ }; +static struct ofservice *ofservice_lookup(struct ofproto *, + const char *target); +static int ofservice_create(struct ofproto *, + const struct ofproto_controller *); +static void ofservice_reconfigure(struct ofservice *, + const struct ofproto_controller *); +static void ofservice_destroy(struct ofproto *, struct ofservice *); + /* An OpenFlow connection. */ struct ofconn { struct ofproto *ofproto; /* The ofproto that owns this connection. */ @@ -201,7 +226,7 @@ struct ofconn { #define OFCONN_REPLY_MAX 100 struct rconn_packet_counter *reply_counter; - /* type == OFCONN_CONTROLLER only. */ + /* type == OFCONN_PRIMARY only. */ enum nx_role role; /* Role. */ struct hmap_node hmap_node; /* In struct ofproto's "controllers" map. */ struct discovery *discovery; /* Controller discovery object, if enabled. */ @@ -225,6 +250,8 @@ static void ofconn_destroy(struct ofconn *); static void ofconn_run(struct ofconn *, struct ofproto *); static void ofconn_wait(struct ofconn *); static bool ofconn_receives_async_msgs(const struct ofconn *); +static char *ofconn_make_name(const struct ofproto *, const char *target); +static void ofconn_set_rate_limit(struct ofconn *, int rate, int burst); static void queue_tx(struct ofpbuf *msg, const struct ofconn *ofconn, struct rconn_packet_counter *counter); @@ -272,8 +299,10 @@ struct ofproto { /* OpenFlow connections. */ struct hmap controllers; /* Controller "struct ofconn"s. */ struct list all_conns; /* Contains "struct ofconn"s. */ - struct pvconn **listeners; - size_t n_listeners; + enum ofproto_fail_mode fail_mode; + + /* OpenFlow listeners. */ + struct hmap services; /* Contains "struct ofservice"s. */ struct pvconn **snoops; size_t n_snoops; @@ -379,8 +408,7 @@ ofproto_create(const char *datapath, const char *datapath_type, /* Initialize OpenFlow connections. */ list_init(&p->all_conns); hmap_init(&p->controllers); - p->listeners = NULL; - p->n_listeners = 0; + hmap_init(&p->services); p->snoops = NULL; p->n_snoops = 0; @@ -409,15 +437,11 @@ ofproto_set_datapath_id(struct ofproto *p, uint64_t datapath_id) uint64_t old_dpid = p->datapath_id; p->datapath_id = datapath_id ? datapath_id : pick_datapath_id(p); if (p->datapath_id != old_dpid) { - struct ofconn *ofconn; - VLOG_INFO("datapath ID changed to %016"PRIx64, p->datapath_id); /* Force all active connections to reconnect, since there is no way to * notify a controller that the datapath ID has changed. */ - LIST_FOR_EACH (ofconn, struct ofconn, node, &p->all_conns) { - rconn_reconnect(ofconn->rconn); - } + ofproto_reconnect_controllers(p); } } @@ -453,13 +477,15 @@ add_controller(struct ofproto *ofproto, const struct ofproto_controller *c) discovery = NULL; } - ofconn = ofconn_create(ofproto, rconn_create(5, 8), OFCONN_CONTROLLER); + ofconn = ofconn_create(ofproto, rconn_create(5, 8), OFCONN_PRIMARY); ofconn->pktbuf = pktbuf_create(); ofconn->miss_send_len = OFP_DEFAULT_MISS_SEND_LEN; if (discovery) { ofconn->discovery = discovery; } else { - rconn_connect(ofconn->rconn, c->target); + char *name = ofconn_make_name(ofproto, c->target); + rconn_connect(ofconn->rconn, c->target, name); + free(name); } hmap_insert(&ofproto->controllers, &ofconn->hmap_node, hash_string(c->target, 0)); @@ -472,9 +498,7 @@ add_controller(struct ofproto *ofproto, const struct ofproto_controller *c) static void update_controller(struct ofconn *ofconn, const struct ofproto_controller *c) { - struct ofproto *ofproto = ofconn->ofproto; int probe_interval; - int i; ofconn->band = (is_in_band_controller(c) ? OFPROTO_IN_BAND : OFPROTO_OUT_OF_BAND); @@ -490,27 +514,13 @@ update_controller(struct ofconn *ofconn, const struct ofproto_controller *c) discovery_set_accept_controller_re(ofconn->discovery, c->accept_re); } - for (i = 0; i < N_SCHEDULERS; i++) { - struct pinsched **s = &ofconn->schedulers[i]; - - if (c->rate_limit > 0) { - if (!*s) { - *s = pinsched_create(c->rate_limit, c->burst_limit, - ofproto->switch_status); - } else { - pinsched_set_limits(*s, c->rate_limit, c->burst_limit); - } - } else { - pinsched_destroy(*s); - *s = NULL; - } - } + ofconn_set_rate_limit(ofconn, c->rate_limit, c->burst_limit); } static const char * ofconn_get_target(const struct ofconn *ofconn) { - return ofconn->discovery ? "discover" : rconn_get_name(ofconn->rconn); + return ofconn->discovery ? "discover" : rconn_get_target(ofconn->rconn); } static struct ofconn * @@ -586,30 +596,72 @@ update_in_band_remotes(struct ofproto *ofproto) free(addrs); } +static void +update_fail_open(struct ofproto *p) +{ + struct ofconn *ofconn; + + if (!hmap_is_empty(&p->controllers) + && p->fail_mode == OFPROTO_FAIL_STANDALONE) { + struct rconn **rconns; + size_t n; + + if (!p->fail_open) { + p->fail_open = fail_open_create(p, p->switch_status); + } + + n = 0; + rconns = xmalloc(hmap_count(&p->controllers) * sizeof *rconns); + HMAP_FOR_EACH (ofconn, struct ofconn, hmap_node, &p->controllers) { + rconns[n++] = ofconn->rconn; + } + + fail_open_set_controllers(p->fail_open, rconns, n); + /* p->fail_open takes ownership of 'rconns'. */ + } else { + fail_open_destroy(p->fail_open); + p->fail_open = NULL; + } +} + void ofproto_set_controllers(struct ofproto *p, const struct ofproto_controller *controllers, size_t n_controllers) { struct shash new_controllers; - enum ofproto_fail_mode fail_mode; - struct ofconn *ofconn, *next; + struct ofconn *ofconn, *next_ofconn; + struct ofservice *ofservice, *next_ofservice; bool ss_exists; size_t i; + /* Create newly configured controllers and services. + * Create a name to ofproto_controller mapping in 'new_controllers'. */ shash_init(&new_controllers); for (i = 0; i < n_controllers; i++) { const struct ofproto_controller *c = &controllers[i]; - shash_add_once(&new_controllers, c->target, &controllers[i]); - if (!find_controller_by_target(p, c->target)) { - add_controller(p, c); + if (!vconn_verify_name(c->target) || !strcmp(c->target, "discover")) { + if (!find_controller_by_target(p, c->target)) { + add_controller(p, c); + } + } else if (!pvconn_verify_name(c->target)) { + if (!ofservice_lookup(p, c->target) && ofservice_create(p, c)) { + continue; + } + } else { + VLOG_WARN_RL(&rl, "%s: unsupported controller \"%s\"", + dpif_name(p->dpif), c->target); + continue; } + + shash_add_once(&new_controllers, c->target, &controllers[i]); } - fail_mode = OFPROTO_FAIL_STANDALONE; + /* Delete controllers that are no longer configured. + * Update configuration of all now-existing controllers. */ ss_exists = false; - HMAP_FOR_EACH_SAFE (ofconn, next, struct ofconn, hmap_node, + HMAP_FOR_EACH_SAFE (ofconn, next_ofconn, struct ofconn, hmap_node, &p->controllers) { struct ofproto_controller *c; @@ -621,36 +673,28 @@ ofproto_set_controllers(struct ofproto *p, if (ofconn->ss) { ss_exists = true; } - if (c->fail == OFPROTO_FAIL_SECURE) { - fail_mode = OFPROTO_FAIL_SECURE; - } } } - shash_destroy(&new_controllers); - update_in_band_remotes(p); - - if (!hmap_is_empty(&p->controllers) - && fail_mode == OFPROTO_FAIL_STANDALONE) { - struct rconn **rconns; - size_t n; + /* Delete services that are no longer configured. + * Update configuration of all now-existing services. */ + HMAP_FOR_EACH_SAFE (ofservice, next_ofservice, struct ofservice, node, + &p->services) { + struct ofproto_controller *c; - if (!p->fail_open) { - p->fail_open = fail_open_create(p, p->switch_status); + c = shash_find_data(&new_controllers, + pvconn_get_name(ofservice->pvconn)); + if (!c) { + ofservice_destroy(p, ofservice); + } else { + ofservice_reconfigure(ofservice, c); } + } - n = 0; - rconns = xmalloc(hmap_count(&p->controllers) * sizeof *rconns); - HMAP_FOR_EACH (ofconn, struct ofconn, hmap_node, &p->controllers) { - rconns[n++] = ofconn->rconn; - } + shash_destroy(&new_controllers); - fail_open_set_controllers(p->fail_open, rconns, n); - /* p->fail_open takes ownership of 'rconns'. */ - } else { - fail_open_destroy(p->fail_open); - p->fail_open = NULL; - } + update_in_band_remotes(p); + update_fail_open(p); if (!hmap_is_empty(&p->controllers) && !ss_exists) { ofconn = CONTAINER_OF(hmap_first(&p->controllers), @@ -660,6 +704,25 @@ ofproto_set_controllers(struct ofproto *p, } } +void +ofproto_set_fail_mode(struct ofproto *p, enum ofproto_fail_mode fail_mode) +{ + p->fail_mode = fail_mode; + update_fail_open(p); +} + +/* Drops the connections between 'ofproto' and all of its controllers, forcing + * them to reconnect. */ +void +ofproto_reconnect_controllers(struct ofproto *ofproto) +{ + struct ofconn *ofconn; + + LIST_FOR_EACH (ofconn, struct ofconn, node, &ofproto->all_conns) { + rconn_reconnect(ofconn->rconn); + } +} + static bool any_extras_changed(const struct ofproto *ofproto, const struct sockaddr_in *extras, size_t n) @@ -790,12 +853,6 @@ set_pvconns(struct pvconn ***pvconnsp, size_t *n_pvconnsp, return retval; } -int -ofproto_set_listeners(struct ofproto *ofproto, const struct svec *listeners) -{ - return set_pvconns(&ofproto->listeners, &ofproto->n_listeners, listeners); -} - int ofproto_set_snoops(struct ofproto *ofproto, const struct svec *snoops) { @@ -842,18 +899,6 @@ ofproto_set_sflow(struct ofproto *ofproto, } } -int -ofproto_set_stp(struct ofproto *ofproto OVS_UNUSED, bool enable_stp) -{ - /* XXX */ - if (enable_stp) { - VLOG_WARN("STP is not yet implemented"); - return EINVAL; - } else { - return 0; - } -} - uint64_t ofproto_get_datapath_id(const struct ofproto *ofproto) { @@ -861,19 +906,15 @@ ofproto_get_datapath_id(const struct ofproto *ofproto) } bool -ofproto_has_controller(const struct ofproto *ofproto) +ofproto_has_primary_controller(const struct ofproto *ofproto) { return !hmap_is_empty(&ofproto->controllers); } -void -ofproto_get_listeners(const struct ofproto *ofproto, struct svec *listeners) +enum ofproto_fail_mode +ofproto_get_fail_mode(const struct ofproto *p) { - size_t i; - - for (i = 0; i < ofproto->n_listeners; i++) { - svec_add(listeners, pvconn_get_name(ofproto->listeners[i])); - } + return p->fail_mode; } void @@ -889,6 +930,7 @@ ofproto_get_snoops(const struct ofproto *ofproto, struct svec *snoops) void ofproto_destroy(struct ofproto *p) { + struct ofservice *ofservice, *next_ofservice; struct ofconn *ofconn, *next_ofconn; struct ofport *ofport; unsigned int port_no; @@ -926,10 +968,11 @@ ofproto_destroy(struct ofproto *p) netflow_destroy(p->netflow); ofproto_sflow_destroy(p->sflow); - for (i = 0; i < p->n_listeners; i++) { - pvconn_close(p->listeners[i]); + HMAP_FOR_EACH_SAFE (ofservice, next_ofservice, struct ofservice, node, + &p->services) { + ofservice_destroy(p, ofservice); } - free(p->listeners); + hmap_destroy(&p->services); for (i = 0; i < p->n_snoops; i++) { pvconn_close(p->snoops[i]); @@ -999,7 +1042,7 @@ add_snooper(struct ofproto *ofproto, struct vconn *vconn) /* Pick a controller for monitoring. */ best = NULL; LIST_FOR_EACH (ofconn, struct ofconn, node, &ofproto->all_conns) { - if (ofconn->type == OFCONN_CONTROLLER + if (ofconn->type == OFCONN_PRIMARY && (!best || snoop_preference(ofconn) > snoop_preference(best))) { best = ofconn; } @@ -1017,6 +1060,7 @@ int ofproto_run1(struct ofproto *p) { struct ofconn *ofconn, *next_ofconn; + struct ofservice *ofservice; char *devname; int error; int i; @@ -1072,14 +1116,24 @@ ofproto_run1(struct ofproto *p) fail_open_run(p->fail_open); } - for (i = 0; i < p->n_listeners; i++) { + HMAP_FOR_EACH (ofservice, struct ofservice, node, &p->services) { struct vconn *vconn; int retval; - retval = pvconn_accept(p->listeners[i], OFP_VERSION, &vconn); + retval = pvconn_accept(ofservice->pvconn, OFP_VERSION, &vconn); if (!retval) { - ofconn_create(p, rconn_new_from_vconn("passive", vconn), - OFCONN_TRANSIENT); + struct ofconn *ofconn; + struct rconn *rconn; + char *name; + + rconn = rconn_create(ofservice->probe_interval, 0); + name = ofconn_make_name(p, vconn_get_name(vconn)); + rconn_connect_unreliably(rconn, vconn, name); + free(name); + + ofconn = ofconn_create(p, rconn, OFCONN_SERVICE); + ofconn_set_rate_limit(ofconn, ofservice->rate_limit, + ofservice->burst_limit); } else if (retval != EAGAIN) { VLOG_WARN_RL(&rl, "accept failed (%s)", strerror(retval)); } @@ -1152,6 +1206,7 @@ ofproto_run2(struct ofproto *p, bool revalidate_all) void ofproto_wait(struct ofproto *p) { + struct ofservice *ofservice; struct ofconn *ofconn; size_t i; @@ -1181,8 +1236,8 @@ ofproto_wait(struct ofproto *p) } else if (p->next_expiration != LLONG_MAX) { poll_timer_wait_until(p->next_expiration); } - for (i = 0; i < p->n_listeners; i++) { - pvconn_wait(p->listeners[i]); + HMAP_FOR_EACH (ofservice, struct ofservice, node, &p->services) { + pvconn_wait(ofservice->pvconn); } for (i = 0; i < p->n_snoops; i++) { pvconn_wait(p->snoops[i]); @@ -1467,7 +1522,7 @@ ofport_remove(struct ofproto *p, struct ofport *ofport) uint16_t odp_port = ofp_port_to_odp_port(ofport->opp.port_no); netdev_monitor_remove(p->netdev_monitor, ofport->netdev); - port_array_set(&p->ports, odp_port, NULL); + port_array_delete(&p->ports, odp_port); shash_delete(&p->port_by_name, shash_find(&p->port_by_name, (char *) ofport->opp.name)); if (p->sflow) { @@ -1602,7 +1657,7 @@ ofconn_create(struct ofproto *p, struct rconn *rconn, enum ofconn_type type) static void ofconn_destroy(struct ofconn *ofconn) { - if (ofconn->type == OFCONN_CONTROLLER) { + if (ofconn->type == OFCONN_PRIMARY) { hmap_remove(&ofconn->ofproto->controllers, &ofconn->hmap_node); } discovery_destroy(ofconn->discovery); @@ -1629,7 +1684,9 @@ ofconn_run(struct ofconn *ofconn, struct ofproto *p) } if (discovery_run(ofconn->discovery, &controller_name)) { if (controller_name) { - rconn_connect(ofconn->rconn, controller_name); + char *ofconn_name = ofconn_make_name(p, controller_name); + rconn_connect(ofconn->rconn, controller_name, ofconn_name); + free(ofconn_name); } else { rconn_disconnect(ofconn->rconn); } @@ -1686,17 +1743,107 @@ ofconn_wait(struct ofconn *ofconn) static bool ofconn_receives_async_msgs(const struct ofconn *ofconn) { - if (ofconn->type == OFCONN_CONTROLLER) { - /* Ordinary controllers always get asynchronous messages unless they + if (ofconn->type == OFCONN_PRIMARY) { + /* Primary controllers always get asynchronous messages unless they * have configured themselves as "slaves". */ return ofconn->role != NX_ROLE_SLAVE; } else { - /* Transient connections don't get asynchronous messages unless they - * have explicitly asked for them by setting a nonzero miss send - * length. */ + /* Service connections don't get asynchronous messages unless they have + * explicitly asked for them by setting a nonzero miss send length. */ return ofconn->miss_send_len > 0; } } + +/* Returns a human-readable name for an OpenFlow connection between 'ofproto' + * and 'target', suitable for use in log messages for identifying the + * connection. + * + * The name is dynamically allocated. The caller should free it (with free()) + * when it is no longer needed. */ +static char * +ofconn_make_name(const struct ofproto *ofproto, const char *target) +{ + return xasprintf("%s<->%s", dpif_base_name(ofproto->dpif), target); +} + +static void +ofconn_set_rate_limit(struct ofconn *ofconn, int rate, int burst) +{ + int i; + + for (i = 0; i < N_SCHEDULERS; i++) { + struct pinsched **s = &ofconn->schedulers[i]; + + if (rate > 0) { + if (!*s) { + *s = pinsched_create(rate, burst, + ofconn->ofproto->switch_status); + } else { + pinsched_set_limits(*s, rate, burst); + } + } else { + pinsched_destroy(*s); + *s = NULL; + } + } +} + +static void +ofservice_reconfigure(struct ofservice *ofservice, + const struct ofproto_controller *c) +{ + ofservice->probe_interval = c->probe_interval; + ofservice->rate_limit = c->rate_limit; + ofservice->burst_limit = c->burst_limit; +} + +/* Creates a new ofservice in 'ofproto'. Returns 0 if successful, otherwise a + * positive errno value. */ +static int +ofservice_create(struct ofproto *ofproto, const struct ofproto_controller *c) +{ + struct ofservice *ofservice; + struct pvconn *pvconn; + int error; + + error = pvconn_open(c->target, &pvconn); + if (error) { + return error; + } + + ofservice = xzalloc(sizeof *ofservice); + hmap_insert(&ofproto->services, &ofservice->node, + hash_string(c->target, 0)); + ofservice->pvconn = pvconn; + + ofservice_reconfigure(ofservice, c); + + return 0; +} + +static void +ofservice_destroy(struct ofproto *ofproto, struct ofservice *ofservice) +{ + hmap_remove(&ofproto->services, &ofservice->node); + pvconn_close(ofservice->pvconn); + free(ofservice); +} + +/* Finds and returns the ofservice within 'ofproto' that has the given + * 'target', or a null pointer if none exists. */ +static struct ofservice * +ofservice_lookup(struct ofproto *ofproto, const char *target) +{ + struct ofservice *ofservice; + + HMAP_FOR_EACH_WITH_HASH (ofservice, struct ofservice, node, + hash_string(target, 0), &ofproto->services) { + if (!strcmp(pvconn_get_name(ofservice->pvconn), target)) { + return ofservice; + } + } + return NULL; +} /* Caller is responsible for initializing the 'cr' member of the returned * rule. */ @@ -1773,7 +1920,7 @@ rule_has_out_port(const struct rule *rule, uint16_t out_port) } for (oa = actions_first(&i, rule->actions, rule->n_actions); oa; oa = actions_next(&i)) { - if (oa->type == htons(OFPAT_OUTPUT) && oa->output.port == out_port) { + if (action_outputs_to_port(oa, out_port)) { return true; } } @@ -2012,7 +2159,7 @@ rule_account(struct ofproto *ofproto, struct rule *rule, uint64_t extra_bytes) && total_bytes > rule->accounted_bytes) { ofproto->ofhooks->account_flow_cb( - &rule->cr.flow, rule->odp_actions, rule->n_odp_actions, + &rule->cr.flow, rule->tags, rule->odp_actions, rule->n_odp_actions, total_bytes - rule->accounted_bytes, ofproto->aux); rule->accounted_bytes = total_bytes; } @@ -2045,15 +2192,11 @@ is_controller_rule(struct rule *rule) * NetFlow expiration messages since it is just part of the control * logic for the network and not real traffic. */ - if (rule && rule->super) { - struct rule *super = rule->super; - - return super->n_actions == 1 && - super->actions[0].type == htons(OFPAT_OUTPUT) && - super->actions[0].output.port == htons(OFPP_CONTROLLER); - } - - return false; + return (rule + && rule->super + && rule->super->n_actions == 1 + && action_outputs_to_port(&rule->super->actions[0], + htons(OFPP_CONTROLLER))); } static void @@ -2170,7 +2313,8 @@ handle_features_request(struct ofproto *p, struct ofconn *ofconn, (1u << OFPAT_SET_NW_DST) | (1u << OFPAT_SET_NW_TOS) | (1u << OFPAT_SET_TP_SRC) | - (1u << OFPAT_SET_TP_DST)); + (1u << OFPAT_SET_TP_DST) | + (1u << OFPAT_ENQUEUE)); PORT_ARRAY_FOR_EACH (port, &p->ports, port_no) { hton_ofp_phy_port(ofpbuf_put(buf, &port->opp, sizeof port->opp)); @@ -2215,7 +2359,7 @@ handle_set_config(struct ofproto *p, struct ofconn *ofconn, } flags = ntohs(osc->flags); - if (ofconn->type == OFCONN_CONTROLLER && ofconn->role != NX_ROLE_SLAVE) { + if (ofconn->type == OFCONN_PRIMARY && ofconn->role != NX_ROLE_SLAVE) { switch (flags & OFPC_FRAG_MASK) { case OFPC_FRAG_NORMAL: dpif_set_drop_frags(p->dpif, false); @@ -2247,11 +2391,10 @@ add_output_group_action(struct odp_actions *actions, uint16_t group, } static void -add_controller_action(struct odp_actions *actions, - const struct ofp_action_output *oao) +add_controller_action(struct odp_actions *actions, uint16_t max_len) { union odp_action *a = odp_actions_add(actions, ODPAT_CONTROLLER); - a->controller.arg = ntohs(oao->max_len); + a->controller.arg = max_len; } struct action_xlate_ctx { @@ -2271,6 +2414,10 @@ struct action_xlate_ctx { uint16_t nf_output_iface; /* Output interface index for NetFlow. */ }; +/* Maximum depth of flow table recursion (due to NXAST_RESUBMIT actions) in a + * flow translation. */ +#define MAX_RESUBMIT_RECURSION 8 + static void do_xlate_actions(const union ofp_action *in, size_t n_in, struct action_xlate_ctx *ctx); @@ -2318,7 +2465,7 @@ lookup_valid_rule(struct ofproto *ofproto, const flow_t *flow) static void xlate_table_action(struct action_xlate_ctx *ctx, uint16_t in_port) { - if (!ctx->recurse) { + if (ctx->recurse < MAX_RESUBMIT_RECURSION) { uint16_t old_in_port; struct rule *rule; @@ -2339,19 +2486,24 @@ xlate_table_action(struct action_xlate_ctx *ctx, uint16_t in_port) do_xlate_actions(rule->actions, rule->n_actions, ctx); ctx->recurse--; } + } else { + struct vlog_rate_limit recurse_rl = VLOG_RATE_LIMIT_INIT(1, 1); + + VLOG_ERR_RL(&recurse_rl, "NXAST_RESUBMIT recursed over %d times", + MAX_RESUBMIT_RECURSION); } } static void -xlate_output_action(struct action_xlate_ctx *ctx, - const struct ofp_action_output *oao) +xlate_output_action__(struct action_xlate_ctx *ctx, + uint16_t port, uint16_t max_len) { uint16_t odp_port; uint16_t prev_nf_output_iface = ctx->nf_output_iface; ctx->nf_output_iface = NF_OUT_DROP; - switch (ntohs(oao->port)) { + switch (port) { case OFPP_IN_PORT: add_output_action(ctx, ctx->flow.in_port); break; @@ -2375,13 +2527,13 @@ xlate_output_action(struct action_xlate_ctx *ctx, add_output_group_action(ctx->out, DP_GROUP_ALL, &ctx->nf_output_iface); break; case OFPP_CONTROLLER: - add_controller_action(ctx->out, oao); + add_controller_action(ctx->out, max_len); break; case OFPP_LOCAL: add_output_action(ctx, ODPP_LOCAL); break; default: - odp_port = ofp_port_to_odp_port(ntohs(oao->port)); + odp_port = ofp_port_to_odp_port(port); if (odp_port != ctx->flow.in_port) { add_output_action(ctx, odp_port); } @@ -2398,6 +2550,65 @@ xlate_output_action(struct action_xlate_ctx *ctx, } } +static void +xlate_output_action(struct action_xlate_ctx *ctx, + const struct ofp_action_output *oao) +{ + xlate_output_action__(ctx, ntohs(oao->port), ntohs(oao->max_len)); +} + +/* If the final ODP action in 'ctx' is "pop priority", drop it, as an + * optimization, because we're going to add another action that sets the + * priority immediately after, or because there are no actions following the + * pop. */ +static void +remove_pop_action(struct action_xlate_ctx *ctx) +{ + size_t n = ctx->out->n_actions; + if (n > 0 && ctx->out->actions[n - 1].type == ODPAT_POP_PRIORITY) { + ctx->out->n_actions--; + } +} + +static void +xlate_enqueue_action(struct action_xlate_ctx *ctx, + const struct ofp_action_enqueue *oae) +{ + uint16_t ofp_port, odp_port; + uint32_t priority; + int error; + + error = dpif_queue_to_priority(ctx->ofproto->dpif, ntohl(oae->queue_id), + &priority); + if (error) { + /* Fall back to ordinary output action. */ + xlate_output_action__(ctx, ntohs(oae->port), 0); + return; + } + + /* Figure out ODP output port. */ + ofp_port = ntohs(oae->port); + if (ofp_port != OFPP_IN_PORT) { + odp_port = ofp_port_to_odp_port(ofp_port); + } else { + odp_port = ctx->flow.in_port; + } + + /* Add ODP actions. */ + remove_pop_action(ctx); + odp_actions_add(ctx->out, ODPAT_SET_PRIORITY)->priority.priority + = priority; + add_output_action(ctx, odp_port); + odp_actions_add(ctx->out, ODPAT_POP_PRIORITY); + + /* Update NetFlow output port. */ + if (ctx->nf_output_iface == NF_OUT_DROP) { + ctx->nf_output_iface = odp_port; + } else if (ctx->nf_output_iface != NF_OUT_FLOOD) { + ctx->nf_output_iface = NF_OUT_MULTI; + } +} + static void xlate_nicira_action(struct action_xlate_ctx *ctx, const struct nx_action_header *nah) @@ -2421,7 +2632,7 @@ xlate_nicira_action(struct action_xlate_ctx *ctx, break; /* If you add a new action here that modifies flow data, don't forget to - * update the flow key in ctx->flow in the same key. */ + * update the flow key in ctx->flow at the same time. */ default: VLOG_DBG_RL(&rl, "unknown Nicira action type %"PRIu16, subtype); @@ -2439,7 +2650,7 @@ do_xlate_actions(const union ofp_action *in, size_t n_in, port = port_array_get(&ctx->ofproto->ports, ctx->flow.in_port); if (port && port->opp.config & (OFPPC_NO_RECV | OFPPC_NO_RECV_STP) && - port->opp.config & (eth_addr_equals(ctx->flow.dl_dst, stp_eth_addr) + port->opp.config & (eth_addr_equals(ctx->flow.dl_dst, eth_addr_stp) ? OFPPC_NO_RECV_STP : OFPPC_NO_RECV)) { /* Drop this flow. */ return; @@ -2466,7 +2677,7 @@ do_xlate_actions(const union ofp_action *in, size_t n_in, case OFPAT_STRIP_VLAN: odp_actions_add(ctx->out, ODPAT_STRIP_VLAN); - ctx->flow.dl_vlan = OFP_VLAN_NONE; + ctx->flow.dl_vlan = htons(OFP_VLAN_NONE); ctx->flow.dl_vlan_pcp = 0; break; @@ -2515,6 +2726,10 @@ do_xlate_actions(const union ofp_action *in, size_t n_in, xlate_nicira_action(ctx, (const struct nx_action_header *) ia); break; + case OFPAT_ENQUEUE: + xlate_enqueue_action(ctx, (const struct ofp_action_enqueue *) ia); + break; + default: VLOG_DBG_RL(&rl, "unknown action type %"PRIu16, type); break; @@ -2542,6 +2757,7 @@ xlate_actions(const union ofp_action *in, size_t n_in, ctx.may_set_up_flow = true; ctx.nf_output_iface = NF_OUT_DROP; do_xlate_actions(in, n_in, &ctx); + remove_pop_action(&ctx); /* Check with in-band control to see if we're allowed to set up this * flow. */ @@ -2556,6 +2772,7 @@ xlate_actions(const union ofp_action *in, size_t n_in, *nf_output_iface = ctx.nf_output_iface; } if (odp_actions_overflow(out)) { + COVERAGE_INC(odp_overflow); odp_actions_init(out); return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_TOO_MANY); } @@ -2570,7 +2787,7 @@ xlate_actions(const union ofp_action *in, size_t n_in, static int reject_slave_controller(struct ofconn *ofconn, const struct ofp_header *oh) { - if (ofconn->type == OFCONN_CONTROLLER && ofconn->role == NX_ROLE_SLAVE) { + if (ofconn->type == OFCONN_PRIMARY && ofconn->role == NX_ROLE_SLAVE) { static struct vlog_rate_limit perm_rl = VLOG_RATE_LIMIT_INIT(1, 5); char *type_name; @@ -3119,6 +3336,95 @@ handle_aggregate_stats_request(struct ofproto *p, struct ofconn *ofconn, return 0; } +struct queue_stats_cbdata { + struct ofconn *ofconn; + struct ofpbuf *msg; + uint16_t port_no; +}; + +static void +put_queue_stats(struct queue_stats_cbdata *cbdata, uint32_t queue_id, + const struct netdev_queue_stats *stats) +{ + struct ofp_queue_stats *reply; + + reply = append_stats_reply(sizeof *reply, cbdata->ofconn, &cbdata->msg); + reply->port_no = htons(cbdata->port_no); + memset(reply->pad, 0, sizeof reply->pad); + reply->queue_id = htonl(queue_id); + reply->tx_bytes = htonll(stats->tx_bytes); + reply->tx_packets = htonll(stats->tx_packets); + reply->tx_errors = htonll(stats->tx_errors); +} + +static void +handle_queue_stats_dump_cb(uint32_t queue_id, + struct netdev_queue_stats *stats, + void *cbdata_) +{ + struct queue_stats_cbdata *cbdata = cbdata_; + + put_queue_stats(cbdata, queue_id, stats); +} + +static void +handle_queue_stats_for_port(struct ofport *port, uint16_t port_no, + uint32_t queue_id, + struct queue_stats_cbdata *cbdata) +{ + cbdata->port_no = port_no; + if (queue_id == OFPQ_ALL) { + netdev_dump_queue_stats(port->netdev, + handle_queue_stats_dump_cb, cbdata); + } else { + struct netdev_queue_stats stats; + + netdev_get_queue_stats(port->netdev, queue_id, &stats); + put_queue_stats(cbdata, queue_id, &stats); + } +} + +static int +handle_queue_stats_request(struct ofproto *ofproto, struct ofconn *ofconn, + const struct ofp_stats_request *osr, + size_t arg_size) +{ + struct ofp_queue_stats_request *qsr; + struct queue_stats_cbdata cbdata; + struct ofport *port; + unsigned int port_no; + uint32_t queue_id; + + if (arg_size != sizeof *qsr) { + return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN); + } + qsr = (struct ofp_queue_stats_request *) osr->body; + + COVERAGE_INC(ofproto_queue_req); + + cbdata.ofconn = ofconn; + cbdata.msg = start_stats_reply(osr, 128); + + port_no = ntohs(qsr->port_no); + queue_id = ntohl(qsr->queue_id); + if (port_no == OFPP_ALL) { + PORT_ARRAY_FOR_EACH (port, &ofproto->ports, port_no) { + handle_queue_stats_for_port(port, port_no, queue_id, &cbdata); + } + } else if (port_no < ofproto->max_ports) { + port = port_array_get(&ofproto->ports, port_no); + if (port) { + handle_queue_stats_for_port(port, port_no, queue_id, &cbdata); + } + } else { + ofpbuf_delete(cbdata.msg); + return ofp_mkerr(OFPET_QUEUE_OP_FAILED, OFPQOFC_BAD_PORT); + } + queue_tx(cbdata.msg, ofconn, ofconn->reply_counter); + + return 0; +} + static int handle_stats_request(struct ofproto *p, struct ofconn *ofconn, struct ofp_header *oh) @@ -3150,6 +3456,9 @@ handle_stats_request(struct ofproto *p, struct ofconn *ofconn, case OFPST_PORT: return handle_port_stats_request(p, ofconn, osr, arg_size); + case OFPST_QUEUE: + return handle_queue_stats_request(p, ofconn, osr, arg_size); + case OFPST_VENDOR: return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_VENDOR); @@ -3186,8 +3495,7 @@ update_stats(struct ofproto *ofproto, struct rule *rule, update_time(ofproto, rule, stats); rule->packet_count += stats->n_packets; rule->byte_count += stats->n_bytes; - netflow_flow_update_flags(&rule->nf_flow, stats->ip_tos, - stats->tcp_flags); + netflow_flow_update_flags(&rule->nf_flow, stats->tcp_flags); } } @@ -3467,6 +3775,7 @@ static int handle_flow_mod(struct ofproto *p, struct ofconn *ofconn, struct ofp_flow_mod *ofm) { + struct ofp_match orig_match; size_t n_actions; int error; @@ -3488,7 +3797,25 @@ handle_flow_mod(struct ofproto *p, struct ofconn *ofconn, return ofp_mkerr(OFPET_FLOW_MOD_FAILED, OFPFMFC_ALL_TABLES_FULL); } + /* Normalize ofp->match. If normalization actually changes anything, then + * log the differences. */ + ofm->match.pad1[0] = ofm->match.pad2[0] = 0; + orig_match = ofm->match; normalize_match(&ofm->match); + if (memcmp(&ofm->match, &orig_match, sizeof orig_match)) { + static struct vlog_rate_limit normal_rl = VLOG_RATE_LIMIT_INIT(1, 1); + if (!VLOG_DROP_INFO(&normal_rl)) { + char *old = ofp_match_to_literal_string(&orig_match); + char *new = ofp_match_to_literal_string(&ofm->match); + VLOG_INFO("%s: normalization changed ofp_match, details:", + rconn_get_name(ofconn->rconn)); + VLOG_INFO(" pre: %s", old); + VLOG_INFO("post: %s", new); + free(old); + free(new); + } + } + if (!ofm->match.wildcards) { ofm->priority = htons(UINT16_MAX); } @@ -3552,7 +3879,7 @@ handle_role_request(struct ofproto *ofproto, } nrr = (struct nx_role_request *) msg; - if (ofconn->type != OFCONN_CONTROLLER) { + if (ofconn->type != OFCONN_PRIMARY) { VLOG_WARN_RL(&rl, "ignoring role request on non-controller " "connection"); return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_EPERM); @@ -3990,7 +4317,7 @@ active_timeout(struct ofproto *ofproto, struct rule *rule) if (odp_flow.stats.n_packets) { update_time(ofproto, rule, &odp_flow.stats); - netflow_flow_update_flags(&rule->nf_flow, odp_flow.stats.ip_tos, + netflow_flow_update_flags(&rule->nf_flow, odp_flow.stats.tcp_flags); } } @@ -4230,7 +4557,8 @@ default_normal_ofhook_cb(const flow_t *flow, const struct ofpbuf *packet, /* Learn source MAC (but don't try to learn from revalidation). */ if (packet != NULL) { tag_type rev_tag = mac_learning_learn(ofproto->ml, flow->dl_src, - 0, flow->in_port); + 0, flow->in_port, + GRAT_ARP_LOCK_NONE); if (rev_tag) { /* The log messages here could actually be useful in debugging, * so keep the rate limit relatively high. */ @@ -4242,7 +4570,8 @@ default_normal_ofhook_cb(const flow_t *flow, const struct ofpbuf *packet, } /* Determine output port. */ - out_port = mac_learning_lookup_tag(ofproto->ml, flow->dl_dst, 0, tags); + out_port = mac_learning_lookup_tag(ofproto->ml, flow->dl_dst, 0, tags, + NULL); if (out_port < 0) { add_output_group_action(actions, DP_GROUP_FLOOD, nf_output_iface); } else if (out_port != flow->in_port) {