+static void
+port_configure_stp(const struct ofproto *ofproto, struct port *port,
+ struct ofproto_port_stp_settings *port_s,
+ int *port_num_counter, unsigned long *port_num_bitmap)
+{
+ const char *config_str;
+ struct iface *iface;
+
+ config_str = get_port_other_config(port->cfg, "stp-enable", NULL);
+ if (config_str && !strcmp(config_str, "false")) {
+ port_s->enable = false;
+ return;
+ } else {
+ port_s->enable = true;
+ }
+
+ /* STP over bonds is not supported. */
+ if (!list_is_singleton(&port->ifaces)) {
+ VLOG_ERR("port %s: cannot enable STP on bonds, disabling",
+ port->name);
+ port_s->enable = false;
+ return;
+ }
+
+ iface = CONTAINER_OF(list_front(&port->ifaces), struct iface, port_elem);
+
+ /* Internal ports shouldn't participate in spanning tree, so
+ * skip them. */
+ if (!strcmp(iface->type, "internal")) {
+ VLOG_DBG("port %s: disable STP on internal ports", port->name);
+ port_s->enable = false;
+ return;
+ }
+
+ /* STP on mirror output ports is not supported. */
+ if (ofproto_is_mirror_output_bundle(ofproto, port)) {
+ VLOG_DBG("port %s: disable STP on mirror ports", port->name);
+ port_s->enable = false;
+ return;
+ }
+
+ config_str = get_port_other_config(port->cfg, "stp-port-num", NULL);
+ if (config_str) {
+ unsigned long int port_num = strtoul(config_str, NULL, 0);
+ int port_idx = port_num - 1;
+
+ if (port_num < 1 || port_num > STP_MAX_PORTS) {
+ VLOG_ERR("port %s: invalid stp-port-num", port->name);
+ port_s->enable = false;
+ return;
+ }
+
+ if (bitmap_is_set(port_num_bitmap, port_idx)) {
+ VLOG_ERR("port %s: duplicate stp-port-num %lu, disabling",
+ port->name, port_num);
+ port_s->enable = false;
+ return;
+ }
+ bitmap_set1(port_num_bitmap, port_idx);
+ port_s->port_num = port_idx;
+ } else {
+ if (*port_num_counter > STP_MAX_PORTS) {
+ VLOG_ERR("port %s: too many STP ports, disabling", port->name);
+ port_s->enable = false;
+ return;
+ }
+
+ port_s->port_num = (*port_num_counter)++;
+ }
+
+ config_str = get_port_other_config(port->cfg, "stp-path-cost", NULL);
+ if (config_str) {
+ port_s->path_cost = strtoul(config_str, NULL, 10);
+ } else {
+ uint32_t current;
+
+ if (netdev_get_features(iface->netdev, ¤t, NULL, NULL, NULL)) {
+ /* Couldn't get speed, so assume 100Mb/s. */
+ port_s->path_cost = 19;
+ } else {
+ unsigned int mbps;
+
+ mbps = netdev_features_to_bps(current) / 1000000;
+ port_s->path_cost = stp_convert_speed_to_cost(mbps);
+ }
+ }
+
+ config_str = get_port_other_config(port->cfg, "stp-port-priority", NULL);
+ if (config_str) {
+ port_s->priority = strtoul(config_str, NULL, 0);
+ } else {
+ port_s->priority = STP_DEFAULT_PORT_PRIORITY;
+ }
+}
+
+/* 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);
+ }
+}
+