netdev: Fix carrier status for down interfaces.
[sliver-openvswitch.git] / vswitchd / bridge.c
index 7174f2c..41fcba5 100644 (file)
@@ -37,6 +37,7 @@
 #include "dynamic-string.h"
 #include "flow.h"
 #include "hash.h"
+#include "hmap.h"
 #include "jsonrpc.h"
 #include "list.h"
 #include "mac-learning.h"
@@ -49,7 +50,6 @@
 #include "ovsdb-data.h"
 #include "packets.h"
 #include "poll-loop.h"
-#include "port-array.h"
 #include "proc-net-compat.h"
 #include "process.h"
 #include "sha1.h"
@@ -57,6 +57,7 @@
 #include "socket-util.h"
 #include "stream-ssl.h"
 #include "svec.h"
+#include "system-stats.h"
 #include "timeval.h"
 #include "util.h"
 #include "unixctl.h"
@@ -84,9 +85,11 @@ struct iface {
 
     /* These members are valid only after bridge_reconfigure() causes them to
      * be initialized. */
+    struct hmap_node dp_ifidx_node; /* In struct bridge's "ifaces" hmap. */
     int dp_ifidx;               /* Index within kernel datapath. */
     struct netdev *netdev;      /* Network device. */
     bool enabled;               /* May be chosen for flows? */
+    const char *type;           /* Usually same as cfg->type. */
     const struct ovsrec_interface *cfg;
 };
 
@@ -141,9 +144,10 @@ 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. */
+    long long int bond_next_fake_iface_update; /* Time of next update. */
     int bond_rebalance_interval; /* Interval between rebalances, in ms. */
     long long int bond_next_rebalance; /* Next rebalancing time. */
+    struct netdev_monitor *monitor; /* Tracks carrier up/down status. */
 
     /* Port mirroring info. */
     mirror_mask_t src_mirrors;  /* Mirrors triggered when packet received. */
@@ -164,7 +168,7 @@ struct bridge {
 
     /* Kernel datapath information. */
     struct dpif *dpif;          /* Datapath. */
-    struct port_array ifaces;   /* Indexed by kernel datapath port number. */
+    struct hmap ifaces;         /* Contains "struct iface"s. */
 
     /* Bridge ports. */
     struct port **ports;
@@ -188,10 +192,10 @@ static struct list all_bridges = LIST_INITIALIZER(&all_bridges);
 /* OVSDB IDL used to obtain configuration. */
 static struct ovsdb_idl *idl;
 
-/* Each time this timer expires, the bridge fetches statistics for every
- * interface and pushes them into the database. */
-#define IFACE_STATS_INTERVAL (5 * 1000) /* In milliseconds. */
-static long long int iface_stats_timer = LLONG_MIN;
+/* Each time this timer expires, the bridge fetches systems and interface
+ * statistics and pushes them into the database. */
+#define STATS_INTERVAL (5 * 1000) /* In milliseconds. */
+static long long int stats_timer = LLONG_MIN;
 
 static struct bridge *bridge_create(const struct ovsrec_bridge *br_cfg);
 static void bridge_destroy(struct bridge *);
@@ -244,16 +248,19 @@ static void mirror_reconfigure(struct bridge *);
 static void mirror_reconfigure_one(struct mirror *, struct ovsrec_mirror *);
 static bool vlan_is_mirrored(const struct mirror *, int vlan);
 
-static struct iface *iface_create(struct port *port, 
+static struct iface *iface_create(struct port *port,
                                   const struct ovsrec_interface *if_cfg);
 static void iface_destroy(struct iface *);
 static struct iface *iface_lookup(const struct bridge *, const char *name);
 static struct iface *iface_from_dp_ifidx(const struct bridge *,
                                          uint16_t dp_ifidx);
-static bool iface_is_internal(const struct bridge *, const char *name);
 static void iface_set_mac(struct iface *);
+static void iface_set_ofport(const struct ovsrec_interface *, int64_t ofport);
 static void iface_update_qos(struct iface *, const struct ovsrec_qos *);
 
+static void shash_from_ovs_idl_map(char **keys, char **values, size_t n,
+                                   struct shash *);
+
 /* Hooks into ofproto processing. */
 static struct ofhooks bridge_ofhooks;
 \f
@@ -268,6 +275,19 @@ bridge_init(const char *remote)
     /* Create connection to database. */
     idl = ovsdb_idl_create(remote, &ovsrec_idl_class);
 
+    ovsdb_idl_set_write_only(idl, &ovsrec_open_vswitch_col_cur_cfg);
+    ovsdb_idl_set_write_only(idl, &ovsrec_open_vswitch_col_statistics);
+    ovsdb_idl_omit(idl, &ovsrec_open_vswitch_col_external_ids);
+
+    ovsdb_idl_omit(idl, &ovsrec_bridge_col_external_ids);
+
+    ovsdb_idl_omit(idl, &ovsrec_port_col_external_ids);
+    ovsdb_idl_omit(idl, &ovsrec_port_col_fake_bridge);
+
+    ovsdb_idl_set_write_only(idl, &ovsrec_interface_col_ofport);
+    ovsdb_idl_set_write_only(idl, &ovsrec_interface_col_statistics);
+    ovsdb_idl_omit(idl, &ovsrec_interface_col_external_ids);
+
     /* Register unixctl commands. */
     unixctl_command_register("fdb/show", bridge_unixctl_fdb_show, NULL);
     unixctl_command_register("bridge/dump-flows", bridge_unixctl_dump_flows,
@@ -293,7 +313,7 @@ bridge_configure_once(const struct ovsrec_open_vswitch *cfg)
     }
     already_configured_once = true;
 
-    iface_stats_timer = time_msec() + IFACE_STATS_INTERVAL;
+    stats_timer = time_msec() + STATS_INTERVAL;
 
     /* Get all the configured bridges' names from 'cfg' into 'bridge_names'. */
     svec_init(&bridge_names);
@@ -344,102 +364,116 @@ bridge_configure_once(const struct ovsrec_open_vswitch *cfg)
     svec_destroy(&dpif_types);
 }
 
-/* Attempt to create the network device 'iface_name' through the netdev
- * library. */
-static int
-set_up_iface(const struct ovsrec_interface *iface_cfg, struct iface *iface,
-             bool create)
+/* Initializes 'options' and fills it with the options for 'if_cfg'. Merges
+ * keys from "options" and "other_config", preferring "options" keys over
+ * "other_config" keys.
+ *
+ * The value strings in '*options' are taken directly from if_cfg, not copied,
+ * so the caller should not modify or free them. */
+static void
+iface_get_options(const struct ovsrec_interface *if_cfg, struct shash *options)
 {
-    struct shash options;
-    int error = 0;
     size_t i;
 
-    shash_init(&options);
-    for (i = 0; i < iface_cfg->n_options; i++) {
-        shash_add(&options, iface_cfg->key_options[i],
-                  xstrdup(iface_cfg->value_options[i]));
-    }
+    shash_from_ovs_idl_map(if_cfg->key_options, if_cfg->value_options,
+                           if_cfg->n_options, options);
 
-    if (create) {
-        struct netdev_options netdev_options;
+    for (i = 0; i < if_cfg->n_other_config; i++) {
+        char *key = if_cfg->key_other_config[i];
+        char *value = if_cfg->value_other_config[i];
 
-        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";
+        if (!shash_find_data(options, key)) {
+            shash_add(options, key, value);
         } else {
-            netdev_options.type = iface_cfg->type;
+            VLOG_WARN("%s: ignoring \"other_config\" key %s that conflicts "
+                      "with \"options\" key %s", if_cfg->name, key, key);
         }
-        netdev_options.args = &options;
-        netdev_options.ethertype = NETDEV_ETH_TYPE_NONE;
+    }
+}
 
-        error = netdev_open(&netdev_options, &iface->netdev);
+/* Returns the type of network device that 'iface' should have.  (This is
+ * ordinarily the same type as the interface, but the network devices for
+ * "internal" ports have type "system".) */
+static const char *
+iface_get_netdev_type(const struct iface *iface)
+{
+    return !strcmp(iface->type, "internal") ? "system" : iface->type;
+}
 
-        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";
-        }
+/* Attempt to create the network device for 'iface' through the netdev
+ * library. */
+static int
+create_iface_netdev(struct iface *iface)
+{
+    struct netdev_options netdev_options;
+    struct shash options;
+    int error;
 
-        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;
-        }
+    memset(&netdev_options, 0, sizeof netdev_options);
+    netdev_options.name = iface->cfg->name;
+    netdev_options.type = iface_get_netdev_type(iface);
+    netdev_options.args = &options;
+    netdev_options.ethertype = NETDEV_ETH_TYPE_NONE;
+
+    iface_get_options(iface->cfg, &options);
+
+    error = netdev_open(&netdev_options, &iface->netdev);
+
+    if (iface->netdev) {
+        iface->enabled = netdev_get_carrier(iface->netdev);
     }
-    shash_destroy_free_data(&options);
+
+    shash_destroy(&options);
 
     return error;
 }
 
 static int
