datapath: Make adding and attaching a vport a single step.
[sliver-openvswitch.git] / vswitchd / bridge.c
index 75cce3c..9916414 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,8 @@
 #include <sys/types.h>
 #include <unistd.h>
 #include "bitmap.h"
+#include "cfm.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);
+
+COVERAGE_DEFINE(bridge_flush);
+COVERAGE_DEFINE(bridge_process_flow);
+COVERAGE_DEFINE(bridge_reconfigure);
 
 struct dst {
     uint16_t vlan;
@@ -90,6 +96,7 @@ struct iface {
     struct netdev *netdev;      /* Network device. */
     bool enabled;               /* May be chosen for flows? */
     const char *type;           /* Usually same as cfg->type. */
+    struct cfm *cfm;            /* Connectivity Fault Management */
     const struct ovsrec_interface *cfg;
 };
 
@@ -255,7 +262,11 @@ 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 iface_update_cfm(struct iface *);
+static void iface_refresh_cfm_stats(struct iface *iface);
+static void iface_send_packet(struct iface *, struct ofpbuf *packet);
 
 static void shash_from_ovs_idl_map(char **keys, char **values, size_t n,
                                    struct shash *);
@@ -272,10 +283,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);
@@ -283,8 +294,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. */
@@ -365,10 +376,7 @@ bridge_configure_once(const struct ovsrec_open_vswitch *cfg)
 
 /* 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. */
+ * "other_config" keys. */
 static void
 iface_get_options(const struct ovsrec_interface *if_cfg, struct shash *options)
 {
@@ -390,68 +398,6 @@ 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
-create_iface_netdev(struct iface *iface)
-{
-    struct netdev_options netdev_options;
-    struct shash options;
-    int error;
-
-    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) {
-        netdev_get_carrier(iface->netdev, &iface->enabled);
-    }
-
-    shash_destroy(&options);
-
-    return error;
-}
-
-static int
-reconfigure_iface_netdev(struct iface *iface)
-{
-    const char *netdev_type, *iface_type;
-    struct shash options;
-    int error;
-
-    /* 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;
-    }
-
-    /* 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(struct bridge *br, struct iface *iface, void *aux OVS_UNUSED)
@@ -509,6 +455,7 @@ iterate_and_prune_ifaces(struct bridge *br,
             if (cb(br, iface, aux)) {
                 j++;
             } else {
+                iface_set_ofport(iface->cfg, -1);
                 iface_destroy(iface);
             }
         }
@@ -531,30 +478,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];
+
+        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);
+        }
+    }
 
-        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];
+    /* 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;
@@ -573,7 +542,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);
@@ -667,18 +636,16 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg)
         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);
+            const char *type = iface ? iface->type : "internal";
             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 && strcmp(dpif_port->type, type))
+                || (iface && iface->netdev
+                    && strcmp(type, netdev_get_type(iface->netdev)))) {
                 if (dpif_port) {
                     error = ofproto_port_del(br->ofproto, dpif_port->port);
                     if (error) {
@@ -692,48 +659,62 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg)
                 }
             }
 
-            /* 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 the port doesn't exist or we don't have the netdev open,
+             * we need to do more work. */
+            if (!dpif_port || (iface && !iface->netdev)) {
+                struct netdev_options options;
+                struct netdev *netdev;
+                struct shash args;
+
+                /* First open the network device. */
+                options.name = if_name;
+                options.type = type;
+                options.args = &args;
+                options.ethertype = NETDEV_ETH_TYPE_NONE;
+
+                shash_init(&args);
+                if (iface) {
+                    iface_get_options(iface->cfg, &args);
                 }
-            }
+                error = netdev_open(&options, &netdev);
+                shash_destroy(&args);
 
-            /* 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) {
-                    VLOG_ERR("ran out of valid port numbers on %s",
-                             dpif_name(br->dpif));
-                    break;
-                } else if (error) {
-                    VLOG_ERR("failed to add %s interface to %s: %s",
-                             if_name, dpif_name(br->dpif), strerror(error));
+                if (error) {
+                    VLOG_WARN("could not open network device %s (%s)",
+                              if_name, strerror(error));
                     continue;
                 }
-            }
 
-            /* If it's an internal port, open the netdev. */
-            if (internal) {
-                if (iface && !iface->netdev) {
-                    error = create_iface_netdev(iface);
+                /* Then add the port if we haven't already. */
+                if (!dpif_port) {
+                    error = dpif_port_add(br->dpif, netdev, NULL);
                     if (error) {
-                        VLOG_WARN("could not create iface %s: %s", iface->name,
-                                  strerror(error));
-                        continue;
+                        netdev_close(netdev);
+                        if (error == EFBIG) {
+                            VLOG_ERR("ran out of valid port numbers on %s",
+                                     dpif_name(br->dpif));
+                            break;
+                        } else {
+                            VLOG_ERR("failed to add %s interface to %s: %s",
+                                     if_name, dpif_name(br->dpif),
+                                     strerror(error));
+                            continue;
+                        }
                     }
                 }
-            } else {
-                assert(iface->netdev != NULL);
+
+                /* Update 'iface'. */
+                if (iface) {
+                    iface->netdev = netdev;
+                    iface->enabled = netdev_get_carrier(iface->netdev);
+                }
+            } else if (iface && iface->netdev) {
+                struct shash args;
+
+                shash_init(&args);
+                iface_get_options(iface->cfg, &args);
+                netdev_reconfigure(iface->netdev, &args);
+                shash_destroy(&args);
             }
         }
         free(dpif_ports);
@@ -894,6 +875,13 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg)
         iterate_and_prune_ifaces(br, set_iface_properties, NULL);
     }
 
