+/* Set spanning tree configuration on 'br'. */
+static void
+bridge_configure_stp(struct bridge *br)
+{
+ if (!br->cfg->stp_enable) {
+ ofproto_set_stp(br->ofproto, NULL);
+ } else {
+ struct ofproto_stp_settings br_s;
+ const char *config_str;
+ struct port *port;
+ int port_num_counter;
+ unsigned long *port_num_bitmap;
+
+ config_str = bridge_get_other_config(br->cfg, "stp-system-id");
+ if (config_str) {
+ uint8_t ea[ETH_ADDR_LEN];
+
+ if (eth_addr_from_string(config_str, ea)) {
+ br_s.system_id = eth_addr_to_uint64(ea);
+ } else {
+ br_s.system_id = eth_addr_to_uint64(br->ea);
+ VLOG_ERR("bridge %s: invalid stp-system-id, defaulting "
+ "to "ETH_ADDR_FMT, br->name, ETH_ADDR_ARGS(br->ea));
+ }
+ } else {
+ br_s.system_id = eth_addr_to_uint64(br->ea);
+ }
+
+ config_str = bridge_get_other_config(br->cfg, "stp-priority");
+ if (config_str) {
+ br_s.priority = strtoul(config_str, NULL, 0);
+ } else {
+ br_s.priority = STP_DEFAULT_BRIDGE_PRIORITY;
+ }
+
+ config_str = bridge_get_other_config(br->cfg, "stp-hello-time");
+ if (config_str) {
+ br_s.hello_time = strtoul(config_str, NULL, 10) * 1000;
+ } else {
+ br_s.hello_time = STP_DEFAULT_HELLO_TIME;
+ }
+
+ config_str = bridge_get_other_config(br->cfg, "stp-max-age");
+ if (config_str) {
+ br_s.max_age = strtoul(config_str, NULL, 10) * 1000;
+ } else {
+ br_s.max_age = STP_DEFAULT_MAX_AGE;
+ }
+
+ config_str = bridge_get_other_config(br->cfg, "stp-forward-delay");
+ if (config_str) {
+ br_s.fwd_delay = strtoul(config_str, NULL, 10) * 1000;
+ } else {
+ br_s.fwd_delay = STP_DEFAULT_FWD_DELAY;
+ }
+
+ /* Configure STP on the bridge. */
+ if (ofproto_set_stp(br->ofproto, &br_s)) {
+ VLOG_ERR("bridge %s: could not enable STP", br->name);
+ return;
+ }
+
+ /* Users must either set the port number with the "stp-port-num"
+ * configuration on all ports or none. If manual configuration
+ * is not done, then we allocate them sequentially. */
+ port_num_counter = 0;
+ port_num_bitmap = bitmap_allocate(STP_MAX_PORTS);
+ HMAP_FOR_EACH (port, hmap_node, &br->ports) {
+ struct ofproto_port_stp_settings port_s;
+ struct iface *iface;
+
+ port_configure_stp(br->ofproto, port, &port_s,
+ &port_num_counter, port_num_bitmap);
+
+ /* As bonds are not supported, just apply configuration to
+ * all interfaces. */
+ LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
+ if (ofproto_port_set_stp(br->ofproto, iface->ofp_port,
+ &port_s)) {
+ VLOG_ERR("port %s: could not enable STP", port->name);
+ continue;
+ }
+ }
+ }
+
+ if (bitmap_scan(port_num_bitmap, 0, STP_MAX_PORTS) != STP_MAX_PORTS
+ && port_num_counter) {
+ VLOG_ERR("bridge %s: must manually configure all STP port "
+ "IDs or none, disabling", br->name);
+ ofproto_set_stp(br->ofproto, NULL);
+ }
+ bitmap_free(port_num_bitmap);
+ }
+}
+
+static bool
+bridge_has_bond_fake_iface(const struct bridge *br, const char *name)
+{
+ const struct port *port = port_lookup(br, name);
+ return port && port_is_bond_fake_iface(port);
+}
+
+static bool
+port_is_bond_fake_iface(const struct port *port)
+{
+ return port->cfg->bond_fake_iface && !list_is_short(&port->ifaces);
+}
+
+static void
+add_del_bridges(const struct ovsrec_open_vswitch *cfg)
+{
+ struct bridge *br, *next;
+ struct shash new_br;
+ size_t i;
+
+ /* Collect new bridges' names and types. */
+ shash_init(&new_br);
+ for (i = 0; i < cfg->n_bridges; i++) {
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+ const struct ovsrec_bridge *br_cfg = cfg->bridges[i];
+
+ if (strchr(br_cfg->name, '/')) {
+ /* Prevent remote ovsdb-server users from accessing arbitrary
+ * directories, e.g. consider a bridge named "../../../etc/". */
+ VLOG_WARN_RL(&rl, "ignoring bridge with invalid name \"%s\"",
+ br_cfg->name);
+ } else if (!shash_add_once(&new_br, br_cfg->name, br_cfg)) {
+ VLOG_WARN_RL(&rl, "bridge %s specified twice", br_cfg->name);
+ }
+ }
+
+ /* Get rid of deleted bridges or those whose types have changed.
+ * Update 'cfg' of bridges that still exist. */
+ HMAP_FOR_EACH_SAFE (br, next, node, &all_bridges) {
+ br->cfg = shash_find_data(&new_br, br->name);
+ if (!br->cfg || strcmp(br->type, ofproto_normalize_type(
+ br->cfg->datapath_type))) {
+ bridge_destroy(br);
+ }
+ }
+
+ /* Add new bridges. */
+ for (i = 0; i < cfg->n_bridges; i++) {
+ const struct ovsrec_bridge *br_cfg = cfg->bridges[i];
+ struct bridge *br = bridge_lookup(br_cfg->name);
+ if (!br) {
+ bridge_create(br_cfg);
+ }
+ }
+
+ shash_destroy(&new_br);
+}
+
+/* Delete each ofproto port on 'br' that doesn't have a corresponding "struct
+ * iface".
+ *
+ * The kernel will reject any attempt to add a given port to a datapath if that
+ * port already belongs to a different datapath, so we must do all port
+ * deletions before any port additions. */
+static void
+bridge_del_ofproto_ports(struct bridge *br)
+{
+ struct ofproto_port_dump dump;
+ struct ofproto_port ofproto_port;
+
+ OFPROTO_PORT_FOR_EACH (&ofproto_port, &dump, br->ofproto) {
+ const char *name = ofproto_port.name;
+ struct iface *iface;
+ const char *type;
+ int error;
+
+ /* Ignore the local port. We can't change it anyhow. */
+ if (!strcmp(name, br->name)) {
+ continue;
+ }
+
+ /* Get the type that 'ofproto_port' should have (ordinarily the
+ * type of its corresponding iface) or NULL if it should be
+ * deleted. */
+ iface = iface_lookup(br, name);
+ type = (iface ? iface->type
+ : bridge_has_bond_fake_iface(br, name) ? "internal"
+ : NULL);
+
+ /* If it's the wrong type then delete the ofproto port. */
+ if (type
+ && !strcmp(ofproto_port.type, type)
+ && (!iface || !iface->netdev
+ || !strcmp(netdev_get_type(iface->netdev), type))) {
+ continue;
+ }
+ error = ofproto_port_del(br->ofproto, ofproto_port.ofp_port);
+ if (error) {
+ VLOG_WARN("bridge %s: failed to remove %s interface (%s)",
+ br->name, name, strerror(error));
+ }
+ if (iface) {
+ netdev_close(iface->netdev);
+ iface->netdev = NULL;
+ }
+ }
+}
+
+static void
+iface_set_ofp_port(struct iface *iface, int ofp_port)
+{
+ struct bridge *br = iface->port->bridge;
+
+ assert(iface->ofp_port < 0 && ofp_port >= 0);
+ iface->ofp_port = ofp_port;
+ hmap_insert(&br->ifaces, &iface->ofp_port_node, hash_int(ofp_port, 0));
+ iface_set_ofport(iface->cfg, ofp_port);
+}
+
+static void
+bridge_refresh_ofp_port(struct bridge *br)
+{
+ struct ofproto_port_dump dump;
+ struct ofproto_port ofproto_port;
+ struct port *port;
+
+ /* Clear all the "ofp_port"es. */
+ hmap_clear(&br->ifaces);
+ HMAP_FOR_EACH (port, hmap_node, &br->ports) {
+ struct iface *iface;
+
+ LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
+ iface->ofp_port = -1;
+ }
+ }
+
+ /* Obtain the correct "ofp_port"s from ofproto. */
+ OFPROTO_PORT_FOR_EACH (&ofproto_port, &dump, br->ofproto) {
+ struct iface *iface = iface_lookup(br, ofproto_port.name);
+ if (iface) {
+ if (iface->ofp_port >= 0) {
+ VLOG_WARN("bridge %s: interface %s reported twice",
+ br->name, ofproto_port.name);
+ } else if (iface_from_ofp_port(br, ofproto_port.ofp_port)) {
+ VLOG_WARN("bridge %s: interface %"PRIu16" reported twice",
+ br->name, ofproto_port.ofp_port);
+ } else {
+ iface_set_ofp_port(iface, ofproto_port.ofp_port);
+ }
+ }
+ }
+}
+
+/* Add an ofproto port for any "struct iface" that doesn't have one.
+ * Delete any "struct iface" for which this fails.
+ * Delete any "struct port" that thereby ends up with no ifaces. */
+static void
+bridge_add_ofproto_ports(struct bridge *br)
+{
+ struct port *port, *next_port;
+
+ HMAP_FOR_EACH_SAFE (port, next_port, hmap_node, &br->ports) {
+ struct iface *iface, *next_iface;
+ struct ofproto_port ofproto_port;
+
+ LIST_FOR_EACH_SAFE (iface, next_iface, port_elem, &port->ifaces) {
+ int error;
+
+ /* Open the netdev. */
+ if (!iface->netdev) {
+ error = netdev_open(iface->name, iface->type, &iface->netdev);
+ if (error) {
+ VLOG_WARN("could not open network device %s (%s)",
+ iface->name, strerror(error));
+ }
+
+ if (iface->netdev
+ && port->cfg->vlan_mode
+ && !strcmp(port->cfg->vlan_mode, "splinter")) {
+ netdev_turn_flags_on(iface->netdev, NETDEV_UP, true);
+ }
+ } else {
+ error = 0;
+ }
+
+ /* Configure the netdev. */
+ if (iface->netdev) {
+ struct shash args;
+
+ shash_init(&args);
+ shash_from_ovs_idl_map(iface->cfg->key_options,
+ iface->cfg->value_options,
+ iface->cfg->n_options, &args);
+ error = netdev_set_config(iface->netdev, &args);
+ shash_destroy(&args);
+
+ if (error) {
+ VLOG_WARN("could not configure network device %s (%s)",
+ iface->name, strerror(error));
+ netdev_close(iface->netdev);
+ iface->netdev = NULL;
+ }
+ }
+
+ /* Add the port, if necessary. */
+ if (iface->netdev && iface->ofp_port < 0) {
+ uint16_t ofp_port;
+ int error;
+
+ error = ofproto_port_add(br->ofproto, iface->netdev,
+ &ofp_port);
+ if (!error) {
+ iface_set_ofp_port(iface, ofp_port);
+ } else {
+ netdev_close(iface->netdev);
+ iface->netdev = NULL;
+ }
+ }
+
+ /* Populate stats columns in new Interface rows. */
+ if (iface->netdev && !iface->cfg->mtu) {
+ iface_refresh_stats(iface);
+ iface_refresh_status(iface);
+ }
+
+ /* Delete the iface if we failed. */
+ if (iface->netdev && iface->ofp_port >= 0) {
+ VLOG_DBG("bridge %s: interface %s is on port %d",
+ br->name, iface->name, iface->ofp_port);
+ } else {
+ if (iface->netdev) {
+ VLOG_ERR("bridge %s: missing %s interface, dropping",
+ br->name, iface->name);
+ } else {
+ /* We already reported a related error, don't bother
+ * duplicating it. */
+ }
+ iface_clear_db_record(iface->cfg);
+ iface_destroy(iface);
+ }
+ }
+ if (list_is_empty(&port->ifaces)) {
+ VLOG_WARN("%s port has no interfaces, dropping", port->name);
+ port_destroy(port);
+ continue;
+ }
+
+ /* Add bond fake iface if necessary. */
+ if (port_is_bond_fake_iface(port)) {
+ if (ofproto_port_query_by_name(br->ofproto, port->name,
+ &ofproto_port)) {
+ struct netdev *netdev;
+ int error;
+
+ error = netdev_open(port->name, "internal", &netdev);
+ if (!error) {
+ ofproto_port_add(br->ofproto, netdev, NULL);
+ netdev_close(netdev);
+ } else {
+ VLOG_WARN("could not open network device %s (%s)",
+ port->name, strerror(error));
+ }
+ } else {
+ /* Already exists, nothing to do. */
+ ofproto_port_destroy(&ofproto_port);
+ }
+ }
+ }
+}
+
+static const char *
+get_ovsrec_key_value(char **keys, char **values, size_t n, const char *key)
+{
+ size_t 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(br_cfg->key_other_config,
+ br_cfg->value_other_config,
+ br_cfg->n_other_config, key);
+}
+
+/* Set Flow eviction threshold */
+static void
+bridge_configure_flow_eviction_threshold(struct bridge *br)
+{
+ const char *threshold_str;
+ unsigned threshold;
+
+ threshold_str = bridge_get_other_config(br->cfg, "flow-eviction-threshold");
+ if (threshold_str) {
+ threshold = strtoul(threshold_str, NULL, 10);
+ } else {
+ threshold = OFPROTO_FLOW_EVICTON_THRESHOLD_DEFAULT;
+ }
+ ofproto_set_flow_eviction_threshold(br->ofproto, threshold);
+}
+
+/* Set forward BPDU option. */
+static void
+bridge_configure_forward_bpdu(struct bridge *br)