-reconfigure_iface(const struct ovsrec_interface *iface_cfg, struct iface *iface)
+reconfigure_iface_netdev(struct iface *iface)
 {
-    return set_up_iface(iface_cfg, iface, false);
-}
+    const char *netdev_type, *iface_type;
+    struct shash options;
+    int error;
 
-static bool
-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);
-        if (error) {
-            VLOG_WARN("could not open netdev on %s, dropping: %s", iface->name,
-                                                               strerror(error));
-            return false;
-        }
+    /* Skip reconfiguration if the device has the wrong type. This shouldn't
+     * happen, but... */
+    iface_type = iface_get_netdev_type(iface);
+    netdev_type = netdev_get_type(iface->netdev);
+    if (iface_type && strcmp(netdev_type, iface_type)) {
+        VLOG_WARN("%s: attempting change device type from %s to %s",
+                  iface->cfg->name, netdev_type, iface_type);
+        return EINVAL;
     }
 
-    return true;
+    /* Reconfigure device. */
+    iface_get_options(iface->cfg, &options);
+    error = netdev_reconfigure(iface->netdev, &options);
+    shash_destroy(&options);
+
+    return error;
 }
 
+/* Callback for iterate_and_prune_ifaces(). */
 static bool
-check_iface_dp_ifidx(struct bridge *br, struct iface *iface,
-                     void *aux OVS_UNUSED)
+check_iface(struct bridge *br, struct iface *iface, void *aux OVS_UNUSED)
 {
-    if (iface->dp_ifidx >= 0) {
-        VLOG_DBG("%s has interface %s on port %d",
-                 dpif_name(br->dpif),
-                 iface->name, iface->dp_ifidx);
-        return true;
-    } else {
+    if (!iface->netdev) {
+        /* We already reported a related error, don't bother duplicating it. */
+        return false;
+    }
+
+    if (iface->dp_ifidx < 0) {
         VLOG_ERR("%s interface not in %s, dropping",
                  iface->name, dpif_name(br->dpif));
         return false;
     }
+
+    VLOG_DBG("%s has interface %s on port %d", dpif_name(br->dpif),
+             iface->name, iface->dp_ifidx);
+    return true;
 }
 
+/* Callback for iterate_and_prune_ifaces(). */
 static bool
 set_iface_properties(struct bridge *br OVS_UNUSED, struct iface *iface,
                      void *aux OVS_UNUSED)
@@ -451,8 +485,7 @@ set_iface_properties(struct bridge *br OVS_UNUSED, struct iface *iface,
 
     /* Set MAC address of internal interfaces other than the local
      * interface. */
-    if (iface->dp_ifidx != ODPP_LOCAL
-        && iface_is_internal(br, iface->name)) {
+    if (iface->dp_ifidx != ODPP_LOCAL && !strcmp(iface->type, "internal")) {
         iface_set_mac(iface);
     }
 
@@ -477,6 +510,7 @@ iterate_and_prune_ifaces(struct bridge *br,
             if (cb(br, iface, aux)) {
                 j++;
             } else {
+                iface_set_ofport(iface->cfg, -1);
                 iface_destroy(iface);
             }
         }
@@ -546,7 +580,7 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg)
     /* Collect old and new bridges. */
     shash_init(&old_br);
     shash_init(&new_br);
-    LIST_FOR_EACH (br, struct bridge, node, &all_bridges) {
+    LIST_FOR_EACH (br, node, &all_bridges) {
         shash_add(&old_br, br->name, br);
     }
     for (i = 0; i < ovs_cfg->n_bridges; i++) {
@@ -557,7 +591,7 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg)
     }
 
     /* Get rid of deleted bridges and add new bridges. */
-    LIST_FOR_EACH_SAFE (br, next, struct bridge, node, &all_bridges) {
+    LIST_FOR_EACH_SAFE (br, next, node, &all_bridges) {
         struct ovsrec_bridge *br_cfg = shash_find_data(&new_br, br->name);
         if (br_cfg) {
             br->cfg = br_cfg;
@@ -584,7 +618,7 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg)
     shash_destroy(&new_br);
 
     /* Reconfigure all bridges. */
-    LIST_FOR_EACH (br, struct bridge, node, &all_bridges) {
+    LIST_FOR_EACH (br, node, &all_bridges) {
         bridge_reconfigure_one(br);
     }
 
@@ -593,7 +627,7 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg)
      * 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. */
-    LIST_FOR_EACH (br, struct bridge, node, &all_bridges) {
+    LIST_FOR_EACH (br, node, &all_bridges) {
         struct odp_port *dpif_ports;
         size_t n_dpif_ports;
         struct shash want_ifaces;
@@ -615,40 +649,68 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg)
         shash_destroy(&want_ifaces);
         free(dpif_ports);
     }
-    LIST_FOR_EACH (br, struct bridge, node, &all_bridges) {
+    LIST_FOR_EACH (br, node, &all_bridges) {
         struct odp_port *dpif_ports;
         size_t n_dpif_ports;
         struct shash cur_ifaces, want_ifaces;
-        struct shash_node *node;
 
         /* Get the set of interfaces currently in this datapath. */
         dpif_port_list(br->dpif, &dpif_ports, &n_dpif_ports);
         shash_init(&cur_ifaces);
         for (i = 0; i < n_dpif_ports; i++) {
             const char *name = dpif_ports[i].devname;
-            shash_add_once(&cur_ifaces, name, NULL);
+            shash_add_once(&cur_ifaces, name, &dpif_ports[i]);
         }
-        free(dpif_ports);
 
         /* Get the set of interfaces we want on this datapath. */
         bridge_get_all_ifaces(br, &want_ifaces);
 
+        hmap_clear(&br->ifaces);
         SHASH_FOR_EACH (node, &want_ifaces) {
             const char *if_name = node->name;
             struct iface *iface = node->data;
-
-            if (shash_find(&cur_ifaces, if_name)) {
-                /* Already exists, just reconfigure it. */
+            bool internal = !iface || !strcmp(iface->type, "internal");
+            struct odp_port *dpif_port = shash_find_data(&cur_ifaces, if_name);
+            int error;
+
+            /* If we have a port or a netdev already, and it's not the type we
+             * want, then delete the port (if any) and close the netdev (if
+             * any). */
+            if (internal
+                ? dpif_port && !(dpif_port->flags & ODP_PORT_INTERNAL)
+                : (iface->netdev
+                   && strcmp(iface->type, netdev_get_type(iface->netdev))))
+            {
+                if (dpif_port) {
+                    error = ofproto_port_del(br->ofproto, dpif_port->port);
+                    if (error) {
+                        continue;
+                    }
+                    dpif_port = NULL;
+                }
                 if (iface) {
-                    reconfigure_iface(iface->cfg, iface);
+                    netdev_close(iface->netdev);
+                    iface->netdev = NULL;
                 }
-            } else {
-                /* Need to add to datapath. */
-                bool internal;
-                int error;
+            }
 
-                /* Add to datapath. */
-                internal = iface_is_internal(br, if_name);
+            /* If it's not an internal port, open (possibly create) the
+             * netdev. */
+            if (!internal) {
+                if (!iface->netdev) {
+                    error = create_iface_netdev(iface);
+                    if (error) {
+                        VLOG_WARN("could not create iface %s: %s", iface->name,
+                                  strerror(error));
+                        continue;
+                    }
+                } else {
+                    reconfigure_iface_netdev(iface);
+                }
+            }
+
+            /* If it's not part of the datapath, add it. */
+            if (!dpif_port) {
                 error = dpif_port_add(br->dpif, if_name,
                                       internal ? ODP_PORT_INTERNAL : 0, NULL);
                 if (error == EFBIG) {
@@ -658,14 +720,30 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg)
                 } else if (error) {
                     VLOG_ERR("failed to add %s interface to %s: %s",
                              if_name, dpif_name(br->dpif), strerror(error));
+                    continue;
+                }
+            }
+
+            /* If it's an internal port, open the netdev. */
+            if (internal) {
+                if (iface && !iface->netdev) {
+                    error = create_iface_netdev(iface);
+                    if (error) {
+                        VLOG_WARN("could not create iface %s: %s", iface->name,
+                                  strerror(error));
+                        continue;
+                    }
                 }
+            } else {
+                assert(iface->netdev != NULL);
             }
         }
+        free(dpif_ports);
         shash_destroy(&cur_ifaces);
         shash_destroy(&want_ifaces);
     }
     sflow_bridge_number = 0;
-    LIST_FOR_EACH (br, struct bridge, node, &all_bridges) {
+    LIST_FOR_EACH (br, node, &all_bridges) {
         uint8_t ea[8];
         uint64_t dpid;
         struct iface *local_iface;
@@ -674,8 +752,7 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg)
 
         bridge_fetch_dp_ifaces(br);
 
-        iterate_and_prune_ifaces(br, check_iface_netdev, NULL);
-        iterate_and_prune_ifaces(br, check_iface_dp_ifidx, NULL);
+        iterate_and_prune_ifaces(br, check_iface, NULL);
 
         /* Pick local port hardware address, datapath ID. */
         bridge_pick_local_hw_addr(br, ea, &hw_addr_iface);
@@ -739,7 +816,7 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg)
             opts.collectors.n = nf_cfg->n_targets;
             opts.collectors.names = nf_cfg->targets;
             if (ofproto_set_netflow(br->ofproto, &opts)) {
-                VLOG_ERR("bridge %s: problem setting netflow collectors", 
+                VLOG_ERR("bridge %s: problem setting netflow collectors",
                          br->name);
             }
         } else {
@@ -752,7 +829,6 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg)
             struct ovsrec_controller **controllers;
             struct ofproto_sflow_options oso;
             size_t n_controllers;
-            size_t i;
 
             memset(&oso, 0, sizeof oso);
 
@@ -803,7 +879,7 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg)
          * the datapath ID before the controller. */
         bridge_reconfigure_remotes(br, managers, n_managers);
     }
-    LIST_FOR_EACH (br, struct bridge, node, &all_bridges) {
+    LIST_FOR_EACH (br, node, &all_bridges) {
         for (i = 0; i < br->n_ports; i++) {
             struct port *port = br->ports[i];
             int j;
@@ -816,7 +892,7 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg)
             }
         }
     }
-    LIST_FOR_EACH (br, struct bridge, node, &all_bridges) {
+    LIST_FOR_EACH (br, node, &all_bridges) {
         iterate_and_prune_ifaces(br, set_iface_properties, NULL);
     }
 
@@ -1085,6 +1161,20 @@ iface_refresh_stats(struct iface *iface)
     ovsrec_interface_set_statistics(iface->cfg, keys, values, n);
 }
 
+static void
+refresh_system_stats(const struct ovsrec_open_vswitch *cfg)
+{
+    struct ovsdb_datum datum;
+    struct shash stats;
+
+    shash_init(&stats);
+    get_system_stats(&stats);
+
+    ovsdb_datum_from_shash(&datum, &stats);
+    ovsdb_idl_txn_write(&cfg->header_, &ovsrec_open_vswitch_col_statistics,
+                        &datum);
+}
+
 void
 bridge_run(void)
 {
@@ -1096,7 +1186,7 @@ bridge_run(void)
 
     /* Let each bridge do the work that it needs to do. */
     datapath_destroyed = false;
-    LIST_FOR_EACH (br, struct bridge, node, &all_bridges) {
+    LIST_FOR_EACH (br, node, &all_bridges) {
         int error = bridge_run_one(br);
         if (error) {
             static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
@@ -1140,28 +1230,31 @@ bridge_run(void)
     }
 #endif
 
-    /* Refresh interface stats if necessary. */
-    if (time_msec() >= iface_stats_timer) {
-        struct ovsdb_idl_txn *txn;
+    /* Refresh system and interface stats if necessary. */
+    if (time_msec() >= stats_timer) {
+        if (cfg) {
+            struct ovsdb_idl_txn *txn;
 
-        txn = ovsdb_idl_txn_create(idl);
-        LIST_FOR_EACH (br, struct bridge, node, &all_bridges) {
-            size_t i;
+            txn = ovsdb_idl_txn_create(idl);
+            LIST_FOR_EACH (br, node, &all_bridges) {
+                size_t i;
 
-            for (i = 0; i < br->n_ports; i++) {
-                struct port *port = br->ports[i];
-                size_t j;
+                for (i = 0; i < br->n_ports; i++) {
+                    struct port *port = br->ports[i];
+                    size_t j;
 
-                for (j = 0; j < port->n_ifaces; j++) {
-                    struct iface *iface = port->ifaces[j];
-                    iface_refresh_stats(iface);
+                    for (j = 0; j < port->n_ifaces; j++) {
+                        struct iface *iface = port->ifaces[j];
+                        iface_refresh_stats(iface);
+                    }
                 }
             }
+            refresh_system_stats(cfg);
+            ovsdb_idl_txn_commit(txn);
+            ovsdb_idl_txn_destroy(txn); /* XXX */
         }
-        ovsdb_idl_txn_commit(txn);
-        ovsdb_idl_txn_destroy(txn); /* XXX */
 
-        iface_stats_timer = time_msec() + IFACE_STATS_INTERVAL;
+        stats_timer = time_msec() + STATS_INTERVAL;
     }
 }
 
@@ -1170,9 +1263,9 @@ bridge_wait(void)
 {
     struct bridge *br;
 
-    LIST_FOR_EACH (br, struct bridge, node, &all_bridges) {
+    LIST_FOR_EACH (br, node, &all_bridges) {
         ofproto_wait(br->ofproto);
-        if (ofproto_has_controller(br->ofproto)) {
+        if (ofproto_has_primary_controller(br->ofproto)) {
             continue;
         }
 
@@ -1180,7 +1273,7 @@ bridge_wait(void)
         bond_wait(br);
     }
     ovsdb_idl_wait(idl);
-    poll_timer_wait_until(iface_stats_timer);
+    poll_timer_wait_until(stats_timer);
 }
 
 /* Forces 'br' to revalidate all of its flows.  This is appropriate when 'br''s
@@ -1229,7 +1322,7 @@ bridge_unixctl_fdb_show(struct unixctl_conn *conn,
     }
 
     ds_put_cstr(&ds, " port  VLAN  MAC                Age\n");
-    LIST_FOR_EACH (e, struct mac_entry, lru_node, &br->ml->lrus) {
+    LIST_FOR_EACH (e, lru_node, &br->ml->lrus) {
         if (e->port < 0 || e->port >= br->n_ports) {
             continue;
         }
@@ -1275,7 +1368,7 @@ bridge_create(const struct ovsrec_bridge *br_cfg)
     br->ml = mac_learning_create();
     eth_addr_nicira_random(br->default_ea);
 
-    port_array_init(&br->ifaces);
+    hmap_init(&br->ifaces);
 
     shash_init(&br->port_by_name);
     shash_init(&br->iface_by_name);
@@ -1307,7 +1400,7 @@ bridge_destroy(struct bridge *br)
         dpif_close(br->dpif);
         ofproto_destroy(br->ofproto);
         mac_learning_destroy(br->ml);
-        port_array_destroy(&br->ifaces);
+        hmap_destroy(&br->ifaces);
         shash_destroy(&br->port_by_name);
         shash_destroy(&br->iface_by_name);
         free(br->ports);
@@ -1321,7 +1414,7 @@ bridge_lookup(const char *name)
 {
     struct bridge *br;
 
-    LIST_FOR_EACH (br, struct bridge, node, &all_bridges) {
+    LIST_FOR_EACH (br, node, &all_bridges) {
         if (!strcmp(br->name, name)) {
             return br;
         }
@@ -1337,7 +1430,7 @@ bridge_unixctl_dump_flows(struct unixctl_conn *conn,
 {
     struct bridge *br;
     struct ds results;
-    
+
     br = bridge_lookup(args);
     if (!br) {
         unixctl_command_reply(conn, 501, "Unknown bridge");
@@ -1367,7 +1460,7 @@ bridge_unixctl_reconnect(struct unixctl_conn *conn,
         }
         ofproto_reconnect_controllers(br->ofproto);
     } else {
-        LIST_FOR_EACH (br, struct bridge, node, &all_bridges) {
+        LIST_FOR_EACH (br, node, &all_bridges) {
             ofproto_reconnect_controllers(br->ofproto);
         }
     }
@@ -1418,7 +1511,6 @@ static void
 bridge_reconfigure_one(struct bridge *br)
 {
     struct shash old_ports, new_ports;
-    struct svec listeners, old_listeners;
     struct svec snoops, old_snoops;
     struct shash_node *node;
     enum ofproto_fail_mode fail_mode;
@@ -1495,8 +1587,8 @@ bridge_reconfigure_one(struct bridge *br)
                 || !strcmp(br->cfg->fail_mode, "standalone")
                     ? OFPROTO_FAIL_STANDALONE
                     : OFPROTO_FAIL_SECURE;
-    if ((ofproto_get_fail_mode(br->ofproto) != fail_mode)
-            && !ofproto_has_controller(br->ofproto)) {
+    if (ofproto_get_fail_mode(br->ofproto) != fail_mode
+        && !ofproto_has_primary_controller(br->ofproto)) {
         ofproto_flush_flows(br->ofproto);
     }
     ofproto_set_fail_mode(br->ofproto, fail_mode);
@@ -1505,18 +1597,6 @@ bridge_reconfigure_one(struct bridge *br)
      * versa.  (XXX Should we delete all flows if we are switching from one
      * controller to another?) */
 
-    /* Configure OpenFlow management listener. */
-    svec_init(&listeners);
-    svec_add_nocopy(&listeners, xasprintf("punix:%s/%s.mgmt",
-                                          ovs_rundir, br->name));
-    svec_init(&old_listeners);
-    ofproto_get_listeners(br->ofproto, &old_listeners);
-    if (!svec_equal(&listeners, &old_listeners)) {
-        ofproto_set_listeners(br->ofproto, &listeners);
-    }
-    svec_destroy(&listeners);
-    svec_destroy(&old_listeners);
-
     /* Configure OpenFlow controller connection snooping. */
     svec_init(&snoops);
     svec_add_nocopy(&snoops, xasprintf("punix:%s/%s.snoop",
@@ -1532,6 +1612,90 @@ bridge_reconfigure_one(struct bridge *br)
     mirror_reconfigure(br);
 }
 
+/* Initializes 'oc' appropriately as a management service controller for
+ * 'br'.
+ *
+ * The caller must free oc->target when it is no longer needed. */
+static void
+bridge_ofproto_controller_for_mgmt(const struct bridge *br,
+                                   struct ofproto_controller *oc)
+{
+    oc->target = xasprintf("punix:%s/%s.mgmt", ovs_rundir, br->name);
+    oc->max_backoff = 0;
+    oc->probe_interval = 60;
+    oc->band = OFPROTO_OUT_OF_BAND;
+    oc->accept_re = NULL;
+    oc->update_resolv_conf = false;
+    oc->rate_limit = 0;
+    oc->burst_limit = 0;
+}
+
+/* Converts ovsrec_controller 'c' into an ofproto_controller in 'oc'.  */
+static void
+bridge_ofproto_controller_from_ovsrec(const struct ovsrec_controller *c,
+                                      struct ofproto_controller *oc)
+{
+    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->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);
+}
+
+/* Configures the IP stack for 'br''s local interface properly according to the
+ * configuration in 'c'.  */
+static void
+bridge_configure_local_iface_netdev(struct bridge *br,
+                                    struct ovsrec_controller *c)
+{
+    struct netdev *netdev;
+    struct in_addr mask, gateway;
+
+    struct iface *local_iface;
+    struct in_addr ip;
+
+    /* Controller discovery does its own TCP/IP configuration later. */
+    if (strcmp(c->target, "discover")) {
+        return;
+    }
+
+    /* If there's no local interface or no IP address, give up. */
+    local_iface = bridge_get_local_iface(br);
+    if (!local_iface || !c->local_ip || !inet_aton(c->local_ip, &ip)) {
+        return;
+    }
+
+    /* Bring up the local interface. */
+    netdev = local_iface->netdev;
+    netdev_turn_flags_on(netdev, NETDEV_UP, true);
+
+    /* Configure the IP address and netmask. */
+    if (!c->local_netmask
+        || !inet_aton(c->local_netmask, &mask)
+        || !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));
+    }
+
+    /* Configure the default gateway. */
+    if (c->local_gateway
+        && inet_aton(c->local_gateway, &gateway)
+        && gateway.s_addr) {
+        if (!netdev_add_router(netdev, gateway)) {
+            VLOG_INFO("bridge %s: configured gateway "IP_FMT,
+                      br->name, IP_ARGS(&gateway.s_addr));
+        }
+    }
+}
+
 static void
 bridge_reconfigure_remotes(struct bridge *br,
                            const struct sockaddr_in *managers,
@@ -1539,99 +1703,81 @@ bridge_reconfigure_remotes(struct bridge *br,
 {
     struct ovsrec_controller **controllers;
     size_t n_controllers;
+    bool had_primary;
+    const char *disable_ib_str;
+    bool disable_in_band = false;
 
-    ofproto_set_extra_in_band_remotes(br->ofproto, managers, n_managers);
+    struct ofproto_controller *ocs;
+    size_t n_ocs;
+    size_t i;
 
-    n_controllers = bridge_get_controllers(br, &controllers);
-    if (ofproto_has_controller(br->ofproto) != (n_controllers != 0)) {
-        ofproto_flush_flows(br->ofproto);
+
+    /* Check if we should disable in-band control on this bridge. */
+    disable_ib_str = bridge_get_other_config(br->cfg, "disable-in-band");
+    if (disable_ib_str && !strcmp(disable_ib_str, "true")) {
+        disable_in_band = true;
     }
 
-    if (!n_controllers) {
-        union ofp_action action;
-        flow_t flow;
-
-        /* Clear out controllers. */
-        ofproto_set_controllers(br->ofproto, NULL, 0);
-
-        /* If there are no controllers and the bridge is in standalone
-         * mode, set up a flow that matches every packet and directs
-         * them to OFPP_NORMAL (which goes to us).  Otherwise, the
-         * switch is in secure mode and we won't pass any traffic until
-         * a controller has been defined and it tells us to do so. */
-        if (ofproto_get_fail_mode(br->ofproto) == OFPROTO_FAIL_STANDALONE) {
-            memset(&action, 0, sizeof action);
-            action.type = htons(OFPAT_OUTPUT);
-            action.output.len = htons(sizeof action);
-            action.output.port = htons(OFPP_NORMAL);
-            memset(&flow, 0, sizeof flow);
-            ofproto_add_flow(br->ofproto, &flow, OVSFW_ALL, 0, &action, 1, 0);
-        }
+    if (disable_in_band) {
+        ofproto_set_extra_in_band_remotes(br->ofproto, NULL, 0);
     } else {
-        struct ofproto_controller *ocs;
-        size_t i;
+        ofproto_set_extra_in_band_remotes(br->ofproto, managers, n_managers);
+    }
+    had_primary = ofproto_has_primary_controller(br->ofproto);
 
-        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];
+    n_controllers = bridge_get_controllers(br, &controllers);
 
-            if (strcmp(c->target, "discover")) {
-                struct iface *local_iface;
-                struct in_addr ip;
+    ocs = xmalloc((n_controllers + 1) * sizeof *ocs);
+    n_ocs = 0;
 
-                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;
+    bridge_ofproto_controller_for_mgmt(br, &ocs[n_ocs++]);
+    for (i = 0; i < n_controllers; i++) {
+        struct ovsrec_controller *c = controllers[i];
 
-                    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 (!strncmp(c->target, "punix:", 6)
+            || !strncmp(c->target, "unix:", 5)) {
+            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
 
-                    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));
-                        }
-                    }
-                }
-            }
+            /* Prevent remote ovsdb-server users from accessing arbitrary Unix
+             * domain sockets and overwriting arbitrary local files. */
+            VLOG_ERR_RL(&rl, "%s: not adding Unix domain socket controller "
+                        "\"%s\" due to possibility for remote exploit",
+                        dpif_name(br->dpif), c->target);
+            continue;
+        }
 
-            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->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);
+        bridge_configure_local_iface_netdev(br, c);
+        bridge_ofproto_controller_from_ovsrec(c, &ocs[n_ocs]);
+        if (disable_in_band) {
+            ocs[n_ocs].band = OFPROTO_OUT_OF_BAND;
         }
-        ofproto_set_controllers(br->ofproto, ocs, n_controllers);
-        free(ocs);
+        n_ocs++;
+    }
+
+    ofproto_set_controllers(br->ofproto, ocs, n_ocs);
+    free(ocs[0].target); /* From bridge_ofproto_controller_for_mgmt(). */
+    free(ocs);
+
+    if (had_primary != ofproto_has_primary_controller(br->ofproto)) {
+        ofproto_flush_flows(br->ofproto);
+    }
+
+    /* If there are no controllers and the bridge is in standalone
+     * mode, set up a flow that matches every packet and directs
+     * them to OFPP_NORMAL (which goes to us).  Otherwise, the
+     * switch is in secure mode and we won't pass any traffic until
+     * a controller has been defined and it tells us to do so. */
+    if (!n_controllers
+        && ofproto_get_fail_mode(br->ofproto) == OFPROTO_FAIL_STANDALONE) {
+        union ofp_action action;
+        struct flow flow;
+
+        memset(&action, 0, sizeof action);
+        action.type = htons(OFPAT_OUTPUT);
+        action.output.len = htons(sizeof action);
+        action.output.port = htons(OFPP_NORMAL);
+        memset(&flow, 0, sizeof flow);
+        ofproto_add_flow(br->ofproto, &flow, OVSFW_ALL, 0, &action, 1, 0);
     }
 }
 
@@ -1676,7 +1822,7 @@ bridge_fetch_dp_ifaces(struct bridge *br)
             iface->dp_ifidx = -1;
         }
     }
-    port_array_clear(&br->ifaces);
+    hmap_clear(&br->ifaces);
 
     dpif_port_list(br->dpif, &dpif_ports, &n_dpif_ports);
     for (i = 0; i < n_dpif_ports; i++) {
@@ -1690,16 +1836,15 @@ bridge_fetch_dp_ifaces(struct bridge *br)
                 VLOG_WARN("%s reported interface %"PRIu16" twice",
                           dpif_name(br->dpif), p->port);
             } else {
-                port_array_set(&br->ifaces, p->port, iface);
                 iface->dp_ifidx = p->port;
+                hmap_insert(&br->ifaces, &iface->dp_ifidx_node,
+                            hash_int(iface->dp_ifidx, 0));
             }
 
-            if (iface->cfg) {
-                int64_t ofport = (iface->dp_ifidx >= 0
-                                  ? odp_port_to_ofp_port(iface->dp_ifidx)
-                                  : -1);
-                ovsrec_interface_set_ofport(iface->cfg, &ofport, 1);
-            }
+            iface_set_ofport(iface->cfg,
+                             (iface->dp_ifidx >= 0
+                              ? odp_port_to_ofp_port(iface->dp_ifidx)
+                              : -1));
         }
     }
     free(dpif_ports);
@@ -1927,6 +2072,22 @@ bond_run(struct bridge *br)
         struct port *port = br->ports[i];
 
         if (port->n_ifaces >= 2) {
+            char *devname;
+
+            /* Track carrier going up and down on interfaces. */
+            while (!netdev_monitor_poll(port->monitor, &devname)) {
+                struct iface *iface;
+
+                iface = port_lookup_iface(port, devname);
+                if (iface) {
+                    bool carrier = netdev_get_carrier(iface->netdev);
+
+                    bond_link_status_update(iface, carrier);
+                    port_update_bond_compat(port);
+                }
+                free(devname);
+            }
+
             for (j = 0; j < port->n_ifaces; j++) {
                 struct iface *iface = port->ifaces[j];
                 if (time_msec() >= iface->delay_expires) {
@@ -1958,6 +2119,7 @@ bond_wait(struct bridge *br)
         if (port->n_ifaces < 2) {
             continue;
         }
+        netdev_monitor_poll_wait(port->monitor);
         for (j = 0; j < port->n_ifaces; j++) {
             struct iface *iface = port->ifaces[j];
             if (iface->delay_expires != LLONG_MAX) {
@@ -1971,7 +2133,7 @@ bond_wait(struct bridge *br)
 }
 
 static bool
-set_dst(struct dst *p, const flow_t *flow,
+set_dst(struct dst *p, const struct flow *flow,
         const struct port *in_port, const struct port *out_port,
         tag_type *tags)
 {
@@ -2059,8 +2221,22 @@ port_includes_vlan(const struct port *port, uint16_t vlan)
     return vlan == port->vlan || port_trunks_vlan(port, vlan);
 }
 
+static bool
+port_is_floodable(const struct port *port)
+{
+    int i;
+
+    for (i = 0; i < port->n_ifaces; i++) {
+        if (!ofproto_port_is_floodable(port->bridge->ofproto,
+                                       port->ifaces[i]->dp_ifidx)) {
+            return false;
+        }
+    }
+    return true;
+}
+
 static size_t
-compose_dsts(const struct bridge *br, const flow_t *flow, uint16_t vlan,
+compose_dsts(const struct bridge *br, const struct flow *flow, uint16_t vlan,
              const struct port *in_port, const struct port *out_port,
              struct dst dsts[], tag_type *tags, uint16_t *nf_output_iface)
 {
@@ -2073,7 +2249,9 @@ compose_dsts(const struct bridge *br, const flow_t *flow, uint16_t vlan,
         /* XXX even better, define each VLAN as a datapath port group */
         for (i = 0; i < br->n_ports; i++) {
             struct port *port = br->ports[i];
-            if (port != in_port && port_includes_vlan(port, vlan)
+            if (port != in_port
+                && port_is_floodable(port)
+                && port_includes_vlan(port, vlan)
                 && !port->is_mirror_output_port
                 && set_dst(dst, flow, in_port, port, tags)) {
                 mirrors |= port->dst_mirrors;
@@ -2148,7 +2326,7 @@ print_dsts(const struct dst *dsts, size_t n)
 }
 
 static void
-compose_actions(struct bridge *br, const flow_t *flow, uint16_t vlan,
+compose_actions(struct bridge *br, const struct flow *flow, uint16_t vlan,
                 const struct port *in_port, const struct port *out_port,
                 tag_type *tags, struct odp_actions *actions,
                 uint16_t *nf_output_iface)
@@ -2168,8 +2346,9 @@ compose_actions(struct bridge *br, const flow_t *flow, uint16_t vlan,
             if (p->vlan == OFP_VLAN_NONE) {
                 odp_actions_add(actions, ODPAT_STRIP_VLAN);
             } else {
-                a = odp_actions_add(actions, ODPAT_SET_VLAN_VID);
-                a->vlan_vid.vlan_vid = htons(p->vlan);
+                a = odp_actions_add(actions, ODPAT_SET_DL_TCI);
+                a->dl_tci.tci = htons(p->vlan & VLAN_VID_MASK);
+                a->dl_tci.tci |= htons(flow->dl_vlan_pcp << VLAN_PCP_SHIFT);
             }
             cur_vlan = p->vlan;
         }
@@ -2182,7 +2361,7 @@ compose_actions(struct bridge *br, const flow_t *flow, uint16_t vlan,
  * 802.1Q header and implicitly tagged ports.  A value of 0 indicates that
  * the packet is untagged and -1 indicates it has an invalid header and
  * should be dropped. */
-static int flow_get_vlan(struct bridge *br, const flow_t *flow,
+static int flow_get_vlan(struct bridge *br, const struct flow *flow,
                          struct port *in_port, bool have_packet)
 {
     /* Note that dl_vlan of 0 and of OFP_VLAN_NONE both mean that the packet
@@ -2228,7 +2407,7 @@ static int flow_get_vlan(struct bridge *br, const flow_t *flow,
  * migration.  Older Citrix-patched Linux DomU used gratuitous ARP replies to
  * indicate this; newer upstream kernels use gratuitous ARP requests. */
 static bool
-is_gratuitous_arp(const flow_t *flow)
+is_gratuitous_arp(const struct flow *flow)
 {
     return (flow->dl_type == htons(ETH_TYPE_ARP)
             && eth_addr_is_broadcast(flow->dl_dst)
@@ -2238,7 +2417,7 @@ is_gratuitous_arp(const flow_t *flow)
 }
 
 static void
-update_learning_table(struct bridge *br, const flow_t *flow, int vlan,
+update_learning_table(struct bridge *br, const struct flow *flow, int vlan,
                       struct port *in_port)
 {
     enum grat_arp_lock_type lock_type;
@@ -2286,7 +2465,7 @@ update_learning_table(struct bridge *br, const flow_t *flow, int vlan,
  * so in one special case.
  */
 static bool
-is_admissible(struct bridge *br, const flow_t *flow, bool have_packet,
+is_admissible(struct bridge *br, const struct flow *flow, bool have_packet,
               tag_type *tags, int *vlanp, struct port **in_portp)
 {
     struct iface *in_iface;
@@ -2312,7 +2491,7 @@ is_admissible(struct bridge *br, const flow_t *flow, bool have_packet,
             static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
 
             VLOG_WARN_RL(&rl, "bridge %s: received packet on unknown "
-                         "interface %"PRIu16, br->name, flow->in_port); 
+                         "interface %"PRIu16, br->name, flow->in_port);
         }
 
         *in_portp = NULL;
@@ -2375,7 +2554,7 @@ is_admissible(struct bridge *br, const flow_t *flow, bool have_packet,
  * 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,
+process_flow(struct bridge *br, const struct flow *flow,
              const struct ofpbuf *packet, struct odp_actions *actions,
              tag_type *tags, uint16_t *nf_output_iface)
 {
@@ -2425,56 +2604,20 @@ done:
     return true;
 }
 
-/* Careful: 'opp' is in host byte order and opp->port_no is an OFP port
- * number. */
-static void
-bridge_port_changed_ofhook_cb(enum ofp_port_reason reason,
-                              const struct ofp_phy_port *opp,
-                              void *br_)
-{
-    struct bridge *br = br_;
-    struct iface *iface;
-    struct port *port;
-
-    iface = iface_from_dp_ifidx(br, ofp_port_to_odp_port(opp->port_no));
-    if (!iface) {
-        return;
-    }
-    port = iface->port;
-
-    if (reason == OFPPR_DELETE) {
-        VLOG_WARN("bridge %s: interface %s deleted unexpectedly",
-                  br->name, iface->name);
-        iface_destroy(iface);
-        if (!port->n_ifaces) {
-            VLOG_WARN("bridge %s: port %s has no interfaces, dropping",
-                      br->name, port->name);
-            port_destroy(port);
-        }
-
-        bridge_flush(br);
-    } else {
-        if (port->n_ifaces > 1) {
-            bool up = !(opp->state & OFPPS_LINK_DOWN);
-            bond_link_status_update(iface, up);
-            port_update_bond_compat(port);
-        }
-    }
-}
-
 static bool
-bridge_normal_ofhook_cb(const flow_t *flow, const struct ofpbuf *packet,
+bridge_normal_ofhook_cb(const struct flow *flow, const struct ofpbuf *packet,
                         struct odp_actions *actions, tag_type *tags,
                         uint16_t *nf_output_iface, void *br_)
 {
     struct bridge *br = br_;
 
     COVERAGE_INC(bridge_process_flow);
+
     return process_flow(br, flow, packet, actions, tags, nf_output_iface);
 }
 
 static void
-bridge_account_flow_ofhook_cb(const flow_t *flow,
+bridge_account_flow_ofhook_cb(const struct flow *flow, tag_type tags,
                               const union odp_action *actions,
                               size_t n_actions, unsigned long long int n_bytes,
                               void *br_)
@@ -2482,20 +2625,24 @@ bridge_account_flow_ofhook_cb(const flow_t *flow,
     struct bridge *br = br_;
     const union odp_action *a;
     struct port *in_port;
-    tag_type tags = 0;
+    tag_type dummy = 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. */
-    if (is_admissible(br, flow, false, &tags, &vlan, &in_port)) {
+    /* 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.
+     *
+     * We test that 'tags' is nonzero to ensure that only flows that include an
+     * OFPP_NORMAL action are used for learning.  This works because
+     * bridge_normal_ofhook_cb() always sets a nonzero tag value. */
+    if (tags && is_admissible(br, flow, false, &dummy, &vlan, &in_port)) {
         update_learning_table(br, flow, vlan, in_port);
     }
 
+    /* Account for bond slave utilization. */
     if (!br->has_bonded_ports) {
         return;
     }
-
     for (a = actions; a < &actions[n_actions]; a++) {
         if (a->type == ODPAT_OUTPUT) {
             struct port *out_port = port_from_dp_ifidx(br, a->output.port);
@@ -2530,7 +2677,6 @@ bridge_account_checkpoint_ofhook_cb(void *br_)
 }
 
 static struct ofhooks bridge_ofhooks = {
-    bridge_port_changed_ofhook_cb,
     bridge_normal_ofhook_cb,
     bridge_account_flow_ofhook_cb,
     bridge_account_checkpoint_ofhook_cb,
@@ -2768,7 +2914,6 @@ bond_rebalance_port(struct port *port)
              * smallest hashes instead of the biggest ones.  There is little
              * reason behind this decision; we could use the opposite sort
              * order to shift away big hashes ahead of small ones. */
-            size_t i;
             bool order_swapped;
 
             for (i = 0; i < from->n_hashes; i++) {
@@ -2849,11 +2994,11 @@ bond_send_learning_packets(struct port *port)
 
     ofpbuf_init(&packet, 128);
     error = n_packets = n_errors = 0;
-    LIST_FOR_EACH (e, struct mac_entry, lru_node, &br->ml->lrus) {
+    LIST_FOR_EACH (e, lru_node, &br->ml->lrus) {
         union ofp_action actions[2], *a;
         uint16_t dp_ifidx;
         tag_type tags = 0;
-        flow_t flow;
+        struct flow flow;
         int retval;
 
         if (e->port == port->port_idx
@@ -2911,7 +3056,7 @@ bond_unixctl_list(struct unixctl_conn *conn,
 
     ds_put_cstr(&ds, "bridge\tbond\tslaves\n");
 
-    LIST_FOR_EACH (br, struct bridge, node, &all_bridges) {
+    LIST_FOR_EACH (br, node, &all_bridges) {
         size_t i;
 
         for (i = 0; i < br->n_ports; i++) {
@@ -2940,7 +3085,7 @@ bond_find(const char *name)
 {
     const struct bridge *br;
 
-    LIST_FOR_EACH (br, struct bridge, node, &all_bridges) {
+    LIST_FOR_EACH (br, node, &all_bridges) {
         size_t i;
 
         for (i = 0; i < br->n_ports; i++) {
@@ -3000,8 +3145,7 @@ bond_unixctl_show(struct unixctl_conn *conn,
                           hash, be->tx_bytes / 1024);
 
             /* MACs. */
-            LIST_FOR_EACH (me, struct mac_entry, lru_node,
-                           &port->bridge->ml->lrus) {
+            LIST_FOR_EACH (me, lru_node, &port->bridge->ml->lrus) {
                 uint16_t dp_ifidx;
                 tag_type tags = 0;
                 if (bond_hash(me->mac) == hash
@@ -3286,7 +3430,7 @@ port_reconfigure(struct port *port, const struct ovsrec_port *cfg)
     if (port->updelay < 0) {
         port->updelay = 0;
     }
-    port->updelay = cfg->bond_downdelay;
+    port->downdelay = cfg->bond_downdelay;
     if (port->downdelay < 0) {
         port->downdelay = 0;
     }
@@ -3309,6 +3453,7 @@ port_reconfigure(struct port *port, const struct ovsrec_port *cfg)
         if (!shash_add_once(&new_ifaces, if_cfg->name, NULL)) {
             VLOG_WARN("port %s: %s specified twice as port interface",
                       port->name, if_cfg->name);
+            iface_set_ofport(if_cfg, -1);
             continue;
         }
 
@@ -3322,8 +3467,15 @@ port_reconfigure(struct port *port, const struct ovsrec_port *cfg)
             }
             iface->cfg = if_cfg;
         } else {
-            iface_create(port, if_cfg);
+            iface = iface_create(port, if_cfg);
         }
+
+        /* Determine interface type.  The local port always has type
+         * "internal".  Other ports take their type from the database and
+         * default to "system" if none is specified. */
+        iface->type = (!strcmp(if_cfg->name, port->bridge->name) ? "internal"
+                       : if_cfg->type[0] ? if_cfg->type
+                       : "system");
     }
     shash_destroy(&new_ifaces);
 
@@ -3353,7 +3505,6 @@ port_reconfigure(struct port *port, const struct ovsrec_port *cfg)
     trunks = NULL;
     if (vlan < 0 && cfg->n_trunks) {
         size_t n_errors;
-        size_t i;
 
         trunks = bitmap_allocate(4096);
         n_errors = 0;
@@ -3415,6 +3566,9 @@ port_destroy(struct port *port)
         del = br->ports[port->port_idx] = br->ports[--br->n_ports];
         del->port_idx = port->port_idx;
 
+        VLOG_INFO("destroyed port %s on bridge %s", port->name, br->name);
+
+        netdev_monitor_destroy(port->monitor);
         free(port->ifaces);
         bitmap_free(port->trunks);
         free(port->name);
@@ -3446,6 +3600,10 @@ port_lookup_iface(const struct port *port, const char *name)
 static void
 port_update_bonding(struct port *port)
 {
+    if (port->monitor) {
+        netdev_monitor_destroy(port->monitor);
+        port->monitor = NULL;
+    }
     if (port->n_ifaces < 2) {
         /* Not a bonded port. */
         if (port->bond_hash) {
@@ -3455,9 +3613,9 @@ port_update_bonding(struct port *port)
             port->bond_fake_iface = false;
         }
     } else {
-        if (!port->bond_hash) {
-            size_t i;
+        size_t i;
 
+        if (!port->bond_hash) {
             port->bond_hash = xcalloc(BOND_MASK + 1, sizeof *port->bond_hash);
             for (i = 0; i <= BOND_MASK; i++) {
                 struct bond_entry *e = &port->bond_hash[i];
@@ -3475,6 +3633,11 @@ port_update_bonding(struct port *port)
         }
         port->bond_compat_is_stale = true;
         port->bond_fake_iface = port->cfg->bond_fake_iface;
+
+        port->monitor = netdev_monitor_create();
+        for (i = 0; i < port->n_ifaces; i++) {
+            netdev_monitor_add(port->monitor, port->ifaces[i]->netdev);
+        }
     }
 }
 
@@ -3516,15 +3679,15 @@ port_update_bond_compat(struct port *port)
 
         /* We need to make the same determination as the Linux bonding
          * code to determine whether a slave should be consider "up".
-         * The Linux function bond_miimon_inspect() supports four 
+         * The Linux function bond_miimon_inspect() supports four
          * BOND_LINK_* states:
-         *      
+         *
          *    - BOND_LINK_UP: carrier detected, updelay has passed.
          *    - BOND_LINK_FAIL: carrier lost, downdelay in progress.
          *    - BOND_LINK_DOWN: carrier lost, downdelay has passed.
          *    - BOND_LINK_BACK: carrier detected, updelay in progress.
          *
-         * The function bond_info_show_slave() only considers BOND_LINK_UP 
+         * The function bond_info_show_slave() only considers BOND_LINK_UP
          * to be "up" and anything else to be "down".
          */
         slave->up = iface->enabled && iface->delay_expires == LLONG_MAX;
@@ -3596,7 +3759,6 @@ iface_create(struct port *port, const struct ovsrec_interface *if_cfg)
     struct bridge *br = port->bridge;
     struct iface *iface;
     char *name = if_cfg->name;
-    int error;
 
     iface = xzalloc(sizeof *iface);
     iface->port = port;
@@ -3610,20 +3772,6 @@ iface_create(struct port *port, const struct ovsrec_interface *if_cfg)
 
     shash_add_assert(&br->iface_by_name, iface->name, iface);
 
-    /* Attempt to create the network interface in case it doesn't exist yet. */
-    if (!iface_is_internal(br, iface->name)) {
-        error = set_up_iface(if_cfg, iface, true);
-        if (error) {
-            VLOG_WARN("could not create iface %s: %s", iface->name,
-                      strerror(error));
-
-            shash_find_and_delete_assert(&br->iface_by_name, iface->name);
-            free(iface->name);
-            free(iface);
-            return NULL;
-        }
-    }
-
     if (port->n_ifaces >= port->allocated_ifaces) {
         port->ifaces = x2nrealloc(port->ifaces, &port->allocated_ifaces,
                                   sizeof *port->ifaces);
@@ -3652,7 +3800,7 @@ iface_destroy(struct iface *iface)
         shash_find_and_delete_assert(&br->iface_by_name, iface->name);
 
         if (iface->dp_ifidx >= 0) {
-            port_array_set(&br->ifaces, iface->dp_ifidx, NULL);
+            hmap_remove(&br->ifaces, &iface->dp_ifidx_node);
         }
 
         del = port->ifaces[iface->port_ifidx] = port->ifaces[--port->n_ifaces];
@@ -3681,40 +3829,16 @@ iface_lookup(const struct bridge *br, const char *name)
 
 static struct iface *
 iface_from_dp_ifidx(const struct bridge *br, uint16_t dp_ifidx)
-{
-    return port_array_get(&br->ifaces, dp_ifidx);
-}
-
-/* Returns true if 'iface' is the name of an "internal" interface on bridge
- * 'br', that is, an interface that is entirely simulated within the datapath.
- * The local port (ODPP_LOCAL) is always an internal interface.  Other local
- * interfaces are created by setting "iface.<iface>.internal = true".
- *
- * In addition, we have a kluge-y feature that creates an internal port with
- * the name of a bonded port if "bonding.<bondname>.fake-iface = true" is set.
- * This feature needs to go away in the long term.  Until then, this is one
- * reason why this function takes a name instead of a struct iface: the fake
- * interfaces created this way do not have a struct iface. */
-static bool
-iface_is_internal(const struct bridge *br, const char *if_name)
 {
     struct iface *iface;
-    struct port *port;
-
-    if (!strcmp(if_name, br->name)) {
-        return true;
-    }
 
-    iface = iface_lookup(br, if_name);
-    if (iface && !strcmp(iface->cfg->type, "internal")) {
-        return true;
-    }
-
-    port = port_lookup(br, if_name);
-    if (port && port->n_ifaces > 1 && port->cfg->bond_fake_iface) {
-        return true;
+    HMAP_FOR_EACH_IN_BUCKET (iface, dp_ifidx_node,
+                             hash_int(dp_ifidx, 0), &br->ifaces) {
+        if (iface->dp_ifidx == dp_ifidx) {
+            return iface;
+        }
     }
-    return false;
+    return NULL;
 }
 
 /* Set Ethernet address of 'iface', if one is specified in the configuration
@@ -3741,6 +3865,19 @@ iface_set_mac(struct iface *iface)
     }
 }
 
+/* Sets the ofport column of 'if_cfg' to 'ofport'. */
+static void
+iface_set_ofport(const struct ovsrec_interface *if_cfg, int64_t ofport)
+{
+    if (if_cfg) {
+        ovsrec_interface_set_ofport(if_cfg, &ofport, 1);
+    }
+}
+
+/* Adds the 'n' key-value pairs in 'keys' in 'values' to 'shash'.
+ *
+ * The value strings in '*shash' are taken directly from values[], not copied,
+ * so the caller should not modify or free them. */
 static void
 shash_from_ovs_idl_map(char **keys, char **values, size_t n,
                        struct shash *shash)