+    LIST_FOR_EACH (br, node, &all_bridges) {
+        struct iface *iface;
+        HMAP_FOR_EACH (iface, dp_ifidx_node, &br->ifaces) {
+            iface_update_cfm(iface);
+        }
+    }
+
     free(managers);
 }
 
@@ -1112,6 +1100,82 @@ dpid_from_hash(const void *data, size_t n)
     return eth_addr_to_uint64(hash);
 }
 
+static void
+iface_refresh_cfm_stats(struct iface *iface)
+{
+    size_t i;
+    struct cfm *cfm;
+    const struct ovsrec_monitor *mon;
+
+    mon = iface->cfg->monitor;
+    cfm = iface->cfm;
+
+    if (!cfm || !mon) {
+        return;
+    }
+
+    for (i = 0; i < mon->n_remote_mps; i++) {
+        const struct ovsrec_maintenance_point *mp;
+        const struct remote_mp *rmp;
+
+        mp = mon->remote_mps[i];
+        rmp = cfm_get_remote_mp(cfm, mp->mpid);
+
+        ovsrec_maintenance_point_set_fault(mp, &rmp->fault, 1);
+    }
+
+    if (hmap_is_empty(&cfm->x_remote_mps)) {
+        ovsrec_monitor_set_unexpected_remote_mpids(mon, NULL, 0);
+    } else {
+        size_t length;
+        struct remote_mp *rmp;
+        int64_t *x_remote_mps;
+
+        length = hmap_count(&cfm->x_remote_mps);
+        x_remote_mps = xzalloc(length * sizeof *x_remote_mps);
+
+        i = 0;
+        HMAP_FOR_EACH (rmp, node, &cfm->x_remote_mps) {
+            x_remote_mps[i++] = rmp->mpid;
+        }
+
+        ovsrec_monitor_set_unexpected_remote_mpids(mon, x_remote_mps, length);
+        free(x_remote_mps);
+    }
+
+    if (hmap_is_empty(&cfm->x_remote_maids)) {
+        ovsrec_monitor_set_unexpected_remote_maids(mon, NULL, 0);
+    } else {
+        size_t length;
+        char **x_remote_maids;
+        struct remote_maid *rmaid;
+
+        length = hmap_count(&cfm->x_remote_maids);
+        x_remote_maids = xzalloc(length * sizeof *x_remote_maids);
+
+        i = 0;
+        HMAP_FOR_EACH (rmaid, node, &cfm->x_remote_maids) {
+            size_t j;
+
+            x_remote_maids[i] = xzalloc(CCM_MAID_LEN * 2 + 1);
+
+            for (j = 0; j < CCM_MAID_LEN; j++) {
+                 snprintf(&x_remote_maids[i][j * 2], 3, "%02hhx",
+                          rmaid->maid[j]);
+            }
+            i++;
+        }
+        ovsrec_monitor_set_unexpected_remote_maids(mon, x_remote_maids, length);
+
+        for (i = 0; i < length; i++) {
+            free(x_remote_maids[i]);
+        }
+        free(x_remote_maids);
+    }
+
+    ovsrec_monitor_set_fault(mon, &cfm->fault, 1);
+}
+
 static void
 iface_refresh_stats(struct iface *iface)
 {
@@ -1244,6 +1308,7 @@ bridge_run(void)
                     for (j = 0; j < port->n_ifaces; j++) {
                         struct iface *iface = port->ifaces[j];
                         iface_refresh_stats(iface);
+                        iface_refresh_cfm_stats(iface);
                     }
                 }
             }
