ofproto: Add support for master/slave controller coordination.
[sliver-openvswitch.git] / vswitchd / bridge.c
index e945ca8..cf02122 100644 (file)
@@ -137,6 +137,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 +158,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 +178,6 @@ struct bridge {
 
     /* Bonding. */
     bool has_bonded_ports;
-    long long int bond_next_rebalance;
 
     /* Flow tracking. */
     bool flush;
@@ -199,9 +203,9 @@ 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 *,
@@ -330,6 +334,7 @@ bridge_init(const struct ovsrec_open_vswitch *cfg)
             }
         }
     }
+    svec_destroy(&bridge_names);
     svec_destroy(&dpif_names);
     svec_destroy(&dpif_types);
 
@@ -341,49 +346,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 +380,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 +403,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 +432,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 +448,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 +464,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,
@@ -759,8 +740,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 +768,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);
@@ -824,18 +813,27 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg)
 }
 
 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 +1055,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 +1097,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 +1160,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 +1185,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 +1223,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 +1260,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 +1303,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 +1403,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 +1510,29 @@ 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)
 {
-    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)) {
+    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 +1540,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));
+                    }
 
-    ofproto_set_controller(br->ofproto, br->controller);
+                    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
@@ -1786,6 +1860,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 +1903,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 +1934,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 +2105,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 +2220,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 +2270,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 +2303,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 +2314,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 +2356,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 +2366,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 +2429,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 +2461,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 +2828,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 +2853,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 +2904,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 +2919,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 +2970,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 +3027,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 +3108,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 +3182,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 +3226,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 +3248,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 +3412,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 +3851,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 +3901,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 +3928,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 +3966,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 +3981,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 +3989,6 @@ mirror_reconfigure_one(struct mirror *m, struct ovsrec_mirror *cfg)
     }
 
     /* Clean up. */
-exit:
     shash_destroy(&src_ports);
     shash_destroy(&dst_ports);
 }