netdev: Fix carrier status for down interfaces.
[sliver-openvswitch.git] / vswitchd / bridge.c
index 5210da1..41fcba5 100644 (file)
@@ -89,6 +89,7 @@ struct iface {
     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;
 };
 
@@ -146,6 +147,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. */
@@ -252,8 +254,8 @@ 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,
@@ -389,6 +391,15 @@ iface_get_options(const struct ovsrec_interface *if_cfg, struct shash *options)
     }
 }
 
+/* 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;
+}
+
 /* Attempt to create the network device for 'iface' through the netdev
  * library. */
 static int
@@ -400,12 +411,7 @@ create_iface_netdev(struct iface *iface)
 
     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";
-    } else {
-        netdev_options.type = iface->cfg->type;
-    }
+    netdev_options.type = iface_get_netdev_type(iface);
     netdev_options.args = &options;
     netdev_options.ethertype = NETDEV_ETH_TYPE_NONE;
 
@@ -414,7 +420,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);
@@ -431,9 +437,7 @@ reconfigure_iface_netdev(struct iface *iface)
 
     /* Skip reconfiguration if the device has the wrong type. This shouldn't
      * happen, but... */
-    iface_type = (!iface->cfg->type[0] ? NULL
-                  : !strcmp(iface->cfg->type, "internal") ? "system"
-                  : iface->cfg->type);
+    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",
@@ -449,38 +453,27 @@ reconfigure_iface_netdev(struct iface *iface)
     return error;
 }
 
+/* Callback for iterate_and_prune_ifaces(). */
 static bool
-check_iface_netdev(struct bridge *br OVS_UNUSED, struct iface *iface,
-                   void *aux OVS_UNUSED)
+check_iface(struct bridge *br, struct iface *iface, void *aux OVS_UNUSED)
 {
     if (!iface->netdev) {
-        int error = create_iface_netdev(iface);
-        if (error) {
-            VLOG_WARN("could not open netdev on %s, dropping: %s", iface->name,
-                                                               strerror(error));
-            return false;
-        }
+        /* We already reported a related error, don't bother duplicating it. */
+        return false;
     }
 
-    return true;
-}
-
-static bool
-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",
-                 dpif_name(br->dpif),
-                 iface->name, iface->dp_ifidx);
-        return true;
-    } else {
+    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)
@@ -492,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);
     }
 
@@ -518,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);
             }
         }
@@ -666,30 +659,58 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg)
         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;
+            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) {
+                    netdev_close(iface->netdev);
+                    iface->netdev = NULL;
+                }
+            }
 
-            if (shash_find(&cur_ifaces, if_name)) {
-                /* Already exists on the datapath.  If we have it open,
-                 * reconfigure it; otherwise we'll open it later. */
-                if (iface && iface->netdev) {
+            /* 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);
                 }
-            } else {
-                /* Need to add to datapath. */
-                bool internal;
-                int error;
+            }
 
-                /* Add to datapath. */
-                internal = iface_is_internal(br, if_name);
+            /* 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) {
@@ -699,9 +720,25 @@ 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);
     }
@@ -715,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);
@@ -1668,12 +1704,25 @@ 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;
 
     struct ofproto_controller *ocs;
     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;
+    }
+
+    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);
@@ -1698,7 +1747,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);
@@ -1717,7 +1770,7 @@ 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 flow flow;
 
         memset(&action, 0, sizeof action);
         action.type = htons(OFPAT_OUTPUT);
@@ -1788,12 +1841,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);
@@ -2021,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) {
@@ -2052,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) {
@@ -2065,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)
 {
@@ -2153,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)
 {
@@ -2167,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;
@@ -2242,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)
@@ -2262,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;
         }
@@ -2276,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
@@ -2322,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)
@@ -2332,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;
@@ -2380,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;
@@ -2469,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)
 {
@@ -2519,45 +2604,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;
-
-    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_)
 {
@@ -2569,7 +2617,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_)
@@ -2629,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,
@@ -2951,7 +2998,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
@@ -3406,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;
         }
 
@@ -3419,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);
 
@@ -3511,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);
@@ -3542,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) {
@@ -3551,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];
@@ -3571,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);
+        }
     }
 }
 
@@ -3692,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;
@@ -3706,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 = create_iface_netdev(iface);
-        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);
@@ -3789,38 +3841,6 @@ iface_from_dp_ifidx(const struct bridge *br, uint16_t dp_ifidx)
     return NULL;
 }
 
-/* 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;
-    }
-    return false;
-}
-
 /* Set Ethernet address of 'iface', if one is specified in the configuration
  * file. */
 static void
@@ -3845,6 +3865,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,