@@ -1260,6 +1325,7 @@ void
 bridge_wait(void)
 {
     struct bridge *br;
+    struct iface *iface;
 
     LIST_FOR_EACH (br, node, &all_bridges) {
         ofproto_wait(br->ofproto);
@@ -1269,6 +1335,12 @@ bridge_wait(void)
 
         mac_learning_wait(br->ml);
         bond_wait(br);
+
+        HMAP_FOR_EACH (iface, dp_ifidx_node, &br->ifaces) {
+            if (iface->cfm) {
+                cfm_wait(iface->cfm);
+            }
+        }
     }
     ovsdb_idl_wait(idl);
     poll_timer_wait_until(stats_timer);
@@ -1469,6 +1541,7 @@ static int
 bridge_run_one(struct bridge *br)
 {
     int error;
+    struct iface *iface;
 
     error = ofproto_run1(br->ofproto);
     if (error) {
@@ -1481,6 +1554,21 @@ bridge_run_one(struct bridge *br)
     error = ofproto_run2(br->ofproto, br->flush);
     br->flush = false;
 
+    HMAP_FOR_EACH (iface, dp_ifidx_node, &br->ifaces) {
+        struct ofpbuf *packet;
+
+        if (!iface->cfm) {
+            continue;
+        }
+
+        packet = cfm_run(iface->cfm);
+        if (packet) {
+            iface_send_packet(iface, packet);
+            ofpbuf_uninit(packet);
+            free(packet);
+        }
+    }
+
     return error;
 }
 
@@ -1598,7 +1686,7 @@ bridge_reconfigure_one(struct bridge *br)
     /* Configure OpenFlow controller connection snooping. */
     svec_init(&snoops);
     svec_add_nocopy(&snoops, xasprintf("punix:%s/%s.snoop",
-                                       ovs_rundir, br->name));
+                                       ovs_rundir(), br->name));
     svec_init(&old_snoops);
     ofproto_get_snoops(br->ofproto, &old_snoops);
     if (!svec_equal(&snoops, &old_snoops)) {
@@ -1618,7 +1706,7 @@ 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->target = xasprintf("punix:%s/%s.mgmt", ovs_rundir(), br->name);
     oc->max_backoff = 0;
     oc->probe_interval = 60;
     oc->band = OFPROTO_OUT_OF_BAND;
@@ -1699,6 +1787,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;
@@ -1707,7 +1799,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);
@@ -1732,7 +1839,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);
@@ -1751,14 +1862,14 @@ bridge_reconfigure_remotes(struct bridge *br,
     if (!n_controllers
         && ofproto_get_fail_mode(br->ofproto) == OFPROTO_FAIL_STANDALONE) {
         union ofp_action action;
-        struct flow 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);
     }
 }
 
