nx-match: Implement support for arbitrary VLAN TCI masks.
[openvswitch] / vswitchd / bridge.c
index 73e6d6871d26b84d0602eb936417174c104e3b9b..e7bc5ab33e9eb5bae2406f4b66a7e1bc0338bca3 100644 (file)
@@ -15,6 +15,7 @@
 
 #include <config.h>
 #include "bridge.h"
+#include "byte-order.h"
 #include <assert.h>
 #include <errno.h>
 #include <arpa/inet.h>
@@ -31,6 +32,7 @@
 #include <sys/types.h>
 #include <unistd.h>
 #include "bitmap.h"
+#include "classifier.h"
 #include "coverage.h"
 #include "dirs.h"
 #include "dpif.h"
 #include "vswitchd/vswitch-idl.h"
 #include "xenserver.h"
 #include "vlog.h"
-#include "xtoxll.h"
 #include "sflow_api.h"
 
-VLOG_DEFINE_THIS_MODULE(bridge)
+VLOG_DEFINE_THIS_MODULE(bridge);
 
 struct dst {
     uint16_t vlan;
@@ -147,6 +148,7 @@ struct port {
     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. */
@@ -254,6 +256,7 @@ 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 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,
@@ -271,10 +274,10 @@ void
 bridge_init(const char *remote)
 {
     /* Create connection to database. */
-    idl = ovsdb_idl_create(remote, &ovsrec_idl_class);
+    idl = ovsdb_idl_create(remote, &ovsrec_idl_class, true);
 
-    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_alert(idl, &ovsrec_open_vswitch_col_cur_cfg);
+    ovsdb_idl_omit_alert(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);
@@ -282,8 +285,8 @@ bridge_init(const char *remote)
     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_alert(idl, &ovsrec_interface_col_ofport);
+    ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_statistics);
     ovsdb_idl_omit(idl, &ovsrec_interface_col_external_ids);
 
     /* Register unixctl commands. */
@@ -418,7 +421,7 @@ create_iface_netdev(struct iface *iface)
     error = netdev_open(&netdev_options, &iface->netdev);
 
     if (iface->netdev) {
-        netdev_get_carrier(iface->netdev, &iface->enabled);
+        iface->enabled = netdev_get_carrier(iface->netdev);
     }
 
     shash_destroy(&options);
@@ -508,6 +511,7 @@ iterate_and_prune_ifaces(struct bridge *br,
             if (cb(br, iface, aux)) {
                 j++;
             } else {
+                iface_set_ofport(iface->cfg, -1);
                 iface_destroy(iface);
             }
         }
@@ -530,30 +534,52 @@ iterate_and_prune_ifaces(struct bridge *br,
  * should not be and in fact is not directly involved in that.  But
  * ovs-vswitchd needs to make sure that ovsdb-server can reach the managers, so
  * it has to tell in-band control where the managers are to enable that.
+ * (Thus, only managers connected in-band are collected.)
  */
 static void
-collect_managers(const struct ovsrec_open_vswitch *ovs_cfg,
-                 struct sockaddr_in **managersp, size_t *n_managersp)
+collect_in_band_managers(const struct ovsrec_open_vswitch *ovs_cfg,
+                         struct sockaddr_in **managersp, size_t *n_managersp)
 {
     struct sockaddr_in *managers = NULL;
     size_t n_managers = 0;
+    struct shash targets;
+    size_t i;
 
-    if (ovs_cfg->n_managers > 0) {
-        size_t i;
+    /* Collect all of the potential targets, as the union of the "managers"
+     * column and the "targets" columns of the rows pointed to by
+     * "manager_options", excluding any that are out-of-band. */
+    shash_init(&targets);
+    for (i = 0; i < ovs_cfg->n_managers; i++) {
+        shash_add_once(&targets, ovs_cfg->managers[i], NULL);
+    }
+    for (i = 0; i < ovs_cfg->n_manager_options; i++) {
+        struct ovsrec_manager *m = ovs_cfg->manager_options[i];
 
-        managers = xmalloc(ovs_cfg->n_managers * sizeof *managers);
-        for (i = 0; i < ovs_cfg->n_managers; i++) {
-            const char *name = ovs_cfg->managers[i];
-            struct sockaddr_in *sin = &managers[i];
+        if (m->connection_mode && !strcmp(m->connection_mode, "out-of-band")) {
+            shash_find_and_delete(&targets, m->target);
+        } else {
+            shash_add_once(&targets, m->target, NULL);
+        }
+    }
+
+    /* Now extract the targets' IP addresses. */
+    if (!shash_is_empty(&targets)) {
+        struct shash_node *node;
 
-            if ((!strncmp(name, "tcp:", 4)
-                 && inet_parse_active(name + 4, JSONRPC_TCP_PORT, sin)) ||
-                (!strncmp(name, "ssl:", 4)
-                 && inet_parse_active(name + 4, JSONRPC_SSL_PORT, sin))) {
+        managers = xmalloc(shash_count(&targets) * sizeof *managers);
+        SHASH_FOR_EACH (node, &targets) {
+            const char *target = node->name;
+            struct sockaddr_in *sin = &managers[n_managers];
+
+            if ((!strncmp(target, "tcp:", 4)
+                 && inet_parse_active(target + 4, JSONRPC_TCP_PORT, sin)) ||
+                (!strncmp(target, "ssl:", 4)
+                 && inet_parse_active(target + 4, JSONRPC_SSL_PORT, sin))) {
                 n_managers++;
             }
         }
     }
+    shash_destroy(&targets);
 
     *managersp = managers;
     *n_managersp = n_managers;
@@ -572,7 +598,7 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg)
 
     COVERAGE_INC(bridge_reconfigure);
 
-    collect_managers(ovs_cfg, &managers, &n_managers);
+    collect_in_band_managers(ovs_cfg, &managers, &n_managers);
 
     /* Collect old and new bridges. */
     shash_init(&old_br);
@@ -1698,6 +1724,10 @@ bridge_reconfigure_remotes(struct bridge *br,
                            const struct sockaddr_in *managers,
                            size_t n_managers)
 {
+    const char *disable_ib_str, *queue_id_str;
+    bool disable_in_band = false;
+    int queue_id;
+
     struct ovsrec_controller **controllers;
     size_t n_controllers;
     bool had_primary;
@@ -1706,7 +1736,22 @@ bridge_reconfigure_remotes(struct bridge *br,
     size_t n_ocs;
     size_t i;
 
-    ofproto_set_extra_in_band_remotes(br->ofproto, managers, n_managers);
+    /* 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;
+    }
+
+    /* Set OpenFlow queue ID for in-band control. */
+    queue_id_str = bridge_get_other_config(br->cfg, "in-band-queue");
+    queue_id = queue_id_str ? strtol(queue_id_str, NULL, 10) : -1;
+    ofproto_set_in_band_queue(br->ofproto, queue_id);
+
+    if (disable_in_band) {
+        ofproto_set_extra_in_band_remotes(br->ofproto, NULL, 0);
+    } else {
+        ofproto_set_extra_in_band_remotes(br->ofproto, managers, n_managers);
+    }
     had_primary = ofproto_has_primary_controller(br->ofproto);
 
     n_controllers = bridge_get_controllers(br, &controllers);
@@ -1731,7 +1776,11 @@ bridge_reconfigure_remotes(struct bridge *br,
         }
 
         bridge_configure_local_iface_netdev(br, c);
-        bridge_ofproto_controller_from_ovsrec(c, &ocs[n_ocs++]);
+        bridge_ofproto_controller_from_ovsrec(c, &ocs[n_ocs]);
+        if (disable_in_band) {
+            ocs[n_ocs].band = OFPROTO_OUT_OF_BAND;
+        }
+        n_ocs++;
     }
 
     ofproto_set_controllers(br->ofproto, ocs, n_ocs);
@@ -1750,14 +1799,14 @@ bridge_reconfigure_remotes(struct bridge *br,
     if (!n_controllers
         && ofproto_get_fail_mode(br->ofproto) == OFPROTO_FAIL_STANDALONE) {
         union ofp_action action;
-        flow_t flow;
+        struct cls_rule rule;
 
         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);
+        cls_rule_init_catchall(&rule, 0);
+        ofproto_add_flow(br->ofproto, &rule, &action, 1);
     }
 }
 
@@ -1821,12 +1870,10 @@ bridge_fetch_dp_ifaces(struct bridge *br)
                             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);
@@ -2054,6 +2101,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) {
@@ -2085,6 +2148,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) {
@@ -2098,13 +2162,14 @@ 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)
 {
     p->vlan = (out_port->vlan >= 0 ? OFP_VLAN_NONE
               : in_port->vlan >= 0 ? in_port->vlan
-              : ntohs(flow->dl_vlan));
+              : flow->vlan_tci == 0 ? OFP_VLAN_NONE
+              : vlan_tci_to_vid(flow->vlan_tci));
     return choose_output_iface(out_port, flow->dl_src, &p->dp_ifidx, tags);
 }
 
@@ -2186,21 +2251,43 @@ 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)
 {
     mirror_mask_t mirrors = in_port->src_mirrors;
+    int flow_vlan;
     struct dst *dst = dsts;
     size_t i;
 
+    flow_vlan = vlan_tci_to_vid(flow->vlan_tci);
+    if (flow_vlan == 0) {
+        flow_vlan = OFP_VLAN_NONE;
+    }
+
     if (out_port == FLOOD_PORT) {
         /* XXX use ODP_FLOOD if no vlans or bonding. */
         /* 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;
@@ -2228,7 +2315,6 @@ compose_dsts(const struct bridge *br, const flow_t *flow, uint16_t vlan,
                     if (port_includes_vlan(port, m->out_vlan)
                         && set_dst(dst, flow, in_port, port, tags))
                     {
-                        int flow_vlan;
 
                         if (port->vlan < 0) {
                             dst->vlan = m->out_vlan;
@@ -2243,10 +2329,6 @@ compose_dsts(const struct bridge *br, const flow_t *flow, uint16_t vlan,
                          * tagging tags place. This is necessary because
                          * dst->vlan is the final vlan, after removing implicit
                          * tags. */
-                        flow_vlan = ntohs(flow->dl_vlan);
-                        if (flow_vlan == 0) {
-                            flow_vlan = OFP_VLAN_NONE;
-                        }
                         if (port == in_port && dst->vlan == flow_vlan) {
                             /* Don't send out input port on same VLAN. */
                             continue;
@@ -2259,7 +2341,7 @@ compose_dsts(const struct bridge *br, const flow_t *flow, uint16_t vlan,
         mirrors &= mirrors - 1;
     }
 
-    partition_dsts(dsts, dst - dsts, ntohs(flow->dl_vlan));
+    partition_dsts(dsts, dst - dsts, flow_vlan);
     return dst - dsts;
 }
 
@@ -2275,7 +2357,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)
@@ -2288,15 +2370,19 @@ compose_actions(struct bridge *br, const flow_t *flow, uint16_t vlan,
     n_dsts = compose_dsts(br, flow, vlan, in_port, out_port, dsts, tags,
                           nf_output_iface);
 
-    cur_vlan = ntohs(flow->dl_vlan);
+    cur_vlan = vlan_tci_to_vid(flow->vlan_tci);
+    if (cur_vlan == 0) {
+        cur_vlan = OFP_VLAN_NONE;
+    }
     for (p = dsts; p < &dsts[n_dsts]; p++) {
         union odp_action *a;
         if (p->vlan != cur_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 |= flow->vlan_tci & htons(VLAN_PCP_MASK);
             }
             cur_vlan = p->vlan;
         }
@@ -2309,28 +2395,19 @@ 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
-     * belongs to VLAN 0, so we should treat both cases identically.  (In the
-     * former case, the packet has an 802.1Q header that specifies VLAN 0,
-     * presumably to allow a priority to be specified.  In the latter case, the
-     * packet does not have any 802.1Q header.) */
-    int vlan = ntohs(flow->dl_vlan);
-    if (vlan == OFP_VLAN_NONE) {
-        vlan = 0;
-    }
+    int vlan = vlan_tci_to_vid(flow->vlan_tci);
     if (in_port->vlan >= 0) {
         if (vlan) {
             /* XXX support double tagging? */
             if (have_packet) {
                 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
-                VLOG_WARN_RL(&rl, "bridge %s: dropping VLAN %"PRIu16" tagged "
+                VLOG_WARN_RL(&rl, "bridge %s: dropping VLAN %d tagged "
                              "packet received on port %s configured with "
                              "implicit VLAN %"PRIu16,
-                             br->name, ntohs(flow->dl_vlan),
-                             in_port->name, in_port->vlan);
+                             br->name, vlan, in_port->name, in_port->vlan);
             }
             return -1;
         }
@@ -2355,7 +2432,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)
@@ -2365,7 +2442,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;
@@ -2413,7 +2490,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;
@@ -2502,7 +2579,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)
 {
@@ -2552,36 +2629,8 @@ 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;
-
-    if (reason == OFPPR_DELETE || !br->has_bonded_ports) {
-        return;
-    }
-
-    iface = iface_from_dp_ifidx(br, ofp_port_to_odp_port(opp->port_no));
-    if (!iface) {
-        return;
-    }
-    port = iface->port;
-
-    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_)
 {
@@ -2593,7 +2642,7 @@ bridge_normal_ofhook_cb(const flow_t *flow, const struct ofpbuf *packet,
 }
 
 static void
-bridge_account_flow_ofhook_cb(const flow_t *flow, tag_type tags,
+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_)
@@ -2653,7 +2702,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,
@@ -2975,7 +3023,7 @@ bond_send_learning_packets(struct port *port)
         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
@@ -3430,6 +3478,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;
         }
 
@@ -3542,6 +3591,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);
@@ -3573,6 +3625,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) {
@@ -3582,9 +3638,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];
@@ -3602,6 +3658,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);
+        }
     }
 }
 
@@ -3829,6 +3890,15 @@ 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,