X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=vswitchd%2Fbridge.c;h=31e6cb073452f1efca9408b85836d4aa308f6123;hb=d799ae067f9d442bf15f3ee757346d1f865cbc38;hp=4cf1b86f8f4931d57a34e23788e101b83a72b2a5;hpb=2280e7223cc5d014fe60ad3be45b8e4d9d401997;p=openvswitch diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c index 4cf1b86f..31e6cb07 100644 --- a/vswitchd/bridge.c +++ b/vswitchd/bridge.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2008, 2009 Nicira Networks +/* Copyright (c) 2008, 2009, 2010 Nicira Networks * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -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" @@ -52,15 +53,16 @@ #include "sha1.h" #include "shash.h" #include "socket-util.h" +#include "stream-ssl.h" #include "svec.h" #include "timeval.h" #include "util.h" #include "unixctl.h" #include "vconn.h" -#include "vconn-ssl.h" #include "vswitchd/vswitch-idl.h" #include "xenserver.h" #include "xtoxll.h" +#include "sflow_api.h" #define THIS_MODULE VLM_bridge #include "vlog.h" @@ -105,8 +107,8 @@ struct mirror { char *name; /* Selection criteria. */ - struct svec src_ports; - struct svec dst_ports; + struct shash src_ports; /* Name is port name; data is always NULL. */ + struct shash dst_ports; /* Name is port name; data is always NULL. */ int *vlans; size_t n_vlans; @@ -136,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. */ @@ -154,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. */ @@ -172,7 +179,6 @@ struct bridge { /* Bonding. */ bool has_bonded_ports; - long long int bond_next_rebalance; /* Flow tracking. */ bool flush; @@ -193,15 +199,20 @@ static struct list all_bridges = LIST_INITIALIZER(&all_bridges); /* Maximum number of datapaths. */ enum { DP_MAX = 256 }; -static struct bridge *bridge_create(const char *name); +static struct bridge *bridge_create(const struct ovsrec_bridge *br_cfg); 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 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 *); @@ -234,18 +245,11 @@ static void port_update_bond_compat(struct port *); static void port_update_vlan_compat(struct port *); static void port_update_bonding(struct port *); -#if 0 -static void mirror_create(struct bridge *, const char *name); +static struct mirror *mirror_create(struct bridge *, const char *name); static void mirror_destroy(struct mirror *); static void mirror_reconfigure(struct bridge *); -static void mirror_reconfigure_one(struct mirror *); +static void mirror_reconfigure_one(struct mirror *, struct ovsrec_mirror *); static bool vlan_is_mirrored(const struct mirror *, int vlan); -#else -static bool vlan_is_mirrored(const struct mirror *m UNUSED, int vlan UNUSED) -{ - return false; -} -#endif static struct iface *iface_create(struct port *port, const struct ovsrec_interface *if_cfg); @@ -292,7 +296,7 @@ void bridge_init(const struct ovsrec_open_vswitch *cfg) { struct svec bridge_names; - struct svec dpif_names; + struct svec dpif_names, dpif_types; size_t i; unixctl_command_register("fdb/show", bridge_unixctl_fdb_show, NULL); @@ -304,31 +308,38 @@ bridge_init(const struct ovsrec_open_vswitch *cfg) svec_sort(&bridge_names); svec_init(&dpif_names); - dp_enumerate(&dpif_names); - for (i = 0; i < dpif_names.n; i++) { - const char *dpif_name = dpif_names.names[i]; + svec_init(&dpif_types); + dp_enumerate_types(&dpif_types); + for (i = 0; i < dpif_types.n; i++) { struct dpif *dpif; int retval; + size_t j; - retval = dpif_open(dpif_name, &dpif); - if (!retval) { - struct svec all_names; - size_t j; + dp_enumerate_names(dpif_types.names[i], &dpif_names); - svec_init(&all_names); - dpif_get_all_names(dpif, &all_names); - for (j = 0; j < all_names.n; j++) { - if (svec_contains(&bridge_names, all_names.names[j])) { - goto found; + for (j = 0; j < dpif_names.n; j++) { + retval = dpif_open(dpif_names.names[j], dpif_types.names[i], &dpif); + if (!retval) { + struct svec all_names; + size_t k; + + svec_init(&all_names); + dpif_get_all_names(dpif, &all_names); + for (k = 0; k < all_names.n; k++) { + if (svec_contains(&bridge_names, all_names.names[k])) { + goto found; + } } + dpif_delete(dpif); + found: + svec_destroy(&all_names); + dpif_close(dpif); } - dpif_delete(dpif); - found: - svec_destroy(&all_names); - dpif_close(dpif); } } + svec_destroy(&bridge_names); svec_destroy(&dpif_names); + svec_destroy(&dpif_types); unixctl_command_register("bridge/dump-flows", bridge_unixctl_dump_flows, NULL); @@ -338,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)) { - vconn_ssl_set_private_key_file(private_key_file); - } - - if (config_string_change(ssl->certificate, &certificate_file)) { - vconn_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)) { - vconn_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 @@ -388,19 +364,14 @@ bridge_configure_ssl(const struct ovsrec_ssl *ssl) /* Attempt to create the network device 'iface_name' through the netdev * library. */ static int -set_up_iface(const struct ovsrec_interface *iface_cfg, bool create) +set_up_iface(const struct ovsrec_interface *iface_cfg, struct iface *iface, + bool create) { struct shash_node *node; struct shash options; - int error; + int error = 0; size_t i; - /* If a type is not explicitly declared, then assume it's an existing - * "system" device. */ - if (iface_cfg->type[0] == '\0' || !strcmp(iface_cfg->type, "system")) { - return 0; - } - shash_init(&options); for (i = 0; i < iface_cfg->n_options; i++) { shash_add(&options, iface_cfg->key_options[i], @@ -408,10 +379,45 @@ set_up_iface(const struct ovsrec_interface *iface_cfg, bool create) } if (create) { - error = netdev_create(iface_cfg->name, iface_cfg->type, &options); - } else { - /* xxx Check to make sure that the type hasn't changed. */ - error = netdev_reconfigure(iface_cfg->name, &options); + struct netdev_options netdev_options; + + memset(&netdev_options, 0, sizeof netdev_options); + netdev_options.name = iface_cfg->name; + 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; + if (iface_is_internal(iface->port->bridge, iface_cfg->name)) { + netdev_options.may_open = true; + } + + error = netdev_open(&netdev_options, &iface->netdev); + + if (iface->netdev) { + netdev_get_carrier(iface->netdev, &iface->enabled); + } + } else if (iface->netdev) { + const char *netdev_type = netdev_get_type(iface->netdev); + 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 { + VLOG_WARN("%s: attempting change device type from %s to %s", + iface_cfg->name, netdev_type, iface_type); + error = EINVAL; + } } SHASH_FOR_EACH (node, &options) { @@ -423,34 +429,30 @@ set_up_iface(const struct ovsrec_interface *iface_cfg, bool create) } static int -reconfigure_iface(const struct ovsrec_interface *iface_cfg) +reconfigure_iface(const struct ovsrec_interface *iface_cfg, struct iface *iface) { - return set_up_iface(iface_cfg, false); + return set_up_iface(iface_cfg, iface, false); } - -/* iterate_and_prune_ifaces() callback function that opens the network device - * for 'iface', if it is not already open, and retrieves the interface's MAC - * address and carrier status. */ static bool -init_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) { - return true; - } else if (!netdev_open(iface->name, NETDEV_ETH_TYPE_NONE, - &iface->netdev)) { - netdev_get_carrier(iface->netdev, &iface->enabled); - return true; - } else { - /* If the network device can't be opened, then we're not going to try - * to do anything with this interface. */ - return false; + if (!iface->netdev) { + int error = set_up_iface(iface->cfg, iface, true); + if (error) { + VLOG_WARN("could not open netdev on %s, dropping: %s", iface->name, + strerror(error)); + return false; + } } + + return true; } 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", @@ -465,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, @@ -514,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) { @@ -521,12 +561,17 @@ 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; COVERAGE_INC(bridge_reconfigure); 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); @@ -552,11 +597,16 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg) SHASH_FOR_EACH (node, &new_br) { const char *br_name = node->name; const struct ovsrec_bridge *br_cfg = node->data; - if (!shash_find_data(&old_br, br_name)) { - br = bridge_create(br_name); - if (br) { - br->cfg = br_cfg; + br = shash_find_data(&old_br, br_name); + if (br) { + /* If the bridge datapath type has changed, we need to tear it + * down and recreate. */ + if (strcmp(br->cfg->datapath_type, br_cfg->datapath_type)) { + bridge_destroy(br); + bridge_create(br_cfg); } + } else { + bridge_create(br_cfg); } } shash_destroy(&old_br); @@ -626,7 +676,7 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg) if (shash_find(&cur_ifaces, if_name)) { /* Already exists, just reconfigure it. */ if (iface) { - reconfigure_iface(iface->cfg); + reconfigure_iface(iface->cfg, iface); } } else { /* Need to add to datapath. */ @@ -650,6 +700,7 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg) shash_destroy(&cur_ifaces); shash_destroy(&want_ifaces); } + sflow_bridge_number = 0; LIST_FOR_EACH (br, struct bridge, node, &all_bridges) { uint8_t ea[8]; uint64_t dpid; @@ -658,8 +709,8 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg) char *dpid_string; bridge_fetch_dp_ifaces(br); - iterate_and_prune_ifaces(br, init_iface_netdev, NULL); + iterate_and_prune_ifaces(br, check_iface_netdev, NULL); iterate_and_prune_ifaces(br, check_iface_dp_ifidx, NULL); /* Pick local port hardware address, datapath ID. */ @@ -691,10 +742,10 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg) dpif_get_netflow_ids(br->dpif, &opts.engine_type, &opts.engine_id); if (nf_cfg->engine_type) { - opts.engine_type = nf_cfg->engine_type; + opts.engine_type = *nf_cfg->engine_type; } if (nf_cfg->engine_id) { - opts.engine_id = nf_cfg->engine_id; + opts.engine_id = *nf_cfg->engine_id; } opts.active_timeout = nf_cfg->active_timeout; @@ -731,6 +782,52 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg) ofproto_set_netflow(br->ofproto, NULL); } + /* Set sFlow configuration on this bridge. */ + if (br->cfg->sflow) { + const struct ovsrec_sflow *sflow_cfg = br->cfg->sflow; + struct ovsrec_controller **controllers; + struct ofproto_sflow_options oso; + size_t n_controllers; + size_t i; + + memset(&oso, 0, sizeof oso); + + oso.targets.n = sflow_cfg->n_targets; + oso.targets.names = sflow_cfg->targets; + + oso.sampling_rate = SFL_DEFAULT_SAMPLING_RATE; + if (sflow_cfg->sampling) { + oso.sampling_rate = *sflow_cfg->sampling; + } + + oso.polling_interval = SFL_DEFAULT_POLLING_INTERVAL; + if (sflow_cfg->polling) { + oso.polling_interval = *sflow_cfg->polling; + } + + oso.header_len = SFL_DEFAULT_HEADER_SIZE; + if (sflow_cfg->header) { + oso.header_len = *sflow_cfg->header; + } + + oso.sub_id = sflow_bridge_number++; + oso.agent_device = sflow_cfg->agent; + + 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); + } else { + ofproto_set_sflow(br->ofproto, NULL); + } + /* Update the controller and related settings. It would be more * straightforward to call this from bridge_reconfigure_one(), but we * can't do it there for two reasons. First, and most importantly, at @@ -740,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++) { @@ -758,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) @@ -994,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; } @@ -1036,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; @@ -1062,41 +1170,43 @@ bridge_unixctl_fdb_show(struct unixctl_conn *conn, } /* Bridge reconfiguration functions. */ - static struct bridge * -bridge_create(const char *name) +bridge_create(const struct ovsrec_bridge *br_cfg) { struct bridge *br; int error; - assert(!bridge_lookup(name)); + assert(!bridge_lookup(br_cfg->name)); br = xzalloc(sizeof *br); - error = dpif_create_and_open(name, &br->dpif); + error = dpif_create_and_open(br_cfg->name, br_cfg->datapath_type, + &br->dpif); if (error) { free(br); return NULL; } dpif_flow_flush(br->dpif); - error = ofproto_create(name, &bridge_ofhooks, br, &br->ofproto); + error = ofproto_create(br_cfg->name, br_cfg->datapath_type, &bridge_ofhooks, + br, &br->ofproto); if (error) { - VLOG_ERR("failed to create switch %s: %s", name, strerror(error)); + VLOG_ERR("failed to create switch %s: %s", br_cfg->name, + strerror(error)); dpif_delete(br->dpif); dpif_close(br->dpif); free(br); return NULL; } - br->name = xstrdup(name); + br->name = xstrdup(br_cfg->name); + br->cfg = br_cfg; br->ml = mac_learning_create(); br->sent_config_request = false; - eth_addr_random(br->default_ea); + eth_addr_nicira_random(br->default_ea); port_array_init(&br->ifaces); br->flush = false; - br->bond_next_rebalance = time_msec() + 10000; list_push_back(&all_bridges, &br->node); @@ -1122,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); @@ -1161,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; @@ -1198,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 @@ -1231,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) @@ -1240,7 +1428,6 @@ bridge_reconfigure_one(const struct ovsrec_open_vswitch *ovs_cfg, struct svec listeners, old_listeners; struct svec snoops, old_snoops; struct shash_node *node; - uint64_t mgmt_id; size_t i; /* Collect old ports. */ @@ -1263,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; @@ -1276,9 +1463,6 @@ bridge_reconfigure_one(const struct ovsrec_open_vswitch *ovs_cfg, } } - dpid_from_string(ovs_cfg->management_id, &mgmt_id); - ofproto_set_mgmt_id(br->ofproto, mgmt_id); - /* Get rid of deleted ports and add new ports. */ SHASH_FOR_EACH (node, &old_ports) { if (!shash_find(&new_ports, node->name)) { @@ -1372,94 +1556,34 @@ bridge_reconfigure_one(const struct ovsrec_open_vswitch *ovs_cfg, svec_destroy(&old_snoops); #endif -#if 0 mirror_reconfigure(br); -#endif + + 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; + + ofproto_set_extra_in_band_remotes(br->ofproto, managers, n_managers); - c = bridge_get_controller(ovs_cfg, br); - if ((br->controller != NULL) != (c != NULL)) { + 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); @@ -1467,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; - ofproto_set_controller(br->ofproto, br->controller); + 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)); + } + } + } + } + + 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 @@ -1726,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) { @@ -1741,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) { @@ -1766,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()); + } } } @@ -1934,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++) { @@ -2049,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 @@ -2085,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. */ @@ -2116,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; } } @@ -2127,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)) { @@ -2150,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. */ @@ -2158,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; } @@ -2219,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) { @@ -2253,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); } } @@ -2624,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) { @@ -2649,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; @@ -2700,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; @@ -2715,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; @@ -2766,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; @@ -2823,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; @@ -2904,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; @@ -2978,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; @@ -3010,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) { @@ -3023,9 +3299,10 @@ 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; } - iface->cfg = if_cfg; } /* Get VLAN tag. */ @@ -3101,18 +3378,17 @@ port_destroy(struct port *port) if (port) { struct bridge *br = port->bridge; struct port *del; + int i; proc_net_compat_update_vlan(port->name, NULL, 0); proc_net_compat_update_bond(port->name, NULL); -#if 0 for (i = 0; i < MAX_MIRRORS; i++) { struct mirror *m = br->mirrors[i]; if (m && m->out_port == port) { mirror_destroy(m); } } -#endif while (port->n_ifaces > 0) { iface_destroy(port->ifaces[port->n_ifaces - 1]); @@ -3187,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; @@ -3252,7 +3534,7 @@ port_update_bond_compat(struct port *port) if (port->bond_fake_iface) { struct netdev *bond_netdev; - if (!netdev_open(port->name, NETDEV_ETH_TYPE_NONE, &bond_netdev)) { + if (!netdev_open_default(port->name, &bond_netdev)) { if (bond.up) { netdev_turn_flags_on(bond_netdev, NETDEV_UP, true); } else { @@ -3320,6 +3602,7 @@ iface_create(struct port *port, const struct ovsrec_interface *if_cfg) iface->tag = tag_create_random(); iface->delay_expires = LLONG_MAX; iface->netdev = NULL; + iface->cfg = if_cfg; if (port->n_ifaces >= port->allocated_ifaces) { port->ifaces = x2nrealloc(port->ifaces, &port->allocated_ifaces, @@ -3332,10 +3615,12 @@ iface_create(struct port *port, const struct ovsrec_interface *if_cfg) /* Attempt to create the network interface in case it * doesn't exist yet. */ - error = set_up_iface(if_cfg, true); - if (error) { - VLOG_WARN("could not create iface %s: %s\n", iface->name, - strerror(error)); + if (!iface_is_internal(port->bridge, iface->name)) { + error = set_up_iface(if_cfg, iface, true); + if (error) { + VLOG_WARN("could not create iface %s: %s", iface->name, + strerror(error)); + } } VLOG_DBG("attached network device %s to port %s", iface->name, port->name); @@ -3369,7 +3654,6 @@ iface_destroy(struct iface *iface) bond_send_learning_packets(port); } - netdev_destroy(iface->name); free(iface->name); free(iface); @@ -3427,7 +3711,7 @@ iface_is_internal(const struct bridge *br, const char *if_name) } port = port_lookup(br, if_name); - if (port->n_ifaces > 1 && port->cfg->bond_fake_iface) { + if (port && port->n_ifaces > 1 && port->cfg->bond_fake_iface) { return true; } return false; @@ -3459,50 +3743,50 @@ iface_set_mac(struct iface *iface) /* Port mirroring. */ -#if 0 static void -mirror_reconfigure(struct bridge *br UNUSED) +mirror_reconfigure(struct bridge *br) { - struct svec old_mirrors, new_mirrors; - size_t i, n_rspan_vlans; + struct shash old_mirrors, new_mirrors; + struct shash_node *node; unsigned long *rspan_vlans; + int i; - /* Collect old and new mirrors. */ - svec_init(&old_mirrors); - svec_init(&new_mirrors); - cfg_get_subsections(&new_mirrors, "mirror.%s", br->name); + /* Collect old mirrors. */ + shash_init(&old_mirrors); for (i = 0; i < MAX_MIRRORS; i++) { if (br->mirrors[i]) { - svec_add(&old_mirrors, br->mirrors[i]->name); + shash_add(&old_mirrors, br->mirrors[i]->name, br->mirrors[i]); } } - /* Get rid of deleted mirrors and add new mirrors. */ - svec_sort(&old_mirrors); - assert(svec_is_unique(&old_mirrors)); - svec_sort(&new_mirrors); - assert(svec_is_unique(&new_mirrors)); - for (i = 0; i < MAX_MIRRORS; i++) { - struct mirror *m = br->mirrors[i]; - if (m && !svec_contains(&new_mirrors, m->name)) { - mirror_destroy(m); + /* Collect new mirrors. */ + shash_init(&new_mirrors); + for (i = 0; i < br->cfg->n_mirrors; i++) { + struct ovsrec_mirror *cfg = br->cfg->mirrors[i]; + if (!shash_add_once(&new_mirrors, cfg->name, cfg)) { + VLOG_WARN("bridge %s: %s specified twice as mirror", + br->name, cfg->name); } } - for (i = 0; i < new_mirrors.n; i++) { - const char *name = new_mirrors.names[i]; - if (!svec_contains(&old_mirrors, name)) { - mirror_create(br, name); + + /* Get rid of deleted mirrors and add new mirrors. */ + SHASH_FOR_EACH (node, &old_mirrors) { + if (!shash_find(&new_mirrors, node->name)) { + mirror_destroy(node->data); } } - svec_destroy(&old_mirrors); - svec_destroy(&new_mirrors); - - /* Reconfigure all mirrors. */ - for (i = 0; i < MAX_MIRRORS; i++) { - if (br->mirrors[i]) { - mirror_reconfigure_one(br->mirrors[i]); + SHASH_FOR_EACH (node, &new_mirrors) { + struct mirror *mirror = shash_find_data(&old_mirrors, node->name); + if (!mirror) { + mirror = mirror_create(br, node->name); + if (!mirror) { + break; + } } + mirror_reconfigure_one(mirror, node->data); } + shash_destroy(&old_mirrors); + shash_destroy(&new_mirrors); /* Update port reserved status. */ for (i = 0; i < br->n_ports; i++) { @@ -3515,31 +3799,29 @@ mirror_reconfigure(struct bridge *br UNUSED) } } - /* Update learning disabled vlans (for RSPAN). */ + /* Update flooded vlans (for RSPAN). */ rspan_vlans = NULL; - n_rspan_vlans = cfg_count("vlan.%s.disable-learning", br->name); - if (n_rspan_vlans) { + if (br->cfg->n_flood_vlans) { rspan_vlans = bitmap_allocate(4096); - for (i = 0; i < n_rspan_vlans; i++) { - int vlan = cfg_get_vlan(i, "vlan.%s.disable-learning", br->name); - if (vlan >= 0) { + for (i = 0; i < br->cfg->n_flood_vlans; i++) { + int64_t vlan = br->cfg->flood_vlans[i]; + if (vlan >= 0 && vlan < 4096) { bitmap_set1(rspan_vlans, vlan); - VLOG_INFO("bridge %s: disabling learning on vlan %d\n", + VLOG_INFO("bridge %s: disabling learning on vlan %"PRId64, br->name, vlan); } else { - VLOG_ERR("bridge %s: invalid value '%s' for learning disabled " - "VLAN", br->name, - cfg_get_string(i, "vlan.%s.disable-learning", br->name)); + VLOG_ERR("bridge %s: invalid value %"PRId64 "for flood VLAN", + br->name, vlan); } } } - if (mac_learning_set_disabled_vlans(br->ml, rspan_vlans)) { + if (mac_learning_set_flood_vlans(br->ml, rspan_vlans)) { bridge_flush(br); } } -static void +static struct mirror * mirror_create(struct bridge *br, const char *name) { struct mirror *m; @@ -3549,7 +3831,7 @@ mirror_create(struct bridge *br, const char *name) if (i >= MAX_MIRRORS) { VLOG_WARN("bridge %s: maximum of %d port mirrors reached, " "cannot create %s", br->name, MAX_MIRRORS, name); - return; + return NULL; } if (!br->mirrors[i]) { break; @@ -3563,12 +3845,14 @@ mirror_create(struct bridge *br, const char *name) m->bridge = br; m->idx = i; m->name = xstrdup(name); - svec_init(&m->src_ports); - svec_init(&m->dst_ports); + shash_init(&m->src_ports); + shash_init(&m->dst_ports); m->vlans = NULL; m->n_vlans = 0; m->out_vlan = -1; m->out_port = NULL; + + return m; } static void @@ -3583,8 +3867,8 @@ mirror_destroy(struct mirror *m) br->ports[i]->dst_mirrors &= ~(MIRROR_MASK_C(1) << m->idx); } - svec_destroy(&m->src_ports); - svec_destroy(&m->dst_ports); + shash_destroy(&m->src_ports); + shash_destroy(&m->dst_ports); free(m->vlans); m->bridge->mirrors[m->idx] = NULL; @@ -3595,45 +3879,36 @@ mirror_destroy(struct mirror *m) } static void -prune_ports(struct mirror *m, struct svec *ports) +mirror_collect_ports(struct mirror *m, struct ovsrec_port **ports, int n_ports, + struct shash *names) { - struct svec tmp; size_t i; - svec_sort_unique(ports); - - svec_init(&tmp); - for (i = 0; i < ports->n; i++) { - const char *name = ports->names[i]; + for (i = 0; i < n_ports; i++) { + const char *name = ports[i]->name; if (port_lookup(m->bridge, name)) { - svec_add(&tmp, name); + shash_add_once(names, name, NULL); } else { - VLOG_WARN("mirror.%s.%s: cannot match on nonexistent port %s", - m->bridge->name, m->name, name); + VLOG_WARN("bridge %s: mirror %s cannot match on nonexistent " + "port %s", m->bridge->name, m->name, name); } } - svec_swap(ports, &tmp); - svec_destroy(&tmp); } static size_t -prune_vlans(struct mirror *m, struct svec *vlan_strings, int **vlans) +mirror_collect_vlans(struct mirror *m, const struct ovsrec_mirror *cfg, + int **vlans) { - size_t n_vlans, i; - - /* This isn't perfect: it won't combine "0" and "00", and the textual sort - * order won't give us numeric sort order. But that's good enough for what - * we need right now. */ - svec_sort_unique(vlan_strings); + size_t n_vlans; + size_t i; - *vlans = xmalloc(sizeof *vlans * vlan_strings->n); + *vlans = xmalloc(sizeof **vlans * cfg->n_select_vlan); n_vlans = 0; - for (i = 0; i < vlan_strings->n; i++) { - const char *name = vlan_strings->names[i]; - int vlan; - if (!str_to_int(name, 10, &vlan) || vlan < 0 || vlan > 4095) { - VLOG_WARN("mirror.%s.%s.select.vlan: ignoring invalid VLAN %s", - m->bridge->name, m->name, name); + for (i = 0; i < cfg->n_select_vlan; i++) { + int64_t vlan = cfg->select_vlan[i]; + if (vlan < 0 || vlan > 4095) { + VLOG_WARN("bridge %s: mirror %s selects invalid VLAN %"PRId64, + m->bridge->name, m->name, vlan); } else { (*vlans)[n_vlans++] = vlan; } @@ -3668,104 +3943,86 @@ port_trunks_any_mirrored_vlan(const struct mirror *m, const struct port *p) } static void -mirror_reconfigure_one(struct mirror *m UNUSED) +mirror_reconfigure_one(struct mirror *m, struct ovsrec_mirror *cfg) { - char *pfx = xasprintf("mirror.%s.%s", m->bridge->name, m->name); - struct svec src_ports, dst_ports, ports; - struct svec vlan_strings; + struct shash src_ports, dst_ports; mirror_mask_t mirror_bit; - const char *out_port_name; struct port *out_port; int out_vlan; size_t n_vlans; int *vlans; size_t i; - bool mirror_all_ports; - bool any_ports_specified; /* Get output port. */ - out_port_name = cfg_get_key(0, "mirror.%s.%s.output.port", - m->bridge->name, m->name); - if (out_port_name) { - out_port = port_lookup(m->bridge, out_port_name); + if (cfg->output_port) { + out_port = port_lookup(m->bridge, cfg->output_port->name); if (!out_port) { - VLOG_ERR("%s.output.port: bridge %s does not have a port " - "named %s", pfx, m->bridge->name, out_port_name); + VLOG_ERR("bridge %s: mirror %s outputs to port not on bridge", + m->bridge->name, m->name); mirror_destroy(m); - free(pfx); return; } out_vlan = -1; - if (cfg_has("%s.output.vlan", pfx)) { - VLOG_ERR("%s.output.port and %s.output.vlan both specified; " - "ignoring %s.output.vlan", pfx, pfx, pfx); + if (cfg->output_vlan) { + VLOG_ERR("bridge %s: mirror %s specifies both output port and " + "output vlan; ignoring output vlan", + m->bridge->name, m->name); } - } else if (cfg_has("%s.output.vlan", pfx)) { + } else if (cfg->output_vlan) { out_port = NULL; - out_vlan = cfg_get_vlan(0, "%s.output.vlan", pfx); + out_vlan = *cfg->output_vlan; } else { - VLOG_ERR("%s: neither %s.output.port nor %s.output.vlan specified, " - "but exactly one is required; disabling port mirror %s", - pfx, pfx, pfx, pfx); + VLOG_ERR("bridge %s: mirror %s does not specify output; ignoring", + m->bridge->name, m->name); mirror_destroy(m); - free(pfx); return; } - /* Get all the ports, and drop duplicates and ports that don't exist. */ - svec_init(&src_ports); - svec_init(&dst_ports); - svec_init(&ports); - cfg_get_all_keys(&src_ports, "%s.select.src-port", pfx); - cfg_get_all_keys(&dst_ports, "%s.select.dst-port", pfx); - cfg_get_all_keys(&ports, "%s.select.port", pfx); - any_ports_specified = src_ports.n || dst_ports.n || ports.n; - svec_append(&src_ports, &ports); - svec_append(&dst_ports, &ports); - svec_destroy(&ports); - prune_ports(m, &src_ports); - prune_ports(m, &dst_ports); - if (any_ports_specified && !src_ports.n && !dst_ports.n) { - VLOG_ERR("%s: none of the specified ports exist; " - "disabling port mirror %s", pfx, pfx); - mirror_destroy(m); - goto exit; - } + shash_init(&src_ports); + shash_init(&dst_ports); + 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. */ - svec_init(&vlan_strings); - cfg_get_all_keys(&vlan_strings, "%s.select.vlan", pfx); - n_vlans = prune_vlans(m, &vlan_strings, &vlans); - svec_destroy(&vlan_strings); + /* Get all the vlans, and drop duplicate and invalid vlans. */ + n_vlans = mirror_collect_vlans(m, cfg, &vlans); + } /* Update mirror data. */ - if (!svec_equal(&m->src_ports, &src_ports) - || !svec_equal(&m->dst_ports, &dst_ports) + if (!shash_equal_keys(&m->src_ports, &src_ports) + || !shash_equal_keys(&m->dst_ports, &dst_ports) || m->n_vlans != n_vlans || memcmp(m->vlans, vlans, sizeof *vlans * n_vlans) || m->out_port != out_port || m->out_vlan != out_vlan) { bridge_flush(m->bridge); } - svec_swap(&m->src_ports, &src_ports); - svec_swap(&m->dst_ports, &dst_ports); + shash_swap(&m->src_ports, &src_ports); + shash_swap(&m->dst_ports, &dst_ports); free(m->vlans); m->vlans = vlans; m->n_vlans = n_vlans; m->out_port = out_port; m->out_vlan = out_vlan; - /* If no selection criteria have been given, mirror for all ports. */ - mirror_all_ports = (!m->src_ports.n) && (!m->dst_ports.n) && (!m->n_vlans); - /* 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 - || svec_contains(&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) @@ -3775,7 +4032,7 @@ mirror_reconfigure_one(struct mirror *m UNUSED) port->src_mirrors &= ~mirror_bit; } - if (mirror_all_ports || svec_contains(&m->dst_ports, port->name)) { + if (shash_find(&m->dst_ports, port->name)) { port->dst_mirrors |= mirror_bit; } else { port->dst_mirrors &= ~mirror_bit; @@ -3783,9 +4040,6 @@ mirror_reconfigure_one(struct mirror *m UNUSED) } /* Clean up. */ -exit: - svec_destroy(&src_ports); - svec_destroy(&dst_ports); - free(pfx); + shash_destroy(&src_ports); + shash_destroy(&dst_ports); } -#endif