X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=vswitchd%2Fbridge.c;h=31e6cb073452f1efca9408b85836d4aa308f6123;hb=d799ae067f9d442bf15f3ee757346d1f865cbc38;hp=e945ca84ca4312262ef8f0d1201b8d59c54380e4;hpb=70150daf2fd88a84f80044a9589e97942b85c25b;p=openvswitch diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c index e945ca84..31e6cb07 100644 --- a/vswitchd/bridge.c +++ b/vswitchd/bridge.c @@ -36,6 +36,7 @@ #include "dynamic-string.h" #include "flow.h" #include "hash.h" +#include "jsonrpc.h" #include "list.h" #include "mac-learning.h" #include "netdev.h" @@ -137,6 +138,9 @@ struct port { int updelay, downdelay; /* Delay before iface goes up/down, in ms. */ bool bond_compat_is_stale; /* Need to call port_update_bond_compat()? */ bool bond_fake_iface; /* Fake a bond interface for legacy compat? */ + long bond_next_fake_iface_update; /* Next update to fake bond stats. */ + int bond_rebalance_interval; /* Interval between rebalances, in ms. */ + long long int bond_next_rebalance; /* Next rebalancing time. */ /* Port mirroring info. */ mirror_mask_t src_mirrors; /* Mirrors triggered when packet received. */ @@ -155,14 +159,16 @@ struct bridge { bool sent_config_request; /* Successfully sent config request? */ uint8_t default_ea[ETH_ADDR_LEN]; /* Default MAC. */ - /* Support for remote controllers. */ - char *controller; /* NULL if there is no remote controller; - * "discover" to do controller discovery; - * otherwise a vconn name. */ - /* OpenFlow switch processing. */ struct ofproto *ofproto; /* OpenFlow switch. */ + /* Description strings. */ + char *mfr_desc; /* Manufacturer. */ + char *hw_desc; /* Hardware. */ + char *sw_desc; /* Software version. */ + char *serial_desc; /* Serial number. */ + char *dp_desc; /* Datapath description. */ + /* Kernel datapath information. */ struct dpif *dpif; /* Datapath. */ struct port_array ifaces; /* Indexed by kernel datapath port number. */ @@ -173,7 +179,6 @@ struct bridge { /* Bonding. */ bool has_bonded_ports; - long long int bond_next_rebalance; /* Flow tracking. */ bool flush; @@ -199,13 +204,15 @@ static void bridge_destroy(struct bridge *); static struct bridge *bridge_lookup(const char *name); static unixctl_cb_func bridge_unixctl_dump_flows; static int bridge_run_one(struct bridge *); -static const struct ovsrec_controller *bridge_get_controller( - const struct ovsrec_open_vswitch *ovs_cfg, - const struct bridge *br); +static size_t bridge_get_controllers(const struct ovsrec_open_vswitch *ovs_cfg, + const struct bridge *br, + struct ovsrec_controller ***controllersp); static void bridge_reconfigure_one(const struct ovsrec_open_vswitch *, struct bridge *); static void bridge_reconfigure_controller(const struct ovsrec_open_vswitch *, - struct bridge *); + struct bridge *, + const struct sockaddr_in *managers, + size_t n_managers); static void bridge_get_all_ifaces(const struct bridge *, struct shash *ifaces); static void bridge_fetch_dp_ifaces(struct bridge *); static void bridge_flush(struct bridge *); @@ -330,6 +337,7 @@ bridge_init(const struct ovsrec_open_vswitch *cfg) } } } + svec_destroy(&bridge_names); svec_destroy(&dpif_names); svec_destroy(&dpif_types); @@ -341,49 +349,14 @@ bridge_init(const struct ovsrec_open_vswitch *cfg) } #ifdef HAVE_OPENSSL -static bool -config_string_change(const char *value, char **valuep) -{ - if (value && (!*valuep || strcmp(value, *valuep))) { - free(*valuep); - *valuep = xstrdup(value); - return true; - } else { - return false; - } -} - static void bridge_configure_ssl(const struct ovsrec_ssl *ssl) { - /* XXX SSL should be configurable on a per-bridge basis. - * XXX should be possible to de-configure SSL. */ - static char *private_key_file; - static char *certificate_file; - static char *cacert_file; - struct stat s; - - if (!ssl) { - /* XXX We can't un-set SSL settings. */ - return; - } - - if (config_string_change(ssl->private_key, &private_key_file)) { - stream_ssl_set_private_key_file(private_key_file); - } - - if (config_string_change(ssl->certificate, &certificate_file)) { - stream_ssl_set_certificate_file(certificate_file); - } - - /* We assume that even if the filename hasn't changed, if the CA cert - * file has been removed, that we want to move back into - * boot-strapping mode. This opens a small security hole, because - * the old certificate will still be trusted until vSwitch is - * restarted. We may want to address this in vconn's SSL library. */ - if (config_string_change(ssl->ca_cert, &cacert_file) - || (cacert_file && stat(cacert_file, &s) && errno == ENOENT)) { - stream_ssl_set_ca_cert_file(cacert_file, ssl->bootstrap_ca_cert); + /* XXX SSL should be configurable on a per-bridge basis. */ + if (ssl) { + stream_ssl_set_private_key_file(ssl->private_key); + stream_ssl_set_certificate_file(ssl->certificate); + stream_ssl_set_ca_cert_file(ssl->ca_cert, ssl->bootstrap_ca_cert); } } #endif @@ -410,7 +383,12 @@ set_up_iface(const struct ovsrec_interface *iface_cfg, struct iface *iface, memset(&netdev_options, 0, sizeof netdev_options); netdev_options.name = iface_cfg->name; - netdev_options.type = iface_cfg->type; + if (!strcmp(iface_cfg->type, "internal")) { + /* An "internal" config type maps to a netdev "system" type. */ + netdev_options.type = "system"; + } else { + netdev_options.type = iface_cfg->type; + } netdev_options.args = &options; netdev_options.ethertype = NETDEV_ETH_TYPE_NONE; netdev_options.may_create = true; @@ -428,6 +406,11 @@ set_up_iface(const struct ovsrec_interface *iface_cfg, struct iface *iface, const char *iface_type = iface_cfg->type && strlen(iface_cfg->type) ? iface_cfg->type : NULL; + /* An "internal" config type maps to a netdev "system" type. */ + if (iface_type && !strcmp(iface_type, "internal")) { + iface_type = "system"; + } + if (!iface_type || !strcmp(netdev_type, iface_type)) { error = netdev_reconfigure(iface->netdev, &options); } else { @@ -452,8 +435,8 @@ reconfigure_iface(const struct ovsrec_interface *iface_cfg, struct iface *iface) } static bool -check_iface_netdev(struct bridge *br UNUSED, struct iface *iface, - void *aux UNUSED) +check_iface_netdev(struct bridge *br OVS_UNUSED, struct iface *iface, + void *aux OVS_UNUSED) { if (!iface->netdev) { int error = set_up_iface(iface->cfg, iface, true); @@ -468,7 +451,8 @@ check_iface_netdev(struct bridge *br UNUSED, struct iface *iface, } static bool -check_iface_dp_ifidx(struct bridge *br, struct iface *iface, void *aux UNUSED) +check_iface_dp_ifidx(struct bridge *br, struct iface *iface, + void *aux OVS_UNUSED) { if (iface->dp_ifidx >= 0) { VLOG_DBG("%s has interface %s on port %d", @@ -483,8 +467,8 @@ check_iface_dp_ifidx(struct bridge *br, struct iface *iface, void *aux UNUSED) } static bool -set_iface_properties(struct bridge *br UNUSED, struct iface *iface, - void *aux UNUSED) +set_iface_properties(struct bridge *br OVS_UNUSED, struct iface *iface, + void *aux OVS_UNUSED) { /* Set policing attributes. */ netdev_set_policing(iface->netdev, @@ -532,6 +516,44 @@ iterate_and_prune_ifaces(struct bridge *br, } } +/* Looks at the list of managers in 'ovs_cfg' and extracts their remote IP + * addresses and ports into '*managersp' and '*n_managersp'. The caller is + * responsible for freeing '*managersp' (with free()). + * + * You may be asking yourself "why does ovs-vswitchd care?", because + * ovsdb-server is responsible for connecting to the managers, and ovs-vswitchd + * should not be and in fact is not directly involved in that. But + * ovs-vswitchd needs to make sure that ovsdb-server can reach the managers, so + * it has to tell in-band control where the managers are to enable that. + */ +static void +collect_managers(const struct ovsrec_open_vswitch *ovs_cfg, + struct sockaddr_in **managersp, size_t *n_managersp) +{ + struct sockaddr_in *managers = NULL; + size_t n_managers = 0; + + if (ovs_cfg->n_managers > 0) { + size_t i; + + managers = xmalloc(ovs_cfg->n_managers * sizeof *managers); + for (i = 0; i < ovs_cfg->n_managers; i++) { + const char *name = ovs_cfg->managers[i]; + struct sockaddr_in *sin = &managers[i]; + + if ((!strncmp(name, "tcp:", 4) + && inet_parse_active(name + 4, JSONRPC_TCP_PORT, sin)) || + (!strncmp(name, "ssl:", 4) + && inet_parse_active(name + 4, JSONRPC_SSL_PORT, sin))) { + n_managers++; + } + } + } + + *managersp = managers; + *n_managersp = n_managers; +} + void bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg) { @@ -539,6 +561,8 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg) struct shash old_br, new_br; struct shash_node *node; struct bridge *br, *next; + struct sockaddr_in *managers; + size_t n_managers; size_t i; int sflow_bridge_number; @@ -546,6 +570,8 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg) txn = ovsdb_idl_txn_create(ovs_cfg->header_.table->idl); + collect_managers(ovs_cfg, &managers, &n_managers); + /* Collect old and new bridges. */ shash_init(&old_br); shash_init(&new_br); @@ -759,8 +785,10 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg) /* Set sFlow configuration on this bridge. */ if (br->cfg->sflow) { const struct ovsrec_sflow *sflow_cfg = br->cfg->sflow; - const struct ovsrec_controller *ctrl; + struct ovsrec_controller **controllers; struct ofproto_sflow_options oso; + size_t n_controllers; + size_t i; memset(&oso, 0, sizeof oso); @@ -785,8 +813,14 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg) oso.sub_id = sflow_bridge_number++; oso.agent_device = sflow_cfg->agent; - ctrl = bridge_get_controller(ovs_cfg, br); - oso.control_ip = ctrl ? ctrl->local_ip : NULL; + oso.control_ip = NULL; + n_controllers = bridge_get_controllers(ovs_cfg, br, &controllers); + for (i = 0; i < n_controllers; i++) { + if (controllers[i]->local_ip) { + oso.control_ip = controllers[i]->local_ip; + break; + } + } ofproto_set_sflow(br->ofproto, &oso); svec_destroy(&oso.targets); @@ -803,7 +837,7 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg) * yet; when a controller is configured, resetting the datapath ID will * immediately disconnect from the controller, so it's better to set * the datapath ID before the controller. */ - bridge_reconfigure_controller(ovs_cfg, br); + bridge_reconfigure_controller(ovs_cfg, br, managers, n_managers); } LIST_FOR_EACH (br, struct bridge, node, &all_bridges) { for (i = 0; i < br->n_ports; i++) { @@ -821,21 +855,32 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg) ovsdb_idl_txn_commit(txn); ovsdb_idl_txn_destroy(txn); /* XXX */ + + free(managers); } static const char * -bridge_get_other_config(const struct ovsrec_bridge *br_cfg, const char *key) +get_ovsrec_key_value(const char *key, char **keys, char **values, size_t n) { size_t i; - for (i = 0; i < br_cfg->n_other_config; i++) { - if (!strcmp(br_cfg->key_other_config[i], key)) { - return br_cfg->value_other_config[i]; + for (i = 0; i < n; i++) { + if (!strcmp(keys[i], key)) { + return values[i]; } } return NULL; } +static const char * +bridge_get_other_config(const struct ovsrec_bridge *br_cfg, const char *key) +{ + return get_ovsrec_key_value(key, + br_cfg->key_other_config, + br_cfg->value_other_config, + br_cfg->n_other_config); +} + static void bridge_pick_local_hw_addr(struct bridge *br, uint8_t ea[ETH_ADDR_LEN], struct iface **hw_addr_iface) @@ -1057,7 +1102,7 @@ bridge_wait(void) LIST_FOR_EACH (br, struct bridge, node, &all_bridges) { ofproto_wait(br->ofproto); - if (br->controller) { + if (ofproto_has_controller(br->ofproto)) { continue; } @@ -1099,7 +1144,7 @@ bridge_get_local_iface(struct bridge *br) /* Bridge unixctl user interface functions. */ static void bridge_unixctl_fdb_show(struct unixctl_conn *conn, - const char *args, void *aux UNUSED) + const char *args, void *aux OVS_UNUSED) { struct ds ds = DS_EMPTY_INITIALIZER; const struct bridge *br; @@ -1162,7 +1207,6 @@ bridge_create(const struct ovsrec_bridge *br_cfg) port_array_init(&br->ifaces); br->flush = false; - br->bond_next_rebalance = time_msec() + 10000; list_push_back(&all_bridges, &br->node); @@ -1188,7 +1232,6 @@ bridge_destroy(struct bridge *br) } dpif_close(br->dpif); ofproto_destroy(br->ofproto); - free(br->controller); mac_learning_destroy(br->ml); port_array_destroy(&br->ifaces); free(br->ports); @@ -1227,7 +1270,7 @@ bridge_get_datapathid(const char *name) * stack, including those normally hidden. */ static void bridge_unixctl_dump_flows(struct unixctl_conn *conn, - const char *args, void *aux UNUSED) + const char *args, void *aux OVS_UNUSED) { struct bridge *br; struct ds results; @@ -1264,21 +1307,31 @@ bridge_run_one(struct bridge *br) return error; } -static const struct ovsrec_controller * -bridge_get_controller(const struct ovsrec_open_vswitch *ovs_cfg, - const struct bridge *br) +static size_t +bridge_get_controllers(const struct ovsrec_open_vswitch *ovs_cfg, + const struct bridge *br, + struct ovsrec_controller ***controllersp) { - const struct ovsrec_controller *controller; + struct ovsrec_controller **controllers; + size_t n_controllers; - controller = (br->cfg->controller ? br->cfg->controller - : ovs_cfg->controller ? ovs_cfg->controller - : NULL); + if (br->cfg->n_controller) { + controllers = br->cfg->controller; + n_controllers = br->cfg->n_controller; + } else { + controllers = ovs_cfg->controller; + n_controllers = ovs_cfg->n_controller; + } - if (controller && !strcmp(controller->target, "none")) { - return NULL; + if (n_controllers == 1 && !strcmp(controllers[0]->target, "none")) { + controllers = NULL; + n_controllers = 0; } - return controller; + if (controllersp) { + *controllersp = controllers; + } + return n_controllers; } static bool @@ -1297,6 +1350,75 @@ check_duplicate_ifaces(struct bridge *br, struct iface *iface, void *ifaces_) } } +static void +bridge_update_desc(struct bridge *br OVS_UNUSED) +{ +#if 0 + bool changed = false; + const char *desc; + + desc = cfg_get_string(0, "bridge.%s.mfr-desc", br->name); + if (desc != br->mfr_desc) { + free(br->mfr_desc); + if (desc) { + br->mfr_desc = xstrdup(desc); + } else { + br->mfr_desc = xstrdup(DEFAULT_MFR_DESC); + } + changed = true; + } + + desc = cfg_get_string(0, "bridge.%s.hw-desc", br->name); + if (desc != br->hw_desc) { + free(br->hw_desc); + if (desc) { + br->hw_desc = xstrdup(desc); + } else { + br->hw_desc = xstrdup(DEFAULT_HW_DESC); + } + changed = true; + } + + desc = cfg_get_string(0, "bridge.%s.sw-desc", br->name); + if (desc != br->sw_desc) { + free(br->sw_desc); + if (desc) { + br->sw_desc = xstrdup(desc); + } else { + br->sw_desc = xstrdup(DEFAULT_SW_DESC); + } + changed = true; + } + + desc = cfg_get_string(0, "bridge.%s.serial-desc", br->name); + if (desc != br->serial_desc) { + free(br->serial_desc); + if (desc) { + br->serial_desc = xstrdup(desc); + } else { + br->serial_desc = xstrdup(DEFAULT_SERIAL_DESC); + } + changed = true; + } + + desc = cfg_get_string(0, "bridge.%s.dp-desc", br->name); + if (desc != br->dp_desc) { + free(br->dp_desc); + if (desc) { + br->dp_desc = xstrdup(desc); + } else { + br->dp_desc = xstrdup(DEFAULT_DP_DESC); + } + changed = true; + } + + if (changed) { + ofproto_set_desc(br->ofproto, br->mfr_desc, br->hw_desc, + br->sw_desc, br->serial_desc, br->dp_desc); + } +#endif +} + static void bridge_reconfigure_one(const struct ovsrec_open_vswitch *ovs_cfg, struct bridge *br) @@ -1328,7 +1450,7 @@ bridge_reconfigure_one(const struct ovsrec_open_vswitch *ovs_cfg, * user didn't specify one. * * XXX perhaps we should synthesize a port ourselves in this case. */ - if (bridge_get_controller(ovs_cfg, br)) { + if (bridge_get_controllers(ovs_cfg, br, NULL)) { char local_name[IF_NAMESIZE]; int error; @@ -1435,91 +1557,33 @@ bridge_reconfigure_one(const struct ovsrec_open_vswitch *ovs_cfg, #endif mirror_reconfigure(br); + + bridge_update_desc(br); } static void bridge_reconfigure_controller(const struct ovsrec_open_vswitch *ovs_cfg, - struct bridge *br) + struct bridge *br, + const struct sockaddr_in *managers, + size_t n_managers) { - char *pfx = xasprintf("bridge.%s.controller", br->name); - const struct ovsrec_controller *c; + struct ovsrec_controller **controllers; + size_t n_controllers; - c = bridge_get_controller(ovs_cfg, br); - if ((br->controller != NULL) != (c != NULL)) { + ofproto_set_extra_in_band_remotes(br->ofproto, managers, n_managers); + + n_controllers = bridge_get_controllers(ovs_cfg, br, &controllers); + if (ofproto_has_controller(br->ofproto) != (n_controllers != 0)) { ofproto_flush_flows(br->ofproto); } - free(br->controller); - br->controller = c ? xstrdup(c->target) : NULL; - - if (c) { - int max_backoff, probe; - int rate_limit, burst_limit; - - if (!strcmp(c->target, "discover")) { - ofproto_set_discovery(br->ofproto, true, - c->discover_accept_regex, - c->discover_update_resolv_conf); - } else { - struct iface *local_iface; - struct in_addr ip; - bool in_band; - - in_band = (!c->connection_mode - || !strcmp(c->connection_mode, "out-of-band")); - ofproto_set_discovery(br->ofproto, false, NULL, NULL); - ofproto_set_in_band(br->ofproto, in_band); - - local_iface = bridge_get_local_iface(br); - if (local_iface && c->local_ip && inet_aton(c->local_ip, &ip)) { - struct netdev *netdev = local_iface->netdev; - struct in_addr ip, mask, gateway; - - if (!c->local_netmask || !inet_aton(c->local_netmask, &mask)) { - mask.s_addr = 0; - } - if (!c->local_gateway - || !inet_aton(c->local_gateway, &gateway)) { - gateway.s_addr = 0; - } - - netdev_turn_flags_on(netdev, NETDEV_UP, true); - if (!mask.s_addr) { - mask.s_addr = guess_netmask(ip.s_addr); - } - if (!netdev_set_in4(netdev, ip, mask)) { - VLOG_INFO("bridge %s: configured IP address "IP_FMT", " - "netmask "IP_FMT, - br->name, IP_ARGS(&ip.s_addr), - IP_ARGS(&mask.s_addr)); - } - if (gateway.s_addr) { - if (!netdev_add_router(netdev, gateway)) { - VLOG_INFO("bridge %s: configured gateway "IP_FMT, - br->name, IP_ARGS(&gateway.s_addr)); - } - } - } - } - - ofproto_set_failure(br->ofproto, - (!c->fail_mode - || !strcmp(c->fail_mode, "standalone") - || !strcmp(c->fail_mode, "open"))); - - probe = c->inactivity_probe ? *c->inactivity_probe / 1000 : 5; - ofproto_set_probe_interval(br->ofproto, probe); - - max_backoff = c->max_backoff ? *c->max_backoff / 1000 : 8; - ofproto_set_max_backoff(br->ofproto, max_backoff); - - rate_limit = c->controller_rate_limit ? *c->controller_rate_limit : 0; - burst_limit = c->controller_burst_limit ? *c->controller_burst_limit : 0; - ofproto_set_rate_limit(br->ofproto, rate_limit, burst_limit); - } else { + if (!n_controllers) { union ofp_action action; flow_t flow; + /* Clear out controllers. */ + ofproto_set_controllers(br->ofproto, NULL, 0); + /* Set up a flow that matches every packet and directs them to * OFPP_NORMAL (which goes to us). */ memset(&action, 0, sizeof action); @@ -1527,17 +1591,78 @@ bridge_reconfigure_controller(const struct ovsrec_open_vswitch *ovs_cfg, action.output.len = htons(sizeof action); action.output.port = htons(OFPP_NORMAL); memset(&flow, 0, sizeof flow); - ofproto_add_flow(br->ofproto, &flow, OFPFW_ALL, 0, - &action, 1, 0); + ofproto_add_flow(br->ofproto, &flow, OVSFW_ALL, 0, &action, 1, 0); + } else { + struct ofproto_controller *ocs; + size_t i; - ofproto_set_in_band(br->ofproto, false); - ofproto_set_max_backoff(br->ofproto, 1); - ofproto_set_probe_interval(br->ofproto, 5); - ofproto_set_failure(br->ofproto, false); - } - free(pfx); + ocs = xmalloc(n_controllers * sizeof *ocs); + for (i = 0; i < n_controllers; i++) { + struct ovsrec_controller *c = controllers[i]; + struct ofproto_controller *oc = &ocs[i]; + + if (strcmp(c->target, "discover")) { + struct iface *local_iface; + struct in_addr ip; + + local_iface = bridge_get_local_iface(br); + if (local_iface && c->local_ip + && inet_aton(c->local_ip, &ip)) { + struct netdev *netdev = local_iface->netdev; + struct in_addr mask, gateway; + + if (!c->local_netmask + || !inet_aton(c->local_netmask, &mask)) { + mask.s_addr = 0; + } + if (!c->local_gateway + || !inet_aton(c->local_gateway, &gateway)) { + gateway.s_addr = 0; + } + + netdev_turn_flags_on(netdev, NETDEV_UP, true); + if (!mask.s_addr) { + mask.s_addr = guess_netmask(ip.s_addr); + } + if (!netdev_set_in4(netdev, ip, mask)) { + VLOG_INFO("bridge %s: configured IP address "IP_FMT", " + "netmask "IP_FMT, + br->name, IP_ARGS(&ip.s_addr), + IP_ARGS(&mask.s_addr)); + } + + if (gateway.s_addr) { + if (!netdev_add_router(netdev, gateway)) { + VLOG_INFO("bridge %s: configured gateway "IP_FMT, + br->name, IP_ARGS(&gateway.s_addr)); + } + } + } + } - ofproto_set_controller(br->ofproto, br->controller); + oc->target = c->target; + oc->max_backoff = c->max_backoff ? *c->max_backoff / 1000 : 8; + oc->probe_interval = (c->inactivity_probe + ? *c->inactivity_probe / 1000 : 5); + oc->fail = (!c->fail_mode + || !strcmp(c->fail_mode, "standalone") + || !strcmp(c->fail_mode, "open") + ? OFPROTO_FAIL_STANDALONE + : OFPROTO_FAIL_SECURE); + oc->band = (!c->connection_mode + || !strcmp(c->connection_mode, "in-band") + ? OFPROTO_IN_BAND + : OFPROTO_OUT_OF_BAND); + oc->accept_re = c->discover_accept_regex; + oc->update_resolv_conf = c->discover_update_resolv_conf; + oc->rate_limit = (c->controller_rate_limit + ? *c->controller_rate_limit : 0); + oc->burst_limit = (c->controller_burst_limit + ? *c->controller_burst_limit : 0); + } + ofproto_set_controllers(br->ofproto, ocs, n_controllers); + free(ocs); + } } static void @@ -1786,6 +1911,34 @@ bond_enable_slave(struct iface *iface, bool enable) port->bond_compat_is_stale = true; } +/* Attempts to make the sum of the bond slaves' statistics appear on the fake + * bond interface. */ +static void +bond_update_fake_iface_stats(struct port *port) +{ + struct netdev_stats bond_stats; + struct netdev *bond_dev; + size_t i; + + memset(&bond_stats, 0, sizeof bond_stats); + + for (i = 0; i < port->n_ifaces; i++) { + struct netdev_stats slave_stats; + + if (!netdev_get_stats(port->ifaces[i]->netdev, &slave_stats)) { + bond_stats.rx_packets += slave_stats.rx_packets; + bond_stats.rx_bytes += slave_stats.rx_bytes; + bond_stats.tx_packets += slave_stats.tx_packets; + bond_stats.tx_bytes += slave_stats.tx_bytes; + } + } + + if (!netdev_open_default(port->name, &bond_dev)) { + netdev_set_stats(bond_dev, &bond_stats); + netdev_close(bond_dev); + } +} + static void bond_run(struct bridge *br) { @@ -1801,6 +1954,12 @@ bond_run(struct bridge *br) bond_enable_slave(iface, !iface->enabled); } } + + if (port->bond_fake_iface + && time_msec() >= port->bond_next_fake_iface_update) { + bond_update_fake_iface_stats(port); + port->bond_next_fake_iface_update = time_msec() + 1000; + } } if (port->bond_compat_is_stale) { @@ -1826,6 +1985,9 @@ bond_wait(struct bridge *br) poll_timer_wait(iface->delay_expires - time_msec()); } } + if (port->bond_fake_iface) { + poll_timer_wait(port->bond_next_fake_iface_update - time_msec()); + } } } @@ -1994,7 +2156,7 @@ compose_dsts(const struct bridge *br, const flow_t *flow, uint16_t vlan, return dst - dsts; } -static void UNUSED +static void OVS_UNUSED print_dsts(const struct dst *dsts, size_t n) { for (; n--; dsts++) { @@ -2109,25 +2271,39 @@ is_bcast_arp_reply(const flow_t *flow) && eth_addr_is_broadcast(flow->dl_dst)); } -/* If the composed actions may be applied to any packet in the given 'flow', - * returns true. Otherwise, the actions should only be applied to 'packet', or - * not at all, if 'packet' was NULL. */ +/* Determines whether packets in 'flow' within 'br' should be forwarded or + * dropped. Returns true if they may be forwarded, false if they should be + * dropped. + * + * If 'have_packet' is true, it indicates that the caller is processing a + * received packet. If 'have_packet' is false, then the caller is just + * revalidating an existing flow because configuration has changed. Either + * way, 'have_packet' only affects logging (there is no point in logging errors + * during revalidation). + * + * Sets '*in_portp' to the input port. This will be a null pointer if + * flow->in_port does not designate a known input port (in which case + * is_admissible() returns false). + * + * When returning true, sets '*vlanp' to the effective VLAN of the input + * packet, as returned by flow_get_vlan(). + * + * May also add tags to '*tags', although the current implementation only does + * so in one special case. + */ static bool -process_flow(struct bridge *br, const flow_t *flow, - const struct ofpbuf *packet, struct odp_actions *actions, - tag_type *tags, uint16_t *nf_output_iface) +is_admissible(struct bridge *br, const flow_t *flow, bool have_packet, + tag_type *tags, int *vlanp, struct port **in_portp) { struct iface *in_iface; struct port *in_port; - struct port *out_port = NULL; /* By default, drop the packet/flow. */ int vlan; - int out_port_idx; /* Find the interface and port structure for the received packet. */ in_iface = iface_from_dp_ifidx(br, flow->in_port); if (!in_iface) { /* No interface? Something fishy... */ - if (packet != NULL) { + if (have_packet) { /* Odd. A few possible reasons here: * * - We deleted an interface but there are still a few packets @@ -2145,27 +2321,29 @@ process_flow(struct bridge *br, const flow_t *flow, "interface %"PRIu16, br->name, flow->in_port); } - /* Return without adding any actions, to drop packets on this flow. */ - return true; + *in_portp = NULL; + return false; } - in_port = in_iface->port; - vlan = flow_get_vlan(br, flow, in_port, !!packet); + *in_portp = in_port = in_iface->port; + *vlanp = vlan = flow_get_vlan(br, flow, in_port, have_packet); if (vlan < 0) { - goto done; + return false; } /* Drop frames for reserved multicast addresses. */ if (eth_addr_is_reserved(flow->dl_dst)) { - goto done; + return false; } /* Drop frames on ports reserved for mirroring. */ if (in_port->is_mirror_output_port) { - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); - VLOG_WARN_RL(&rl, "bridge %s: dropping packet received on port %s, " - "which is reserved exclusively for mirroring", - br->name, in_port->name); - goto done; + if (have_packet) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); + VLOG_WARN_RL(&rl, "bridge %s: dropping packet received on port " + "%s, which is reserved exclusively for mirroring", + br->name, in_port->name); + } + return false; } /* Packets received on bonds need special attention to avoid duplicates. */ @@ -2176,7 +2354,7 @@ process_flow(struct bridge *br, const flow_t *flow, *tags |= in_port->active_iface_tag; if (in_port->active_iface != in_iface->port_ifidx) { /* Drop all multicast packets on inactive slaves. */ - goto done; + return false; } } @@ -2187,20 +2365,39 @@ process_flow(struct bridge *br, const flow_t *flow, src_idx = mac_learning_lookup(br->ml, flow->dl_src, vlan); if (src_idx != -1 && src_idx != in_port->port_idx && !is_bcast_arp_reply(flow)) { - goto done; + return false; } } - /* MAC learning. */ - out_port = FLOOD_PORT; + return true; +} + +/* If the composed actions may be applied to any packet in the given 'flow', + * returns true. Otherwise, the actions should only be applied to 'packet', or + * not at all, if 'packet' was NULL. */ +static bool +process_flow(struct bridge *br, const flow_t *flow, + const struct ofpbuf *packet, struct odp_actions *actions, + tag_type *tags, uint16_t *nf_output_iface) +{ + struct port *in_port; + struct port *out_port; + int vlan; + int out_port_idx; + + /* Check whether we should drop packets in this flow. */ + if (!is_admissible(br, flow, packet != NULL, tags, &vlan, &in_port)) { + out_port = NULL; + goto done; + } + /* Learn source MAC (but don't try to learn from revalidation). */ if (packet) { update_learning_table(br, flow, vlan, in_port); } /* Determine output port. */ - out_port_idx = mac_learning_lookup_tag(br->ml, flow->dl_dst, vlan, - tags); + out_port_idx = mac_learning_lookup_tag(br->ml, flow->dl_dst, vlan, tags); if (out_port_idx >= 0 && out_port_idx < br->n_ports) { out_port = br->ports[out_port_idx]; } else if (!packet && !eth_addr_is_multicast(flow->dl_dst)) { @@ -2210,6 +2407,8 @@ process_flow(struct bridge *br, const flow_t *flow, * on a bond and blackhole packets before the learning table is * updated to reflect the correct port. */ return false; + } else { + out_port = FLOOD_PORT; } /* Don't send packets out their input ports. */ @@ -2218,8 +2417,10 @@ process_flow(struct bridge *br, const flow_t *flow, } done: - compose_actions(br, flow, vlan, in_port, out_port, tags, actions, - nf_output_iface); + if (in_port) { + compose_actions(br, flow, vlan, in_port, out_port, tags, actions, + nf_output_iface); + } return true; } @@ -2279,18 +2480,16 @@ bridge_account_flow_ofhook_cb(const flow_t *flow, void *br_) { struct bridge *br = br_; - struct port *in_port; const union odp_action *a; + struct port *in_port; + tag_type tags = 0; + int vlan; /* Feed information from the active flows back into the learning table * to ensure that table is always in sync with what is actually flowing * through the datapath. */ - in_port = port_from_dp_ifidx(br, flow->in_port); - if (in_port) { - int vlan = flow_get_vlan(br, flow, in_port, false); - if (vlan >= 0) { - update_learning_table(br, flow, vlan, in_port); - } + if (is_admissible(br, flow, false, &tags, &vlan, &in_port)) { + update_learning_table(br, flow, vlan, in_port); } if (!br->has_bonded_ports) { @@ -2313,22 +2512,18 @@ static void bridge_account_checkpoint_ofhook_cb(void *br_) { struct bridge *br = br_; + long long int now; size_t i; if (!br->has_bonded_ports) { return; } - /* The current ofproto implementation calls this callback at least once a - * second, so this timer implementation is sufficient. */ - if (time_msec() < br->bond_next_rebalance) { - return; - } - br->bond_next_rebalance = time_msec() + 10000; - + now = time_msec(); for (i = 0; i < br->n_ports; i++) { struct port *port = br->ports[i]; - if (port->n_ifaces > 1) { + if (port->n_ifaces > 1 && now >= port->bond_next_rebalance) { + port->bond_next_rebalance = now + port->bond_rebalance_interval; bond_rebalance_port(port); } } @@ -2684,7 +2879,7 @@ bond_send_learning_packets(struct port *port) n_packets++; compose_benign_packet(&packet, "Open vSwitch Bond Failover", 0xf177, e->mac); - flow_extract(&packet, ODPP_NONE, &flow); + flow_extract(&packet, 0, ODPP_NONE, &flow); retval = ofproto_send_packet(br->ofproto, &flow, actions, a - actions, &packet); if (retval) { @@ -2709,7 +2904,7 @@ bond_send_learning_packets(struct port *port) static void bond_unixctl_list(struct unixctl_conn *conn, - const char *args UNUSED, void *aux UNUSED) + const char *args OVS_UNUSED, void *aux OVS_UNUSED) { struct ds ds = DS_EMPTY_INITIALIZER; const struct bridge *br; @@ -2760,7 +2955,7 @@ bond_find(const char *name) static void bond_unixctl_show(struct unixctl_conn *conn, - const char *args, void *aux UNUSED) + const char *args, void *aux OVS_UNUSED) { struct ds ds = DS_EMPTY_INITIALIZER; const struct port *port; @@ -2775,7 +2970,7 @@ bond_unixctl_show(struct unixctl_conn *conn, ds_put_format(&ds, "updelay: %d ms\n", port->updelay); ds_put_format(&ds, "downdelay: %d ms\n", port->downdelay); ds_put_format(&ds, "next rebalance: %lld ms\n", - port->bridge->bond_next_rebalance - time_msec()); + port->bond_next_rebalance - time_msec()); for (j = 0; j < port->n_ifaces; j++) { const struct iface *iface = port->ifaces[j]; struct bond_entry *be; @@ -2826,7 +3021,7 @@ bond_unixctl_show(struct unixctl_conn *conn, static void bond_unixctl_migrate(struct unixctl_conn *conn, const char *args_, - void *aux UNUSED) + void *aux OVS_UNUSED) { char *args = (char *) args_; char *save_ptr = NULL; @@ -2883,7 +3078,7 @@ bond_unixctl_migrate(struct unixctl_conn *conn, const char *args_, static void bond_unixctl_set_active_slave(struct unixctl_conn *conn, const char *args_, - void *aux UNUSED) + void *aux OVS_UNUSED) { char *args = (char *) args_; char *save_ptr = NULL; @@ -2964,21 +3159,21 @@ enable_slave(struct unixctl_conn *conn, const char *args_, bool enable) static void bond_unixctl_enable_slave(struct unixctl_conn *conn, const char *args, - void *aux UNUSED) + void *aux OVS_UNUSED) { enable_slave(conn, args, true); } static void bond_unixctl_disable_slave(struct unixctl_conn *conn, const char *args, - void *aux UNUSED) + void *aux OVS_UNUSED) { enable_slave(conn, args, false); } static void bond_unixctl_hash(struct unixctl_conn *conn, const char *args, - void *aux UNUSED) + void *aux OVS_UNUSED) { uint8_t mac[ETH_ADDR_LEN]; uint8_t hash; @@ -3038,10 +3233,22 @@ port_create(struct bridge *br, const char *name) return port; } +static const char * +get_port_other_config(const struct ovsrec_port *port, const char *key, + const char *default_value) +{ + const char *value = get_ovsrec_key_value(key, + port->key_other_config, + port->value_other_config, + port->n_other_config); + return value ? value : default_value; +} + static void port_reconfigure(struct port *port, const struct ovsrec_port *cfg) { struct shash old_ifaces, new_ifaces; + long long int next_rebalance; struct shash_node *node; unsigned long *trunks; int vlan; @@ -3070,6 +3277,15 @@ port_reconfigure(struct port *port, const struct ovsrec_port *cfg) if (port->downdelay < 0) { port->downdelay = 0; } + port->bond_rebalance_interval = atoi( + get_port_other_config(cfg, "bond-rebalance-interval", "10000")); + if (port->bond_rebalance_interval < 1000) { + port->bond_rebalance_interval = 1000; + } + next_rebalance = time_msec() + port->bond_rebalance_interval; + if (port->bond_next_rebalance > next_rebalance) { + port->bond_next_rebalance = next_rebalance; + } /* Get rid of deleted interfaces and add new interfaces. */ SHASH_FOR_EACH (node, &old_ifaces) { @@ -3083,7 +3299,7 @@ port_reconfigure(struct port *port, const struct ovsrec_port *cfg) iface = shash_find_data(&old_ifaces, if_cfg->name); if (!iface) { - iface = iface_create(port, if_cfg); + iface_create(port, if_cfg); } else { iface->cfg = if_cfg; } @@ -3247,6 +3463,12 @@ port_update_bonding(struct port *port) } port->no_ifaces_tag = tag_create_random(); bond_choose_active_iface(port); + port->bond_next_rebalance + = time_msec() + port->bond_rebalance_interval; + + if (port->cfg->bond_fake_iface) { + port->bond_next_fake_iface_update = time_msec(); + } } port->bond_compat_is_stale = true; port->bond_fake_iface = port->cfg->bond_fake_iface; @@ -3680,7 +3902,7 @@ mirror_collect_vlans(struct mirror *m, const struct ovsrec_mirror *cfg, size_t n_vlans; size_t i; - *vlans = xmalloc(sizeof *vlans * cfg->n_select_vlan); + *vlans = xmalloc(sizeof **vlans * cfg->n_select_vlan); n_vlans = 0; for (i = 0; i < cfg->n_select_vlan; i++) { int64_t vlan = cfg->select_vlan[i]; @@ -3730,9 +3952,6 @@ mirror_reconfigure_one(struct mirror *m, struct ovsrec_mirror *cfg) size_t n_vlans; int *vlans; size_t i; - bool mirror_all_ports; - bool any_ports_specified; - bool any_vlans_specified; /* Get output port. */ if (cfg->output_port) { @@ -3760,30 +3979,25 @@ mirror_reconfigure_one(struct mirror *m, struct ovsrec_mirror *cfg) return; } - /* Get all the ports, and drop duplicates and ports that don't exist. */ shash_init(&src_ports); shash_init(&dst_ports); - mirror_collect_ports(m, cfg->select_src_port, cfg->n_select_src_port, - &src_ports); - mirror_collect_ports(m, cfg->select_dst_port, cfg->n_select_dst_port, - &dst_ports); - any_ports_specified = cfg->n_select_dst_port || cfg->n_select_dst_port; - if (any_ports_specified - && shash_is_empty(&src_ports) && shash_is_empty(&dst_ports)) { - VLOG_ERR("bridge %s: disabling mirror %s since none of the specified " - "selection ports exists", m->bridge->name, m->name); - mirror_destroy(m); - goto exit; - } + if (cfg->select_all) { + for (i = 0; i < m->bridge->n_ports; i++) { + const char *name = m->bridge->ports[i]->name; + shash_add_once(&src_ports, name, NULL); + shash_add_once(&dst_ports, name, NULL); + } + vlans = NULL; + n_vlans = 0; + } else { + /* Get ports, and drop duplicates and ports that don't exist. */ + mirror_collect_ports(m, cfg->select_src_port, cfg->n_select_src_port, + &src_ports); + mirror_collect_ports(m, cfg->select_dst_port, cfg->n_select_dst_port, + &dst_ports); - /* Get all the vlans, and drop duplicate and invalid vlans. */ - n_vlans = mirror_collect_vlans(m, cfg, &vlans); - any_vlans_specified = cfg->n_select_vlan > 0; - if (any_vlans_specified && !n_vlans) { - VLOG_ERR("bridge %s: disabling mirror %s since none of the specified " - "VLANs exists", m->bridge->name, m->name); - mirror_destroy(m); - goto exit; + /* Get all the vlans, and drop duplicate and invalid vlans. */ + n_vlans = mirror_collect_vlans(m, cfg, &vlans); } /* Update mirror data. */ @@ -3803,16 +4017,12 @@ mirror_reconfigure_one(struct mirror *m, struct ovsrec_mirror *cfg) m->out_port = out_port; m->out_vlan = out_vlan; - /* If no selection criteria have been given, mirror for all ports. */ - mirror_all_ports = !any_ports_specified && !any_vlans_specified; - /* Update ports. */ mirror_bit = MIRROR_MASK_C(1) << m->idx; for (i = 0; i < m->bridge->n_ports; i++) { struct port *port = m->bridge->ports[i]; - if (mirror_all_ports - || shash_find(&m->src_ports, port->name) + if (shash_find(&m->src_ports, port->name) || (m->n_vlans && (!port->vlan ? port_trunks_any_mirrored_vlan(m, port) @@ -3822,7 +4032,7 @@ mirror_reconfigure_one(struct mirror *m, struct ovsrec_mirror *cfg) port->src_mirrors &= ~mirror_bit; } - if (mirror_all_ports || shash_find(&m->dst_ports, port->name)) { + if (shash_find(&m->dst_ports, port->name)) { port->dst_mirrors |= mirror_bit; } else { port->dst_mirrors &= ~mirror_bit; @@ -3830,7 +4040,6 @@ mirror_reconfigure_one(struct mirror *m, struct ovsrec_mirror *cfg) } /* Clean up. */ -exit: shash_destroy(&src_ports); shash_destroy(&dst_ports); }