@@ -1822,12 +1933,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);
@@ -2060,10 +2169,11 @@ bond_run(struct bridge *br)
             /* Track carrier going up and down on interfaces. */
             while (!netdev_monitor_poll(port->monitor, &devname)) {
                 struct iface *iface;
-                bool carrier;
 
                 iface = port_lookup_iface(port, devname);
-                if (iface && !netdev_get_carrier(iface->netdev, &carrier)) {
+                if (iface) {
+                    bool carrier = netdev_get_carrier(iface->netdev);
+
                     bond_link_status_update(iface, carrier);
                     port_update_bond_compat(port);
                 }
@@ -2121,7 +2231,8 @@ set_dst(struct dst *p, const struct flow *flow,
 {
     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);
 }
 
@@ -2223,9 +2334,15 @@ compose_dsts(const struct bridge *br, const struct flow *flow, uint16_t vlan,
              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 */
@@ -2261,7 +2378,6 @@ compose_dsts(const struct bridge *br, const struct flow *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;
@@ -2276,10 +2392,6 @@ compose_dsts(const struct bridge *br, const struct flow *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;
@@ -2292,7 +2404,7 @@ compose_dsts(const struct bridge *br, const struct flow *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;
 }
 
@@ -2321,7 +2433,10 @@ compose_actions(struct bridge *br, const struct flow *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) {
@@ -2330,7 +2445,7 @@ compose_actions(struct bridge *br, const struct flow *flow, uint16_t vlan,
             } else {
                 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);
+                a->dl_tci.tci |= flow->vlan_tci & htons(VLAN_PCP_MASK);
             }
             cur_vlan = p->vlan;
         }
@@ -2346,25 +2461,16 @@ compose_actions(struct bridge *br, const struct flow *flow, uint16_t vlan,
 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;
         }
@@ -2591,10 +2697,20 @@ 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 iface *iface;
     struct bridge *br = br_;
 
     COVERAGE_INC(bridge_process_flow);
 
+    iface = iface_from_dp_ifidx(br, flow->in_port);
+
+    if (cfm_should_process_flow(flow)) {
+        if (packet && iface->cfm) {
+            cfm_process_heartbeat(iface->cfm, packet);
+        }
+        return false;
+    }
+
     return process_flow(br, flow, packet, actions, tags, nf_output_iface);
 }
 
@@ -3435,6 +3551,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;
         }
 
@@ -3547,6 +3664,8 @@ 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);
@@ -3732,6 +3851,26 @@ port_update_vlan_compat(struct port *port)
 \f
 /* Interface functions. */
 
+static void
+iface_send_packet(struct iface *iface, struct ofpbuf *packet)
+{
+    struct flow flow;
+    union ofp_action action;
+
+    memset(&action, 0, sizeof action);
+    action.output.type = htons(OFPAT_OUTPUT);
+    action.output.len  = htons(sizeof action);
+    action.output.port = htons(odp_port_to_ofp_port(iface->dp_ifidx));
+
+    flow_extract(packet, 0, ODPP_NONE, &flow);
+
+    if (ofproto_send_packet(iface->port->bridge->ofproto, &flow, &action, 1,
+                            packet)) {
+        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+        VLOG_WARN_RL(&rl, "interface %s: Failed to send packet.", iface->name);
+    }
+}
+
 static struct iface *
 iface_create(struct port *port, const struct ovsrec_interface *if_cfg)
 {
@@ -3793,6 +3932,8 @@ iface_destroy(struct iface *iface)
             bond_send_learning_packets(port);
         }
 
+        cfm_destroy(iface->cfm);
+
         free(iface->name);
         free(iface);
 
@@ -3844,6 +3985,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,
@@ -3920,6 +4070,56 @@ iface_update_qos(struct iface *iface, const struct ovsrec_qos *qos)
         }
     }
 }
+
+static void
+iface_update_cfm(struct iface *iface)
+{
+    size_t i;
+    struct cfm *cfm;
+    uint16_t *remote_mps;
+    struct ovsrec_monitor *mon;
+    uint8_t ea[ETH_ADDR_LEN], maid[CCM_MAID_LEN];
+
+    mon = iface->cfg->monitor;
+
+    if (!mon) {
+        return;
+    }
+
+    if (netdev_get_etheraddr(iface->netdev, ea)) {
+        VLOG_WARN("interface %s: Failed to get ethernet address. "
+                  "Skipping Monitor.", iface->name);
+        return;
+    }
+
+    if (!cfm_generate_maid(mon->md_name, mon->ma_name, maid)) {
+        VLOG_WARN("interface %s: Failed to generate MAID.", iface->name);
+        return;
+    }
+
+    if (!iface->cfm) {
+        iface->cfm = cfm_create();
+    }
+
+    cfm           = iface->cfm;
+    cfm->mpid     = mon->mpid;
+    cfm->interval = mon->interval ? *mon->interval : 1000;
+
+    memcpy(cfm->eth_src, ea, sizeof cfm->eth_src);
+    memcpy(cfm->maid, maid, sizeof cfm->maid);
+
+    remote_mps = xzalloc(mon->n_remote_mps * sizeof *remote_mps);
+    for(i = 0; i < mon->n_remote_mps; i++) {
+        remote_mps[i] = mon->remote_mps[i]->mpid;
+    }
+    cfm_update_remote_mps(cfm, remote_mps, mon->n_remote_mps);
+    free(remote_mps);
+
+    if (!cfm_configure(iface->cfm)) {
+        cfm_destroy(iface->cfm);
+        iface->cfm = NULL;
+    }
+}
 \f
 /* Port mirroring. */