Merge 'master' into 'next'.
authorBen Pfaff <blp@nicira.com>
Thu, 12 May 2011 19:05:42 +0000 (12:05 -0700)
committerBen Pfaff <blp@nicira.com>
Thu, 12 May 2011 19:05:42 +0000 (12:05 -0700)
28 files changed:
1  2 
include/openflow/nicira-ext.h
lib/automake.mk
lib/bond.c
lib/bond.h
lib/dpif-linux.c
lib/dpif.c
lib/dpif.h
lib/flow.h
lib/learning-switch.c
lib/netdev-linux.c
lib/ofp-parse.c
lib/ofp-print.c
lib/ofp-util.c
lib/ofp-util.h
ofproto/connmgr.c
ofproto/in-band.c
ofproto/ofproto-dpif.c
ofproto/ofproto.c
ofproto/ofproto.h
ofproto/private.h
tests/ofproto.at
tests/ovs-ofctl.at
utilities/ovs-controller.c
utilities/ovs-ofctl.8.in
utilities/ovs-ofctl.c
utilities/ovs-openflowd.c
vswitchd/bridge.c
vswitchd/ovs-vswitchd.c

@@@ -108,7 -108,9 +108,7 @@@ enum nx_flow_mod_failed_code 
      NXFMFC_HARDWARE = 0x100,
  
      /* A nonexistent table ID was specified in the "command" field of struct
 -     * ofp_flow_mod, when the nxt_flow_mod_table_id extension is enabled.
 -     * (This extension is not yet implemented on this branch of Open
 -     * vSwitch.) */
 +     * ofp_flow_mod, when the nxt_flow_mod_table_id extension is enabled. */
      NXFMFC_BAD_TABLE_ID = 0x101
  };
  \f
@@@ -134,27 -136,15 +134,24 @@@ enum nicira_type 
      NXT_FLOW_END_CONFIG__OBSOLETE,
      NXT_FLOW_END__OBSOLETE,
      NXT_MGMT__OBSOLETE,
-     /* Use the high 32 bits of the cookie field as the tunnel ID in the flow
-      * match. */
-     NXT_TUN_ID_FROM_COOKIE,
+     NXT_TUN_ID_FROM_COOKIE__OBSOLETE,
  
      /* Controller role support.  The request body is struct nx_role_request.
       * The reply echos the request. */
      NXT_ROLE_REQUEST,
      NXT_ROLE_REPLY,
  
 +    /* Use the upper 8 bits of the 'command' member in struct ofp_flow_mod to
 +     * designate the table to which a flow is to be added?  See the big comment
 +     * on struct nxt_flow_mod_table_id for more information.
 +     *
 +     * A screwup caused this extension to be assigned the same value as
 +     * NXT_SET_FLOW_FORMAT (see below).  The two extensions do have different
 +     * lengths, so they can still be distinguished. */
 +    NXT_FLOW_MOD_TABLE_ID,
 +
      /* Flexible flow specification (aka NXM = Nicira Extended Match). */
 -    NXT_SET_FLOW_FORMAT,        /* Set flow format. */
 +    NXT_SET_FLOW_FORMAT = NXT_FLOW_MOD_TABLE_ID, /* Set flow format. */
      NXT_FLOW_MOD,               /* Analogous to OFPT_FLOW_MOD. */
      NXT_FLOW_REMOVED            /* Analogous to OFPT_FLOW_REMOVED. */
  };
@@@ -177,63 -167,6 +174,53 @@@ enum nicira_stats_type 
      NXST_AGGREGATE              /* Analogous to OFPST_AGGREGATE. */
  };
  
- /* NXT_TUN_ID_FROM_COOKIE request. */
- struct nxt_tun_id_cookie {
-     struct ofp_header header;
-     ovs_be32 vendor;            /* NX_VENDOR_ID. */
-     ovs_be32 subtype;           /* NXT_TUN_ID_FROM_COOKIE */
-     uint8_t set;                /* Nonzero to enable, zero to disable. */
-     uint8_t pad[7];
- };
- OFP_ASSERT(sizeof(struct nxt_tun_id_cookie) == 24);
 +/* This command enables or disables an Open vSwitch extension that allows a
 + * controller to specify the OpenFlow table to which a flow should be added,
 + * instead of having the switch decide which table is most appropriate as
 + * required by OpenFlow 1.0.  By default, the extension is disabled.
 + *
 + * When this feature is enabled, Open vSwitch treats struct ofp_flow_mod's
 + * 16-bit 'command' member as two separate fields.  The upper 8 bits are used
 + * as the table ID, the lower 8 bits specify the command as usual.  A table ID
 + * of 0xff is treated like a wildcarded table ID.
 + *
 + * The specific treatment of the table ID depends on the type of flow mod:
 + *
 + *    - OFPFC_ADD: Given a specific table ID, the flow is always placed in that
 + *      table.  If an identical flow already exists in that table only, then it
 + *      is replaced.  If the flow cannot be placed in the specified table,
 + *      either because the table is full or because the table cannot support
 + *      flows of the given type, the switch replies with an
 + *      OFPFMFC_ALL_TABLES_FULL error.  (A controller can distinguish these
 + *      cases by comparing the current and maximum number of entries reported
 + *      in ofp_table_stats.)
 + *
 + *      If the table ID is wildcarded, the switch picks an appropriate table
 + *      itself.  If an identical flow already exist in the selected flow table,
 + *      then it is replaced.  The choice of table might depend on the flows
 + *      that are already in the switch; for example, if one table fills up then
 + *      the switch might fall back to another one.
 + *
 + *    - OFPFC_MODIFY, OFPFC_DELETE: Given a specific table ID, only flows
 + *      within that table are matched and modified or deleted.  If the table ID
 + *      is wildcarded, flows within any table may be matched and modified or
 + *      deleted.
 + *
 + *    - OFPFC_MODIFY_STRICT, OFPFC_DELETE_STRICT: Given a specific table ID,
 + *      only a flow within that table may be matched and modified or deleted.
 + *      If the table ID is wildcarded and exactly one flow within any table
 + *      matches, then it is modified or deleted; if flows in more than one
 + *      table match, then none is modified or deleted.
 + */
 +struct nxt_flow_mod_table_id {
 +    struct ofp_header header;
 +    uint32_t vendor;            /* NX_VENDOR_ID. */
 +    uint32_t subtype;           /* NXT_FLOW_MOD_TABLE_ID. */
 +    uint8_t set;                /* Nonzero to enable, zero to disable. */
 +    uint8_t pad[7];
 +};
 +OFP_ASSERT(sizeof(struct nxt_flow_mod_table_id) == 24);
 +
  /* Configures the "role" of the sending controller.  The default role is:
   *
   *    - Other (NX_ROLE_OTHER), which allows the controller access to all
@@@ -658,12 -591,6 +645,6 @@@ enum nx_mp_algorithm 
       */
      NX_MP_ALG_ITER_HASH         /* Iterative Hash. */
  };
- /* Wildcard for tunnel ID. */
- #define NXFW_TUN_ID  (1 << 25)
- #define NXFW_ALL NXFW_TUN_ID
- #define OVSFW_ALL (OFPFW_ALL | NXFW_ALL)
  \f
  /* Action structure for NXAST_AUTOPATH.
   *
@@@ -1219,8 -1146,6 +1200,6 @@@ OFP_ASSERT(sizeof(struct nx_action_auto
  
  enum nx_flow_format {
      NXFF_OPENFLOW10 = 0,         /* Standard OpenFlow 1.0 compatible. */
-     NXFF_TUN_ID_FROM_COOKIE = 1, /* OpenFlow 1.0, plus obtain tunnel ID from
-                                   * cookie. */
      NXFF_NXM = 2                 /* Nicira extended match. */
  };
  
diff --combined lib/automake.mk
@@@ -55,8 -55,6 +55,8 @@@ lib_libopenvswitch_a_SOURCES = 
        lib/hash.h \
        lib/hmap.c \
        lib/hmap.h \
 +      lib/hmapx.c \
 +      lib/hmapx.h \
        lib/json.c \
        lib/json.h \
        lib/jsonrpc.c \
        lib/vconn-stream.c \
        lib/vconn.c \
        lib/vconn.h \
 +      lib/vlan-bitmap.c \
 +      lib/vlan-bitmap.h \
        lib/vlog.c \
        lib/vlog.h
  nodist_lib_libopenvswitch_a_SOURCES = \
@@@ -200,6 -196,7 +200,7 @@@ lib_libopenvswitch_a_SOURCES += 
        lib/dpif-linux.c \
        lib/dpif-linux.h \
        lib/netdev-linux.c \
+       lib/netdev-linux.h \
        lib/netdev-vport.c \
        lib/netdev-vport.h \
        lib/netlink-protocol.h \
@@@ -223,6 -220,8 +224,8 @@@ lib/dhparams.c: lib/dh1024.pem lib/dh20
         openssl dhparam -C -in $(srcdir)/lib/dh4096.pem -noout)        \
        | sed 's/\(get_dh[0-9]*\)()/\1(void)/' > lib/dhparams.c.tmp
        mv lib/dhparams.c.tmp lib/dhparams.c
+ else
+ lib_libopenvswitch_a_SOURCES += lib/stream-nossl.c
  endif
  
  EXTRA_DIST += \
diff --combined lib/bond.c
@@@ -74,7 -74,7 +74,7 @@@ struct bond_slave 
      uint64_t tx_bytes;          /* Sum across 'tx_bytes' of entries. */
  
      /* BM_STABLE specific bonding info. */
-     uint16_t stb_id;            /* ID used for 'stb_slaves' ordering. */
+     uint32_t stb_id;            /* ID used for 'stb_slaves' ordering. */
  };
  
  /* A bond, that is, a set of network devices grouped to improve performance or
@@@ -93,6 -93,7 +93,7 @@@ struct bond 
      int updelay, downdelay;     /* Delay before slave goes up/down, in ms. */
      bool lacp_negotiated;       /* LACP negotiations were successful. */
      bool bond_revalidate;       /* True if flows need revalidation. */
+     uint32_t basis;             /* Basis for flow hash function. */
  
      /* SLB specific bonding info. */
      struct bond_entry *hash;     /* An array of (BOND_MASK + 1) elements. */
@@@ -129,8 -130,9 +130,9 @@@ static void bond_link_status_update(str
  static void bond_choose_active_slave(struct bond *, struct tag_set *);
  static bool bond_is_tcp_hash(const struct bond *);
  static unsigned int bond_hash_src(const uint8_t mac[ETH_ADDR_LEN],
-                                   uint16_t vlan);
- static unsigned int bond_hash_tcp(const struct flow *, uint16_t vlan);
+                                   uint16_t vlan, uint32_t basis);
+ static unsigned int bond_hash_tcp(const struct flow *, uint16_t vlan,
+                                   uint32_t basis);
  static struct bond_entry *lookup_bond_entry(const struct bond *,
                                              const struct flow *,
                                              uint16_t vlan);
@@@ -291,6 -293,11 +293,11 @@@ bond_reconfigure(struct bond *bond, con
          revalidate = true;
      }
  
+     if (bond->basis != s->basis) {
+         bond->basis = s->basis;
+         revalidate = true;
+     }
      if (bond->detect == BLSM_CARRIER) {
          struct bond_slave *slave;
  
      return revalidate;
  }
  
 +static void
 +bond_slave_set_netdev__(struct bond *bond, struct bond_slave *slave,
 +                        struct netdev *netdev)
 +{
 +    if (slave->netdev != netdev) {
 +        if (bond->monitor) {
 +            if (slave->netdev) {
 +                netdev_monitor_remove(bond->monitor, slave->netdev);
 +            }
 +            netdev_monitor_add(bond->monitor, netdev);
 +        }
 +        slave->netdev = netdev;
 +    }
 +}
 +
  /* Registers 'slave_' as a slave of 'bond'.  The 'slave_' pointer is an
   * arbitrary client-provided pointer that uniquely identifies a slave within a
   * bond.  If 'slave_' already exists within 'bond' then this function
   * 'slave_' or destroying 'bond'.
   */
  void
- bond_slave_register(struct bond *bond, void *slave_, uint16_t stb_id,
+ bond_slave_register(struct bond *bond, void *slave_, uint32_t stb_id,
                      struct netdev *netdev)
  {
      struct bond_slave *slave = bond_slave_lookup(bond, slave_);
          bond->bond_revalidate = true;
      }
  
 -    if (slave->netdev != netdev) {
 -        if (bond->monitor) {
 -            if (slave->netdev) {
 -                netdev_monitor_remove(bond->monitor, slave->netdev);
 -            }
 -            netdev_monitor_add(bond->monitor, netdev);
 -        }
 -        slave->netdev = netdev;
 -    }
 +    bond_slave_set_netdev__(bond, slave, netdev);
  
      free(slave->name);
      slave->name = xstrdup(netdev_get_name(netdev));
  }
  
 +/* Updates the network device to be used with 'slave_' to 'netdev'.
 + *
 + * This is useful if the caller closes and re-opens the network device
 + * registered with bond_slave_register() but doesn't need to change anything
 + * else. */
 +void
 +bond_slave_set_netdev(struct bond *bond, void *slave_, struct netdev *netdev)
 +{
 +    struct bond_slave *slave = bond_slave_lookup(bond, slave_);
 +    if (slave) {
 +        bond_slave_set_netdev__(bond, slave, netdev);
 +    }
 +}
 +
  /* Unregisters 'slave_' from 'bond'.  If 'bond' does not contain such a slave
   * then this function has no effect.
   *
@@@ -999,6 -985,8 +1006,8 @@@ bond_unixctl_show(struct unixctl_conn *
                        bond_is_tcp_hash(bond) ? "balance-tcp" : "balance-slb");
      }
  
+     ds_put_format(&ds, "bond-hash-basis: %"PRIu32"\n", bond->basis);
      ds_put_format(&ds, "bond-detect-mode: %s\n",
                    bond->monitor ? "carrier" : "miimon");
  
@@@ -1227,11 -1215,13 +1236,13 @@@ bond_unixctl_hash(struct unixctl_conn *
      uint8_t hash;
      char *hash_cstr;
      unsigned int vlan;
-     char *mac_s, *vlan_s;
+     uint32_t basis;
+     char *mac_s, *vlan_s, *basis_s;
      char *save_ptr = NULL;
  
      mac_s  = strtok_r(args, " ", &save_ptr);
      vlan_s = strtok_r(NULL, " ", &save_ptr);
+     basis_s = strtok_r(NULL, " ", &save_ptr);
  
      if (vlan_s) {
          if (sscanf(vlan_s, "%u", &vlan) != 1) {
          vlan = OFP_VLAN_NONE;
      }
  
+     if (basis_s) {
+         if (sscanf(basis_s, "%"PRIu32, &basis) != 1) {
+             unixctl_command_reply(conn, 501, "invalid basis");
+             return;
+         }
+     } else {
+         basis = 0;
+     }
      if (sscanf(mac_s, ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(mac))
          == ETH_ADDR_SCAN_COUNT) {
-         hash = bond_hash_src(mac, vlan) & BOND_MASK;
+         hash = bond_hash_src(mac, vlan, basis) & BOND_MASK;
  
          hash_cstr = xasprintf("%u", hash);
          unixctl_command_reply(conn, 200, hash_cstr);
@@@ -1376,13 -1375,13 +1396,13 @@@ bond_is_tcp_hash(const struct bond *bon
  }
  
  static unsigned int
- bond_hash_src(const uint8_t mac[ETH_ADDR_LEN], uint16_t vlan)
+ bond_hash_src(const uint8_t mac[ETH_ADDR_LEN], uint16_t vlan, uint32_t basis)
  {
-     return hash_bytes(mac, ETH_ADDR_LEN, vlan);
+     return hash_3words(hash_bytes(mac, ETH_ADDR_LEN, 0), vlan, basis);
  }
  
  static unsigned int
- bond_hash_tcp(const struct flow *flow, uint16_t vlan)
+ bond_hash_tcp(const struct flow *flow, uint16_t vlan, uint32_t basis)
  {
      struct flow hash_flow = *flow;
      hash_flow.vlan_tci = vlan;
      /* The symmetric quality of this hash function is not required, but
       * flow_hash_symmetric_l4 already exists, and is sufficient for our
       * purposes, so we use it out of convenience. */
-     return flow_hash_symmetric_l4(&hash_flow, 0);
+     return flow_hash_symmetric_l4(&hash_flow, basis);
  }
  
  static unsigned int
@@@ -1399,8 -1398,8 +1419,8 @@@ bond_hash(const struct bond *bond, cons
      assert(bond->balance != BM_AB);
  
      return (bond_is_tcp_hash(bond)
-             ? bond_hash_tcp(flow, vlan)
-             : bond_hash_src(flow->dl_src, vlan));
+             ? bond_hash_tcp(flow, vlan, bond->basis)
+             : bond_hash_src(flow->dl_src, vlan, bond->basis));
  }
  
  static struct bond_entry *
diff --combined lib/bond.h
@@@ -50,6 -50,7 +50,7 @@@ const char *bond_detect_mode_to_string(
  /* Configuration for a bond as a whole. */
  struct bond_settings {
      char *name;                 /* Bond's name, for log messages. */
+     uint32_t basis;             /* Flow hashing basis. */
  
      /* Balancing configuration. */
      enum bond_mode balance;
@@@ -74,8 -75,7 +75,8 @@@ void bond_destroy(struct bond *)
  
  bool bond_reconfigure(struct bond *, const struct bond_settings *);
  void bond_slave_register(struct bond *, void *slave_,
-                          uint16_t stable_id, struct netdev *);
+                          uint32_t stable_id, struct netdev *);
 +void bond_slave_set_netdev(struct bond *, void *slave_, struct netdev *);
  void bond_slave_unregister(struct bond *, const void *slave);
  
  void bond_run(struct bond *, struct tag_set *, bool lacp_negotiated);
diff --combined lib/dpif-linux.c
@@@ -35,6 -35,7 +35,7 @@@
  #include "bitmap.h"
  #include "dpif-provider.h"
  #include "netdev.h"
+ #include "netdev-linux.h"
  #include "netdev-vport.h"
  #include "netlink-socket.h"
  #include "netlink.h"
@@@ -431,6 -432,12 +432,12 @@@ dpif_linux_port_query__(const struct dp
          dpif_port->name = xstrdup(reply.name);
          dpif_port->type = xstrdup(netdev_vport_get_netdev_type(&reply));
          dpif_port->port_no = reply.port_no;
+         if (reply.stats) {
+             netdev_stats_from_rtnl_link_stats64(&dpif_port->stats,
+                                                 reply.stats);
+         } else {
+             memset(&dpif_port->stats, 0xff, sizeof dpif_port->stats);
+         }
          ofpbuf_delete(buf);
      }
      return error;
@@@ -472,6 -479,8 +479,8 @@@ dpif_linux_flow_flush(struct dpif *dpif
  
  struct dpif_linux_port_state {
      struct nl_dump dump;
+     unsigned long *port_bitmap; /* Ports in the datapath. */
+     bool complete;              /* Dump completed without error. */
  };
  
  static int
@@@ -483,6 -492,8 +492,8 @@@ dpif_linux_port_dump_start(const struc
      struct ofpbuf *buf;
  
      *statep = state = xmalloc(sizeof *state);
+     state->port_bitmap = bitmap_allocate(LRU_MAX_PORTS);
+     state->complete = false;
  
      dpif_linux_vport_init(&request);
      request.cmd = ODP_DP_CMD_GET;
@@@ -506,6 -517,7 +517,7 @@@ dpif_linux_port_dump_next(const struct 
      int error;
  
      if (!nl_dump_next(&state->dump, &buf)) {
+         state->complete = true;
          return EOF;
      }
  
          return error;
      }
  
+     if (vport.port_no < LRU_MAX_PORTS) {
+         bitmap_set1(state->port_bitmap, vport.port_no);
+     }
      dpif_port->name = (char *) vport.name;
      dpif_port->type = (char *) netdev_vport_get_netdev_type(&vport);
      dpif_port->port_no = vport.port_no;
+     if (vport.stats) {
+         netdev_stats_from_rtnl_link_stats64(&dpif_port->stats, vport.stats);
+     } else {
+         memset(&dpif_port->stats, 0xff, sizeof dpif_port->stats);
+     }
      return 0;
  }
  
  static int
- dpif_linux_port_dump_done(const struct dpif *dpif OVS_UNUSED, void *state_)
+ dpif_linux_port_dump_done(const struct dpif *dpif_, void *state_)
  {
+     struct dpif_linux *dpif = dpif_linux_cast(dpif_);
      struct dpif_linux_port_state *state = state_;
      int error = nl_dump_done(&state->dump);
+     if (state->complete) {
+         uint16_t i;
+         for (i = 0; i < LRU_MAX_PORTS; i++) {
+             if (!bitmap_is_set(state->port_bitmap, i)) {
+                 dpif_linux_push_port(dpif, i);
+             }
+         }
+     }
+     free(state->port_bitmap);
      free(state);
      return error;
  }
@@@ -994,12 -1028,12 +1028,12 @@@ dpif_linux_recv_purge(struct dpif *dpif
  
  const struct dpif_class dpif_linux_class = {
      "system",
 -    NULL,                       /* run */
 -    NULL,                       /* wait */
      dpif_linux_enumerate,
      dpif_linux_open,
      dpif_linux_close,
      dpif_linux_destroy,
 +    NULL,                       /* run */
 +    NULL,                       /* wait */
      dpif_linux_get_stats,
      dpif_linux_get_drop_frags,
      dpif_linux_set_drop_frags,
diff --combined lib/dpif.c
@@@ -101,6 -101,38 +101,6 @@@ dp_initialize(void
      }
  }
  
 -/* Performs periodic work needed by all the various kinds of dpifs.
 - *
 - * If your program opens any dpifs, it must call both this function and
 - * netdev_run() within its main poll loop. */
 -void
 -dp_run(void)
 -{
 -    struct shash_node *node;
 -    SHASH_FOR_EACH(node, &dpif_classes) {
 -        const struct registered_dpif_class *registered_class = node->data;
 -        if (registered_class->dpif_class->run) {
 -            registered_class->dpif_class->run();
 -        }
 -    }
 -}
 -
 -/* Arranges for poll_block() to wake up when dp_run() needs to be called.
 - *
 - * If your program opens any dpifs, it must call both this function and
 - * netdev_wait() within its main poll loop. */
 -void
 -dp_wait(void)
 -{
 -    struct shash_node *node;
 -    SHASH_FOR_EACH(node, &dpif_classes) {
 -        const struct registered_dpif_class *registered_class = node->data;
 -        if (registered_class->dpif_class->wait) {
 -            registered_class->dpif_class->wait();
 -        }
 -    }
 -}
 -
  /* Registers a new datapath provider.  After successful registration, new
   * datapaths of that type can be opened using dpif_open(). */
  int
@@@ -200,10 -232,8 +200,10 @@@ dp_enumerate_names(const char *type, st
      return error;
  }
  
 -/* Parses 'datapath name', which is of the form type@name into its
 - * component pieces.  'name' and 'type' must be freed by the caller. */
 +/* Parses 'datapath_name_', which is of the form [type@]name into its
 + * component pieces.  'name' and 'type' must be freed by the caller.
 + *
 + * The returned 'type' is normalized, as if by dpif_normalize_type(). */
  void
  dp_parse_name(const char *datapath_name_, char **name, char **type)
  {
      if (separator) {
          *separator = '\0';
          *type = datapath_name;
 -        *name = xstrdup(separator + 1);
 +        *name = xstrdup(dpif_normalize_type(separator + 1));
      } else {
          *name = datapath_name;
 -        *type = NULL;
 +        *type = xstrdup(dpif_normalize_type(NULL));
      }
  }
  
@@@ -230,7 -260,9 +230,7 @@@ do_open(const char *name, const char *t
  
      dp_initialize();
  
 -    if (!type || *type == '\0') {
 -        type = "system";
 -    }
 +    type = dpif_normalize_type(type);
  
      registered_class = shash_find_data(&dpif_classes, type);
      if (!registered_class) {
@@@ -315,25 -347,6 +315,25 @@@ dpif_close(struct dpif *dpif
      }
  }
  
 +/* Performs periodic work needed by 'dpif'. */
 +void
 +dpif_run(struct dpif *dpif)
 +{
 +    if (dpif->dpif_class->run) {
 +        dpif->dpif_class->run(dpif);
 +    }
 +}
 +
 +/* Arranges for poll_block() to wake up when dp_run() needs to be called for
 + * 'dpif'. */
 +void
 +dpif_wait(struct dpif *dpif)
 +{
 +    if (dpif->dpif_class->wait) {
 +        dpif->dpif_class->wait(dpif);
 +    }
 +}
 +
  /* Returns the name of datapath 'dpif' prefixed with the type
   * (for use in log messages). */
  const char *
@@@ -350,16 -363,6 +350,16 @@@ dpif_base_name(const struct dpif *dpif
      return dpif->base_name;
  }
  
 +/* Returns the fully spelled out name for the given datapath 'type'.
 + *
 + * Normalized type string can be compared with strcmp().  Unnormalized type
 + * string might be the same even if they have different spellings. */
 +const char *
 +dpif_normalize_type(const char *type)
 +{
 +    return type && type[0] ? type : "system";
 +}
 +
  /* Destroys the datapath that 'dpif' is connected to, first removing all of its
   * ports.  After calling this function, it does not make sense to pass 'dpif'
   * to any functions other than dpif_name() or dpif_close(). */
@@@ -469,6 -472,7 +469,7 @@@ dpif_port_clone(struct dpif_port *dst, 
      dst->name = xstrdup(src->name);
      dst->type = xstrdup(src->type);
      dst->port_no = src->port_no;
+     dst->stats = src->stats;
  }
  
  /* Frees memory allocated to members of 'dpif_port'.
@@@ -522,14 -526,11 +523,14 @@@ dpif_port_query_by_name(const struct dp
      } else {
          memset(port, 0, sizeof *port);
  
 -        /* Log level is DBG here because all the current callers are interested
 -         * in whether 'dpif' actually has a port 'devname', so that it's not an
 -         * issue worth logging if it doesn't. */
 -        VLOG_DBG_RL(&error_rl, "%s: failed to query port %s: %s",
 -                    dpif_name(dpif), devname, strerror(error));
 +        /* For ENOENT or ENODEV we use DBG level because the caller is probably
 +         * interested in whether 'dpif' actually has a port 'devname', so that
 +         * it's not an issue worth logging if it doesn't.  Other errors are
 +         * uncommon and more likely to indicate a real problem. */
 +        VLOG_RL(&error_rl,
 +                error == ENOENT || error == ENODEV ? VLL_DBG : VLL_WARN,
 +                "%s: failed to query port %s: %s",
 +                dpif_name(dpif), devname, strerror(error));
      }
      return error;
  }
diff --combined lib/dpif.h
@@@ -23,6 -23,7 +23,7 @@@
  #include <stdint.h>
  #include "openflow/openflow.h"
  #include "openvswitch/datapath-protocol.h"
+ #include "netdev.h"
  #include "util.h"
  
  #ifdef  __cplusplus
@@@ -31,16 -32,17 +32,15 @@@ extern "C" 
  
  struct dpif;
  struct ds;
- struct netdev;
  struct nlattr;
  struct ofpbuf;
  struct sset;
  struct dpif_class;
  
 -void dp_run(void);
 -void dp_wait(void);
 -
  int dp_register_provider(const struct dpif_class *);
  int dp_unregister_provider(const char *type);
  void dp_enumerate_types(struct sset *types);
 +const char *dpif_normalize_type(const char *);
  
  int dp_enumerate_names(const char *type, struct sset *names);
  void dp_parse_name(const char *datapath_name, char **name, char **type);
@@@ -50,9 -52,6 +50,9 @@@ int dpif_create(const char *name, cons
  int dpif_create_and_open(const char *name, const char *type, struct dpif **);
  void dpif_close(struct dpif *);
  
 +void dpif_run(struct dpif *);
 +void dpif_wait(struct dpif *);
 +
  const char *dpif_name(const struct dpif *);
  const char *dpif_base_name(const struct dpif *);
  
@@@ -72,6 -71,7 +72,7 @@@ struct dpif_port 
      char *name;                 /* Network device name, e.g. "eth0". */
      char *type;                 /* Network device type, e.g. "system". */
      uint32_t port_no;           /* Port number within datapath. */
+     struct netdev_stats stats;  /* Port statistics. */
  };
  void dpif_port_clone(struct dpif_port *, const struct dpif_port *);
  void dpif_port_destroy(struct dpif_port *);
diff --combined lib/flow.h
@@@ -45,7 -45,7 +45,7 @@@ struct flow 
      uint32_t regs[FLOW_N_REGS]; /* Registers. */
      ovs_be32 nw_src;            /* IPv4 source address. */
      ovs_be32 nw_dst;            /* IPv4 destination address. */
 -    uint16_t in_port;           /* Input switch port. */
 +    uint16_t in_port;           /* OpenFlow port number of input port. */
      ovs_be16 vlan_tci;          /* If 802.1Q, TCI | VLAN_CFI; otherwise 0. */
      ovs_be16 dl_type;           /* Ethernet frame type. */
      ovs_be16 tp_src;            /* TCP/UDP source port. */
@@@ -118,7 -118,7 +118,7 @@@ typedef unsigned int OVS_BITWISE flow_w
  #define FWW_TP_DST      ((OVS_FORCE flow_wildcards_t) (1 << 7))
  /* Same meanings as corresponding OFPFW_* bits, but differ in value. */
  #define FWW_NW_TOS      ((OVS_FORCE flow_wildcards_t) (1 << 1))
--/* No corresponding OFPFW_* or OVSFW_* bits. */
++/* No corresponding OFPFW_* bits. */
  #define FWW_ETH_MCAST   ((OVS_FORCE flow_wildcards_t) (1 << 8))
                                                         /* multicast bit only */
  #define FWW_ARP_SHA     ((OVS_FORCE flow_wildcards_t) (1 << 9))
diff --combined lib/learning-switch.c
@@@ -238,10 -238,8 +238,9 @@@ lswitch_process_packet(struct lswitch *
      case OFPUTIL_OFPST_PORT_REPLY:
      case OFPUTIL_OFPST_TABLE_REPLY:
      case OFPUTIL_OFPST_AGGREGATE_REPLY:
-     case OFPUTIL_NXT_TUN_ID_FROM_COOKIE:
      case OFPUTIL_NXT_ROLE_REQUEST:
      case OFPUTIL_NXT_ROLE_REPLY:
 +    case OFPUTIL_NXT_FLOW_MOD_TABLE_ID:
      case OFPUTIL_NXT_SET_FLOW_FORMAT:
      case OFPUTIL_NXT_FLOW_MOD:
      case OFPUTIL_NXT_FLOW_REMOVED:
diff --combined lib/netdev-linux.c
@@@ -15,6 -15,9 +15,9 @@@
   */
  
  #include <config.h>
+ #include "netdev-linux.h"
  #include <assert.h>
  #include <errno.h>
  #include <fcntl.h>
@@@ -2014,7 -2017,7 +2017,7 @@@ netdev_linux_get_next_hop(const struct 
      while (fgets(line, sizeof line, stream)) {
          if (++ln >= 2) {
              char iface[17];
 -            uint32_t dest, gateway, mask;
 +            ovs_be32 dest, gateway, mask;
              int refcnt, metric, mtu;
              unsigned int flags, use, window, irtt;
  
@@@ -2081,7 -2084,7 +2084,7 @@@ netdev_linux_get_status(const struct ne
   * ENXIO indicates that there is not ARP table entry for 'ip' on 'netdev'. */
  static int
  netdev_linux_arp_lookup(const struct netdev *netdev,
 -                        uint32_t ip, uint8_t mac[ETH_ADDR_LEN])
 +                        ovs_be32 ip, uint8_t mac[ETH_ADDR_LEN])
  {
      struct arpreq r;
      struct sockaddr_in sin;
@@@ -3925,7 -3928,55 +3928,55 @@@ tc_calc_buffer(unsigned int Bps, int mt
      unsigned int min_burst = tc_buffer_per_jiffy(Bps) + mtu;
      return tc_bytes_to_ticks(Bps, MAX(burst_bytes, min_burst));
  }
\f
+ /* Public utility functions. */
+ #define COPY_NETDEV_STATS                                   \
+     dst->rx_packets = src->rx_packets;                      \
+     dst->tx_packets = src->tx_packets;                      \
+     dst->rx_bytes = src->rx_bytes;                          \
+     dst->tx_bytes = src->tx_bytes;                          \
+     dst->rx_errors = src->rx_errors;                        \
+     dst->tx_errors = src->tx_errors;                        \
+     dst->rx_dropped = src->rx_dropped;                      \
+     dst->tx_dropped = src->tx_dropped;                      \
+     dst->multicast = src->multicast;                        \
+     dst->collisions = src->collisions;                      \
+     dst->rx_length_errors = src->rx_length_errors;          \
+     dst->rx_over_errors = src->rx_over_errors;              \
+     dst->rx_crc_errors = src->rx_crc_errors;                \
+     dst->rx_frame_errors = src->rx_frame_errors;            \
+     dst->rx_fifo_errors = src->rx_fifo_errors;              \
+     dst->rx_missed_errors = src->rx_missed_errors;          \
+     dst->tx_aborted_errors = src->tx_aborted_errors;        \
+     dst->tx_carrier_errors = src->tx_carrier_errors;        \
+     dst->tx_fifo_errors = src->tx_fifo_errors;              \
+     dst->tx_heartbeat_errors = src->tx_heartbeat_errors;    \
+     dst->tx_window_errors = src->tx_window_errors
+ /* Copies 'src' into 'dst', performing format conversion in the process. */
+ void
+ netdev_stats_from_rtnl_link_stats(struct netdev_stats *dst,
+                                   const struct rtnl_link_stats *src)
+ {
+     COPY_NETDEV_STATS;
+ }
+ /* Copies 'src' into 'dst', performing format conversion in the process. */
+ void
+ netdev_stats_from_rtnl_link_stats64(struct netdev_stats *dst,
+                                     const struct rtnl_link_stats64 *src)
+ {
+     COPY_NETDEV_STATS;
+ }
+ /* Copies 'src' into 'dst', performing format conversion in the process. */
+ void
+ netdev_stats_to_rtnl_link_stats64(struct rtnl_link_stats64 *dst,
+                                   const struct netdev_stats *src)
+ {
+     COPY_NETDEV_STATS;
+ }
  \f
  /* Utility functions. */
  
@@@ -3945,7 -3996,6 +3996,6 @@@ get_stats_via_netlink(int ifindex, stru
      struct ofpbuf request;
      struct ofpbuf *reply;
      struct ifinfomsg *ifi;
-     const struct rtnl_link_stats *rtnl_stats;
      struct nlattr *attrs[ARRAY_SIZE(rtnlgrp_link_policy)];
      int error;
  
          return EPROTO;
      }
  
-     rtnl_stats = nl_attr_get(attrs[IFLA_STATS]);
-     stats->rx_packets = rtnl_stats->rx_packets;
-     stats->tx_packets = rtnl_stats->tx_packets;
-     stats->rx_bytes = rtnl_stats->rx_bytes;
-     stats->tx_bytes = rtnl_stats->tx_bytes;
-     stats->rx_errors = rtnl_stats->rx_errors;
-     stats->tx_errors = rtnl_stats->tx_errors;
-     stats->rx_dropped = rtnl_stats->rx_dropped;
-     stats->tx_dropped = rtnl_stats->tx_dropped;
-     stats->multicast = rtnl_stats->multicast;
-     stats->collisions = rtnl_stats->collisions;
-     stats->rx_length_errors = rtnl_stats->rx_length_errors;
-     stats->rx_over_errors = rtnl_stats->rx_over_errors;
-     stats->rx_crc_errors = rtnl_stats->rx_crc_errors;
-     stats->rx_frame_errors = rtnl_stats->rx_frame_errors;
-     stats->rx_fifo_errors = rtnl_stats->rx_fifo_errors;
-     stats->rx_missed_errors = rtnl_stats->rx_missed_errors;
-     stats->tx_aborted_errors = rtnl_stats->tx_aborted_errors;
-     stats->tx_carrier_errors = rtnl_stats->tx_carrier_errors;
-     stats->tx_fifo_errors = rtnl_stats->tx_fifo_errors;
-     stats->tx_heartbeat_errors = rtnl_stats->tx_heartbeat_errors;
-     stats->tx_window_errors = rtnl_stats->tx_window_errors;
+     netdev_stats_from_rtnl_link_stats(stats, nl_attr_get(attrs[IFLA_STATS]));
  
      ofpbuf_delete(reply);
  
diff --combined lib/ofp-parse.c
@@@ -608,6 -608,9 +608,6 @@@ parse_field_value(struct cls_rule *rule
          if (!parse_port_name(value, &port_no)) {
              port_no = atoi(value);
          }
 -        if (port_no == OFPP_LOCAL) {
 -            port_no = ODPP_LOCAL;
 -        }
          cls_rule_set_in_port(rule, port_no);
          break;
  
@@@ -729,14 -732,17 +729,14 @@@ parse_reg_value(struct cls_rule *rule, 
   * man page) into 'pf'.  If 'actions' is specified, an action must be in
   * 'string' and may be expanded or reallocated. */
  void
 -parse_ofp_str(struct flow_mod *fm, uint8_t *table_idx,
 -              struct ofpbuf *actions, char *string)
 +parse_ofp_str(struct flow_mod *fm, struct ofpbuf *actions, char *string)
  {
      char *save_ptr = NULL;
      char *name;
  
 -    if (table_idx) {
 -        *table_idx = 0xff;
 -    }
      cls_rule_init_catchall(&fm->cr, OFP_DEFAULT_PRIORITY);
      fm->cookie = htonll(0);
 +    fm->table_id = 0xff;
      fm->command = UINT16_MAX;
      fm->idle_timeout = OFP_FLOW_PERMANENT;
      fm->hard_timeout = OFP_FLOW_PERMANENT;
                  ovs_fatal(0, "field %s missing value", name);
              }
  
 -            if (table_idx && !strcmp(name, "table")) {
 -                *table_idx = atoi(value);
 +            if (!strcmp(name, "table")) {
 +                fm->table_id = atoi(value);
              } else if (!strcmp(name, "out_port")) {
                  fm->out_port = atoi(value);
              } else if (!strcmp(name, "priority")) {
   * flow. */
  void
  parse_ofp_flow_mod_str(struct list *packets, enum nx_flow_format *cur_format,
 -                       char *string, uint16_t command)
 +                       bool *flow_mod_table_id, char *string, uint16_t command)
  {
      bool is_del = command == OFPFC_DELETE || command == OFPFC_DELETE_STRICT;
      enum nx_flow_format min_format, next_format;
      struct flow_mod fm;
  
      ofpbuf_init(&actions, 64);
 -    parse_ofp_str(&fm, NULL, is_del ? NULL : &actions, string);
 +    parse_ofp_str(&fm, is_del ? NULL : &actions, string);
      fm.command = command;
  
-     min_format = ofputil_min_flow_format(&fm.cr, true, fm.cookie);
+     min_format = ofputil_min_flow_format(&fm.cr);
      next_format = MAX(*cur_format, min_format);
      if (next_format != *cur_format) {
          struct ofpbuf *sff = ofputil_make_set_flow_format(next_format);
          *cur_format = next_format;
      }
  
 -    ofm = ofputil_encode_flow_mod(&fm, *cur_format);
 +    if (fm.table_id != 0xff && !*flow_mod_table_id) {
 +        struct ofpbuf *sff = ofputil_make_flow_mod_table_id(true);
 +        list_push_back(packets, &sff->list_node);
 +        *flow_mod_table_id = true;
 +    }
 +
 +    ofm = ofputil_encode_flow_mod(&fm, *cur_format, *flow_mod_table_id);
      list_push_back(packets, &ofm->list_node);
  
      ofpbuf_uninit(&actions);
   * 'stream' and the command is always OFPFC_ADD.  Returns false if end-of-file
   * is reached before reading a flow, otherwise true. */
  bool
 -parse_ofp_flow_mod_file(struct list *packets, enum nx_flow_format *cur,
 +parse_ofp_flow_mod_file(struct list *packets,
 +                        enum nx_flow_format *cur, bool *flow_mod_table_id,
                          FILE *stream, uint16_t command)
  {
      struct ds s;
      ds_init(&s);
      ok = ds_get_preprocessed_line(&s, stream) == 0;
      if (ok) {
 -        parse_ofp_flow_mod_str(packets, cur, ds_cstr(&s), command);
 +        parse_ofp_flow_mod_str(packets, cur, flow_mod_table_id,
 +                               ds_cstr(&s), command);
      }
      ds_destroy(&s);
  
@@@ -900,10 -898,11 +900,10 @@@ parse_ofp_flow_stats_request_str(struc
                                   bool aggregate, char *string)
  {
      struct flow_mod fm;
 -    uint8_t table_id;
  
 -    parse_ofp_str(&fm, &table_id, NULL, string);
 +    parse_ofp_str(&fm, NULL, string);
      fsr->aggregate = aggregate;
      fsr->match = fm.cr;
      fsr->out_port = fm.out_port;
 -    fsr->table_id = table_id;
 +    fsr->table_id = fm.table_id;
  }
diff --combined lib/ofp-print.c
@@@ -722,7 -722,7 +722,7 @@@ static void print_wild(struct ds *strin
  }
  
  static void
 -print_ip_netmask(struct ds *string, const char *leader, uint32_t ip,
 +print_ip_netmask(struct ds *string, const char *leader, ovs_be32 ip,
                   uint32_t wild_bits, int verbosity)
  {
      if (wild_bits >= 32 && verbosity < 2) {
@@@ -780,9 -780,6 +780,6 @@@ ofp_match_to_string(const struct ofp_ma
              skip_type = false;
          }
      }
-     if (w & NXFW_TUN_ID) {
-         ds_put_cstr(&f, "tun_id_wild,");
-     }
      print_wild(&f, "in_port=", w & OFPFW_IN_PORT, verbosity,
                 "%d", ntohs(om->in_port));
      print_wild(&f, "dl_vlan=", w & OFPFW_DL_VLAN, verbosity,
@@@ -837,7 -834,7 +834,7 @@@ ofp_print_flow_mod(struct ds *s, const 
      bool need_priority;
      int error;
  
-     error = ofputil_decode_flow_mod(&fm, oh, NXFF_OPENFLOW10, true);
 -    error = ofputil_decode_flow_mod(&fm, oh);
++    error = ofputil_decode_flow_mod(&fm, oh, true);
      if (error) {
          ofp_print_error(s, error);
          return;
      default:
          ds_put_format(s, "cmd:%d", fm.command);
      }
 +    if (fm.table_id != 0) {
 +        ds_put_format(s, " table_id:%d", fm.table_id);
 +    }
  
      ds_put_char(s, ' ');
      if (verbosity >= 3 && code == OFPUTIL_OFPT_FLOW_MOD) {
@@@ -937,7 -931,7 +934,7 @@@ ofp_print_flow_removed(struct ds *strin
      struct ofputil_flow_removed fr;
      int error;
  
-     error = ofputil_decode_flow_removed(&fr, oh, NXFF_OPENFLOW10);
+     error = ofputil_decode_flow_removed(&fr, oh);
      if (error) {
          ofp_print_error(string, error);
          return;
@@@ -1073,7 -1067,7 +1070,7 @@@ ofp_print_flow_stats_request(struct ds 
      struct flow_stats_request fsr;
      int error;
  
-     error = ofputil_decode_flow_stats_request(&fsr, oh, NXFF_OPENFLOW10);
+     error = ofputil_decode_flow_stats_request(&fsr, oh);
      if (error) {
          ofp_print_error(string, error);
          return;
@@@ -1106,7 -1100,7 +1103,7 @@@ ofp_print_flow_stats_reply(struct ds *s
          struct ofputil_flow_stats fs;
          int retval;
  
-         retval = ofputil_decode_flow_stats_reply(&fs, &b, NXFF_OPENFLOW10);
+         retval = ofputil_decode_flow_stats_reply(&fs, &b);
          if (retval) {
              if (retval != EOF) {
                  ds_put_cstr(string, " ***parse error***");
@@@ -1334,13 -1328,6 +1331,6 @@@ ofp_print_echo(struct ds *string, cons
      }
  }
  
- static void
- ofp_print_nxt_tun_id_from_cookie(struct ds *string,
-                                  const struct nxt_tun_id_cookie *ntic)
- {
-     ds_put_format(string, " set=%"PRIu8, ntic->set);
- }
  static void
  ofp_print_nxt_role_message(struct ds *string,
                             const struct nx_role_request *nrr)
      }
  }
  
 +static void
 +ofp_print_nxt_flow_mod_table_id(struct ds *string,
 +                                const struct nxt_flow_mod_table_id *nfmti)
 +{
 +    ds_put_format(string, " %s", nfmti->set ? "enable" : "disable");
 +}
 +
  static void
  ofp_print_nxt_set_flow_format(struct ds *string,
                                const struct nxt_set_flow_format *nsff)
@@@ -1517,19 -1497,11 +1507,15 @@@ ofp_to_string__(const struct ofp_heade
          ofp_print_ofpst_aggregate_reply(string, oh);
          break;
  
-     case OFPUTIL_NXT_TUN_ID_FROM_COOKIE:
-         ofp_print_nxt_tun_id_from_cookie(string, msg);
-         break;
      case OFPUTIL_NXT_ROLE_REQUEST:
      case OFPUTIL_NXT_ROLE_REPLY:
          ofp_print_nxt_role_message(string, msg);
          break;
  
 +    case OFPUTIL_NXT_FLOW_MOD_TABLE_ID:
 +        ofp_print_nxt_flow_mod_table_id(string, msg);
 +        break;
 +
      case OFPUTIL_NXT_SET_FLOW_FORMAT:
          ofp_print_nxt_set_flow_format(string, msg);
          break;
diff --combined lib/ofp-util.c
@@@ -102,25 -102,17 +102,17 @@@ enum 
  };
  
  /* Converts the ofp_match in 'match' into a cls_rule in 'rule', with the given
-  * 'priority'.
-  *
-  * 'flow_format' must either NXFF_OPENFLOW10 or NXFF_TUN_ID_FROM_COOKIE.  In
-  * the latter case only, 'flow''s tun_id field will be taken from the high bits
-  * of 'cookie', if 'match''s wildcards do not indicate that tun_id is
-  * wildcarded. */
+  * 'priority'. */
  void
  ofputil_cls_rule_from_match(const struct ofp_match *match,
-                             unsigned int priority,
-                             enum nx_flow_format flow_format,
-                             ovs_be64 cookie, struct cls_rule *rule)
+                             unsigned int priority, struct cls_rule *rule)
  {
      struct flow_wildcards *wc = &rule->wc;
      unsigned int ofpfw;
      ovs_be16 vid, pcp;
  
      /* Initialize rule->priority. */
-     ofpfw = ntohl(match->wildcards);
-     ofpfw &= flow_format == NXFF_TUN_ID_FROM_COOKIE ? OVSFW_ALL : OFPFW_ALL;
+     ofpfw = ntohl(match->wildcards) & OFPFW_ALL;
      rule->priority = !ofpfw ? UINT16_MAX : priority;
  
      /* Initialize most of rule->wc. */
      wc->nw_src_mask = ofputil_wcbits_to_netmask(ofpfw >> OFPFW_NW_SRC_SHIFT);
      wc->nw_dst_mask = ofputil_wcbits_to_netmask(ofpfw >> OFPFW_NW_DST_SHIFT);
  
-     if (flow_format == NXFF_TUN_ID_FROM_COOKIE && !(ofpfw & NXFW_TUN_ID)) {
-         cls_rule_set_tun_id(rule, htonll(ntohll(cookie) >> 32));
-     }
      if (ofpfw & OFPFW_DL_DST) {
          /* OpenFlow 1.0 OFPFW_DL_DST covers the whole Ethernet destination, but
           * Open vSwitch breaks the Ethernet destination into bits as FWW_DL_DST
      /* Initialize most of rule->flow. */
      rule->flow.nw_src = match->nw_src;
      rule->flow.nw_dst = match->nw_dst;
 -    rule->flow.in_port = (match->in_port == htons(OFPP_LOCAL) ? ODPP_LOCAL
 -                     : ntohs(match->in_port));
 +    rule->flow.in_port = ntohs(match->in_port);
      rule->flow.dl_type = ofputil_dl_type_from_openflow(match->dl_type);
      rule->flow.tp_src = match->tp_src;
      rule->flow.tp_dst = match->tp_dst;
      cls_rule_zero_wildcarded_fields(rule);
  }
  
- /* Convert 'rule' into the OpenFlow match structure 'match'.  'flow_format'
-  * must either NXFF_OPENFLOW10 or NXFF_TUN_ID_FROM_COOKIE.
-  *
-  * The NXFF_TUN_ID_FROM_COOKIE flow format requires modifying the flow cookie.
-  * This function can help with that, if 'cookie_out' is nonnull.  For
-  * NXFF_OPENFLOW10, or if the tunnel ID is wildcarded, 'cookie_in' will be
-  * copied directly to '*cookie_out'.  For NXFF_TUN_ID_FROM_COOKIE when tunnel
-  * ID is matched, 'cookie_in' will be modified appropriately before setting
-  * '*cookie_out'.
-  */
+ /* Convert 'rule' into the OpenFlow match structure 'match'. */
  void
- ofputil_cls_rule_to_match(const struct cls_rule *rule,
-                           enum nx_flow_format flow_format,
-                           struct ofp_match *match,
-                           ovs_be64 cookie_in, ovs_be64 *cookie_out)
+ ofputil_cls_rule_to_match(const struct cls_rule *rule, struct ofp_match *match)
  {
      const struct flow_wildcards *wc = &rule->wc;
      unsigned int ofpfw;
          ofpfw |= OFPFW_NW_TOS;
      }
  
-     /* Tunnel ID. */
-     if (flow_format == NXFF_TUN_ID_FROM_COOKIE) {
-         if (wc->tun_id_mask == htonll(0)) {
-             ofpfw |= NXFW_TUN_ID;
-         } else {
-             uint32_t cookie_lo = ntohll(cookie_in);
-             uint32_t cookie_hi = ntohll(rule->flow.tun_id);
-             cookie_in = htonll(cookie_lo | ((uint64_t) cookie_hi << 32));
-         }
-     }
-     if (cookie_out) {
-         *cookie_out = cookie_in;
-     }
      /* Translate VLANs. */
      match->dl_vlan = htons(0);
      match->dl_vlan_pcp = 0;
  
      /* Compose most of the match structure. */
      match->wildcards = htonl(ofpfw);
 -    match->in_port = htons(rule->flow.in_port == ODPP_LOCAL ? OFPP_LOCAL
 -                           : rule->flow.in_port);
 +    match->in_port = htons(rule->flow.in_port);
      memcpy(match->dl_src, rule->flow.dl_src, ETH_ADDR_LEN);
      memcpy(match->dl_dst, rule->flow.dl_dst, ETH_ADDR_LEN);
      match->dl_type = ofputil_dl_type_to_openflow(rule->flow.dl_type);
@@@ -377,27 -341,16 +339,27 @@@ ofputil_lookup_openflow_message(const s
                                  const struct ofputil_msg_type **typep)
  {
      const struct ofputil_msg_type *type;
 +    bool found;
  
 +    found = false;
      for (type = cat->types; type < &cat->types[cat->n_types]; type++) {
          if (type->value == value) {
 -            if (!ofputil_length_ok(cat, type, size)) {
 -                return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN);
 +            if (ofputil_length_ok(cat, type, size)) {
 +                *typep = type;
 +                return 0;
              }
 -            *typep = type;
 -            return 0;
 +
 +            /* We found a matching command type but it had the wrong length.
 +             * Probably this is just an error.  However, a screwup means that
 +             * NXT_SET_FLOW_FORMAT and NXT_FLOW_MOD_TABLE_ID have the same
 +             * value.  They do have different lengths, so we can distinguish
 +             * them that way. */
 +            found = true;
          }
      }
 +    if (found) {
 +        return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN);
 +    }
  
      VLOG_WARN_RL(&bad_ofmsg_rl, "received %s of unknown type %"PRIu32,
                   cat->name, value);
@@@ -408,14 -361,7 +370,10 @@@ static in
  ofputil_decode_vendor(const struct ofp_header *oh,
                        const struct ofputil_msg_type **typep)
  {
 +    BUILD_ASSERT_DECL(sizeof(struct nxt_set_flow_format)
 +                      != sizeof(struct nxt_flow_mod_table_id));
 +
      static const struct ofputil_msg_type nxt_messages[] = {
-         { OFPUTIL_NXT_TUN_ID_FROM_COOKIE,
-           NXT_TUN_ID_FROM_COOKIE, "NXT_TUN_ID_FROM_COOKIE",
-           sizeof(struct nxt_tun_id_cookie), 0 },
          { OFPUTIL_NXT_ROLE_REQUEST,
            NXT_ROLE_REQUEST, "NXT_ROLE_REQUEST",
            sizeof(struct nx_role_request), 0 },
            NXT_SET_FLOW_FORMAT, "NXT_SET_FLOW_FORMAT",
            sizeof(struct nxt_set_flow_format), 0 },
  
 +        { OFPUTIL_NXT_FLOW_MOD_TABLE_ID,
 +          NXT_FLOW_MOD_TABLE_ID, "NXT_FLOW_MOD_TABLE_ID",
 +          sizeof(struct nxt_flow_mod_table_id), 0 },
 +
          { OFPUTIL_NXT_FLOW_MOD,
            NXT_FLOW_MOD, "NXT_FLOW_MOD",
            sizeof(struct nx_flow_mod), 8 },
@@@ -816,7 -758,6 +774,6 @@@ ofputil_flow_format_is_valid(enum nx_fl
  {
      switch (flow_format) {
      case NXFF_OPENFLOW10:
-     case NXFF_TUN_ID_FROM_COOKIE:
      case NXFF_NXM:
          return true;
      }
@@@ -830,8 -771,6 +787,6 @@@ ofputil_flow_format_to_string(enum nx_f
      switch (flow_format) {
      case NXFF_OPENFLOW10:
          return "openflow10";
-     case NXFF_TUN_ID_FROM_COOKIE:
-         return "tun_id_from_cookie";
      case NXFF_NXM:
          return "nxm";
      default:
@@@ -843,7 -782,6 +798,6 @@@ in
  ofputil_flow_format_from_string(const char *s)
  {
      return (!strcmp(s, "openflow10") ? NXFF_OPENFLOW10
-             : !strcmp(s, "tun_id_from_cookie") ? NXFF_TUN_ID_FROM_COOKIE
              : !strcmp(s, "nxm") ? NXFF_NXM
              : -1);
  }
@@@ -861,100 -799,43 +815,43 @@@ regs_fully_wildcarded(const struct flow
      return true;
  }
  
- static inline bool
- is_nxm_required(const struct cls_rule *rule, bool cookie_support,
-                 ovs_be64 cookie)
+ /* Returns the minimum nx_flow_format to use for sending 'rule' to a switch
+  * (e.g. to add or remove a flow).  Only NXM can handle tunnel IDs, registers,
+  * or fixing the Ethernet multicast bit.  Otherwise, it's better to use
+  * NXFF_OPENFLOW10 for backward compatibility. */
+ enum nx_flow_format
+ ofputil_min_flow_format(const struct cls_rule *rule)
  {
      const struct flow_wildcards *wc = &rule->wc;
-     uint32_t cookie_hi;
-     uint64_t tun_id;
  
      /* Only NXM supports separately wildcards the Ethernet multicast bit. */
      if (!(wc->wildcards & FWW_DL_DST) != !(wc->wildcards & FWW_ETH_MCAST)) {
-         return true;
+         return NXFF_NXM;
      }
  
      /* Only NXM supports matching ARP hardware addresses. */
      if (!(wc->wildcards & FWW_ARP_SHA) || !(wc->wildcards & FWW_ARP_THA)) {
-         return true;
+         return NXFF_NXM;
      }
  
      /* Only NXM supports matching IPv6 traffic. */
      if (!(wc->wildcards & FWW_DL_TYPE)
              && (rule->flow.dl_type == htons(ETH_TYPE_IPV6))) {
-         return true;
+         return NXFF_NXM;
      }
  
      /* Only NXM supports matching registers. */
      if (!regs_fully_wildcarded(wc)) {
-         return true;
+         return NXFF_NXM;
      }
  
-     switch (wc->tun_id_mask) {
-     case CONSTANT_HTONLL(0):
-         /* Other formats can fully wildcard tun_id. */
-         break;
-     case CONSTANT_HTONLL(UINT64_MAX):
-         /* Only NXM supports tunnel ID matching without a cookie. */
-         if (!cookie_support) {
-             return true;
-         }
-         /* Only NXM supports 64-bit tunnel IDs. */
-         tun_id = ntohll(rule->flow.tun_id);
-         if (tun_id > UINT32_MAX) {
-             return true;
-         }
-         /* Only NXM supports a cookie whose top 32 bits conflict with the
-          * tunnel ID. */
-         cookie_hi = ntohll(cookie) >> 32;
-         if (cookie_hi && cookie_hi != tun_id) {
-             return true;
-         }
-         break;
-     default:
-         /* Only NXM supports partial matches on tunnel ID. */
-         return true;
+     /* Only NXM supports matching tun_id. */
+     if (wc->tun_id_mask != htonll(0)) {
+         return NXFF_NXM;
      }
  
      /* Other formats can express this rule. */
-     return false;
- }
- /* Returns the minimum nx_flow_format to use for sending 'rule' to a switch
-  * (e.g. to add or remove a flow).  'cookie_support' should be true if the
-  * command to be sent includes a flow cookie (as OFPT_FLOW_MOD does, for
-  * example) or false if the command does not (OFPST_FLOW and OFPST_AGGREGATE do
-  * not, for example).  If 'cookie_support' is true, then 'cookie' should be the
-  * cookie to be sent; otherwise its value is ignored.
-  *
-  * The "best" flow format is chosen on this basis:
-  *
-  *   - It must be capable of expressing the rule.  NXFF_OPENFLOW10 flows can't
-  *     handle tunnel IDs.  NXFF_TUN_ID_FROM_COOKIE flows can't handle registers
-  *     or fixing the Ethernet multicast bit, and can't handle tunnel IDs that
-  *     conflict with the high 32 bits of the cookie or commands that don't
-  *     support cookies.
-  *
-  *   - Otherwise, the chosen format should be as backward compatible as
-  *     possible.  (NXFF_OPENFLOW10 is more backward compatible than
-  *     NXFF_TUN_ID_FROM_COOKIE, which is more backward compatible than
-  *     NXFF_NXM.)
-  */
- enum nx_flow_format
- ofputil_min_flow_format(const struct cls_rule *rule, bool cookie_support,
-                         ovs_be64 cookie)
- {
-     if (is_nxm_required(rule, cookie_support, cookie)) {
-         return NXFF_NXM;
-     } else if (rule->wc.tun_id_mask != htonll(0)) {
-         return NXFF_TUN_ID_FROM_COOKIE;
-     } else {
-         return NXFF_OPENFLOW10;
-     }
+     return NXFF_OPENFLOW10;
  }
  
  /* Returns an OpenFlow message that can be used to set the flow format to
  struct ofpbuf *
  ofputil_make_set_flow_format(enum nx_flow_format flow_format)
  {
+     struct nxt_set_flow_format *sff;
      struct ofpbuf *msg;
  
-     if (flow_format == NXFF_OPENFLOW10
-         || flow_format == NXFF_TUN_ID_FROM_COOKIE) {
-         struct nxt_tun_id_cookie *tic;
-         tic = make_nxmsg(sizeof *tic, NXT_TUN_ID_FROM_COOKIE, &msg);
-         tic->set = flow_format == NXFF_TUN_ID_FROM_COOKIE;
-     } else {
-         struct nxt_set_flow_format *sff;
-         sff = make_nxmsg(sizeof *sff, NXT_SET_FLOW_FORMAT, &msg);
-         sff->format = htonl(flow_format);
-     }
+     sff = make_nxmsg(sizeof *sff, NXT_SET_FLOW_FORMAT, &msg);
+     sff->format = htonl(flow_format);
  
      return msg;
  }
  
 +/* Returns an OpenFlow message that can be used to turn the flow_mod_table_id
 + * extension on or off (according to 'flow_mod_table_id'). */
 +struct ofpbuf *
 +ofputil_make_flow_mod_table_id(bool flow_mod_table_id)
 +{
 +    struct nxt_flow_mod_table_id *nfmti;
 +    struct ofpbuf *msg;
 +
 +    nfmti = make_nxmsg(sizeof *nfmti, NXT_FLOW_MOD_TABLE_ID, &msg);
 +    nfmti->set = flow_mod_table_id;
 +    return msg;
 +}
 +
  /* Converts an OFPT_FLOW_MOD or NXT_FLOW_MOD message 'oh' into an abstract
   * flow_mod in 'fm'.  Returns 0 if successful, otherwise an OpenFlow error
   * code.
   *
-  * For OFPT_FLOW_MOD messages, 'flow_format' should be the current flow format
-  * at the time when the message was received.  Otherwise 'flow_format' is
-  * ignored.
-  *
 + * 'flow_mod_table_id' should be true if the NXT_FLOW_MOD_TABLE_ID extension is
 + * enabled, false otherwise.
 + *
   * Does not validate the flow_mod actions. */
  int
 -ofputil_decode_flow_mod(struct flow_mod *fm, const struct ofp_header *oh)
 +ofputil_decode_flow_mod(struct flow_mod *fm, const struct ofp_header *oh,
-                         enum nx_flow_format flow_format,
 +                        bool flow_mod_table_id)
  {
      const struct ofputil_msg_type *type;
 +    uint16_t command;
      struct ofpbuf b;
  
      ofpbuf_use_const(&b, oh, ntohs(oh->length));
          }
  
          /* Translate the message. */
-         ofputil_cls_rule_from_match(&match, ntohs(ofm->priority), flow_format,
-                                     ofm->cookie, &fm->cr);
+         ofputil_cls_rule_from_match(&match, ntohs(ofm->priority), &fm->cr);
          fm->cookie = ofm->cookie;
 -        fm->command = ntohs(ofm->command);
 +        command = ntohs(ofm->command);
          fm->idle_timeout = ntohs(ofm->idle_timeout);
          fm->hard_timeout = ntohs(ofm->hard_timeout);
          fm->buffer_id = ntohl(ofm->buffer_id);
  
          /* Translate the message. */
          fm->cookie = nfm->cookie;
 -        fm->command = ntohs(nfm->command);
 +        command = ntohs(nfm->command);
          fm->idle_timeout = ntohs(nfm->idle_timeout);
          fm->hard_timeout = ntohs(nfm->hard_timeout);
          fm->buffer_id = ntohl(nfm->buffer_id);
          NOT_REACHED();
      }
  
 +    if (flow_mod_table_id) {
 +        fm->command = command & 0xff;
 +        fm->table_id = command >> 8;
 +    } else {
 +        fm->command = command;
 +        fm->table_id = 0xff;
 +    }
 +
      return 0;
  }
  
  /* Converts 'fm' into an OFPT_FLOW_MOD or NXT_FLOW_MOD message according to
 - * 'flow_format' and returns the message. */
 + * 'flow_format' and returns the message.
 + *
 + * 'flow_mod_table_id' should be true if the NXT_FLOW_MOD_TABLE_ID extension is
 + * enabled, false otherwise. */
  struct ofpbuf *
  ofputil_encode_flow_mod(const struct flow_mod *fm,
 -                        enum nx_flow_format flow_format)
 +                        enum nx_flow_format flow_format,
 +                        bool flow_mod_table_id)
  {
      size_t actions_len = fm->n_actions * sizeof *fm->actions;
      struct ofpbuf *msg;
 +    uint16_t command;
 +
 +    command = (flow_mod_table_id
 +               ? (fm->command & 0xff) | (fm->table_id << 8)
 +               : fm->command);
  
-     if (flow_format == NXFF_OPENFLOW10
-         || flow_format == NXFF_TUN_ID_FROM_COOKIE) {
+     if (flow_format == NXFF_OPENFLOW10) {
          struct ofp_flow_mod *ofm;
  
          msg = ofpbuf_new(sizeof *ofm + actions_len);
          ofm = put_openflow(sizeof *ofm, OFPT_FLOW_MOD, msg);
-         ofputil_cls_rule_to_match(&fm->cr, flow_format, &ofm->match,
-                                   fm->cookie, &ofm->cookie);
-         ofm->command = htons(command);
+         ofputil_cls_rule_to_match(&fm->cr, &ofm->match);
 -        ofm->cookie = fm->cookie;
+         ofm->command = htons(fm->command);
          ofm->idle_timeout = htons(fm->idle_timeout);
          ofm->hard_timeout = htons(fm->hard_timeout);
          ofm->priority = htons(fm->cr.priority);
  
          nfm = msg->data;
          nfm->cookie = fm->cookie;
 -        nfm->command = htons(fm->command);
 +        nfm->command = htons(command);
          nfm->idle_timeout = htons(fm->idle_timeout);
          nfm->hard_timeout = htons(fm->hard_timeout);
          nfm->priority = htons(fm->cr.priority);
  static int
  ofputil_decode_ofpst_flow_request(struct flow_stats_request *fsr,
                                    const struct ofp_header *oh,
-                                   enum nx_flow_format flow_format,
                                    bool aggregate)
  {
      const struct ofp_flow_stats_request *ofsr = ofputil_stats_body(oh);
  
      fsr->aggregate = aggregate;
-     ofputil_cls_rule_from_match(&ofsr->match, 0, flow_format, 0, &fsr->match);
+     ofputil_cls_rule_from_match(&ofsr->match, 0, &fsr->match);
      fsr->out_port = ntohs(ofsr->out_port);
      fsr->table_id = ofsr->table_id;
  
@@@ -1202,17 -1031,11 +1081,11 @@@ ofputil_decode_nxst_flow_request(struc
  }
  
  /* Converts an OFPST_FLOW, OFPST_AGGREGATE, NXST_FLOW, or NXST_AGGREGATE
-  * request 'oh', received when the current flow format was 'flow_format', into
-  * an abstract flow_stats_request in 'fsr'.  Returns 0 if successful, otherwise
-  * an OpenFlow error code.
-  *
-  * For OFPST_FLOW and OFPST_AGGREGATE messages, 'flow_format' should be the
-  * current flow format at the time when the message was received.  Otherwise
-  * 'flow_format' is ignored. */
+  * request 'oh', into an abstract flow_stats_request in 'fsr'.  Returns 0 if
+  * successful, otherwise an OpenFlow error code. */
  int
  ofputil_decode_flow_stats_request(struct flow_stats_request *fsr,
-                                   const struct ofp_header *oh,
-                                   enum nx_flow_format flow_format)
+                                   const struct ofp_header *oh)
  {
      const struct ofputil_msg_type *type;
      struct ofpbuf b;
      code = ofputil_msg_type_code(type);
      switch (code) {
      case OFPUTIL_OFPST_FLOW_REQUEST:
-         return ofputil_decode_ofpst_flow_request(fsr, oh, flow_format, false);
+         return ofputil_decode_ofpst_flow_request(fsr, oh, false);
  
      case OFPUTIL_OFPST_AGGREGATE_REQUEST:
-         return ofputil_decode_ofpst_flow_request(fsr, oh, flow_format, true);
+         return ofputil_decode_ofpst_flow_request(fsr, oh, true);
  
      case OFPUTIL_NXST_FLOW_REQUEST:
          return ofputil_decode_nxst_flow_request(fsr, oh, false);
@@@ -1250,8 -1073,7 +1123,7 @@@ ofputil_encode_flow_stats_request(cons
  {
      struct ofpbuf *msg;
  
-     if (flow_format == NXFF_OPENFLOW10
-         || flow_format == NXFF_TUN_ID_FROM_COOKIE) {
+     if (flow_format == NXFF_OPENFLOW10) {
          struct ofp_flow_stats_request *ofsr;
          int type;
  
  
          type = fsr->aggregate ? OFPST_AGGREGATE : OFPST_FLOW;
          ofsr = ofputil_make_stats_request(sizeof *ofsr, type, &msg);
-         ofputil_cls_rule_to_match(&fsr->match, flow_format, &ofsr->match,
-                                   0, NULL);
+         ofputil_cls_rule_to_match(&fsr->match, &ofsr->match);
          ofsr->table_id = fsr->table_id;
          ofsr->out_port = htons(fsr->out_port);
      } else if (flow_format == NXFF_NXM) {
  }
  
  /* Converts an OFPST_FLOW or NXST_FLOW reply in 'msg' into an abstract
-  * ofputil_flow_stats in 'fs'.  For OFPST_FLOW messages, 'flow_format' should
-  * be the current flow format at the time when the request corresponding to the
-  * reply in 'msg' was sent.  Otherwise 'flow_format' is ignored.
+  * ofputil_flow_stats in 'fs'.
   *
   * Multiple OFPST_FLOW or NXST_FLOW replies can be packed into a single
   * OpenFlow message.  Calling this function multiple times for a single 'msg'
   * otherwise a positive errno value. */
  int
  ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs,
-                                 struct ofpbuf *msg,
-                                 enum nx_flow_format flow_format)
+                                 struct ofpbuf *msg)
  {
      const struct ofputil_msg_type *type;
      int code;
  
          fs->cookie = get_32aligned_be64(&ofs->cookie);
          ofputil_cls_rule_from_match(&ofs->match, ntohs(ofs->priority),
-                                     flow_format, fs->cookie, &fs->rule);
+                                     &fs->rule);
          fs->table_id = ofs->table_id;
          fs->duration_sec = ntohl(ofs->duration_sec);
          fs->duration_nsec = ntohl(ofs->duration_nsec);
      return 0;
  }
  
- /* Converts an OFPT_FLOW_REMOVED or NXT_FLOW_REMOVED message 'oh', received
-  * when the current flow format was 'flow_format', into an abstract
-  * ofputil_flow_removed in 'fr'.  Returns 0 if successful, otherwise an
-  * OpenFlow error code.
-  *
-  * For OFPT_FLOW_REMOVED messages, 'flow_format' should be the current flow
-  * format at the time when the message was received.  Otherwise 'flow_format'
-  * is ignored. */
+ /* Converts an OFPT_FLOW_REMOVED or NXT_FLOW_REMOVED message 'oh' into an
+  * abstract ofputil_flow_removed in 'fr'.  Returns 0 if successful, otherwise
+  * an OpenFlow error code. */
  int
  ofputil_decode_flow_removed(struct ofputil_flow_removed *fr,
-                             const struct ofp_header *oh,
-                             enum nx_flow_format flow_format)
+                             const struct ofp_header *oh)
  {
      const struct ofputil_msg_type *type;
      enum ofputil_msg_code code;
  
          ofr = (const struct ofp_flow_removed *) oh;
          ofputil_cls_rule_from_match(&ofr->match, ntohs(ofr->priority),
-                                     flow_format, ofr->cookie, &fr->rule);
+                                     &fr->rule);
          fr->cookie = ofr->cookie;
          fr->reason = ofr->reason;
          fr->duration_sec = ntohl(ofr->duration_sec);
@@@ -1466,14 -1278,12 +1328,12 @@@ ofputil_encode_flow_removed(const struc
  {
      struct ofpbuf *msg;
  
-     if (flow_format == NXFF_OPENFLOW10
-         || flow_format == NXFF_TUN_ID_FROM_COOKIE) {
+     if (flow_format == NXFF_OPENFLOW10) {
          struct ofp_flow_removed *ofr;
  
          ofr = make_openflow_xid(sizeof *ofr, OFPT_FLOW_REMOVED, htonl(0),
                                  &msg);
-         ofputil_cls_rule_to_match(&fr->rule, flow_format, &ofr->match,
-                                   fr->cookie, &ofr->cookie);
+         ofputil_cls_rule_to_match(&fr->rule, &ofr->match);
          ofr->priority = htons(fr->rule.priority);
          ofr->reason = fr->reason;
          ofr->duration_sec = htonl(fr->duration_sec);
@@@ -1768,7 -1578,7 +1628,7 @@@ make_flow_mod(uint16_t command, const s
      ofm->header.length = htons(size);
      ofm->cookie = 0;
      ofm->priority = htons(MIN(rule->priority, UINT16_MAX));
-     ofputil_cls_rule_to_match(rule, NXFF_OPENFLOW10, &ofm->match, 0, NULL);
+     ofputil_cls_rule_to_match(rule, &ofm->match);
      ofm->command = htons(command);
      return out;
  }
@@@ -1915,6 -1725,19 +1775,6 @@@ make_echo_reply(const struct ofp_heade
      return out;
  }
  
 -/* Converts the members of 'opp' from host to network byte order. */
 -void
 -hton_ofp_phy_port(struct ofp_phy_port *opp)
 -{
 -    opp->port_no = htons(opp->port_no);
 -    opp->config = htonl(opp->config);
 -    opp->state = htonl(opp->state);
 -    opp->curr = htonl(opp->curr);
 -    opp->advertised = htonl(opp->advertised);
 -    opp->supported = htonl(opp->supported);
 -    opp->peer = htonl(opp->peer);
 -}
 -
  static int
  check_action_exact_len(const union ofp_action *a, unsigned int len,
                         unsigned int required_len)
@@@ -2099,7 -1922,7 +1959,7 @@@ check_action(const union ofp_action *a
          if (error) {
              return error;
          }
-         if (a->vlan_vid.vlan_vid & ~7) {
+         if (a->vlan_pcp.vlan_pcp & ~7) {
              return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_ARGUMENT);
          }
          return 0;
@@@ -2166,9 -1989,10 +2026,9 @@@ validate_actions(const union ofp_actio
      return 0;
  }
  
 -/* Returns true if 'action' outputs to 'port' (which must be in network byte
 - * order), false otherwise. */
 +/* Returns true if 'action' outputs to 'port', false otherwise. */
  bool
 -action_outputs_to_port(const union ofp_action *action, uint16_t port)
 +action_outputs_to_port(const union ofp_action *action, ovs_be16 port)
  {
      switch (ntohs(action->type)) {
      case OFPAT_OUTPUT:
@@@ -2212,7 -2036,7 +2072,7 @@@ normalize_match(struct ofp_match *m
      enum { OFPFW_TP = OFPFW_TP_SRC | OFPFW_TP_DST };
      uint32_t wc;
  
-     wc = ntohl(m->wildcards) & OVSFW_ALL;
+     wc = ntohl(m->wildcards) & OFPFW_ALL;
      if (wc & OFPFW_DL_TYPE) {
          m->dl_type = 0;
  
diff --combined lib/ofp-util.h
@@@ -71,11 -71,9 +71,10 @@@ enum ofputil_msg_code 
      OFPUTIL_OFPST_AGGREGATE_REPLY,
  
      /* NXT_* messages. */
-     OFPUTIL_NXT_TUN_ID_FROM_COOKIE,
      OFPUTIL_NXT_ROLE_REQUEST,
      OFPUTIL_NXT_ROLE_REPLY,
      OFPUTIL_NXT_SET_FLOW_FORMAT,
 +    OFPUTIL_NXT_FLOW_MOD_TABLE_ID,
      OFPUTIL_NXT_FLOW_MOD,
      OFPUTIL_NXT_FLOW_REMOVED,
  
@@@ -101,11 -99,8 +100,8 @@@ int ofputil_netmask_to_wcbits(ovs_be32 
  
  /* Work with OpenFlow 1.0 ofp_match. */
  void ofputil_cls_rule_from_match(const struct ofp_match *,
-                                  unsigned int priority, enum nx_flow_format,
-                                  ovs_be64 cookie, struct cls_rule *);
- void ofputil_cls_rule_to_match(const struct cls_rule *, enum nx_flow_format,
-                                struct ofp_match *,
-                                ovs_be64 cookie_in, ovs_be64 *cookie_out);
+                                  unsigned int priority, struct cls_rule *);
+ void ofputil_cls_rule_to_match(const struct cls_rule *, struct ofp_match *);
  void normalize_match(struct ofp_match *);
  char *ofp_match_to_literal_string(const struct ofp_match *match);
  
@@@ -117,20 -112,14 +113,18 @@@ ovs_be16 ofputil_dl_type_from_openflow(
  bool ofputil_flow_format_is_valid(enum nx_flow_format);
  const char *ofputil_flow_format_to_string(enum nx_flow_format);
  int ofputil_flow_format_from_string(const char *);
- enum nx_flow_format ofputil_min_flow_format(const struct cls_rule *,
-                                             bool cookie_support,
-                                             ovs_be64 cookie);
+ enum nx_flow_format ofputil_min_flow_format(const struct cls_rule *);
  
  struct ofpbuf *ofputil_make_set_flow_format(enum nx_flow_format);
  
 +/* NXT_FLOW_MOD_TABLE_ID extension. */
 +struct ofpbuf *ofputil_make_flow_mod_table_id(bool flow_mod_table_id);
 +
  /* Flow format independent flow_mod. */
  struct flow_mod {
      struct cls_rule cr;
      ovs_be64 cookie;
 +    uint8_t table_id;
      uint16_t command;
      uint16_t idle_timeout;
      uint16_t hard_timeout;
      size_t n_actions;
  };
  
 -int ofputil_decode_flow_mod(struct flow_mod *, const struct ofp_header *);
 +int ofputil_decode_flow_mod(struct flow_mod *, const struct ofp_header *,
-                             enum nx_flow_format, bool flow_mod_table_id);
++                            bool flow_mod_table_id);
  struct ofpbuf *ofputil_encode_flow_mod(const struct flow_mod *,
 -                                       enum nx_flow_format);
 +                                       enum nx_flow_format,
 +                                       bool flow_mod_table_id);
  
  /* Flow stats or aggregate stats request, independent of flow format. */
  struct flow_stats_request {
  };
  
  int ofputil_decode_flow_stats_request(struct flow_stats_request *,
-                                       const struct ofp_header *,
-                                       enum nx_flow_format);
+                                       const struct ofp_header *);
  struct ofpbuf *ofputil_encode_flow_stats_request(
      const struct flow_stats_request *, enum nx_flow_format);
  
@@@ -177,8 -163,7 +170,7 @@@ struct ofputil_flow_stats 
  };
  
  int ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *,
-                                     struct ofpbuf *msg,
-                                     enum nx_flow_format);
+                                     struct ofpbuf *msg);
  
  /* Flow removed message, independent of flow format. */
  struct ofputil_flow_removed {
  };
  
  int ofputil_decode_flow_removed(struct ofputil_flow_removed *,
-                                 const struct ofp_header *,
-                                 enum nx_flow_format);
+                                 const struct ofp_header *);
  struct ofpbuf *ofputil_encode_flow_removed(const struct ofputil_flow_removed *,
                                             enum nx_flow_format);
  
@@@ -262,6 -246,8 +253,6 @@@ struct ofpbuf *make_unbuffered_packet_o
                                            uint16_t in_port, uint16_t out_port);
  struct ofpbuf *make_echo_request(void);
  struct ofpbuf *make_echo_reply(const struct ofp_header *rq);
 -
 -void hton_ofp_phy_port(struct ofp_phy_port *);
  \f
  /* Actions. */
  
@@@ -277,7 -263,7 +268,7 @@@ const union ofp_action *actions_next(st
  
  int validate_actions(const union ofp_action *, size_t n_actions,
                       const struct flow *, int max_ports);
 -bool action_outputs_to_port(const union ofp_action *, uint16_t port);
 +bool action_outputs_to_port(const union ofp_action *, ovs_be16 port);
  
  int ofputil_pull_actions(struct ofpbuf *, unsigned int actions_len,
                           union ofp_action **, size_t *);
diff --combined ofproto/connmgr.c
@@@ -22,6 -22,7 +22,6 @@@
  #include <stdlib.h>
  
  #include "coverage.h"
 -#include "dpif.h"
  #include "fail-open.h"
  #include "in-band.h"
  #include "odp-util.h"
@@@ -30,7 -31,6 +30,7 @@@
  #include "pinsched.h"
  #include "poll-loop.h"
  #include "pktbuf.h"
 +#include "private.h"
  #include "rconn.h"
  #include "shash.h"
  #include "timeval.h"
@@@ -49,7 -49,6 +49,7 @@@ struct ofconn 
      struct rconn *rconn;        /* OpenFlow connection. */
      enum ofconn_type type;      /* Type. */
      enum nx_flow_format flow_format; /* Currently selected flow format. */
 +    bool flow_mod_table_id;     /* NXT_FLOW_MOD_TABLE_ID enabled? */
  
      /* OFPT_PACKET_IN related data. */
      struct rconn_packet_counter *packet_in_counter; /* # queued on 'rconn'. */
@@@ -391,11 -390,9 +391,10 @@@ connmgr_set_controllers(struct connmgr 
                          const struct ofproto_controller *controllers,
                          size_t n_controllers)
  {
 +    bool had_controllers = connmgr_has_controllers(mgr);
      struct shash new_controllers;
      struct ofconn *ofconn, *next_ofconn;
      struct ofservice *ofservice, *next_ofservice;
-     bool ss_exists;
      size_t i;
  
      /* Create newly configured controllers and services.
  
      /* Delete controllers that are no longer configured.
       * Update configuration of all now-existing controllers. */
-     ss_exists = false;
      HMAP_FOR_EACH_SAFE (ofconn, next_ofconn, hmap_node, &mgr->controllers) {
          struct ofproto_controller *c;
  
  
      update_in_band_remotes(mgr);
      update_fail_open(mgr);
 +    if (had_controllers != connmgr_has_controllers(mgr)) {
 +        ofproto_flush_flows(mgr->ofproto);
 +    }
  }
  
  /* Drops the connections between 'mgr' and all of its primary and secondary
@@@ -725,24 -718,6 +723,24 @@@ ofconn_set_flow_format(struct ofconn *o
      ofconn->flow_format = flow_format;
  }
  
 +/* Returns true if the NXT_FLOW_MOD_TABLE_ID extension is enabled, false
 + * otherwise.
 + *
 + * By default the extension is not enabled. */
 +bool
 +ofconn_get_flow_mod_table_id(const struct ofconn *ofconn)
 +{
 +    return ofconn->flow_mod_table_id;
 +}
 +
 +/* Enables or disables (according to 'enable') the NXT_FLOW_MOD_TABLE_ID
 + * extension on 'ofconn'. */
 +void
 +ofconn_set_flow_mod_table_id(struct ofconn *ofconn, bool enable)
 +{
 +    ofconn->flow_mod_table_id = enable;
 +}
 +
  /* Returns the default miss send length for 'ofconn'. */
  int
  ofconn_get_miss_send_len(const struct ofconn *ofconn)
@@@ -792,7 -767,6 +790,7 @@@ ofconn_create(struct connmgr *mgr, stru
      ofconn->rconn = rconn;
      ofconn->type = type;
      ofconn->flow_format = NXFF_OPENFLOW10;
 +    ofconn->flow_mod_table_id = false;
      ofconn->role = NX_ROLE_OTHER;
      ofconn->packet_in_counter = rconn_packet_counter_create ();
      ofconn->pktbuf = NULL;
@@@ -945,11 -919,13 +943,11 @@@ ofconn_send(const struct ofconn *ofconn
  \f
  /* Sending asynchronous messages. */
  
 -static void schedule_packet_in(struct ofconn *, const struct dpif_upcall *,
 +static void schedule_packet_in(struct ofconn *, struct ofputil_packet_in,
                                 const struct flow *, struct ofpbuf *rw_packet);
  
  /* Sends an OFPT_PORT_STATUS message with 'opp' and 'reason' to appropriate
 - * controllers managed by 'mgr'.
 - *
 - * 'opp' is in *HOST* byte order. */
 + * controllers managed by 'mgr'. */
  void
  connmgr_send_port_status(struct connmgr *mgr, const struct ofp_phy_port *opp,
                           uint8_t reason)
          ops = make_openflow_xid(sizeof *ops, OFPT_PORT_STATUS, 0, &b);
          ops->reason = reason;
          ops->desc = *opp;
 -        hton_ofp_phy_port(&ops->desc);
          ofconn_send(ofconn, b, NULL);
      }
  }
@@@ -1000,15 -977,15 +998,15 @@@ connmgr_send_flow_removed(struct connmg
      }
  }
  
 -/* Given 'upcall', of type DPIF_UC_ACTION or DPIF_UC_MISS, sends an
 - * OFPT_PACKET_IN message to each OpenFlow controller as necessary according to
 - * their individual configurations.
 +/* Given 'pin', sends an OFPT_PACKET_IN message to each OpenFlow controller as
 + * necessary according to their individual configurations.
   *
   * 'rw_packet' may be NULL.  Otherwise, 'rw_packet' must contain the same data
 - * as upcall->packet.  (rw_packet == upcall->packet is also valid.)  Ownership
 - * of 'rw_packet' is transferred to this function. */
 + * as pin->packet.  (rw_packet == pin->packet is also valid.)  Ownership of
 + * 'rw_packet' is transferred to this function. */
  void
 -connmgr_send_packet_in(struct connmgr *mgr, const struct dpif_upcall *upcall,
 +connmgr_send_packet_in(struct connmgr *mgr,
 +                       const struct ofputil_packet_in *pin,
                         const struct flow *flow, struct ofpbuf *rw_packet)
  {
      struct ofconn *ofconn, *prev;
      LIST_FOR_EACH (ofconn, node, &mgr->all_conns) {
          if (ofconn_receives_async_msgs(ofconn)) {
              if (prev) {
 -                schedule_packet_in(prev, upcall, flow, NULL);
 +                schedule_packet_in(prev, *pin, flow, NULL);
              }
              prev = ofconn;
          }
      }
      if (prev) {
 -        schedule_packet_in(prev, upcall, flow, rw_packet);
 +        schedule_packet_in(prev, *pin, flow, rw_packet);
      } else {
          ofpbuf_delete(rw_packet);
      }
@@@ -1039,45 -1016,50 +1037,45 @@@ do_send_packet_in(struct ofpbuf *ofp_pa
                            ofconn->packet_in_counter, 100);
  }
  
 -/* Takes 'upcall', whose packet has the flow specified by 'flow', composes an
 +/* Takes 'pin', whose packet has the flow specified by 'flow', composes an
   * OpenFlow packet-in message from it, and passes it to 'ofconn''s packet
   * scheduler for sending.
   *
   * 'rw_packet' may be NULL.  Otherwise, 'rw_packet' must contain the same data
 - * as upcall->packet.  (rw_packet == upcall->packet is also valid.)  Ownership
 - * of 'rw_packet' is transferred to this function. */
 + * as pin->packet.  (rw_packet == pin->packet is also valid.)  Ownership of
 + * 'rw_packet' is transferred to this function. */
  static void
 -schedule_packet_in(struct ofconn *ofconn, const struct dpif_upcall *upcall,
 +schedule_packet_in(struct ofconn *ofconn, struct ofputil_packet_in pin,
                     const struct flow *flow, struct ofpbuf *rw_packet)
  {
      struct connmgr *mgr = ofconn->connmgr;
 -    struct ofputil_packet_in pin;
 -
 -    /* Figure out the easy parts. */
 -    pin.packet = upcall->packet;
 -    pin.in_port = odp_port_to_ofp_port(flow->in_port);
 -    pin.reason = upcall->type == DPIF_UC_MISS ? OFPR_NO_MATCH : OFPR_ACTION;
  
      /* Get OpenFlow buffer_id. */
 -    if (upcall->type == DPIF_UC_ACTION) {
 +    if (pin.reason == OFPR_ACTION) {
          pin.buffer_id = UINT32_MAX;
      } else if (mgr->fail_open && fail_open_is_active(mgr->fail_open)) {
          pin.buffer_id = pktbuf_get_null();
      } else if (!ofconn->pktbuf) {
          pin.buffer_id = UINT32_MAX;
      } else {
 -        pin.buffer_id = pktbuf_save(ofconn->pktbuf, upcall->packet,
 -                                    flow->in_port);
 +        pin.buffer_id = pktbuf_save(ofconn->pktbuf, pin.packet, flow->in_port);
      }
  
      /* Figure out how much of the packet to send. */
 -    pin.send_len = upcall->packet->size;
 +    if (pin.reason == OFPR_NO_MATCH) {
 +        pin.send_len = pin.packet->size;
 +    } else {
 +        /* Caller should have initialized 'send_len' to 'max_len' specified in
 +         * struct ofp_action_output. */
 +    }
      if (pin.buffer_id != UINT32_MAX) {
          pin.send_len = MIN(pin.send_len, ofconn->miss_send_len);
      }
 -    if (upcall->type == DPIF_UC_ACTION) {
 -        pin.send_len = MIN(pin.send_len, upcall->userdata);
 -    }
  
      /* Make OFPT_PACKET_IN and hand over to packet scheduler.  It might
       * immediately call into do_send_packet_in() or it might buffer it for a
       * while (until a later call to pinsched_run()). */
 -    pinsched_send(ofconn->schedulers[upcall->type == DPIF_UC_MISS ? 0 : 1],
 +    pinsched_send(ofconn->schedulers[pin.reason == OFPR_NO_MATCH ? 0 : 1],
                    flow->in_port, ofputil_encode_packet_in(&pin, rw_packet),
                    do_send_packet_in, ofconn);
  }
@@@ -1097,13 -1079,8 +1095,13 @@@ connmgr_get_fail_mode(const struct conn
  void
  connmgr_set_fail_mode(struct connmgr *mgr, enum ofproto_fail_mode fail_mode)
  {
 -    mgr->fail_mode = fail_mode;
 -    update_fail_open(mgr);
 +    if (mgr->fail_mode != fail_mode) {
 +        mgr->fail_mode = fail_mode;
 +        update_fail_open(mgr);
 +        if (!connmgr_has_controllers(mgr)) {
 +            ofproto_flush_flows(mgr->ofproto);
 +        }
 +    }
  }
  \f
  /* Fail-open implementation. */
@@@ -1286,23 -1263,6 +1284,23 @@@ connmgr_flushed(struct connmgr *mgr
      if (mgr->fail_open) {
          fail_open_flushed(mgr->fail_open);
      }
 +
 +    /* If there are no controllers and we're 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 (!connmgr_has_controllers(mgr)
 +        && mgr->fail_mode == OFPROTO_FAIL_STANDALONE) {
 +        union ofp_action action;
 +        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);
 +        cls_rule_init_catchall(&rule, 0);
 +        ofproto_add_flow(mgr->ofproto, &rule, &action, 1);
 +    }
  }
  \f
  /* Creates a new ofservice for 'target' in 'mgr'.  Returns 0 if successful,
diff --combined ofproto/in-band.c
  #include "openflow/openflow.h"
  #include "packets.h"
  #include "poll-loop.h"
 +#include "private.h"
  #include "timeval.h"
  #include "vlog.h"
  
  VLOG_DEFINE_THIS_MODULE(in_band);
  
- /* In-band control allows a single network to be used for OpenFlow traffic and
-  * other data traffic.  See ovs-vswitchd.conf.db(5) for a description of
-  * configuring in-band control.
-  *
-  * This comment is an attempt to describe how in-band control works at a
-  * wire- and implementation-level.  Correctly implementing in-band
-  * control has proven difficult due to its many subtleties, and has thus
-  * gone through many iterations.  Please read through and understand the
-  * reasoning behind the chosen rules before making modifications.
-  *
-  * In Open vSwitch, in-band control is implemented as "hidden" flows (in that
-  * they are not visible through OpenFlow) and at a higher priority than
-  * wildcarded flows can be set up by through OpenFlow.  This is done so that
-  * the OpenFlow controller cannot interfere with them and possibly break
-  * connectivity with its switches.  It is possible to see all flows, including
-  * in-band ones, with the ovs-appctl "bridge/dump-flows" command.
-  *
-  * The Open vSwitch implementation of in-band control can hide traffic to
-  * arbitrary "remotes", where each remote is one TCP port on one IP address.
-  * Currently the remotes are automatically configured as the in-band OpenFlow
-  * controllers plus the OVSDB managers, if any.  (The latter is a requirement
-  * because OVSDB managers are responsible for configuring OpenFlow controllers,
-  * so if the manager cannot be reached then OpenFlow cannot be reconfigured.)
-  *
-  * The following rules (with the OFPP_NORMAL action) are set up on any bridge
-  * that has any remotes:
-  *
-  *    (a) DHCP requests sent from the local port.
-  *    (b) ARP replies to the local port's MAC address.
-  *    (c) ARP requests from the local port's MAC address.
-  *
-  * In-band also sets up the following rules for each unique next-hop MAC
-  * address for the remotes' IPs (the "next hop" is either the remote
-  * itself, if it is on a local subnet, or the gateway to reach the remote):
-  *
-  *    (d) ARP replies to the next hop's MAC address.
-  *    (e) ARP requests from the next hop's MAC address.
-  *
-  * In-band also sets up the following rules for each unique remote IP address:
-  *
-  *    (f) ARP replies containing the remote's IP address as a target.
-  *    (g) ARP requests containing the remote's IP address as a source.
-  *
-  * In-band also sets up the following rules for each unique remote (IP,port)
-  * pair:
-  *
-  *    (h) TCP traffic to the remote's IP and port.
-  *    (i) TCP traffic from the remote's IP and port.
-  *
-  * The goal of these rules is to be as narrow as possible to allow a
-  * switch to join a network and be able to communicate with the
-  * remotes.  As mentioned earlier, these rules have higher priority
-  * than the controller's rules, so if they are too broad, they may
-  * prevent the controller from implementing its policy.  As such,
-  * in-band actively monitors some aspects of flow and packet processing
-  * so that the rules can be made more precise.
-  *
-  * In-band control monitors attempts to add flows into the datapath that
-  * could interfere with its duties.  The datapath only allows exact
-  * match entries, so in-band control is able to be very precise about
-  * the flows it prevents.  Flows that miss in the datapath are sent to
-  * userspace to be processed, so preventing these flows from being
-  * cached in the "fast path" does not affect correctness.  The only type
-  * of flow that is currently prevented is one that would prevent DHCP
-  * replies from being seen by the local port.  For example, a rule that
-  * forwarded all DHCP traffic to the controller would not be allowed,
-  * but one that forwarded to all ports (including the local port) would.
-  *
-  * As mentioned earlier, packets that miss in the datapath are sent to
-  * the userspace for processing.  The userspace has its own flow table,
-  * the "classifier", so in-band checks whether any special processing
-  * is needed before the classifier is consulted.  If a packet is a DHCP
-  * response to a request from the local port, the packet is forwarded to
-  * the local port, regardless of the flow table.  Note that this requires
-  * L7 processing of DHCP replies to determine whether the 'chaddr' field
-  * matches the MAC address of the local port.
-  *
-  * It is interesting to note that for an L3-based in-band control
-  * mechanism, the majority of rules are devoted to ARP traffic.  At first
-  * glance, some of these rules appear redundant.  However, each serves an
-  * important role.  First, in order to determine the MAC address of the
-  * remote side (controller or gateway) for other ARP rules, we must allow
-  * ARP traffic for our local port with rules (b) and (c).  If we are
-  * between a switch and its connection to the remote, we have to
-  * allow the other switch's ARP traffic to through.  This is done with
-  * rules (d) and (e), since we do not know the addresses of the other
-  * switches a priori, but do know the remote's or gateway's.  Finally,
-  * if the remote is running in a local guest VM that is not reached
-  * through the local port, the switch that is connected to the VM must
-  * allow ARP traffic based on the remote's IP address, since it will
-  * not know the MAC address of the local port that is sending the traffic
-  * or the MAC address of the remote in the guest VM.
-  *
-  * With a few notable exceptions below, in-band should work in most
-  * network setups.  The following are considered "supported' in the
-  * current implementation:
-  *
-  *    - Locally Connected.  The switch and remote are on the same
-  *      subnet.  This uses rules (a), (b), (c), (h), and (i).
-  *
-  *    - Reached through Gateway.  The switch and remote are on
-  *      different subnets and must go through a gateway.  This uses
-  *      rules (a), (b), (c), (h), and (i).
-  *
-  *    - Between Switch and Remote.  This switch is between another
-  *      switch and the remote, and we want to allow the other
-  *      switch's traffic through.  This uses rules (d), (e), (h), and
-  *      (i).  It uses (b) and (c) indirectly in order to know the MAC
-  *      address for rules (d) and (e).  Note that DHCP for the other
-  *      switch will not work unless an OpenFlow controller explicitly lets this
-  *      switch pass the traffic.
-  *
-  *    - Between Switch and Gateway.  This switch is between another
-  *      switch and the gateway, and we want to allow the other switch's
-  *      traffic through.  This uses the same rules and logic as the
-  *      "Between Switch and Remote" configuration described earlier.
-  *
-  *    - Remote on Local VM.  The remote is a guest VM on the
-  *      system running in-band control.  This uses rules (a), (b), (c),
-  *      (h), and (i).
-  *
-  *    - Remote on Local VM with Different Networks.  The remote
-  *      is a guest VM on the system running in-band control, but the
-  *      local port is not used to connect to the remote.  For
-  *      example, an IP address is configured on eth0 of the switch.  The
-  *      remote's VM is connected through eth1 of the switch, but an
-  *      IP address has not been configured for that port on the switch.
-  *      As such, the switch will use eth0 to connect to the remote,
-  *      and eth1's rules about the local port will not work.  In the
-  *      example, the switch attached to eth0 would use rules (a), (b),
-  *      (c), (h), and (i) on eth0.  The switch attached to eth1 would use
-  *      rules (f), (g), (h), and (i).
-  *
-  * The following are explicitly *not* supported by in-band control:
-  *
-  *    - Specify Remote by Name.  Currently, the remote must be
-  *      identified by IP address.  A naive approach would be to permit
-  *      all DNS traffic.  Unfortunately, this would prevent the
-  *      controller from defining any policy over DNS.  Since switches
-  *      that are located behind us need to connect to the remote,
-  *      in-band cannot simply add a rule that allows DNS traffic from
-  *      the local port.  The "correct" way to support this is to parse
-  *      DNS requests to allow all traffic related to a request for the
-  *      remote's name through.  Due to the potential security
-  *      problems and amount of processing, we decided to hold off for
-  *      the time-being.
-  *
-  *    - Differing Remotes for Switches.  All switches must know
-  *      the L3 addresses for all the remotes that other switches
-  *      may use, since rules need to be set up to allow traffic related
-  *      to those remotes through.  See rules (f), (g), (h), and (i).
-  *
-  *    - Differing Routes for Switches.  In order for the switch to
-  *      allow other switches to connect to a remote through a
-  *      gateway, it allows the gateway's traffic through with rules (d)
-  *      and (e).  If the routes to the remote differ for the two
-  *      switches, we will not know the MAC address of the alternate
-  *      gateway.
-  */
  /* Priorities used in classifier for in-band rules.  These values are higher
   * than any that may be set with OpenFlow, and "18" kind of looks like "IB".
   * The ordering of priorities is not important because all of the rules set up
diff --combined ofproto/ofproto-dpif.c
index ea0e9e4,0000000..93acd42
mode 100644,000000..100644
--- /dev/null
@@@ -1,3932 -1,0 +1,3934 @@@
-                 struct lacp_slave_settings *lacp)
 +/*
 + * Copyright (c) 2009, 2010, 2011 Nicira Networks.
 + *
 + * Licensed under the Apache License, Version 2.0 (the "License");
 + * you may not use this file except in compliance with the License.
 + * You may obtain a copy of the License at:
 + *
 + *     http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing, software
 + * distributed under the License is distributed on an "AS IS" BASIS,
 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 + * See the License for the specific language governing permissions and
 + * limitations under the License.
 + */
 +
 +#include <config.h>
 +
 +#include "ofproto/private.h"
 +
 +#include <errno.h>
 +
 +#include "autopath.h"
 +#include "bond.h"
 +#include "byte-order.h"
 +#include "connmgr.h"
 +#include "coverage.h"
 +#include "cfm.h"
 +#include "dpif.h"
 +#include "dynamic-string.h"
 +#include "fail-open.h"
 +#include "hmapx.h"
 +#include "lacp.h"
 +#include "mac-learning.h"
 +#include "multipath.h"
 +#include "netdev.h"
 +#include "netlink.h"
 +#include "nx-match.h"
 +#include "odp-util.h"
 +#include "ofp-util.h"
 +#include "ofpbuf.h"
 +#include "ofp-print.h"
 +#include "ofproto-sflow.h"
 +#include "poll-loop.h"
 +#include "timer.h"
 +#include "unaligned.h"
 +#include "unixctl.h"
 +#include "vlan-bitmap.h"
 +#include "vlog.h"
 +
 +VLOG_DEFINE_THIS_MODULE(ofproto_dpif);
 +
 +COVERAGE_DEFINE(ofproto_dpif_ctlr_action);
 +COVERAGE_DEFINE(ofproto_dpif_expired);
 +COVERAGE_DEFINE(ofproto_dpif_no_packet_in);
 +COVERAGE_DEFINE(ofproto_dpif_xlate);
 +COVERAGE_DEFINE(facet_changed_rule);
 +COVERAGE_DEFINE(facet_invalidated);
 +COVERAGE_DEFINE(facet_revalidate);
 +COVERAGE_DEFINE(facet_unexpected);
 +
 +/* Maximum depth of flow table recursion (due to NXAST_RESUBMIT actions) in a
 + * flow translation. */
 +#define MAX_RESUBMIT_RECURSION 16
 +
 +struct ofport_dpif;
 +struct ofproto_dpif;
 +
 +struct rule_dpif {
 +    struct rule up;
 +
 +    long long int used;         /* Time last used; time created if not used. */
 +
 +    /* These statistics:
 +     *
 +     *   - Do include packets and bytes from facets that have been deleted or
 +     *     whose own statistics have been folded into the rule.
 +     *
 +     *   - Do include packets and bytes sent "by hand" that were accounted to
 +     *     the rule without any facet being involved (this is a rare corner
 +     *     case in rule_execute()).
 +     *
 +     *   - Do not include packet or bytes that can be obtained from any facet's
 +     *     packet_count or byte_count member or that can be obtained from the
 +     *     datapath by, e.g., dpif_flow_get() for any facet.
 +     */
 +    uint64_t packet_count;       /* Number of packets received. */
 +    uint64_t byte_count;         /* Number of bytes received. */
 +
 +    struct list facets;          /* List of "struct facet"s. */
 +};
 +
 +static struct rule_dpif *rule_dpif_cast(const struct rule *rule)
 +{
 +    return rule ? CONTAINER_OF(rule, struct rule_dpif, up) : NULL;
 +}
 +
 +static struct rule_dpif *rule_dpif_lookup(struct ofproto_dpif *ofproto,
 +                                          const struct flow *flow);
 +
 +#define MAX_MIRRORS 32
 +typedef uint32_t mirror_mask_t;
 +#define MIRROR_MASK_C(X) UINT32_C(X)
 +BUILD_ASSERT_DECL(sizeof(mirror_mask_t) * CHAR_BIT >= MAX_MIRRORS);
 +struct ofmirror {
 +    struct ofproto_dpif *ofproto; /* Owning ofproto. */
 +    size_t idx;                 /* In ofproto's "mirrors" array. */
 +    void *aux;                  /* Key supplied by ofproto's client. */
 +    char *name;                 /* Identifier for log messages. */
 +
 +    /* Selection criteria. */
 +    struct hmapx srcs;          /* Contains "struct ofbundle *"s. */
 +    struct hmapx dsts;          /* Contains "struct ofbundle *"s. */
 +    unsigned long *vlans;       /* Bitmap of chosen VLANs, NULL selects all. */
 +
 +    /* Output (mutually exclusive). */
 +    struct ofbundle *out;       /* Output port or NULL. */
 +    int out_vlan;               /* Output VLAN or -1. */
 +};
 +
 +static void mirror_destroy(struct ofmirror *);
 +
 +/* A group of one or more OpenFlow ports. */
 +#define OFBUNDLE_FLOOD ((struct ofbundle *) 1)
 +struct ofbundle {
 +    struct ofproto_dpif *ofproto; /* Owning ofproto. */
 +    struct hmap_node hmap_node; /* In struct ofproto's "bundles" hmap. */
 +    void *aux;                  /* Key supplied by ofproto's client. */
 +    char *name;                 /* Identifier for log messages. */
 +
 +    /* Configuration. */
 +    struct list ports;          /* Contains "struct ofport"s. */
 +    int vlan;                   /* -1=trunk port, else a 12-bit VLAN ID. */
 +    unsigned long *trunks;      /* Bitmap of trunked VLANs, if 'vlan' == -1.
 +                                 * NULL if all VLANs are trunked. */
 +    struct lacp *lacp;          /* LACP if LACP is enabled, otherwise NULL. */
 +    struct bond *bond;          /* Nonnull iff more than one port. */
 +
 +    /* Status. */
 +    bool floodable;             /* True if no port has OFPPC_NO_FLOOD set. */
 +
 +    /* Port mirroring info. */
 +    mirror_mask_t src_mirrors;  /* Mirrors triggered when packet received. */
 +    mirror_mask_t dst_mirrors;  /* Mirrors triggered when packet sent. */
 +    mirror_mask_t mirror_out;   /* Mirrors that output to this bundle. */
 +};
 +
 +static void bundle_remove(struct ofport *);
 +static void bundle_destroy(struct ofbundle *);
 +static void bundle_del_port(struct ofport_dpif *);
 +static void bundle_run(struct ofbundle *);
 +static void bundle_wait(struct ofbundle *);
 +
 +struct action_xlate_ctx {
 +/* action_xlate_ctx_init() initializes these members. */
 +
 +    /* The ofproto. */
 +    struct ofproto_dpif *ofproto;
 +
 +    /* Flow to which the OpenFlow actions apply.  xlate_actions() will modify
 +     * this flow when actions change header fields. */
 +    struct flow flow;
 +
 +    /* The packet corresponding to 'flow', or a null pointer if we are
 +     * revalidating without a packet to refer to. */
 +    const struct ofpbuf *packet;
 +
 +    /* If nonnull, called just before executing a resubmit action.
 +     *
 +     * This is normally null so the client has to set it manually after
 +     * calling action_xlate_ctx_init(). */
 +    void (*resubmit_hook)(struct action_xlate_ctx *, struct rule_dpif *);
 +
 +/* xlate_actions() initializes and uses these members.  The client might want
 + * to look at them after it returns. */
 +
 +    struct ofpbuf *odp_actions; /* Datapath actions. */
 +    tag_type tags;              /* Tags associated with OFPP_NORMAL actions. */
 +    bool may_set_up_flow;       /* True ordinarily; false if the actions must
 +                                 * be reassessed for every packet. */
 +    uint16_t nf_output_iface;   /* Output interface index for NetFlow. */
 +
 +/* xlate_actions() initializes and uses these members, but the client has no
 + * reason to look at them. */
 +
 +    int recurse;                /* Recursion level, via xlate_table_action. */
 +    int last_pop_priority;      /* Offset in 'odp_actions' just past most
 +                                 * recent ODP_ACTION_ATTR_SET_PRIORITY. */
 +};
 +
 +static void action_xlate_ctx_init(struct action_xlate_ctx *,
 +                                  struct ofproto_dpif *, const struct flow *,
 +                                  const struct ofpbuf *);
 +static struct ofpbuf *xlate_actions(struct action_xlate_ctx *,
 +                                    const union ofp_action *in, size_t n_in);
 +
 +/* An exact-match instantiation of an OpenFlow flow. */
 +struct facet {
 +    long long int used;         /* Time last used; time created if not used. */
 +
 +    /* These statistics:
 +     *
 +     *   - Do include packets and bytes sent "by hand", e.g. with
 +     *     dpif_execute().
 +     *
 +     *   - Do include packets and bytes that were obtained from the datapath
 +     *     when a flow was deleted (e.g. dpif_flow_del()) or when its
 +     *     statistics were reset (e.g. dpif_flow_put() with
 +     *     DPIF_FP_ZERO_STATS).
 +     *
 +     *   - Do not include any packets or bytes that can currently be obtained
 +     *     from the datapath by, e.g., dpif_flow_get().
 +     */
 +    uint64_t packet_count;       /* Number of packets received. */
 +    uint64_t byte_count;         /* Number of bytes received. */
 +
 +    uint64_t dp_packet_count;    /* Last known packet count in the datapath. */
 +    uint64_t dp_byte_count;      /* Last known byte count in the datapath. */
 +
 +    uint64_t rs_packet_count;    /* Packets pushed to resubmit children. */
 +    uint64_t rs_byte_count;      /* Bytes pushed to resubmit children. */
 +    long long int rs_used;       /* Used time pushed to resubmit children. */
 +
 +    /* Number of bytes passed to account_cb.  This may include bytes that can
 +     * currently obtained from the datapath (thus, it can be greater than
 +     * byte_count). */
 +    uint64_t accounted_bytes;
 +
 +    struct hmap_node hmap_node;  /* In owning ofproto's 'facets' hmap. */
 +    struct list list_node;       /* In owning rule's 'facets' list. */
 +    struct rule_dpif *rule;      /* Owning rule. */
 +    struct flow flow;            /* Exact-match flow. */
 +    bool installed;              /* Installed in datapath? */
 +    bool may_install;            /* True ordinarily; false if actions must
 +                                  * be reassessed for every packet. */
 +    size_t actions_len;          /* Number of bytes in actions[]. */
 +    struct nlattr *actions;      /* Datapath actions. */
 +    tag_type tags;               /* Tags. */
 +    struct netflow_flow nf_flow; /* Per-flow NetFlow tracking data. */
 +};
 +
 +static struct facet *facet_create(struct rule_dpif *, const struct flow *,
 +                                  const struct ofpbuf *packet);
 +static void facet_remove(struct ofproto_dpif *, struct facet *);
 +static void facet_free(struct facet *);
 +
 +static struct facet *facet_find(struct ofproto_dpif *, const struct flow *);
 +static struct facet *facet_lookup_valid(struct ofproto_dpif *,
 +                                        const struct flow *);
 +static bool facet_revalidate(struct ofproto_dpif *, struct facet *);
 +
 +static void facet_execute(struct ofproto_dpif *, struct facet *,
 +                          struct ofpbuf *packet);
 +
 +static int facet_put__(struct ofproto_dpif *, struct facet *,
 +                       const struct nlattr *actions, size_t actions_len,
 +                       struct dpif_flow_stats *);
 +static void facet_install(struct ofproto_dpif *, struct facet *,
 +                          bool zero_stats);
 +static void facet_uninstall(struct ofproto_dpif *, struct facet *);
 +static void facet_flush_stats(struct ofproto_dpif *, struct facet *);
 +
 +static void facet_make_actions(struct ofproto_dpif *, struct facet *,
 +                               const struct ofpbuf *packet);
 +static void facet_update_time(struct ofproto_dpif *, struct facet *,
 +                              long long int used);
 +static void facet_update_stats(struct ofproto_dpif *, struct facet *,
 +                               const struct dpif_flow_stats *);
 +static void facet_push_stats(struct facet *);
 +static void facet_account(struct ofproto_dpif *, struct facet *,
 +                          uint64_t extra_bytes);
 +
 +static bool facet_is_controller_flow(struct facet *);
 +
 +static void flow_push_stats(const struct rule_dpif *,
 +                            struct flow *, uint64_t packets, uint64_t bytes,
 +                            long long int used);
 +
 +struct ofport_dpif {
 +    struct ofport up;
 +
 +    uint32_t odp_port;
 +    struct ofbundle *bundle;    /* Bundle that contains this port, if any. */
 +    struct list bundle_node;    /* In struct ofbundle's "ports" list. */
 +    struct cfm *cfm;            /* Connectivity Fault Management, if any. */
 +    tag_type tag;               /* Tag associated with this port. */
++    uint32_t bond_stable_id;    /* stable_id to use as bond slave, or 0. */
 +};
 +
 +static struct ofport_dpif *
 +ofport_dpif_cast(const struct ofport *ofport)
 +{
 +    assert(ofport->ofproto->ofproto_class == &ofproto_dpif_class);
 +    return ofport ? CONTAINER_OF(ofport, struct ofport_dpif, up) : NULL;
 +}
 +
 +static void port_run(struct ofport_dpif *);
 +static void port_wait(struct ofport_dpif *);
 +static int set_cfm(struct ofport *, const struct cfm *,
 +                   const uint16_t *remote_mps, size_t n_remote_mps);
 +
 +struct ofproto_dpif {
 +    struct ofproto up;
 +    struct dpif *dpif;
 +    int max_ports;
 +
 +    /* Statistics. */
 +    uint64_t n_matches;
 +
 +    /* Bridging. */
 +    struct netflow *netflow;
 +    struct ofproto_sflow *sflow;
 +    struct hmap bundles;        /* Contains "struct ofbundle"s. */
 +    struct mac_learning *ml;
 +    struct ofmirror *mirrors[MAX_MIRRORS];
 +    bool has_bonded_bundles;
 +
 +    /* Expiration. */
 +    struct timer next_expiration;
 +
 +    /* Facets. */
 +    struct hmap facets;
 +    bool need_revalidate;
 +    struct tag_set revalidate_set;
 +};
 +
 +static void ofproto_dpif_unixctl_init(void);
 +
 +static struct ofproto_dpif *
 +ofproto_dpif_cast(const struct ofproto *ofproto)
 +{
 +    assert(ofproto->ofproto_class == &ofproto_dpif_class);
 +    return CONTAINER_OF(ofproto, struct ofproto_dpif, up);
 +}
 +
 +static struct ofport_dpif *get_ofp_port(struct ofproto_dpif *,
 +                                        uint16_t ofp_port);
 +static struct ofport_dpif *get_odp_port(struct ofproto_dpif *,
 +                                        uint32_t odp_port);
 +
 +/* Packet processing. */
 +static void update_learning_table(struct ofproto_dpif *,
 +                                  const struct flow *, int vlan,
 +                                  struct ofbundle *);
 +static bool is_admissible(struct ofproto_dpif *, const struct flow *,
 +                          bool have_packet, tag_type *, int *vlanp,
 +                          struct ofbundle **in_bundlep);
 +static void handle_upcall(struct ofproto_dpif *, struct dpif_upcall *);
 +
 +/* Flow expiration. */
 +static int expire(struct ofproto_dpif *);
 +
 +/* Utilities. */
 +static int send_packet(struct ofproto_dpif *,
 +                       uint32_t odp_port, uint16_t vlan_tci,
 +                       const struct ofpbuf *packet);
 +
 +/* Global variables. */
 +static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
 +\f
 +/* Factory functions. */
 +
 +static void
 +enumerate_types(struct sset *types)
 +{
 +    dp_enumerate_types(types);
 +}
 +
 +static int
 +enumerate_names(const char *type, struct sset *names)
 +{
 +    return dp_enumerate_names(type, names);
 +}
 +
 +static int
 +del(const char *type, const char *name)
 +{
 +    struct dpif *dpif;
 +    int error;
 +
 +    error = dpif_open(name, type, &dpif);
 +    if (!error) {
 +        error = dpif_delete(dpif);
 +        dpif_close(dpif);
 +    }
 +    return error;
 +}
 +\f
 +/* Basic life-cycle. */
 +
 +static struct ofproto *
 +alloc(void)
 +{
 +    struct ofproto_dpif *ofproto = xmalloc(sizeof *ofproto);
 +    return &ofproto->up;
 +}
 +
 +static void
 +dealloc(struct ofproto *ofproto_)
 +{
 +    struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
 +    free(ofproto);
 +}
 +
 +static int
 +construct(struct ofproto *ofproto_)
 +{
 +    struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
 +    const char *name = ofproto->up.name;
 +    int error;
 +    int i;
 +
 +    error = dpif_create_and_open(name, ofproto->up.type, &ofproto->dpif);
 +    if (error) {
 +        VLOG_ERR("failed to open datapath %s: %s", name, strerror(error));
 +        return error;
 +    }
 +
 +    ofproto->max_ports = dpif_get_max_ports(ofproto->dpif);
 +    ofproto->n_matches = 0;
 +
 +    error = dpif_recv_set_mask(ofproto->dpif,
 +                               ((1u << DPIF_UC_MISS) |
 +                                (1u << DPIF_UC_ACTION) |
 +                                (1u << DPIF_UC_SAMPLE)));
 +    if (error) {
 +        VLOG_ERR("failed to listen on datapath %s: %s", name, strerror(error));
 +        dpif_close(ofproto->dpif);
 +        return error;
 +    }
 +    dpif_flow_flush(ofproto->dpif);
 +    dpif_recv_purge(ofproto->dpif);
 +
 +    ofproto->netflow = NULL;
 +    ofproto->sflow = NULL;
 +    hmap_init(&ofproto->bundles);
 +    ofproto->ml = mac_learning_create();
 +    for (i = 0; i < MAX_MIRRORS; i++) {
 +        ofproto->mirrors[i] = NULL;
 +    }
 +    ofproto->has_bonded_bundles = false;
 +
 +    timer_set_duration(&ofproto->next_expiration, 1000);
 +
 +    hmap_init(&ofproto->facets);
 +    ofproto->need_revalidate = false;
 +    tag_set_init(&ofproto->revalidate_set);
 +
 +    ofproto->up.tables = xmalloc(sizeof *ofproto->up.tables);
 +    classifier_init(&ofproto->up.tables[0]);
 +    ofproto->up.n_tables = 1;
 +
 +    ofproto_dpif_unixctl_init();
 +
 +    return 0;
 +}
 +
 +static void
 +destruct(struct ofproto *ofproto_)
 +{
 +    struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
 +    int i;
 +
 +    for (i = 0; i < MAX_MIRRORS; i++) {
 +        mirror_destroy(ofproto->mirrors[i]);
 +    }
 +
 +    netflow_destroy(ofproto->netflow);
 +    ofproto_sflow_destroy(ofproto->sflow);
 +    hmap_destroy(&ofproto->bundles);
 +    mac_learning_destroy(ofproto->ml);
 +
 +    hmap_destroy(&ofproto->facets);
 +
 +    dpif_close(ofproto->dpif);
 +}
 +
 +static int
 +run(struct ofproto *ofproto_)
 +{
 +    struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
 +    struct ofport_dpif *ofport;
 +    struct ofbundle *bundle;
 +    int i;
 +
 +    dpif_run(ofproto->dpif);
 +
 +    for (i = 0; i < 50; i++) {
 +        struct dpif_upcall packet;
 +        int error;
 +
 +        error = dpif_recv(ofproto->dpif, &packet);
 +        if (error) {
 +            if (error == ENODEV) {
 +                /* Datapath destroyed. */
 +                return error;
 +            }
 +            break;
 +        }
 +
 +        handle_upcall(ofproto, &packet);
 +    }
 +
 +    if (timer_expired(&ofproto->next_expiration)) {
 +        int delay = expire(ofproto);
 +        timer_set_duration(&ofproto->next_expiration, delay);
 +    }
 +
 +    if (ofproto->netflow) {
 +        netflow_run(ofproto->netflow);
 +    }
 +    if (ofproto->sflow) {
 +        ofproto_sflow_run(ofproto->sflow);
 +    }
 +
 +    HMAP_FOR_EACH (ofport, up.hmap_node, &ofproto->up.ports) {
 +        port_run(ofport);
 +    }
 +    HMAP_FOR_EACH (bundle, hmap_node, &ofproto->bundles) {
 +        bundle_run(bundle);
 +    }
 +
 +    /* Now revalidate if there's anything to do. */
 +    if (ofproto->need_revalidate
 +        || !tag_set_is_empty(&ofproto->revalidate_set)) {
 +        struct tag_set revalidate_set = ofproto->revalidate_set;
 +        bool revalidate_all = ofproto->need_revalidate;
 +        struct facet *facet, *next;
 +
 +        /* Clear the revalidation flags. */
 +        tag_set_init(&ofproto->revalidate_set);
 +        ofproto->need_revalidate = false;
 +
 +        HMAP_FOR_EACH_SAFE (facet, next, hmap_node, &ofproto->facets) {
 +            if (revalidate_all
 +                || tag_set_intersects(&revalidate_set, facet->tags)) {
 +                facet_revalidate(ofproto, facet);
 +            }
 +        }
 +    }
 +
 +    return 0;
 +}
 +
 +static void
 +wait(struct ofproto *ofproto_)
 +{
 +    struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
 +    struct ofport_dpif *ofport;
 +    struct ofbundle *bundle;
 +
 +    dpif_wait(ofproto->dpif);
 +    dpif_recv_wait(ofproto->dpif);
 +    if (ofproto->sflow) {
 +        ofproto_sflow_wait(ofproto->sflow);
 +    }
 +    if (!tag_set_is_empty(&ofproto->revalidate_set)) {
 +        poll_immediate_wake();
 +    }
 +    HMAP_FOR_EACH (ofport, up.hmap_node, &ofproto->up.ports) {
 +        port_wait(ofport);
 +    }
 +    HMAP_FOR_EACH (bundle, hmap_node, &ofproto->bundles) {
 +        bundle_wait(bundle);
 +    }
 +    if (ofproto->need_revalidate) {
 +        /* Shouldn't happen, but if it does just go around again. */
 +        VLOG_DBG_RL(&rl, "need revalidate in ofproto_wait_cb()");
 +        poll_immediate_wake();
 +    } else {
 +        timer_wait(&ofproto->next_expiration);
 +    }
 +}
 +
 +static void
 +flush(struct ofproto *ofproto_)
 +{
 +    struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
 +    struct facet *facet, *next_facet;
 +
 +    HMAP_FOR_EACH_SAFE (facet, next_facet, hmap_node, &ofproto->facets) {
 +        /* Mark the facet as not installed so that facet_remove() doesn't
 +         * bother trying to uninstall it.  There is no point in uninstalling it
 +         * individually since we are about to blow away all the facets with
 +         * dpif_flow_flush(). */
 +        facet->installed = false;
 +        facet->dp_packet_count = 0;
 +        facet->dp_byte_count = 0;
 +        facet_remove(ofproto, facet);
 +    }
 +    dpif_flow_flush(ofproto->dpif);
 +}
 +
 +static void
 +get_features(struct ofproto *ofproto_ OVS_UNUSED,
 +             bool *arp_match_ip, uint32_t *actions)
 +{
 +    *arp_match_ip = true;
 +    *actions = ((1u << OFPAT_OUTPUT) |
 +                (1u << OFPAT_SET_VLAN_VID) |
 +                (1u << OFPAT_SET_VLAN_PCP) |
 +                (1u << OFPAT_STRIP_VLAN) |
 +                (1u << OFPAT_SET_DL_SRC) |
 +                (1u << OFPAT_SET_DL_DST) |
 +                (1u << OFPAT_SET_NW_SRC) |
 +                (1u << OFPAT_SET_NW_DST) |
 +                (1u << OFPAT_SET_NW_TOS) |
 +                (1u << OFPAT_SET_TP_SRC) |
 +                (1u << OFPAT_SET_TP_DST) |
 +                (1u << OFPAT_ENQUEUE));
 +}
 +
 +static void
 +get_tables(struct ofproto *ofproto_, struct ofp_table_stats *ots)
 +{
 +    struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
 +    struct odp_stats s;
 +
 +    strcpy(ots->name, "classifier");
 +
 +    dpif_get_dp_stats(ofproto->dpif, &s);
 +    put_32aligned_be64(&ots->lookup_count, htonll(s.n_hit + s.n_missed));
 +    put_32aligned_be64(&ots->matched_count,
 +                       htonll(s.n_hit + ofproto->n_matches));
 +}
 +
 +static int
 +set_netflow(struct ofproto *ofproto_,
 +            const struct netflow_options *netflow_options)
 +{
 +    struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
 +
 +    if (netflow_options) {
 +        if (!ofproto->netflow) {
 +            ofproto->netflow = netflow_create();
 +        }
 +        return netflow_set_options(ofproto->netflow, netflow_options);
 +    } else {
 +        netflow_destroy(ofproto->netflow);
 +        ofproto->netflow = NULL;
 +        return 0;
 +    }
 +}
 +
 +static struct ofport *
 +port_alloc(void)
 +{
 +    struct ofport_dpif *port = xmalloc(sizeof *port);
 +    return &port->up;
 +}
 +
 +static void
 +port_dealloc(struct ofport *port_)
 +{
 +    struct ofport_dpif *port = ofport_dpif_cast(port_);
 +    free(port);
 +}
 +
 +static int
 +port_construct(struct ofport *port_)
 +{
 +    struct ofport_dpif *port = ofport_dpif_cast(port_);
 +    struct ofproto_dpif *ofproto = ofproto_dpif_cast(port->up.ofproto);
 +
 +    port->odp_port = ofp_port_to_odp_port(port->up.ofp_port);
 +    port->bundle = NULL;
 +    port->cfm = NULL;
 +    port->tag = tag_create_random();
 +
 +    if (ofproto->sflow) {
 +        ofproto_sflow_add_port(ofproto->sflow, port->odp_port,
 +                               netdev_get_name(port->up.netdev));
 +    }
 +
 +    return 0;
 +}
 +
 +static void
 +port_destruct(struct ofport *port_)
 +{
 +    struct ofport_dpif *port = ofport_dpif_cast(port_);
 +    struct ofproto_dpif *ofproto = ofproto_dpif_cast(port->up.ofproto);
 +
 +    bundle_remove(port_);
 +    set_cfm(port_, NULL, NULL, 0);
 +    if (ofproto->sflow) {
 +        ofproto_sflow_del_port(ofproto->sflow, port->odp_port);
 +    }
 +}
 +
 +static void
 +port_modified(struct ofport *port_)
 +{
 +    struct ofport_dpif *port = ofport_dpif_cast(port_);
 +
 +    if (port->bundle && port->bundle->bond) {
 +        bond_slave_set_netdev(port->bundle->bond, port, port->up.netdev);
 +    }
 +}
 +
 +static void
 +port_reconfigured(struct ofport *port_, ovs_be32 old_config)
 +{
 +    struct ofport_dpif *port = ofport_dpif_cast(port_);
 +    struct ofproto_dpif *ofproto = ofproto_dpif_cast(port->up.ofproto);
 +    ovs_be32 changed = old_config ^ port->up.opp.config;
 +
 +    if (changed & htonl(OFPPC_NO_RECV | OFPPC_NO_RECV_STP |
 +                        OFPPC_NO_FWD | OFPPC_NO_FLOOD)) {
 +        ofproto->need_revalidate = true;
 +    }
 +}
 +
 +static int
 +set_sflow(struct ofproto *ofproto_,
 +          const struct ofproto_sflow_options *sflow_options)
 +{
 +    struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
 +    struct ofproto_sflow *os = ofproto->sflow;
 +    if (sflow_options) {
 +        if (!os) {
 +            struct ofport_dpif *ofport;
 +
 +            os = ofproto->sflow = ofproto_sflow_create(ofproto->dpif);
 +            HMAP_FOR_EACH (ofport, up.hmap_node, &ofproto->up.ports) {
 +                ofproto_sflow_add_port(os, ofport->odp_port,
 +                                       netdev_get_name(ofport->up.netdev));
 +            }
 +        }
 +        ofproto_sflow_set_options(os, sflow_options);
 +    } else {
 +        ofproto_sflow_destroy(os);
 +        ofproto->sflow = NULL;
 +    }
 +    return 0;
 +}
 +
 +static int
 +set_cfm(struct ofport *ofport_, const struct cfm *cfm,
 +        const uint16_t *remote_mps, size_t n_remote_mps)
 +{
 +    struct ofport_dpif *ofport = ofport_dpif_cast(ofport_);
 +    int error;
 +
 +    if (!cfm) {
 +        error = 0;
 +    } else {
 +        if (!ofport->cfm) {
 +            ofport->cfm = cfm_create();
 +        }
 +
 +        ofport->cfm->mpid = cfm->mpid;
 +        ofport->cfm->interval = cfm->interval;
 +        memcpy(ofport->cfm->maid, cfm->maid, CCM_MAID_LEN);
 +
 +        cfm_update_remote_mps(ofport->cfm, remote_mps, n_remote_mps);
 +
 +        if (cfm_configure(ofport->cfm)) {
 +            return 0;
 +        }
 +
 +        error = EINVAL;
 +    }
 +    cfm_destroy(ofport->cfm);
 +    ofport->cfm = NULL;
 +    return error;
 +}
 +
 +static int
 +get_cfm(struct ofport *ofport_, const struct cfm **cfmp)
 +{
 +    struct ofport_dpif *ofport = ofport_dpif_cast(ofport_);
 +    *cfmp = ofport->cfm;
 +    return 0;
 +}
 +\f
 +/* Bundles. */
 +
 +/* Expires all MAC learning entries associated with 'port' and forces ofproto
 + * to revalidate every flow. */
 +static void
 +bundle_flush_macs(struct ofbundle *bundle)
 +{
 +    struct ofproto_dpif *ofproto = bundle->ofproto;
 +    struct mac_learning *ml = ofproto->ml;
 +    struct mac_entry *mac, *next_mac;
 +
 +    ofproto->need_revalidate = true;
 +    LIST_FOR_EACH_SAFE (mac, next_mac, lru_node, &ml->lrus) {
 +        if (mac->port.p == bundle) {
 +            mac_learning_expire(ml, mac);
 +        }
 +    }
 +}
 +
 +static struct ofbundle *
 +bundle_lookup(const struct ofproto_dpif *ofproto, void *aux)
 +{
 +    struct ofbundle *bundle;
 +
 +    HMAP_FOR_EACH_IN_BUCKET (bundle, hmap_node, hash_pointer(aux, 0),
 +                             &ofproto->bundles) {
 +        if (bundle->aux == aux) {
 +            return bundle;
 +        }
 +    }
 +    return NULL;
 +}
 +
 +/* Looks up each of the 'n_auxes' pointers in 'auxes' as bundles and adds the
 + * ones that are found to 'bundles'. */
 +static void
 +bundle_lookup_multiple(struct ofproto_dpif *ofproto,
 +                       void **auxes, size_t n_auxes,
 +                       struct hmapx *bundles)
 +{
 +    size_t i;
 +
 +    hmapx_init(bundles);
 +    for (i = 0; i < n_auxes; i++) {
 +        struct ofbundle *bundle = bundle_lookup(ofproto, auxes[i]);
 +        if (bundle) {
 +            hmapx_add(bundles, bundle);
 +        }
 +    }
 +}
 +
 +static void
 +bundle_del_port(struct ofport_dpif *port)
 +{
 +    struct ofbundle *bundle = port->bundle;
 +
 +    list_remove(&port->bundle_node);
 +    port->bundle = NULL;
 +
 +    if (bundle->lacp) {
 +        lacp_slave_unregister(bundle->lacp, port);
 +    }
 +    if (bundle->bond) {
 +        bond_slave_unregister(bundle->bond, port);
 +    }
 +
 +    bundle->floodable = true;
 +    LIST_FOR_EACH (port, bundle_node, &bundle->ports) {
 +        if (port->up.opp.config & htonl(OFPPC_NO_FLOOD)) {
 +            bundle->floodable = false;
 +        }
 +    }
 +}
 +
 +static bool
 +bundle_add_port(struct ofbundle *bundle, uint32_t ofp_port,
-                              s->lacp ? &s->lacp_slaves[i] : NULL)) {
++                struct lacp_slave_settings *lacp,
++                uint32_t bond_stable_id)
 +{
 +    struct ofport_dpif *port;
 +
 +    port = get_ofp_port(bundle->ofproto, ofp_port);
 +    if (!port) {
 +        return false;
 +    }
 +
 +    if (port->bundle != bundle) {
 +        if (port->bundle) {
 +            bundle_del_port(port);
 +        }
 +
 +        port->bundle = bundle;
 +        list_push_back(&bundle->ports, &port->bundle_node);
 +        if (port->up.opp.config & htonl(OFPPC_NO_FLOOD)) {
 +            bundle->floodable = false;
 +        }
 +    }
 +    if (lacp) {
 +        lacp_slave_register(bundle->lacp, port, lacp);
 +    }
 +
++    port->bond_stable_id = bond_stable_id;
++
 +    return true;
 +}
 +
 +static void
 +bundle_destroy(struct ofbundle *bundle)
 +{
 +    struct ofproto_dpif *ofproto;
 +    struct ofport_dpif *port, *next_port;
 +    int i;
 +
 +    if (!bundle) {
 +        return;
 +    }
 +
 +    ofproto = bundle->ofproto;
 +    for (i = 0; i < MAX_MIRRORS; i++) {
 +        struct ofmirror *m = ofproto->mirrors[i];
 +        if (m) {
 +            if (m->out == bundle) {
 +                mirror_destroy(m);
 +            } else if (hmapx_find_and_delete(&m->srcs, bundle)
 +                       || hmapx_find_and_delete(&m->dsts, bundle)) {
 +                ofproto->need_revalidate = true;
 +            }
 +        }
 +    }
 +
 +    LIST_FOR_EACH_SAFE (port, next_port, bundle_node, &bundle->ports) {
 +        bundle_del_port(port);
 +    }
 +
 +    bundle_flush_macs(bundle);
 +    hmap_remove(&ofproto->bundles, &bundle->hmap_node);
 +    free(bundle->name);
 +    free(bundle->trunks);
 +    lacp_destroy(bundle->lacp);
 +    bond_destroy(bundle->bond);
 +    free(bundle);
 +}
 +
 +static int
 +bundle_set(struct ofproto *ofproto_, void *aux,
 +           const struct ofproto_bundle_settings *s)
 +{
 +    struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
 +    bool need_flush = false;
 +    const unsigned long *trunks;
 +    struct ofport_dpif *port;
 +    struct ofbundle *bundle;
 +    size_t i;
 +    bool ok;
 +
 +    if (!s) {
 +        bundle_destroy(bundle_lookup(ofproto, aux));
 +        return 0;
 +    }
 +
 +    assert(s->n_slaves == 1 || s->bond != NULL);
 +    assert((s->lacp != NULL) == (s->lacp_slaves != NULL));
 +
 +    bundle = bundle_lookup(ofproto, aux);
 +    if (!bundle) {
 +        bundle = xmalloc(sizeof *bundle);
 +
 +        bundle->ofproto = ofproto;
 +        hmap_insert(&ofproto->bundles, &bundle->hmap_node,
 +                    hash_pointer(aux, 0));
 +        bundle->aux = aux;
 +        bundle->name = NULL;
 +
 +        list_init(&bundle->ports);
 +        bundle->vlan = -1;
 +        bundle->trunks = NULL;
 +        bundle->lacp = NULL;
 +        bundle->bond = NULL;
 +
 +        bundle->floodable = true;
 +
 +        bundle->src_mirrors = 0;
 +        bundle->dst_mirrors = 0;
 +        bundle->mirror_out = 0;
 +    }
 +
 +    if (!bundle->name || strcmp(s->name, bundle->name)) {
 +        free(bundle->name);
 +        bundle->name = xstrdup(s->name);
 +    }
 +
 +    /* LACP. */
 +    if (s->lacp) {
 +        if (!bundle->lacp) {
 +            bundle->lacp = lacp_create();
 +        }
 +        lacp_configure(bundle->lacp, s->lacp);
 +    } else {
 +        lacp_destroy(bundle->lacp);
 +        bundle->lacp = NULL;
 +    }
 +
 +    /* Update set of ports. */
 +    ok = true;
 +    for (i = 0; i < s->n_slaves; i++) {
 +        if (!bundle_add_port(bundle, s->slaves[i],
-             uint16_t stable_id = (bundle->lacp
-                                   ? lacp_slave_get_port_id(bundle->lacp, port)
-                                   : port->odp_port);
-             bond_slave_register(bundle->bond, port, stable_id,
++                             s->lacp ? &s->lacp_slaves[i] : NULL,
++                             s->bond_stable_ids ? s->bond_stable_ids[i] : 0)) {
 +            ok = false;
 +        }
 +    }
 +    if (!ok || list_size(&bundle->ports) != s->n_slaves) {
 +        struct ofport_dpif *next_port;
 +
 +        LIST_FOR_EACH_SAFE (port, next_port, bundle_node, &bundle->ports) {
 +            for (i = 0; i < s->n_slaves; i++) {
 +                if (s->slaves[i] == odp_port_to_ofp_port(port->odp_port)) {
 +                    goto found;
 +                }
 +            }
 +
 +            bundle_del_port(port);
 +        found: ;
 +        }
 +    }
 +    assert(list_size(&bundle->ports) <= s->n_slaves);
 +
 +    if (list_is_empty(&bundle->ports)) {
 +        bundle_destroy(bundle);
 +        return EINVAL;
 +    }
 +
 +    /* Set VLAN tag. */
 +    if (s->vlan != bundle->vlan) {
 +        bundle->vlan = s->vlan;
 +        need_flush = true;
 +    }
 +
 +    /* Get trunked VLANs. */
 +    trunks = s->vlan == -1 ? NULL : s->trunks;
 +    if (!vlan_bitmap_equal(trunks, bundle->trunks)) {
 +        free(bundle->trunks);
 +        bundle->trunks = vlan_bitmap_clone(trunks);
 +        need_flush = true;
 +    }
 +
 +    /* Bonding. */
 +    if (!list_is_short(&bundle->ports)) {
 +        bundle->ofproto->has_bonded_bundles = true;
 +        if (bundle->bond) {
 +            if (bond_reconfigure(bundle->bond, s->bond)) {
 +                ofproto->need_revalidate = true;
 +            }
 +        } else {
 +            bundle->bond = bond_create(s->bond);
 +        }
 +
 +        LIST_FOR_EACH (port, bundle_node, &bundle->ports) {
++            bond_slave_register(bundle->bond, port, port->bond_stable_id,
 +                                port->up.netdev);
 +        }
 +    } else {
 +        bond_destroy(bundle->bond);
 +        bundle->bond = NULL;
 +    }
 +
 +    /* If we changed something that would affect MAC learning, un-learn
 +     * everything on this port and force flow revalidation. */
 +    if (need_flush) {
 +        bundle_flush_macs(bundle);
 +    }
 +
 +    return 0;
 +}
 +
 +static void
 +bundle_remove(struct ofport *port_)
 +{
 +    struct ofport_dpif *port = ofport_dpif_cast(port_);
 +    struct ofbundle *bundle = port->bundle;
 +
 +    if (bundle) {
 +        bundle_del_port(port);
 +        if (list_is_empty(&bundle->ports)) {
 +            bundle_destroy(bundle);
 +        } else if (list_is_short(&bundle->ports)) {
 +            bond_destroy(bundle->bond);
 +            bundle->bond = NULL;
 +        }
 +    }
 +}
 +
 +static void
 +send_pdu_cb(void *port_, const struct lacp_pdu *pdu)
 +{
 +    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 10);
 +    struct ofport_dpif *port = port_;
 +    uint8_t ea[ETH_ADDR_LEN];
 +    int error;
 +
 +    error = netdev_get_etheraddr(port->up.netdev, ea);
 +    if (!error) {
 +        struct lacp_pdu *packet_pdu;
 +        struct ofpbuf packet;
 +
 +        ofpbuf_init(&packet, 0);
 +        packet_pdu = eth_compose(&packet, eth_addr_lacp, ea, ETH_TYPE_LACP,
 +                                 sizeof *packet_pdu);
 +        *packet_pdu = *pdu;
 +        error = netdev_send(port->up.netdev, &packet);
 +        if (error) {
 +            VLOG_WARN_RL(&rl, "port %s: sending LACP PDU on iface %s failed "
 +                         "(%s)", port->bundle->name,
 +                         netdev_get_name(port->up.netdev), strerror(error));
 +        }
 +        ofpbuf_uninit(&packet);
 +    } else {
 +        VLOG_ERR_RL(&rl, "port %s: cannot obtain Ethernet address of iface "
 +                    "%s (%s)", port->bundle->name,
 +                    netdev_get_name(port->up.netdev), strerror(error));
 +    }
 +}
 +
 +static void
 +bundle_send_learning_packets(struct ofbundle *bundle)
 +{
 +    struct ofproto_dpif *ofproto = bundle->ofproto;
 +    int error, n_packets, n_errors;
 +    struct mac_entry *e;
 +
 +    error = n_packets = n_errors = 0;
 +    LIST_FOR_EACH (e, lru_node, &ofproto->ml->lrus) {
 +        if (e->port.p != bundle) {
 +            int ret = bond_send_learning_packet(bundle->bond, e->mac, e->vlan);
 +            if (ret) {
 +                error = ret;
 +                n_errors++;
 +            }
 +            n_packets++;
 +        }
 +    }
 +
 +    if (n_errors) {
 +        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
 +        VLOG_WARN_RL(&rl, "bond %s: %d errors sending %d gratuitous learning "
 +                     "packets, last error was: %s",
 +                     bundle->name, n_errors, n_packets, strerror(error));
 +    } else {
 +        VLOG_DBG("bond %s: sent %d gratuitous learning packets",
 +                 bundle->name, n_packets);
 +    }
 +}
 +
 +static void
 +bundle_run(struct ofbundle *bundle)
 +{
 +    if (bundle->lacp) {
 +        lacp_run(bundle->lacp, send_pdu_cb);
 +    }
 +    if (bundle->bond) {
 +        struct ofport_dpif *port;
 +
 +        LIST_FOR_EACH (port, bundle_node, &bundle->ports) {
 +            bool may_enable = lacp_slave_may_enable(bundle->lacp, port);
 +            bond_slave_set_lacp_may_enable(bundle->bond, port, may_enable);
 +        }
 +
 +        bond_run(bundle->bond, &bundle->ofproto->revalidate_set,
 +                 lacp_negotiated(bundle->lacp));
 +        if (bond_should_send_learning_packets(bundle->bond)) {
 +            bundle_send_learning_packets(bundle);
 +        }
 +    }
 +}
 +
 +static void
 +bundle_wait(struct ofbundle *bundle)
 +{
 +    if (bundle->lacp) {
 +        lacp_wait(bundle->lacp);
 +    }
 +    if (bundle->bond) {
 +        bond_wait(bundle->bond);
 +    }
 +}
 +\f
 +/* Mirrors. */
 +
 +static int
 +mirror_scan(struct ofproto_dpif *ofproto)
 +{
 +    int idx;
 +
 +    for (idx = 0; idx < MAX_MIRRORS; idx++) {
 +        if (!ofproto->mirrors[idx]) {
 +            return idx;
 +        }
 +    }
 +    return -1;
 +}
 +
 +static struct ofmirror *
 +mirror_lookup(struct ofproto_dpif *ofproto, void *aux)
 +{
 +    int i;
 +
 +    for (i = 0; i < MAX_MIRRORS; i++) {
 +        struct ofmirror *mirror = ofproto->mirrors[i];
 +        if (mirror && mirror->aux == aux) {
 +            return mirror;
 +        }
 +    }
 +
 +    return NULL;
 +}
 +
 +static int
 +mirror_set(struct ofproto *ofproto_, void *aux,
 +           const struct ofproto_mirror_settings *s)
 +{
 +    struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
 +    mirror_mask_t mirror_bit;
 +    struct ofbundle *bundle;
 +    struct ofmirror *mirror;
 +    struct ofbundle *out;
 +    struct hmapx srcs;          /* Contains "struct ofbundle *"s. */
 +    struct hmapx dsts;          /* Contains "struct ofbundle *"s. */
 +    int out_vlan;
 +
 +    mirror = mirror_lookup(ofproto, aux);
 +    if (!s) {
 +        mirror_destroy(mirror);
 +        return 0;
 +    }
 +    if (!mirror) {
 +        int idx;
 +
 +        idx = mirror_scan(ofproto);
 +        if (idx < 0) {
 +            VLOG_WARN("bridge %s: maximum of %d port mirrors reached, "
 +                      "cannot create %s",
 +                      ofproto->up.name, MAX_MIRRORS, s->name);
 +            return EFBIG;
 +        }
 +
 +        mirror = ofproto->mirrors[idx] = xzalloc(sizeof *mirror);
 +        mirror->ofproto = ofproto;
 +        mirror->idx = idx;
 +        mirror->out_vlan = -1;
 +        mirror->name = NULL;
 +    }
 +
 +    if (!mirror->name || strcmp(s->name, mirror->name)) {
 +        free(mirror->name);
 +        mirror->name = xstrdup(s->name);
 +    }
 +
 +    /* Get the new configuration. */
 +    if (s->out_bundle) {
 +        out = bundle_lookup(ofproto, s->out_bundle);
 +        if (!out) {
 +            mirror_destroy(mirror);
 +            return EINVAL;
 +        }
 +        out_vlan = -1;
 +    } else {
 +        out = NULL;
 +        out_vlan = s->out_vlan;
 +    }
 +    bundle_lookup_multiple(ofproto, s->srcs, s->n_srcs, &srcs);
 +    bundle_lookup_multiple(ofproto, s->dsts, s->n_dsts, &dsts);
 +
 +    /* If the configuration has not changed, do nothing. */
 +    if (hmapx_equals(&srcs, &mirror->srcs)
 +        && hmapx_equals(&dsts, &mirror->dsts)
 +        && vlan_bitmap_equal(mirror->vlans, s->src_vlans)
 +        && mirror->out == out
 +        && mirror->out_vlan == out_vlan)
 +    {
 +        hmapx_destroy(&srcs);
 +        hmapx_destroy(&dsts);
 +        return 0;
 +    }
 +
 +    hmapx_swap(&srcs, &mirror->srcs);
 +    hmapx_destroy(&srcs);
 +
 +    hmapx_swap(&dsts, &mirror->dsts);
 +    hmapx_destroy(&dsts);
 +
 +    free(mirror->vlans);
 +    mirror->vlans = vlan_bitmap_clone(s->src_vlans);
 +
 +    mirror->out = out;
 +    mirror->out_vlan = out_vlan;
 +
 +    /* Update bundles. */
 +    mirror_bit = MIRROR_MASK_C(1) << mirror->idx;
 +    HMAP_FOR_EACH (bundle, hmap_node, &mirror->ofproto->bundles) {
 +        if (hmapx_contains(&mirror->srcs, bundle)) {
 +            bundle->src_mirrors |= mirror_bit;
 +        } else {
 +            bundle->src_mirrors &= ~mirror_bit;
 +        }
 +
 +        if (hmapx_contains(&mirror->dsts, bundle)) {
 +            bundle->dst_mirrors |= mirror_bit;
 +        } else {
 +            bundle->dst_mirrors &= ~mirror_bit;
 +        }
 +
 +        if (mirror->out == bundle) {
 +            bundle->mirror_out |= mirror_bit;
 +        } else {
 +            bundle->mirror_out &= ~mirror_bit;
 +        }
 +    }
 +
 +    ofproto->need_revalidate = true;
 +    mac_learning_flush(ofproto->ml);
 +
 +    return 0;
 +}
 +
 +static void
 +mirror_destroy(struct ofmirror *mirror)
 +{
 +    struct ofproto_dpif *ofproto;
 +    mirror_mask_t mirror_bit;
 +    struct ofbundle *bundle;
 +
 +    if (!mirror) {
 +        return;
 +    }
 +
 +    ofproto = mirror->ofproto;
 +    ofproto->need_revalidate = true;
 +    mac_learning_flush(ofproto->ml);
 +
 +    mirror_bit = MIRROR_MASK_C(1) << mirror->idx;
 +    HMAP_FOR_EACH (bundle, hmap_node, &ofproto->bundles) {
 +        bundle->src_mirrors &= ~mirror_bit;
 +        bundle->dst_mirrors &= ~mirror_bit;
 +        bundle->mirror_out &= ~mirror_bit;
 +    }
 +
 +    hmapx_destroy(&mirror->srcs);
 +    hmapx_destroy(&mirror->dsts);
 +    free(mirror->vlans);
 +
 +    ofproto->mirrors[mirror->idx] = NULL;
 +    free(mirror->name);
 +    free(mirror);
 +}
 +
 +static int
 +set_flood_vlans(struct ofproto *ofproto_, unsigned long *flood_vlans)
 +{
 +    struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
 +    if (mac_learning_set_flood_vlans(ofproto->ml, flood_vlans)) {
 +        ofproto->need_revalidate = true;
 +        mac_learning_flush(ofproto->ml);
 +    }
 +    return 0;
 +}
 +
 +static bool
 +is_mirror_output_bundle(struct ofproto *ofproto_, void *aux)
 +{
 +    struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
 +    struct ofbundle *bundle = bundle_lookup(ofproto, aux);
 +    return bundle && bundle->mirror_out != 0;
 +}
 +\f
 +/* Ports. */
 +
 +static struct ofport_dpif *
 +get_ofp_port(struct ofproto_dpif *ofproto, uint16_t ofp_port)
 +{
 +    return ofport_dpif_cast(ofproto_get_port(&ofproto->up, ofp_port));
 +}
 +
 +static struct ofport_dpif *
 +get_odp_port(struct ofproto_dpif *ofproto, uint32_t odp_port)
 +{
 +    return get_ofp_port(ofproto, odp_port_to_ofp_port(odp_port));
 +}
 +
 +static void
 +ofproto_port_from_dpif_port(struct ofproto_port *ofproto_port,
 +                            struct dpif_port *dpif_port)
 +{
 +    ofproto_port->name = dpif_port->name;
 +    ofproto_port->type = dpif_port->type;
 +    ofproto_port->ofp_port = odp_port_to_ofp_port(dpif_port->port_no);
 +}
 +
 +static void
 +port_run(struct ofport_dpif *ofport)
 +{
 +    if (ofport->cfm) {
 +        cfm_run(ofport->cfm);
 +
 +        if (cfm_should_send_ccm(ofport->cfm)) {
 +            struct ofpbuf packet;
 +            struct ccm *ccm;
 +
 +            ofpbuf_init(&packet, 0);
 +            ccm = eth_compose(&packet, eth_addr_ccm, ofport->up.opp.hw_addr,
 +                              ETH_TYPE_CFM, sizeof *ccm);
 +            cfm_compose_ccm(ofport->cfm, ccm);
 +            send_packet(ofproto_dpif_cast(ofport->up.ofproto),
 +                        ofport->odp_port, 0, &packet);
 +            ofpbuf_uninit(&packet);
 +        }
 +    }
 +}
 +
 +static void
 +port_wait(struct ofport_dpif *ofport)
 +{
 +    if (ofport->cfm) {
 +        cfm_wait(ofport->cfm);
 +    }
 +}
 +
 +static int
 +port_query_by_name(const struct ofproto *ofproto_, const char *devname,
 +                   struct ofproto_port *ofproto_port)
 +{
 +    struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
 +    struct dpif_port dpif_port;
 +    int error;
 +
 +    error = dpif_port_query_by_name(ofproto->dpif, devname, &dpif_port);
 +    if (!error) {
 +        ofproto_port_from_dpif_port(ofproto_port, &dpif_port);
 +    }
 +    return error;
 +}
 +
 +static int
 +port_add(struct ofproto *ofproto_, struct netdev *netdev, uint16_t *ofp_portp)
 +{
 +    struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
 +    uint16_t odp_port;
 +    int error;
 +
 +    error = dpif_port_add(ofproto->dpif, netdev, &odp_port);
 +    if (!error) {
 +        *ofp_portp = odp_port_to_ofp_port(odp_port);
 +    }
 +    return error;
 +}
 +
 +static int
 +port_del(struct ofproto *ofproto_, uint16_t ofp_port)
 +{
 +    struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
 +    int error;
 +
 +    error = dpif_port_del(ofproto->dpif, ofp_port_to_odp_port(ofp_port));
 +    if (!error) {
 +        struct ofport_dpif *ofport = get_ofp_port(ofproto, ofp_port);
 +        if (ofport) {
 +            /* The caller is going to close ofport->up.netdev.  If this is a
 +             * bonded port, then the bond is using that netdev, so remove it
 +             * from the bond.  The client will need to reconfigure everything
 +             * after deleting ports, so then the slave will get re-added. */
 +            bundle_remove(&ofport->up);
 +        }
 +    }
 +    return error;
 +}
 +
 +struct port_dump_state {
 +    struct dpif_port_dump dump;
 +    bool done;
 +};
 +
 +static int
 +port_dump_start(const struct ofproto *ofproto_, void **statep)
 +{
 +    struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
 +    struct port_dump_state *state;
 +
 +    *statep = state = xmalloc(sizeof *state);
 +    dpif_port_dump_start(&state->dump, ofproto->dpif);
 +    state->done = false;
 +    return 0;
 +}
 +
 +static int
 +port_dump_next(const struct ofproto *ofproto_ OVS_UNUSED, void *state_,
 +               struct ofproto_port *port)
 +{
 +    struct port_dump_state *state = state_;
 +    struct dpif_port dpif_port;
 +
 +    if (dpif_port_dump_next(&state->dump, &dpif_port)) {
 +        ofproto_port_from_dpif_port(port, &dpif_port);
 +        return 0;
 +    } else {
 +        int error = dpif_port_dump_done(&state->dump);
 +        state->done = true;
 +        return error ? error : EOF;
 +    }
 +}
 +
 +static int
 +port_dump_done(const struct ofproto *ofproto_ OVS_UNUSED, void *state_)
 +{
 +    struct port_dump_state *state = state_;
 +
 +    if (!state->done) {
 +        dpif_port_dump_done(&state->dump);
 +    }
 +    free(state);
 +    return 0;
 +}
 +
 +static int
 +port_poll(const struct ofproto *ofproto_, char **devnamep)
 +{
 +    struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
 +    return dpif_port_poll(ofproto->dpif, devnamep);
 +}
 +
 +static void
 +port_poll_wait(const struct ofproto *ofproto_)
 +{
 +    struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
 +    dpif_port_poll_wait(ofproto->dpif);
 +}
 +
 +static int
 +port_is_lacp_current(const struct ofport *ofport_)
 +{
 +    const struct ofport_dpif *ofport = ofport_dpif_cast(ofport_);
 +    return (ofport->bundle && ofport->bundle->lacp
 +            ? lacp_slave_is_current(ofport->bundle->lacp, ofport)
 +            : -1);
 +}
 +\f
 +/* Upcall handling. */
 +
 +/* Given 'upcall', of type DPIF_UC_ACTION or DPIF_UC_MISS, sends an
 + * OFPT_PACKET_IN message to each OpenFlow controller as necessary according to
 + * their individual configurations.
 + *
 + * If 'clone' is true, the caller retains ownership of 'upcall->packet'.
 + * Otherwise, ownership is transferred to this function. */
 +static void
 +send_packet_in(struct ofproto_dpif *ofproto, struct dpif_upcall *upcall,
 +               const struct flow *flow, bool clone)
 +{
 +    struct ofputil_packet_in pin;
 +
 +    pin.packet = upcall->packet;
 +    pin.in_port = flow->in_port;
 +    pin.reason = upcall->type == DPIF_UC_MISS ? OFPR_NO_MATCH : OFPR_ACTION;
 +    pin.buffer_id = 0;          /* not yet known */
 +    pin.send_len = upcall->userdata;
 +    connmgr_send_packet_in(ofproto->up.connmgr, &pin, flow,
 +                           clone ? NULL : upcall->packet);
 +}
 +
 +static bool
 +process_special(struct ofproto_dpif *ofproto, const struct flow *flow,
 +                const struct ofpbuf *packet)
 +{
 +    if (cfm_should_process_flow(flow)) {
 +        struct ofport_dpif *ofport = get_ofp_port(ofproto, flow->in_port);
 +        if (ofport && ofport->cfm) {
 +            cfm_process_heartbeat(ofport->cfm, packet);
 +        }
 +        return true;
 +    } else if (flow->dl_type == htons(ETH_TYPE_LACP)) {
 +        struct ofport_dpif *port = get_ofp_port(ofproto, flow->in_port);
 +        if (port && port->bundle && port->bundle->lacp) {
 +            const struct lacp_pdu *pdu = parse_lacp_packet(packet);
 +            if (pdu) {
 +                lacp_process_pdu(port->bundle->lacp, port, pdu);
 +            }
 +            return true;
 +        }
 +    }
 +    return false;
 +}
 +
 +static void
 +handle_miss_upcall(struct ofproto_dpif *ofproto, struct dpif_upcall *upcall)
 +{
 +    struct facet *facet;
 +    struct flow flow;
 +
 +    /* Obtain in_port and tun_id, at least. */
 +    odp_flow_key_to_flow(upcall->key, upcall->key_len, &flow);
 +
 +    /* Set header pointers in 'flow'. */
 +    flow_extract(upcall->packet, flow.tun_id, flow.in_port, &flow);
 +
 +    /* Handle 802.1ag and LACP. */
 +    if (process_special(ofproto, &flow, upcall->packet)) {
 +        ofpbuf_delete(upcall->packet);
 +        ofproto->n_matches++;
 +        return;
 +    }
 +
 +    /* Check with in-band control to see if this packet should be sent
 +     * to the local port regardless of the flow table. */
 +    if (connmgr_msg_in_hook(ofproto->up.connmgr, &flow, upcall->packet)) {
 +        send_packet(ofproto, OFPP_LOCAL, 0, upcall->packet);
 +    }
 +
 +    facet = facet_lookup_valid(ofproto, &flow);
 +    if (!facet) {
 +        struct rule_dpif *rule = rule_dpif_lookup(ofproto, &flow);
 +        if (!rule) {
 +            /* Don't send a packet-in if OFPPC_NO_PACKET_IN asserted. */
 +            struct ofport_dpif *port = get_ofp_port(ofproto, flow.in_port);
 +            if (port) {
 +                if (port->up.opp.config & htonl(OFPPC_NO_PACKET_IN)) {
 +                    COVERAGE_INC(ofproto_dpif_no_packet_in);
 +                    /* XXX install 'drop' flow entry */
 +                    ofpbuf_delete(upcall->packet);
 +                    return;
 +                }
 +            } else {
 +                VLOG_WARN_RL(&rl, "packet-in on unknown port %"PRIu16,
 +                             flow.in_port);
 +            }
 +
 +            send_packet_in(ofproto, upcall, &flow, false);
 +            return;
 +        }
 +
 +        facet = facet_create(rule, &flow, upcall->packet);
 +    } else if (!facet->may_install) {
 +        /* The facet is not installable, that is, we need to process every
 +         * packet, so process the current packet's actions into 'facet'. */
 +        facet_make_actions(ofproto, facet, upcall->packet);
 +    }
 +
 +    if (facet->rule->up.cr.priority == FAIL_OPEN_PRIORITY) {
 +        /*
 +         * Extra-special case for fail-open mode.
 +         *
 +         * We are in fail-open mode and the packet matched the fail-open rule,
 +         * but we are connected to a controller too.  We should send the packet
 +         * up to the controller in the hope that it will try to set up a flow
 +         * and thereby allow us to exit fail-open.
 +         *
 +         * See the top-level comment in fail-open.c for more information.
 +         */
 +        send_packet_in(ofproto, upcall, &flow, true);
 +    }
 +
 +    facet_execute(ofproto, facet, upcall->packet);
 +    facet_install(ofproto, facet, false);
 +    ofproto->n_matches++;
 +}
 +
 +static void
 +handle_upcall(struct ofproto_dpif *ofproto, struct dpif_upcall *upcall)
 +{
 +    struct flow flow;
 +
 +    switch (upcall->type) {
 +    case DPIF_UC_ACTION:
 +        COVERAGE_INC(ofproto_dpif_ctlr_action);
 +        odp_flow_key_to_flow(upcall->key, upcall->key_len, &flow);
 +        send_packet_in(ofproto, upcall, &flow, false);
 +        break;
 +
 +    case DPIF_UC_SAMPLE:
 +        if (ofproto->sflow) {
 +            odp_flow_key_to_flow(upcall->key, upcall->key_len, &flow);
 +            ofproto_sflow_received(ofproto->sflow, upcall, &flow);
 +        }
 +        ofpbuf_delete(upcall->packet);
 +        break;
 +
 +    case DPIF_UC_MISS:
 +        handle_miss_upcall(ofproto, upcall);
 +        break;
 +
 +    case DPIF_N_UC_TYPES:
 +    default:
 +        VLOG_WARN_RL(&rl, "upcall has unexpected type %"PRIu32, upcall->type);
 +        break;
 +    }
 +}
 +\f
 +/* Flow expiration. */
 +
 +static int facet_max_idle(const struct ofproto_dpif *);
 +static void update_stats(struct ofproto_dpif *);
 +static void rule_expire(struct rule_dpif *);
 +static void expire_facets(struct ofproto_dpif *, int dp_max_idle);
 +
 +/* This function is called periodically by run().  Its job is to collect
 + * updates for the flows that have been installed into the datapath, most
 + * importantly when they last were used, and then use that information to
 + * expire flows that have not been used recently.
 + *
 + * Returns the number of milliseconds after which it should be called again. */
 +static int
 +expire(struct ofproto_dpif *ofproto)
 +{
 +    struct rule_dpif *rule, *next_rule;
 +    struct cls_cursor cursor;
 +    int dp_max_idle;
 +
 +    /* Update stats for each flow in the datapath. */
 +    update_stats(ofproto);
 +
 +    /* Expire facets that have been idle too long. */
 +    dp_max_idle = facet_max_idle(ofproto);
 +    expire_facets(ofproto, dp_max_idle);
 +
 +    /* Expire OpenFlow flows whose idle_timeout or hard_timeout has passed. */
 +    cls_cursor_init(&cursor, &ofproto->up.tables[0], NULL);
 +    CLS_CURSOR_FOR_EACH_SAFE (rule, next_rule, up.cr, &cursor) {
 +        rule_expire(rule);
 +    }
 +
 +    /* All outstanding data in existing flows has been accounted, so it's a
 +     * good time to do bond rebalancing. */
 +    if (ofproto->has_bonded_bundles) {
 +        struct ofbundle *bundle;
 +
 +        HMAP_FOR_EACH (bundle, hmap_node, &ofproto->bundles) {
 +            if (bundle->bond) {
 +                bond_rebalance(bundle->bond, &ofproto->revalidate_set);
 +            }
 +        }
 +    }
 +
 +    return MIN(dp_max_idle, 1000);
 +}
 +
 +/* Update 'packet_count', 'byte_count', and 'used' members of installed facets.
 + *
 + * This function also pushes statistics updates to rules which each facet
 + * resubmits into.  Generally these statistics will be accurate.  However, if a
 + * facet changes the rule it resubmits into at some time in between
 + * update_stats() runs, it is possible that statistics accrued to the
 + * old rule will be incorrectly attributed to the new rule.  This could be
 + * avoided by calling update_stats() whenever rules are created or
 + * deleted.  However, the performance impact of making so many calls to the
 + * datapath do not justify the benefit of having perfectly accurate statistics.
 + */
 +static void
 +update_stats(struct ofproto_dpif *p)
 +{
 +    const struct dpif_flow_stats *stats;
 +    struct dpif_flow_dump dump;
 +    const struct nlattr *key;
 +    size_t key_len;
 +
 +    dpif_flow_dump_start(&dump, p->dpif);
 +    while (dpif_flow_dump_next(&dump, &key, &key_len, NULL, NULL, &stats)) {
 +        struct facet *facet;
 +        struct flow flow;
 +
 +        if (odp_flow_key_to_flow(key, key_len, &flow)) {
 +            struct ds s;
 +
 +            ds_init(&s);
 +            odp_flow_key_format(key, key_len, &s);
 +            VLOG_WARN_RL(&rl, "failed to convert ODP flow key to flow: %s",
 +                         ds_cstr(&s));
 +            ds_destroy(&s);
 +
 +            continue;
 +        }
 +        facet = facet_find(p, &flow);
 +
 +        if (facet && facet->installed) {
 +
 +            if (stats->n_packets >= facet->dp_packet_count) {
 +                uint64_t extra = stats->n_packets - facet->dp_packet_count;
 +                facet->packet_count += extra;
 +            } else {
 +                VLOG_WARN_RL(&rl, "unexpected packet count from the datapath");
 +            }
 +
 +            if (stats->n_bytes >= facet->dp_byte_count) {
 +                facet->byte_count += stats->n_bytes - facet->dp_byte_count;
 +            } else {
 +                VLOG_WARN_RL(&rl, "unexpected byte count from datapath");
 +            }
 +
 +            facet->dp_packet_count = stats->n_packets;
 +            facet->dp_byte_count = stats->n_bytes;
 +
 +            facet_update_time(p, facet, stats->used);
 +            facet_account(p, facet, stats->n_bytes);
 +            facet_push_stats(facet);
 +        } else {
 +            /* There's a flow in the datapath that we know nothing about.
 +             * Delete it. */
 +            COVERAGE_INC(facet_unexpected);
 +            dpif_flow_del(p->dpif, key, key_len, NULL);
 +        }
 +    }
 +    dpif_flow_dump_done(&dump);
 +}
 +
 +/* Calculates and returns the number of milliseconds of idle time after which
 + * facets should expire from the datapath and we should fold their statistics
 + * into their parent rules in userspace. */
 +static int
 +facet_max_idle(const struct ofproto_dpif *ofproto)
 +{
 +    /*
 +     * Idle time histogram.
 +     *
 +     * Most of the time a switch has a relatively small number of facets.  When
 +     * this is the case we might as well keep statistics for all of them in
 +     * userspace and to cache them in the kernel datapath for performance as
 +     * well.
 +     *
 +     * As the number of facets increases, the memory required to maintain
 +     * statistics about them in userspace and in the kernel becomes
 +     * significant.  However, with a large number of facets it is likely that
 +     * only a few of them are "heavy hitters" that consume a large amount of
 +     * bandwidth.  At this point, only heavy hitters are worth caching in the
 +     * kernel and maintaining in userspaces; other facets we can discard.
 +     *
 +     * The technique used to compute the idle time is to build a histogram with
 +     * N_BUCKETS buckets whose width is BUCKET_WIDTH msecs each.  Each facet
 +     * that is installed in the kernel gets dropped in the appropriate bucket.
 +     * After the histogram has been built, we compute the cutoff so that only
 +     * the most-recently-used 1% of facets (but at least 1000 flows) are kept
 +     * cached.  At least the most-recently-used bucket of facets is kept, so
 +     * actually an arbitrary number of facets can be kept in any given
 +     * expiration run (though the next run will delete most of those unless
 +     * they receive additional data).
 +     *
 +     * This requires a second pass through the facets, in addition to the pass
 +     * made by update_stats(), because the former function never looks
 +     * at uninstallable facets.
 +     */
 +    enum { BUCKET_WIDTH = ROUND_UP(100, TIME_UPDATE_INTERVAL) };
 +    enum { N_BUCKETS = 5000 / BUCKET_WIDTH };
 +    int buckets[N_BUCKETS] = { 0 };
 +    struct facet *facet;
 +    int total, bucket;
 +    long long int now;
 +    int i;
 +
 +    total = hmap_count(&ofproto->facets);
 +    if (total <= 1000) {
 +        return N_BUCKETS * BUCKET_WIDTH;
 +    }
 +
 +    /* Build histogram. */
 +    now = time_msec();
 +    HMAP_FOR_EACH (facet, hmap_node, &ofproto->facets) {
 +        long long int idle = now - facet->used;
 +        int bucket = (idle <= 0 ? 0
 +                      : idle >= BUCKET_WIDTH * N_BUCKETS ? N_BUCKETS - 1
 +                      : (unsigned int) idle / BUCKET_WIDTH);
 +        buckets[bucket]++;
 +    }
 +
 +    /* Find the first bucket whose flows should be expired. */
 +    for (bucket = 0; bucket < N_BUCKETS; bucket++) {
 +        if (buckets[bucket]) {
 +            int subtotal = 0;
 +            do {
 +                subtotal += buckets[bucket++];
 +            } while (bucket < N_BUCKETS && subtotal < MAX(1000, total / 100));
 +            break;
 +        }
 +    }
 +
 +    if (VLOG_IS_DBG_ENABLED()) {
 +        struct ds s;
 +
 +        ds_init(&s);
 +        ds_put_cstr(&s, "keep");
 +        for (i = 0; i < N_BUCKETS; i++) {
 +            if (i == bucket) {
 +                ds_put_cstr(&s, ", drop");
 +            }
 +            if (buckets[i]) {
 +                ds_put_format(&s, " %d:%d", i * BUCKET_WIDTH, buckets[i]);
 +            }
 +        }
 +        VLOG_INFO("%s: %s (msec:count)", ofproto->up.name, ds_cstr(&s));
 +        ds_destroy(&s);
 +    }
 +
 +    return bucket * BUCKET_WIDTH;
 +}
 +
 +static void
 +facet_active_timeout(struct ofproto_dpif *ofproto, struct facet *facet)
 +{
 +    if (ofproto->netflow && !facet_is_controller_flow(facet) &&
 +        netflow_active_timeout_expired(ofproto->netflow, &facet->nf_flow)) {
 +        struct ofexpired expired;
 +
 +        if (facet->installed) {
 +            struct dpif_flow_stats stats;
 +
 +            facet_put__(ofproto, facet, facet->actions, facet->actions_len,
 +                        &stats);
 +            facet_update_stats(ofproto, facet, &stats);
 +        }
 +
 +        expired.flow = facet->flow;
 +        expired.packet_count = facet->packet_count;
 +        expired.byte_count = facet->byte_count;
 +        expired.used = facet->used;
 +        netflow_expire(ofproto->netflow, &facet->nf_flow, &expired);
 +    }
 +}
 +
 +static void
 +expire_facets(struct ofproto_dpif *ofproto, int dp_max_idle)
 +{
 +    long long int cutoff = time_msec() - dp_max_idle;
 +    struct facet *facet, *next_facet;
 +
 +    HMAP_FOR_EACH_SAFE (facet, next_facet, hmap_node, &ofproto->facets) {
 +        facet_active_timeout(ofproto, facet);
 +        if (facet->used < cutoff) {
 +            facet_remove(ofproto, facet);
 +        }
 +    }
 +}
 +
 +/* If 'rule' is an OpenFlow rule, that has expired according to OpenFlow rules,
 + * then delete it entirely. */
 +static void
 +rule_expire(struct rule_dpif *rule)
 +{
 +    struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
 +    struct facet *facet, *next_facet;
 +    long long int now;
 +    uint8_t reason;
 +
 +    /* Has 'rule' expired? */
 +    now = time_msec();
 +    if (rule->up.hard_timeout
 +        && now > rule->up.created + rule->up.hard_timeout * 1000) {
 +        reason = OFPRR_HARD_TIMEOUT;
 +    } else if (rule->up.idle_timeout && list_is_empty(&rule->facets)
 +               && now > rule->used + rule->up.idle_timeout * 1000) {
 +        reason = OFPRR_IDLE_TIMEOUT;
 +    } else {
 +        return;
 +    }
 +
 +    COVERAGE_INC(ofproto_dpif_expired);
 +
 +    /* Update stats.  (This is a no-op if the rule expired due to an idle
 +     * timeout, because that only happens when the rule has no facets left.) */
 +    LIST_FOR_EACH_SAFE (facet, next_facet, list_node, &rule->facets) {
 +        facet_remove(ofproto, facet);
 +    }
 +
 +    /* Get rid of the rule. */
 +    ofproto_rule_expire(&rule->up, reason);
 +}
 +\f
 +/* Facets. */
 +
 +/* Creates and returns a new facet owned by 'rule', given a 'flow' and an
 + * example 'packet' within that flow.
 + *
 + * The caller must already have determined that no facet with an identical
 + * 'flow' exists in 'ofproto' and that 'flow' is the best match for 'rule' in
 + * the ofproto's classifier table. */
 +static struct facet *
 +facet_create(struct rule_dpif *rule, const struct flow *flow,
 +             const struct ofpbuf *packet)
 +{
 +    struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
 +    struct facet *facet;
 +
 +    facet = xzalloc(sizeof *facet);
 +    facet->used = time_msec();
 +    hmap_insert(&ofproto->facets, &facet->hmap_node, flow_hash(flow, 0));
 +    list_push_back(&rule->facets, &facet->list_node);
 +    facet->rule = rule;
 +    facet->flow = *flow;
 +    netflow_flow_init(&facet->nf_flow);
 +    netflow_flow_update_time(ofproto->netflow, &facet->nf_flow, facet->used);
 +
 +    facet_make_actions(ofproto, facet, packet);
 +
 +    return facet;
 +}
 +
 +static void
 +facet_free(struct facet *facet)
 +{
 +    free(facet->actions);
 +    free(facet);
 +}
 +
 +/* Executes, within 'ofproto', the 'n_actions' actions in 'actions' on
 + * 'packet', which arrived on 'in_port'.
 + *
 + * Takes ownership of 'packet'. */
 +static bool
 +execute_odp_actions(struct ofproto_dpif *ofproto, const struct flow *flow,
 +                    const struct nlattr *odp_actions, size_t actions_len,
 +                    struct ofpbuf *packet)
 +{
 +    if (actions_len == NLA_ALIGN(NLA_HDRLEN + sizeof(uint64_t))
 +        && odp_actions->nla_type == ODP_ACTION_ATTR_CONTROLLER) {
 +        /* As an optimization, avoid a round-trip from userspace to kernel to
 +         * userspace.  This also avoids possibly filling up kernel packet
 +         * buffers along the way. */
 +        struct dpif_upcall upcall;
 +
 +        upcall.type = DPIF_UC_ACTION;
 +        upcall.packet = packet;
 +        upcall.key = NULL;
 +        upcall.key_len = 0;
 +        upcall.userdata = nl_attr_get_u64(odp_actions);
 +        upcall.sample_pool = 0;
 +        upcall.actions = NULL;
 +        upcall.actions_len = 0;
 +
 +        send_packet_in(ofproto, &upcall, flow, false);
 +
 +        return true;
 +    } else {
 +        int error;
 +
 +        error = dpif_execute(ofproto->dpif, odp_actions, actions_len, packet);
 +        ofpbuf_delete(packet);
 +        return !error;
 +    }
 +}
 +
 +/* Executes the actions indicated by 'facet' on 'packet' and credits 'facet''s
 + * statistics appropriately.  'packet' must have at least sizeof(struct
 + * ofp_packet_in) bytes of headroom.
 + *
 + * For correct results, 'packet' must actually be in 'facet''s flow; that is,
 + * applying flow_extract() to 'packet' would yield the same flow as
 + * 'facet->flow'.
 + *
 + * 'facet' must have accurately composed ODP actions; that is, it must not be
 + * in need of revalidation.
 + *
 + * Takes ownership of 'packet'. */
 +static void
 +facet_execute(struct ofproto_dpif *ofproto, struct facet *facet,
 +              struct ofpbuf *packet)
 +{
 +    struct dpif_flow_stats stats;
 +
 +    assert(ofpbuf_headroom(packet) >= sizeof(struct ofp_packet_in));
 +
 +    flow_extract_stats(&facet->flow, packet, &stats);
 +    stats.used = time_msec();
 +    if (execute_odp_actions(ofproto, &facet->flow,
 +                            facet->actions, facet->actions_len, packet)) {
 +        facet_update_stats(ofproto, facet, &stats);
 +    }
 +}
 +
 +/* Remove 'facet' from 'ofproto' and free up the associated memory:
 + *
 + *   - If 'facet' was installed in the datapath, uninstalls it and updates its
 + *     rule's statistics, via facet_uninstall().
 + *
 + *   - Removes 'facet' from its rule and from ofproto->facets.
 + */
 +static void
 +facet_remove(struct ofproto_dpif *ofproto, struct facet *facet)
 +{
 +    facet_uninstall(ofproto, facet);
 +    facet_flush_stats(ofproto, facet);
 +    hmap_remove(&ofproto->facets, &facet->hmap_node);
 +    list_remove(&facet->list_node);
 +    facet_free(facet);
 +}
 +
 +/* Composes the ODP actions for 'facet' based on its rule's actions. */
 +static void
 +facet_make_actions(struct ofproto_dpif *p, struct facet *facet,
 +                   const struct ofpbuf *packet)
 +{
 +    const struct rule_dpif *rule = facet->rule;
 +    struct ofpbuf *odp_actions;
 +    struct action_xlate_ctx ctx;
 +
 +    action_xlate_ctx_init(&ctx, p, &facet->flow, packet);
 +    odp_actions = xlate_actions(&ctx, rule->up.actions, rule->up.n_actions);
 +    facet->tags = ctx.tags;
 +    facet->may_install = ctx.may_set_up_flow;
 +    facet->nf_flow.output_iface = ctx.nf_output_iface;
 +
 +    if (facet->actions_len != odp_actions->size
 +        || memcmp(facet->actions, odp_actions->data, odp_actions->size)) {
 +        free(facet->actions);
 +        facet->actions_len = odp_actions->size;
 +        facet->actions = xmemdup(odp_actions->data, odp_actions->size);
 +    }
 +
 +    ofpbuf_delete(odp_actions);
 +}
 +
 +static int
 +facet_put__(struct ofproto_dpif *ofproto, struct facet *facet,
 +            const struct nlattr *actions, size_t actions_len,
 +            struct dpif_flow_stats *stats)
 +{
 +    struct odputil_keybuf keybuf;
 +    enum dpif_flow_put_flags flags;
 +    struct ofpbuf key;
 +
 +    flags = DPIF_FP_CREATE | DPIF_FP_MODIFY;
 +    if (stats) {
 +        flags |= DPIF_FP_ZERO_STATS;
 +        facet->dp_packet_count = 0;
 +        facet->dp_byte_count = 0;
 +    }
 +
 +    ofpbuf_use_stack(&key, &keybuf, sizeof keybuf);
 +    odp_flow_key_from_flow(&key, &facet->flow);
 +
 +    return dpif_flow_put(ofproto->dpif, flags, key.data, key.size,
 +                         actions, actions_len, stats);
 +}
 +
 +/* If 'facet' is installable, inserts or re-inserts it into 'p''s datapath.  If
 + * 'zero_stats' is true, clears any existing statistics from the datapath for
 + * 'facet'. */
 +static void
 +facet_install(struct ofproto_dpif *p, struct facet *facet, bool zero_stats)
 +{
 +    struct dpif_flow_stats stats;
 +
 +    if (facet->may_install
 +        && !facet_put__(p, facet, facet->actions, facet->actions_len,
 +                        zero_stats ? &stats : NULL)) {
 +        facet->installed = true;
 +    }
 +}
 +
 +static void
 +facet_account(struct ofproto_dpif *ofproto,
 +              struct facet *facet, uint64_t extra_bytes)
 +{
 +    uint64_t total_bytes, n_bytes;
 +    struct ofbundle *in_bundle;
 +    const struct nlattr *a;
 +    tag_type dummy = 0;
 +    unsigned int left;
 +    int vlan;
 +
 +    total_bytes = facet->byte_count + extra_bytes;
 +    if (total_bytes <= facet->accounted_bytes) {
 +        return;
 +    }
 +    n_bytes = total_bytes - facet->accounted_bytes;
 +    facet->accounted_bytes = total_bytes;
 +
 +    /* Test that 'tags' is nonzero to ensure that only flows that include an
 +     * OFPP_NORMAL action are used for learning and bond slave rebalancing.
 +     * This works because OFPP_NORMAL always sets a nonzero tag value.
 +     *
 +     * 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 (!facet->tags
 +        || !is_admissible(ofproto, &facet->flow, false, &dummy,
 +                          &vlan, &in_bundle)) {
 +        return;
 +    }
 +
 +    update_learning_table(ofproto, &facet->flow, vlan, in_bundle);
 +
 +    if (!ofproto->has_bonded_bundles) {
 +        return;
 +    }
 +    NL_ATTR_FOR_EACH_UNSAFE (a, left, facet->actions, facet->actions_len) {
 +        if (nl_attr_type(a) == ODP_ACTION_ATTR_OUTPUT) {
 +            struct ofport_dpif *port;
 +
 +            port = get_odp_port(ofproto, nl_attr_get_u32(a));
 +            if (port && port->bundle && port->bundle->bond) {
 +                bond_account(port->bundle->bond, &facet->flow, vlan, n_bytes);
 +            }
 +        }
 +    }
 +}
 +
 +/* If 'rule' is installed in the datapath, uninstalls it. */
 +static void
 +facet_uninstall(struct ofproto_dpif *p, struct facet *facet)
 +{
 +    if (facet->installed) {
 +        struct odputil_keybuf keybuf;
 +        struct dpif_flow_stats stats;
 +        struct ofpbuf key;
 +
 +        ofpbuf_use_stack(&key, &keybuf, sizeof keybuf);
 +        odp_flow_key_from_flow(&key, &facet->flow);
 +
 +        if (!dpif_flow_del(p->dpif, key.data, key.size, &stats)) {
 +            facet_update_stats(p, facet, &stats);
 +        }
 +        facet->installed = false;
 +        facet->dp_packet_count = 0;
 +        facet->dp_byte_count = 0;
 +    } else {
 +        assert(facet->dp_packet_count == 0);
 +        assert(facet->dp_byte_count == 0);
 +    }
 +}
 +
 +/* Returns true if the only action for 'facet' is to send to the controller.
 + * (We don't report NetFlow expiration messages for such facets because they
 + * are just part of the control logic for the network, not real traffic). */
 +static bool
 +facet_is_controller_flow(struct facet *facet)
 +{
 +    return (facet
 +            && facet->rule->up.n_actions == 1
 +            && action_outputs_to_port(&facet->rule->up.actions[0],
 +                                      htons(OFPP_CONTROLLER)));
 +}
 +
 +/* Folds all of 'facet''s statistics into its rule.  Also updates the
 + * accounting ofhook and emits a NetFlow expiration if appropriate.  All of
 + * 'facet''s statistics in the datapath should have been zeroed and folded into
 + * its packet and byte counts before this function is called. */
 +static void
 +facet_flush_stats(struct ofproto_dpif *ofproto, struct facet *facet)
 +{
 +    assert(!facet->dp_byte_count);
 +    assert(!facet->dp_packet_count);
 +
 +    facet_push_stats(facet);
 +    facet_account(ofproto, facet, 0);
 +
 +    if (ofproto->netflow && !facet_is_controller_flow(facet)) {
 +        struct ofexpired expired;
 +        expired.flow = facet->flow;
 +        expired.packet_count = facet->packet_count;
 +        expired.byte_count = facet->byte_count;
 +        expired.used = facet->used;
 +        netflow_expire(ofproto->netflow, &facet->nf_flow, &expired);
 +    }
 +
 +    facet->rule->packet_count += facet->packet_count;
 +    facet->rule->byte_count += facet->byte_count;
 +
 +    /* Reset counters to prevent double counting if 'facet' ever gets
 +     * reinstalled. */
 +    facet->packet_count = 0;
 +    facet->byte_count = 0;
 +    facet->rs_packet_count = 0;
 +    facet->rs_byte_count = 0;
 +    facet->accounted_bytes = 0;
 +
 +    netflow_flow_clear(&facet->nf_flow);
 +}
 +
 +/* Searches 'ofproto''s table of facets for one exactly equal to 'flow'.
 + * Returns it if found, otherwise a null pointer.
 + *
 + * The returned facet might need revalidation; use facet_lookup_valid()
 + * instead if that is important. */
 +static struct facet *
 +facet_find(struct ofproto_dpif *ofproto, const struct flow *flow)
 +{
 +    struct facet *facet;
 +
 +    HMAP_FOR_EACH_WITH_HASH (facet, hmap_node, flow_hash(flow, 0),
 +                             &ofproto->facets) {
 +        if (flow_equal(flow, &facet->flow)) {
 +            return facet;
 +        }
 +    }
 +
 +    return NULL;
 +}
 +
 +/* Searches 'ofproto''s table of facets for one exactly equal to 'flow'.
 + * Returns it if found, otherwise a null pointer.
 + *
 + * The returned facet is guaranteed to be valid. */
 +static struct facet *
 +facet_lookup_valid(struct ofproto_dpif *ofproto, const struct flow *flow)
 +{
 +    struct facet *facet = facet_find(ofproto, flow);
 +
 +    /* The facet we found might not be valid, since we could be in need of
 +     * revalidation.  If it is not valid, don't return it. */
 +    if (facet
 +        && ofproto->need_revalidate
 +        && !facet_revalidate(ofproto, facet)) {
 +        COVERAGE_INC(facet_invalidated);
 +        return NULL;
 +    }
 +
 +    return facet;
 +}
 +
 +/* Re-searches 'ofproto''s classifier for a rule matching 'facet':
 + *
 + *   - If the rule found is different from 'facet''s current rule, moves
 + *     'facet' to the new rule and recompiles its actions.
 + *
 + *   - If the rule found is the same as 'facet''s current rule, leaves 'facet'
 + *     where it is and recompiles its actions anyway.
 + *
 + *   - If there is none, destroys 'facet'.
 + *
 + * Returns true if 'facet' still exists, false if it has been destroyed. */
 +static bool
 +facet_revalidate(struct ofproto_dpif *ofproto, struct facet *facet)
 +{
 +    struct action_xlate_ctx ctx;
 +    struct ofpbuf *odp_actions;
 +    struct rule_dpif *new_rule;
 +    bool actions_changed;
 +
 +    COVERAGE_INC(facet_revalidate);
 +
 +    /* Determine the new rule. */
 +    new_rule = rule_dpif_lookup(ofproto, &facet->flow);
 +    if (!new_rule) {
 +        /* No new rule, so delete the facet. */
 +        facet_remove(ofproto, facet);
 +        return false;
 +    }
 +
 +    /* Calculate new ODP actions.
 +     *
 +     * We do not modify any 'facet' state yet, because we might need to, e.g.,
 +     * emit a NetFlow expiration and, if so, we need to have the old state
 +     * around to properly compose it. */
 +    action_xlate_ctx_init(&ctx, ofproto, &facet->flow, NULL);
 +    odp_actions = xlate_actions(&ctx,
 +                                new_rule->up.actions, new_rule->up.n_actions);
 +    actions_changed = (facet->actions_len != odp_actions->size
 +                       || memcmp(facet->actions, odp_actions->data,
 +                                 facet->actions_len));
 +
 +    /* If the ODP actions changed or the installability changed, then we need
 +     * to talk to the datapath. */
 +    if (actions_changed || ctx.may_set_up_flow != facet->installed) {
 +        if (ctx.may_set_up_flow) {
 +            struct dpif_flow_stats stats;
 +
 +            facet_put__(ofproto, facet,
 +                        odp_actions->data, odp_actions->size, &stats);
 +            facet_update_stats(ofproto, facet, &stats);
 +        } else {
 +            facet_uninstall(ofproto, facet);
 +        }
 +
 +        /* The datapath flow is gone or has zeroed stats, so push stats out of
 +         * 'facet' into 'rule'. */
 +        facet_flush_stats(ofproto, facet);
 +    }
 +
 +    /* Update 'facet' now that we've taken care of all the old state. */
 +    facet->tags = ctx.tags;
 +    facet->nf_flow.output_iface = ctx.nf_output_iface;
 +    facet->may_install = ctx.may_set_up_flow;
 +    if (actions_changed) {
 +        free(facet->actions);
 +        facet->actions_len = odp_actions->size;
 +        facet->actions = xmemdup(odp_actions->data, odp_actions->size);
 +    }
 +    if (facet->rule != new_rule) {
 +        COVERAGE_INC(facet_changed_rule);
 +        list_remove(&facet->list_node);
 +        list_push_back(&new_rule->facets, &facet->list_node);
 +        facet->rule = new_rule;
 +        facet->used = new_rule->up.created;
 +        facet->rs_used = facet->used;
 +    }
 +
 +    ofpbuf_delete(odp_actions);
 +
 +    return true;
 +}
 +
 +/* Updates 'facet''s used time.  Caller is responsible for calling
 + * facet_push_stats() to update the flows which 'facet' resubmits into. */
 +static void
 +facet_update_time(struct ofproto_dpif *ofproto, struct facet *facet,
 +                  long long int used)
 +{
 +    if (used > facet->used) {
 +        facet->used = used;
 +        if (used > facet->rule->used) {
 +            facet->rule->used = used;
 +        }
 +        netflow_flow_update_time(ofproto->netflow, &facet->nf_flow, used);
 +    }
 +}
 +
 +/* Folds the statistics from 'stats' into the counters in 'facet'.
 + *
 + * Because of the meaning of a facet's counters, it only makes sense to do this
 + * if 'stats' are not tracked in the datapath, that is, if 'stats' represents a
 + * packet that was sent by hand or if it represents statistics that have been
 + * cleared out of the datapath. */
 +static void
 +facet_update_stats(struct ofproto_dpif *ofproto, struct facet *facet,
 +                   const struct dpif_flow_stats *stats)
 +{
 +    if (stats->n_packets || stats->used > facet->used) {
 +        facet_update_time(ofproto, facet, stats->used);
 +        facet->packet_count += stats->n_packets;
 +        facet->byte_count += stats->n_bytes;
 +        facet_push_stats(facet);
 +        netflow_flow_update_flags(&facet->nf_flow, stats->tcp_flags);
 +    }
 +}
 +
 +static void
 +facet_push_stats(struct facet *facet)
 +{
 +    uint64_t rs_packets, rs_bytes;
 +
 +    assert(facet->packet_count >= facet->rs_packet_count);
 +    assert(facet->byte_count >= facet->rs_byte_count);
 +    assert(facet->used >= facet->rs_used);
 +
 +    rs_packets = facet->packet_count - facet->rs_packet_count;
 +    rs_bytes = facet->byte_count - facet->rs_byte_count;
 +
 +    if (rs_packets || rs_bytes || facet->used > facet->rs_used) {
 +        facet->rs_packet_count = facet->packet_count;
 +        facet->rs_byte_count = facet->byte_count;
 +        facet->rs_used = facet->used;
 +
 +        flow_push_stats(facet->rule, &facet->flow,
 +                        rs_packets, rs_bytes, facet->used);
 +    }
 +}
 +
 +struct ofproto_push {
 +    struct action_xlate_ctx ctx;
 +    uint64_t packets;
 +    uint64_t bytes;
 +    long long int used;
 +};
 +
 +static void
 +push_resubmit(struct action_xlate_ctx *ctx, struct rule_dpif *rule)
 +{
 +    struct ofproto_push *push = CONTAINER_OF(ctx, struct ofproto_push, ctx);
 +
 +    if (rule) {
 +        rule->packet_count += push->packets;
 +        rule->byte_count += push->bytes;
 +        rule->used = MAX(push->used, rule->used);
 +    }
 +}
 +
 +/* Pushes flow statistics to the rules which 'flow' resubmits into given
 + * 'rule''s actions. */
 +static void
 +flow_push_stats(const struct rule_dpif *rule,
 +                struct flow *flow, uint64_t packets, uint64_t bytes,
 +                long long int used)
 +{
 +    struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
 +    struct ofproto_push push;
 +
 +    push.packets = packets;
 +    push.bytes = bytes;
 +    push.used = used;
 +
 +    action_xlate_ctx_init(&push.ctx, ofproto, flow, NULL);
 +    push.ctx.resubmit_hook = push_resubmit;
 +    ofpbuf_delete(xlate_actions(&push.ctx,
 +                                rule->up.actions, rule->up.n_actions));
 +}
 +\f
 +/* Rules. */
 +
 +static struct rule_dpif *
 +rule_dpif_lookup(struct ofproto_dpif *ofproto, const struct flow *flow)
 +{
 +    return rule_dpif_cast(rule_from_cls_rule(
 +                              classifier_lookup(&ofproto->up.tables[0],
 +                                                flow)));
 +}
 +
 +static struct rule *
 +rule_alloc(void)
 +{
 +    struct rule_dpif *rule = xmalloc(sizeof *rule);
 +    return &rule->up;
 +}
 +
 +static void
 +rule_dealloc(struct rule *rule_)
 +{
 +    struct rule_dpif *rule = rule_dpif_cast(rule_);
 +    free(rule);
 +}
 +
 +static int
 +rule_construct(struct rule *rule_)
 +{
 +    struct rule_dpif *rule = rule_dpif_cast(rule_);
 +    struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
 +    struct rule_dpif *old_rule;
 +    int error;
 +
 +    error = validate_actions(rule->up.actions, rule->up.n_actions,
 +                             &rule->up.cr.flow, ofproto->max_ports);
 +    if (error) {
 +        return error;
 +    }
 +
 +    old_rule = rule_dpif_cast(rule_from_cls_rule(classifier_find_rule_exactly(
 +                                                     &ofproto->up.tables[0],
 +                                                     &rule->up.cr)));
 +    if (old_rule) {
 +        ofproto_rule_destroy(&old_rule->up);
 +    }
 +
 +    rule->used = rule->up.created;
 +    rule->packet_count = 0;
 +    rule->byte_count = 0;
 +    list_init(&rule->facets);
 +    classifier_insert(&ofproto->up.tables[0], &rule->up.cr);
 +
 +    ofproto->need_revalidate = true;
 +
 +    return 0;
 +}
 +
 +static void
 +rule_destruct(struct rule *rule_)
 +{
 +    struct rule_dpif *rule = rule_dpif_cast(rule_);
 +    struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
 +    struct facet *facet, *next_facet;
 +
 +    classifier_remove(&ofproto->up.tables[0], &rule->up.cr);
 +    LIST_FOR_EACH_SAFE (facet, next_facet, list_node, &rule->facets) {
 +        facet_revalidate(ofproto, facet);
 +    }
 +    ofproto->need_revalidate = true;
 +}
 +
 +static void
 +rule_get_stats(struct rule *rule_, uint64_t *packets, uint64_t *bytes)
 +{
 +    struct rule_dpif *rule = rule_dpif_cast(rule_);
 +    struct facet *facet;
 +
 +    /* Start from historical data for 'rule' itself that are no longer tracked
 +     * in facets.  This counts, for example, facets that have expired. */
 +    *packets = rule->packet_count;
 +    *bytes = rule->byte_count;
 +
 +    /* Add any statistics that are tracked by facets.  This includes
 +     * statistical data recently updated by ofproto_update_stats() as well as
 +     * stats for packets that were executed "by hand" via dpif_execute(). */
 +    LIST_FOR_EACH (facet, list_node, &rule->facets) {
 +        *packets += facet->packet_count;
 +        *bytes += facet->byte_count;
 +    }
 +}
 +
 +static int
 +rule_execute(struct rule *rule_, struct flow *flow, struct ofpbuf *packet)
 +{
 +    struct rule_dpif *rule = rule_dpif_cast(rule_);
 +    struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
 +    struct action_xlate_ctx ctx;
 +    struct ofpbuf *odp_actions;
 +    struct facet *facet;
 +    size_t size;
 +
 +    /* First look for a related facet.  If we find one, account it to that. */
 +    facet = facet_lookup_valid(ofproto, flow);
 +    if (facet && facet->rule == rule) {
 +        facet_execute(ofproto, facet, packet);
 +        return 0;
 +    }
 +
 +    /* Otherwise, if 'rule' is in fact the correct rule for 'packet', then
 +     * create a new facet for it and use that. */
 +    if (rule_dpif_lookup(ofproto, flow) == rule) {
 +        facet = facet_create(rule, flow, packet);
 +        facet_execute(ofproto, facet, packet);
 +        facet_install(ofproto, facet, true);
 +        return 0;
 +    }
 +
 +    /* We can't account anything to a facet.  If we were to try, then that
 +     * facet would have a non-matching rule, busting our invariants. */
 +    action_xlate_ctx_init(&ctx, ofproto, flow, packet);
 +    odp_actions = xlate_actions(&ctx, rule->up.actions, rule->up.n_actions);
 +    size = packet->size;
 +    if (execute_odp_actions(ofproto, flow, odp_actions->data,
 +                            odp_actions->size, packet)) {
 +        rule->used = time_msec();
 +        rule->packet_count++;
 +        rule->byte_count += size;
 +        flow_push_stats(rule, flow, 1, size, rule->used);
 +    }
 +    ofpbuf_delete(odp_actions);
 +
 +    return 0;
 +}
 +
 +static int
 +rule_modify_actions(struct rule *rule_,
 +                    const union ofp_action *actions, size_t n_actions)
 +{
 +    struct rule_dpif *rule = rule_dpif_cast(rule_);
 +    struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
 +    int error;
 +
 +    error = validate_actions(actions, n_actions, &rule->up.cr.flow,
 +                             ofproto->max_ports);
 +    if (!error) {
 +        ofproto->need_revalidate = true;
 +    }
 +    return error;
 +}
 +\f
 +/* Sends 'packet' out of port 'odp_port' within 'ofproto'.  If 'vlan_tci' is
 + * zero the packet will not have any 802.1Q hader; if it is nonzero, then the
 + * packet will be sent with the VLAN TCI specified by 'vlan_tci & ~VLAN_CFI'.
 + *
 + * Returns 0 if successful, otherwise a positive errno value. */
 +static int
 +send_packet(struct ofproto_dpif *ofproto, uint32_t odp_port, uint16_t vlan_tci,
 +            const struct ofpbuf *packet)
 +{
 +    struct ofpbuf odp_actions;
 +    int error;
 +
 +    ofpbuf_init(&odp_actions, 32);
 +    if (vlan_tci != 0) {
 +        nl_msg_put_u32(&odp_actions, ODP_ACTION_ATTR_SET_DL_TCI,
 +                       ntohs(vlan_tci & ~VLAN_CFI));
 +    }
 +    nl_msg_put_u32(&odp_actions, ODP_ACTION_ATTR_OUTPUT, odp_port);
 +    error = dpif_execute(ofproto->dpif, odp_actions.data, odp_actions.size,
 +                         packet);
 +    ofpbuf_uninit(&odp_actions);
 +
 +    if (error) {
 +        VLOG_WARN_RL(&rl, "%s: failed to send packet on port %"PRIu32" (%s)",
 +                     ofproto->up.name, odp_port, strerror(error));
 +    }
 +    return error;
 +}
 +\f
 +/* OpenFlow to ODP action translation. */
 +
 +static void do_xlate_actions(const union ofp_action *in, size_t n_in,
 +                             struct action_xlate_ctx *ctx);
 +static bool xlate_normal(struct action_xlate_ctx *);
 +
 +static void
 +add_output_action(struct action_xlate_ctx *ctx, uint16_t ofp_port)
 +{
 +    const struct ofport_dpif *ofport = get_ofp_port(ctx->ofproto, ofp_port);
 +    uint16_t odp_port = ofp_port_to_odp_port(ofp_port);
 +
 +    if (ofport) {
 +        if (ofport->up.opp.config & htonl(OFPPC_NO_FWD)) {
 +            /* Forwarding disabled on port. */
 +            return;
 +        }
 +    } else {
 +        /*
 +         * We don't have an ofport record for this port, but it doesn't hurt to
 +         * allow forwarding to it anyhow.  Maybe such a port will appear later
 +         * and we're pre-populating the flow table.
 +         */
 +    }
 +
 +    nl_msg_put_u32(ctx->odp_actions, ODP_ACTION_ATTR_OUTPUT, odp_port);
 +    ctx->nf_output_iface = ofp_port;
 +}
 +
 +static void
 +xlate_table_action(struct action_xlate_ctx *ctx, uint16_t in_port)
 +{
 +    if (ctx->recurse < MAX_RESUBMIT_RECURSION) {
 +        struct rule_dpif *rule;
 +        uint16_t old_in_port;
 +
 +        /* Look up a flow with 'in_port' as the input port.  Then restore the
 +         * original input port (otherwise OFPP_NORMAL and OFPP_IN_PORT will
 +         * have surprising behavior). */
 +        old_in_port = ctx->flow.in_port;
 +        ctx->flow.in_port = in_port;
 +        rule = rule_dpif_lookup(ctx->ofproto, &ctx->flow);
 +        ctx->flow.in_port = old_in_port;
 +
 +        if (ctx->resubmit_hook) {
 +            ctx->resubmit_hook(ctx, rule);
 +        }
 +
 +        if (rule) {
 +            ctx->recurse++;
 +            do_xlate_actions(rule->up.actions, rule->up.n_actions, ctx);
 +            ctx->recurse--;
 +        }
 +    } else {
 +        static struct vlog_rate_limit recurse_rl = VLOG_RATE_LIMIT_INIT(1, 1);
 +
 +        VLOG_ERR_RL(&recurse_rl, "NXAST_RESUBMIT recursed over %d times",
 +                    MAX_RESUBMIT_RECURSION);
 +    }
 +}
 +
 +static void
 +flood_packets(struct ofproto_dpif *ofproto,
 +              uint16_t ofp_in_port, ovs_be32 mask,
 +              uint16_t *nf_output_iface, struct ofpbuf *odp_actions)
 +{
 +    struct ofport_dpif *ofport;
 +
 +    HMAP_FOR_EACH (ofport, up.hmap_node, &ofproto->up.ports) {
 +        uint16_t ofp_port = ofport->up.ofp_port;
 +        if (ofp_port != ofp_in_port && !(ofport->up.opp.config & mask)) {
 +            nl_msg_put_u32(odp_actions, ODP_ACTION_ATTR_OUTPUT,
 +                           ofport->odp_port);
 +        }
 +    }
 +    *nf_output_iface = NF_OUT_FLOOD;
 +}
 +
 +static void
 +xlate_output_action__(struct action_xlate_ctx *ctx,
 +                      uint16_t port, uint16_t max_len)
 +{
 +    uint16_t prev_nf_output_iface = ctx->nf_output_iface;
 +
 +    ctx->nf_output_iface = NF_OUT_DROP;
 +
 +    switch (port) {
 +    case OFPP_IN_PORT:
 +        add_output_action(ctx, ctx->flow.in_port);
 +        break;
 +    case OFPP_TABLE:
 +        xlate_table_action(ctx, ctx->flow.in_port);
 +        break;
 +    case OFPP_NORMAL:
 +        xlate_normal(ctx);
 +        break;
 +    case OFPP_FLOOD:
 +        flood_packets(ctx->ofproto, ctx->flow.in_port, htonl(OFPPC_NO_FLOOD),
 +                      &ctx->nf_output_iface, ctx->odp_actions);
 +        break;
 +    case OFPP_ALL:
 +        flood_packets(ctx->ofproto, ctx->flow.in_port, htonl(0),
 +                      &ctx->nf_output_iface, ctx->odp_actions);
 +        break;
 +    case OFPP_CONTROLLER:
 +        nl_msg_put_u64(ctx->odp_actions, ODP_ACTION_ATTR_CONTROLLER, max_len);
 +        break;
 +    case OFPP_LOCAL:
 +        add_output_action(ctx, OFPP_LOCAL);
 +        break;
 +    default:
 +        if (port != ctx->flow.in_port) {
 +            add_output_action(ctx, port);
 +        }
 +        break;
 +    }
 +
 +    if (prev_nf_output_iface == NF_OUT_FLOOD) {
 +        ctx->nf_output_iface = NF_OUT_FLOOD;
 +    } else if (ctx->nf_output_iface == NF_OUT_DROP) {
 +        ctx->nf_output_iface = prev_nf_output_iface;
 +    } else if (prev_nf_output_iface != NF_OUT_DROP &&
 +               ctx->nf_output_iface != NF_OUT_FLOOD) {
 +        ctx->nf_output_iface = NF_OUT_MULTI;
 +    }
 +}
 +
 +static void
 +xlate_output_action(struct action_xlate_ctx *ctx,
 +                    const struct ofp_action_output *oao)
 +{
 +    xlate_output_action__(ctx, ntohs(oao->port), ntohs(oao->max_len));
 +}
 +
 +/* If the final ODP action in 'ctx' is "pop priority", drop it, as an
 + * optimization, because we're going to add another action that sets the
 + * priority immediately after, or because there are no actions following the
 + * pop.  */
 +static void
 +remove_pop_action(struct action_xlate_ctx *ctx)
 +{
 +    if (ctx->odp_actions->size == ctx->last_pop_priority) {
 +        ctx->odp_actions->size -= NLA_ALIGN(NLA_HDRLEN);
 +        ctx->last_pop_priority = -1;
 +    }
 +}
 +
 +static void
 +add_pop_action(struct action_xlate_ctx *ctx)
 +{
 +    if (ctx->odp_actions->size != ctx->last_pop_priority) {
 +        nl_msg_put_flag(ctx->odp_actions, ODP_ACTION_ATTR_POP_PRIORITY);
 +        ctx->last_pop_priority = ctx->odp_actions->size;
 +    }
 +}
 +
 +static void
 +xlate_enqueue_action(struct action_xlate_ctx *ctx,
 +                     const struct ofp_action_enqueue *oae)
 +{
 +    uint16_t ofp_port, odp_port;
 +    uint32_t priority;
 +    int error;
 +
 +    error = dpif_queue_to_priority(ctx->ofproto->dpif, ntohl(oae->queue_id),
 +                                   &priority);
 +    if (error) {
 +        /* Fall back to ordinary output action. */
 +        xlate_output_action__(ctx, ntohs(oae->port), 0);
 +        return;
 +    }
 +
 +    /* Figure out ODP output port. */
 +    ofp_port = ntohs(oae->port);
 +    if (ofp_port == OFPP_IN_PORT) {
 +        ofp_port = ctx->flow.in_port;
 +    }
 +    odp_port = ofp_port_to_odp_port(ofp_port);
 +
 +    /* Add ODP actions. */
 +    remove_pop_action(ctx);
 +    nl_msg_put_u32(ctx->odp_actions, ODP_ACTION_ATTR_SET_PRIORITY, priority);
 +    add_output_action(ctx, odp_port);
 +    add_pop_action(ctx);
 +
 +    /* Update NetFlow output port. */
 +    if (ctx->nf_output_iface == NF_OUT_DROP) {
 +        ctx->nf_output_iface = odp_port;
 +    } else if (ctx->nf_output_iface != NF_OUT_FLOOD) {
 +        ctx->nf_output_iface = NF_OUT_MULTI;
 +    }
 +}
 +
 +static void
 +xlate_set_queue_action(struct action_xlate_ctx *ctx,
 +                       const struct nx_action_set_queue *nasq)
 +{
 +    uint32_t priority;
 +    int error;
 +
 +    error = dpif_queue_to_priority(ctx->ofproto->dpif, ntohl(nasq->queue_id),
 +                                   &priority);
 +    if (error) {
 +        /* Couldn't translate queue to a priority, so ignore.  A warning
 +         * has already been logged. */
 +        return;
 +    }
 +
 +    remove_pop_action(ctx);
 +    nl_msg_put_u32(ctx->odp_actions, ODP_ACTION_ATTR_SET_PRIORITY, priority);
 +}
 +
 +static void
 +xlate_set_dl_tci(struct action_xlate_ctx *ctx)
 +{
 +    ovs_be16 tci = ctx->flow.vlan_tci;
 +    if (!(tci & htons(VLAN_CFI))) {
 +        nl_msg_put_flag(ctx->odp_actions, ODP_ACTION_ATTR_STRIP_VLAN);
 +    } else {
 +        nl_msg_put_be16(ctx->odp_actions, ODP_ACTION_ATTR_SET_DL_TCI,
 +                        tci & ~htons(VLAN_CFI));
 +    }
 +}
 +
 +struct xlate_reg_state {
 +    ovs_be16 vlan_tci;
 +    ovs_be64 tun_id;
 +};
 +
 +static void
 +save_reg_state(const struct action_xlate_ctx *ctx,
 +               struct xlate_reg_state *state)
 +{
 +    state->vlan_tci = ctx->flow.vlan_tci;
 +    state->tun_id = ctx->flow.tun_id;
 +}
 +
 +static void
 +update_reg_state(struct action_xlate_ctx *ctx,
 +                 const struct xlate_reg_state *state)
 +{
 +    if (ctx->flow.vlan_tci != state->vlan_tci) {
 +        xlate_set_dl_tci(ctx);
 +    }
 +    if (ctx->flow.tun_id != state->tun_id) {
 +        nl_msg_put_be64(ctx->odp_actions,
 +                        ODP_ACTION_ATTR_SET_TUNNEL, ctx->flow.tun_id);
 +    }
 +}
 +
 +static void
 +xlate_autopath(struct action_xlate_ctx *ctx,
 +               const struct nx_action_autopath *naa)
 +{
 +    uint16_t ofp_port = ntohl(naa->id);
 +    struct ofport_dpif *port = get_ofp_port(ctx->ofproto, ofp_port);
 +
 +    if (!port || !port->bundle) {
 +        ofp_port = OFPP_NONE;
 +    } else if (port->bundle->bond) {
 +        /* Autopath does not support VLAN hashing. */
 +        struct ofport_dpif *slave = bond_choose_output_slave(
 +            port->bundle->bond, &ctx->flow, OFP_VLAN_NONE, &ctx->tags);
 +        if (slave) {
 +            ofp_port = slave->up.ofp_port;
 +        }
 +    }
 +    autopath_execute(naa, &ctx->flow, ofp_port);
 +}
 +
 +static void
 +xlate_nicira_action(struct action_xlate_ctx *ctx,
 +                    const struct nx_action_header *nah)
 +{
 +    const struct nx_action_resubmit *nar;
 +    const struct nx_action_set_tunnel *nast;
 +    const struct nx_action_set_queue *nasq;
 +    const struct nx_action_multipath *nam;
 +    const struct nx_action_autopath *naa;
 +    enum nx_action_subtype subtype = ntohs(nah->subtype);
 +    struct xlate_reg_state state;
 +    ovs_be64 tun_id;
 +
 +    assert(nah->vendor == htonl(NX_VENDOR_ID));
 +    switch (subtype) {
 +    case NXAST_RESUBMIT:
 +        nar = (const struct nx_action_resubmit *) nah;
 +        xlate_table_action(ctx, ntohs(nar->in_port));
 +        break;
 +
 +    case NXAST_SET_TUNNEL:
 +        nast = (const struct nx_action_set_tunnel *) nah;
 +        tun_id = htonll(ntohl(nast->tun_id));
 +        nl_msg_put_be64(ctx->odp_actions, ODP_ACTION_ATTR_SET_TUNNEL, tun_id);
 +        ctx->flow.tun_id = tun_id;
 +        break;
 +
 +    case NXAST_DROP_SPOOFED_ARP:
 +        if (ctx->flow.dl_type == htons(ETH_TYPE_ARP)) {
 +            nl_msg_put_flag(ctx->odp_actions,
 +                            ODP_ACTION_ATTR_DROP_SPOOFED_ARP);
 +        }
 +        break;
 +
 +    case NXAST_SET_QUEUE:
 +        nasq = (const struct nx_action_set_queue *) nah;
 +        xlate_set_queue_action(ctx, nasq);
 +        break;
 +
 +    case NXAST_POP_QUEUE:
 +        add_pop_action(ctx);
 +        break;
 +
 +    case NXAST_REG_MOVE:
 +        save_reg_state(ctx, &state);
 +        nxm_execute_reg_move((const struct nx_action_reg_move *) nah,
 +                             &ctx->flow);
 +        update_reg_state(ctx, &state);
 +        break;
 +
 +    case NXAST_REG_LOAD:
 +        save_reg_state(ctx, &state);
 +        nxm_execute_reg_load((const struct nx_action_reg_load *) nah,
 +                             &ctx->flow);
 +        update_reg_state(ctx, &state);
 +        break;
 +
 +    case NXAST_NOTE:
 +        /* Nothing to do. */
 +        break;
 +
 +    case NXAST_SET_TUNNEL64:
 +        tun_id = ((const struct nx_action_set_tunnel64 *) nah)->tun_id;
 +        nl_msg_put_be64(ctx->odp_actions, ODP_ACTION_ATTR_SET_TUNNEL, tun_id);
 +        ctx->flow.tun_id = tun_id;
 +        break;
 +
 +    case NXAST_MULTIPATH:
 +        nam = (const struct nx_action_multipath *) nah;
 +        multipath_execute(nam, &ctx->flow);
 +        break;
 +
 +    case NXAST_AUTOPATH:
 +        naa = (const struct nx_action_autopath *) nah;
 +        xlate_autopath(ctx, naa);
 +        break;
 +
 +    /* If you add a new action here that modifies flow data, don't forget to
 +     * update the flow key in ctx->flow at the same time. */
 +
 +    case NXAST_SNAT__OBSOLETE:
 +    default:
 +        VLOG_DBG_RL(&rl, "unknown Nicira action type %d", (int) subtype);
 +        break;
 +    }
 +}
 +
 +static void
 +do_xlate_actions(const union ofp_action *in, size_t n_in,
 +                 struct action_xlate_ctx *ctx)
 +{
 +    const struct ofport_dpif *port;
 +    struct actions_iterator iter;
 +    const union ofp_action *ia;
 +
 +    port = get_ofp_port(ctx->ofproto, ctx->flow.in_port);
 +    if (port
 +        && port->up.opp.config & htonl(OFPPC_NO_RECV | OFPPC_NO_RECV_STP) &&
 +        port->up.opp.config & (eth_addr_equals(ctx->flow.dl_dst, eth_addr_stp)
 +                               ? htonl(OFPPC_NO_RECV_STP)
 +                               : htonl(OFPPC_NO_RECV))) {
 +        /* Drop this flow. */
 +        return;
 +    }
 +
 +    for (ia = actions_first(&iter, in, n_in); ia; ia = actions_next(&iter)) {
 +        enum ofp_action_type type = ntohs(ia->type);
 +        const struct ofp_action_dl_addr *oada;
 +
 +        switch (type) {
 +        case OFPAT_OUTPUT:
 +            xlate_output_action(ctx, &ia->output);
 +            break;
 +
 +        case OFPAT_SET_VLAN_VID:
 +            ctx->flow.vlan_tci &= ~htons(VLAN_VID_MASK);
 +            ctx->flow.vlan_tci |= ia->vlan_vid.vlan_vid | htons(VLAN_CFI);
 +            xlate_set_dl_tci(ctx);
 +            break;
 +
 +        case OFPAT_SET_VLAN_PCP:
 +            ctx->flow.vlan_tci &= ~htons(VLAN_PCP_MASK);
 +            ctx->flow.vlan_tci |= htons(
 +                (ia->vlan_pcp.vlan_pcp << VLAN_PCP_SHIFT) | VLAN_CFI);
 +            xlate_set_dl_tci(ctx);
 +            break;
 +
 +        case OFPAT_STRIP_VLAN:
 +            ctx->flow.vlan_tci = htons(0);
 +            xlate_set_dl_tci(ctx);
 +            break;
 +
 +        case OFPAT_SET_DL_SRC:
 +            oada = ((struct ofp_action_dl_addr *) ia);
 +            nl_msg_put_unspec(ctx->odp_actions, ODP_ACTION_ATTR_SET_DL_SRC,
 +                              oada->dl_addr, ETH_ADDR_LEN);
 +            memcpy(ctx->flow.dl_src, oada->dl_addr, ETH_ADDR_LEN);
 +            break;
 +
 +        case OFPAT_SET_DL_DST:
 +            oada = ((struct ofp_action_dl_addr *) ia);
 +            nl_msg_put_unspec(ctx->odp_actions, ODP_ACTION_ATTR_SET_DL_DST,
 +                              oada->dl_addr, ETH_ADDR_LEN);
 +            memcpy(ctx->flow.dl_dst, oada->dl_addr, ETH_ADDR_LEN);
 +            break;
 +
 +        case OFPAT_SET_NW_SRC:
 +            nl_msg_put_be32(ctx->odp_actions, ODP_ACTION_ATTR_SET_NW_SRC,
 +                            ia->nw_addr.nw_addr);
 +            ctx->flow.nw_src = ia->nw_addr.nw_addr;
 +            break;
 +
 +        case OFPAT_SET_NW_DST:
 +            nl_msg_put_be32(ctx->odp_actions, ODP_ACTION_ATTR_SET_NW_DST,
 +                            ia->nw_addr.nw_addr);
 +            ctx->flow.nw_dst = ia->nw_addr.nw_addr;
 +            break;
 +
 +        case OFPAT_SET_NW_TOS:
 +            nl_msg_put_u8(ctx->odp_actions, ODP_ACTION_ATTR_SET_NW_TOS,
 +                          ia->nw_tos.nw_tos);
 +            ctx->flow.nw_tos = ia->nw_tos.nw_tos;
 +            break;
 +
 +        case OFPAT_SET_TP_SRC:
 +            nl_msg_put_be16(ctx->odp_actions, ODP_ACTION_ATTR_SET_TP_SRC,
 +                            ia->tp_port.tp_port);
 +            ctx->flow.tp_src = ia->tp_port.tp_port;
 +            break;
 +
 +        case OFPAT_SET_TP_DST:
 +            nl_msg_put_be16(ctx->odp_actions, ODP_ACTION_ATTR_SET_TP_DST,
 +                            ia->tp_port.tp_port);
 +            ctx->flow.tp_dst = ia->tp_port.tp_port;
 +            break;
 +
 +        case OFPAT_VENDOR:
 +            xlate_nicira_action(ctx, (const struct nx_action_header *) ia);
 +            break;
 +
 +        case OFPAT_ENQUEUE:
 +            xlate_enqueue_action(ctx, (const struct ofp_action_enqueue *) ia);
 +            break;
 +
 +        default:
 +            VLOG_DBG_RL(&rl, "unknown action type %d", (int) type);
 +            break;
 +        }
 +    }
 +}
 +
 +static void
 +action_xlate_ctx_init(struct action_xlate_ctx *ctx,
 +                      struct ofproto_dpif *ofproto, const struct flow *flow,
 +                      const struct ofpbuf *packet)
 +{
 +    ctx->ofproto = ofproto;
 +    ctx->flow = *flow;
 +    ctx->packet = packet;
 +    ctx->resubmit_hook = NULL;
 +}
 +
 +static struct ofpbuf *
 +xlate_actions(struct action_xlate_ctx *ctx,
 +              const union ofp_action *in, size_t n_in)
 +{
 +    COVERAGE_INC(ofproto_dpif_xlate);
 +
 +    ctx->odp_actions = ofpbuf_new(512);
 +    ctx->tags = 0;
 +    ctx->may_set_up_flow = true;
 +    ctx->nf_output_iface = NF_OUT_DROP;
 +    ctx->recurse = 0;
 +    ctx->last_pop_priority = -1;
 +
 +    if (process_special(ctx->ofproto, &ctx->flow, ctx->packet)) {
 +        ctx->may_set_up_flow = false;
 +    } else {
 +        do_xlate_actions(in, n_in, ctx);
 +    }
 +
 +    remove_pop_action(ctx);
 +
 +    /* Check with in-band control to see if we're allowed to set up this
 +     * flow. */
 +    if (!connmgr_may_set_up_flow(ctx->ofproto->up.connmgr, &ctx->flow,
 +                                 ctx->odp_actions->data,
 +                                 ctx->odp_actions->size)) {
 +        ctx->may_set_up_flow = false;
 +    }
 +
 +    return ctx->odp_actions;
 +}
 +\f
 +/* OFPP_NORMAL implementation. */
 +
 +struct dst {
 +    struct ofport_dpif *port;
 +    uint16_t vlan;
 +};
 +
 +struct dst_set {
 +    struct dst builtin[32];
 +    struct dst *dsts;
 +    size_t n, allocated;
 +};
 +
 +static void dst_set_init(struct dst_set *);
 +static void dst_set_add(struct dst_set *, const struct dst *);
 +static void dst_set_free(struct dst_set *);
 +
 +static struct ofport_dpif *ofbundle_get_a_port(const struct ofbundle *);
 +
 +static bool
 +set_dst(struct action_xlate_ctx *ctx, struct dst *dst,
 +        const struct ofbundle *in_bundle, const struct ofbundle *out_bundle)
 +{
 +    dst->vlan = (out_bundle->vlan >= 0 ? OFP_VLAN_NONE
 +                 : in_bundle->vlan >= 0 ? in_bundle->vlan
 +                 : ctx->flow.vlan_tci == 0 ? OFP_VLAN_NONE
 +                 : vlan_tci_to_vid(ctx->flow.vlan_tci));
 +
 +    dst->port = (!out_bundle->bond
 +                 ? ofbundle_get_a_port(out_bundle)
 +                 : bond_choose_output_slave(out_bundle->bond, &ctx->flow,
 +                                            dst->vlan, &ctx->tags));
 +
 +    return dst->port != NULL;
 +}
 +
 +static int
 +mirror_mask_ffs(mirror_mask_t mask)
 +{
 +    BUILD_ASSERT_DECL(sizeof(unsigned int) >= sizeof(mask));
 +    return ffs(mask);
 +}
 +
 +static void
 +dst_set_init(struct dst_set *set)
 +{
 +    set->dsts = set->builtin;
 +    set->n = 0;
 +    set->allocated = ARRAY_SIZE(set->builtin);
 +}
 +
 +static void
 +dst_set_add(struct dst_set *set, const struct dst *dst)
 +{
 +    if (set->n >= set->allocated) {
 +        size_t new_allocated;
 +        struct dst *new_dsts;
 +
 +        new_allocated = set->allocated * 2;
 +        new_dsts = xmalloc(new_allocated * sizeof *new_dsts);
 +        memcpy(new_dsts, set->dsts, set->n * sizeof *new_dsts);
 +
 +        dst_set_free(set);
 +
 +        set->dsts = new_dsts;
 +        set->allocated = new_allocated;
 +    }
 +    set->dsts[set->n++] = *dst;
 +}
 +
 +static void
 +dst_set_free(struct dst_set *set)
 +{
 +    if (set->dsts != set->builtin) {
 +        free(set->dsts);
 +    }
 +}
 +
 +static bool
 +dst_is_duplicate(const struct dst_set *set, const struct dst *test)
 +{
 +    size_t i;
 +    for (i = 0; i < set->n; i++) {
 +        if (set->dsts[i].vlan == test->vlan
 +            && set->dsts[i].port == test->port) {
 +            return true;
 +        }
 +    }
 +    return false;
 +}
 +
 +static bool
 +ofbundle_trunks_vlan(const struct ofbundle *bundle, uint16_t vlan)
 +{
 +    return bundle->vlan < 0 && vlan_bitmap_contains(bundle->trunks, vlan);
 +}
 +
 +static bool
 +ofbundle_includes_vlan(const struct ofbundle *bundle, uint16_t vlan)
 +{
 +    return vlan == bundle->vlan || ofbundle_trunks_vlan(bundle, vlan);
 +}
 +
 +/* Returns an arbitrary interface within 'bundle'. */
 +static struct ofport_dpif *
 +ofbundle_get_a_port(const struct ofbundle *bundle)
 +{
 +    return CONTAINER_OF(list_front(&bundle->ports),
 +                        struct ofport_dpif, bundle_node);
 +}
 +
 +static void
 +compose_dsts(struct action_xlate_ctx *ctx, uint16_t vlan,
 +             const struct ofbundle *in_bundle,
 +             const struct ofbundle *out_bundle, struct dst_set *set)
 +{
 +    struct dst dst;
 +
 +    if (out_bundle == OFBUNDLE_FLOOD) {
 +        struct ofbundle *bundle;
 +
 +        HMAP_FOR_EACH (bundle, hmap_node, &ctx->ofproto->bundles) {
 +            if (bundle != in_bundle
 +                && ofbundle_includes_vlan(bundle, vlan)
 +                && bundle->floodable
 +                && !bundle->mirror_out
 +                && set_dst(ctx, &dst, in_bundle, bundle)) {
 +                dst_set_add(set, &dst);
 +            }
 +        }
 +        ctx->nf_output_iface = NF_OUT_FLOOD;
 +    } else if (out_bundle && set_dst(ctx, &dst, in_bundle, out_bundle)) {
 +        dst_set_add(set, &dst);
 +        ctx->nf_output_iface = dst.port->odp_port;
 +    }
 +}
 +
 +static bool
 +vlan_is_mirrored(const struct ofmirror *m, int vlan)
 +{
 +    return vlan_bitmap_contains(m->vlans, vlan);
 +}
 +
 +static void
 +compose_mirror_dsts(struct action_xlate_ctx *ctx,
 +                    uint16_t vlan, const struct ofbundle *in_bundle,
 +                    struct dst_set *set)
 +{
 +    struct ofproto_dpif *ofproto = ctx->ofproto;
 +    mirror_mask_t mirrors;
 +    int flow_vlan;
 +    size_t i;
 +
 +    mirrors = in_bundle->src_mirrors;
 +    for (i = 0; i < set->n; i++) {
 +        mirrors |= set->dsts[i].port->bundle->dst_mirrors;
 +    }
 +
 +    if (!mirrors) {
 +        return;
 +    }
 +
 +    flow_vlan = vlan_tci_to_vid(ctx->flow.vlan_tci);
 +    if (flow_vlan == 0) {
 +        flow_vlan = OFP_VLAN_NONE;
 +    }
 +
 +    while (mirrors) {
 +        struct ofmirror *m = ofproto->mirrors[mirror_mask_ffs(mirrors) - 1];
 +        if (vlan_is_mirrored(m, vlan)) {
 +            struct dst dst;
 +
 +            if (m->out) {
 +                if (set_dst(ctx, &dst, in_bundle, m->out)
 +                    && !dst_is_duplicate(set, &dst)) {
 +                    dst_set_add(set, &dst);
 +                }
 +            } else {
 +                struct ofbundle *bundle;
 +
 +                HMAP_FOR_EACH (bundle, hmap_node, &ofproto->bundles) {
 +                    if (ofbundle_includes_vlan(bundle, m->out_vlan)
 +                        && set_dst(ctx, &dst, in_bundle, bundle))
 +                    {
 +                        if (bundle->vlan < 0) {
 +                            dst.vlan = m->out_vlan;
 +                        }
 +                        if (dst_is_duplicate(set, &dst)) {
 +                            continue;
 +                        }
 +
 +                        /* Use the vlan tag on the original flow instead of
 +                         * the one passed in the vlan parameter.  This ensures
 +                         * that we compare the vlan from before any implicit
 +                         * tagging tags place. This is necessary because
 +                         * dst->vlan is the final vlan, after removing implicit
 +                         * tags. */
 +                        if (bundle == in_bundle && dst.vlan == flow_vlan) {
 +                            /* Don't send out input port on same VLAN. */
 +                            continue;
 +                        }
 +                        dst_set_add(set, &dst);
 +                    }
 +                }
 +            }
 +        }
 +        mirrors &= mirrors - 1;
 +    }
 +}
 +
 +static void
 +compose_actions(struct action_xlate_ctx *ctx, uint16_t vlan,
 +                const struct ofbundle *in_bundle,
 +                const struct ofbundle *out_bundle)
 +{
 +    uint16_t initial_vlan, cur_vlan;
 +    const struct dst *dst;
 +    struct dst_set set;
 +
 +    dst_set_init(&set);
 +    compose_dsts(ctx, vlan, in_bundle, out_bundle, &set);
 +    compose_mirror_dsts(ctx, vlan, in_bundle, &set);
 +
 +    /* Output all the packets we can without having to change the VLAN. */
 +    initial_vlan = vlan_tci_to_vid(ctx->flow.vlan_tci);
 +    if (initial_vlan == 0) {
 +        initial_vlan = OFP_VLAN_NONE;
 +    }
 +    for (dst = set.dsts; dst < &set.dsts[set.n]; dst++) {
 +        if (dst->vlan != initial_vlan) {
 +            continue;
 +        }
 +        nl_msg_put_u32(ctx->odp_actions,
 +                       ODP_ACTION_ATTR_OUTPUT, dst->port->odp_port);
 +    }
 +
 +    /* Then output the rest. */
 +    cur_vlan = initial_vlan;
 +    for (dst = set.dsts; dst < &set.dsts[set.n]; dst++) {
 +        if (dst->vlan == initial_vlan) {
 +            continue;
 +        }
 +        if (dst->vlan != cur_vlan) {
 +            if (dst->vlan == OFP_VLAN_NONE) {
 +                nl_msg_put_flag(ctx->odp_actions, ODP_ACTION_ATTR_STRIP_VLAN);
 +            } else {
 +                ovs_be16 tci;
 +                tci = htons(dst->vlan & VLAN_VID_MASK);
 +                tci |= ctx->flow.vlan_tci & htons(VLAN_PCP_MASK);
 +                nl_msg_put_be16(ctx->odp_actions,
 +                                ODP_ACTION_ATTR_SET_DL_TCI, tci);
 +            }
 +            cur_vlan = dst->vlan;
 +        }
 +        nl_msg_put_u32(ctx->odp_actions,
 +                       ODP_ACTION_ATTR_OUTPUT, dst->port->odp_port);
 +    }
 +
 +    dst_set_free(&set);
 +}
 +
 +/* Returns the effective vlan of a packet, taking into account both the
 + * 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 ofproto_dpif *ofproto, const struct flow *flow,
 +              struct ofbundle *in_bundle, bool have_packet)
 +{
 +    int vlan = vlan_tci_to_vid(flow->vlan_tci);
 +    if (in_bundle->vlan >= 0) {
 +        if (vlan) {
 +            if (have_packet) {
 +                static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
 +                VLOG_WARN_RL(&rl, "bridge %s: dropping VLAN %d tagged "
 +                             "packet received on port %s configured with "
 +                             "implicit VLAN %"PRIu16,
 +                             ofproto->up.name, vlan,
 +                             in_bundle->name, in_bundle->vlan);
 +            }
 +            return -1;
 +        }
 +        vlan = in_bundle->vlan;
 +    } else {
 +        if (!ofbundle_includes_vlan(in_bundle, vlan)) {
 +            if (have_packet) {
 +                static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
 +                VLOG_WARN_RL(&rl, "bridge %s: dropping VLAN %d tagged "
 +                             "packet received on port %s not configured for "
 +                             "trunking VLAN %d",
 +                             ofproto->up.name, vlan, in_bundle->name, vlan);
 +            }
 +            return -1;
 +        }
 +    }
 +
 +    return vlan;
 +}
 +
 +/* A VM broadcasts a gratuitous ARP to indicate that it has resumed after
 + * 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 struct flow *flow)
 +{
 +    return (flow->dl_type == htons(ETH_TYPE_ARP)
 +            && eth_addr_is_broadcast(flow->dl_dst)
 +            && (flow->nw_proto == ARP_OP_REPLY
 +                || (flow->nw_proto == ARP_OP_REQUEST
 +                    && flow->nw_src == flow->nw_dst)));
 +}
 +
 +static void
 +update_learning_table(struct ofproto_dpif *ofproto,
 +                      const struct flow *flow, int vlan,
 +                      struct ofbundle *in_bundle)
 +{
 +    struct mac_entry *mac;
 +
 +    if (!mac_learning_may_learn(ofproto->ml, flow->dl_src, vlan)) {
 +        return;
 +    }
 +
 +    mac = mac_learning_insert(ofproto->ml, flow->dl_src, vlan);
 +    if (is_gratuitous_arp(flow)) {
 +        /* We don't want to learn from gratuitous ARP packets that are
 +         * reflected back over bond slaves so we lock the learning table. */
 +        if (!in_bundle->bond) {
 +            mac_entry_set_grat_arp_lock(mac);
 +        } else if (mac_entry_is_grat_arp_locked(mac)) {
 +            return;
 +        }
 +    }
 +
 +    if (mac_entry_is_new(mac) || mac->port.p != in_bundle) {
 +        /* The log messages here could actually be useful in debugging,
 +         * so keep the rate limit relatively high. */
 +        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(30, 300);
 +        VLOG_DBG_RL(&rl, "bridge %s: learned that "ETH_ADDR_FMT" is "
 +                    "on port %s in VLAN %d",
 +                    ofproto->up.name, ETH_ADDR_ARGS(flow->dl_src),
 +                    in_bundle->name, vlan);
 +
 +        mac->port.p = in_bundle;
 +        tag_set_add(&ofproto->revalidate_set,
 +                    mac_learning_changed(ofproto->ml, mac));
 +    }
 +}
 +
 +/* Determines whether packets in 'flow' within 'br' should be forwarded or
 + * dropped.  Returns true if they may be forwarded, false if they should be
 + * dropped.
 + *
 + * If 'have_packet' is true, it indicates that the caller is processing a
 + * received packet.  If 'have_packet' is false, then the caller is just
 + * revalidating an existing flow because configuration has changed.  Either
 + * way, 'have_packet' only affects logging (there is no point in logging errors
 + * during revalidation).
 + *
 + * Sets '*in_portp' to the input port.  This will be a null pointer if
 + * flow->in_port does not designate a known input port (in which case
 + * is_admissible() returns false).
 + *
 + * When returning true, sets '*vlanp' to the effective VLAN of the input
 + * packet, as returned by flow_get_vlan().
 + *
 + * May also add tags to '*tags', although the current implementation only does
 + * so in one special case.
 + */
 +static bool
 +is_admissible(struct ofproto_dpif *ofproto, const struct flow *flow,
 +              bool have_packet,
 +              tag_type *tags, int *vlanp, struct ofbundle **in_bundlep)
 +{
 +    struct ofport_dpif *in_port;
 +    struct ofbundle *in_bundle;
 +    int vlan;
 +
 +    /* Find the port and bundle for the received packet. */
 +    in_port = get_ofp_port(ofproto, flow->in_port);
 +    *in_bundlep = in_bundle = in_port->bundle;
 +    if (!in_port || !in_bundle) {
 +        /* No interface?  Something fishy... */
 +        if (have_packet) {
 +            /* Odd.  A few possible reasons here:
 +             *
 +             * - We deleted a port but there are still a few packets queued up
 +             *   from it.
 +             *
 +             * - Someone externally added a port (e.g. "ovs-dpctl add-if") that
 +             *   we don't know about.
 +             *
 +             * - Packet arrived on the local port but the local port is not
 +             *   part of a bundle.
 +             */
 +            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
 +
 +            VLOG_WARN_RL(&rl, "bridge %s: received packet on unknown "
 +                         "port %"PRIu16,
 +                         ofproto->up.name, flow->in_port);
 +        }
 +        return false;
 +    }
 +    *vlanp = vlan = flow_get_vlan(ofproto, flow, in_bundle, have_packet);
 +    if (vlan < 0) {
 +        return false;
 +    }
 +
 +    /* Drop frames for reserved multicast addresses. */
 +    if (eth_addr_is_reserved(flow->dl_dst)) {
 +        return false;
 +    }
 +
 +    /* Drop frames on bundles reserved for mirroring. */
 +    if (in_bundle->mirror_out) {
 +        if (have_packet) {
 +            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
 +            VLOG_WARN_RL(&rl, "bridge %s: dropping packet received on port "
 +                         "%s, which is reserved exclusively for mirroring",
 +                         ofproto->up.name, in_bundle->name);
 +        }
 +        return false;
 +    }
 +
 +    if (in_bundle->bond) {
 +        struct mac_entry *mac;
 +
 +        switch (bond_check_admissibility(in_bundle->bond, in_port,
 +                                         flow->dl_dst, tags)) {
 +        case BV_ACCEPT:
 +            break;
 +
 +        case BV_DROP:
 +            return false;
 +
 +        case BV_DROP_IF_MOVED:
 +            mac = mac_learning_lookup(ofproto->ml, flow->dl_src, vlan, NULL);
 +            if (mac && mac->port.p != in_bundle &&
 +                (!is_gratuitous_arp(flow)
 +                 || mac_entry_is_grat_arp_locked(mac))) {
 +                return false;
 +            }
 +            break;
 +        }
 +    }
 +
 +    return true;
 +}
 +
 +/* If the composed actions may be applied to any packet in the given 'flow',
 + * returns true.  Otherwise, the actions should only be applied to 'packet', or
 + * not at all, if 'packet' was NULL. */
 +static bool
 +xlate_normal(struct action_xlate_ctx *ctx)
 +{
 +    struct ofbundle *in_bundle;
 +    struct ofbundle *out_bundle;
 +    struct mac_entry *mac;
 +    int vlan;
 +
 +    /* Check whether we should drop packets in this flow. */
 +    if (!is_admissible(ctx->ofproto, &ctx->flow, ctx->packet != NULL,
 +                       &ctx->tags, &vlan, &in_bundle)) {
 +        out_bundle = NULL;
 +        goto done;
 +    }
 +
 +    /* Learn source MAC (but don't try to learn from revalidation). */
 +    if (ctx->packet) {
 +        update_learning_table(ctx->ofproto, &ctx->flow, vlan, in_bundle);
 +    }
 +
 +    /* Determine output bundle. */
 +    mac = mac_learning_lookup(ctx->ofproto->ml, ctx->flow.dl_dst, vlan,
 +                              &ctx->tags);
 +    if (mac) {
 +        out_bundle = mac->port.p;
 +    } else if (!ctx->packet && !eth_addr_is_multicast(ctx->flow.dl_dst)) {
 +        /* If we are revalidating but don't have a learning entry then eject
 +         * the flow.  Installing a flow that floods packets opens up a window
 +         * of time where we could learn from a packet reflected on a bond and
 +         * blackhole packets before the learning table is updated to reflect
 +         * the correct port. */
 +        return false;
 +    } else {
 +        out_bundle = OFBUNDLE_FLOOD;
 +    }
 +
 +    /* Don't send packets out their input bundles. */
 +    if (in_bundle == out_bundle) {
 +        out_bundle = NULL;
 +    }
 +
 +done:
 +    if (in_bundle) {
 +        compose_actions(ctx, vlan, in_bundle, out_bundle);
 +    }
 +
 +    return true;
 +}
 +\f
 +static bool
 +get_drop_frags(struct ofproto *ofproto_)
 +{
 +    struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
 +    bool drop_frags;
 +
 +    dpif_get_drop_frags(ofproto->dpif, &drop_frags);
 +    return drop_frags;
 +}
 +
 +static void
 +set_drop_frags(struct ofproto *ofproto_, bool drop_frags)
 +{
 +    struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
 +
 +    dpif_set_drop_frags(ofproto->dpif, drop_frags);
 +}
 +
 +static int
 +packet_out(struct ofproto *ofproto_, struct ofpbuf *packet,
 +           const struct flow *flow,
 +           const union ofp_action *ofp_actions, size_t n_ofp_actions)
 +{
 +    struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
 +    int error;
 +
 +    error = validate_actions(ofp_actions, n_ofp_actions, flow,
 +                             ofproto->max_ports);
 +    if (!error) {
 +        struct action_xlate_ctx ctx;
 +        struct ofpbuf *odp_actions;
 +
 +        action_xlate_ctx_init(&ctx, ofproto, flow, packet);
 +        odp_actions = xlate_actions(&ctx, ofp_actions, n_ofp_actions);
 +        dpif_execute(ofproto->dpif, odp_actions->data, odp_actions->size,
 +                     packet);
 +        ofpbuf_delete(odp_actions);
 +    }
 +    return error;
 +}
 +
 +static void
 +get_netflow_ids(const struct ofproto *ofproto_,
 +                uint8_t *engine_type, uint8_t *engine_id)
 +{
 +    struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
 +
 +    dpif_get_netflow_ids(ofproto->dpif, engine_type, engine_id);
 +}
 +\f
 +static struct ofproto_dpif *
 +ofproto_dpif_lookup(const char *name)
 +{
 +    struct ofproto *ofproto = ofproto_lookup(name);
 +    return (ofproto && ofproto->ofproto_class == &ofproto_dpif_class
 +            ? ofproto_dpif_cast(ofproto)
 +            : NULL);
 +}
 +
 +static void
 +ofproto_unixctl_fdb_show(struct unixctl_conn *conn,
 +                         const char *args, void *aux OVS_UNUSED)
 +{
 +    struct ds ds = DS_EMPTY_INITIALIZER;
 +    const struct ofproto_dpif *ofproto;
 +    const struct mac_entry *e;
 +
 +    ofproto = ofproto_dpif_lookup(args);
 +    if (!ofproto) {
 +        unixctl_command_reply(conn, 501, "no such bridge");
 +        return;
 +    }
 +
 +    ds_put_cstr(&ds, " port  VLAN  MAC                Age\n");
 +    LIST_FOR_EACH (e, lru_node, &ofproto->ml->lrus) {
 +        struct ofbundle *bundle = e->port.p;
 +        ds_put_format(&ds, "%5d  %4d  "ETH_ADDR_FMT"  %3d\n",
 +                      ofbundle_get_a_port(bundle)->odp_port,
 +                      e->vlan, ETH_ADDR_ARGS(e->mac), mac_entry_age(e));
 +    }
 +    unixctl_command_reply(conn, 200, ds_cstr(&ds));
 +    ds_destroy(&ds);
 +}
 +
 +struct ofproto_trace {
 +    struct action_xlate_ctx ctx;
 +    struct flow flow;
 +    struct ds *result;
 +};
 +
 +static void
 +trace_format_rule(struct ds *result, int level, const struct rule *rule)
 +{
 +    ds_put_char_multiple(result, '\t', level);
 +    if (!rule) {
 +        ds_put_cstr(result, "No match\n");
 +        return;
 +    }
 +
 +    ds_put_format(result, "Rule: cookie=%#"PRIx64" ",
 +                  ntohll(rule->flow_cookie));
 +    cls_rule_format(&rule->cr, result);
 +    ds_put_char(result, '\n');
 +
 +    ds_put_char_multiple(result, '\t', level);
 +    ds_put_cstr(result, "OpenFlow ");
 +    ofp_print_actions(result, (const struct ofp_action_header *) rule->actions,
 +                      rule->n_actions * sizeof *rule->actions);
 +    ds_put_char(result, '\n');
 +}
 +
 +static void
 +trace_format_flow(struct ds *result, int level, const char *title,
 +                 struct ofproto_trace *trace)
 +{
 +    ds_put_char_multiple(result, '\t', level);
 +    ds_put_format(result, "%s: ", title);
 +    if (flow_equal(&trace->ctx.flow, &trace->flow)) {
 +        ds_put_cstr(result, "unchanged");
 +    } else {
 +        flow_format(result, &trace->ctx.flow);
 +        trace->flow = trace->ctx.flow;
 +    }
 +    ds_put_char(result, '\n');
 +}
 +
 +static void
 +trace_resubmit(struct action_xlate_ctx *ctx, struct rule_dpif *rule)
 +{
 +    struct ofproto_trace *trace = CONTAINER_OF(ctx, struct ofproto_trace, ctx);
 +    struct ds *result = trace->result;
 +
 +    ds_put_char(result, '\n');
 +    trace_format_flow(result, ctx->recurse + 1, "Resubmitted flow", trace);
 +    trace_format_rule(result, ctx->recurse + 1, &rule->up);
 +}
 +
 +static void
 +ofproto_unixctl_trace(struct unixctl_conn *conn, const char *args_,
 +                      void *aux OVS_UNUSED)
 +{
 +    char *dpname, *in_port_s, *tun_id_s, *packet_s;
 +    char *args = xstrdup(args_);
 +    char *save_ptr = NULL;
 +    struct ofproto_dpif *ofproto;
 +    struct ofpbuf packet;
 +    struct rule_dpif *rule;
 +    struct ds result;
 +    struct flow flow;
 +    uint16_t in_port;
 +    ovs_be64 tun_id;
 +    char *s;
 +
 +    ofpbuf_init(&packet, strlen(args) / 2);
 +    ds_init(&result);
 +
 +    dpname = strtok_r(args, " ", &save_ptr);
 +    tun_id_s = strtok_r(NULL, " ", &save_ptr);
 +    in_port_s = strtok_r(NULL, " ", &save_ptr);
 +    packet_s = strtok_r(NULL, "", &save_ptr); /* Get entire rest of line. */
 +    if (!dpname || !in_port_s || !packet_s) {
 +        unixctl_command_reply(conn, 501, "Bad command syntax");
 +        goto exit;
 +    }
 +
 +    ofproto = ofproto_dpif_lookup(dpname);
 +    if (!ofproto) {
 +        unixctl_command_reply(conn, 501, "Unknown ofproto (use ofproto/list "
 +                              "for help)");
 +        goto exit;
 +    }
 +
 +    tun_id = htonll(strtoull(tun_id_s, NULL, 0));
 +    in_port = ofp_port_to_odp_port(atoi(in_port_s));
 +
 +    packet_s = ofpbuf_put_hex(&packet, packet_s, NULL);
 +    packet_s += strspn(packet_s, " ");
 +    if (*packet_s != '\0') {
 +        unixctl_command_reply(conn, 501, "Trailing garbage in command");
 +        goto exit;
 +    }
 +    if (packet.size < ETH_HEADER_LEN) {
 +        unixctl_command_reply(conn, 501, "Packet data too short for Ethernet");
 +        goto exit;
 +    }
 +
 +    ds_put_cstr(&result, "Packet: ");
 +    s = ofp_packet_to_string(packet.data, packet.size, packet.size);
 +    ds_put_cstr(&result, s);
 +    free(s);
 +
 +    flow_extract(&packet, tun_id, in_port, &flow);
 +    ds_put_cstr(&result, "Flow: ");
 +    flow_format(&result, &flow);
 +    ds_put_char(&result, '\n');
 +
 +    rule = rule_dpif_lookup(ofproto, &flow);
 +    trace_format_rule(&result, 0, &rule->up);
 +    if (rule) {
 +        struct ofproto_trace trace;
 +        struct ofpbuf *odp_actions;
 +
 +        trace.result = &result;
 +        trace.flow = flow;
 +        action_xlate_ctx_init(&trace.ctx, ofproto, &flow, &packet);
 +        trace.ctx.resubmit_hook = trace_resubmit;
 +        odp_actions = xlate_actions(&trace.ctx,
 +                                    rule->up.actions, rule->up.n_actions);
 +
 +        ds_put_char(&result, '\n');
 +        trace_format_flow(&result, 0, "Final flow", &trace);
 +        ds_put_cstr(&result, "Datapath actions: ");
 +        format_odp_actions(&result, odp_actions->data, odp_actions->size);
 +        ofpbuf_delete(odp_actions);
 +    }
 +
 +    unixctl_command_reply(conn, 200, ds_cstr(&result));
 +
 +exit:
 +    ds_destroy(&result);
 +    ofpbuf_uninit(&packet);
 +    free(args);
 +}
 +
 +static void
 +ofproto_dpif_unixctl_init(void)
 +{
 +    static bool registered;
 +    if (registered) {
 +        return;
 +    }
 +    registered = true;
 +
 +    unixctl_command_register("ofproto/trace", ofproto_unixctl_trace, NULL);
 +    unixctl_command_register("fdb/show", ofproto_unixctl_fdb_show, NULL);
 +}
 +\f
 +const struct ofproto_class ofproto_dpif_class = {
 +    enumerate_types,
 +    enumerate_names,
 +    del,
 +    alloc,
 +    construct,
 +    destruct,
 +    dealloc,
 +    run,
 +    wait,
 +    flush,
 +    get_features,
 +    get_tables,
 +    port_alloc,
 +    port_construct,
 +    port_destruct,
 +    port_dealloc,
 +    port_modified,
 +    port_reconfigured,
 +    port_query_by_name,
 +    port_add,
 +    port_del,
 +    port_dump_start,
 +    port_dump_next,
 +    port_dump_done,
 +    port_poll,
 +    port_poll_wait,
 +    port_is_lacp_current,
 +    rule_alloc,
 +    rule_construct,
 +    rule_destruct,
 +    rule_dealloc,
 +    rule_get_stats,
 +    rule_execute,
 +    rule_modify_actions,
 +    get_drop_frags,
 +    set_drop_frags,
 +    packet_out,
 +    set_netflow,
 +    get_netflow_ids,
 +    set_sflow,
 +    set_cfm,
 +    get_cfm,
 +    bundle_set,
 +    bundle_remove,
 +    mirror_set,
 +    set_flood_vlans,
 +    is_mirror_output_bundle,
 +};
diff --combined ofproto/ofproto.c
  #include "ofproto.h"
  #include <errno.h>
  #include <inttypes.h>
 -#include <sys/socket.h>
 -#include <net/if.h>
 -#include <netinet/in.h>
  #include <stdbool.h>
  #include <stdlib.h>
 -#include "autopath.h"
  #include "byte-order.h"
 -#include "cfm.h"
  #include "classifier.h"
  #include "connmgr.h"
  #include "coverage.h"
 -#include "dpif.h"
  #include "dynamic-string.h"
 -#include "fail-open.h"
  #include "hash.h"
  #include "hmap.h"
 -#include "in-band.h"
 -#include "mac-learning.h"
 -#include "multipath.h"
  #include "netdev.h"
 -#include "netflow.h"
 -#include "netlink.h"
  #include "nx-match.h"
 -#include "odp-util.h"
  #include "ofp-print.h"
  #include "ofp-util.h"
 -#include "ofproto-sflow.h"
  #include "ofpbuf.h"
  #include "openflow/nicira-ext.h"
  #include "openflow/openflow.h"
 -#include "openvswitch/datapath-protocol.h"
  #include "packets.h"
  #include "pinsched.h"
  #include "pktbuf.h"
  #include "poll-loop.h"
 -#include "rconn.h"
 +#include "private.h"
  #include "shash.h"
  #include "sset.h"
 -#include "stream-ssl.h"
 -#include "tag.h"
 -#include "timer.h"
  #include "timeval.h"
  #include "unaligned.h"
  #include "unixctl.h"
 -#include "vconn.h"
  #include "vlog.h"
  
  VLOG_DEFINE_THIS_MODULE(ofproto);
  
 -COVERAGE_DEFINE(facet_changed_rule);
 -COVERAGE_DEFINE(facet_revalidate);
 -COVERAGE_DEFINE(odp_overflow);
  COVERAGE_DEFINE(ofproto_agg_request);
 -COVERAGE_DEFINE(ofproto_costly_flags);
 -COVERAGE_DEFINE(ofproto_ctlr_action);
 -COVERAGE_DEFINE(ofproto_del_rule);
  COVERAGE_DEFINE(ofproto_error);
 -COVERAGE_DEFINE(ofproto_expiration);
 -COVERAGE_DEFINE(ofproto_expired);
  COVERAGE_DEFINE(ofproto_flows_req);
  COVERAGE_DEFINE(ofproto_flush);
 -COVERAGE_DEFINE(ofproto_invalidated);
  COVERAGE_DEFINE(ofproto_no_packet_in);
 -COVERAGE_DEFINE(ofproto_ofp2odp);
 -COVERAGE_DEFINE(ofproto_packet_in);
  COVERAGE_DEFINE(ofproto_packet_out);
  COVERAGE_DEFINE(ofproto_queue_req);
  COVERAGE_DEFINE(ofproto_recv_openflow);
  COVERAGE_DEFINE(ofproto_reinit_ports);
 -COVERAGE_DEFINE(ofproto_unexpected_rule);
  COVERAGE_DEFINE(ofproto_uninstallable);
  COVERAGE_DEFINE(ofproto_update_port);
  
 -/* Maximum depth of flow table recursion (due to NXAST_RESUBMIT actions) in a
 - * flow translation. */
 -#define MAX_RESUBMIT_RECURSION 16
 +static void ofport_destroy__(struct ofport *);
 +static void ofport_destroy(struct ofport *);
  
 -struct rule;
 +static int rule_create(struct ofproto *, const struct cls_rule *,
 +                       const union ofp_action *, size_t n_actions,
 +                       uint16_t idle_timeout, uint16_t hard_timeout,
 +                       ovs_be64 flow_cookie, bool send_flow_removed,
 +                       struct rule **rulep);
  
 -struct ofport {
 -    struct hmap_node hmap_node; /* In struct ofproto's "ports" hmap. */
 -    struct netdev *netdev;
 -    struct ofp_phy_port opp;    /* In host byte order. */
 -    uint16_t odp_port;
 -    struct cfm *cfm;            /* Connectivity Fault Management, if any. */
 -};
 +static uint64_t pick_datapath_id(const struct ofproto *);
 +static uint64_t pick_fallback_dpid(void);
  
 -static void ofport_free(struct ofport *);
 -static void ofport_run(struct ofproto *, struct ofport *);
 -static void ofport_wait(struct ofport *);
 +static void ofproto_destroy__(struct ofproto *);
 +static void ofproto_flush_flows__(struct ofproto *);
  
 -struct action_xlate_ctx {
 -/* action_xlate_ctx_init() initializes these members. */
 +static void ofproto_rule_destroy__(struct rule *);
 +static void ofproto_rule_send_removed(struct rule *, uint8_t reason);
  
 -    /* The ofproto. */
 -    struct ofproto *ofproto;
 +static void handle_openflow(struct ofconn *, struct ofpbuf *);
  
 -    /* Flow to which the OpenFlow actions apply.  xlate_actions() will modify
 -     * this flow when actions change header fields. */
 -    struct flow flow;
 +static void update_port(struct ofproto *, const char *devname);
 +static int init_ports(struct ofproto *);
 +static void reinit_ports(struct ofproto *);
  
 -    /* The packet corresponding to 'flow', or a null pointer if we are
 -     * revalidating without a packet to refer to. */
 -    const struct ofpbuf *packet;
 +static void ofproto_unixctl_init(void);
  
 -    /* If nonnull, called just before executing a resubmit action.
 -     *
 -     * This is normally null so the client has to set it manually after
 -     * calling action_xlate_ctx_init(). */
 -    void (*resubmit_hook)(struct action_xlate_ctx *, struct rule *);
 +/* All registered ofproto classes, in probe order. */
 +static const struct ofproto_class **ofproto_classes;
 +static size_t n_ofproto_classes;
 +static size_t allocated_ofproto_classes;
  
 -    /* If true, the speciality of 'flow' should be checked before executing
 -     * its actions.  If special_cb returns false on 'flow' rendered
 -     * uninstallable and no actions will be executed. */
 -    bool check_special;
 +/* Map from datapath name to struct ofproto, for use by unixctl commands. */
 +static struct hmap all_ofprotos = HMAP_INITIALIZER(&all_ofprotos);
  
 -/* xlate_actions() initializes and uses these members.  The client might want
 - * to look at them after it returns. */
 +static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
  
 -    struct ofpbuf *odp_actions; /* Datapath actions. */
 -    tag_type tags;              /* Tags associated with OFPP_NORMAL actions. */
 -    bool may_set_up_flow;       /* True ordinarily; false if the actions must
 -                                 * be reassessed for every packet. */
 -    uint16_t nf_output_iface;   /* Output interface index for NetFlow. */
 +static void
 +ofproto_initialize(void)
 +{
 +    static bool inited;
  
 -/* xlate_actions() initializes and uses these members, but the client has no
 - * reason to look at them. */
 +    if (!inited) {
 +        inited = true;
 +        ofproto_class_register(&ofproto_dpif_class);
 +    }
 +}
  
 -    int recurse;                /* Recursion level, via xlate_table_action. */
 -    int last_pop_priority;      /* Offset in 'odp_actions' just past most
 -                                 * recent ODP_ACTION_ATTR_SET_PRIORITY. */
 -};
 +/* 'type' should be a normalized datapath type, as returned by
 + * ofproto_normalize_type().  Returns the corresponding ofproto_class
 + * structure, or a null pointer if there is none registered for 'type'. */
 +static const struct ofproto_class *
 +ofproto_class_find__(const char *type)
 +{
 +    size_t i;
  
 -static void action_xlate_ctx_init(struct action_xlate_ctx *,
 -                                  struct ofproto *, const struct flow *,
 -                                  const struct ofpbuf *);
 -static struct ofpbuf *xlate_actions(struct action_xlate_ctx *,
 -                                    const union ofp_action *in, size_t n_in);
 -
 -/* An OpenFlow flow. */
 -struct rule {
 -    long long int used;         /* Time last used; time created if not used. */
 -    long long int created;      /* Creation time. */
 -
 -    /* These statistics:
 -     *
 -     *   - Do include packets and bytes from facets that have been deleted or
 -     *     whose own statistics have been folded into the rule.
 -     *
 -     *   - Do include packets and bytes sent "by hand" that were accounted to
 -     *     the rule without any facet being involved (this is a rare corner
 -     *     case in rule_execute()).
 -     *
 -     *   - Do not include packet or bytes that can be obtained from any facet's
 -     *     packet_count or byte_count member or that can be obtained from the
 -     *     datapath by, e.g., dpif_flow_get() for any facet.
 -     */
 -    uint64_t packet_count;       /* Number of packets received. */
 -    uint64_t byte_count;         /* Number of bytes received. */
 -
 -    ovs_be64 flow_cookie;        /* Controller-issued identifier. */
 -
 -    struct cls_rule cr;          /* In owning ofproto's classifier. */
 -    uint16_t idle_timeout;       /* In seconds from time of last use. */
 -    uint16_t hard_timeout;       /* In seconds from time of creation. */
 -    bool send_flow_removed;      /* Send a flow removed message? */
 -    int n_actions;               /* Number of elements in actions[]. */
 -    union ofp_action *actions;   /* OpenFlow actions. */
 -    struct list facets;          /* List of "struct facet"s. */
 -};
 +    ofproto_initialize();
 +    for (i = 0; i < n_ofproto_classes; i++) {
 +        const struct ofproto_class *class = ofproto_classes[i];
 +        struct sset types;
 +        bool found;
  
 -static struct rule *rule_from_cls_rule(const struct cls_rule *);
 -static bool rule_is_hidden(const struct rule *);
 -
 -static struct rule *rule_create(const struct cls_rule *,
 -                                const union ofp_action *, size_t n_actions,
 -                                uint16_t idle_timeout, uint16_t hard_timeout,
 -                                ovs_be64 flow_cookie, bool send_flow_removed);
 -static void rule_destroy(struct ofproto *, struct rule *);
 -static void rule_free(struct rule *);
 -
 -static struct rule *rule_lookup(struct ofproto *, const struct flow *);
 -static void rule_insert(struct ofproto *, struct rule *);
 -static void rule_remove(struct ofproto *, struct rule *);
 -
 -static void rule_send_removed(struct ofproto *, struct rule *, uint8_t reason);
 -static void rule_get_stats(const struct rule *, uint64_t *packets,
 -                           uint64_t *bytes);
 -
 -/* An exact-match instantiation of an OpenFlow flow. */
 -struct facet {
 -    long long int used;         /* Time last used; time created if not used. */
 -
 -    /* These statistics:
 -     *
 -     *   - Do include packets and bytes sent "by hand", e.g. with
 -     *     dpif_execute().
 -     *
 -     *   - Do include packets and bytes that were obtained from the datapath
 -     *     when a flow was deleted (e.g. dpif_flow_del()) or when its
 -     *     statistics were reset (e.g. dpif_flow_put() with
 -     *     DPIF_FP_ZERO_STATS).
 -     *
 -     *   - Do not include any packets or bytes that can currently be obtained
 -     *     from the datapath by, e.g., dpif_flow_get().
 -     */
 -    uint64_t packet_count;       /* Number of packets received. */
 -    uint64_t byte_count;         /* Number of bytes received. */
 -
 -    uint64_t dp_packet_count;    /* Last known packet count in the datapath. */
 -    uint64_t dp_byte_count;      /* Last known byte count in the datapath. */
 -
 -    uint64_t rs_packet_count;    /* Packets pushed to resubmit children. */
 -    uint64_t rs_byte_count;      /* Bytes pushed to resubmit children. */
 -    long long int rs_used;       /* Used time pushed to resubmit children. */
 -
 -    /* Number of bytes passed to account_cb.  This may include bytes that can
 -     * currently obtained from the datapath (thus, it can be greater than
 -     * byte_count). */
 -    uint64_t accounted_bytes;
 -
 -    struct hmap_node hmap_node;  /* In owning ofproto's 'facets' hmap. */
 -    struct list list_node;       /* In owning rule's 'facets' list. */
 -    struct rule *rule;           /* Owning rule. */
 -    struct flow flow;            /* Exact-match flow. */
 -    bool installed;              /* Installed in datapath? */
 -    bool may_install;            /* True ordinarily; false if actions must
 -                                  * be reassessed for every packet. */
 -    size_t actions_len;          /* Number of bytes in actions[]. */
 -    struct nlattr *actions;      /* Datapath actions. */
 -    tag_type tags;               /* Tags (set only by hooks). */
 -    struct netflow_flow nf_flow; /* Per-flow NetFlow tracking data. */
 -};
 +        sset_init(&types);
 +        class->enumerate_types(&types);
 +        found = sset_contains(&types, type);
 +        sset_destroy(&types);
  
 -static struct facet *facet_create(struct ofproto *, struct rule *,
 -                                  const struct flow *,
 -                                  const struct ofpbuf *packet);
 -static void facet_remove(struct ofproto *, struct facet *);
 -static void facet_free(struct facet *);
 -
 -static struct facet *facet_lookup_valid(struct ofproto *, const struct flow *);
 -static bool facet_revalidate(struct ofproto *, struct facet *);
 -
 -static void facet_install(struct ofproto *, struct facet *, bool zero_stats);
 -static void facet_uninstall(struct ofproto *, struct facet *);
 -static void facet_flush_stats(struct ofproto *, struct facet *);
 -
 -static void facet_make_actions(struct ofproto *, struct facet *,
 -                               const struct ofpbuf *packet);
 -static void facet_reset_dp_stats(struct facet *, struct dpif_flow_stats *);
 -static void facet_update_stats(struct ofproto *, struct facet *,
 -                               const struct dpif_flow_stats *);
 -static void facet_push_stats(struct ofproto *, struct facet *);
 -
 -static void send_packet_in(struct ofproto *, struct dpif_upcall *,
 -                           const struct flow *, bool clone);
 -
 -struct ofproto {
 -    /* Settings. */
 -    uint64_t datapath_id;       /* Datapath ID. */
 -    uint64_t fallback_dpid;     /* Datapath ID if no better choice found. */
 -    char *mfr_desc;             /* Manufacturer. */
 -    char *hw_desc;              /* Hardware. */
 -    char *sw_desc;              /* Software version. */
 -    char *serial_desc;          /* Serial number. */
 -    char *dp_desc;              /* Datapath description. */
 -
 -    /* Datapath. */
 -    struct dpif *dpif;
 -    struct netdev_monitor *netdev_monitor;
 -    struct hmap ports;          /* Contains "struct ofport"s. */
 -    struct shash port_by_name;
 -    uint32_t max_ports;
 -
 -    /* Configuration. */
 -    struct netflow *netflow;
 -    struct ofproto_sflow *sflow;
 -
 -    /* Flow table. */
 -    struct classifier cls;
 -    struct timer next_expiration;
 -
 -    /* Facets. */
 -    struct hmap facets;
 -    bool need_revalidate;
 -    struct tag_set revalidate_set;
 -
 -    /* OpenFlow connections. */
 -    struct connmgr *connmgr;
 -
 -    /* Hooks for ovs-vswitchd. */
 -    const struct ofhooks *ofhooks;
 -    void *aux;
 -
 -    /* Used by default ofhooks. */
 -    struct mac_learning *ml;
 -};
 +        if (found) {
 +            return class;
 +        }
 +    }
 +    VLOG_WARN("unknown datapath type %s", type);
 +    return NULL;
 +}
  
 -/* Map from dpif name to struct ofproto, for use by unixctl commands. */
 -static struct shash all_ofprotos = SHASH_INITIALIZER(&all_ofprotos);
 +/* Registers a new ofproto class.  After successful registration, new ofprotos
 + * of that type can be created using ofproto_create(). */
 +int
 +ofproto_class_register(const struct ofproto_class *new_class)
 +{
 +    size_t i;
  
 -static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
 +    for (i = 0; i < n_ofproto_classes; i++) {
 +        if (ofproto_classes[i] == new_class) {
 +            return EEXIST;
 +        }
 +    }
  
 -static const struct ofhooks default_ofhooks;
 +    if (n_ofproto_classes >= allocated_ofproto_classes) {
 +        ofproto_classes = x2nrealloc(ofproto_classes,
 +                                     &allocated_ofproto_classes,
 +                                     sizeof *ofproto_classes);
 +    }
 +    ofproto_classes[n_ofproto_classes++] = new_class;
 +    return 0;
 +}
  
 -static uint64_t pick_datapath_id(const struct ofproto *);
 -static uint64_t pick_fallback_dpid(void);
 +/* Unregisters a datapath provider.  'type' must have been previously
 + * registered and not currently be in use by any ofprotos.  After
 + * unregistration new datapaths of that type cannot be opened using
 + * ofproto_create(). */
 +int
 +ofproto_class_unregister(const struct ofproto_class *class)
 +{
 +    size_t i;
  
 -static void ofproto_flush_flows__(struct ofproto *);
 -static int ofproto_expire(struct ofproto *);
 -static void flow_push_stats(struct ofproto *, const struct rule *,
 -                            struct flow *, uint64_t packets, uint64_t bytes,
 -                            long long int used);
 +    for (i = 0; i < n_ofproto_classes; i++) {
 +        if (ofproto_classes[i] == class) {
 +            for (i++; i < n_ofproto_classes; i++) {
 +                ofproto_classes[i - 1] = ofproto_classes[i];
 +            }
 +            n_ofproto_classes--;
 +            return 0;
 +        }
 +    }
 +    VLOG_WARN("attempted to unregister an ofproto class that is not "
 +              "registered");
 +    return EAFNOSUPPORT;
 +}
  
 -static void handle_upcall(struct ofproto *, struct dpif_upcall *);
 +/* Clears 'types' and enumerates all registered ofproto types into it.  The
 + * caller must first initialize the sset. */
 +void
 +ofproto_enumerate_types(struct sset *types)
 +{
 +    size_t i;
  
 -static void handle_openflow(struct ofconn *, struct ofpbuf *);
 +    ofproto_initialize();
 +    for (i = 0; i < n_ofproto_classes; i++) {
 +        ofproto_classes[i]->enumerate_types(types);
 +    }
 +}
  
 -static struct ofport *get_port(const struct ofproto *, uint16_t odp_port);
 -static void update_port(struct ofproto *, const char *devname);
 -static int init_ports(struct ofproto *);
 -static void reinit_ports(struct ofproto *);
 +/* Returns the fully spelled out name for the given ofproto 'type'.
 + *
 + * Normalized type string can be compared with strcmp().  Unnormalized type
 + * string might be the same even if they have different spellings. */
 +const char *
 +ofproto_normalize_type(const char *type)
 +{
 +    return type && type[0] ? type : "system";
 +}
  
 -static void ofproto_unixctl_init(void);
 +/* Clears 'names' and enumerates the names of all known created ofprotos with
 + * the given 'type'.  The caller must first initialize the sset.  Returns 0 if
 + * successful, otherwise a positive errno value.
 + *
 + * Some kinds of datapaths might not be practically enumerable.  This is not
 + * considered an error. */
 +int
 +ofproto_enumerate_names(const char *type, struct sset *names)
 +{
 +    const struct ofproto_class *class = ofproto_class_find__(type);
 +    return class ? class->enumerate_names(type, names) : EAFNOSUPPORT;
 + }
  
  int
 -ofproto_create(const char *datapath, const char *datapath_type,
 -               const struct ofhooks *ofhooks, void *aux,
 +ofproto_create(const char *datapath_name, const char *datapath_type,
                 struct ofproto **ofprotop)
  {
 -    char local_name[IF_NAMESIZE];
 -    struct ofproto *p;
 -    struct dpif *dpif;
 +    const struct ofproto_class *class;
 +    struct ofproto *ofproto;
      int error;
  
      *ofprotop = NULL;
  
 +    ofproto_initialize();
      ofproto_unixctl_init();
  
 -    /* Connect to datapath and start listening for messages. */
 -    error = dpif_open(datapath, datapath_type, &dpif);
 -    if (error) {
 -        VLOG_ERR("failed to open datapath %s: %s", datapath, strerror(error));
 -        return error;
 -    }
 -    error = dpif_recv_set_mask(dpif,
 -                               ((1u << DPIF_UC_MISS) |
 -                                (1u << DPIF_UC_ACTION) |
 -                                (1u << DPIF_UC_SAMPLE)));
 -    if (error) {
 -        VLOG_ERR("failed to listen on datapath %s: %s",
 -                 datapath, strerror(error));
 -        dpif_close(dpif);
 -        return error;
 +    datapath_type = ofproto_normalize_type(datapath_type);
 +    class = ofproto_class_find__(datapath_type);
 +    if (!class) {
 +        VLOG_WARN("could not create datapath %s of unknown type %s",
 +                  datapath_name, datapath_type);
 +        return EAFNOSUPPORT;
      }
 -    dpif_flow_flush(dpif);
 -    dpif_recv_purge(dpif);
  
 -    error = dpif_port_get_name(dpif, ODPP_LOCAL,
 -                               local_name, sizeof local_name);
 +    ofproto = class->alloc();
 +    if (!ofproto) {
 +        VLOG_ERR("failed to allocate datapath %s of type %s",
 +                 datapath_name, datapath_type);
 +        return ENOMEM;
 +    }
 +
 +    /* Initialize. */
 +    memset(ofproto, 0, sizeof *ofproto);
 +    ofproto->ofproto_class = class;
 +    ofproto->name = xstrdup(datapath_name);
 +    ofproto->type = xstrdup(datapath_type);
 +    hmap_insert(&all_ofprotos, &ofproto->hmap_node,
 +                hash_string(ofproto->name, 0));
 +    ofproto->datapath_id = 0;
 +    ofproto->fallback_dpid = pick_fallback_dpid();
 +    ofproto->mfr_desc = xstrdup(DEFAULT_MFR_DESC);
 +    ofproto->hw_desc = xstrdup(DEFAULT_HW_DESC);
 +    ofproto->sw_desc = xstrdup(DEFAULT_SW_DESC);
 +    ofproto->serial_desc = xstrdup(DEFAULT_SERIAL_DESC);
 +    ofproto->dp_desc = xstrdup(DEFAULT_DP_DESC);
 +    ofproto->netdev_monitor = netdev_monitor_create();
 +    hmap_init(&ofproto->ports);
 +    shash_init(&ofproto->port_by_name);
 +    ofproto->tables = NULL;
 +    ofproto->n_tables = 0;
 +    ofproto->connmgr = connmgr_create(ofproto, datapath_name, datapath_name);
 +
 +    error = ofproto->ofproto_class->construct(ofproto);
      if (error) {
 -        VLOG_ERR("%s: cannot get name of datapath local port (%s)",
 -                 datapath, strerror(error));
 +        VLOG_ERR("failed to open datapath %s: %s",
 +                 datapath_name, strerror(error));
 +        ofproto_destroy__(ofproto);
          return error;
      }
 +    assert(ofproto->n_tables > 0);
  
 -    /* Initialize settings. */
 -    p = xzalloc(sizeof *p);
 -    p->fallback_dpid = pick_fallback_dpid();
 -    p->datapath_id = p->fallback_dpid;
 -    p->mfr_desc = xstrdup(DEFAULT_MFR_DESC);
 -    p->hw_desc = xstrdup(DEFAULT_HW_DESC);
 -    p->sw_desc = xstrdup(DEFAULT_SW_DESC);
 -    p->serial_desc = xstrdup(DEFAULT_SERIAL_DESC);
 -    p->dp_desc = xstrdup(DEFAULT_DP_DESC);
 -
 -    /* Initialize datapath. */
 -    p->dpif = dpif;
 -    p->netdev_monitor = netdev_monitor_create();
 -    hmap_init(&p->ports);
 -    shash_init(&p->port_by_name);
 -    p->max_ports = dpif_get_max_ports(dpif);
 -
 -    /* Initialize submodules. */
 -    p->netflow = NULL;
 -    p->sflow = NULL;
 -
 -    /* Initialize flow table. */
 -    classifier_init(&p->cls);
 -    timer_set_duration(&p->next_expiration, 1000);
 -
 -    /* Initialize facet table. */
 -    hmap_init(&p->facets);
 -    p->need_revalidate = false;
 -    tag_set_init(&p->revalidate_set);
 -
 -    /* Initialize hooks. */
 -    if (ofhooks) {
 -        p->ofhooks = ofhooks;
 -        p->aux = aux;
 -        p->ml = NULL;
 -    } else {
 -        p->ofhooks = &default_ofhooks;
 -        p->aux = p;
 -        p->ml = mac_learning_create();
 -    }
 -
 -    /* Pick final datapath ID. */
 -    p->datapath_id = pick_datapath_id(p);
 -    VLOG_INFO("using datapath ID %016"PRIx64, p->datapath_id);
 +    ofproto->datapath_id = pick_datapath_id(ofproto);
 +    VLOG_INFO("using datapath ID %016"PRIx64, ofproto->datapath_id);
 +    init_ports(ofproto);
  
 -    shash_add_once(&all_ofprotos, dpif_name(p->dpif), p);
 -
 -    /* Initialize OpenFlow connections. */
 -    p->connmgr = connmgr_create(p, datapath, local_name);
 -
 -    *ofprotop = p;
 +    *ofprotop = ofproto;
      return 0;
  }
  
  ofproto_set_netflow(struct ofproto *ofproto,
                      const struct netflow_options *nf_options)
  {
 -    if (nf_options && !sset_is_empty(&nf_options->collectors)) {
 -        if (!ofproto->netflow) {
 -            ofproto->netflow = netflow_create();
 -        }
 -        return netflow_set_options(ofproto->netflow, nf_options);
 +    if (nf_options && sset_is_empty(&nf_options->collectors)) {
 +        nf_options = NULL;
 +    }
 +
 +    if (ofproto->ofproto_class->set_netflow) {
 +        return ofproto->ofproto_class->set_netflow(ofproto, nf_options);
      } else {
 -        netflow_destroy(ofproto->netflow);
 -        ofproto->netflow = NULL;
 -        return 0;
 +        return nf_options ? EOPNOTSUPP : 0;
      }
  }
  
 -void
 +int
  ofproto_set_sflow(struct ofproto *ofproto,
                    const struct ofproto_sflow_options *oso)
  {
 -    struct ofproto_sflow *os = ofproto->sflow;
 -    if (oso) {
 -        if (!os) {
 -            struct ofport *ofport;
 +    if (oso && sset_is_empty(&oso->targets)) {
 +        oso = NULL;
 +    }
  
 -            os = ofproto->sflow = ofproto_sflow_create(ofproto->dpif);
 -            HMAP_FOR_EACH (ofport, hmap_node, &ofproto->ports) {
 -                ofproto_sflow_add_port(os, ofport->odp_port,
 -                                       netdev_get_name(ofport->netdev));
 -            }
 -        }
 -        ofproto_sflow_set_options(os, oso);
 +    if (ofproto->ofproto_class->set_sflow) {
 +        return ofproto->ofproto_class->set_sflow(ofproto, oso);
      } else {
 -        ofproto_sflow_destroy(os);
 -        ofproto->sflow = NULL;
 +        return oso ? EOPNOTSUPP : 0;
      }
  }
  \f
  /* Connectivity Fault Management configuration. */
  
 -/* Clears the CFM configuration from 'port_no' on 'ofproto'. */
 +/* Clears the CFM configuration from 'ofp_port' on 'ofproto'. */
  void
 -ofproto_iface_clear_cfm(struct ofproto *ofproto, uint32_t port_no)
 +ofproto_port_clear_cfm(struct ofproto *ofproto, uint16_t ofp_port)
  {
 -    struct ofport *ofport = get_port(ofproto, port_no);
 -    if (ofport && ofport->cfm){
 -        cfm_destroy(ofport->cfm);
 -        ofport->cfm = NULL;
 +    struct ofport *ofport = ofproto_get_port(ofproto, ofp_port);
 +    if (ofport && ofproto->ofproto_class->set_cfm) {
 +        ofproto->ofproto_class->set_cfm(ofport, NULL, NULL, 0);
      }
  }
  
 -/* Configures connectivity fault management on 'port_no' in 'ofproto'.  Takes
 +/* Configures connectivity fault management on 'ofp_port' in 'ofproto'.  Takes
   * basic configuration from the configuration members in 'cfm', and the set of
   * remote maintenance points from the 'n_remote_mps' elements in 'remote_mps'.
   * Ignores the statistics members of 'cfm'.
   *
 - * This function has no effect if 'ofproto' does not have a port 'port_no'. */
 + * This function has no effect if 'ofproto' does not have a port 'ofp_port'. */
  void
 -ofproto_iface_set_cfm(struct ofproto *ofproto, uint32_t port_no,
 -                      const struct cfm *cfm,
 -                      const uint16_t *remote_mps, size_t n_remote_mps)
 +ofproto_port_set_cfm(struct ofproto *ofproto, uint16_t ofp_port,
 +                     const struct cfm *cfm,
 +                     const uint16_t *remote_mps, size_t n_remote_mps)
  {
      struct ofport *ofport;
 +    int error;
  
 -    ofport = get_port(ofproto, port_no);
 +    ofport = ofproto_get_port(ofproto, ofp_port);
      if (!ofport) {
 -        VLOG_WARN("%s: cannot configure CFM on nonexistent port %"PRIu32,
 -                  dpif_name(ofproto->dpif), port_no);
 +        VLOG_WARN("%s: cannot configure CFM on nonexistent port %"PRIu16,
 +                  ofproto->name, ofp_port);
          return;
      }
  
 -    if (!ofport->cfm) {
 -        ofport->cfm = cfm_create();
 +    error = (ofproto->ofproto_class->set_cfm
 +             ? ofproto->ofproto_class->set_cfm(ofport, cfm,
 +                                               remote_mps, n_remote_mps)
 +             : EOPNOTSUPP);
 +    if (error) {
 +        VLOG_WARN("%s: CFM configuration on port %"PRIu16" (%s) failed (%s)",
 +                  ofproto->name, ofp_port, netdev_get_name(ofport->netdev),
 +                  strerror(error));
      }
 +}
  
 -    ofport->cfm->mpid = cfm->mpid;
 -    ofport->cfm->interval = cfm->interval;
 -    memcpy(ofport->cfm->maid, cfm->maid, CCM_MAID_LEN);
 +/* Returns the connectivity fault management object associated with 'ofp_port'
 + * within 'ofproto', or a null pointer if 'ofproto' does not have a port
 + * 'ofp_port' or if that port does not have CFM configured.  The caller must
 + * not modify or destroy the returned object. */
 +const struct cfm *
 +ofproto_port_get_cfm(struct ofproto *ofproto, uint16_t ofp_port)
 +{
 +    struct ofport *ofport;
 +    const struct cfm *cfm;
  
 -    cfm_update_remote_mps(ofport->cfm, remote_mps, n_remote_mps);
 +    ofport = ofproto_get_port(ofproto, ofp_port);
 +    return (ofport
 +            && ofproto->ofproto_class->get_cfm
 +            && !ofproto->ofproto_class->get_cfm(ofport, &cfm)) ? cfm : NULL;
 +}
  
 -    if (!cfm_configure(ofport->cfm)) {
 -        VLOG_WARN("%s: CFM configuration on port %"PRIu32" (%s) failed",
 -                  dpif_name(ofproto->dpif), port_no,
 -                  netdev_get_name(ofport->netdev));
 -        cfm_destroy(ofport->cfm);
 -        ofport->cfm = NULL;
 -    }
 +/* Checks the status of LACP negotiation for 'ofp_port' within ofproto.
 + * Returns 1 if LACP partner information for 'ofp_port' is up-to-date,
 + * 0 if LACP partner information is not current (generally indicating a
 + * connectivity problem), or -1 if LACP is not enabled on 'ofp_port'. */
 +int
 +ofproto_port_is_lacp_current(struct ofproto *ofproto, uint16_t ofp_port)
 +{
 +    struct ofport *ofport = ofproto_get_port(ofproto, ofp_port);
 +    return (ofport && ofproto->ofproto_class->port_is_lacp_current
 +            ? ofproto->ofproto_class->port_is_lacp_current(ofport)
 +            : -1);
  }
 +\f
 +/* Bundles. */
  
 -/* Returns the connectivity fault management object associated with 'port_no'
 - * within 'ofproto', or a null pointer if 'ofproto' does not have a port
 - * 'port_no' or if that port does not have CFM configured.  The caller must not
 - * modify or destroy the returned object. */
 -const struct cfm *
 -ofproto_iface_get_cfm(struct ofproto *ofproto, uint32_t port_no)
 +/* Registers a "bundle" associated with client data pointer 'aux' in 'ofproto'.
 + * A bundle is the same concept as a Port in OVSDB, that is, it consists of one
 + * or more "slave" devices (Interfaces, in OVSDB) along with a VLAN
 + * configuration plus, if there is more than one slave, a bonding
 + * configuration.
 + *
 + * If 'aux' is already registered then this function updates its configuration
 + * to 's'.  Otherwise, this function registers a new bundle.
 + *
 + * Bundles only affect the NXAST_AUTOPATH action and output to the OFPP_NORMAL
 + * port. */
 +int
 +ofproto_bundle_register(struct ofproto *ofproto, void *aux,
 +                        const struct ofproto_bundle_settings *s)
  {
 -    struct ofport *ofport = get_port(ofproto, port_no);
 -    return ofport ? ofport->cfm : NULL;
 +    return (ofproto->ofproto_class->bundle_set
 +            ? ofproto->ofproto_class->bundle_set(ofproto, aux, s)
 +            : EOPNOTSUPP);
  }
 +
 +/* Unregisters the bundle registered on 'ofproto' with auxiliary data 'aux'.
 + * If no such bundle has been registered, this has no effect. */
 +int
 +ofproto_bundle_unregister(struct ofproto *ofproto, void *aux)
 +{
 +    return ofproto_bundle_register(ofproto, aux, NULL);
 +}
 +
  \f
 -uint64_t
 -ofproto_get_datapath_id(const struct ofproto *ofproto)
 +/* Registers a mirror associated with client data pointer 'aux' in 'ofproto'.
 + * If 'aux' is already registered then this function updates its configuration
 + * to 's'.  Otherwise, this function registers a new mirror.
 + *
 + * Mirrors affect only the treatment of packets output to the OFPP_NORMAL
 + * port.  */
 +int
 +ofproto_mirror_register(struct ofproto *ofproto, void *aux,
 +                        const struct ofproto_mirror_settings *s)
  {
 -    return ofproto->datapath_id;
 +    return (ofproto->ofproto_class->mirror_set
 +            ? ofproto->ofproto_class->mirror_set(ofproto, aux, s)
 +            : EOPNOTSUPP);
  }
  
 -bool
 -ofproto_has_primary_controller(const struct ofproto *ofproto)
 +/* Unregisters the mirror registered on 'ofproto' with auxiliary data 'aux'.
 + * If no mirror has been registered, this has no effect. */
 +int
 +ofproto_mirror_unregister(struct ofproto *ofproto, void *aux)
  {
 -    return connmgr_has_controllers(ofproto->connmgr);
 +    return ofproto_mirror_register(ofproto, aux, NULL);
  }
  
 -enum ofproto_fail_mode
 -ofproto_get_fail_mode(const struct ofproto *p)
 +/* Configures the VLANs whose bits are set to 1 in 'flood_vlans' as VLANs on
 + * which all packets are flooded, instead of using MAC learning.  If
 + * 'flood_vlans' is NULL, then MAC learning applies to all VLANs.
 + *
 + * Flood VLANs affect only the treatment of packets output to the OFPP_NORMAL
 + * port. */
 +int
 +ofproto_set_flood_vlans(struct ofproto *ofproto, unsigned long *flood_vlans)
  {
 -    return connmgr_get_fail_mode(p->connmgr);
 +    return (ofproto->ofproto_class->set_flood_vlans
 +            ? ofproto->ofproto_class->set_flood_vlans(ofproto, flood_vlans)
 +            : EOPNOTSUPP);
  }
  
 +/* Returns true if 'aux' is a registered bundle that is currently in use as the
 + * output for a mirror. */
 +bool
 +ofproto_is_mirror_output_bundle(struct ofproto *ofproto, void *aux)
 +{
 +    return (ofproto->ofproto_class->is_mirror_output_bundle
 +            ? ofproto->ofproto_class->is_mirror_output_bundle(ofproto, aux)
 +            : false);
 +}
 +\f
  bool
  ofproto_has_snoops(const struct ofproto *ofproto)
  {
@@@ -587,32 -682,6 +587,32 @@@ ofproto_get_snoops(const struct ofprot
      connmgr_get_snoops(ofproto->connmgr, snoops);
  }
  
 +static void
 +ofproto_destroy__(struct ofproto *ofproto)
 +{
 +    size_t i;
 +
 +    connmgr_destroy(ofproto->connmgr);
 +
 +    hmap_remove(&all_ofprotos, &ofproto->hmap_node);
 +    free(ofproto->name);
 +    free(ofproto->mfr_desc);
 +    free(ofproto->hw_desc);
 +    free(ofproto->sw_desc);
 +    free(ofproto->serial_desc);
 +    free(ofproto->dp_desc);
 +    netdev_monitor_destroy(ofproto->netdev_monitor);
 +    hmap_destroy(&ofproto->ports);
 +    shash_destroy(&ofproto->port_by_name);
 +
 +    for (i = 0; i < ofproto->n_tables; i++) {
 +        classifier_destroy(&ofproto->tables[i]);
 +    }
 +    free(ofproto->tables);
 +
 +    ofproto->ofproto_class->dealloc(ofproto);
 +}
 +
  void
  ofproto_destroy(struct ofproto *p)
  {
          return;
      }
  
 -    shash_find_and_delete(&all_ofprotos, dpif_name(p->dpif));
 -
      ofproto_flush_flows__(p);
 -    connmgr_destroy(p->connmgr);
 -    classifier_destroy(&p->cls);
 -    hmap_destroy(&p->facets);
 -
 -    dpif_close(p->dpif);
 -    netdev_monitor_destroy(p->netdev_monitor);
      HMAP_FOR_EACH_SAFE (ofport, next_ofport, hmap_node, &p->ports) {
          hmap_remove(&p->ports, &ofport->hmap_node);
 -        ofport_free(ofport);
 +        ofport_destroy(ofport);
      }
 -    shash_destroy(&p->port_by_name);
 -
 -    netflow_destroy(p->netflow);
 -    ofproto_sflow_destroy(p->sflow);
 -
 -    mac_learning_destroy(p->ml);
  
 -    free(p->mfr_desc);
 -    free(p->hw_desc);
 -    free(p->sw_desc);
 -    free(p->serial_desc);
 -    free(p->dp_desc);
 -
 -    hmap_destroy(&p->ports);
 -
 -    free(p);
 +    p->ofproto_class->destruct(p);
 +    ofproto_destroy__(p);
  }
  
 +/* Destroys the datapath with the respective 'name' and 'type'.  With the Linux
 + * kernel datapath, for example, this destroys the datapath in the kernel, and
 + * with the netdev-based datapath, it tears down the data structures that
 + * represent the datapath.
 + *
 + * The datapath should not be currently open as an ofproto. */
  int
 -ofproto_run(struct ofproto *p)
 +ofproto_delete(const char *name, const char *type)
  {
 -    int error = ofproto_run1(p);
 -    if (!error) {
 -        error = ofproto_run2(p, false);
 -    }
 -    return error;
 +    const struct ofproto_class *class = ofproto_class_find__(type);
 +    return (!class ? EAFNOSUPPORT
 +            : !class->del ? EACCES
 +            : class->del(type, name));
  }
  
  static void
@@@ -659,48 -744,134 +659,48 @@@ process_port_change(struct ofproto *ofp
  }
  
  int
 -ofproto_run1(struct ofproto *p)
 +ofproto_run(struct ofproto *p)
  {
 -    struct ofport *ofport;
      char *devname;
      int error;
 -    int i;
  
 -    if (shash_is_empty(&p->port_by_name)) {
 -        init_ports(p);
 +    error = p->ofproto_class->run(p);
 +    if (error == ENODEV) {
 +        /* Someone destroyed the datapath behind our back.  The caller
 +         * better destroy us and give up, because we're just going to
 +         * spin from here on out. */
 +        static struct vlog_rate_limit rl2 = VLOG_RATE_LIMIT_INIT(1, 5);
 +        VLOG_ERR_RL(&rl2, "%s: datapath was destroyed externally",
 +                    p->name);
 +        return ENODEV;
      }
  
 -    for (i = 0; i < 50; i++) {
 -        struct dpif_upcall packet;
 -
 -        error = dpif_recv(p->dpif, &packet);
 -        if (error) {
 -            if (error == ENODEV) {
 -                /* Someone destroyed the datapath behind our back.  The caller
 -                 * better destroy us and give up, because we're just going to
 -                 * spin from here on out. */
 -                static struct vlog_rate_limit rl2 = VLOG_RATE_LIMIT_INIT(1, 5);
 -                VLOG_ERR_RL(&rl2, "%s: datapath was destroyed externally",
 -                            dpif_name(p->dpif));
 -                return ENODEV;
 -            }
 -            break;
 +    if (p->ofproto_class->port_poll) {
 +        while ((error = p->ofproto_class->port_poll(p, &devname)) != EAGAIN) {
 +            process_port_change(p, error, devname);
          }
 -
 -        handle_upcall(p, &packet);
 -    }
 -
 -    while ((error = dpif_port_poll(p->dpif, &devname)) != EAGAIN) {
 -        process_port_change(p, error, devname);
      }
      while ((error = netdev_monitor_poll(p->netdev_monitor,
                                          &devname)) != EAGAIN) {
          process_port_change(p, error, devname);
      }
  
 -    HMAP_FOR_EACH (ofport, hmap_node, &p->ports) {
 -        ofport_run(p, ofport);
 -    }
 -
      connmgr_run(p->connmgr, handle_openflow);
  
 -    if (timer_expired(&p->next_expiration)) {
 -        int delay = ofproto_expire(p);
 -        timer_set_duration(&p->next_expiration, delay);
 -        COVERAGE_INC(ofproto_expiration);
 -    }
 -
 -    if (p->netflow) {
 -        netflow_run(p->netflow);
 -    }
 -    if (p->sflow) {
 -        ofproto_sflow_run(p->sflow);
 -    }
 -
 -    return 0;
 -}
 -
 -int
 -ofproto_run2(struct ofproto *p, bool revalidate_all)
 -{
 -    /* Figure out what we need to revalidate now, if anything. */
 -    struct tag_set revalidate_set = p->revalidate_set;
 -    if (p->need_revalidate) {
 -        revalidate_all = true;
 -    }
 -
 -    /* Clear the revalidation flags. */
 -    tag_set_init(&p->revalidate_set);
 -    p->need_revalidate = false;
 -
 -    /* Now revalidate if there's anything to do. */
 -    if (revalidate_all || !tag_set_is_empty(&revalidate_set)) {
 -        struct facet *facet, *next;
 -
 -        HMAP_FOR_EACH_SAFE (facet, next, hmap_node, &p->facets) {
 -            if (revalidate_all
 -                || tag_set_intersects(&revalidate_set, facet->tags)) {
 -                facet_revalidate(p, facet);
 -            }
 -        }
 -    }
 -
      return 0;
  }
  
  void
  ofproto_wait(struct ofproto *p)
  {
 -    struct ofport *ofport;
 -
 -    HMAP_FOR_EACH (ofport, hmap_node, &p->ports) {
 -        ofport_wait(ofport);
 +    p->ofproto_class->wait(p);
 +    if (p->ofproto_class->port_poll_wait) {
 +        p->ofproto_class->port_poll_wait(p);
      }
 -    dpif_recv_wait(p->dpif);
 -    dpif_port_poll_wait(p->dpif);
      netdev_monitor_poll_wait(p->netdev_monitor);
 -    if (p->sflow) {
 -        ofproto_sflow_wait(p->sflow);
 -    }
 -    if (!tag_set_is_empty(&p->revalidate_set)) {
 -        poll_immediate_wake();
 -    }
 -    if (p->need_revalidate) {
 -        /* Shouldn't happen, but if it does just go around again. */
 -        VLOG_DBG_RL(&rl, "need revalidate in ofproto_wait_cb()");
 -        poll_immediate_wake();
 -    } else {
 -        timer_wait(&p->next_expiration);
 -    }
      connmgr_wait(p->connmgr);
  }
  
 -void
 -ofproto_revalidate(struct ofproto *ofproto, tag_type tag)
 -{
 -    tag_set_add(&ofproto->revalidate_set, tag);
 -}
 -
 -struct tag_set *
 -ofproto_get_revalidate_set(struct ofproto *ofproto)
 -{
 -    return &ofproto->revalidate_set;
 -}
 -
  bool
  ofproto_is_alive(const struct ofproto *p)
  {
@@@ -729,149 -900,79 +729,149 @@@ ofproto_free_ofproto_controller_info(st
      shash_destroy(info);
  }
  
 -/* Deletes port number 'odp_port' from the datapath for 'ofproto'.
 +/* Makes a deep copy of 'old' into 'port'. */
 +void
 +ofproto_port_clone(struct ofproto_port *port, const struct ofproto_port *old)
 +{
 +    port->name = xstrdup(old->name);
 +    port->type = xstrdup(old->type);
 +    port->ofp_port = old->ofp_port;
 +}
 +
 +/* Frees memory allocated to members of 'ofproto_port'.
   *
 - * This is almost the same as calling dpif_port_del() directly on the
 - * datapath, but it also makes 'ofproto' close its open netdev for the port
 - * (if any).  This makes it possible to create a new netdev of a different
 - * type under the same name, which otherwise the netdev library would refuse
 - * to do because of the conflict.  (The netdev would eventually get closed on
 - * the next trip through ofproto_run(), but this interface is more direct.)
 + * Do not call this function on an ofproto_port obtained from
 + * ofproto_port_dump_next(): that function retains ownership of the data in the
 + * ofproto_port. */
 +void
 +ofproto_port_destroy(struct ofproto_port *ofproto_port)
 +{
 +    free(ofproto_port->name);
 +    free(ofproto_port->type);
 +}
 +
 +/* Initializes 'dump' to begin dumping the ports in an ofproto.
   *
 - * Returns 0 if successful, otherwise a positive errno. */
 -int
 -ofproto_port_del(struct ofproto *ofproto, uint16_t odp_port)
 + * This function provides no status indication.  An error status for the entire
 + * dump operation is provided when it is completed by calling
 + * ofproto_port_dump_done().
 + */
 +void
 +ofproto_port_dump_start(struct ofproto_port_dump *dump,
 +                        const struct ofproto *ofproto)
  {
 -    struct ofport *ofport = get_port(ofproto, odp_port);
 -    const char *name = ofport ? netdev_get_name(ofport->netdev) : "<unknown>";
 -    int error;
 +    dump->ofproto = ofproto;
 +    dump->error = ofproto->ofproto_class->port_dump_start(ofproto,
 +                                                          &dump->state);
 +}
  
 -    error = dpif_port_del(ofproto->dpif, odp_port);
 -    if (error) {
 -        VLOG_ERR("%s: failed to remove port %"PRIu16" (%s) interface (%s)",
 -                 dpif_name(ofproto->dpif), odp_port, name, strerror(error));
 -    } else if (ofport) {
 -        /* 'name' is the netdev's name and update_port() is going to close the
 -         * netdev.  Just in case update_port() refers to 'name' after it
 -         * destroys 'ofport', make a copy of it around the update_port()
 -         * call. */
 -        char *devname = xstrdup(name);
 -        update_port(ofproto, devname);
 -        free(devname);
 +/* Attempts to retrieve another port from 'dump', which must have been created
 + * with ofproto_port_dump_start().  On success, stores a new ofproto_port into
 + * 'port' and returns true.  On failure, returns false.
 + *
 + * Failure might indicate an actual error or merely that the last port has been
 + * dumped.  An error status for the entire dump operation is provided when it
 + * is completed by calling ofproto_port_dump_done().
 + *
 + * The ofproto owns the data stored in 'port'.  It will remain valid until at
 + * least the next time 'dump' is passed to ofproto_port_dump_next() or
 + * ofproto_port_dump_done(). */
 +bool
 +ofproto_port_dump_next(struct ofproto_port_dump *dump,
 +                       struct ofproto_port *port)
 +{
 +    const struct ofproto *ofproto = dump->ofproto;
 +
 +    if (dump->error) {
 +        return false;
      }
 -    return error;
 +
 +    dump->error = ofproto->ofproto_class->port_dump_next(ofproto, dump->state,
 +                                                         port);
 +    if (dump->error) {
 +        ofproto->ofproto_class->port_dump_done(ofproto, dump->state);
 +        return false;
 +    }
 +    return true;
  }
  
 -/* Checks if 'ofproto' thinks 'odp_port' should be included in floods.  Returns
 - * true if 'odp_port' exists and should be included, false otherwise. */
 -bool
 -ofproto_port_is_floodable(struct ofproto *ofproto, uint16_t odp_port)
 +/* Completes port table dump operation 'dump', which must have been created
 + * with ofproto_port_dump_start().  Returns 0 if the dump operation was
 + * error-free, otherwise a positive errno value describing the problem. */
 +int
 +ofproto_port_dump_done(struct ofproto_port_dump *dump)
  {
 -    struct ofport *ofport = get_port(ofproto, odp_port);
 -    return ofport && !(ofport->opp.config & OFPPC_NO_FLOOD);
 +    const struct ofproto *ofproto = dump->ofproto;
 +    if (!dump->error) {
 +        dump->error = ofproto->ofproto_class->port_dump_done(ofproto,
 +                                                             dump->state);
 +    }
 +    return dump->error == EOF ? 0 : dump->error;
  }
  
 -/* Sends 'packet' out of port 'port_no' within 'p'.  If 'vlan_tci' is zero the
 - * packet will not have any 802.1Q hader; if it is nonzero, then the packet
 - * will be sent with the VLAN TCI specified by 'vlan_tci & ~VLAN_CFI'.
 - *
 - * Returns 0 if successful, otherwise a positive errno value. */
 +/* Attempts to add 'netdev' as a port on 'ofproto'.  If successful, returns 0
 + * and sets '*ofp_portp' to the new port's OpenFlow port number (if 'ofp_portp'
 + * is non-null).  On failure, returns a positive errno value and sets
 + * '*ofp_portp' to OFPP_NONE (if 'ofp_portp' is non-null). */
  int
 -ofproto_send_packet(struct ofproto *ofproto,
 -                    uint32_t port_no, uint16_t vlan_tci,
 -                    const struct ofpbuf *packet)
 +ofproto_port_add(struct ofproto *ofproto, struct netdev *netdev,
 +                 uint16_t *ofp_portp)
  {
 -    struct ofpbuf odp_actions;
 +    uint16_t ofp_port;
      int error;
  
 -    ofpbuf_init(&odp_actions, 32);
 -    if (vlan_tci != 0) {
 -        nl_msg_put_u32(&odp_actions, ODP_ACTION_ATTR_SET_DL_TCI,
 -                       ntohs(vlan_tci & ~VLAN_CFI));
 +    error = ofproto->ofproto_class->port_add(ofproto, netdev, &ofp_port);
 +    if (!error) {
 +        update_port(ofproto, netdev_get_name(netdev));
 +    }
 +    if (ofp_portp) {
 +        *ofp_portp = error ? OFPP_NONE : ofp_port;
      }
 -    nl_msg_put_u32(&odp_actions, ODP_ACTION_ATTR_OUTPUT, port_no);
 -    error = dpif_execute(ofproto->dpif, odp_actions.data, odp_actions.size,
 -                         packet);
 -    ofpbuf_uninit(&odp_actions);
 +    return error;
 +}
  
 +/* Looks up a port named 'devname' in 'ofproto'.  On success, returns 0 and
 + * initializes '*port' appropriately; on failure, returns a positive errno
 + * value.
 + *
 + * The caller owns the data in 'ofproto_port' and must free it with
 + * ofproto_port_destroy() when it is no longer needed. */
 +int
 +ofproto_port_query_by_name(const struct ofproto *ofproto, const char *devname,
 +                           struct ofproto_port *port)
 +{
 +    int error;
 +
 +    error = ofproto->ofproto_class->port_query_by_name(ofproto, devname, port);
      if (error) {
 -        VLOG_WARN_RL(&rl, "%s: failed to send packet on port %"PRIu32" (%s)",
 -                     dpif_name(ofproto->dpif), port_no, strerror(error));
 +        memset(port, 0, sizeof *port);
 +    }
 +    return error;
 +}
 +
 +/* Deletes port number 'ofp_port' from the datapath for 'ofproto'.
 + * Returns 0 if successful, otherwise a positive errno. */
 +int
 +ofproto_port_del(struct ofproto *ofproto, uint16_t ofp_port)
 +{
 +    struct ofport *ofport = ofproto_get_port(ofproto, ofp_port);
 +    const char *name = ofport ? netdev_get_name(ofport->netdev) : "<unknown>";
 +    int error;
 +
 +    error = ofproto->ofproto_class->port_del(ofproto, ofp_port);
 +    if (!error && ofport) {
 +        /* 'name' is the netdev's name and update_port() is going to close the
 +         * netdev.  Just in case update_port() refers to 'name' after it
 +         * destroys 'ofport', make a copy of it around the update_port()
 +         * call. */
 +        char *devname = xstrdup(name);
 +        update_port(ofproto, devname);
 +        free(devname);
      }
      return error;
  }
  
 -/* Adds a flow to the OpenFlow flow table in 'p' that matches 'cls_rule' and
 +/* Adds a flow to OpenFlow flow table 0 in 'p' that matches 'cls_rule' and
   * performs the 'n_actions' actions in 'actions'.  The new flow will not
   * timeout.
   *
   * (0...65535, inclusive) then the flow will be visible to OpenFlow
   * controllers; otherwise, it will be hidden.
   *
 - * The caller retains ownership of 'cls_rule' and 'actions'. */
 + * The caller retains ownership of 'cls_rule' and 'actions'.
 + *
 + * This is a helper function for in-band control and fail-open. */
  void
  ofproto_add_flow(struct ofproto *p, const struct cls_rule *cls_rule,
                   const union ofp_action *actions, size_t n_actions)
  {
      struct rule *rule;
 -    rule = rule_create(cls_rule, actions, n_actions, 0, 0, 0, false);
 -    rule_insert(p, rule);
 +    rule_create(p, cls_rule, actions, n_actions, 0, 0, 0, false, &rule);
  }
  
 +/* Searches for a rule with matching criteria exactly equal to 'target' in
 + * ofproto's table 0 and, if it finds one, deletes it.
 + *
 + * This is a helper function for in-band control and fail-open. */
  void
  ofproto_delete_flow(struct ofproto *ofproto, const struct cls_rule *target)
  {
      struct rule *rule;
  
 -    rule = rule_from_cls_rule(classifier_find_rule_exactly(&ofproto->cls,
 -                                                           target));
 -    if (rule) {
 -        rule_remove(ofproto, rule);
 -    }
 +    rule = rule_from_cls_rule(classifier_find_rule_exactly(
 +                                  &ofproto->tables[0], target));
 +    ofproto_rule_destroy(rule);
  }
  
  static void
  ofproto_flush_flows__(struct ofproto *ofproto)
  {
 -    struct facet *facet, *next_facet;
 -    struct rule *rule, *next_rule;
 -    struct cls_cursor cursor;
 +    size_t i;
  
      COVERAGE_INC(ofproto_flush);
  
 -    HMAP_FOR_EACH_SAFE (facet, next_facet, hmap_node, &ofproto->facets) {
 -        /* Mark the facet as not installed so that facet_remove() doesn't
 -         * bother trying to uninstall it.  There is no point in uninstalling it
 -         * individually since we are about to blow away all the facets with
 -         * dpif_flow_flush(). */
 -        facet->installed = false;
 -        facet->dp_packet_count = 0;
 -        facet->dp_byte_count = 0;
 -        facet_remove(ofproto, facet);
 +    if (ofproto->ofproto_class->flush) {
 +        ofproto->ofproto_class->flush(ofproto);
      }
  
 -    cls_cursor_init(&cursor, &ofproto->cls, NULL);
 -    CLS_CURSOR_FOR_EACH_SAFE (rule, next_rule, cr, &cursor) {
 -        rule_remove(ofproto, rule);
 -    }
 +    for (i = 0; i < ofproto->n_tables; i++) {
 +        struct rule *rule, *next_rule;
 +        struct cls_cursor cursor;
  
 -    dpif_flow_flush(ofproto->dpif);
 +        cls_cursor_init(&cursor, &ofproto->tables[i], NULL);
 +        CLS_CURSOR_FOR_EACH_SAFE (rule, next_rule, cr, &cursor) {
 +            ofproto_rule_destroy(rule);
 +        }
 +    }
  }
  
 +/* Deletes all of the flows from all of ofproto's flow tables, then
 + * reintroduces rules required by in-band control and fail open. */
  void
  ofproto_flush_flows(struct ofproto *ofproto)
  {
  static void
  reinit_ports(struct ofproto *p)
  {
 -    struct dpif_port_dump dump;
 +    struct ofproto_port_dump dump;
      struct sset devnames;
      struct ofport *ofport;
 -    struct dpif_port dpif_port;
 +    struct ofproto_port ofproto_port;
      const char *devname;
  
      COVERAGE_INC(ofproto_reinit_ports);
      HMAP_FOR_EACH (ofport, hmap_node, &p->ports) {
          sset_add(&devnames, netdev_get_name(ofport->netdev));
      }
 -    DPIF_PORT_FOR_EACH (&dpif_port, &dump, p->dpif) {
 -        sset_add(&devnames, dpif_port.name);
 +    OFPROTO_PORT_FOR_EACH (&ofproto_port, &dump, p) {
 +        sset_add(&devnames, ofproto_port.name);
      }
  
      SSET_FOR_EACH (devname, &devnames) {
      sset_destroy(&devnames);
  }
  
 -/* Opens and returns a netdev for 'dpif_port', or a null pointer if the netdev
 - * cannot be opened.  On success, also fills in 'opp', in *HOST* byte order. */
 +/* Opens and returns a netdev for 'ofproto_port', or a null pointer if the
 + * netdev cannot be opened.  On success, also fills in 'opp'.  */
  static struct netdev *
 -ofport_open(const struct dpif_port *dpif_port, struct ofp_phy_port *opp)
 +ofport_open(const struct ofproto_port *ofproto_port, struct ofp_phy_port *opp)
  {
 +    uint32_t curr, advertised, supported, peer;
      struct netdev_options netdev_options;
      enum netdev_flags flags;
      struct netdev *netdev;
      int error;
  
      memset(&netdev_options, 0, sizeof netdev_options);
 -    netdev_options.name = dpif_port->name;
 -    netdev_options.type = dpif_port->type;
 +    netdev_options.name = ofproto_port->name;
 +    netdev_options.type = ofproto_port->type;
      netdev_options.ethertype = NETDEV_ETH_TYPE_NONE;
  
      error = netdev_open(&netdev_options, &netdev);
      if (error) {
          VLOG_WARN_RL(&rl, "ignoring port %s (%"PRIu16") because netdev %s "
                       "cannot be opened (%s)",
 -                     dpif_port->name, dpif_port->port_no,
 -                     dpif_port->name, strerror(error));
 +                     ofproto_port->name, ofproto_port->ofp_port,
 +                     ofproto_port->name, strerror(error));
          return NULL;
      }
  
      netdev_get_flags(netdev, &flags);
 +    netdev_get_features(netdev, &curr, &advertised, &supported, &peer);
  
 -    opp->port_no = odp_port_to_ofp_port(dpif_port->port_no);
 +    opp->port_no = htons(ofproto_port->ofp_port);
      netdev_get_etheraddr(netdev, opp->hw_addr);
 -    ovs_strzcpy(opp->name, dpif_port->name, sizeof opp->name);
 -    opp->config = flags & NETDEV_UP ? 0 : OFPPC_PORT_DOWN;
 -    opp->state = netdev_get_carrier(netdev) ? 0 : OFPPS_LINK_DOWN;
 -    netdev_get_features(netdev, &opp->curr, &opp->advertised,
 -                        &opp->supported, &opp->peer);
 -    return netdev;
 -}
 +    ovs_strzcpy(opp->name, ofproto_port->name, sizeof opp->name);
 +    opp->config = flags & NETDEV_UP ? 0 : htonl(OFPPC_PORT_DOWN);
 +    opp->state = netdev_get_carrier(netdev) ? 0 : htonl(OFPPS_LINK_DOWN);
 +    opp->curr = htonl(curr);
 +    opp->advertised = htonl(advertised);
 +    opp->supported = htonl(supported);
 +    opp->peer = htonl(peer);
  
 -static bool
 -ofport_conflicts(const struct ofproto *p, const struct dpif_port *dpif_port)
 -{
 -    if (get_port(p, dpif_port->port_no)) {
 -        VLOG_WARN_RL(&rl, "ignoring duplicate port %"PRIu16" in datapath",
 -                     dpif_port->port_no);
 -        return true;
 -    } else if (shash_find(&p->port_by_name, dpif_port->name)) {
 -        VLOG_WARN_RL(&rl, "ignoring duplicate device %s in datapath",
 -                     dpif_port->name);
 -        return true;
 -    } else {
 -        return false;
 -    }
 +    return netdev;
  }
  
  /* Returns true if most fields of 'a' and 'b' are equal.  Differences in name,
@@@ -1010,7 -1123,7 +1010,7 @@@ ofport_equal(const struct ofp_phy_port 
      BUILD_ASSERT_DECL(sizeof *a == 48); /* Detect ofp_phy_port changes. */
      return (!memcmp(a->hw_addr, b->hw_addr, sizeof a->hw_addr)
              && a->state == b->state
 -            && !((a->config ^ b->config) & OFPPC_PORT_DOWN)
 +            && !((a->config ^ b->config) & htonl(OFPPC_PORT_DOWN))
              && a->curr == b->curr
              && a->advertised == b->advertised
              && a->supported == b->supported
@@@ -1026,49 -1139,41 +1026,49 @@@ ofport_install(struct ofproto *p
  {
      const char *netdev_name = netdev_get_name(netdev);
      struct ofport *ofport;
 -
 -    connmgr_send_port_status(p->connmgr, opp, OFPPR_ADD);
 +    int error;
  
      /* Create ofport. */
 -    ofport = xmalloc(sizeof *ofport);
 +    ofport = p->ofproto_class->port_alloc();
 +    if (!ofport) {
 +        error = ENOMEM;
 +        goto error;
 +    }
 +    ofport->ofproto = p;
      ofport->netdev = netdev;
      ofport->opp = *opp;
 -    ofport->odp_port = ofp_port_to_odp_port(opp->port_no);
 -    ofport->cfm = NULL;
 +    ofport->ofp_port = ntohs(opp->port_no);
  
      /* Add port to 'p'. */
      netdev_monitor_add(p->netdev_monitor, ofport->netdev);
 -    hmap_insert(&p->ports, &ofport->hmap_node, hash_int(ofport->odp_port, 0));
 +    hmap_insert(&p->ports, &ofport->hmap_node, hash_int(ofport->ofp_port, 0));
      shash_add(&p->port_by_name, netdev_name, ofport);
 -    if (p->sflow) {
 -        ofproto_sflow_add_port(p->sflow, ofport->odp_port, netdev_name);
 +
 +    /* Let the ofproto_class initialize its private data. */
 +    error = p->ofproto_class->port_construct(ofport);
 +    if (error) {
 +        goto error;
 +    }
 +    connmgr_send_port_status(p->connmgr, opp, OFPPR_ADD);
 +    return;
 +
 +error:
 +    VLOG_WARN_RL(&rl, "%s: could not add port %s (%s)",
 +                 p->name, netdev_name, strerror(error));
 +    if (ofport) {
 +        ofport_destroy__(ofport);
 +    } else {
 +        netdev_close(netdev);
      }
  }
  
  /* Removes 'ofport' from 'p' and destroys it. */
  static void
 -ofport_remove(struct ofproto *p, struct ofport *ofport)
 +ofport_remove(struct ofport *ofport)
  {
 -    connmgr_send_port_status(p->connmgr, &ofport->opp, OFPPR_DELETE);
 -
 -    netdev_monitor_remove(p->netdev_monitor, ofport->netdev);
 -    hmap_remove(&p->ports, &ofport->hmap_node);
 -    shash_delete(&p->port_by_name,
 -                 shash_find(&p->port_by_name,
 -                            netdev_get_name(ofport->netdev)));
 -    if (p->sflow) {
 -        ofproto_sflow_del_port(p->sflow, ofport->odp_port);
 -    }
 -
 -    ofport_free(ofport);
 +    connmgr_send_port_status(ofport->ofproto->connmgr, &ofport->opp,
 +                             OFPPR_DELETE);
 +    ofport_destroy(ofport);
  }
  
  /* If 'ofproto' contains an ofport named 'name', removes it from 'ofproto' and
@@@ -1078,7 -1183,7 +1078,7 @@@ ofport_remove_with_name(struct ofproto 
  {
      struct ofport *port = shash_find_data(&ofproto->port_by_name, name);
      if (port) {
 -        ofport_remove(ofproto, port);
 +        ofport_remove(port);
      }
  }
  
   * Does not handle a name or port number change.  The caller must implement
   * such a change as a delete followed by an add.  */
  static void
 -ofport_modified(struct ofproto *ofproto, struct ofport *port,
 -                struct netdev *netdev, struct ofp_phy_port *opp)
 +ofport_modified(struct ofport *port, struct ofp_phy_port *opp)
  {
      memcpy(port->opp.hw_addr, opp->hw_addr, ETH_ADDR_LEN);
 -    port->opp.config = ((port->opp.config & ~OFPPC_PORT_DOWN)
 -                        | (opp->config & OFPPC_PORT_DOWN));
 +    port->opp.config = ((port->opp.config & ~htonl(OFPPC_PORT_DOWN))
 +                        | (opp->config & htonl(OFPPC_PORT_DOWN)));
      port->opp.state = opp->state;
      port->opp.curr = opp->curr;
      port->opp.advertised = opp->advertised;
      port->opp.supported = opp->supported;
      port->opp.peer = opp->peer;
  
 -    netdev_monitor_remove(ofproto->netdev_monitor, port->netdev);
 -    netdev_monitor_add(ofproto->netdev_monitor, netdev);
 -
 -    netdev_close(port->netdev);
 -    port->netdev = netdev;
 -
 -    connmgr_send_port_status(ofproto->connmgr, &port->opp, OFPPR_MODIFY);
 +    connmgr_send_port_status(port->ofproto->connmgr, &port->opp, OFPPR_MODIFY);
  }
  
 -static void
 -ofport_run(struct ofproto *ofproto, struct ofport *ofport)
 +void
 +ofproto_port_unregister(struct ofproto *ofproto, uint16_t ofp_port)
  {
 -    if (ofport->cfm) {
 -        cfm_run(ofport->cfm);
 -
 -        if (cfm_should_send_ccm(ofport->cfm)) {
 -            struct ofpbuf packet;
 -            struct ccm *ccm;
 -
 -            ofpbuf_init(&packet, 0);
 -            ccm = eth_compose(&packet, eth_addr_ccm, ofport->opp.hw_addr,
 -                              ETH_TYPE_CFM,  sizeof *ccm);
 -            cfm_compose_ccm(ofport->cfm, ccm);
 -            ofproto_send_packet(ofproto, ofport->odp_port, 0, &packet);
 -            ofpbuf_uninit(&packet);
 +    struct ofport *port = ofproto_get_port(ofproto, ofp_port);
 +    if (port) {
 +        if (port->ofproto->ofproto_class->set_cfm) {
 +            port->ofproto->ofproto_class->set_cfm(port, NULL, NULL, 0);
 +        }
 +        if (port->ofproto->ofproto_class->bundle_remove) {
 +            port->ofproto->ofproto_class->bundle_remove(port);
          }
      }
  }
  
  static void
 -ofport_wait(struct ofport *ofport)
 +ofport_destroy__(struct ofport *port)
  {
 -    if (ofport->cfm) {
 -        cfm_wait(ofport->cfm);
 -    }
 +    struct ofproto *ofproto = port->ofproto;
 +    const char *name = netdev_get_name(port->netdev);
 +
 +    netdev_monitor_remove(ofproto->netdev_monitor, port->netdev);
 +    hmap_remove(&ofproto->ports, &port->hmap_node);
 +    shash_delete(&ofproto->port_by_name,
 +                 shash_find(&ofproto->port_by_name, name));
 +
 +    netdev_close(port->netdev);
 +    ofproto->ofproto_class->port_dealloc(port);
  }
  
  static void
 -ofport_free(struct ofport *ofport)
 +ofport_destroy(struct ofport *port)
  {
 -    if (ofport) {
 -        cfm_destroy(ofport->cfm);
 -        netdev_close(ofport->netdev);
 -        free(ofport);
 -    }
 +    if (port) {
 +        port->ofproto->ofproto_class->port_destruct(port);
 +        ofport_destroy__(port);
 +     }
  }
  
 -static struct ofport *
 -get_port(const struct ofproto *ofproto, uint16_t odp_port)
 +struct ofport *
 +ofproto_get_port(const struct ofproto *ofproto, uint16_t ofp_port)
  {
      struct ofport *port;
  
      HMAP_FOR_EACH_IN_BUCKET (port, hmap_node,
 -                             hash_int(odp_port, 0), &ofproto->ports) {
 -        if (port->odp_port == odp_port) {
 +                             hash_int(ofp_port, 0), &ofproto->ports) {
 +        if (port->ofp_port == ofp_port) {
              return port;
          }
      }
  static void
  update_port(struct ofproto *ofproto, const char *name)
  {
 -    struct dpif_port dpif_port;
 +    struct ofproto_port ofproto_port;
      struct ofp_phy_port opp;
      struct netdev *netdev;
      struct ofport *port;
      COVERAGE_INC(ofproto_update_port);
  
      /* Fetch 'name''s location and properties from the datapath. */
 -    netdev = (!dpif_port_query_by_name(ofproto->dpif, name, &dpif_port)
 -              ? ofport_open(&dpif_port, &opp)
 +    netdev = (!ofproto_port_query_by_name(ofproto, name, &ofproto_port)
 +              ? ofport_open(&ofproto_port, &opp)
                : NULL);
      if (netdev) {
 -        port = get_port(ofproto, dpif_port.port_no);
 +        port = ofproto_get_port(ofproto, ofproto_port.ofp_port);
          if (port && !strcmp(netdev_get_name(port->netdev), name)) {
              /* 'name' hasn't changed location.  Any properties changed? */
              if (!ofport_equal(&port->opp, &opp)) {
 -                ofport_modified(ofproto, port, netdev, &opp);
 -            } else {
 -                netdev_close(netdev);
 +                ofport_modified(port, &opp);
 +            }
 +
 +            /* Install the newly opened netdev in case it has changed. */
 +            netdev_monitor_remove(ofproto->netdev_monitor, port->netdev);
 +            netdev_monitor_add(ofproto->netdev_monitor, netdev);
 +
 +            netdev_close(port->netdev);
 +            port->netdev = netdev;
 +
 +            if (port->ofproto->ofproto_class->port_modified) {
 +                port->ofproto->ofproto_class->port_modified(port);
              }
          } else {
              /* If 'port' is nonnull then its name differs from 'name' and thus
               * we should delete it.  If we think there's a port named 'name'
               * then its port number must be wrong now so delete it too. */
              if (port) {
 -                ofport_remove(ofproto, port);
 +                ofport_remove(port);
              }
              ofport_remove_with_name(ofproto, name);
              ofport_install(ofproto, netdev, &opp);
          /* Any port named 'name' is gone now. */
          ofport_remove_with_name(ofproto, name);
      }
 -    dpif_port_destroy(&dpif_port);
 +    ofproto_port_destroy(&ofproto_port);
  }
  
  static int
  init_ports(struct ofproto *p)
  {
 -    struct dpif_port_dump dump;
 -    struct dpif_port dpif_port;
 -
 -    DPIF_PORT_FOR_EACH (&dpif_port, &dump, p->dpif) {
 -        if (!ofport_conflicts(p, &dpif_port)) {
 +    struct ofproto_port_dump dump;
 +    struct ofproto_port ofproto_port;
 +
 +    OFPROTO_PORT_FOR_EACH (&ofproto_port, &dump, p) {
 +        uint16_t ofp_port = ofproto_port.ofp_port;
 +        if (ofproto_get_port(p, ofp_port)) {
 +            VLOG_WARN_RL(&rl, "ignoring duplicate port %"PRIu16" in datapath",
 +                         ofp_port);
 +        } else if (shash_find(&p->port_by_name, ofproto_port.name)) {
 +            VLOG_WARN_RL(&rl, "ignoring duplicate device %s in datapath",
 +                         ofproto_port.name);
 +        } else {
              struct ofp_phy_port opp;
              struct netdev *netdev;
  
 -            netdev = ofport_open(&dpif_port, &opp);
 +            netdev = ofport_open(&ofproto_port, &opp);
              if (netdev) {
                  ofport_install(p, netdev, &opp);
              }
      return 0;
  }
  \f
 -/* Returns true if 'rule' should be hidden from the controller.
 - *
 - * Rules with priority higher than UINT16_MAX are set up by ofproto itself
 - * (e.g. by in-band control) and are intentionally hidden from the
 - * controller. */
 -static bool
 -rule_is_hidden(const struct rule *rule)
 -{
 -    return rule->cr.priority > UINT16_MAX;
 -}
 -
 -/* Creates and returns a new rule initialized as specified.
 - *
 - * The caller is responsible for inserting the rule into the classifier (with
 - * rule_insert()). */
 -static struct rule *
 -rule_create(const struct cls_rule *cls_rule,
 +/* Creates a new rule initialized as specified, inserts it into 'ofproto''s
 + * flow table, and stores the new rule into '*rulep'.  Returns 0 on success,
 + * otherwise a positive errno value or OpenFlow error code. */
 +static int
 +rule_create(struct ofproto *ofproto, const struct cls_rule *cls_rule,
              const union ofp_action *actions, size_t n_actions,
              uint16_t idle_timeout, uint16_t hard_timeout,
 -            ovs_be64 flow_cookie, bool send_flow_removed)
 +            ovs_be64 flow_cookie, bool send_flow_removed,
 +            struct rule **rulep)
  {
 -    struct rule *rule = xzalloc(sizeof *rule);
 +    struct rule *rule;
 +    int error;
 +
 +    rule = ofproto->ofproto_class->rule_alloc();
 +    if (!rule) {
 +        error = ENOMEM;
 +        goto error;
 +    }
 +
 +    rule->ofproto = ofproto;
      rule->cr = *cls_rule;
 +    rule->flow_cookie = flow_cookie;
 +    rule->created = time_msec();
      rule->idle_timeout = idle_timeout;
      rule->hard_timeout = hard_timeout;
 -    rule->flow_cookie = flow_cookie;
 -    rule->used = rule->created = time_msec();
      rule->send_flow_removed = send_flow_removed;
 -    list_init(&rule->facets);
      if (n_actions > 0) {
 -        rule->n_actions = n_actions;
          rule->actions = xmemdup(actions, n_actions * sizeof *actions);
 +    } else {
 +        rule->actions = NULL;
      }
 +    rule->n_actions = n_actions;
  
 -    return rule;
 -}
 +    error = ofproto->ofproto_class->rule_construct(rule);
 +    if (error) {
 +        ofproto_rule_destroy__(rule);
 +        goto error;
 +    }
  
 -static struct rule *
 -rule_from_cls_rule(const struct cls_rule *cls_rule)
 -{
 -    return cls_rule ? CONTAINER_OF(cls_rule, struct rule, cr) : NULL;
 +    *rulep = rule;
 +    return 0;
 +
 +error:
 +    VLOG_WARN_RL(&rl, "%s: failed to create rule (%s)",
 +                 ofproto->name, strerror(error));
 +    *rulep = NULL;
 +    return error;
  }
  
  static void
 -rule_free(struct rule *rule)
 +ofproto_rule_destroy__(struct rule *rule)
  {
      free(rule->actions);
 -    free(rule);
 +    rule->ofproto->ofproto_class->rule_dealloc(rule);
  }
  
 -/* Destroys 'rule' and iterates through all of its facets and revalidates them,
 - * destroying any that no longer has a rule (which is probably all of them).
 - *
 - * The caller must have already removed 'rule' from the classifier. */
 -static void
 -rule_destroy(struct ofproto *ofproto, struct rule *rule)
 +/* Destroys 'rule' and removes it from the flow table and the datapath. */
 +void
 +ofproto_rule_destroy(struct rule *rule)
  {
 -    struct facet *facet, *next_facet;
 -    LIST_FOR_EACH_SAFE (facet, next_facet, list_node, &rule->facets) {
 -        facet_revalidate(ofproto, facet);
 +    if (rule) {
 +        rule->ofproto->ofproto_class->rule_destruct(rule);
 +        ofproto_rule_destroy__(rule);
      }
 -    rule_free(rule);
  }
  
  /* Returns true if 'rule' has an OpenFlow OFPAT_OUTPUT or OFPAT_ENQUEUE action
@@@ -1317,133 -1411,1127 +1317,133 @@@ rule_has_out_port(const struct rule *ru
      return false;
  }
  
 -/* Executes, within 'ofproto', the 'n_actions' actions in 'actions' on
 - * 'packet', which arrived on 'in_port'.
 - *
 - * Takes ownership of 'packet'. */
 -static bool
 -execute_odp_actions(struct ofproto *ofproto, const struct flow *flow,
 -                    const struct nlattr *odp_actions, size_t actions_len,
 -                    struct ofpbuf *packet)
 -{
 -    if (actions_len == NLA_ALIGN(NLA_HDRLEN + sizeof(uint64_t))
 -        && odp_actions->nla_type == ODP_ACTION_ATTR_CONTROLLER) {
 -        /* As an optimization, avoid a round-trip from userspace to kernel to
 -         * userspace.  This also avoids possibly filling up kernel packet
 -         * buffers along the way. */
 -        struct dpif_upcall upcall;
 -
 -        upcall.type = DPIF_UC_ACTION;
 -        upcall.packet = packet;
 -        upcall.key = NULL;
 -        upcall.key_len = 0;
 -        upcall.userdata = nl_attr_get_u64(odp_actions);
 -        upcall.sample_pool = 0;
 -        upcall.actions = NULL;
 -        upcall.actions_len = 0;
 -
 -        send_packet_in(ofproto, &upcall, flow, false);
 -
 -        return true;
 -    } else {
 -        int error;
 -
 -        error = dpif_execute(ofproto->dpif, odp_actions, actions_len, packet);
 -        ofpbuf_delete(packet);
 -        return !error;
 -    }
 -}
 -
 -/* Executes the actions indicated by 'facet' on 'packet' and credits 'facet''s
 +/* Executes the actions indicated by 'rule' on 'packet' and credits 'rule''s
   * statistics appropriately.  'packet' must have at least sizeof(struct
   * ofp_packet_in) bytes of headroom.
   *
 - * For correct results, 'packet' must actually be in 'facet''s flow; that is,
 - * applying flow_extract() to 'packet' would yield the same flow as
 - * 'facet->flow'.
 - *
 - * 'facet' must have accurately composed ODP actions; that is, it must not be
 - * in need of revalidation.
 - *
 - * Takes ownership of 'packet'. */
 -static void
 -facet_execute(struct ofproto *ofproto, struct facet *facet,
 -              struct ofpbuf *packet)
 -{
 -    struct dpif_flow_stats stats;
 -
 -    assert(ofpbuf_headroom(packet) >= sizeof(struct ofp_packet_in));
 -
 -    flow_extract_stats(&facet->flow, packet, &stats);
 -    stats.used = time_msec();
 -    if (execute_odp_actions(ofproto, &facet->flow,
 -                            facet->actions, facet->actions_len, packet)) {
 -        facet_update_stats(ofproto, facet, &stats);
 -    }
 -}
 -
 -/* Executes the actions indicated by 'rule' on 'packet' and credits 'rule''s
 - * statistics (or the statistics for one of its facets) appropriately.
 - * 'packet' must have at least sizeof(struct ofp_packet_in) bytes of headroom.
 - *
   * 'packet' doesn't necessarily have to match 'rule'.  'rule' will be credited
   * with statistics for 'packet' either way.
   *
 - * Takes ownership of 'packet'. */
 -static void
 -rule_execute(struct ofproto *ofproto, struct rule *rule, uint16_t in_port,
 -             struct ofpbuf *packet)
 -{
 -    struct action_xlate_ctx ctx;
 -    struct ofpbuf *odp_actions;
 -    struct facet *facet;
 -    struct flow flow;
 -    size_t size;
 -
 -    assert(ofpbuf_headroom(packet) >= sizeof(struct ofp_packet_in));
 -
 -    flow_extract(packet, 0, in_port, &flow);
 -
 -    /* First look for a related facet.  If we find one, account it to that. */
 -    facet = facet_lookup_valid(ofproto, &flow);
 -    if (facet && facet->rule == rule) {
 -        facet_execute(ofproto, facet, packet);
 -        return;
 -    }
 -
 -    /* Otherwise, if 'rule' is in fact the correct rule for 'packet', then
 -     * create a new facet for it and use that. */
 -    if (rule_lookup(ofproto, &flow) == rule) {
 -        facet = facet_create(ofproto, rule, &flow, packet);
 -        facet_execute(ofproto, facet, packet);
 -        facet_install(ofproto, facet, true);
 -        return;
 -    }
 -
 -    /* We can't account anything to a facet.  If we were to try, then that
 -     * facet would have a non-matching rule, busting our invariants. */
 -    action_xlate_ctx_init(&ctx, ofproto, &flow, packet);
 -    odp_actions = xlate_actions(&ctx, rule->actions, rule->n_actions);
 -    size = packet->size;
 -    if (execute_odp_actions(ofproto, &flow, odp_actions->data,
 -                            odp_actions->size, packet)) {
 -        rule->used = time_msec();
 -        rule->packet_count++;
 -        rule->byte_count += size;
 -        flow_push_stats(ofproto, rule, &flow, 1, size, rule->used);
 -    }
 -    ofpbuf_delete(odp_actions);
 -}
 -
 -/* Inserts 'rule' into 'p''s flow table. */
 -static void
 -rule_insert(struct ofproto *p, struct rule *rule)
 -{
 -    struct rule *displaced_rule;
 -
 -    displaced_rule = rule_from_cls_rule(classifier_insert(&p->cls, &rule->cr));
 -    if (displaced_rule) {
 -        rule_destroy(p, displaced_rule);
 -    }
 -    p->need_revalidate = true;
 -}
 -
 -/* Creates and returns a new facet within 'ofproto' owned by 'rule', given a
 - * 'flow' and an example 'packet' within that flow.
 - *
 - * The caller must already have determined that no facet with an identical
 - * 'flow' exists in 'ofproto' and that 'flow' is the best match for 'rule' in
 - * 'ofproto''s classifier table. */
 -static struct facet *
 -facet_create(struct ofproto *ofproto, struct rule *rule,
 -             const struct flow *flow, const struct ofpbuf *packet)
 -{
 -    struct facet *facet;
 -
 -    facet = xzalloc(sizeof *facet);
 -    facet->used = time_msec();
 -    hmap_insert(&ofproto->facets, &facet->hmap_node, flow_hash(flow, 0));
 -    list_push_back(&rule->facets, &facet->list_node);
 -    facet->rule = rule;
 -    facet->flow = *flow;
 -    netflow_flow_init(&facet->nf_flow);
 -    netflow_flow_update_time(ofproto->netflow, &facet->nf_flow, facet->used);
 -
 -    facet_make_actions(ofproto, facet, packet);
 -
 -    return facet;
 -}
 -
 -static void
 -facet_free(struct facet *facet)
 -{
 -    free(facet->actions);
 -    free(facet);
 -}
 -
 -/* Remove 'rule' from 'ofproto' and free up the associated memory:
 - *
 - *   - Removes 'rule' from the classifier.
 - *
 - *   - If 'rule' has facets, revalidates them (and possibly uninstalls and
 - *     destroys them), via rule_destroy().
 - */
 -static void
 -rule_remove(struct ofproto *ofproto, struct rule *rule)
 -{
 -    COVERAGE_INC(ofproto_del_rule);
 -    ofproto->need_revalidate = true;
 -    classifier_remove(&ofproto->cls, &rule->cr);
 -    rule_destroy(ofproto, rule);
 -}
 -
 -/* Remove 'facet' from 'ofproto' and free up the associated memory:
 - *
 - *   - If 'facet' was installed in the datapath, uninstalls it and updates its
 - *     rule's statistics, via facet_uninstall().
 - *
 - *   - Removes 'facet' from its rule and from ofproto->facets.
 - */
 -static void
 -facet_remove(struct ofproto *ofproto, struct facet *facet)
 -{
 -    facet_uninstall(ofproto, facet);
 -    facet_flush_stats(ofproto, facet);
 -    hmap_remove(&ofproto->facets, &facet->hmap_node);
 -    list_remove(&facet->list_node);
 -    facet_free(facet);
 -}
 -
 -/* Composes the ODP actions for 'facet' based on its rule's actions. */
 -static void
 -facet_make_actions(struct ofproto *p, struct facet *facet,
 -                   const struct ofpbuf *packet)
 -{
 -    const struct rule *rule = facet->rule;
 -    struct ofpbuf *odp_actions;
 -    struct action_xlate_ctx ctx;
 -
 -    action_xlate_ctx_init(&ctx, p, &facet->flow, packet);
 -    odp_actions = xlate_actions(&ctx, rule->actions, rule->n_actions);
 -    facet->tags = ctx.tags;
 -    facet->may_install = ctx.may_set_up_flow;
 -    facet->nf_flow.output_iface = ctx.nf_output_iface;
 -
 -    if (facet->actions_len != odp_actions->size
 -        || memcmp(facet->actions, odp_actions->data, odp_actions->size)) {
 -        free(facet->actions);
 -        facet->actions_len = odp_actions->size;
 -        facet->actions = xmemdup(odp_actions->data, odp_actions->size);
 -    }
 -
 -    ofpbuf_delete(odp_actions);
 -}
 -
 -/* Updates 'facet''s flow in the datapath setting its actions to 'actions_len'
 - * bytes of actions in 'actions'.  If 'stats' is non-null, statistics counters
 - * in the datapath will be zeroed and 'stats' will be updated with traffic new
 - * since 'facet' was last updated.
 - *
 - * Returns 0 if successful, otherwise a positive errno value.*/
 -static int
 -facet_put__(struct ofproto *ofproto, struct facet *facet,
 -            const struct nlattr *actions, size_t actions_len,
 -            struct dpif_flow_stats *stats)
 -{
 -    struct odputil_keybuf keybuf;
 -    enum dpif_flow_put_flags flags;
 -    struct ofpbuf key;
 -    int ret;
 -
 -    flags = DPIF_FP_CREATE | DPIF_FP_MODIFY;
 -    if (stats) {
 -        flags |= DPIF_FP_ZERO_STATS;
 -    }
 -
 -    ofpbuf_use_stack(&key, &keybuf, sizeof keybuf);
 -    odp_flow_key_from_flow(&key, &facet->flow);
 -
 -    ret = dpif_flow_put(ofproto->dpif, flags, key.data, key.size,
 -                        actions, actions_len, stats);
 -
 -    if (stats) {
 -        facet_reset_dp_stats(facet, stats);
 -    }
 -
 -    return ret;
 -}
 -
 -/* If 'facet' is installable, inserts or re-inserts it into 'p''s datapath.  If
 - * 'zero_stats' is true, clears any existing statistics from the datapath for
 - * 'facet'. */
 -static void
 -facet_install(struct ofproto *p, struct facet *facet, bool zero_stats)
 -{
 -    struct dpif_flow_stats stats;
 -
 -    if (facet->may_install
 -        && !facet_put__(p, facet, facet->actions, facet->actions_len,
 -                        zero_stats ? &stats : NULL)) {
 -        facet->installed = true;
 -    }
 -}
 -
 -/* Ensures that the bytes in 'facet', plus 'extra_bytes', have been passed up
 - * to the accounting hook function in the ofhooks structure. */
 -static void
 -facet_account(struct ofproto *ofproto,
 -              struct facet *facet, uint64_t extra_bytes)
 -{
 -    uint64_t total_bytes = facet->byte_count + extra_bytes;
 -
 -    if (ofproto->ofhooks->account_flow_cb
 -        && total_bytes > facet->accounted_bytes)
 -    {
 -        ofproto->ofhooks->account_flow_cb(
 -            &facet->flow, facet->tags, facet->actions, facet->actions_len,
 -            total_bytes - facet->accounted_bytes, ofproto->aux);
 -        facet->accounted_bytes = total_bytes;
 -    }
 -}
 -
 -/* If 'rule' is installed in the datapath, uninstalls it. */
 -static void
 -facet_uninstall(struct ofproto *p, struct facet *facet)
 -{
 -    if (facet->installed) {
 -        struct odputil_keybuf keybuf;
 -        struct dpif_flow_stats stats;
 -        struct ofpbuf key;
 -        int error;
 -
 -        ofpbuf_use_stack(&key, &keybuf, sizeof keybuf);
 -        odp_flow_key_from_flow(&key, &facet->flow);
 -
 -        error = dpif_flow_del(p->dpif, key.data, key.size, &stats);
 -        facet_reset_dp_stats(facet, &stats);
 -        if (!error) {
 -            facet_update_stats(p, facet, &stats);
 -        }
 -
 -        facet->installed = false;
 -    } else {
 -        assert(facet->dp_packet_count == 0);
 -        assert(facet->dp_byte_count == 0);
 -    }
 -}
 -
 -/* Returns true if the only action for 'facet' is to send to the controller.
 - * (We don't report NetFlow expiration messages for such facets because they
 - * are just part of the control logic for the network, not real traffic). */
 -static bool
 -facet_is_controller_flow(struct facet *facet)
 -{
 -    return (facet
 -            && facet->rule->n_actions == 1
 -            && action_outputs_to_port(&facet->rule->actions[0],
 -                                      htons(OFPP_CONTROLLER)));
 -}
 -
 -/* Resets 'facet''s datapath statistics counters.  This should be called when
 - * 'facet''s statistics are cleared in the datapath.  If 'stats' is non-null,
 - * it should contain the statistics returned by dpif when 'facet' was reset in
 - * the datapath.  'stats' will be modified to only included statistics new
 - * since 'facet' was last updated. */
 -static void
 -facet_reset_dp_stats(struct facet *facet, struct dpif_flow_stats *stats)
 -{
 -    if (stats && facet->dp_packet_count < stats->n_packets
 -        && facet->dp_byte_count < stats->n_bytes) {
 -        stats->n_packets -= facet->dp_packet_count;
 -        stats->n_bytes -= facet->dp_byte_count;
 -    }
 -
 -    facet->dp_packet_count = 0;
 -    facet->dp_byte_count = 0;
 -}
 -
 -/* Folds all of 'facet''s statistics into its rule.  Also updates the
 - * accounting ofhook and emits a NetFlow expiration if appropriate.  All of
 - * 'facet''s statistics in the datapath should have been zeroed and folded into
 - * its packet and byte counts before this function is called. */
 -static void
 -facet_flush_stats(struct ofproto *ofproto, struct facet *facet)
 -{
 -    assert(!facet->dp_byte_count);
 -    assert(!facet->dp_packet_count);
 -
 -    facet_push_stats(ofproto, facet);
 -    facet_account(ofproto, facet, 0);
 -
 -    if (ofproto->netflow && !facet_is_controller_flow(facet)) {
 -        struct ofexpired expired;
 -        expired.flow = facet->flow;
 -        expired.packet_count = facet->packet_count;
 -        expired.byte_count = facet->byte_count;
 -        expired.used = facet->used;
 -        netflow_expire(ofproto->netflow, &facet->nf_flow, &expired);
 -    }
 -
 -    facet->rule->packet_count += facet->packet_count;
 -    facet->rule->byte_count += facet->byte_count;
 -
 -    /* Reset counters to prevent double counting if 'facet' ever gets
 -     * reinstalled. */
 -    facet->packet_count = 0;
 -    facet->byte_count = 0;
 -    facet->rs_packet_count = 0;
 -    facet->rs_byte_count = 0;
 -    facet->accounted_bytes = 0;
 -
 -    netflow_flow_clear(&facet->nf_flow);
 -}
 -
 -/* Searches 'ofproto''s table of facets for one exactly equal to 'flow'.
 - * Returns it if found, otherwise a null pointer.
 - *
 - * The returned facet might need revalidation; use facet_lookup_valid()
 - * instead if that is important. */
 -static struct facet *
 -facet_find(struct ofproto *ofproto, const struct flow *flow)
 -{
 -    struct facet *facet;
 -
 -    HMAP_FOR_EACH_WITH_HASH (facet, hmap_node, flow_hash(flow, 0),
 -                             &ofproto->facets) {
 -        if (flow_equal(flow, &facet->flow)) {
 -            return facet;
 -        }
 -    }
 -
 -    return NULL;
 -}
 -
 -/* Searches 'ofproto''s table of facets for one exactly equal to 'flow'.
 - * Returns it if found, otherwise a null pointer.
 - *
 - * The returned facet is guaranteed to be valid. */
 -static struct facet *
 -facet_lookup_valid(struct ofproto *ofproto, const struct flow *flow)
 -{
 -    struct facet *facet = facet_find(ofproto, flow);
 -
 -    /* The facet we found might not be valid, since we could be in need of
 -     * revalidation.  If it is not valid, don't return it. */
 -    if (facet
 -        && ofproto->need_revalidate
 -        && !facet_revalidate(ofproto, facet)) {
 -        COVERAGE_INC(ofproto_invalidated);
 -        return NULL;
 -    }
 -
 -    return facet;
 -}
 -
 -/* Re-searches 'ofproto''s classifier for a rule matching 'facet':
 - *
 - *   - If the rule found is different from 'facet''s current rule, moves
 - *     'facet' to the new rule and recompiles its actions.
 - *
 - *   - If the rule found is the same as 'facet''s current rule, leaves 'facet'
 - *     where it is and recompiles its actions anyway.
 - *
 - *   - If there is none, destroys 'facet'.
 - *
 - * Returns true if 'facet' still exists, false if it has been destroyed. */
 -static bool
 -facet_revalidate(struct ofproto *ofproto, struct facet *facet)
 -{
 -    struct action_xlate_ctx ctx;
 -    struct ofpbuf *odp_actions;
 -    struct rule *new_rule;
 -    bool actions_changed;
 -
 -    COVERAGE_INC(facet_revalidate);
 -
 -    /* Determine the new rule. */
 -    new_rule = rule_lookup(ofproto, &facet->flow);
 -    if (!new_rule) {
 -        /* No new rule, so delete the facet. */
 -        facet_remove(ofproto, facet);
 -        return false;
 -    }
 -
 -    /* Calculate new ODP actions.
 -     *
 -     * We do not modify any 'facet' state yet, because we might need to, e.g.,
 -     * emit a NetFlow expiration and, if so, we need to have the old state
 -     * around to properly compose it. */
 -    action_xlate_ctx_init(&ctx, ofproto, &facet->flow, NULL);
 -    odp_actions = xlate_actions(&ctx, new_rule->actions, new_rule->n_actions);
 -    actions_changed = (facet->actions_len != odp_actions->size
 -                       || memcmp(facet->actions, odp_actions->data,
 -                                 facet->actions_len));
 -
 -    /* If the ODP actions changed or the installability changed, then we need
 -     * to talk to the datapath. */
 -    if (actions_changed || ctx.may_set_up_flow != facet->installed) {
 -        if (ctx.may_set_up_flow) {
 -            struct dpif_flow_stats stats;
 -
 -            facet_put__(ofproto, facet,
 -                        odp_actions->data, odp_actions->size, &stats);
 -            facet_update_stats(ofproto, facet, &stats);
 -        } else {
 -            facet_uninstall(ofproto, facet);
 -        }
 -
 -        /* The datapath flow is gone or has zeroed stats, so push stats out of
 -         * 'facet' into 'rule'. */
 -        facet_flush_stats(ofproto, facet);
 -    }
 -
 -    /* Update 'facet' now that we've taken care of all the old state. */
 -    facet->tags = ctx.tags;
 -    facet->nf_flow.output_iface = ctx.nf_output_iface;
 -    facet->may_install = ctx.may_set_up_flow;
 -    if (actions_changed) {
 -        free(facet->actions);
 -        facet->actions_len = odp_actions->size;
 -        facet->actions = xmemdup(odp_actions->data, odp_actions->size);
 -    }
 -    if (facet->rule != new_rule) {
 -        COVERAGE_INC(facet_changed_rule);
 -        list_remove(&facet->list_node);
 -        list_push_back(&new_rule->facets, &facet->list_node);
 -        facet->rule = new_rule;
 -        facet->used = new_rule->created;
 -        facet->rs_used = facet->used;
 -    }
 -
 -    ofpbuf_delete(odp_actions);
 -
 -    return true;
 -}
 -\f
 -static void
 -send_error_oh(const struct ofconn *ofconn, const struct ofp_header *oh,
 -              int error)
 -{
 -    struct ofpbuf *buf = ofputil_encode_error_msg(error, oh);
 -    if (buf) {
 -        COVERAGE_INC(ofproto_error);
 -        ofconn_send_reply(ofconn, buf);
 -    }
 -}
 -
 -static int
 -handle_echo_request(struct ofconn *ofconn, const struct ofp_header *oh)
 -{
 -    ofconn_send_reply(ofconn, make_echo_reply(oh));
 -    return 0;
 -}
 -
 -static int
 -handle_features_request(struct ofconn *ofconn, const struct ofp_header *oh)
 -{
 -    struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
 -    struct ofp_switch_features *osf;
 -    struct ofpbuf *buf;
 -    struct ofport *port;
 -
 -    osf = make_openflow_xid(sizeof *osf, OFPT_FEATURES_REPLY, oh->xid, &buf);
 -    osf->datapath_id = htonll(ofproto->datapath_id);
 -    osf->n_buffers = htonl(pktbuf_capacity());
 -    osf->n_tables = 2;
 -    osf->capabilities = htonl(OFPC_FLOW_STATS | OFPC_TABLE_STATS |
 -                              OFPC_PORT_STATS | OFPC_ARP_MATCH_IP);
 -    osf->actions = htonl((1u << OFPAT_OUTPUT) |
 -                         (1u << OFPAT_SET_VLAN_VID) |
 -                         (1u << OFPAT_SET_VLAN_PCP) |
 -                         (1u << OFPAT_STRIP_VLAN) |
 -                         (1u << OFPAT_SET_DL_SRC) |
 -                         (1u << OFPAT_SET_DL_DST) |
 -                         (1u << OFPAT_SET_NW_SRC) |
 -                         (1u << OFPAT_SET_NW_DST) |
 -                         (1u << OFPAT_SET_NW_TOS) |
 -                         (1u << OFPAT_SET_TP_SRC) |
 -                         (1u << OFPAT_SET_TP_DST) |
 -                         (1u << OFPAT_ENQUEUE));
 -
 -    HMAP_FOR_EACH (port, hmap_node, &ofproto->ports) {
 -        hton_ofp_phy_port(ofpbuf_put(buf, &port->opp, sizeof port->opp));
 -    }
 -
 -    ofconn_send_reply(ofconn, buf);
 -    return 0;
 -}
 -
 -static int
 -handle_get_config_request(struct ofconn *ofconn, const struct ofp_header *oh)
 -{
 -    struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
 -    struct ofpbuf *buf;
 -    struct ofp_switch_config *osc;
 -    uint16_t flags;
 -    bool drop_frags;
 -
 -    /* Figure out flags. */
 -    dpif_get_drop_frags(ofproto->dpif, &drop_frags);
 -    flags = drop_frags ? OFPC_FRAG_DROP : OFPC_FRAG_NORMAL;
 -
 -    /* Send reply. */
 -    osc = make_openflow_xid(sizeof *osc, OFPT_GET_CONFIG_REPLY, oh->xid, &buf);
 -    osc->flags = htons(flags);
 -    osc->miss_send_len = htons(ofconn_get_miss_send_len(ofconn));
 -    ofconn_send_reply(ofconn, buf);
 -
 -    return 0;
 -}
 -
 -static int
 -handle_set_config(struct ofconn *ofconn, const struct ofp_switch_config *osc)
 -{
 -    struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
 -    uint16_t flags = ntohs(osc->flags);
 -
 -    if (ofconn_get_type(ofconn) == OFCONN_PRIMARY
 -        && ofconn_get_role(ofconn) != NX_ROLE_SLAVE) {
 -        switch (flags & OFPC_FRAG_MASK) {
 -        case OFPC_FRAG_NORMAL:
 -            dpif_set_drop_frags(ofproto->dpif, false);
 -            break;
 -        case OFPC_FRAG_DROP:
 -            dpif_set_drop_frags(ofproto->dpif, true);
 -            break;
 -        default:
 -            VLOG_WARN_RL(&rl, "requested bad fragment mode (flags=%"PRIx16")",
 -                         osc->flags);
 -            break;
 -        }
 -    }
 -
 -    ofconn_set_miss_send_len(ofconn, ntohs(osc->miss_send_len));
 -
 -    return 0;
 -}
 -
 -static void do_xlate_actions(const union ofp_action *in, size_t n_in,
 -                             struct action_xlate_ctx *ctx);
 -
 -static void
 -add_output_action(struct action_xlate_ctx *ctx, uint16_t port)
 -{
 -    const struct ofport *ofport = get_port(ctx->ofproto, port);
 -
 -    if (ofport) {
 -        if (ofport->opp.config & OFPPC_NO_FWD) {
 -            /* Forwarding disabled on port. */
 -            return;
 -        }
 -    } else {
 -        /*
 -         * We don't have an ofport record for this port, but it doesn't hurt to
 -         * allow forwarding to it anyhow.  Maybe such a port will appear later
 -         * and we're pre-populating the flow table.
 -         */
 -    }
 -
 -    nl_msg_put_u32(ctx->odp_actions, ODP_ACTION_ATTR_OUTPUT, port);
 -    ctx->nf_output_iface = port;
 -}
 -
 -static struct rule *
 -rule_lookup(struct ofproto *ofproto, const struct flow *flow)
 -{
 -    return rule_from_cls_rule(classifier_lookup(&ofproto->cls, flow));
 -}
 -
 -static void
 -xlate_table_action(struct action_xlate_ctx *ctx, uint16_t in_port)
 -{
 -    if (ctx->recurse < MAX_RESUBMIT_RECURSION) {
 -        uint16_t old_in_port;
 -        struct rule *rule;
 -
 -        /* Look up a flow with 'in_port' as the input port.  Then restore the
 -         * original input port (otherwise OFPP_NORMAL and OFPP_IN_PORT will
 -         * have surprising behavior). */
 -        old_in_port = ctx->flow.in_port;
 -        ctx->flow.in_port = in_port;
 -        rule = rule_lookup(ctx->ofproto, &ctx->flow);
 -        ctx->flow.in_port = old_in_port;
 -
 -        if (ctx->resubmit_hook) {
 -            ctx->resubmit_hook(ctx, rule);
 -        }
 -
 -        if (rule) {
 -            ctx->recurse++;
 -            do_xlate_actions(rule->actions, rule->n_actions, ctx);
 -            ctx->recurse--;
 -        }
 -    } else {
 -        static struct vlog_rate_limit recurse_rl = VLOG_RATE_LIMIT_INIT(1, 1);
 -
 -        VLOG_ERR_RL(&recurse_rl, "NXAST_RESUBMIT recursed over %d times",
 -                    MAX_RESUBMIT_RECURSION);
 -    }
 -}
 -
 -static void
 -flood_packets(struct ofproto *ofproto, uint16_t odp_in_port, uint32_t mask,
 -              uint16_t *nf_output_iface, struct ofpbuf *odp_actions)
 -{
 -    struct ofport *ofport;
 -
 -    HMAP_FOR_EACH (ofport, hmap_node, &ofproto->ports) {
 -        uint16_t odp_port = ofport->odp_port;
 -        if (odp_port != odp_in_port && !(ofport->opp.config & mask)) {
 -            nl_msg_put_u32(odp_actions, ODP_ACTION_ATTR_OUTPUT, odp_port);
 -        }
 -    }
 -    *nf_output_iface = NF_OUT_FLOOD;
 -}
 -
 -static void
 -xlate_output_action__(struct action_xlate_ctx *ctx,
 -                      uint16_t port, uint16_t max_len)
 -{
 -    uint16_t odp_port;
 -    uint16_t prev_nf_output_iface = ctx->nf_output_iface;
 -
 -    ctx->nf_output_iface = NF_OUT_DROP;
 -
 -    switch (port) {
 -    case OFPP_IN_PORT:
 -        add_output_action(ctx, ctx->flow.in_port);
 -        break;
 -    case OFPP_TABLE:
 -        xlate_table_action(ctx, ctx->flow.in_port);
 -        break;
 -    case OFPP_NORMAL:
 -        if (!ctx->ofproto->ofhooks->normal_cb(&ctx->flow, ctx->packet,
 -                                              ctx->odp_actions, &ctx->tags,
 -                                              &ctx->nf_output_iface,
 -                                              ctx->ofproto->aux)) {
 -            COVERAGE_INC(ofproto_uninstallable);
 -            ctx->may_set_up_flow = false;
 -        }
 -        break;
 -    case OFPP_FLOOD:
 -        flood_packets(ctx->ofproto, ctx->flow.in_port, OFPPC_NO_FLOOD,
 -                      &ctx->nf_output_iface, ctx->odp_actions);
 -        break;
 -    case OFPP_ALL:
 -        flood_packets(ctx->ofproto, ctx->flow.in_port, 0,
 -                      &ctx->nf_output_iface, ctx->odp_actions);
 -        break;
 -    case OFPP_CONTROLLER:
 -        nl_msg_put_u64(ctx->odp_actions, ODP_ACTION_ATTR_CONTROLLER, max_len);
 -        break;
 -    case OFPP_LOCAL:
 -        add_output_action(ctx, ODPP_LOCAL);
 -        break;
 -    default:
 -        odp_port = ofp_port_to_odp_port(port);
 -        if (odp_port != ctx->flow.in_port) {
 -            add_output_action(ctx, odp_port);
 -        }
 -        break;
 -    }
 -
 -    if (prev_nf_output_iface == NF_OUT_FLOOD) {
 -        ctx->nf_output_iface = NF_OUT_FLOOD;
 -    } else if (ctx->nf_output_iface == NF_OUT_DROP) {
 -        ctx->nf_output_iface = prev_nf_output_iface;
 -    } else if (prev_nf_output_iface != NF_OUT_DROP &&
 -               ctx->nf_output_iface != NF_OUT_FLOOD) {
 -        ctx->nf_output_iface = NF_OUT_MULTI;
 -    }
 -}
 -
 -static void
 -xlate_output_action(struct action_xlate_ctx *ctx,
 -                    const struct ofp_action_output *oao)
 -{
 -    xlate_output_action__(ctx, ntohs(oao->port), ntohs(oao->max_len));
 -}
 -
 -/* If the final ODP action in 'ctx' is "pop priority", drop it, as an
 - * optimization, because we're going to add another action that sets the
 - * priority immediately after, or because there are no actions following the
 - * pop.  */
 -static void
 -remove_pop_action(struct action_xlate_ctx *ctx)
 -{
 -    if (ctx->odp_actions->size == ctx->last_pop_priority) {
 -        ctx->odp_actions->size -= NLA_ALIGN(NLA_HDRLEN);
 -        ctx->last_pop_priority = -1;
 -    }
 -}
 -
 -static void
 -add_pop_action(struct action_xlate_ctx *ctx)
 -{
 -    if (ctx->odp_actions->size != ctx->last_pop_priority) {
 -        nl_msg_put_flag(ctx->odp_actions, ODP_ACTION_ATTR_POP_PRIORITY);
 -        ctx->last_pop_priority = ctx->odp_actions->size;
 -    }
 -}
 -
 -static void
 -xlate_enqueue_action(struct action_xlate_ctx *ctx,
 -                     const struct ofp_action_enqueue *oae)
 -{
 -    uint16_t ofp_port, odp_port;
 -    uint32_t priority;
 -    int error;
 -
 -    error = dpif_queue_to_priority(ctx->ofproto->dpif, ntohl(oae->queue_id),
 -                                   &priority);
 -    if (error) {
 -        /* Fall back to ordinary output action. */
 -        xlate_output_action__(ctx, ntohs(oae->port), 0);
 -        return;
 -    }
 -
 -    /* Figure out ODP output port. */
 -    ofp_port = ntohs(oae->port);
 -    if (ofp_port != OFPP_IN_PORT) {
 -        odp_port = ofp_port_to_odp_port(ofp_port);
 -    } else {
 -        odp_port = ctx->flow.in_port;
 -    }
 -
 -    /* Add ODP actions. */
 -    remove_pop_action(ctx);
 -    nl_msg_put_u32(ctx->odp_actions, ODP_ACTION_ATTR_SET_PRIORITY, priority);
 -    add_output_action(ctx, odp_port);
 -    add_pop_action(ctx);
 -
 -    /* Update NetFlow output port. */
 -    if (ctx->nf_output_iface == NF_OUT_DROP) {
 -        ctx->nf_output_iface = odp_port;
 -    } else if (ctx->nf_output_iface != NF_OUT_FLOOD) {
 -        ctx->nf_output_iface = NF_OUT_MULTI;
 -    }
 -}
 -
 -static void
 -xlate_set_queue_action(struct action_xlate_ctx *ctx,
 -                       const struct nx_action_set_queue *nasq)
 + * Takes ownership of 'packet'. */
 +static int
 +rule_execute(struct rule *rule, uint16_t in_port, struct ofpbuf *packet)
  {
 -    uint32_t priority;
 -    int error;
 -
 -    error = dpif_queue_to_priority(ctx->ofproto->dpif, ntohl(nasq->queue_id),
 -                                   &priority);
 -    if (error) {
 -        /* Couldn't translate queue to a priority, so ignore.  A warning
 -         * has already been logged. */
 -        return;
 -    }
 +    struct flow flow;
  
 -    remove_pop_action(ctx);
 -    nl_msg_put_u32(ctx->odp_actions, ODP_ACTION_ATTR_SET_PRIORITY, priority);
 -}
 +    assert(ofpbuf_headroom(packet) >= sizeof(struct ofp_packet_in));
  
 -static void
 -xlate_set_dl_tci(struct action_xlate_ctx *ctx)
 -{
 -    ovs_be16 tci = ctx->flow.vlan_tci;
 -    if (!(tci & htons(VLAN_CFI))) {
 -        nl_msg_put_flag(ctx->odp_actions, ODP_ACTION_ATTR_STRIP_VLAN);
 -    } else {
 -        nl_msg_put_be16(ctx->odp_actions, ODP_ACTION_ATTR_SET_DL_TCI,
 -                        tci & ~htons(VLAN_CFI));
 -    }
 +    flow_extract(packet, 0, in_port, &flow);
 +    return rule->ofproto->ofproto_class->rule_execute(rule, &flow, packet);
  }
  
 -struct xlate_reg_state {
 -    ovs_be16 vlan_tci;
 -    ovs_be64 tun_id;
 -};
 -
 -static void
 -save_reg_state(const struct action_xlate_ctx *ctx,
 -               struct xlate_reg_state *state)
 +/* Returns true if 'rule' should be hidden from the controller.
 + *
 + * Rules with priority higher than UINT16_MAX are set up by ofproto itself
 + * (e.g. by in-band control) and are intentionally hidden from the
 + * controller. */
 +static bool
 +rule_is_hidden(const struct rule *rule)
  {
 -    state->vlan_tci = ctx->flow.vlan_tci;
 -    state->tun_id = ctx->flow.tun_id;
 +    return rule->cr.priority > UINT16_MAX;
  }
 -
 +\f
  static void
 -update_reg_state(struct action_xlate_ctx *ctx,
 -                 const struct xlate_reg_state *state)
 +send_error_oh(const struct ofconn *ofconn, const struct ofp_header *oh,
 +              int error)
  {
 -    if (ctx->flow.vlan_tci != state->vlan_tci) {
 -        xlate_set_dl_tci(ctx);
 -    }
 -    if (ctx->flow.tun_id != state->tun_id) {
 -        nl_msg_put_be64(ctx->odp_actions,
 -                        ODP_ACTION_ATTR_SET_TUNNEL, ctx->flow.tun_id);
 +    struct ofpbuf *buf = ofputil_encode_error_msg(error, oh);
 +    if (buf) {
 +        COVERAGE_INC(ofproto_error);
 +        ofconn_send_reply(ofconn, buf);
      }
  }
  
 -static void
 -xlate_nicira_action(struct action_xlate_ctx *ctx,
 -                    const struct nx_action_header *nah)
 -{
 -    const struct nx_action_resubmit *nar;
 -    const struct nx_action_set_tunnel *nast;
 -    const struct nx_action_set_queue *nasq;
 -    const struct nx_action_multipath *nam;
 -    const struct nx_action_autopath *naa;
 -    enum nx_action_subtype subtype = ntohs(nah->subtype);
 -    const struct ofhooks *ofhooks = ctx->ofproto->ofhooks;
 -    struct xlate_reg_state state;
 -    uint16_t autopath_port;
 -    ovs_be64 tun_id;
 -
 -    assert(nah->vendor == htonl(NX_VENDOR_ID));
 -    switch (subtype) {
 -    case NXAST_RESUBMIT:
 -        nar = (const struct nx_action_resubmit *) nah;
 -        xlate_table_action(ctx, ofp_port_to_odp_port(ntohs(nar->in_port)));
 -        break;
 -
 -    case NXAST_SET_TUNNEL:
 -        nast = (const struct nx_action_set_tunnel *) nah;
 -        tun_id = htonll(ntohl(nast->tun_id));
 -        nl_msg_put_be64(ctx->odp_actions, ODP_ACTION_ATTR_SET_TUNNEL, tun_id);
 -        ctx->flow.tun_id = tun_id;
 -        break;
 -
 -    case NXAST_DROP_SPOOFED_ARP:
 -        if (ctx->flow.dl_type == htons(ETH_TYPE_ARP)) {
 -            nl_msg_put_flag(ctx->odp_actions,
 -                            ODP_ACTION_ATTR_DROP_SPOOFED_ARP);
 -        }
 -        break;
 -
 -    case NXAST_SET_QUEUE:
 -        nasq = (const struct nx_action_set_queue *) nah;
 -        xlate_set_queue_action(ctx, nasq);
 -        break;
 -
 -    case NXAST_POP_QUEUE:
 -        add_pop_action(ctx);
 -        break;
 -
 -    case NXAST_REG_MOVE:
 -        save_reg_state(ctx, &state);
 -        nxm_execute_reg_move((const struct nx_action_reg_move *) nah,
 -                             &ctx->flow);
 -        update_reg_state(ctx, &state);
 -        break;
 -
 -    case NXAST_REG_LOAD:
 -        save_reg_state(ctx, &state);
 -        nxm_execute_reg_load((const struct nx_action_reg_load *) nah,
 -                             &ctx->flow);
 -        update_reg_state(ctx, &state);
 -        break;
 -
 -    case NXAST_NOTE:
 -        /* Nothing to do. */
 -        break;
 -
 -    case NXAST_SET_TUNNEL64:
 -        tun_id = ((const struct nx_action_set_tunnel64 *) nah)->tun_id;
 -        nl_msg_put_be64(ctx->odp_actions, ODP_ACTION_ATTR_SET_TUNNEL, tun_id);
 -        ctx->flow.tun_id = tun_id;
 -        break;
 -
 -    case NXAST_MULTIPATH:
 -        nam = (const struct nx_action_multipath *) nah;
 -        multipath_execute(nam, &ctx->flow);
 -        break;
 -
 -    case NXAST_AUTOPATH:
 -        naa = (const struct nx_action_autopath *) nah;
 -        autopath_port = (ofhooks->autopath_cb
 -                         ? ofhooks->autopath_cb(&ctx->flow, ntohl(naa->id),
 -                                                &ctx->tags, ctx->ofproto->aux)
 -                         : OFPP_NONE);
 -        autopath_execute(naa, &ctx->flow, autopath_port);
 -        break;
 -
 -    /* If you add a new action here that modifies flow data, don't forget to
 -     * update the flow key in ctx->flow at the same time. */
 -
 -    case NXAST_SNAT__OBSOLETE:
 -    default:
 -        VLOG_DBG_RL(&rl, "unknown Nicira action type %d", (int) subtype);
 -        break;
 -    }
 +static int
 +handle_echo_request(struct ofconn *ofconn, const struct ofp_header *oh)
 +{
 +    ofconn_send_reply(ofconn, make_echo_reply(oh));
 +    return 0;
  }
  
 -static void
 -do_xlate_actions(const union ofp_action *in, size_t n_in,
 -                 struct action_xlate_ctx *ctx)
 +static int
 +handle_features_request(struct ofconn *ofconn, const struct ofp_header *oh)
  {
 -    struct actions_iterator iter;
 -    const union ofp_action *ia;
 -    const struct ofport *port;
 -
 -    port = get_port(ctx->ofproto, ctx->flow.in_port);
 -    if (port && port->opp.config & (OFPPC_NO_RECV | OFPPC_NO_RECV_STP) &&
 -        port->opp.config & (eth_addr_equals(ctx->flow.dl_dst, eth_addr_stp)
 -                            ? OFPPC_NO_RECV_STP : OFPPC_NO_RECV)) {
 -        /* Drop this flow. */
 -        return;
 -    }
 -
 -    for (ia = actions_first(&iter, in, n_in); ia; ia = actions_next(&iter)) {
 -        enum ofp_action_type type = ntohs(ia->type);
 -        const struct ofp_action_dl_addr *oada;
 -
 -        switch (type) {
 -        case OFPAT_OUTPUT:
 -            xlate_output_action(ctx, &ia->output);
 -            break;
 -
 -        case OFPAT_SET_VLAN_VID:
 -            ctx->flow.vlan_tci &= ~htons(VLAN_VID_MASK);
 -            ctx->flow.vlan_tci |= ia->vlan_vid.vlan_vid | htons(VLAN_CFI);
 -            xlate_set_dl_tci(ctx);
 -            break;
 +    struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
 +    struct ofp_switch_features *osf;
 +    struct ofpbuf *buf;
 +    struct ofport *port;
 +    bool arp_match_ip;
 +    uint32_t actions;
  
 -        case OFPAT_SET_VLAN_PCP:
 -            ctx->flow.vlan_tci &= ~htons(VLAN_PCP_MASK);
 -            ctx->flow.vlan_tci |= htons(
 -                (ia->vlan_pcp.vlan_pcp << VLAN_PCP_SHIFT) | VLAN_CFI);
 -            xlate_set_dl_tci(ctx);
 -            break;
 +    ofproto->ofproto_class->get_features(ofproto, &arp_match_ip, &actions);
 +    assert(actions & (1 << OFPAT_OUTPUT)); /* sanity check */
  
 -        case OFPAT_STRIP_VLAN:
 -            ctx->flow.vlan_tci = htons(0);
 -            xlate_set_dl_tci(ctx);
 -            break;
 +    osf = make_openflow_xid(sizeof *osf, OFPT_FEATURES_REPLY, oh->xid, &buf);
 +    osf->datapath_id = htonll(ofproto->datapath_id);
 +    osf->n_buffers = htonl(pktbuf_capacity());
 +    osf->n_tables = ofproto->n_tables;
 +    osf->capabilities = htonl(OFPC_FLOW_STATS | OFPC_TABLE_STATS |
 +                              OFPC_PORT_STATS);
 +    if (arp_match_ip) {
 +        osf->capabilities |= htonl(OFPC_ARP_MATCH_IP);
 +    }
 +    osf->actions = htonl(actions);
  
 -        case OFPAT_SET_DL_SRC:
 -            oada = ((struct ofp_action_dl_addr *) ia);
 -            nl_msg_put_unspec(ctx->odp_actions, ODP_ACTION_ATTR_SET_DL_SRC,
 -                              oada->dl_addr, ETH_ADDR_LEN);
 -            memcpy(ctx->flow.dl_src, oada->dl_addr, ETH_ADDR_LEN);
 -            break;
 +    HMAP_FOR_EACH (port, hmap_node, &ofproto->ports) {
 +        ofpbuf_put(buf, &port->opp, sizeof port->opp);
 +    }
  
 -        case OFPAT_SET_DL_DST:
 -            oada = ((struct ofp_action_dl_addr *) ia);
 -            nl_msg_put_unspec(ctx->odp_actions, ODP_ACTION_ATTR_SET_DL_DST,
 -                              oada->dl_addr, ETH_ADDR_LEN);
 -            memcpy(ctx->flow.dl_dst, oada->dl_addr, ETH_ADDR_LEN);
 -            break;
 +    ofconn_send_reply(ofconn, buf);
 +    return 0;
 +}
  
 -        case OFPAT_SET_NW_SRC:
 -            nl_msg_put_be32(ctx->odp_actions, ODP_ACTION_ATTR_SET_NW_SRC,
 -                            ia->nw_addr.nw_addr);
 -            ctx->flow.nw_src = ia->nw_addr.nw_addr;
 -            break;
 +static int
 +handle_get_config_request(struct ofconn *ofconn, const struct ofp_header *oh)
 +{
 +    struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
 +    struct ofpbuf *buf;
 +    struct ofp_switch_config *osc;
 +    uint16_t flags;
 +    bool drop_frags;
  
 -        case OFPAT_SET_NW_DST:
 -            nl_msg_put_be32(ctx->odp_actions, ODP_ACTION_ATTR_SET_NW_DST,
 -                            ia->nw_addr.nw_addr);
 -            ctx->flow.nw_dst = ia->nw_addr.nw_addr;
 -            break;
 +    /* Figure out flags. */
 +    drop_frags = ofproto->ofproto_class->get_drop_frags(ofproto);
 +    flags = drop_frags ? OFPC_FRAG_DROP : OFPC_FRAG_NORMAL;
  
 -        case OFPAT_SET_NW_TOS:
 -            nl_msg_put_u8(ctx->odp_actions, ODP_ACTION_ATTR_SET_NW_TOS,
 -                          ia->nw_tos.nw_tos);
 -            ctx->flow.nw_tos = ia->nw_tos.nw_tos;
 -            break;
 +    /* Send reply. */
 +    osc = make_openflow_xid(sizeof *osc, OFPT_GET_CONFIG_REPLY, oh->xid, &buf);
 +    osc->flags = htons(flags);
 +    osc->miss_send_len = htons(ofconn_get_miss_send_len(ofconn));
 +    ofconn_send_reply(ofconn, buf);
  
 -        case OFPAT_SET_TP_SRC:
 -            nl_msg_put_be16(ctx->odp_actions, ODP_ACTION_ATTR_SET_TP_SRC,
 -                            ia->tp_port.tp_port);
 -            ctx->flow.tp_src = ia->tp_port.tp_port;
 -            break;
 +    return 0;
 +}
  
 -        case OFPAT_SET_TP_DST:
 -            nl_msg_put_be16(ctx->odp_actions, ODP_ACTION_ATTR_SET_TP_DST,
 -                            ia->tp_port.tp_port);
 -            ctx->flow.tp_dst = ia->tp_port.tp_port;
 -            break;
 +static int
 +handle_set_config(struct ofconn *ofconn, const struct ofp_switch_config *osc)
 +{
 +    struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
 +    uint16_t flags = ntohs(osc->flags);
  
 -        case OFPAT_VENDOR:
 -            xlate_nicira_action(ctx, (const struct nx_action_header *) ia);
 +    if (ofconn_get_type(ofconn) == OFCONN_PRIMARY
 +        && ofconn_get_role(ofconn) != NX_ROLE_SLAVE) {
 +        switch (flags & OFPC_FRAG_MASK) {
 +        case OFPC_FRAG_NORMAL:
 +            ofproto->ofproto_class->set_drop_frags(ofproto, false);
              break;
 -
 -        case OFPAT_ENQUEUE:
 -            xlate_enqueue_action(ctx, (const struct ofp_action_enqueue *) ia);
 +        case OFPC_FRAG_DROP:
 +            ofproto->ofproto_class->set_drop_frags(ofproto, true);
              break;
 -
          default:
 -            VLOG_DBG_RL(&rl, "unknown action type %d", (int) type);
 +            VLOG_WARN_RL(&rl, "requested bad fragment mode (flags=%"PRIx16")",
 +                         osc->flags);
              break;
          }
      }
 -}
 -
 -static void
 -action_xlate_ctx_init(struct action_xlate_ctx *ctx,
 -                      struct ofproto *ofproto, const struct flow *flow,
 -                      const struct ofpbuf *packet)
 -{
 -    ctx->ofproto = ofproto;
 -    ctx->flow = *flow;
 -    ctx->packet = packet;
 -    ctx->resubmit_hook = NULL;
 -    ctx->check_special = true;
 -}
 -
 -static void
 -ofproto_process_cfm(struct ofproto *ofproto, const struct flow *flow,
 -                    const struct ofpbuf *packet)
 -{
 -    struct ofport *ofport;
 -
 -    ofport = get_port(ofproto, flow->in_port);
 -    if (ofport && ofport->cfm) {
 -        cfm_process_heartbeat(ofport->cfm, packet);
 -    }
 -}
 -
 -static struct ofpbuf *
 -xlate_actions(struct action_xlate_ctx *ctx,
 -              const union ofp_action *in, size_t n_in)
 -{
 -    COVERAGE_INC(ofproto_ofp2odp);
 -
 -    ctx->odp_actions = ofpbuf_new(512);
 -    ctx->tags = 0;
 -    ctx->may_set_up_flow = true;
 -    ctx->nf_output_iface = NF_OUT_DROP;
 -    ctx->recurse = 0;
 -    ctx->last_pop_priority = -1;
 -
 -    if (ctx->check_special && cfm_should_process_flow(&ctx->flow)) {
 -        if (ctx->packet) {
 -            ofproto_process_cfm(ctx->ofproto, &ctx->flow, ctx->packet);
 -        }
 -        ctx->may_set_up_flow = false;
 -    } else if (ctx->check_special
 -               && ctx->ofproto->ofhooks->special_cb
 -               && !ctx->ofproto->ofhooks->special_cb(&ctx->flow, ctx->packet,
 -                                                     ctx->ofproto->aux)) {
 -        ctx->may_set_up_flow = false;
 -    } else {
 -        do_xlate_actions(in, n_in, ctx);
 -    }
  
 -    remove_pop_action(ctx);
 -
 -    /* Check with in-band control to see if we're allowed to set up this
 -     * flow. */
 -    if (!connmgr_may_set_up_flow(ctx->ofproto->connmgr, &ctx->flow,
 -                                 ctx->odp_actions->data,
 -                                 ctx->odp_actions->size)) {
 -        ctx->may_set_up_flow = false;
 -    }
 +    ofconn_set_miss_send_len(ofconn, ntohs(osc->miss_send_len));
  
 -    return ctx->odp_actions;
 +    return 0;
  }
  
  /* Checks whether 'ofconn' is a slave controller.  If so, returns an OpenFlow
@@@ -1473,6 -2561,8 +1473,6 @@@ handle_packet_out(struct ofconn *ofconn
      struct ofp_packet_out *opo;
      struct ofpbuf payload, *buffer;
      union ofp_action *ofp_actions;
 -    struct action_xlate_ctx ctx;
 -    struct ofpbuf *odp_actions;
      struct ofpbuf request;
      struct flow flow;
      size_t n_ofp_actions;
          buffer = NULL;
      }
  
 -    /* Extract flow, check actions. */
 -    flow_extract(&payload, 0, ofp_port_to_odp_port(ntohs(opo->in_port)),
 -                 &flow);
 -    error = validate_actions(ofp_actions, n_ofp_actions, &flow, p->max_ports);
 -    if (error) {
 -        goto exit;
 -    }
 -
 -    /* Send. */
 -    action_xlate_ctx_init(&ctx, p, &flow, &payload);
 -    odp_actions = xlate_actions(&ctx, ofp_actions, n_ofp_actions);
 -    dpif_execute(p->dpif, odp_actions->data, odp_actions->size, &payload);
 -    ofpbuf_delete(odp_actions);
 -
 -exit:
 +    /* Send out packet. */
 +    flow_extract(&payload, 0, ntohs(opo->in_port), &flow);
 +    error = p->ofproto_class->packet_out(p, &payload, &flow,
 +                                         ofp_actions, n_ofp_actions);
      ofpbuf_delete(buffer);
 -    return 0;
 +
 +    return error;
  }
  
  static void
 -update_port_config(struct ofproto *p, struct ofport *port,
 -                   uint32_t config, uint32_t mask)
 +update_port_config(struct ofport *port, ovs_be32 config, ovs_be32 mask)
  {
 +    ovs_be32 old_config = port->opp.config;
 +
      mask &= config ^ port->opp.config;
 -    if (mask & OFPPC_PORT_DOWN) {
 -        if (config & OFPPC_PORT_DOWN) {
 +    if (mask & htonl(OFPPC_PORT_DOWN)) {
 +        if (config & htonl(OFPPC_PORT_DOWN)) {
              netdev_turn_flags_off(port->netdev, NETDEV_UP, true);
          } else {
              netdev_turn_flags_on(port->netdev, NETDEV_UP, true);
          }
      }
 -#define REVALIDATE_BITS (OFPPC_NO_RECV | OFPPC_NO_RECV_STP |    \
 -                         OFPPC_NO_FWD | OFPPC_NO_FLOOD)
 -    if (mask & REVALIDATE_BITS) {
 -        COVERAGE_INC(ofproto_costly_flags);
 -        port->opp.config ^= mask & REVALIDATE_BITS;
 -        p->need_revalidate = true;
 -    }
 -#undef REVALIDATE_BITS
 -    if (mask & OFPPC_NO_PACKET_IN) {
 -        port->opp.config ^= OFPPC_NO_PACKET_IN;
 +
 +    port->opp.config ^= mask & (htonl(OFPPC_NO_RECV | OFPPC_NO_RECV_STP |
 +                                      OFPPC_NO_FLOOD | OFPPC_NO_FWD |
 +                                      OFPPC_NO_PACKET_IN));
 +    if (port->opp.config != old_config) {
 +        port->ofproto->ofproto_class->port_reconfigured(port, old_config);
      }
  }
  
@@@ -1554,13 -2657,13 +1554,13 @@@ handle_port_mod(struct ofconn *ofconn, 
          return error;
      }
  
 -    port = get_port(p, ofp_port_to_odp_port(ntohs(opm->port_no)));
 +    port = ofproto_get_port(p, ntohs(opm->port_no));
      if (!port) {
          return ofp_mkerr(OFPET_PORT_MOD_FAILED, OFPPMFC_BAD_PORT);
      } else if (memcmp(port->opp.hw_addr, opm->hw_addr, OFP_ETH_ALEN)) {
          return ofp_mkerr(OFPET_PORT_MOD_FAILED, OFPPMFC_BAD_HW_ADDR);
      } else {
 -        update_port_config(p, port, ntohl(opm->config), ntohl(opm->mask));
 +        update_port_config(port, opm->config, opm->mask);
          if (opm->advertise) {
              netdev_set_advertisements(port->netdev, ntohl(opm->advertise));
          }
@@@ -1668,28 -2771,19 +1668,21 @@@ handle_table_stats_request(struct ofcon
      struct ofproto *p = ofconn_get_ofproto(ofconn);
      struct ofp_table_stats *ots;
      struct ofpbuf *msg;
 +    size_t i;
  
 -    msg = start_ofp_stats_reply(request, sizeof *ots * 2);
 +    msg = start_ofp_stats_reply(request, sizeof *ots * p->n_tables);
  
 -    /* Classifier table. */
 -    ots = append_ofp_stats_reply(sizeof *ots, ofconn, &msg);
 -    memset(ots, 0, sizeof *ots);
 -    strcpy(ots->name, "classifier");
 -    ots->wildcards = htonl(OFPFW_ALL);
 -    ots->max_entries = htonl(1024 * 1024); /* An arbitrary big number. */
 -    ots->active_count = htonl(classifier_count(&p->cls));
 -    put_32aligned_be64(&ots->lookup_count, htonll(0));  /* XXX */
 -    put_32aligned_be64(&ots->matched_count, htonll(0)); /* XXX */
 +    ots = ofpbuf_put_zeros(msg, sizeof *ots * p->n_tables);
 +    for (i = 0; i < p->n_tables; i++) {
 +        ots[i].table_id = i;
 +        sprintf(ots[i].name, "table%d", i);
-         ots[i].wildcards = htonl(OVSFW_ALL);
++        ots[i].wildcards = htonl(OFPFW_ALL);
 +        ots[i].max_entries = htonl(1000000); /* An arbitrary big number. */
 +        ots[i].active_count = htonl(classifier_count(&p->tables[i]));
 +    }
 +
 +    p->ofproto_class->get_tables(p, ots);
  
-     if (ofconn_get_flow_format(ofconn) == NXFF_OPENFLOW10) {
-         /* OpenFlow 1.0 only supports the OFPFW_* bits. */
-         for (i = 0; i < p->n_tables; i++) {
-             ots[i].wildcards &= htonl(OFPFW_ALL);
-         }
-     }
      ofconn_send_reply(ofconn, msg);
      return 0;
  }
@@@ -1707,7 -2801,7 +1700,7 @@@ append_port_stat(struct ofport *port, s
      netdev_get_stats(port->netdev, &stats);
  
      ops = append_ofp_stats_reply(sizeof *ops, ofconn, msgp);
 -    ops->port_no = htons(port->opp.port_no);
 +    ops->port_no = port->opp.port_no;
      memset(ops->pad, 0, sizeof ops->pad);
      put_32aligned_be64(&ops->rx_packets, htonll(stats.rx_packets));
      put_32aligned_be64(&ops->tx_packets, htonll(stats.tx_packets));
@@@ -1734,7 -2828,7 +1727,7 @@@ handle_port_stats_request(struct ofcon
  
      msg = start_ofp_stats_reply(oh, sizeof *ops * 16);
      if (psr->port_no != htons(OFPP_NONE)) {
 -        port = get_port(p, ofp_port_to_odp_port(ntohs(psr->port_no)));
 +        port = ofproto_get_port(p, ntohs(psr->port_no));
          if (port) {
              append_port_stat(port, ofconn, &msg);
          }
@@@ -1770,10 -2864,8 +1763,9 @@@ static voi
  put_ofp_flow_stats(struct ofconn *ofconn, struct rule *rule,
                     ovs_be16 out_port, struct ofpbuf **replyp)
  {
 +    struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
      struct ofp_flow_stats *ofs;
      uint64_t packet_count, byte_count;
-     ovs_be64 cookie;
      size_t act_len, len;
  
      if (rule_is_hidden(rule) || !rule_has_out_port(rule, out_port)) {
      act_len = sizeof *rule->actions * rule->n_actions;
      len = offsetof(struct ofp_flow_stats, actions) + act_len;
  
 -    rule_get_stats(rule, &packet_count, &byte_count);
 +    ofproto->ofproto_class->rule_get_stats(rule, &packet_count, &byte_count);
  
      ofs = append_ofp_stats_reply(len, ofconn, replyp);
      ofs->length = htons(len);
 -    ofs->table_id = 0;
 +    ofs->table_id = rule->table_id;
      ofs->pad = 0;
-     ofputil_cls_rule_to_match(&rule->cr, ofconn_get_flow_format(ofconn),
-                               &ofs->match, rule->flow_cookie, &cookie);
-     put_32aligned_be64(&ofs->cookie, cookie);
+     ofputil_cls_rule_to_match(&rule->cr, &ofs->match);
+     put_32aligned_be64(&ofs->cookie, rule->flow_cookie);
      calc_flow_duration(rule->created, &ofs->duration_sec, &ofs->duration_nsec);
      ofs->priority = htons(rule->cr.priority);
      ofs->idle_timeout = htons(rule->idle_timeout);
      }
  }
  
 -static bool
 -is_valid_table(uint8_t table_id)
 +static struct classifier *
 +first_matching_table(struct ofproto *ofproto, uint8_t table_id)
  {
 -    if (table_id == 0 || table_id == 0xff) {
 -        return true;
 +    if (table_id == 0xff) {
 +        return &ofproto->tables[0];
 +    } else if (table_id < ofproto->n_tables) {
 +        return &ofproto->tables[table_id];
      } else {
          /* It would probably be better to reply with an error but there doesn't
           * seem to be any appropriate value, so that might just be
           * confusing. */
          VLOG_WARN_RL(&rl, "controller asked for invalid table %"PRIu8,
                       table_id);
 -        return false;
 +        return NULL;
      }
  }
  
 +static struct classifier *
 +next_matching_table(struct ofproto *ofproto,
 +                    struct classifier *cls, uint8_t table_id)
 +{
 +    return (table_id == 0xff && cls != &ofproto->tables[ofproto->n_tables - 1]
 +            ? cls + 1
 +            : NULL);
 +}
 +
 +/* Assigns CLS to each classifier table, in turn, that matches TABLE_ID in
 + * OFPROTO:
 + *
 + *   - If TABLE_ID is 0xff, this iterates over every classifier table in
 + *     OFPROTO.
 + *
 + *   - If TABLE_ID is the number of a table in OFPROTO, then the loop iterates
 + *     only once, for that table.
 + *
 + *   - Otherwise, TABLE_ID isn't valid for OFPROTO, so ofproto logs a warning
 + *     and does not enter the loop at all.
 + *
 + * All parameters are evaluated multiple times.
 + */
 +#define FOR_EACH_MATCHING_TABLE(CLS, TABLE_ID, OFPROTO)         \
 +    for ((CLS) = first_matching_table(OFPROTO, TABLE_ID);       \
 +         (CLS) != NULL;                                         \
 +         (CLS) = next_matching_table(OFPROTO, CLS, TABLE_ID))
 +
  static int
  handle_flow_stats_request(struct ofconn *ofconn, const struct ofp_header *oh)
  {
      const struct ofp_flow_stats_request *fsr = ofputil_stats_body(oh);
      struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
 +    struct classifier *cls;
 +    struct cls_rule target;
      struct ofpbuf *reply;
  
      COVERAGE_INC(ofproto_flows_req);
      reply = start_ofp_stats_reply(oh, 1024);
-     ofputil_cls_rule_from_match(&fsr->match, 0, NXFF_OPENFLOW10, 0, &target);
 -    if (is_valid_table(fsr->table_id)) {
++    ofputil_cls_rule_from_match(&fsr->match, 0, &target);
 +    FOR_EACH_MATCHING_TABLE (cls, fsr->table_id, ofproto) {
          struct cls_cursor cursor;
 -        struct cls_rule target;
          struct rule *rule;
  
 -        ofputil_cls_rule_from_match(&fsr->match, 0, &target);
 -        cls_cursor_init(&cursor, &ofproto->cls, &target);
 +        cls_cursor_init(&cursor, cls, &target);
          CLS_CURSOR_FOR_EACH (rule, cr, &cursor) {
              put_ofp_flow_stats(ofconn, rule, fsr->out_port, &reply);
          }
@@@ -1888,8 -2948,7 +1879,8 @@@ put_nx_flow_stats(struct ofconn *ofconn
          return;
      }
  
 -    rule_get_stats(rule, &packet_count, &byte_count);
 +    rule->ofproto->ofproto_class->rule_get_stats(rule,
 +                                                 &packet_count, &byte_count);
  
      act_len = sizeof *rule->actions * rule->n_actions;
  
@@@ -1920,7 -2979,6 +1911,7 @@@ handle_nxst_flow(struct ofconn *ofconn
  {
      struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
      struct nx_flow_stats_request *nfsr;
 +    struct classifier *cls;
      struct cls_rule target;
      struct ofpbuf *reply;
      struct ofpbuf b;
  
      COVERAGE_INC(ofproto_flows_req);
      reply = start_nxstats_reply(&nfsr->nsm, 1024);
 -    if (is_valid_table(nfsr->table_id)) {
 +    FOR_EACH_MATCHING_TABLE (cls, nfsr->table_id, ofproto) {
          struct cls_cursor cursor;
          struct rule *rule;
  
 -        cls_cursor_init(&cursor, &ofproto->cls, &target);
 +        cls_cursor_init(&cursor, cls, &target);
          CLS_CURSOR_FOR_EACH (rule, cr, &cursor) {
              put_nx_flow_stats(ofconn, rule, nfsr->out_port, &reply);
          }
@@@ -1960,14 -3018,11 +1951,14 @@@ flow_stats_ds(struct rule *rule, struc
      uint64_t packet_count, byte_count;
      size_t act_len = sizeof *rule->actions * rule->n_actions;
  
 -    rule_get_stats(rule, &packet_count, &byte_count);
 +    rule->ofproto->ofproto_class->rule_get_stats(rule,
 +                                                 &packet_count, &byte_count);
  
 +    if (rule->table_id != 0) {
 +        ds_put_format(results, "table_id=%"PRIu8", ", rule->table_id);
 +    }
      ds_put_format(results, "duration=%llds, ",
                    (time_msec() - rule->created) / 1000);
 -    ds_put_format(results, "idle=%.3fs, ", (time_msec() - rule->used) / 1000.0);
      ds_put_format(results, "priority=%u, ", rule->cr.priority);
      ds_put_format(results, "n_packets=%"PRIu64", ", packet_count);
      ds_put_format(results, "n_bytes=%"PRIu64", ", byte_count);
  void
  ofproto_get_all_flows(struct ofproto *p, struct ds *results)
  {
 -    struct cls_cursor cursor;
 -    struct rule *rule;
 +    struct classifier *cls;
 +
 +    for (cls = &p->tables[0]; cls < &p->tables[p->n_tables]; cls++) {
 +        struct cls_cursor cursor;
 +        struct rule *rule;
  
 -    cls_cursor_init(&cursor, &p->cls, NULL);
 -    CLS_CURSOR_FOR_EACH (rule, cr, &cursor) {
 -        flow_stats_ds(rule, results);
 +        cls_cursor_init(&cursor, cls, NULL);
 +        CLS_CURSOR_FOR_EACH (rule, cr, &cursor) {
 +            flow_stats_ds(rule, results);
 +        }
      }
  }
  
 +/* Obtains the NetFlow engine type and engine ID for 'ofproto' into
 + * '*engine_type' and '*engine_id', respectively. */
 +void
 +ofproto_get_netflow_ids(const struct ofproto *ofproto,
 +                        uint8_t *engine_type, uint8_t *engine_id)
 +{
 +    ofproto->ofproto_class->get_netflow_ids(ofproto, engine_type, engine_id);
 +}
 +
  static void
  query_aggregate_stats(struct ofproto *ofproto, struct cls_rule *target,
                        ovs_be16 out_port, uint8_t table_id,
  {
      uint64_t total_packets = 0;
      uint64_t total_bytes = 0;
 +    struct classifier *cls;
      int n_flows = 0;
  
      COVERAGE_INC(ofproto_agg_request);
  
 -    if (is_valid_table(table_id)) {
 +    FOR_EACH_MATCHING_TABLE (cls, table_id, ofproto) {
          struct cls_cursor cursor;
          struct rule *rule;
  
 -        cls_cursor_init(&cursor, &ofproto->cls, target);
 +        cls_cursor_init(&cursor, cls, target);
          CLS_CURSOR_FOR_EACH (rule, cr, &cursor) {
              if (!rule_is_hidden(rule) && rule_has_out_port(rule, out_port)) {
                  uint64_t packet_count;
                  uint64_t byte_count;
  
 -                rule_get_stats(rule, &packet_count, &byte_count);
 +                ofproto->ofproto_class->rule_get_stats(rule, &packet_count,
 +                                                       &byte_count);
  
                  total_packets += packet_count;
                  total_bytes += byte_count;
@@@ -2056,8 -3096,7 +2047,7 @@@ handle_aggregate_stats_request(struct o
      struct cls_rule target;
      struct ofpbuf *msg;
  
-     ofputil_cls_rule_from_match(&request->match, 0, NXFF_OPENFLOW10, 0,
-                                 &target);
+     ofputil_cls_rule_from_match(&request->match, 0, &target);
  
      msg = start_ofp_stats_reply(oh, sizeof *reply);
      reply = append_ofp_stats_reply(sizeof *reply, ofconn, &msg);
@@@ -2114,7 -3153,7 +2104,7 @@@ put_queue_stats(struct queue_stats_cbda
      struct ofp_queue_stats *reply;
  
      reply = append_ofp_stats_reply(sizeof *reply, cbdata->ofconn, &cbdata->msg);
 -    reply->port_no = htons(cbdata->ofport->opp.port_no);
 +    reply->port_no = cbdata->ofport->opp.port_no;
      memset(reply->pad, 0, sizeof reply->pad);
      reply->queue_id = htonl(queue_id);
      put_32aligned_be64(&reply->tx_bytes, htonll(stats->tx_bytes));
@@@ -2175,8 -3214,8 +2165,8 @@@ handle_queue_stats_request(struct ofcon
          HMAP_FOR_EACH (port, hmap_node, &ofproto->ports) {
              handle_queue_stats_for_port(port, queue_id, &cbdata);
          }
 -    } else if (port_no < ofproto->max_ports) {
 -        port = get_port(ofproto, ofp_port_to_odp_port(port_no));
 +    } else if (port_no < OFPP_MAX) {
 +        port = ofproto_get_port(ofproto, port_no);
          if (port) {
              handle_queue_stats_for_port(port, queue_id, &cbdata);
          }
      return 0;
  }
  
 -/* Updates 'facet''s used time.  Caller is responsible for calling
 - * facet_push_stats() to update the flows which 'facet' resubmits into. */
 -static void
 -facet_update_time(struct ofproto *ofproto, struct facet *facet,
 -                  long long int used)
 -{
 -    if (used > facet->used) {
 -        facet->used = used;
 -        if (used > facet->rule->used) {
 -            facet->rule->used = used;
 -        }
 -        netflow_flow_update_time(ofproto->netflow, &facet->nf_flow, used);
 -    }
 -}
 -
 -/* Folds the statistics from 'stats' into the counters in 'facet'.
 - *
 - * Because of the meaning of a facet's counters, it only makes sense to do this
 - * if 'stats' are not tracked in the datapath, that is, if 'stats' represents a
 - * packet that was sent by hand or if it represents statistics that have been
 - * cleared out of the datapath. */
 -static void
 -facet_update_stats(struct ofproto *ofproto, struct facet *facet,
 -                   const struct dpif_flow_stats *stats)
 -{
 -    if (stats->n_packets || stats->used > facet->used) {
 -        facet_update_time(ofproto, facet, stats->used);
 -        facet->packet_count += stats->n_packets;
 -        facet->byte_count += stats->n_bytes;
 -        facet_push_stats(ofproto, facet);
 -        netflow_flow_update_flags(&facet->nf_flow, stats->tcp_flags);
 -    }
 -}
 -
 -static void
 -facet_push_stats(struct ofproto *ofproto, struct facet *facet)
 -{
 -    uint64_t rs_packets, rs_bytes;
 -
 -    assert(facet->packet_count >= facet->rs_packet_count);
 -    assert(facet->byte_count >= facet->rs_byte_count);
 -    assert(facet->used >= facet->rs_used);
 -
 -    rs_packets = facet->packet_count - facet->rs_packet_count;
 -    rs_bytes = facet->byte_count - facet->rs_byte_count;
 -
 -    if (rs_packets || rs_bytes || facet->used > facet->rs_used) {
 -        facet->rs_packet_count = facet->packet_count;
 -        facet->rs_byte_count = facet->byte_count;
 -        facet->rs_used = facet->used;
 -
 -        flow_push_stats(ofproto, facet->rule, &facet->flow,
 -                        rs_packets, rs_bytes, facet->used);
 -    }
 -}
 -
 -struct ofproto_push {
 -    struct action_xlate_ctx ctx;
 -    uint64_t packets;
 -    uint64_t bytes;
 -    long long int used;
 -};
 -
 -static void
 -push_resubmit(struct action_xlate_ctx *ctx, struct rule *rule)
 -{
 -    struct ofproto_push *push = CONTAINER_OF(ctx, struct ofproto_push, ctx);
 -
 -    if (rule) {
 -        rule->packet_count += push->packets;
 -        rule->byte_count += push->bytes;
 -        rule->used = MAX(push->used, rule->used);
 -    }
 -}
 -
 -/* Pushes flow statistics to the rules which 'flow' resubmits into given
 - * 'rule''s actions. */
 -static void
 -flow_push_stats(struct ofproto *ofproto, const struct rule *rule,
 -                struct flow *flow, uint64_t packets, uint64_t bytes,
 -                long long int used)
 -{
 -    struct ofproto_push push;
 -
 -    push.packets = packets;
 -    push.bytes = bytes;
 -    push.used = used;
 -
 -    action_xlate_ctx_init(&push.ctx, ofproto, flow, NULL);
 -    push.ctx.resubmit_hook = push_resubmit;
 -    ofpbuf_delete(xlate_actions(&push.ctx, rule->actions, rule->n_actions));
 -}
 -
  /* Implements OFPFC_ADD and the cases for OFPFC_MODIFY and OFPFC_MODIFY_STRICT
   * in which no matching flow already exists in the flow table.
   *
@@@ -2205,70 -3337,43 +2195,70 @@@ add_flow(struct ofconn *ofconn, struct 
      struct ofpbuf *packet;
      struct rule *rule;
      uint16_t in_port;
 +    int buf_err;
      int error;
  
 -    if (fm->flags & OFPFF_CHECK_OVERLAP
 -        && classifier_rule_overlaps(&p->cls, &fm->cr)) {
 -        return ofp_mkerr(OFPET_FLOW_MOD_FAILED, OFPFMFC_OVERLAP);
 +    if (fm->flags & OFPFF_CHECK_OVERLAP) {
 +        struct classifier *cls;
 +
 +        FOR_EACH_MATCHING_TABLE (cls, fm->table_id, p) {
 +            if (classifier_rule_overlaps(cls, &fm->cr)) {
 +                return ofp_mkerr(OFPET_FLOW_MOD_FAILED, OFPFMFC_OVERLAP);
 +            }
 +        }
      }
  
 -    error = 0;
 -    if (fm->buffer_id != UINT32_MAX) {
 -        error = ofconn_pktbuf_retrieve(ofconn, fm->buffer_id,
 -                                       &packet, &in_port);
 -    } else {
 -        packet = NULL;
 -        in_port = UINT16_MAX;
 +    buf_err = ofconn_pktbuf_retrieve(ofconn, fm->buffer_id, &packet, &in_port);
 +    error = rule_create(p, &fm->cr, fm->actions, fm->n_actions,
 +                        fm->idle_timeout, fm->hard_timeout, fm->cookie,
 +                        fm->flags & OFPFF_SEND_FLOW_REM, &rule);
 +    if (error) {
 +        ofpbuf_delete(packet);
 +        return error;
      }
  
 -    rule = rule_create(&fm->cr, fm->actions, fm->n_actions,
 -                       fm->idle_timeout, fm->hard_timeout, fm->cookie,
 -                       fm->flags & OFPFF_SEND_FLOW_REM);
 -    rule_insert(p, rule);
      if (packet) {
 -        rule_execute(p, rule, in_port, packet);
 +        assert(!buf_err);
 +        return rule_execute(rule, in_port, packet);
      }
 -    return error;
 +    return buf_err;
  }
  
 -static struct rule *
 -find_flow_strict(struct ofproto *p, const struct flow_mod *fm)
 +/* Searches 'p' for an exact match for 'fm', in the table or tables indicated
 + * by fm->table_id.  Returns 0 if no match was found, 1 if exactly one match
 + * was found, 2 if more than one match was found.  If exactly one match is
 + * found, sets '*rulep' to the match, otherwise to NULL.
 + *
 + * This implements the rules for "strict" matching explained in the comment on
 + * struct nxt_flow_mod_table_id in nicira-ext.h.
 + *
 + * Ignores hidden rules. */
 +static int
 +find_flow_strict(struct ofproto *p, const struct flow_mod *fm,
 +                 struct rule **rulep)
  {
 -    return rule_from_cls_rule(classifier_find_rule_exactly(&p->cls, &fm->cr));
 +    struct classifier *cls;
 +
 +    *rulep = NULL;
 +    FOR_EACH_MATCHING_TABLE (cls, fm->table_id, p) {
 +        struct rule *rule;
 +
 +        rule = rule_from_cls_rule(classifier_find_rule_exactly(cls, &fm->cr));
 +        if (rule && !rule_is_hidden(rule)) {
 +            if (*rulep) {
 +                *rulep = NULL;
 +                return 2;
 +            }
 +            *rulep = rule;
 +        }
 +    }
 +    return *rulep != NULL;
  }
  
  static int
  send_buffered_packet(struct ofconn *ofconn,
                       struct rule *rule, uint32_t buffer_id)
  {
 -    struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
      struct ofpbuf *packet;
      uint16_t in_port;
      int error;
          return error;
      }
  
 -    rule_execute(ofproto, rule, in_port, packet);
 -
 -    return 0;
 +    return rule_execute(rule, in_port, packet);
  }
  \f
  /* OFPFC_MODIFY and OFPFC_MODIFY_STRICT. */
@@@ -2293,7 -3400,8 +2283,7 @@@ struct modify_flows_cbdata 
      struct rule *match;
  };
  
 -static int modify_flow(struct ofproto *, const struct flow_mod *,
 -                       struct rule *);
 +static int modify_flow(const struct flow_mod *, struct rule *);
  
  /* Implements OFPFC_MODIFY.  Returns 0 on success or an OpenFlow error code as
   * encoded by ofp_mkerr() on failure.
@@@ -2305,30 -3413,18 +2295,30 @@@ modify_flows_loose(struct ofconn *ofcon
  {
      struct ofproto *p = ofconn_get_ofproto(ofconn);
      struct rule *match = NULL;
 -    struct cls_cursor cursor;
 -    struct rule *rule;
 +    struct classifier *cls;
 +    int error;
 +
 +    error = 0;
 +    FOR_EACH_MATCHING_TABLE (cls, fm->table_id, p) {
 +        struct cls_cursor cursor;
 +        struct rule *rule;
  
 -    cls_cursor_init(&cursor, &p->cls, &fm->cr);
 -    CLS_CURSOR_FOR_EACH (rule, cr, &cursor) {
 -        if (!rule_is_hidden(rule)) {
 -            match = rule;
 -            modify_flow(p, fm, rule);
 +        cls_cursor_init(&cursor, cls, &fm->cr);
 +        CLS_CURSOR_FOR_EACH (rule, cr, &cursor) {
 +            if (!rule_is_hidden(rule)) {
 +                int retval = modify_flow(fm, rule);
 +                if (!retval) {
 +                    match = rule;
 +                } else {
 +                    error = retval;
 +                }
 +            }
          }
      }
  
 -    if (match) {
 +    if (error) {
 +        return error;
 +    } else if (match) {
          /* This credits the packet to whichever flow happened to match last.
           * That's weird.  Maybe we should do a lookup for the flow that
           * actually matches the packet?  Who knows. */
@@@ -2348,79 -3444,57 +2338,79 @@@ static in
  modify_flow_strict(struct ofconn *ofconn, struct flow_mod *fm)
  {
      struct ofproto *p = ofconn_get_ofproto(ofconn);
 -    struct rule *rule = find_flow_strict(p, fm);
 -    if (rule && !rule_is_hidden(rule)) {
 -        modify_flow(p, fm, rule);
 -        return send_buffered_packet(ofconn, rule, fm->buffer_id);
 -    } else {
 +    struct rule *rule;
 +    int error;
 +
 +    switch (find_flow_strict(p, fm, &rule)) {
 +    case 0:
          return add_flow(ofconn, fm);
 +
 +    case 1:
 +        error = modify_flow(fm, rule);
 +        if (!error) {
 +            error = send_buffered_packet(ofconn, rule, fm->buffer_id);
 +        }
 +        return error;
 +
 +    case 2:
 +        return 0;
 +
 +    default:
 +        NOT_REACHED();
      }
  }
  
  /* Implements core of OFPFC_MODIFY and OFPFC_MODIFY_STRICT where 'rule' has
 - * been identified as a flow in 'p''s flow table to be modified, by changing
 - * the rule's actions to match those in 'ofm' (which is followed by 'n_actions'
 - * ofp_action[] structures). */
 + * been identified as a flow to be modified, by changing the rule's actions to
 + * match those in 'ofm' (which is followed by 'n_actions' ofp_action[]
 + * structures). */
  static int
 -modify_flow(struct ofproto *p, const struct flow_mod *fm, struct rule *rule)
 +modify_flow(const struct flow_mod *fm, struct rule *rule)
  {
      size_t actions_len = fm->n_actions * sizeof *rule->actions;
 +    int error;
  
 -    rule->flow_cookie = fm->cookie;
 -
 -    /* If the actions are the same, do nothing. */
      if (fm->n_actions == rule->n_actions
          && (!fm->n_actions
              || !memcmp(fm->actions, rule->actions, actions_len))) {
 -        return 0;
 +        error = 0;
 +    } else {
 +        error = rule->ofproto->ofproto_class->rule_modify_actions(
 +            rule, fm->actions, fm->n_actions);
 +        if (!error) {
 +            free(rule->actions);
 +            rule->actions = (fm->n_actions
 +                             ? xmemdup(fm->actions, actions_len)
 +                             : NULL);
 +            rule->n_actions = fm->n_actions;
 +        }
      }
  
 -    /* Replace actions. */
 -    free(rule->actions);
 -    rule->actions = fm->n_actions ? xmemdup(fm->actions, actions_len) : NULL;
 -    rule->n_actions = fm->n_actions;
 -
 -    p->need_revalidate = true;
 +    if (!error) {
 +        rule->flow_cookie = fm->cookie;
 +    }
  
 -    return 0;
 +    return error;
  }
  \f
  /* OFPFC_DELETE implementation. */
  
 -static void delete_flow(struct ofproto *, struct rule *, ovs_be16 out_port);
 +static void delete_flow(struct rule *, ovs_be16 out_port);
  
  /* Implements OFPFC_DELETE. */
  static void
  delete_flows_loose(struct ofproto *p, const struct flow_mod *fm)
  {
 -    struct rule *rule, *next_rule;
 -    struct cls_cursor cursor;
 +    struct classifier *cls;
 +
 +    FOR_EACH_MATCHING_TABLE (cls, fm->table_id, p) {
 +        struct rule *rule, *next_rule;
 +        struct cls_cursor cursor;
  
 -    cls_cursor_init(&cursor, &p->cls, &fm->cr);
 -    CLS_CURSOR_FOR_EACH_SAFE (rule, next_rule, cr, &cursor) {
 -        delete_flow(p, rule, htons(fm->out_port));
 +        cls_cursor_init(&cursor, cls, &fm->cr);
 +        CLS_CURSOR_FOR_EACH_SAFE (rule, next_rule, cr, &cursor) {
 +            delete_flow(rule, htons(fm->out_port));
 +        }
      }
  }
  
  static void
  delete_flow_strict(struct ofproto *p, struct flow_mod *fm)
  {
 -    struct rule *rule = find_flow_strict(p, fm);
 -    if (rule) {
 -        delete_flow(p, rule, htons(fm->out_port));
 +    struct rule *rule;
 +    if (find_flow_strict(p, fm, &rule) == 1) {
 +        delete_flow(rule, htons(fm->out_port));
      }
  }
  
   * 'out_port' is htons(OFPP_NONE) or if 'rule' actually outputs to the
   * specified 'out_port'. */
  static void
 -delete_flow(struct ofproto *p, struct rule *rule, ovs_be16 out_port)
 +delete_flow(struct rule *rule, ovs_be16 out_port)
  {
      if (rule_is_hidden(rule)) {
          return;
          return;
      }
  
 -    rule_send_removed(p, rule, OFPRR_DELETE);
 -    rule_remove(p, rule);
 +    ofproto_rule_send_removed(rule, OFPRR_DELETE);
 +    ofproto_rule_destroy(rule);
 +}
 +
 +static void
 +ofproto_rule_send_removed(struct rule *rule, uint8_t reason)
 +{
 +    struct ofputil_flow_removed fr;
 +
 +    if (rule_is_hidden(rule) || !rule->send_flow_removed) {
 +        return;
 +    }
 +
 +    fr.rule = rule->cr;
 +    fr.cookie = rule->flow_cookie;
 +    fr.reason = reason;
 +    calc_flow_duration__(rule->created, &fr.duration_sec, &fr.duration_nsec);
 +    fr.idle_timeout = rule->idle_timeout;
 +    rule->ofproto->ofproto_class->rule_get_stats(rule, &fr.packet_count,
 +                                                 &fr.byte_count);
 +
 +    connmgr_send_flow_removed(rule->ofproto->connmgr, &fr);
 +}
 +
 +/* Sends an OpenFlow "flow removed" message with the given 'reason' (either
 + * OFPRR_HARD_TIMEOUT or OFPRR_IDLE_TIMEOUT), and then removes 'rule' from its
 + * ofproto.
 + *
 + * ofproto implementation ->run() functions should use this function to expire
 + * OpenFlow flows. */
 +void
 +ofproto_rule_expire(struct rule *rule, uint8_t reason)
 +{
 +    assert(reason == OFPRR_HARD_TIMEOUT || reason == OFPRR_IDLE_TIMEOUT);
 +    ofproto_rule_send_removed(rule, reason);
 +    ofproto_rule_destroy(rule);
  }
  \f
  static int
@@@ -2503,8 -3543,7 +2493,8 @@@ handle_flow_mod(struct ofconn *ofconn, 
          return error;
      }
  
-     error = ofputil_decode_flow_mod(&fm, oh, ofconn_get_flow_format(ofconn),
 -    error = ofputil_decode_flow_mod(&fm, oh);
++    error = ofputil_decode_flow_mod(&fm, oh,
 +                                    ofconn_get_flow_mod_table_id(ofconn));
      if (error) {
          return error;
      }
          return ofp_mkerr(OFPET_FLOW_MOD_FAILED, OFPFMFC_ALL_TABLES_FULL);
      }
  
 -    error = validate_actions(fm.actions, fm.n_actions,
 -                             &fm.cr.flow, p->max_ports);
 -    if (error) {
 -        return error;
 -    }
 -
      switch (fm.command) {
      case OFPFC_ADD:
          return add_flow(ofconn, &fm);
          return 0;
  
      default:
 +        if (fm.command > 0xff) {
 +            VLOG_WARN_RL(&rl, "flow_mod has explicit table_id but "
 +                         "flow_mod_table_id extension is not enabled");
 +        }
          return ofp_mkerr(OFPET_FLOW_MOD_FAILED, OFPFMFC_BAD_COMMAND);
      }
  }
  
- static int
- handle_tun_id_from_cookie(struct ofconn *ofconn, const struct ofp_header *oh)
- {
-     const struct nxt_tun_id_cookie *msg
-         = (const struct nxt_tun_id_cookie *) oh;
-     enum nx_flow_format flow_format;
-     flow_format = msg->set ? NXFF_TUN_ID_FROM_COOKIE : NXFF_OPENFLOW10;
-     ofconn_set_flow_format(ofconn, flow_format);
-     return 0;
- }
  static int
  handle_role_request(struct ofconn *ofconn, const struct ofp_header *oh)
  {
      return 0;
  }
  
 +static int
 +handle_nxt_flow_mod_table_id(struct ofconn *ofconn,
 +                             const struct ofp_header *oh)
 +{
 +    const struct nxt_flow_mod_table_id *msg
 +        = (const struct nxt_flow_mod_table_id *) oh;
 +
 +    ofconn_set_flow_mod_table_id(ofconn, msg->set != 0);
 +    return 0;
 +}
 +
  static int
  handle_nxt_set_flow_format(struct ofconn *ofconn, const struct ofp_header *oh)
  {
  
      format = ntohl(msg->format);
      if (format == NXFF_OPENFLOW10
-         || format == NXFF_TUN_ID_FROM_COOKIE
          || format == NXFF_NXM) {
          ofconn_set_flow_format(ofconn, format);
          return 0;
@@@ -2673,15 -3689,9 +2649,12 @@@ handle_openflow__(struct ofconn *ofconn
          return 0;
  
          /* Nicira extension requests. */
-     case OFPUTIL_NXT_TUN_ID_FROM_COOKIE:
-         return handle_tun_id_from_cookie(ofconn, oh);
      case OFPUTIL_NXT_ROLE_REQUEST:
          return handle_role_request(ofconn, oh);
  
 +    case OFPUTIL_NXT_FLOW_MOD_TABLE_ID:
 +        return handle_nxt_flow_mod_table_id(ofconn, oh);
 +
      case OFPUTIL_NXT_SET_FLOW_FORMAT:
          return handle_nxt_set_flow_format(ofconn, oh);
  
@@@ -2759,12 -3769,452 +2732,12 @@@ handle_openflow(struct ofconn *ofconn, 
      COVERAGE_INC(ofproto_recv_openflow);
  }
  \f
 -static void
 -handle_miss_upcall(struct ofproto *p, struct dpif_upcall *upcall)
 -{
 -    struct facet *facet;
 -    struct flow flow;
 -
 -    /* Obtain in_port and tun_id, at least. */
 -    odp_flow_key_to_flow(upcall->key, upcall->key_len, &flow);
 -
 -    /* Set header pointers in 'flow'. */
 -    flow_extract(upcall->packet, flow.tun_id, flow.in_port, &flow);
 -
 -    if (cfm_should_process_flow(&flow)) {
 -        ofproto_process_cfm(p, &flow, upcall->packet);
 -        ofpbuf_delete(upcall->packet);
 -        return;
 -    } else if (p->ofhooks->special_cb
 -               && !p->ofhooks->special_cb(&flow, upcall->packet, p->aux)) {
 -        ofpbuf_delete(upcall->packet);
 -        return;
 -    }
 -
 -    /* Check with in-band control to see if this packet should be sent
 -     * to the local port regardless of the flow table. */
 -    if (connmgr_msg_in_hook(p->connmgr, &flow, upcall->packet)) {
 -        ofproto_send_packet(p, ODPP_LOCAL, 0, upcall->packet);
 -    }
 -
 -    facet = facet_lookup_valid(p, &flow);
 -    if (!facet) {
 -        struct rule *rule = rule_lookup(p, &flow);
 -        if (!rule) {
 -            /* Don't send a packet-in if OFPPC_NO_PACKET_IN asserted. */
 -            struct ofport *port = get_port(p, flow.in_port);
 -            if (port) {
 -                if (port->opp.config & OFPPC_NO_PACKET_IN) {
 -                    COVERAGE_INC(ofproto_no_packet_in);
 -                    /* XXX install 'drop' flow entry */
 -                    ofpbuf_delete(upcall->packet);
 -                    return;
 -                }
 -            } else {
 -                VLOG_WARN_RL(&rl, "packet-in on unknown port %"PRIu16,
 -                             flow.in_port);
 -            }
 -
 -            COVERAGE_INC(ofproto_packet_in);
 -            send_packet_in(p, upcall, &flow, false);
 -            return;
 -        }
 -
 -        facet = facet_create(p, rule, &flow, upcall->packet);
 -    } else if (!facet->may_install) {
 -        /* The facet is not installable, that is, we need to process every
 -         * packet, so process the current packet's actions into 'facet'. */
 -        facet_make_actions(p, facet, upcall->packet);
 -    }
 -
 -    if (facet->rule->cr.priority == FAIL_OPEN_PRIORITY) {
 -        /*
 -         * Extra-special case for fail-open mode.
 -         *
 -         * We are in fail-open mode and the packet matched the fail-open rule,
 -         * but we are connected to a controller too.  We should send the packet
 -         * up to the controller in the hope that it will try to set up a flow
 -         * and thereby allow us to exit fail-open.
 -         *
 -         * See the top-level comment in fail-open.c for more information.
 -         */
 -        send_packet_in(p, upcall, &flow, true);
 -    }
 -
 -    facet_execute(p, facet, upcall->packet);
 -    facet_install(p, facet, false);
 -}
 -
 -static void
 -handle_upcall(struct ofproto *p, struct dpif_upcall *upcall)
 -{
 -    struct flow flow;
 -
 -    switch (upcall->type) {
 -    case DPIF_UC_ACTION:
 -        COVERAGE_INC(ofproto_ctlr_action);
 -        odp_flow_key_to_flow(upcall->key, upcall->key_len, &flow);
 -        send_packet_in(p, upcall, &flow, false);
 -        break;
 -
 -    case DPIF_UC_SAMPLE:
 -        if (p->sflow) {
 -            odp_flow_key_to_flow(upcall->key, upcall->key_len, &flow);
 -            ofproto_sflow_received(p->sflow, upcall, &flow);
 -        }
 -        ofpbuf_delete(upcall->packet);
 -        break;
 -
 -    case DPIF_UC_MISS:
 -        handle_miss_upcall(p, upcall);
 -        break;
 -
 -    case DPIF_N_UC_TYPES:
 -    default:
 -        VLOG_WARN_RL(&rl, "upcall has unexpected type %"PRIu32, upcall->type);
 -        break;
 -    }
 -}
 -\f
 -/* Flow expiration. */
 -
 -static int ofproto_dp_max_idle(const struct ofproto *);
 -static void ofproto_update_stats(struct ofproto *);
 -static void rule_expire(struct ofproto *, struct rule *);
 -static void ofproto_expire_facets(struct ofproto *, int dp_max_idle);
 -
 -/* This function is called periodically by ofproto_run().  Its job is to
 - * collect updates for the flows that have been installed into the datapath,
 - * most importantly when they last were used, and then use that information to
 - * expire flows that have not been used recently.
 - *
 - * Returns the number of milliseconds after which it should be called again. */
 -static int
 -ofproto_expire(struct ofproto *ofproto)
 -{
 -    struct rule *rule, *next_rule;
 -    struct cls_cursor cursor;
 -    int dp_max_idle;
 -
 -    /* Update stats for each flow in the datapath. */
 -    ofproto_update_stats(ofproto);
 -
 -    /* Expire facets that have been idle too long. */
 -    dp_max_idle = ofproto_dp_max_idle(ofproto);
 -    ofproto_expire_facets(ofproto, dp_max_idle);
 -
 -    /* Expire OpenFlow flows whose idle_timeout or hard_timeout has passed. */
 -    cls_cursor_init(&cursor, &ofproto->cls, NULL);
 -    CLS_CURSOR_FOR_EACH_SAFE (rule, next_rule, cr, &cursor) {
 -        rule_expire(ofproto, rule);
 -    }
 -
 -    /* Let the hook know that we're at a stable point: all outstanding data
 -     * in existing flows has been accounted to the account_cb.  Thus, the
 -     * hook can now reasonably do operations that depend on having accurate
 -     * flow volume accounting (currently, that's just bond rebalancing). */
 -    if (ofproto->ofhooks->account_checkpoint_cb) {
 -        ofproto->ofhooks->account_checkpoint_cb(ofproto->aux);
 -    }
 -
 -    return MIN(dp_max_idle, 1000);
 -}
 -
 -/* Update 'packet_count', 'byte_count', and 'used' members of installed facets.
 - *
 - * This function also pushes statistics updates to rules which each facet
 - * resubmits into.  Generally these statistics will be accurate.  However, if a
 - * facet changes the rule it resubmits into at some time in between
 - * ofproto_update_stats() runs, it is possible that statistics accrued to the
 - * old rule will be incorrectly attributed to the new rule.  This could be
 - * avoided by calling ofproto_update_stats() whenever rules are created or
 - * deleted.  However, the performance impact of making so many calls to the
 - * datapath do not justify the benefit of having perfectly accurate statistics.
 - */
 -static void
 -ofproto_update_stats(struct ofproto *p)
 -{
 -    const struct dpif_flow_stats *stats;
 -    struct dpif_flow_dump dump;
 -    const struct nlattr *key;
 -    size_t key_len;
 -
 -    dpif_flow_dump_start(&dump, p->dpif);
 -    while (dpif_flow_dump_next(&dump, &key, &key_len, NULL, NULL, &stats)) {
 -        struct facet *facet;
 -        struct flow flow;
 -
 -        if (odp_flow_key_to_flow(key, key_len, &flow)) {
 -            struct ds s;
 -
 -            ds_init(&s);
 -            odp_flow_key_format(key, key_len, &s);
 -            VLOG_WARN_RL(&rl, "failed to convert ODP flow key to flow: %s",
 -                         ds_cstr(&s));
 -            ds_destroy(&s);
 -
 -            continue;
 -        }
 -        facet = facet_find(p, &flow);
 -
 -        if (facet && facet->installed) {
 -
 -            if (stats->n_packets >= facet->dp_packet_count) {
 -                facet->packet_count += stats->n_packets - facet->dp_packet_count;
 -            } else {
 -                VLOG_WARN_RL(&rl, "unexpected packet count from the datapath");
 -            }
 -
 -            if (stats->n_bytes >= facet->dp_byte_count) {
 -                facet->byte_count += stats->n_bytes - facet->dp_byte_count;
 -            } else {
 -                VLOG_WARN_RL(&rl, "unexpected byte count from datapath");
 -            }
 -
 -            facet->dp_packet_count = stats->n_packets;
 -            facet->dp_byte_count = stats->n_bytes;
 -
 -            facet_update_time(p, facet, stats->used);
 -            facet_account(p, facet, stats->n_bytes);
 -            facet_push_stats(p, facet);
 -        } else {
 -            /* There's a flow in the datapath that we know nothing about.
 -             * Delete it. */
 -            COVERAGE_INC(ofproto_unexpected_rule);
 -            dpif_flow_del(p->dpif, key, key_len, NULL);
 -        }
 -    }
 -    dpif_flow_dump_done(&dump);
 -}
 -
 -/* Calculates and returns the number of milliseconds of idle time after which
 - * facets should expire from the datapath and we should fold their statistics
 - * into their parent rules in userspace. */
 -static int
 -ofproto_dp_max_idle(const struct ofproto *ofproto)
 -{
 -    /*
 -     * Idle time histogram.
 -     *
 -     * Most of the time a switch has a relatively small number of facets.  When
 -     * this is the case we might as well keep statistics for all of them in
 -     * userspace and to cache them in the kernel datapath for performance as
 -     * well.
 -     *
 -     * As the number of facets increases, the memory required to maintain
 -     * statistics about them in userspace and in the kernel becomes
 -     * significant.  However, with a large number of facets it is likely that
 -     * only a few of them are "heavy hitters" that consume a large amount of
 -     * bandwidth.  At this point, only heavy hitters are worth caching in the
 -     * kernel and maintaining in userspaces; other facets we can discard.
 -     *
 -     * The technique used to compute the idle time is to build a histogram with
 -     * N_BUCKETS buckets whose width is BUCKET_WIDTH msecs each.  Each facet
 -     * that is installed in the kernel gets dropped in the appropriate bucket.
 -     * After the histogram has been built, we compute the cutoff so that only
 -     * the most-recently-used 1% of facets (but at least 1000 flows) are kept
 -     * cached.  At least the most-recently-used bucket of facets is kept, so
 -     * actually an arbitrary number of facets can be kept in any given
 -     * expiration run (though the next run will delete most of those unless
 -     * they receive additional data).
 -     *
 -     * This requires a second pass through the facets, in addition to the pass
 -     * made by ofproto_update_stats(), because the former function never looks
 -     * at uninstallable facets.
 -     */
 -    enum { BUCKET_WIDTH = ROUND_UP(100, TIME_UPDATE_INTERVAL) };
 -    enum { N_BUCKETS = 5000 / BUCKET_WIDTH };
 -    int buckets[N_BUCKETS] = { 0 };
 -    struct facet *facet;
 -    int total, bucket;
 -    long long int now;
 -    int i;
 -
 -    total = hmap_count(&ofproto->facets);
 -    if (total <= 1000) {
 -        return N_BUCKETS * BUCKET_WIDTH;
 -    }
 -
 -    /* Build histogram. */
 -    now = time_msec();
 -    HMAP_FOR_EACH (facet, hmap_node, &ofproto->facets) {
 -        long long int idle = now - facet->used;
 -        int bucket = (idle <= 0 ? 0
 -                      : idle >= BUCKET_WIDTH * N_BUCKETS ? N_BUCKETS - 1
 -                      : (unsigned int) idle / BUCKET_WIDTH);
 -        buckets[bucket]++;
 -    }
 -
 -    /* Find the first bucket whose flows should be expired. */
 -    for (bucket = 0; bucket < N_BUCKETS; bucket++) {
 -        if (buckets[bucket]) {
 -            int subtotal = 0;
 -            do {
 -                subtotal += buckets[bucket++];
 -            } while (bucket < N_BUCKETS && subtotal < MAX(1000, total / 100));
 -            break;
 -        }
 -    }
 -
 -    if (VLOG_IS_DBG_ENABLED()) {
 -        struct ds s;
 -
 -        ds_init(&s);
 -        ds_put_cstr(&s, "keep");
 -        for (i = 0; i < N_BUCKETS; i++) {
 -            if (i == bucket) {
 -                ds_put_cstr(&s, ", drop");
 -            }
 -            if (buckets[i]) {
 -                ds_put_format(&s, " %d:%d", i * BUCKET_WIDTH, buckets[i]);
 -            }
 -        }
 -        VLOG_INFO("%s: %s (msec:count)",
 -                  dpif_name(ofproto->dpif), ds_cstr(&s));
 -        ds_destroy(&s);
 -    }
 -
 -    return bucket * BUCKET_WIDTH;
 -}
 -
 -static void
 -facet_active_timeout(struct ofproto *ofproto, struct facet *facet)
 -{
 -    if (ofproto->netflow && !facet_is_controller_flow(facet) &&
 -        netflow_active_timeout_expired(ofproto->netflow, &facet->nf_flow)) {
 -        struct ofexpired expired;
 -
 -        if (facet->installed) {
 -            struct dpif_flow_stats stats;
 -
 -            facet_put__(ofproto, facet, facet->actions, facet->actions_len,
 -                        &stats);
 -            facet_update_stats(ofproto, facet, &stats);
 -        }
 -
 -        expired.flow = facet->flow;
 -        expired.packet_count = facet->packet_count;
 -        expired.byte_count = facet->byte_count;
 -        expired.used = facet->used;
 -        netflow_expire(ofproto->netflow, &facet->nf_flow, &expired);
 -    }
 -}
 -
 -static void
 -ofproto_expire_facets(struct ofproto *ofproto, int dp_max_idle)
 -{
 -    long long int cutoff = time_msec() - dp_max_idle;
 -    struct facet *facet, *next_facet;
 -
 -    HMAP_FOR_EACH_SAFE (facet, next_facet, hmap_node, &ofproto->facets) {
 -        facet_active_timeout(ofproto, facet);
 -        if (facet->used < cutoff) {
 -            facet_remove(ofproto, facet);
 -        }
 -    }
 -}
 -
 -/* If 'rule' is an OpenFlow rule, that has expired according to OpenFlow rules,
 - * then delete it entirely. */
 -static void
 -rule_expire(struct ofproto *ofproto, struct rule *rule)
 -{
 -    struct facet *facet, *next_facet;
 -    long long int now;
 -    uint8_t reason;
 -
 -    /* Has 'rule' expired? */
 -    now = time_msec();
 -    if (rule->hard_timeout
 -        && now > rule->created + rule->hard_timeout * 1000) {
 -        reason = OFPRR_HARD_TIMEOUT;
 -    } else if (rule->idle_timeout && list_is_empty(&rule->facets)
 -               && now >rule->used + rule->idle_timeout * 1000) {
 -        reason = OFPRR_IDLE_TIMEOUT;
 -    } else {
 -        return;
 -    }
 -
 -    COVERAGE_INC(ofproto_expired);
 -
 -    /* Update stats.  (This is a no-op if the rule expired due to an idle
 -     * timeout, because that only happens when the rule has no facets left.) */
 -    LIST_FOR_EACH_SAFE (facet, next_facet, list_node, &rule->facets) {
 -        facet_remove(ofproto, facet);
 -    }
 -
 -    /* Get rid of the rule. */
 -    if (!rule_is_hidden(rule)) {
 -        rule_send_removed(ofproto, rule, reason);
 -    }
 -    rule_remove(ofproto, rule);
 -}
 -\f
 -static void
 -rule_send_removed(struct ofproto *p, struct rule *rule, uint8_t reason)
 -{
 -    struct ofputil_flow_removed fr;
 -
 -    if (!rule->send_flow_removed) {
 -        return;
 -    }
 -
 -    fr.rule = rule->cr;
 -    fr.cookie = rule->flow_cookie;
 -    fr.reason = reason;
 -    calc_flow_duration__(rule->created, &fr.duration_sec, &fr.duration_nsec);
 -    fr.idle_timeout = rule->idle_timeout;
 -    fr.packet_count = rule->packet_count;
 -    fr.byte_count = rule->byte_count;
 -
 -    connmgr_send_flow_removed(p->connmgr, &fr);
 -}
 -
 -/* Obtains statistics for 'rule' and stores them in '*packets' and '*bytes'.
 - * The returned statistics include statistics for all of 'rule''s facets. */
 -static void
 -rule_get_stats(const struct rule *rule, uint64_t *packets, uint64_t *bytes)
 -{
 -    uint64_t p, b;
 -    struct facet *facet;
 -
 -    /* Start from historical data for 'rule' itself that are no longer tracked
 -     * in facets.  This counts, for example, facets that have expired. */
 -    p = rule->packet_count;
 -    b = rule->byte_count;
 -
 -    /* Add any statistics that are tracked by facets.  This includes
 -     * statistical data recently updated by ofproto_update_stats() as well as
 -     * stats for packets that were executed "by hand" via dpif_execute(). */
 -    LIST_FOR_EACH (facet, list_node, &rule->facets) {
 -        p += facet->packet_count;
 -        b += facet->byte_count;
 -    }
 -
 -    *packets = p;
 -    *bytes = b;
 -}
 -
 -/* Given 'upcall', of type DPIF_UC_ACTION or DPIF_UC_MISS, sends an
 - * OFPT_PACKET_IN message to each OpenFlow controller as necessary according to
 - * their individual configurations.
 - *
 - * If 'clone' is true, the caller retains ownership of 'upcall->packet'.
 - * Otherwise, ownership is transferred to this function. */
 -static void
 -send_packet_in(struct ofproto *ofproto, struct dpif_upcall *upcall,
 -               const struct flow *flow, bool clone)
 -{
 -    connmgr_send_packet_in(ofproto->connmgr, upcall, flow,
 -                           clone ? NULL : upcall->packet);
 -}
 -
  static uint64_t
  pick_datapath_id(const struct ofproto *ofproto)
  {
      const struct ofport *port;
  
 -    port = get_port(ofproto, ODPP_LOCAL);
 +    port = ofproto_get_port(ofproto, OFPP_LOCAL);
      if (port) {
          uint8_t ea[ETH_ADDR_LEN];
          int error;
@@@ -2787,35 -4237,159 +2760,35 @@@ pick_fallback_dpid(void
      return eth_addr_to_uint64(ea);
  }
  \f
 -static void
 -ofproto_unixctl_list(struct unixctl_conn *conn, const char *arg OVS_UNUSED,
 -                     void *aux OVS_UNUSED)
 -{
 -    const struct shash_node *node;
 -    struct ds results;
 -
 -    ds_init(&results);
 -    SHASH_FOR_EACH (node, &all_ofprotos) {
 -        ds_put_format(&results, "%s\n", node->name);
 -    }
 -    unixctl_command_reply(conn, 200, ds_cstr(&results));
 -    ds_destroy(&results);
 -}
 -
 -struct ofproto_trace {
 -    struct action_xlate_ctx ctx;
 -    struct flow flow;
 -    struct ds *result;
 -};
 +/* unixctl commands. */
  
 -static void
 -trace_format_rule(struct ds *result, int level, const struct rule *rule)
 +struct ofproto *
 +ofproto_lookup(const char *name)
  {
 -    ds_put_char_multiple(result, '\t', level);
 -    if (!rule) {
 -        ds_put_cstr(result, "No match\n");
 -        return;
 -    }
 -
 -    ds_put_format(result, "Rule: cookie=%#"PRIx64" ",
 -                  ntohll(rule->flow_cookie));
 -    cls_rule_format(&rule->cr, result);
 -    ds_put_char(result, '\n');
 -
 -    ds_put_char_multiple(result, '\t', level);
 -    ds_put_cstr(result, "OpenFlow ");
 -    ofp_print_actions(result, (const struct ofp_action_header *) rule->actions,
 -                      rule->n_actions * sizeof *rule->actions);
 -    ds_put_char(result, '\n');
 -}
 +    struct ofproto *ofproto;
  
 -static void
 -trace_format_flow(struct ds *result, int level, const char *title,
 -                 struct ofproto_trace *trace)
 -{
 -    ds_put_char_multiple(result, '\t', level);
 -    ds_put_format(result, "%s: ", title);
 -    if (flow_equal(&trace->ctx.flow, &trace->flow)) {
 -        ds_put_cstr(result, "unchanged");
 -    } else {
 -        flow_format(result, &trace->ctx.flow);
 -        trace->flow = trace->ctx.flow;
 +    HMAP_FOR_EACH_WITH_HASH (ofproto, hmap_node, hash_string(name, 0),
 +                             &all_ofprotos) {
 +        if (!strcmp(ofproto->name, name)) {
 +            return ofproto;
 +        }
      }
 -    ds_put_char(result, '\n');
 -}
 -
 -static void
 -trace_resubmit(struct action_xlate_ctx *ctx, struct rule *rule)
 -{
 -    struct ofproto_trace *trace = CONTAINER_OF(ctx, struct ofproto_trace, ctx);
 -    struct ds *result = trace->result;
 -
 -    ds_put_char(result, '\n');
 -    trace_format_flow(result, ctx->recurse + 1, "Resubmitted flow", trace);
 -    trace_format_rule(result, ctx->recurse + 1, rule);
 +    return NULL;
  }
  
  static void
 -ofproto_unixctl_trace(struct unixctl_conn *conn, const char *args_,
 -                      void *aux OVS_UNUSED)
 +ofproto_unixctl_list(struct unixctl_conn *conn, const char *arg OVS_UNUSED,
 +                     void *aux OVS_UNUSED)
  {
 -    char *dpname, *in_port_s, *tun_id_s, *packet_s;
 -    char *args = xstrdup(args_);
 -    char *save_ptr = NULL;
      struct ofproto *ofproto;
 -    struct ofpbuf packet;
 -    struct rule *rule;
 -    struct ds result;
 -    struct flow flow;
 -    uint16_t in_port;
 -    ovs_be64 tun_id;
 -    char *s;
 -
 -    ofpbuf_init(&packet, strlen(args) / 2);
 -    ds_init(&result);
 -
 -    dpname = strtok_r(args, " ", &save_ptr);
 -    tun_id_s = strtok_r(NULL, " ", &save_ptr);
 -    in_port_s = strtok_r(NULL, " ", &save_ptr);
 -    packet_s = strtok_r(NULL, "", &save_ptr); /* Get entire rest of line. */
 -    if (!dpname || !in_port_s || !packet_s) {
 -        unixctl_command_reply(conn, 501, "Bad command syntax");
 -        goto exit;
 -    }
 -
 -    ofproto = shash_find_data(&all_ofprotos, dpname);
 -    if (!ofproto) {
 -        unixctl_command_reply(conn, 501, "Unknown ofproto (use ofproto/list "
 -                              "for help)");
 -        goto exit;
 -    }
 -
 -    tun_id = htonll(strtoull(tun_id_s, NULL, 0));
 -    in_port = ofp_port_to_odp_port(atoi(in_port_s));
 -
 -    packet_s = ofpbuf_put_hex(&packet, packet_s, NULL);
 -    packet_s += strspn(packet_s, " ");
 -    if (*packet_s != '\0') {
 -        unixctl_command_reply(conn, 501, "Trailing garbage in command");
 -        goto exit;
 -    }
 -    if (packet.size < ETH_HEADER_LEN) {
 -        unixctl_command_reply(conn, 501, "Packet data too short for Ethernet");
 -        goto exit;
 -    }
 -
 -    ds_put_cstr(&result, "Packet: ");
 -    s = ofp_packet_to_string(packet.data, packet.size, packet.size);
 -    ds_put_cstr(&result, s);
 -    free(s);
 -
 -    flow_extract(&packet, tun_id, in_port, &flow);
 -    ds_put_cstr(&result, "Flow: ");
 -    flow_format(&result, &flow);
 -    ds_put_char(&result, '\n');
 +    struct ds results;
  
 -    rule = rule_lookup(ofproto, &flow);
 -    trace_format_rule(&result, 0, rule);
 -    if (rule) {
 -        struct ofproto_trace trace;
 -        struct ofpbuf *odp_actions;
 -
 -        trace.result = &result;
 -        trace.flow = flow;
 -        action_xlate_ctx_init(&trace.ctx, ofproto, &flow, &packet);
 -        trace.ctx.resubmit_hook = trace_resubmit;
 -        odp_actions = xlate_actions(&trace.ctx,
 -                                    rule->actions, rule->n_actions);
 -
 -        ds_put_char(&result, '\n');
 -        trace_format_flow(&result, 0, "Final flow", &trace);
 -        ds_put_cstr(&result, "Datapath actions: ");
 -        format_odp_actions(&result, odp_actions->data, odp_actions->size);
 -        ofpbuf_delete(odp_actions);
 +    ds_init(&results);
 +    HMAP_FOR_EACH (ofproto, hmap_node, &all_ofprotos) {
 +        ds_put_format(&results, "%s\n", ofproto->name);
      }
 -
 -    unixctl_command_reply(conn, 200, ds_cstr(&result));
 -
 -exit:
 -    ds_destroy(&result);
 -    ofpbuf_uninit(&packet);
 -    free(args);
 +    unixctl_command_reply(conn, 200, ds_cstr(&results));
 +    ds_destroy(&results);
  }
  
  static void
@@@ -2828,4 -4402,63 +2801,4 @@@ ofproto_unixctl_init(void
      registered = true;
  
      unixctl_command_register("ofproto/list", ofproto_unixctl_list, NULL);
 -    unixctl_command_register("ofproto/trace", ofproto_unixctl_trace, NULL);
 -}
 -\f
 -static bool
 -default_normal_ofhook_cb(const struct flow *flow, const struct ofpbuf *packet,
 -                         struct ofpbuf *odp_actions, tag_type *tags,
 -                         uint16_t *nf_output_iface, void *ofproto_)
 -{
 -    struct ofproto *ofproto = ofproto_;
 -    struct mac_entry *dst_mac;
 -
 -    /* Drop frames for reserved multicast addresses. */
 -    if (eth_addr_is_reserved(flow->dl_dst)) {
 -        return true;
 -    }
 -
 -    /* Learn source MAC (but don't try to learn from revalidation). */
 -    if (packet != NULL
 -        && mac_learning_may_learn(ofproto->ml, flow->dl_src, 0)) {
 -        struct mac_entry *src_mac;
 -
 -        src_mac = mac_learning_insert(ofproto->ml, flow->dl_src, 0);
 -        if (mac_entry_is_new(src_mac) || src_mac->port.i != flow->in_port) {
 -            /* The log messages here could actually be useful in debugging,
 -             * so keep the rate limit relatively high. */
 -            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(30, 300);
 -            VLOG_DBG_RL(&rl, "learned that "ETH_ADDR_FMT" is on port %"PRIu16,
 -                        ETH_ADDR_ARGS(flow->dl_src), flow->in_port);
 -
 -            ofproto_revalidate(ofproto,
 -                               mac_learning_changed(ofproto->ml, src_mac));
 -            src_mac->port.i = flow->in_port;
 -        }
 -    }
 -
 -    /* Determine output port. */
 -    dst_mac = mac_learning_lookup(ofproto->ml, flow->dl_dst, 0, tags);
 -    if (!dst_mac) {
 -        flood_packets(ofproto, flow->in_port, OFPPC_NO_FLOOD,
 -                      nf_output_iface, odp_actions);
 -    } else {
 -        int out_port = dst_mac->port.i;
 -        if (out_port != flow->in_port) {
 -            nl_msg_put_u32(odp_actions, ODP_ACTION_ATTR_OUTPUT, out_port);
 -            *nf_output_iface = out_port;
 -        } else {
 -            /* Drop. */
 -        }
 -    }
 -
 -    return true;
  }
 -
 -static const struct ofhooks default_ofhooks = {
 -    default_normal_ofhook_cb,
 -    NULL,
 -    NULL,
 -    NULL,
 -    NULL
 -};
diff --combined ofproto/ofproto.h
@@@ -31,9 -31,9 +31,9 @@@
  extern "C" {
  #endif
  
 +struct cfm;
  struct cls_rule;
 -struct nlattr;
 -struct ofhooks;
 +struct netdev;
  struct ofproto;
  struct shash;
  
@@@ -92,60 -92,18 +92,60 @@@ struct ofproto_controller 
  #define DEFAULT_SERIAL_DESC "None"
  #define DEFAULT_DP_DESC "None"
  
 +void ofproto_enumerate_types(struct sset *types);
 +const char *ofproto_normalize_type(const char *);
 +
 +int ofproto_enumerate_names(const char *type, struct sset *names);
 +void ofproto_parse_name(const char *name, char **dp_name, char **dp_type);
 +
  int ofproto_create(const char *datapath, const char *datapath_type,
 -                   const struct ofhooks *, void *aux,
                     struct ofproto **ofprotop);
  void ofproto_destroy(struct ofproto *);
 +int ofproto_delete(const char *name, const char *type);
 +
  int ofproto_run(struct ofproto *);
 -int ofproto_run1(struct ofproto *);
 -int ofproto_run2(struct ofproto *, bool revalidate_all);
  void ofproto_wait(struct ofproto *);
  bool ofproto_is_alive(const struct ofproto *);
  
 -int ofproto_port_del(struct ofproto *, uint16_t odp_port);
 -bool ofproto_port_is_floodable(struct ofproto *, uint16_t odp_port);
 +/* A port within an OpenFlow switch.
 + *
 + * 'name' and 'type' are suitable for passing to netdev_open(). */
 +struct ofproto_port {
 +    char *name;                 /* Network device name, e.g. "eth0". */
 +    char *type;                 /* Network device type, e.g. "system". */
 +    uint16_t ofp_port;          /* OpenFlow port number. */
 +};
 +void ofproto_port_clone(struct ofproto_port *, const struct ofproto_port *);
 +void ofproto_port_destroy(struct ofproto_port *);
 +
 +struct ofproto_port_dump {
 +    const struct ofproto *ofproto;
 +    int error;
 +    void *state;
 +};
 +void ofproto_port_dump_start(struct ofproto_port_dump *,
 +                             const struct ofproto *);
 +bool ofproto_port_dump_next(struct ofproto_port_dump *, struct ofproto_port *);
 +int ofproto_port_dump_done(struct ofproto_port_dump *);
 +
 +/* Iterates through each OFPROTO_PORT in OFPROTO, using DUMP as state.
 + *
 + * Arguments all have pointer type.
 + *
 + * If you break out of the loop, then you need to free the dump structure by
 + * hand using ofproto_port_dump_done(). */
 +#define OFPROTO_PORT_FOR_EACH(OFPROTO_PORT, DUMP, OFPROTO)  \
 +    for (ofproto_port_dump_start(DUMP, OFPROTO);            \
 +         (ofproto_port_dump_next(DUMP, OFPROTO_PORT)        \
 +          ? true                                            \
 +          : (ofproto_port_dump_done(DUMP), false));         \
 +        )
 +
 +int ofproto_port_add(struct ofproto *, struct netdev *, uint16_t *ofp_portp);
 +int ofproto_port_del(struct ofproto *, uint16_t ofp_port);
 +
 +int ofproto_port_query_by_name(const struct ofproto *, const char *devname,
 +                               struct ofproto_port *);
  
  /* Top-level configuration. */
  void ofproto_set_datapath_id(struct ofproto *, uint64_t datapath_id);
@@@ -163,73 -121,52 +163,74 @@@ void ofproto_set_desc(struct ofproto *
  int ofproto_set_snoops(struct ofproto *, const struct sset *snoops);
  int ofproto_set_netflow(struct ofproto *,
                          const struct netflow_options *nf_options);
 -void ofproto_set_sflow(struct ofproto *, const struct ofproto_sflow_options *);
 +int ofproto_set_sflow(struct ofproto *, const struct ofproto_sflow_options *);
  
 -/* Configuration of individual interfaces. */
 -struct cfm;
 +/* Configuration of ports. */
 +
 +void ofproto_port_unregister(struct ofproto *, uint16_t ofp_port);
 +
 +void ofproto_port_clear_cfm(struct ofproto *, uint16_t ofp_port);
 +void ofproto_port_set_cfm(struct ofproto *, uint16_t ofp_port,
 +                          const struct cfm *,
 +                          const uint16_t *remote_mps, size_t n_remote_mps);
 +const struct cfm *ofproto_port_get_cfm(struct ofproto *, uint16_t ofp_port);
 +int ofproto_port_is_lacp_current(struct ofproto *, uint16_t ofp_port);
  
 -void ofproto_iface_clear_cfm(struct ofproto *, uint32_t port_no);
 -void ofproto_iface_set_cfm(struct ofproto *, uint32_t port_no,
 -                           const struct cfm *,
 -                           const uint16_t *remote_mps, size_t n_remote_mps);
 -const struct cfm *ofproto_iface_get_cfm(struct ofproto *, uint32_t port_no);
 +/* Configuration of bundles. */
 +struct ofproto_bundle_settings {
 +    char *name;                 /* For use in log messages. */
 +
 +    uint16_t *slaves;           /* OpenFlow port numbers for slaves. */
 +    size_t n_slaves;
 +
 +    int vlan;                   /* VLAN if access port, -1 if trunk port. */
 +    unsigned long *trunks;      /* vlan_bitmap, NULL to trunk all VLANs. */
 +
 +    struct bond_settings *bond; /* Must be nonnull iff if n_slaves > 1. */
++    uint32_t *bond_stable_ids;  /* Array of n_slaves elements. */
 +
 +    struct lacp_settings *lacp;              /* Nonnull to enable LACP. */
 +    struct lacp_slave_settings *lacp_slaves; /* Array of n_slaves elements. */
 +};
 +
 +int ofproto_bundle_register(struct ofproto *, void *aux,
 +                            const struct ofproto_bundle_settings *);
 +int ofproto_bundle_unregister(struct ofproto *, void *aux);
 +
 +/* Configuration of mirrors. */
 +struct ofproto_mirror_settings {
 +    /* Name for log messages. */
 +    char *name;
 +
 +    /* Bundles that select packets for mirroring upon ingress.  */
 +    void **srcs;                /* A set of registered ofbundle handles. */
 +    size_t n_srcs;
 +
 +    /* Bundles that select packets for mirroring upon egress.  */
 +    void **dsts;                /* A set of registered ofbundle handles. */
 +    size_t n_dsts;
 +
 +    /* VLANs of packets to select for mirroring. */
 +    unsigned long *src_vlans;   /* vlan_bitmap, NULL selects all VLANs. */
 +
 +    /* Output (mutually exclusive). */
 +    void *out_bundle;           /* A registered ofbundle handle or NULL. */
 +    uint16_t out_vlan;          /* Output VLAN, only if out_bundle is NULL. */
 +};
 +
 +int ofproto_mirror_register(struct ofproto *, void *aux,
 +                            const struct ofproto_mirror_settings *);
 +int ofproto_mirror_unregister(struct ofproto *, void *aux);
 +
 +int ofproto_set_flood_vlans(struct ofproto *, unsigned long *flood_vlans);
 +bool ofproto_is_mirror_output_bundle(struct ofproto *, void *aux);
  
  /* Configuration querying. */
 -uint64_t ofproto_get_datapath_id(const struct ofproto *);
 -bool ofproto_has_primary_controller(const struct ofproto *);
 -enum ofproto_fail_mode ofproto_get_fail_mode(const struct ofproto *);
 -void ofproto_get_listeners(const struct ofproto *, struct sset *);
  bool ofproto_has_snoops(const struct ofproto *);
  void ofproto_get_snoops(const struct ofproto *, struct sset *);
  void ofproto_get_all_flows(struct ofproto *p, struct ds *);
 -
 -/* Functions for use by ofproto implementation modules, not by clients. */
 -int ofproto_send_packet(struct ofproto *, uint32_t port_no, uint16_t vlan_tci,
 -                        const struct ofpbuf *);
 -void ofproto_add_flow(struct ofproto *, const struct cls_rule *,
 -                      const union ofp_action *, size_t n_actions);
 -void ofproto_delete_flow(struct ofproto *, const struct cls_rule *);
 -void ofproto_flush_flows(struct ofproto *);
 -
 -/* Hooks for ovs-vswitchd. */
 -struct ofhooks {
 -    bool (*normal_cb)(const struct flow *, const struct ofpbuf *packet,
 -                      struct ofpbuf *odp_actions, tag_type *,
 -                      uint16_t *nf_output_iface, void *aux);
 -    bool (*special_cb)(const struct flow *flow, const struct ofpbuf *packet,
 -                       void *aux);
 -    void (*account_flow_cb)(const struct flow *, tag_type tags,
 -                            const struct nlattr *odp_actions,
 -                            size_t actions_len,
 -                            uint64_t n_bytes, void *aux);
 -    void (*account_checkpoint_cb)(void *aux);
 -
 -    uint16_t (*autopath_cb)(const struct flow *, uint32_t id,
 -                            tag_type *, void *aux);
 -};
 -void ofproto_revalidate(struct ofproto *, tag_type);
 -struct tag_set *ofproto_get_revalidate_set(struct ofproto *);
 +void ofproto_get_netflow_ids(const struct ofproto *,
 +                             uint8_t *engine_type, uint8_t *engine_id);
  
  void ofproto_get_ofproto_controller_info(const struct ofproto *, struct shash *);
  void ofproto_free_ofproto_controller_info(struct shash *);
diff --combined ofproto/private.h
index f2e3204,0000000..35a6d1c
mode 100644,000000..100644
--- /dev/null
@@@ -1,743 -1,0 +1,743 @@@
-      *   - 'wildcards' to OVSFW_ALL.
 +/*
 + * Copyright (c) 2009, 2010, 2011 Nicira Networks.
 + *
 + * Licensed under the Apache License, Version 2.0 (the "License");
 + * you may not use this file except in compliance with the License.
 + * You may obtain a copy of the License at:
 + *
 + *     http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing, software
 + * distributed under the License is distributed on an "AS IS" BASIS,
 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 + * See the License for the specific language governing permissions and
 + * limitations under the License.
 + */
 +
 +#ifndef OFPROTO_PRIVATE_H
 +#define OFPROTO_PRIVATE_H 1
 +
 +/* Definitions for use within ofproto. */
 +
 +#include "ofproto/ofproto.h"
 +#include "classifier.h"
 +#include "list.h"
 +#include "shash.h"
 +#include "timeval.h"
 +
 +/* An OpenFlow switch.
 + *
 + * With few exceptions, ofproto implementations may look at these fields but
 + * should not modify them. */
 +struct ofproto {
 +    const struct ofproto_class *ofproto_class;
 +    char *type;                 /* Datapath type. */
 +    char *name;                 /* Datapath name. */
 +    struct hmap_node hmap_node; /* In global 'all_ofprotos' hmap. */
 +
 +    /* Settings. */
 +    uint64_t fallback_dpid;     /* Datapath ID if no better choice found. */
 +    uint64_t datapath_id;       /* Datapath ID. */
 +    char *mfr_desc;             /* Manufacturer. */
 +    char *hw_desc;              /* Hardware. */
 +    char *sw_desc;              /* Software version. */
 +    char *serial_desc;          /* Serial number. */
 +    char *dp_desc;              /* Datapath description. */
 +
 +    /* Datapath. */
 +    struct netdev_monitor *netdev_monitor;
 +    struct hmap ports;          /* Contains "struct ofport"s. */
 +    struct shash port_by_name;
 +
 +    /* Flow tables. */
 +    struct classifier *tables;  /* Each classifier contains "struct rule"s. */
 +    int n_tables;
 +
 +    /* OpenFlow connections. */
 +    struct connmgr *connmgr;
 +};
 +
 +struct ofproto *ofproto_lookup(const char *name);
 +struct ofport *ofproto_get_port(const struct ofproto *, uint16_t ofp_port);
 +
 +/* An OpenFlow port within a "struct ofproto".
 + *
 + * With few exceptions, ofproto implementations may look at these fields but
 + * should not modify them. */
 +struct ofport {
 +    struct ofproto *ofproto;    /* The ofproto that contains this port. */
 +    struct hmap_node hmap_node; /* In struct ofproto's "ports" hmap. */
 +    struct netdev *netdev;
 +    struct ofp_phy_port opp;
 +    uint16_t ofp_port;          /* OpenFlow port number. */
 +};
 +
 +/* An OpenFlow flow within a "struct ofproto".
 + *
 + * With few exceptions, ofproto implementations may look at these fields but
 + * should not modify them. */
 +struct rule {
 +    struct ofproto *ofproto;     /* The ofproto that contains this rule. */
 +    struct cls_rule cr;          /* In owning ofproto's classifier. */
 +
 +    ovs_be64 flow_cookie;        /* Controller-issued identifier. */
 +
 +    long long int created;       /* Creation time. */
 +    uint16_t idle_timeout;       /* In seconds from time of last use. */
 +    uint16_t hard_timeout;       /* In seconds from time of creation. */
 +    uint8_t table_id;            /* Index in ofproto's 'tables' array. */
 +    bool send_flow_removed;      /* Send a flow removed message? */
 +
 +    union ofp_action *actions;   /* OpenFlow actions. */
 +    int n_actions;               /* Number of elements in actions[]. */
 +};
 +
 +static inline struct rule *
 +rule_from_cls_rule(const struct cls_rule *cls_rule)
 +{
 +    return cls_rule ? CONTAINER_OF(cls_rule, struct rule, cr) : NULL;
 +}
 +
 +void ofproto_rule_expire(struct rule *, uint8_t reason);
 +void ofproto_rule_destroy(struct rule *);
 +
 +/* ofproto class structure, to be defined by each ofproto implementation.
 + *
 + *
 + * Data Structures
 + * ===============
 + *
 + * These functions work primarily with three different kinds of data
 + * structures:
 + *
 + *   - "struct ofproto", which represents an OpenFlow switch.
 + *
 + *   - "struct ofport", which represents a port within an ofproto.
 + *
 + *   - "struct rule", which represents an OpenFlow flow within an ofproto.
 + *
 + * Each of these data structures contains all of the implementation-independent
 + * generic state for the respective concept, called the "base" state.  None of
 + * them contains any extra space for ofproto implementations to use.  Instead,
 + * each implementation is expected to declare its own data structure that
 + * contains an instance of the generic data structure plus additional
 + * implementation-specific members, called the "derived" state.  The
 + * implementation can use casts or (preferably) the CONTAINER_OF macro to
 + * obtain access to derived state given only a pointer to the embedded generic
 + * data structure.
 + *
 + *
 + * Life Cycle
 + * ==========
 + *
 + * Four stylized functions accompany each of these data structures:
 + *
 + *            "alloc"       "construct"       "destruct"       "dealloc"
 + *            ------------  ----------------  ---------------  --------------
 + *   ofproto  ->alloc       ->construct       ->destruct       ->dealloc
 + *   ofport   ->port_alloc  ->port_construct  ->port_destruct  ->port_dealloc
 + *   rule     ->rule_alloc  ->rule_construct  ->rule_destruct  ->rule_dealloc
 + *
 + * Any instance of a given data structure goes through the following life
 + * cycle:
 + *
 + *   1. The client calls the "alloc" function to obtain raw memory.  If "alloc"
 + *      fails, skip all the other steps.
 + *
 + *   2. The client initializes all of the data structure's base state.  If this
 + *      fails, skip to step 7.
 + *
 + *   3. The client calls the "construct" function.  The implementation
 + *      initializes derived state.  It may refer to the already-initialized
 + *      base state.  If "construct" fails, skip to step 6.
 + *
 + *   4. The data structure is now initialized and in use.
 + *
 + *   5. When the data structure is no longer needed, the client calls the
 + *      "destruct" function.  The implementation uninitializes derived state.
 + *      The base state has not been uninitialized yet, so the implementation
 + *      may still refer to it.
 + *
 + *   6. The client uninitializes all of the data structure's base state.
 + *
 + *   7. The client calls the "dealloc" to free the raw memory.  The
 + *      implementation must not refer to base or derived state in the data
 + *      structure, because it has already been uninitialized.
 + *
 + * Each "alloc" function allocates and returns a new instance of the respective
 + * data structure.  The "alloc" function is not given any information about the
 + * use of the new data structure, so it cannot perform much initialization.
 + * Its purpose is just to ensure that the new data structure has enough room
 + * for base and derived state.  It may return a null pointer if memory is not
 + * available, in which case none of the other functions is called.
 + *
 + * Each "construct" function initializes derived state in its respective data
 + * structure.  When "construct" is called, all of the base state has already
 + * been initialized, so the "construct" function may refer to it.  The
 + * "construct" function is allowed to fail, in which case the client calls the
 + * "dealloc" function (but not the "destruct" function).
 + *
 + * Each "destruct" function uninitializes and frees derived state in its
 + * respective data structure.  When "destruct" is called, the base state has
 + * not yet been uninitialized, so the "destruct" function may refer to it.  The
 + * "destruct" function is not allowed to fail.
 + *
 + * Each "dealloc" function frees raw memory that was allocated by the the
 + * "alloc" function.  The memory's base and derived members might not have ever
 + * been initialized (but if "construct" returned successfully, then it has been
 + * "destruct"ed already).  The "dealloc" function is not allowed to fail.
 + *
 + *
 + * Conventions
 + * ===========
 + *
 + * Most of these functions return 0 if they are successful or a positive error
 + * code on failure.  Depending on the function, valid error codes are either
 + * errno values or OpenFlow error codes constructed with ofp_mkerr().
 + *
 + * Most of these functions are expected to execute synchronously, that is, to
 + * block as necessary to obtain a result.  Thus, these functions may return
 + * EAGAIN (or EWOULDBLOCK or EINPROGRESS) only where the function descriptions
 + * explicitly say those errors are a possibility.  We may relax this
 + * requirement in the future if and when we encounter performance problems. */
 +struct ofproto_class {
 +/* ## ----------------- ## */
 +/* ## Factory Functions ## */
 +/* ## ----------------- ## */
 +
 +    /* Enumerates the types of all support ofproto types into 'types'.  The
 +     * caller has already initialized 'types' and other ofproto classes might
 +     * already have added names to it. */
 +    void (*enumerate_types)(struct sset *types);
 +
 +    /* Enumerates the names of all existing datapath of the specified 'type'
 +     * into 'names' 'all_dps'.  The caller has already initialized 'names' as
 +     * an empty sset.
 +     *
 +     * 'type' is one of the types enumerated by ->enumerate_types().
 +     *
 +     * Returns 0 if successful, otherwise a positive errno value.
 +     */
 +    int (*enumerate_names)(const char *type, struct sset *names);
 +
 +    /* Deletes the datapath with the specified 'type' and 'name'.  The caller
 +     * should have closed any open ofproto with this 'type' and 'name'; this
 +     * function is allowed to fail if that is not the case.
 +     *
 +     * 'type' is one of the types enumerated by ->enumerate_types().
 +     * 'name' is one of the names enumerated by ->enumerate_names() for 'type'.
 +     *
 +     * Returns 0 if successful, otherwise a positive errno value.
 +     */
 +    int (*del)(const char *type, const char *name);
 +
 +/* ## --------------------------- ## */
 +/* ## Top-Level ofproto Functions ## */
 +/* ## --------------------------- ## */
 +
 +    /* Life-cycle functions for an "ofproto" (see "Life Cycle" above).
 +     *
 +     * ->construct() should not modify most base members of the ofproto.  In
 +     * particular, the client will initialize the ofproto's 'ports' member
 +     * after construction is complete.
 +     *
 +     * ->construct() should initialize the base 'n_tables' member to the number
 +     * of flow tables supported by the datapath (between 1 and 254, inclusive),
 +     * initialize the base 'tables' member with space for one classifier per
 +     * table, and initialize each classifier with classifier_init.  Each flow
 +     * table should be initially empty, so ->construct() should delete flows
 +     * from the underlying datapath, if necessary, rather than populating the
 +     * tables.
 +     *
 +     * Only one ofproto instance needs to be supported for any given datapath.
 +     * If a datapath is already open as part of one "ofproto", then another
 +     * attempt to "construct" the same datapath as part of another ofproto is
 +     * allowed to fail with an error.
 +     *
 +     * ->construct() returns 0 if successful, otherwise a positive errno
 +     * value. */
 +    struct ofproto *(*alloc)(void);
 +    int (*construct)(struct ofproto *ofproto);
 +    void (*destruct)(struct ofproto *ofproto);
 +    void (*dealloc)(struct ofproto *ofproto);
 +
 +    /* Performs any periodic activity required by 'ofproto'.  It should:
 +     *
 +     *   - Call connmgr_send_packet_in() for each received packet that missed
 +     *     in the OpenFlow flow table or that had a OFPP_CONTROLLER output
 +     *     action.
 +     *
 +     *   - Call ofproto_rule_expire() for each OpenFlow flow that has reached
 +     *     its hard_timeout or idle_timeout, to expire the flow.
 +     *
 +     * Returns 0 if successful, otherwise a positive errno value.  The ENODEV
 +     * return value specifically means that the datapath underlying 'ofproto'
 +     * has been destroyed (externally, e.g. by an admin running ovs-dpctl).
 +     */
 +    int (*run)(struct ofproto *ofproto);
 +
 +    /* Causes the poll loop to wake up when 'ofproto''s 'run' function needs to
 +     * be called, e.g. by calling the timer or fd waiting functions in
 +     * poll-loop.h.  */
 +    void (*wait)(struct ofproto *ofproto);
 +
 +    /* Every "struct rule" in 'ofproto' is about to be deleted, one by one.
 +     * This function may prepare for that, for example by clearing state in
 +     * advance.  It should *not* actually delete any "struct rule"s from
 +     * 'ofproto', only prepare for it.
 +     *
 +     * This function is optional; it's really just for optimization in case
 +     * it's cheaper to delete all the flows from your hardware in a single pass
 +     * than to do it one by one. */
 +    void (*flush)(struct ofproto *ofproto);
 +
 +    /* Helper for the OpenFlow OFPT_FEATURES_REQUEST request.
 +     *
 +     * The implementation should store true in '*arp_match_ip' if the switch
 +     * supports matching IP addresses inside ARP requests and replies, false
 +     * otherwise.
 +     *
 +     * The implementation should store in '*actions' a bitmap of the supported
 +     * OpenFlow actions: the bit with value (1 << n) should be set to 1 if the
 +     * implementation supports the action with value 'n', and to 0 otherwise.
 +     * For example, if the implementation supports the OFPAT_OUTPUT and
 +     * OFPAT_ENQUEUE actions, but no others, it would set '*actions' to (1 <<
 +     * OFPAT_OUTPUT) | (1 << OFPAT_ENQUEUE).  Vendor actions are not included
 +     * in '*actions'. */
 +    void (*get_features)(struct ofproto *ofproto,
 +                         bool *arp_match_ip, uint32_t *actions);
 +
 +    /* Helper for the OpenFlow OFPST_TABLE statistics request.
 +     *
 +     * The 'ots' array contains 'ofproto->n_tables' elements.  Each element is
 +     * initialized as:
 +     *
 +     *   - 'table_id' to the array index.
 +     *
 +     *   - 'name' to "table#" where # is the table ID.
 +     *
++     *   - 'wildcards' to OFPFW_ALL.
 +     *
 +     *   - 'max_entries' to 1,000,000.
 +     *
 +     *   - 'active_count' to the classifier_count() for the table.
 +     *
 +     *   - 'lookup_count' and 'matched_count' to 0.
 +     *
 +     * The implementation should update any members in each element for which
 +     * it has better values:
 +     *
 +     *   - 'name' to a more meaningful name.
 +     *
 +     *   - 'wildcards' to the set of wildcards actually supported by the table
 +     *     (if it doesn't support all OpenFlow wildcards).
 +     *
 +     *   - 'max_entries' to the maximum number of flows actually supported by
 +     *     the hardware.
 +     *
 +     *   - 'lookup_count' to the number of packets looked up in this flow table
 +     *     so far.
 +     *
 +     *   - 'matched_count' to the number of packets looked up in this flow
 +     *     table so far that matched one of the flow entries.
 +     *
 +     * Keep in mind that all of the members of struct ofp_table_stats are in
 +     * network byte order.
 +     */
 +    void (*get_tables)(struct ofproto *ofproto, struct ofp_table_stats *ots);
 +
 +/* ## ---------------- ## */
 +/* ## ofport Functions ## */
 +/* ## ---------------- ## */
 +
 +    /* Life-cycle functions for a "struct ofport" (see "Life Cycle" above).
 +     *
 +     * ->port_construct() should not modify any base members of the ofport.
 +     *
 +     * ofports are managed by the base ofproto code.  The ofproto
 +     * implementation should only create and destroy them in response to calls
 +     * to these functions.  The base ofproto code will create and destroy
 +     * ofports in the following situations:
 +     *
 +     *   - Just after the ->construct() function is called, the base ofproto
 +     *     iterates over all of the implementation's ports, using
 +     *     ->port_dump_start() and related functions, and constructs an ofport
 +     *     for each dumped port.
 +     *
 +     *   - If ->port_poll() reports that a specific port has changed, then the
 +     *     base ofproto will query that port with ->port_query_by_name() and
 +     *     construct or destruct ofports as necessary to reflect the updated
 +     *     set of ports.
 +     *
 +     *   - If ->port_poll() returns ENOBUFS to report an unspecified port set
 +     *     change, then the base ofproto will iterate over all of the
 +     *     implementation's ports, in the same way as at ofproto
 +     *     initialization, and construct and destruct ofports to reflect all of
 +     *     the changes.
 +     *
 +     * ->port_construct() returns 0 if successful, otherwise a positive errno
 +     * value.
 +     */
 +    struct ofport *(*port_alloc)(void);
 +    int (*port_construct)(struct ofport *ofport);
 +    void (*port_destruct)(struct ofport *ofport);
 +    void (*port_dealloc)(struct ofport *ofport);
 +
 +    /* Called after 'ofport->netdev' is replaced by a new netdev object.  If
 +     * the ofproto implementation uses the ofport's netdev internally, then it
 +     * should switch to using the new one.  The old one has been closed.
 +     *
 +     * An ofproto implementation that doesn't need to do anything in this
 +     * function may use a null pointer. */
 +    void (*port_modified)(struct ofport *ofport);
 +
 +    /* Called after an OpenFlow OFPT_PORT_MOD request changes a port's
 +     * configuration.  'ofport->opp.config' contains the new configuration.
 +     * 'old_config' contains the previous configuration.
 +     *
 +     * The caller implements OFPPC_PORT_DOWN using netdev functions to turn
 +     * NETDEV_UP on and off, so this function doesn't have to do anything for
 +     * that bit (and it won't be called if that is the only bit that
 +     * changes). */
 +    void (*port_reconfigured)(struct ofport *ofport, ovs_be32 old_config);
 +
 +    /* Looks up a port named 'devname' in 'ofproto'.  On success, initializes
 +     * '*port' appropriately.
 +     *
 +     * The caller owns the data in 'port' and must free it with
 +     * ofproto_port_destroy() when it is no longer needed. */
 +    int (*port_query_by_name)(const struct ofproto *ofproto,
 +                              const char *devname, struct ofproto_port *port);
 +
 +    /* Attempts to add 'netdev' as a port on 'ofproto'.  Returns 0 if
 +     * successful, otherwise a positive errno value.  If successful, sets
 +     * '*ofp_portp' to the new port's port number.
 +     *
 +     * It doesn't matter whether the new port will be returned by a later call
 +     * to ->port_poll(); the implementation may do whatever is more
 +     * convenient. */
 +    int (*port_add)(struct ofproto *ofproto, struct netdev *netdev,
 +                    uint16_t *ofp_portp);
 +
 +    /* Deletes port number 'ofp_port' from the datapath for 'ofproto'.  Returns
 +     * 0 if successful, otherwise a positive errno value.
 +     *
 +     * It doesn't matter whether the new port will be returned by a later call
 +     * to ->port_poll(); the implementation may do whatever is more
 +     * convenient. */
 +    int (*port_del)(struct ofproto *ofproto, uint16_t ofp_port);
 +
 +    /* Attempts to begin dumping the ports in 'ofproto'.  On success, returns 0
 +     * and initializes '*statep' with any data needed for iteration.  On
 +     * failure, returns a positive errno value. */
 +    int (*port_dump_start)(const struct ofproto *ofproto, void **statep);
 +
 +    /* Attempts to retrieve another port from 'ofproto' for 'state', which was
 +     * initialized by a successful call to the 'port_dump_start' function for
 +     * 'ofproto'.  On success, stores a new ofproto_port into 'port' and
 +     * returns 0.  Returns EOF if the end of the port table has been reached,
 +     * or a positive errno value on error.  This function will not be called
 +     * again once it returns nonzero once for a given iteration (but the
 +     * 'port_dump_done' function will be called afterward).
 +     *
 +     * The ofproto provider retains ownership of the data stored in 'port'.  It
 +     * must remain valid until at least the next call to 'port_dump_next' or
 +     * 'port_dump_done' for 'state'. */
 +    int (*port_dump_next)(const struct ofproto *ofproto, void *state,
 +                          struct ofproto_port *port);
 +
 +    /* Releases resources from 'ofproto' for 'state', which was initialized by
 +     * a successful call to the 'port_dump_start' function for 'ofproto'.  */
 +    int (*port_dump_done)(const struct ofproto *ofproto, void *state);
 +
 +    /* Polls for changes in the set of ports in 'ofproto'.  If the set of ports
 +     * in 'ofproto' has changed, then this function should do one of the
 +     * following:
 +     *
 +     * - Preferably: store the name of the device that was added to or deleted
 +     *   from 'ofproto' in '*devnamep' and return 0.  The caller is responsible
 +     *   for freeing '*devnamep' (with free()) when it no longer needs it.
 +     *
 +     * - Alternatively: return ENOBUFS, without indicating the device that was
 +     *   added or deleted.
 +     *
 +     * Occasional 'false positives', in which the function returns 0 while
 +     * indicating a device that was not actually added or deleted or returns
 +     * ENOBUFS without any change, are acceptable.
 +     *
 +     * The purpose of 'port_poll' is to let 'ofproto' know about changes made
 +     * externally to the 'ofproto' object, e.g. by a system administrator via
 +     * ovs-dpctl.  Therefore, it's OK, and even preferable, for port_poll() to
 +     * not report changes made through calls to 'port_add' or 'port_del' on the
 +     * same 'ofproto' object.  (But it's OK for it to report them too, just
 +     * slightly less efficient.)
 +     *
 +     * If the set of ports in 'ofproto' has not changed, returns EAGAIN.  May
 +     * also return other positive errno values to indicate that something has
 +     * gone wrong.
 +     *
 +     * If the set of ports in a datapath is fixed, or if the only way that the
 +     * set of ports in a datapath can change is through ->port_add() and
 +     * ->port_del(), then this function may be a null pointer.
 +     */
 +    int (*port_poll)(const struct ofproto *ofproto, char **devnamep);
 +
 +    /* Arranges for the poll loop to wake up when ->port_poll() will return a
 +     * value other than EAGAIN.
 +     *
 +     * If the set of ports in a datapath is fixed, or if the only way that the
 +     * set of ports in a datapath can change is through ->port_add() and
 +     * ->port_del(), or if the poll loop will always wake up anyway when
 +     * ->port_poll() will return a value other than EAGAIN, then this function
 +     * may be a null pointer.
 +     */
 +    void (*port_poll_wait)(const struct ofproto *ofproto);
 +
 +    /* Checks the status of LACP negotiation for 'port'.  Returns 1 if LACP
 +     * partner information for 'port' is up-to-date, 0 if LACP partner
 +     * information is not current (generally indicating a connectivity
 +     * problem), or -1 if LACP is not enabled on 'port'.
 +     *
 +     * This function may be a null pointer if the ofproto implementation does
 +     * not support LACP. */
 +    int (*port_is_lacp_current)(const struct ofport *port);
 +
 +/* ## ----------------------- ## */
 +/* ## OpenFlow Rule Functions ## */
 +/* ## ----------------------- ## */
 +
 +    /* Life-cycle functions for a "struct rule" (see "Life Cycle" above).
 +     *
 +     * ->rule_construct() should first check whether the rule is acceptable:
 +     *
 +     *   - Validate that the matching rule in 'rule->cr' is supported by the
 +     *     datapath.  If not, then return an OpenFlow error code (as returned
 +     *     by ofp_mkerr()).
 +     *
 +     *     For example, if the datapath does not support registers, then it
 +     *     should return an error if 'rule->cr' does not wildcard all
 +     *     registers.
 +     *
 +     *   - Validate that 'rule->actions' and 'rule->n_actions' are well-formed
 +     *     OpenFlow actions that can be correctly implemented by the datapath.
 +     *     If not, then return an OpenFlow error code (as returned by
 +     *     ofp_mkerr()).
 +     *
 +     *     The validate_actions() function (in ofp-util.c) can be useful as a
 +     *     model for action validation, but it accepts all of the OpenFlow
 +     *     actions that OVS understands.  If your ofproto implementation only
 +     *     implements a subset of those, then you should implement your own
 +     *     action validation.
 +     *
 +     * If the rule is acceptable, then ->rule_construct() should modify the
 +     * flow table:
 +     *
 +     *   - If there was already a rule with exactly the same matching criteria
 +     *     and priority in the classifier, then it should destroy it (with
 +     *     ofproto_rule_destroy()).
 +     *
 +     *     To the greatest extent possible, the old rule should be destroyed
 +     *     only if inserting the new rule succeeds; that is, ->rule_construct()
 +     *     should be transactional.
 +     *
 +     *     The function classifier_find_rule_exactly() can locate such a rule.
 +     *
 +     *   - Insert the new rule into the ofproto's 'cls' classifier, and into
 +     *     the datapath flow table.
 +     *
 +     *     The function classifier_insert() inserts a rule into the classifier.
 +     *
 +     * Other than inserting 'rule->cr' into the classifier, ->rule_construct()
 +     * should not modify any base members of struct rule.
 +     *
 +     * ->rule_destruct() should remove 'rule' from the ofproto's 'cls'
 +     * classifier (e.g. with classifier_remove()) and from the datapath flow
 +     * table. */
 +    struct rule *(*rule_alloc)(void);
 +    int (*rule_construct)(struct rule *rule);
 +    void (*rule_destruct)(struct rule *rule);
 +    void (*rule_dealloc)(struct rule *rule);
 +
 +    /* Obtains statistics for 'rule', storing the number of packets that have
 +     * matched it in '*packet_count' and the number of bytes in those packets
 +     * in '*byte_count'. */
 +    void (*rule_get_stats)(struct rule *rule, uint64_t *packet_count,
 +                           uint64_t *byte_count);
 +
 +    /* Applies the actions in 'rule' to 'packet'.  (This implements sending
 +     * buffered packets for OpenFlow OFPT_FLOW_MOD commands.)
 +     *
 +     * Takes ownership of 'packet' (so it should eventually free it, with
 +     * ofpbuf_delete()).
 +     *
 +     * 'flow' reflects the flow information for 'packet'.  All of the
 +     * information in 'flow' is extracted from 'packet', except for
 +     * flow->tun_id and flow->in_port, which are assigned the correct values
 +     * for the incoming packet.  The register values are zeroed.
 +     *
 +     * The statistics for 'packet' should be included in 'rule'.
 +     *
 +     * Returns 0 if successful, otherwise an OpenFlow error code (as returned
 +     * by ofp_mkerr()). */
 +    int (*rule_execute)(struct rule *rule, struct flow *flow,
 +                        struct ofpbuf *packet);
 +
 +    /* Validates that the 'n' elements in 'actions' are well-formed OpenFlow
 +     * actions that can be correctly implemented by the datapath.  If not, then
 +     * return an OpenFlow error code (as returned by ofp_mkerr()).  If so,
 +     * then update the datapath to implement the new actions and return 0.
 +     *
 +     * When this function runs, 'rule' still has its original actions.  If this
 +     * function returns 0, then the caller will update 'rule' with the new
 +     * actions and free the old ones. */
 +    int (*rule_modify_actions)(struct rule *rule,
 +                               const union ofp_action *actions, size_t n);
 +
 +    /* These functions implement the OpenFlow IP fragment handling policy.  By
 +     * default ('drop_frags' == false), an OpenFlow switch should treat IP
 +     * fragments the same way as other packets (although TCP and UDP port
 +     * numbers cannot be determined).  With 'drop_frags' == true, the switch
 +     * should drop all IP fragments without passing them through the flow
 +     * table. */
 +    bool (*get_drop_frags)(struct ofproto *ofproto);
 +    void (*set_drop_frags)(struct ofproto *ofproto, bool drop_frags);
 +
 +    /* Implements the OpenFlow OFPT_PACKET_OUT command.  The datapath should
 +     * execute the 'n_actions' in the 'actions' array on 'packet'.
 +     *
 +     * The caller retains ownership of 'packet', so ->packet_out() should not
 +     * modify or free it.
 +     *
 +     * This function must validate that the 'n_actions' elements in 'actions'
 +     * are well-formed OpenFlow actions that can be correctly implemented by
 +     * the datapath.  If not, then it should return an OpenFlow error code (as
 +     * returned by ofp_mkerr()).
 +     *
 +     * 'flow' reflects the flow information for 'packet'.  All of the
 +     * information in 'flow' is extracted from 'packet', except for
 +     * flow->in_port, which is taken from the OFPT_PACKET_OUT message.
 +     * flow->tun_id and its register values are zeroed.
 +     *
 +     * 'packet' is not matched against the OpenFlow flow table, so its
 +     * statistics should not be included in OpenFlow flow statistics.
 +     *
 +     * Returns 0 if successful, otherwise an OpenFlow error code (as returned
 +     * by ofp_mkerr()). */
 +    int (*packet_out)(struct ofproto *ofproto, struct ofpbuf *packet,
 +                      const struct flow *flow,
 +                      const union ofp_action *actions,
 +                      size_t n_actions);
 +
 +/* ## ------------------------- ## */
 +/* ## OFPP_NORMAL configuration ## */
 +/* ## ------------------------- ## */
 +
 +    /* Configures NetFlow on 'ofproto' according to the options in
 +     * 'netflow_options', or turns off NetFlow if 'netflow_options' is NULL.
 +     *
 +     * EOPNOTSUPP as a return value indicates that 'ofproto' does not support
 +     * sFlow, as does a null pointer. */
 +    int (*set_netflow)(struct ofproto *ofproto,
 +                       const struct netflow_options *netflow_options);
 +
 +    void (*get_netflow_ids)(const struct ofproto *ofproto,
 +                            uint8_t *engine_type, uint8_t *engine_id);
 +
 +    /* Configures sFlow on 'ofproto' according to the options in
 +     * 'sflow_options', or turns off sFlow if 'sflow_options' is NULL.
 +     *
 +     * EOPNOTSUPP as a return value indicates that 'ofproto' does not support
 +     * sFlow, as does a null pointer. */
 +    int (*set_sflow)(struct ofproto *ofproto,
 +                     const struct ofproto_sflow_options *sflow_options);
 +
 +    /* Configures connectivity fault management on 'ofport'.
 +     *
 +     * If 'cfm' is nonnull, takes basic configuration from the configuration
 +     * members in 'cfm', and the set of remote maintenance points from the
 +     * 'n_remote_mps' elements in 'remote_mps'.  Ignores the statistics members
 +     * of 'cfm'.
 +     *
 +     * If 'cfm' is null, removes any connectivity fault management
 +     * configuration from 'ofport'.
 +     *
 +     * EOPNOTSUPP as a return value indicates that this ofproto_class does not
 +     * support CFM, as does a null pointer. */
 +    int (*set_cfm)(struct ofport *ofport, const struct cfm *cfm,
 +                   const uint16_t *remote_mps, size_t n_remote_mps);
 +
 +    /* Stores the connectivity fault management object associated with 'ofport'
 +     * in '*cfmp'.  Stores a null pointer in '*cfmp' if CFM is not configured
 +     * on 'ofport'.  The caller must not modify or destroy the returned object.
 +     *
 +     * This function may be NULL if this ofproto_class does not support CFM. */
 +    int (*get_cfm)(struct ofport *ofport, const struct cfm **cfmp);
 +
 +    /* If 's' is nonnull, this function registers a "bundle" associated with
 +     * client data pointer 'aux' in 'ofproto'.  A bundle is the same concept as
 +     * a Port in OVSDB, that is, it consists of one or more "slave" devices
 +     * (Interfaces, in OVSDB) along with VLAN and LACP configuration and, if
 +     * there is more than one slave, a bonding configuration.  If 'aux' is
 +     * already registered then this function updates its configuration to 's'.
 +     * Otherwise, this function registers a new bundle.
 +     *
 +     * If 's' is NULL, this function unregisters the bundle registered on
 +     * 'ofproto' associated with client data pointer 'aux'.  If no such bundle
 +     * has been registered, this has no effect.
 +     *
 +     * This function affects only the behavior of the NXAST_AUTOPATH action and
 +     * output to the OFPP_NORMAL port.  An implementation that does not support
 +     * it at all may set it to NULL or return EOPNOTSUPP.  An implementation
 +     * that supports only a subset of the functionality should implement what
 +     * it can and return 0. */
 +    int (*bundle_set)(struct ofproto *ofproto, void *aux,
 +                      const struct ofproto_bundle_settings *s);
 +
 +    /* If 'port' is part of any bundle, removes it from that bundle.  If the
 +     * bundle now has no ports, deletes the bundle.  If the bundle now has only
 +     * one port, deconfigures the bundle's bonding configuration. */
 +    void (*bundle_remove)(struct ofport *ofport);
 +
 +    /* If 's' is nonnull, this function registers a mirror associated with
 +     * client data pointer 'aux' in 'ofproto'.  A mirror is the same concept as
 +     * a Mirror in OVSDB.  If 'aux' is already registered then this function
 +     * updates its configuration to 's'.  Otherwise, this function registers a
 +     * new mirror.
 +     *
 +     * If 's' is NULL, this function unregisters the mirror registered on
 +     * 'ofproto' associated with client data pointer 'aux'.  If no such mirror
 +     * has been registered, this has no effect.
 +     *
 +     * This function affects only the behavior of the OFPP_NORMAL action.  An
 +     * implementation that does not support it at all may set it to NULL or
 +     * return EOPNOTSUPP.  An implementation that supports only a subset of the
 +     * functionality should implement what it can and return 0. */
 +    int (*mirror_set)(struct ofproto *ofproto, void *aux,
 +                      const struct ofproto_mirror_settings *s);
 +
 +    /* Configures the VLANs whose bits are set to 1 in 'flood_vlans' as VLANs
 +     * on which all packets are flooded, instead of using MAC learning.  If
 +     * 'flood_vlans' is NULL, then MAC learning applies to all VLANs.
 +     *
 +     * This function affects only the behavior of the OFPP_NORMAL action.  An
 +     * implementation that does not support it may set it to NULL or return
 +     * EOPNOTSUPP. */
 +    int (*set_flood_vlans)(struct ofproto *ofproto,
 +                           unsigned long *flood_vlans);
 +
 +    /* Returns true if 'aux' is a registered bundle that is currently in use as
 +     * the output for a mirror. */
 +    bool (*is_mirror_output_bundle)(struct ofproto *ofproto, void *aux);
 +};
 +
 +extern const struct ofproto_class ofproto_dpif_class;
 +
 +int ofproto_class_register(const struct ofproto_class *);
 +int ofproto_class_unregister(const struct ofproto_class *);
 +
 +void ofproto_add_flow(struct ofproto *, const struct cls_rule *,
 +                      const union ofp_action *, size_t n_actions);
 +void ofproto_delete_flow(struct ofproto *, const struct cls_rule *);
 +void ofproto_flush_flows(struct ofproto *);
 +
 +#endif /* ofproto/private.h */
diff --combined tests/ofproto.at
@@@ -11,7 -11,7 +11,7 @@@ OFPROTO_STAR
  AT_CHECK([ovs-ofctl -vANY:ANY:WARN show br0], [0], [stdout])
  AT_CHECK([STRIP_XIDS stdout], [0], [dnl
  OFPT_FEATURES_REPLY: ver:0x1, dpid:fedcba9876543210
 -n_tables:2, n_buffers:256
 +n_tables:1, n_buffers:256
  features: capabilities:0x87, actions:0xfff
   LOCAL(br0): addr:aa:55:aa:55:00:00, config: 0x1, state:0x1
  OFPT_GET_CONFIG_REPLY: frags=normal miss_send_len=0
@@@ -33,7 -33,7 +33,7 @@@ d
      AT_CHECK([ovs-ofctl -vANY:ANY:WARN show br0], [0], [stdout])
      AT_CHECK_UNQUOTED([STRIP_XIDS stdout], [0], [dnl
  OFPT_FEATURES_REPLY: ver:0x1, dpid:fedcba9876543210
 -n_tables:2, n_buffers:256
 +n_tables:1, n_buffers:256
  features: capabilities:0x87, actions:0xfff
   LOCAL(br0): addr:aa:55:aa:55:00:00, config: $config, state:$state
  OFPT_GET_CONFIG_REPLY: frags=normal miss_send_len=0
@@@ -48,13 -48,9 +48,10 @@@ AT_CHECK([ovs-ofctl dump-flows br0 | ST
  ])
  AT_CHECK([echo 'in_port=1,actions=0' | ovs-ofctl add-flows br0 -])
  AT_CHECK([ovs-ofctl add-flow br0 in_port=0,actions=1])
- dnl Tests for a bug in which ofproto ignored tun_id in tun_id_from_cookie
- dnl flow_mod commands.
- AT_CHECK([ovs-ofctl add-flow -F tun_id_from_cookie br0 tun_id=1,actions=mod_vlan_vid:4])
  AT_CHECK([ovs-ofctl dump-flows br0 | STRIP_XIDS | STRIP_DURATION | sort], [0], [dnl
 + cookie=0x0, duration=?s, table_id=0, n_packets=0, n_bytes=0, in_port=0 actions=output:1
   cookie=0x0, duration=?s, table_id=0, n_packets=0, n_bytes=0, in_port=1 actions=output:0
-  cookie=0x100000000, duration=?s, table_id=0, n_packets=0, n_bytes=0, tun_id=0x1 actions=mod_vlan_vid:4
+  cookie=0x0, duration=?s, table_id=0, n_packets=0, n_bytes=0, in_port=65534 actions=output:1
  NXST_FLOW reply:
  ])
  AT_CHECK([ovs-ofctl del-flows br0])
diff --combined tests/ovs-ofctl.at
@@@ -14,7 -14,7 +14,7 @@@ actions=note:41.42.43,note:00.01.02.03.
  tun_id=0x1234,cookie=0x5678,actions=flood
  actions=set_tunnel:0x1234,set_tunnel64:0x9876,set_tunnel:0x123456789
  actions=multipath(eth_src, 50, hrw, 12, 0, NXM_NX_REG0[0..3]),multipath(symmetric_l4, 1024, iter_hash, 5000, 5050, NXM_NX_REG0[0..12])
 -actions=drop
 +table=1,actions=drop
  tun_id=0x1234000056780000/0xffff0000ffff0000,actions=drop
  ]])
  AT_CHECK([ovs-ofctl parse-flows flows.txt
@@@ -28,31 -28,23 +28,23 @@@ OFPT_FLOW_MOD: ADD tcp,nw_src=192.168.0
  OFPT_FLOW_MOD: ADD udp,nw_src=192.168.0.3,tp_dst=53 actions=pop_queue,output:1
  OFPT_FLOW_MOD: ADD priority=60000 cookie:0x123456789abcdef hard:10 actions=CONTROLLER:65535
  OFPT_FLOW_MOD: ADD actions=note:41.42.43.00.00.00,note:00.01.02.03.04.05.06.07.00.00.00.00.00.00,note:00.00.00.00.00.00
- NXT_TUN_ID_FROM_COOKIE: set=1
- OFPT_FLOW_MOD: ADD cookie:0x123400005678 actions=FLOOD
- OFPT_FLOW_MOD: ADD actions=set_tunnel:0x1234,set_tunnel64:0x9876,set_tunnel64:0x123456789
- OFPT_FLOW_MOD: ADD actions=multipath(eth_src,50,hrw,12,0,NXM_NX_REG0[0..3]),multipath(symmetric_l4,1024,iter_hash,5000,5050,NXM_NX_REG0[0..12])
- NXT_FLOW_MOD_TABLE_ID: enable
- OFPT_FLOW_MOD: ADD table_id:1 actions=drop
  NXT_SET_FLOW_FORMAT: format=nxm
- NXT_FLOW_MOD: ADD table_id:255 tun_id=0x1234000056780000/0xffff0000ffff0000 actions=drop
+ NXT_FLOW_MOD: ADD tun_id=0x1234 cookie:0x5678 actions=FLOOD
+ NXT_FLOW_MOD: ADD actions=set_tunnel:0x1234,set_tunnel64:0x9876,set_tunnel64:0x123456789
+ NXT_FLOW_MOD: ADD actions=multipath(eth_src,50,hrw,12,0,NXM_NX_REG0[0..3]),multipath(symmetric_l4,1024,iter_hash,5000,5050,NXM_NX_REG0[0..12])
+ NXT_FLOW_MOD: ADD actions=drop
+ NXT_FLOW_MOD: ADD tun_id=0x1234000056780000/0xffff0000ffff0000 actions=drop
  ]])
  AT_CHECK([sed 's/.*|//' stderr], [0], [dnl
  normalization changed ofp_match, details:
   pre: wildcards=  0x3820f8  in_port=65534  dl_src=00:0a:e4:25:6b:b0  dl_dst=00:00:00:00:00:00  dl_vlan=    9  dl_vlan_pcp=  0  dl_type=     0  nw_tos=   0  nw_proto=   0  nw_src=         0  nw_dst=         0  tp_src=    0  tp_dst=    0
  post: wildcards=  0x3ffff8  in_port=65534  dl_src=00:0a:e4:25:6b:b0  dl_dst=00:00:00:00:00:00  dl_vlan=    9  dl_vlan_pcp=  0  dl_type=     0  nw_tos=   0  nw_proto=   0  nw_src=         0  nw_dst=         0  tp_src=    0  tp_dst=    0
  normalization changed ofp_match, details:
 - pre: wildcards=  0x3820ff  in_port=65534  dl_src=00:00:00:00:00:00  dl_dst=00:00:00:00:00:00  dl_vlan=    0  dl_vlan_pcp=  0  dl_type=     0  nw_tos=   0  nw_proto=   0  nw_src=         0  nw_dst=         0  tp_src=    0  tp_dst=    0
 -post: wildcards=  0x3fffff  in_port=65534  dl_src=00:00:00:00:00:00  dl_dst=00:00:00:00:00:00  dl_vlan=    0  dl_vlan_pcp=  0  dl_type=     0  nw_tos=   0  nw_proto=   0  nw_src=         0  nw_dst=         0  tp_src=    0  tp_dst=    0
 + pre: wildcards=  0x3820ff  in_port=    0  dl_src=00:00:00:00:00:00  dl_dst=00:00:00:00:00:00  dl_vlan=    0  dl_vlan_pcp=  0  dl_type=     0  nw_tos=   0  nw_proto=   0  nw_src=         0  nw_dst=         0  tp_src=    0  tp_dst=    0
 +post: wildcards=  0x3fffff  in_port=    0  dl_src=00:00:00:00:00:00  dl_dst=00:00:00:00:00:00  dl_vlan=    0  dl_vlan_pcp=  0  dl_type=     0  nw_tos=   0  nw_proto=   0  nw_src=         0  nw_dst=         0  tp_src=    0  tp_dst=    0
  normalization changed ofp_match, details:
-  pre: wildcards=  0x3820ff  in_port=    0  dl_src=00:00:00:00:00:00  dl_dst=00:00:00:00:00:00  dl_vlan=    0  dl_vlan_pcp=  0  dl_type=     0  nw_tos=   0  nw_proto=   0  nw_src=         0  nw_dst=         0  tp_src=    0  tp_dst=    0
- post: wildcards=  0x3fffff  in_port=    0  dl_src=00:00:00:00:00:00  dl_dst=00:00:00:00:00:00  dl_vlan=    0  dl_vlan_pcp=  0  dl_type=     0  nw_tos=   0  nw_proto=   0  nw_src=         0  nw_dst=         0  tp_src=    0  tp_dst=    0
- normalization changed ofp_match, details:
-  pre: wildcards=  0x3820ff  in_port=    0  dl_src=00:00:00:00:00:00  dl_dst=00:00:00:00:00:00  dl_vlan=    0  dl_vlan_pcp=  0  dl_type=     0  nw_tos=   0  nw_proto=   0  nw_src=         0  nw_dst=         0  tp_src=    0  tp_dst=    0
- post: wildcards=  0x3fffff  in_port=    0  dl_src=00:00:00:00:00:00  dl_dst=00:00:00:00:00:00  dl_vlan=    0  dl_vlan_pcp=  0  dl_type=     0  nw_tos=   0  nw_proto=   0  nw_src=         0  nw_dst=         0  tp_src=    0  tp_dst=    0
- normalization changed ofp_match, details:
-  pre: wildcards= 0x23820ff  in_port=    0  dl_src=00:00:00:00:00:00  dl_dst=00:00:00:00:00:00  dl_vlan=    0  dl_vlan_pcp=  0  dl_type=     0  nw_tos=   0  nw_proto=   0  nw_src=         0  nw_dst=         0  tp_src=    0  tp_dst=    0
- post: wildcards= 0x23fffff  in_port=    0  dl_src=00:00:00:00:00:00  dl_dst=00:00:00:00:00:00  dl_vlan=    0  dl_vlan_pcp=  0  dl_type=     0  nw_tos=   0  nw_proto=   0  nw_src=         0  nw_dst=         0  tp_src=    0  tp_dst=    0
+  pre: wildcards=  0x3820ff  in_port=65534  dl_src=00:00:00:00:00:00  dl_dst=00:00:00:00:00:00  dl_vlan=    0  dl_vlan_pcp=  0  dl_type=     0  nw_tos=   0  nw_proto=   0  nw_src=         0  nw_dst=         0  tp_src=    0  tp_dst=    0
+ post: wildcards=  0x3fffff  in_port=65534  dl_src=00:00:00:00:00:00  dl_dst=00:00:00:00:00:00  dl_vlan=    0  dl_vlan_pcp=  0  dl_type=     0  nw_tos=   0  nw_proto=   0  nw_src=         0  nw_dst=         0  tp_src=    0  tp_dst=    0
  ])
  AT_CLEANUP
  
@@@ -316,7 -308,7 +308,7 @@@ AT_CHECK([ovs-ofctl parse-nx-match < nx
  <any>
  
  # in port
 -NXM_OF_IN_PORT(fffe)
 +NXM_OF_IN_PORT(0000)
  NXM_OF_IN_PORT(fffe)
  
  # eth dst
@@@ -470,7 -462,7 +462,7 @@@ dnl Check that "-F openflow10" rejects 
  dnl OpenFlow 1.0 doesn't support tunnels.
  AT_SETUP([ovs-ofctl -F option and tun_id])
  AT_CHECK([ovs-ofctl -F openflow10 add-flow dummy tun_id=123,actions=drop],
-   [1], [], [ovs-ofctl: flow cannot be expressed in flow format openflow10 (flow format tun_id_from_cookie or better is required)
+   [1], [], [ovs-ofctl: flow cannot be expressed in flow format openflow10 (flow format nxm or better is required)
  ])
  AT_CLEANUP
  
@@@ -227,7 -227,6 +227,7 @@@ new_switch(struct switch_ *sw, struct v
      cfg.mode = (action_normal ? LSW_NORMAL
                  : learn_macs ? LSW_LEARN
                  : LSW_FLOOD);
 +    cfg.exact_flows = exact_flows;
      cfg.max_idle = set_up_flows ? max_idle : -1;
      cfg.default_flows = &default_flows;
      cfg.default_queue = default_queue;
@@@ -261,7 -260,6 +261,7 @@@ static voi
  read_flow_file(const char *name)
  {
      enum nx_flow_format flow_format;
 +    bool flow_mod_table_id;
      FILE *stream;
  
      stream = fopen(optarg, "r");
      }
  
      flow_format = NXFF_OPENFLOW10;
 -    while (parse_ofp_flow_mod_file(&default_flows, &flow_format, stream,
 -                                   OFPFC_ADD)) {
 +    flow_mod_table_id = false;
 +    while (parse_ofp_flow_mod_file(&default_flows,
 +                                   &flow_format, &flow_mod_table_id,
 +                                   stream, OFPFC_ADD)) {
          continue;
      }
  
@@@ -328,10 -324,8 +328,8 @@@ parse_options(int argc, char *argv[]
          {"version",     no_argument, 0, 'V'},
          DAEMON_LONG_OPTIONS,
          VLOG_LONG_OPTIONS,
- #ifdef HAVE_OPENSSL
-         STREAM_SSL_LONG_OPTIONS
+         STREAM_SSL_LONG_OPTIONS,
          {"peer-ca-cert", required_argument, 0, OPT_PEER_CA_CERT},
- #endif
          {0, 0, 0, 0},
      };
      char *short_options = long_options_to_short_options(long_options);
          VLOG_OPTION_HANDLERS
          DAEMON_OPTION_HANDLERS
  
- #ifdef HAVE_OPENSSL
          STREAM_SSL_OPTION_HANDLERS
  
          case OPT_PEER_CA_CERT:
              stream_ssl_set_peer_ca_cert_file(optarg);
              break;
- #endif
  
          case '?':
              exit(EXIT_FAILURE);
diff --combined utilities/ovs-ofctl.8.in
@@@ -365,25 -365,6 +365,25 @@@ specified as a decimal number between 
  When \fBdl_type\fR and \fBnw_proto\fR take other values, the values of
  these settings are ignored (see \fBFlow Syntax\fR above).
  .
 +.IP \fBtable=\fInumber\fR
 +If specified, limits the flow manipulation and flow dump commands to
 +only apply to the table with the given \fInumber\fR.
 +\fInumber\fR is a number between 0 and 254, inclusive.
 +.
 +Behavior varies if \fBtable\fR is not specified.  For flow table
 +modification commands without \fB\-\-strict\fR, the switch will choose
 +the table for these commands to operate on.  For flow table
 +modification commands with \fB\-\-strict\fR, the command will operate
 +on any single matching flow in any table; it will do nothing if there
 +are matches in more than one table.  The \fBdump-flows\fR and
 +\fBdump-aggregate\fR commands will gather statistics about flows from
 +all tables.
 +.IP
 +When this field is specified in \fBadd-flow\fR, \fBadd-flows\fR,
 +\fBmod-flows\fR and \fBdel-flows\fR commands, it activates a Nicira
 +extension to OpenFlow, which as of this writing is only known to be
 +implemented by Open vSwitch.
 +.
  .PP
  The following shorthand notations are also available:
  .
@@@ -732,6 -713,14 +732,6 @@@ If set, a matching flow must include a
  The \fBdump\-flows\fR and \fBdump\-aggregate\fR commands support an
  additional optional field:
  .
 -.IP \fBtable=\fInumber\fR
 -If specified, limits the flows about which statistics are gathered to
 -those in the table with the given \fInumber\fR.  Tables are numbered
 -as shown by the \fBdump\-tables\fR command.
 -.
 -If this field is not specified, or if \fInumber\fR is given as
 -\fB255\fR, statistics are gathered about flows from all tables.
 -.
  .SS "Table Entry Output"
  .
  The \fBdump\-tables\fR and \fBdump\-aggregate\fR commands print information 
@@@ -775,11 -764,6 +775,6 @@@ increasing capability
  This is the standard OpenFlow 1.0 flow format.  It should be supported
  by all OpenFlow switches.
  .
- .IP "\fBtun_id_from_cookie\fR"
- This Nicira extension to OpenFlow adds minimal and limited support for
- \fBtun_id\fR, but it does not support any other Nicira flow
- extensions.  (This flow format is deprecated.)
- .
  .IP "\fBnxm\fR (Nicira Extended Match)"
  This Nicira extension to OpenFlow is flexible and extensible.  It
  supports all of the Nicira flow extensions, such as \fBtun_id\fR and
diff --combined utilities/ovs-ofctl.c
@@@ -32,6 -32,7 +32,6 @@@
  #include "command-line.h"
  #include "compiler.h"
  #include "dirs.h"
 -#include "dpif.h"
  #include "dynamic-string.h"
  #include "netlink.h"
  #include "nx-match.h"
@@@ -40,7 -41,6 +40,7 @@@
  #include "ofp-print.h"
  #include "ofp-util.h"
  #include "ofpbuf.h"
 +#include "ofproto/ofproto.h"
  #include "openflow/nicira-ext.h"
  #include "openflow/openflow.h"
  #include "random.h"
@@@ -92,7 -92,7 +92,7 @@@ parse_options(int argc, char *argv[]
          {"help", no_argument, 0, 'h'},
          {"version", no_argument, 0, 'V'},
          VLOG_LONG_OPTIONS,
-         STREAM_SSL_LONG_OPTIONS
+         STREAM_SSL_LONG_OPTIONS,
          {0, 0, 0, 0},
      };
      char *short_options = long_options_to_short_options(long_options);
@@@ -220,17 -220,12 +220,17 @@@ static voi
  open_vconn__(const char *name, const char *default_suffix,
               struct vconn **vconnp)
  {
 -    struct dpif *dpif;
 +    char *datapath_name, *datapath_type, *socket_name;
 +    char *bridge_path;
      struct stat s;
 -    char *bridge_path, *datapath_name, *datapath_type;
  
      bridge_path = xasprintf("%s/%s.%s", ovs_rundir(), name, default_suffix);
 -    dp_parse_name(name, &datapath_name, &datapath_type);
 +
 +    ofproto_parse_name(name, &datapath_name, &datapath_type);
 +    socket_name = xasprintf("%s/%s.%s",
 +                            ovs_rundir(), datapath_name, default_suffix);
 +    free(datapath_name);
 +    free(datapath_type);
  
      if (strstr(name, ":")) {
          run(vconn_open_block(name, OFP_VERSION, vconnp),
          open_vconn_socket(name, vconnp);
      } else if (!stat(bridge_path, &s) && S_ISSOCK(s.st_mode)) {
          open_vconn_socket(bridge_path, vconnp);
 -    } else if (!dpif_open(datapath_name, datapath_type, &dpif)) {
 -        char dpif_name[IF_NAMESIZE + 1];
 -        char *socket_name;
 -
 -        run(dpif_port_get_name(dpif, ODPP_LOCAL, dpif_name, sizeof dpif_name),
 -            "obtaining name of %s", dpif_name);
 -        dpif_close(dpif);
 -        if (strcmp(dpif_name, name)) {
 -            VLOG_DBG("datapath %s is named %s", name, dpif_name);
 -        }
 -
 -        socket_name = xasprintf("%s/%s.%s",
 -                                ovs_rundir(), dpif_name, default_suffix);
 -        if (stat(socket_name, &s)) {
 -            ovs_fatal(errno, "cannot connect to %s: stat failed on %s",
 -                      name, socket_name);
 -        } else if (!S_ISSOCK(s.st_mode)) {
 +    } else if (!stat(socket_name, &s)) {
 +        if (!S_ISSOCK(s.st_mode)) {
              ovs_fatal(0, "cannot connect to %s: %s is not a socket",
                        name, socket_name);
          }
 -
          open_vconn_socket(socket_name, vconnp);
 -        free(socket_name);
      } else {
          ovs_fatal(0, "%s is not a valid connection method", name);
      }
  
 -    free(datapath_name);
 -    free(datapath_type);
      free(bridge_path);
 +    free(socket_name);
  }
  
  static void
@@@ -502,8 -515,6 +502,6 @@@ negotiate_highest_flow_format(struct vc
  
          if (try_set_flow_format(vconn, NXFF_NXM)) {
              flow_format = NXFF_NXM;
-         } else if (try_set_flow_format(vconn, NXFF_TUN_ID_FROM_COOKIE)) {
-             flow_format = NXFF_TUN_ID_FROM_COOKIE;
          } else {
              flow_format = NXFF_OPENFLOW10;
          }
@@@ -531,7 -542,7 +529,7 @@@ do_dump_flows__(int argc, char *argv[]
      parse_ofp_flow_stats_request_str(&fsr, aggregate, argc > 2 ? argv[2] : "");
  
      open_vconn(argv[1], &vconn);
-     min_flow_format = ofputil_min_flow_format(&fsr.match, false, 0);
+     min_flow_format = ofputil_min_flow_format(&fsr.match);
      flow_format = negotiate_highest_flow_format(vconn, min_flow_format);
      request = ofputil_encode_flow_stats_request(&fsr, flow_format);
      dump_stats_transaction(argv[1], request);
@@@ -615,7 -626,6 +613,7 @@@ static voi
  do_flow_mod_file__(int argc OVS_UNUSED, char *argv[], uint16_t command)
  {
      enum nx_flow_format flow_format;
 +    bool flow_mod_table_id;
      struct list requests;
      struct vconn *vconn;
      FILE *file;
  
      list_init(&requests);
      flow_format = set_initial_format_for_flow_mod(&requests);
 +    flow_mod_table_id = false;
  
      open_vconn(argv[1], &vconn);
 -    while (parse_ofp_flow_mod_file(&requests, &flow_format, file, command)) {
 +    while (parse_ofp_flow_mod_file(&requests, &flow_format, &flow_mod_table_id,
 +                                   file, command)) {
          check_final_format_for_flow_mod(flow_format);
          transact_multiple_noreply(vconn, &requests);
      }
@@@ -646,7 -654,6 +644,7 @@@ static voi
  do_flow_mod__(int argc, char *argv[], uint16_t command)
  {
      enum nx_flow_format flow_format;
 +    bool flow_mod_table_id;
      struct list requests;
      struct vconn *vconn;
  
  
      list_init(&requests);
      flow_format = set_initial_format_for_flow_mod(&requests);
 +    flow_mod_table_id = false;
  
 -    parse_ofp_flow_mod_str(&requests, &flow_format, argc > 2 ? argv[2] : "",
 -                           command);
 +    parse_ofp_flow_mod_str(&requests, &flow_format, &flow_mod_table_id,
 +                           argc > 2 ? argv[2] : "", command);
      check_final_format_for_flow_mod(flow_format);
  
      open_vconn(argv[1], &vconn);
@@@ -1005,7 -1011,7 +1003,7 @@@ fte_insert(struct classifier *cls, cons
      fte->rule = *rule;
      fte->versions[index] = version;
  
 -    old = fte_from_cls_rule(classifier_insert(cls, &fte->rule));
 +    old = fte_from_cls_rule(classifier_replace(cls, &fte->rule));
      if (old) {
          fte_version_free(old->versions[index]);
          fte->versions[!index] = old->versions[!index];
@@@ -1035,9 -1041,10 +1033,9 @@@ read_flows_from_file(const char *filena
          enum nx_flow_format min_ff;
          struct ofpbuf actions;
          struct flow_mod fm;
 -        uint8_t table_idx;
  
          ofpbuf_init(&actions, 64);
 -        parse_ofp_str(&fm, &table_idx, &actions, ds_cstr(&s));
 +        parse_ofp_str(&fm, &actions, ds_cstr(&s));
  
          version = xmalloc(sizeof *version);
          version->cookie = fm.cookie;
          version->n_actions = actions.size / sizeof *version->actions;
          version->actions = ofpbuf_steal_data(&actions);
  
-         min_ff = ofputil_min_flow_format(&fm.cr, true, fm.cookie);
+         min_ff = ofputil_min_flow_format(&fm.cr);
          min_flow_format = MAX(min_flow_format, min_ff);
          check_final_format_for_flow_mod(min_flow_format);
  
@@@ -1113,8 -1120,7 +1111,7 @@@ read_flows_from_switch(struct vconn *vc
                  struct ofputil_flow_stats fs;
                  int retval;
  
-                 retval = ofputil_decode_flow_stats_reply(&fs, reply,
-                                                          flow_format);
+                 retval = ofputil_decode_flow_stats_reply(&fs, reply);
                  if (retval) {
                      if (retval != EOF) {
                          ovs_fatal(0, "parse error in reply");
@@@ -1151,7 -1157,6 +1148,7 @@@ fte_make_flow_mod(const struct fte *fte
  
      fm.cr = fte->rule;
      fm.cookie = version->cookie;
 +    fm.table_id = 0xff;
      fm.command = command;
      fm.idle_timeout = version->idle_timeout;
      fm.hard_timeout = version->hard_timeout;
          fm.n_actions = 0;
      }
  
 -    ofm = ofputil_encode_flow_mod(&fm, flow_format);
 +    ofm = ofputil_encode_flow_mod(&fm, flow_format, false);
      list_push_back(packets, &ofm->list_node);
  }
  
@@@ -1300,18 -1305,15 +1297,18 @@@ static voi
  do_parse_flow(int argc OVS_UNUSED, char *argv[])
  {
      enum nx_flow_format flow_format;
 +    bool flow_mod_table_id;
      struct list packets;
  
      flow_format = NXFF_OPENFLOW10;
      if (preferred_flow_format > 0) {
          flow_format = preferred_flow_format;
      }
 +    flow_mod_table_id = false;
  
      list_init(&packets);
 -    parse_ofp_flow_mod_str(&packets, &flow_format, argv[1], OFPFC_ADD);
 +    parse_ofp_flow_mod_str(&packets, &flow_format, &flow_mod_table_id,
 +                           argv[1], OFPFC_ADD);
      print_packet_list(&packets);
  }
  
@@@ -1321,7 -1323,6 +1318,7 @@@ static voi
  do_parse_flows(int argc OVS_UNUSED, char *argv[])
  {
      enum nx_flow_format flow_format;
 +    bool flow_mod_table_id;
      struct list packets;
      FILE *file;
  
      if (preferred_flow_format > 0) {
          flow_format = preferred_flow_format;
      }
 +    flow_mod_table_id = false;
  
      list_init(&packets);
 -    while (parse_ofp_flow_mod_file(&packets, &flow_format, file, OFPFC_ADD)) {
 +    while (parse_ofp_flow_mod_file(&packets, &flow_format, &flow_mod_table_id,
 +                                   file, OFPFC_ADD)) {
          print_packet_list(&packets);
      }
      fclose(file);
@@@ -28,6 -28,7 +28,6 @@@
  #include "compiler.h"
  #include "daemon.h"
  #include "dirs.h"
 -#include "dpif.h"
  #include "dummy.h"
  #include "leak-checker.h"
  #include "list.h"
@@@ -92,6 -93,7 +92,6 @@@ main(int argc, char *argv[]
      struct ofproto *ofproto;
      struct ofsettings s;
      int error;
 -    struct dpif *dpif;
      struct netflow_options nf_options;
      const char *port;
      bool exiting;
      VLOG_INFO("Open vSwitch version %s", VERSION BUILDNR);
      VLOG_INFO("OpenFlow protocol version 0x%02x", OFP_VERSION);
  
 -    error = dpif_create_and_open(s.dp_name, s.dp_type, &dpif);
 +    error = ofproto_create(s.dp_name, s.dp_type, &ofproto);
      if (error) {
 -        VLOG_FATAL("could not create datapath (%s)", strerror(error));
 +        VLOG_FATAL("could not initialize OpenFlow switch (%s)",
 +                   strerror(error));
      }
  
      /* Add ports to the datapath if requested by the user. */
                         port, strerror(error));
          }
  
 -        error = dpif_port_add(dpif, netdev, NULL);
 +        error = ofproto_port_add(ofproto, netdev, NULL);
          if (error) {
              VLOG_FATAL("failed to add %s as a port (%s)",
                         port, strerror(error));
          netdev_close(netdev);
      }
  
 -    /* Start OpenFlow processing. */
 -    error = ofproto_create(s.dp_name, s.dp_type, NULL, NULL, &ofproto);
 -    if (error) {
 -        VLOG_FATAL("could not initialize openflow switch (%s)",
 -                   strerror(error));
 -    }
 +    /* Configure OpenFlow switch. */
      if (s.datapath_id) {
          ofproto_set_datapath_id(ofproto, s.datapath_id);
      }
              VLOG_FATAL("unrecoverable datapath error (%s)", strerror(error));
          }
          unixctl_server_run(unixctl);
 -        dp_run();
          netdev_run();
  
          ofproto_wait(ofproto);
          unixctl_server_wait(unixctl);
 -        dp_wait();
          netdev_wait();
          if (exiting) {
              poll_immediate_wake();
          poll_block();
      }
  
 -    dpif_close(dpif);
 +    ofproto_destroy(ofproto);
  
      return 0;
  }
@@@ -269,10 -277,8 +269,8 @@@ parse_options(int argc, char *argv[], s
          DAEMON_LONG_OPTIONS,
          VLOG_LONG_OPTIONS,
          LEAK_CHECKER_LONG_OPTIONS,
- #ifdef HAVE_OPENSSL
-         STREAM_SSL_LONG_OPTIONS
+         STREAM_SSL_LONG_OPTIONS,
          {"bootstrap-ca-cert", required_argument, 0, OPT_BOOTSTRAP_CA_CERT},
- #endif
          {0, 0, 0, 0},
      };
      char *short_options = long_options_to_short_options(long_options);
  
          LEAK_CHECKER_OPTION_HANDLERS
  
- #ifdef HAVE_OPENSSL
          STREAM_SSL_OPTION_HANDLERS
  
          case OPT_BOOTSTRAP_CA_CERT:
              stream_ssl_set_ca_cert_file(optarg, true);
              break;
- #endif
  
          case '?':
              exit(EXIT_FAILURE);
      }
  
      /* Local vconns. */
 -    dp_parse_name(argv[0], &s->dp_name, &s->dp_type);
 +    ofproto_parse_name(argv[0], &s->dp_name, &s->dp_type);
  
      /* Figure out controller names. */
      s->run_forever = false;
diff --combined vswitchd/bridge.c
  
  #include <config.h>
  #include "bridge.h"
 -#include "byte-order.h"
  #include <assert.h>
  #include <errno.h>
 -#include <arpa/inet.h>
 -#include <ctype.h>
  #include <inttypes.h>
 -#include <sys/socket.h>
 -#include <net/if.h>
 -#include <openflow/openflow.h>
 -#include <signal.h>
  #include <stdlib.h>
 -#include <strings.h>
 -#include <sys/stat.h>
 -#include <sys/socket.h>
 -#include <sys/types.h>
 -#include <unistd.h>
  #include "bitmap.h"
  #include "bond.h"
  #include "cfm.h"
 -#include "classifier.h"
  #include "coverage.h"
  #include "daemon.h"
  #include "dirs.h"
 -#include "dpif.h"
  #include "dynamic-string.h"
 -#include "flow.h"
  #include "hash.h"
  #include "hmap.h"
  #include "jsonrpc.h"
  #include "lacp.h"
  #include "list.h"
 -#include "mac-learning.h"
  #include "netdev.h"
 -#include "netlink.h"
 -#include "odp-util.h"
  #include "ofp-print.h"
  #include "ofpbuf.h"
 -#include "ofproto/netflow.h"
  #include "ofproto/ofproto.h"
 -#include "ovsdb-data.h"
 -#include "packets.h"
  #include "poll-loop.h"
 -#include "process.h"
  #include "sha1.h"
  #include "shash.h"
  #include "socket-util.h"
  #include "stream-ssl.h"
  #include "sset.h"
 -#include "svec.h"
  #include "system-stats.h"
  #include "timeval.h"
  #include "util.h"
  #include "unixctl.h"
 -#include "vconn.h"
  #include "vswitchd/vswitch-idl.h"
  #include "xenserver.h"
  #include "vlog.h"
  #include "sflow_api.h"
 +#include "vlan-bitmap.h"
  
  VLOG_DEFINE_THIS_MODULE(bridge);
  
 -COVERAGE_DEFINE(bridge_flush);
 -COVERAGE_DEFINE(bridge_process_flow);
  COVERAGE_DEFINE(bridge_reconfigure);
  
 -struct dst {
 -    struct iface *iface;
 -    uint16_t vlan;
 -};
 -
 -struct dst_set {
 -    struct dst builtin[32];
 -    struct dst *dsts;
 -    size_t n, allocated;
 -};
 -
 -static void dst_set_init(struct dst_set *);
 -static void dst_set_add(struct dst_set *, const struct dst *);
 -static void dst_set_free(struct dst_set *);
 -
  struct iface {
      /* These members are always valid. */
      struct list port_elem;      /* Element in struct port's "ifaces" list. */
 +    struct hmap_node name_node; /* In struct bridge's "iface_by_name" hmap. */
      struct port *port;          /* Containing port. */
      char *name;                 /* Host network device name. */
      tag_type tag;               /* Tag associated with this interface. */
  
      /* 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 hmap_node ofp_port_node; /* In struct bridge's "ifaces" hmap. */
 +    int ofp_port;               /* OpenFlow port number, -1 if unknown. */
      struct netdev *netdev;      /* Network device. */
      const char *type;           /* Usually same as cfg->type. */
      const struct ovsrec_interface *cfg;
  };
  
 -#define MAX_MIRRORS 32
 -typedef uint32_t mirror_mask_t;
 -#define MIRROR_MASK_C(X) UINT32_C(X)
 -BUILD_ASSERT_DECL(sizeof(mirror_mask_t) * CHAR_BIT >= MAX_MIRRORS);
  struct mirror {
 +    struct uuid uuid;           /* UUID of this "mirror" record in database. */
 +    struct hmap_node hmap_node; /* In struct bridge's "mirrors" hmap. */
      struct bridge *bridge;
 -    size_t idx;
      char *name;
 -    struct uuid uuid;           /* UUID of this "mirror" record in database. */
 -
 -    /* Selection criteria. */
 -    struct sset src_ports;      /* Source port names. */
 -    struct sset dst_ports;      /* Destination port names. */
 -    int *vlans;
 -    size_t n_vlans;
 -
 -    /* Output. */
 -    struct port *out_port;
 -    int out_vlan;
  };
  
 -#define FLOOD_PORT ((struct port *) 1) /* The 'flood' output port. */
  struct port {
      struct bridge *bridge;
      struct hmap_node hmap_node; /* Element in struct bridge's "ports" hmap. */
      char *name;
  
 -    int vlan;                   /* -1=trunk port, else a 12-bit VLAN ID. */
 -    unsigned long *trunks;      /* Bitmap of trunked VLANs, if 'vlan' == -1.
 -                                 * NULL if all VLANs are trunked. */
      const struct ovsrec_port *cfg;
  
      /* An ordinary bridge port has 1 interface.
       * A bridge port for bonding has at least 2 interfaces. */
      struct list ifaces;         /* List of "struct iface"s. */
 -
 -    struct lacp *lacp;          /* NULL if LACP is not enabled. */
 -
 -    /* Bonding info. */
 -    struct bond *bond;
 -
 -    /* Port mirroring info. */
 -    mirror_mask_t src_mirrors;  /* Mirrors triggered when packet received. */
 -    mirror_mask_t dst_mirrors;  /* Mirrors triggered when packet sent. */
 -    bool is_mirror_output_port; /* Does port mirroring send frames here? */
  };
  
  struct bridge {
 -    struct list node;           /* Node in global list of bridges. */
 +    struct hmap_node node;      /* In 'all_bridges'. */
      char *name;                 /* User-specified arbitrary name. */
 -    struct mac_learning *ml;    /* MAC learning table. */
 +    char *type;                 /* Datapath type. */
      uint8_t ea[ETH_ADDR_LEN];   /* Bridge Ethernet Address. */
      uint8_t default_ea[ETH_ADDR_LEN]; /* Default MAC. */
      const struct ovsrec_bridge *cfg;
      /* OpenFlow switch processing. */
      struct ofproto *ofproto;    /* OpenFlow switch. */
  
 -    /* Kernel datapath information. */
 -    struct dpif *dpif;          /* Datapath. */
 -    struct hmap ifaces;         /* "struct iface"s indexed by dp_ifidx. */
 -
      /* Bridge ports. */
      struct hmap ports;          /* "struct port"s indexed by name. */
 -    struct shash iface_by_name; /* "struct iface"s indexed by name. */
 -
 -    /* Bonding. */
 -    bool has_bonded_ports;
 -
 -    /* Flow tracking. */
 -    bool flush;
 +    struct hmap ifaces;         /* "struct iface"s indexed by ofp_port. */
 +    struct hmap iface_by_name;  /* "struct iface"s indexed by name. */
  
      /* Port mirroring. */
 -    struct mirror *mirrors[MAX_MIRRORS];
 +    struct hmap mirrors;        /* "struct mirror" indexed by UUID. */
  
      /* Synthetic local port if necessary. */
      struct ovsrec_port synth_local_port;
      struct ovsrec_interface *synth_local_ifacep;
  };
  
 -/* List of all bridges. */
 -static struct list all_bridges = LIST_INITIALIZER(&all_bridges);
 +/* All bridges, indexed by name. */
 +static struct hmap all_bridges = HMAP_INITIALIZER(&all_bridges);
  
  /* OVSDB IDL used to obtain configuration. */
  static struct ovsdb_idl *idl;
@@@ -133,26 -209,21 +133,26 @@@ static long long int stats_timer = LLON
  #define DB_LIMIT_INTERVAL (1 * 1000) /* In milliseconds. */
  static long long int db_limiter = LLONG_MIN;
  
 -static struct bridge *bridge_create(const struct ovsrec_bridge *br_cfg);
 +static void add_del_bridges(const struct ovsrec_open_vswitch *);
 +static void bridge_del_ofprotos(void);
 +static bool bridge_add_ofprotos(struct bridge *);
 +static void bridge_create(const struct ovsrec_bridge *);
  static void bridge_destroy(struct bridge *);
  static struct bridge *bridge_lookup(const char *name);
  static unixctl_cb_func bridge_unixctl_dump_flows;
  static unixctl_cb_func bridge_unixctl_reconnect;
 -static int bridge_run_one(struct bridge *);
  static size_t bridge_get_controllers(const struct bridge *br,
                                       struct ovsrec_controller ***controllersp);
 -static void bridge_reconfigure_one(struct bridge *);
 -static void bridge_reconfigure_remotes(struct bridge *,
 -                                       const struct sockaddr_in *managers,
 -                                       size_t n_managers);
 -static void bridge_get_all_ifaces(const struct bridge *, struct shash *ifaces);
 -static void bridge_fetch_dp_ifaces(struct bridge *);
 -static void bridge_flush(struct bridge *);
 +static void bridge_add_del_ports(struct bridge *);
 +static void bridge_add_ofproto_ports(struct bridge *);
 +static void bridge_del_ofproto_ports(struct bridge *);
 +static void bridge_refresh_ofp_port(struct bridge *);
 +static void bridge_configure_datapath_id(struct bridge *);
 +static void bridge_configure_netflow(struct bridge *);
 +static void bridge_configure_sflow(struct bridge *, int *sflow_bridge_number);
 +static void bridge_configure_remotes(struct bridge *,
 +                                     const struct sockaddr_in *managers,
 +                                     size_t n_managers);
  static void bridge_pick_local_hw_addr(struct bridge *,
                                        uint8_t ea[ETH_ADDR_LEN],
                                        struct iface **hw_addr_iface);
@@@ -160,41 -231,42 +160,41 @@@ static uint64_t bridge_pick_datapath_id
                                          const uint8_t bridge_ea[ETH_ADDR_LEN],
                                          struct iface *hw_addr_iface);
  static uint64_t dpid_from_hash(const void *, size_t nbytes);
 +static bool bridge_has_bond_fake_iface(const struct bridge *,
 +                                       const char *name);
 +static bool port_is_bond_fake_iface(const struct port *);
  
 -static unixctl_cb_func bridge_unixctl_fdb_show;
  static unixctl_cb_func cfm_unixctl_show;
  static unixctl_cb_func qos_unixctl_show;
  
 -static void port_run(struct port *);
 -static void port_wait(struct port *);
 -static struct port *port_create(struct bridge *, const char *name);
 -static void port_reconfigure(struct port *, const struct ovsrec_port *);
 -static void port_del_ifaces(struct port *, const struct ovsrec_port *);
 +static struct port *port_create(struct bridge *, const struct ovsrec_port *);
 +static void port_add_ifaces(struct port *);
 +static void port_del_ifaces(struct port *);
  static void port_destroy(struct port *);
  static struct port *port_lookup(const struct bridge *, const char *name);
 -static struct iface *port_get_an_iface(const struct port *);
 -static struct port *port_from_dp_ifidx(const struct bridge *,
 -                                       uint16_t dp_ifidx);
 -static void port_reconfigure_lacp(struct port *);
 -static void port_reconfigure_bond(struct port *);
 -static void port_send_learning_packets(struct port *);
 -
 -static void mirror_create(struct bridge *, struct ovsrec_mirror *);
 +static void port_configure(struct port *);
 +static struct lacp_settings *port_configure_lacp(struct port *,
 +                                                 struct lacp_settings *);
 +static void port_configure_bond(struct port *, struct bond_settings *);
 +
 +static void bridge_configure_mirrors(struct bridge *);
 +static struct mirror *mirror_create(struct bridge *,
 +                                    const struct ovsrec_mirror *);
  static void mirror_destroy(struct mirror *);
 -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 bool mirror_configure(struct mirror *, const struct ovsrec_mirror *);
  
 +static void iface_configure_lacp(struct iface *, struct lacp_slave_settings *);
  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_find(const char *name);
 -static struct iface *iface_from_dp_ifidx(const struct bridge *,
 -                                         uint16_t dp_ifidx);
 +static struct iface *iface_from_ofp_port(const struct bridge *,
 +                                         uint16_t ofp_port);
  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_configure_qos(struct iface *, const struct ovsrec_qos *);
 +static void iface_configure_cfm(struct iface *);
  static bool iface_refresh_cfm_stats(struct iface *iface);
  static bool iface_get_carrier(const struct iface *);
  static bool iface_is_synthetic(const struct iface *);
@@@ -203,6 -275,9 +203,6 @@@ static void shash_from_ovs_idl_map(cha
                                     struct shash *);
  static void shash_to_ovs_idl_map(struct shash *,
                                   char ***keys, char ***values, size_t *n);
 -
 -/* Hooks into ofproto processing. */
 -static struct ofhooks bridge_ofhooks;
  \f
  /* Public functions. */
  
@@@ -267,6 -342,7 +267,6 @@@ bridge_init(const char *remote
      ovsdb_idl_omit(idl, &ovsrec_ssl_col_external_ids);
  
      /* Register unixctl commands. */
 -    unixctl_command_register("fdb/show", bridge_unixctl_fdb_show, NULL);
      unixctl_command_register("cfm/show", cfm_unixctl_show, NULL);
      unixctl_command_register("qos/show", qos_unixctl_show, NULL);
      unixctl_command_register("bridge/dump-flows", bridge_unixctl_dump_flows,
@@@ -282,12 -358,131 +282,12 @@@ bridge_exit(void
  {
      struct bridge *br, *next_br;
  
 -    LIST_FOR_EACH_SAFE (br, next_br, node, &all_bridges) {
 +    HMAP_FOR_EACH_SAFE (br, next_br, node, &all_bridges) {
          bridge_destroy(br);
      }
      ovsdb_idl_destroy(idl);
  }
  
 -/* Performs configuration that is only necessary once at ovs-vswitchd startup,
 - * but for which the ovs-vswitchd configuration 'cfg' is required. */
 -static void
 -bridge_configure_once(const struct ovsrec_open_vswitch *cfg)
 -{
 -    static bool already_configured_once;
 -    struct sset bridge_names;
 -    struct sset dpif_names, dpif_types;
 -    const char *type;
 -    size_t i;
 -
 -    /* Only do this once per ovs-vswitchd run. */
 -    if (already_configured_once) {
 -        return;
 -    }
 -    already_configured_once = true;
 -
 -    stats_timer = time_msec() + STATS_INTERVAL;
 -
 -    /* Get all the configured bridges' names from 'cfg' into 'bridge_names'. */
 -    sset_init(&bridge_names);
 -    for (i = 0; i < cfg->n_bridges; i++) {
 -        sset_add(&bridge_names, cfg->bridges[i]->name);
 -    }
 -
 -    /* Iterate over all system dpifs and delete any of them that do not appear
 -     * in 'cfg'. */
 -    sset_init(&dpif_names);
 -    sset_init(&dpif_types);
 -    dp_enumerate_types(&dpif_types);
 -    SSET_FOR_EACH (type, &dpif_types) {
 -        const char *name;
 -
 -        dp_enumerate_names(type, &dpif_names);
 -
 -        /* Delete each dpif whose name is not in 'bridge_names'. */
 -        SSET_FOR_EACH (name, &dpif_names) {
 -            if (!sset_contains(&bridge_names, name)) {
 -                struct dpif *dpif;
 -                int retval;
 -
 -                retval = dpif_open(name, type, &dpif);
 -                if (!retval) {
 -                    dpif_delete(dpif);
 -                    dpif_close(dpif);
 -                }
 -            }
 -        }
 -    }
 -    sset_destroy(&bridge_names);
 -    sset_destroy(&dpif_names);
 -    sset_destroy(&dpif_types);
 -}
 -
 -/* Callback for iterate_and_prune_ifaces(). */
 -static bool
 -check_iface(struct bridge *br, struct iface *iface, void *aux OVS_UNUSED)
 -{
 -    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)
 -{
 -    /* Set policing attributes. */
 -    netdev_set_policing(iface->netdev,
 -                        iface->cfg->ingress_policing_rate,
 -                        iface->cfg->ingress_policing_burst);
 -
 -    /* Set MAC address of internal interfaces other than the local
 -     * interface. */
 -    iface_set_mac(iface);
 -
 -    return true;
 -}
 -
 -/* Calls 'cb' for each interfaces in 'br', passing along the 'aux' argument.
 - * Deletes from 'br' all the interfaces for which 'cb' returns false, and then
 - * deletes from 'br' any ports that no longer have any interfaces. */
 -static void
 -iterate_and_prune_ifaces(struct bridge *br,
 -                         bool (*cb)(struct bridge *, struct iface *,
 -                                    void *aux),
 -                         void *aux)
 -{
 -    struct port *port, *next_port;
 -
 -    HMAP_FOR_EACH_SAFE (port, next_port, hmap_node, &br->ports) {
 -        struct iface *iface, *next_iface;
 -
 -        LIST_FOR_EACH_SAFE (iface, next_iface, port_elem, &port->ifaces) {
 -            if (!cb(br, iface, aux)) {
 -                iface_set_ofport(iface->cfg, -1);
 -                iface_destroy(iface);
 -            }
 -        }
 -
 -        if (list_is_empty(&port->ifaces)) {
 -            VLOG_WARN("%s port has no interfaces, dropping", port->name);
 -            port_destroy(port);
 -        }
 -    }
 -}
 -
  /* Looks at the list of managers in 'ovs_cfg' and extracts their remote IP
   * addresses and ports into '*managersp' and '*n_managersp'.  The caller is
   * responsible for freeing '*managersp' (with free()).
@@@ -347,597 -542,403 +347,597 @@@ collect_in_band_managers(const struct o
  static void
  bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg)
  {
 -    struct shash old_br, new_br;
 -    struct shash_node *node;
 -    struct bridge *br, *next;
      struct sockaddr_in *managers;
 -    size_t n_managers;
 -    size_t i;
 +    struct bridge *br, *next;
      int sflow_bridge_number;
 +    size_t n_managers;
  
      COVERAGE_INC(bridge_reconfigure);
  
 -    collect_in_band_managers(ovs_cfg, &managers, &n_managers);
 -
 -    /* Collect old and new bridges. */
 -    shash_init(&old_br);
 -    shash_init(&new_br);
 -    LIST_FOR_EACH (br, node, &all_bridges) {
 -        shash_add(&old_br, br->name, br);
 +    /* Create and destroy "struct bridge"s, "struct port"s, and "struct
 +     * iface"s according to 'ovs_cfg', with only very minimal configuration
 +     * otherwise.
 +     *
 +     * This is purely an update to bridge data structures.  Nothing is pushed
 +     * down to ofproto or lower layers. */
 +    add_del_bridges(ovs_cfg);
 +    HMAP_FOR_EACH (br, node, &all_bridges) {
 +        bridge_add_del_ports(br);
      }
 -    for (i = 0; i < ovs_cfg->n_bridges; i++) {
 -        const struct ovsrec_bridge *br_cfg = ovs_cfg->bridges[i];
 -        if (!shash_add_once(&new_br, br_cfg->name, br_cfg)) {
 -            VLOG_WARN("more than one bridge named %s", br_cfg->name);
 +
 +    /* Delete all datapaths and datapath ports that are no longer configured.
 +     *
 +     * 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.  A datapath always has a
 +     * "local port" so we must delete not-configured datapaths too. */
 +    bridge_del_ofprotos();
 +    HMAP_FOR_EACH (br, node, &all_bridges) {
 +        if (br->ofproto) {
 +            bridge_del_ofproto_ports(br);
          }
      }
  
 -    /* Get rid of deleted bridges and add new 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;
 -        } else {
 +    /* Create datapaths and datapath ports that are missing.
 +     *
 +     * After this is done, we have our final set of bridges, ports, and
 +     * interfaces.  Every "struct bridge" has an ofproto, every "struct port"
 +     * has at least one iface, every "struct iface" has a valid ofp_port and
 +     * netdev. */
 +    HMAP_FOR_EACH_SAFE (br, next, node, &all_bridges) {
 +        if (!br->ofproto && !bridge_add_ofprotos(br)) {
              bridge_destroy(br);
          }
      }
 -    SHASH_FOR_EACH (node, &new_br) {
 -        const char *br_name = node->name;
 -        const struct ovsrec_bridge *br_cfg = node->data;
 -        br = shash_find_data(&old_br, br_name);
 -        if (br) {
 -            /* If the bridge datapath type has changed, we need to tear it
 -             * down and recreate. */
 -            if (strcmp(br->cfg->datapath_type, br_cfg->datapath_type)) {
 -                bridge_destroy(br);
 -                bridge_create(br_cfg);
 -            }
 -        } else {
 -            bridge_create(br_cfg);
 -        }
 +    HMAP_FOR_EACH (br, node, &all_bridges) {
 +        bridge_refresh_ofp_port(br);
 +        bridge_add_ofproto_ports(br);
      }
 -    shash_destroy(&old_br);
 -    shash_destroy(&new_br);
  
 -    /* Reconfigure all bridges. */
 -    LIST_FOR_EACH (br, node, &all_bridges) {
 -        bridge_reconfigure_one(br);
 -    }
 +    /* Complete the configuration. */
 +    sflow_bridge_number = 0;
 +    collect_in_band_managers(ovs_cfg, &managers, &n_managers);
 +    HMAP_FOR_EACH (br, node, &all_bridges) {
 +        struct port *port;
  
 -    /* Add and delete ports on all datapaths.
 -     *
 -     * 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, node, &all_bridges) {
 -        struct dpif_port_dump dump;
 -        struct shash want_ifaces;
 -        struct dpif_port dpif_port;
 -
 -        bridge_get_all_ifaces(br, &want_ifaces);
 -        DPIF_PORT_FOR_EACH (&dpif_port, &dump, br->dpif) {
 -            if (!shash_find(&want_ifaces, dpif_port.name)
 -                && strcmp(dpif_port.name, br->name)) {
 -                int retval = dpif_port_del(br->dpif, dpif_port.port_no);
 -                if (retval) {
 -                    VLOG_WARN("failed to remove %s interface from %s: %s",
 -                              dpif_port.name, dpif_name(br->dpif),
 -                              strerror(retval));
 -                }
 +        HMAP_FOR_EACH (port, hmap_node, &br->ports) {
 +            struct iface *iface;
 +
 +            port_configure(port);
 +
 +            HMAP_FOR_EACH (iface, ofp_port_node, &br->ifaces) {
 +                iface_configure_cfm(iface);
 +                iface_configure_qos(iface, port->cfg->qos);
 +                iface_set_mac(iface);
              }
          }
 -        shash_destroy(&want_ifaces);
 +        bridge_configure_mirrors(br);
 +        bridge_configure_datapath_id(br);
 +        bridge_configure_remotes(br, managers, n_managers);
 +        bridge_configure_netflow(br);
 +        bridge_configure_sflow(br, &sflow_bridge_number);
      }
 -    LIST_FOR_EACH (br, node, &all_bridges) {
 -        struct shash cur_ifaces, want_ifaces;
 -        struct dpif_port_dump dump;
 -        struct dpif_port dpif_port;
 +    free(managers);
  
 -        /* Get the set of interfaces currently in this datapath. */
 -        shash_init(&cur_ifaces);
 -        DPIF_PORT_FOR_EACH (&dpif_port, &dump, br->dpif) {
 -            struct dpif_port *port_info = xmalloc(sizeof *port_info);
 -            dpif_port_clone(port_info, &dpif_port);
 -            shash_add(&cur_ifaces, dpif_port.name, port_info);
 -        }
 +    /* ovs-vswitchd has completed initialization, so allow the process that
 +     * forked us to exit successfully. */
 +    daemonize_complete();
 +}
  
 -        /* Get the set of interfaces we want on this datapath. */
 -        bridge_get_all_ifaces(br, &want_ifaces);
 +/* Iterate over all ofprotos and delete any of them that do not have a
 + * configured bridge or that are the wrong type. */
 +static void
 +bridge_del_ofprotos(void)
 +{
 +    struct sset names;
 +    struct sset types;
 +    const char *type;
  
 -        hmap_clear(&br->ifaces);
 -        SHASH_FOR_EACH (node, &want_ifaces) {
 -            const char *if_name = node->name;
 -            struct iface *iface = node->data;
 -            struct dpif_port *dpif_port;
 -            const char *type;
 -            int error;
 +    sset_init(&names);
 +    sset_init(&types);
 +    ofproto_enumerate_types(&types);
 +    SSET_FOR_EACH (type, &types) {
 +        const char *name;
  
 -            type = iface ? iface->type : "internal";
 -            dpif_port = shash_find_data(&cur_ifaces, if_name);
 -
 -            /* 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 ((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_no);
 -                    if (error) {
 -                        continue;
 -                    }
 -                    dpif_port = NULL;
 -                }
 -                if (iface) {
 -                    if (iface->port->bond) {
 -                        /* The bond has a pointer to the netdev, so remove it
 -                         * from the bond before closing the netdev.  The slave
 -                         * will get added back to the bond later, after a new
 -                         * netdev is available. */
 -                        bond_slave_unregister(iface->port->bond, iface);
 -                    }
 -                    netdev_close(iface->netdev);
 -                    iface->netdev = NULL;
 -                }
 +        ofproto_enumerate_names(type, &names);
 +        SSET_FOR_EACH (name, &names) {
 +            struct bridge *br = bridge_lookup(name);
 +            if (!br || strcmp(type, br->type)) {
 +                ofproto_delete(name, type);
              }
 +        }
 +    }
 +    sset_destroy(&names);
 +    sset_destroy(&types);
 +}
  
 -            /* 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;
 +static bool
 +bridge_add_ofprotos(struct bridge *br)
 +{
 +    int error = ofproto_create(br->name, br->type, &br->ofproto);
 +    if (error) {
 +        VLOG_ERR("failed to create bridge %s: %s", br->name, strerror(error));
 +        return false;
 +    }
 +    return true;
 +}
  
 -                shash_init(&args);
 -                if (iface) {
 -                    shash_from_ovs_idl_map(iface->cfg->key_options,
 -                                           iface->cfg->value_options,
 -                                           iface->cfg->n_options, &args);
 -                }
 -                error = netdev_open(&options, &netdev);
 -                shash_destroy(&args);
 +static void
 +port_configure(struct port *port)
 +{
 +    const struct ovsrec_port *cfg = port->cfg;
 +    struct bond_settings bond_settings;
 +    struct lacp_settings lacp_settings;
 +    struct ofproto_bundle_settings s;
 +    struct iface *iface;
  
 -                if (error) {
 -                    VLOG_WARN("could not open network device %s (%s)",
 -                              if_name, strerror(error));
 -                    continue;
 -                }
 +    /* Get name. */
 +    s.name = port->name;
  
 -                /* Then add the port if we haven't already. */
 -                if (!dpif_port) {
 -                    error = dpif_port_add(br->dpif, netdev, NULL);
 -                    if (error) {
 -                        netdev_close(netdev);
 -                        if (error == EFBIG) {
 -                            VLOG_ERR("ran out of valid port numbers on %s",
 -                                     dpif_name(br->dpif));
 -                            break;
 -                        } else {
 -                            VLOG_WARN("failed to add %s interface to %s: %s",
 -                                      if_name, dpif_name(br->dpif),
 -                                      strerror(error));
 -                            continue;
 -                        }
 -                    }
 -                }
 +    /* Get slaves. */
 +    s.n_slaves = 0;
 +    s.slaves = xmalloc(list_size(&port->ifaces) * sizeof *s.slaves);
 +    LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
 +        s.slaves[s.n_slaves++] = iface->ofp_port;
 +    }
  
 -                /* Update 'iface'. */
 -                if (iface) {
 -                    iface->netdev = netdev;
 -                }
 -            } else if (iface && iface->netdev) {
 -                struct shash args;
 -
 -                shash_init(&args);
 -                shash_from_ovs_idl_map(iface->cfg->key_options,
 -                                       iface->cfg->value_options,
 -                                       iface->cfg->n_options, &args);
 -                netdev_set_config(iface->netdev, &args);
 -                shash_destroy(&args);
 +    /* Get VLAN tag. */
 +    s.vlan = -1;
 +    if (cfg->tag) {
 +        if (list_is_short(&port->ifaces)) {
 +            if (*cfg->tag >= 0 && *cfg->tag <= 4095) {
 +                s.vlan = *cfg->tag;
 +                VLOG_DBG("port %s: assigning VLAN tag %d", port->name, s.vlan);
              }
 +        } else {
 +            /* It's possible that bonded, VLAN-tagged ports make sense.  Maybe
 +             * they even work as-is.  But they have not been tested. */
 +            VLOG_WARN("port %s: VLAN tags not supported on bonded ports",
 +                      port->name);
          }
 -        shash_destroy(&want_ifaces);
 +    }
  
 -        SHASH_FOR_EACH (node, &cur_ifaces) {
 -            struct dpif_port *port_info = node->data;
 -            dpif_port_destroy(port_info);
 -            free(port_info);
 -        }
 -        shash_destroy(&cur_ifaces);
 +    /* Get VLAN trunks. */
 +    s.trunks = NULL;
 +    if (s.vlan < 0 && cfg->n_trunks) {
 +        s.trunks = vlan_bitmap_from_array(cfg->trunks, cfg->n_trunks);
 +    } else if (s.vlan >= 0 && cfg->n_trunks) {
 +        VLOG_ERR("port %s: ignoring trunks in favor of implicit vlan",
 +                 port->name);
      }
 -    sflow_bridge_number = 0;
 -    LIST_FOR_EACH (br, node, &all_bridges) {
 -        uint8_t ea[ETH_ADDR_LEN];
 -        uint64_t dpid;
 -        struct iface *local_iface;
 -        struct iface *hw_addr_iface;
 -        char *dpid_string;
  
 -        bridge_fetch_dp_ifaces(br);
 +    /* Get LACP settings. */
 +    s.lacp = port_configure_lacp(port, &lacp_settings);
 +    if (s.lacp) {
 +        size_t i = 0;
  
 -        /* Delete interfaces that cannot be opened.
 -         *
 -         * From this point forward we are guaranteed that every "struct iface"
 -         * has nonnull 'netdev' and correct 'dp_ifidx'. */
 -        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);
 -        local_iface = iface_from_dp_ifidx(br, ODPP_LOCAL);
 -        if (local_iface) {
 -            int error = netdev_set_etheraddr(local_iface->netdev, ea);
 -            if (error) {
 -                static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
 -                VLOG_ERR_RL(&rl, "bridge %s: failed to set bridge "
 -                            "Ethernet address: %s",
 -                            br->name, strerror(error));
 -            }
 +        s.lacp_slaves = xmalloc(s.n_slaves * sizeof *s.lacp_slaves);
 +        LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
 +            iface_configure_lacp(iface, &s.lacp_slaves[i++]);
          }
 -        memcpy(br->ea, ea, ETH_ADDR_LEN);
 -
 -        dpid = bridge_pick_datapath_id(br, ea, hw_addr_iface);
 -        ofproto_set_datapath_id(br->ofproto, dpid);
 +    } else {
 +        s.lacp_slaves = NULL;
 +    }
  
 -        dpid_string = xasprintf("%016"PRIx64, dpid);
 -        ovsrec_bridge_set_datapath_id(br->cfg, dpid_string);
 -        free(dpid_string);
 +    /* Get bond settings. */
 +    if (s.n_slaves > 1) {
 +        port_configure_bond(port, &bond_settings);
 +        s.bond = &bond_settings;
 +    } else {
 +        s.bond = NULL;
 +    }
  
 -        /* Set NetFlow configuration on this bridge. */
 -        if (br->cfg->netflow) {
 -            struct ovsrec_netflow *nf_cfg = br->cfg->netflow;
 -            struct netflow_options opts;
 +    /* Register. */
 +    ofproto_bundle_register(port->bridge->ofproto, port, &s);
  
 -            memset(&opts, 0, sizeof opts);
 +    /* Clean up. */
 +    free(s.trunks);
 +    free(s.lacp_slaves);
 +}
  
 -            dpif_get_netflow_ids(br->dpif, &opts.engine_type, &opts.engine_id);
 -            if (nf_cfg->engine_type) {
 -                opts.engine_type = *nf_cfg->engine_type;
 -            }
 -            if (nf_cfg->engine_id) {
 -                opts.engine_id = *nf_cfg->engine_id;
 -            }
 +/* Pick local port hardware address and datapath ID for 'br'. */
 +static void
 +bridge_configure_datapath_id(struct bridge *br)
 +{
 +    uint8_t ea[ETH_ADDR_LEN];
 +    uint64_t dpid;
 +    struct iface *local_iface;
 +    struct iface *hw_addr_iface;
 +    char *dpid_string;
  
 -            opts.active_timeout = nf_cfg->active_timeout;
 -            if (!opts.active_timeout) {
 -                opts.active_timeout = -1;
 -            } else if (opts.active_timeout < 0) {
 -                VLOG_WARN("bridge %s: active timeout interval set to negative "
 -                          "value, using default instead (%d seconds)", br->name,
 -                          NF_ACTIVE_TIMEOUT_DEFAULT);
 -                opts.active_timeout = -1;
 -            }
 +    bridge_pick_local_hw_addr(br, ea, &hw_addr_iface);
 +    local_iface = iface_from_ofp_port(br, OFPP_LOCAL);
 +    if (local_iface) {
 +        int error = netdev_set_etheraddr(local_iface->netdev, ea);
 +        if (error) {
 +            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
 +            VLOG_ERR_RL(&rl, "bridge %s: failed to set bridge "
 +                        "Ethernet address: %s",
 +                        br->name, strerror(error));
 +        }
 +    }
 +    memcpy(br->ea, ea, ETH_ADDR_LEN);
  
 -            opts.add_id_to_iface = nf_cfg->add_id_to_interface;
 -            if (opts.add_id_to_iface) {
 -                if (opts.engine_id > 0x7f) {
 -                    VLOG_WARN("bridge %s: netflow port mangling may conflict "
 -                              "with another vswitch, choose an engine id less "
 -                              "than 128", br->name);
 -                }
 -                if (hmap_count(&br->ports) > 508) {
 -                    VLOG_WARN("bridge %s: netflow port mangling will conflict "
 -                              "with another port when more than 508 ports are "
 -                              "used", br->name);
 -                }
 -            }
 +    dpid = bridge_pick_datapath_id(br, ea, hw_addr_iface);
 +    ofproto_set_datapath_id(br->ofproto, dpid);
  
 -            sset_init(&opts.collectors);
 -            sset_add_array(&opts.collectors,
 -                           nf_cfg->targets, nf_cfg->n_targets);
 -            if (ofproto_set_netflow(br->ofproto, &opts)) {
 -                VLOG_ERR("bridge %s: problem setting netflow collectors",
 -                         br->name);
 -            }
 -            sset_destroy(&opts.collectors);
 -        } else {
 -            ofproto_set_netflow(br->ofproto, NULL);
 -        }
 +    dpid_string = xasprintf("%016"PRIx64, dpid);
 +    ovsrec_bridge_set_datapath_id(br->cfg, dpid_string);
 +    free(dpid_string);
 +}
  
 -        /* Set sFlow configuration on this bridge. */
 -        if (br->cfg->sflow) {
 -            const struct ovsrec_sflow *sflow_cfg = br->cfg->sflow;
 -            struct ovsrec_controller **controllers;
 -            struct ofproto_sflow_options oso;
 -            size_t n_controllers;
 +/* Set NetFlow configuration on 'br'. */
 +static void
 +bridge_configure_netflow(struct bridge *br)
 +{
 +    struct ovsrec_netflow *cfg = br->cfg->netflow;
 +    struct netflow_options opts;
  
 -            memset(&oso, 0, sizeof oso);
 +    if (!cfg) {
 +        ofproto_set_netflow(br->ofproto, NULL);
 +        return;
 +    }
  
 -            sset_init(&oso.targets);
 -            sset_add_array(&oso.targets,
 -                           sflow_cfg->targets, sflow_cfg->n_targets);
 +    memset(&opts, 0, sizeof opts);
  
 -            oso.sampling_rate = SFL_DEFAULT_SAMPLING_RATE;
 -            if (sflow_cfg->sampling) {
 -                oso.sampling_rate = *sflow_cfg->sampling;
 -            }
 +    /* Get default NetFlow configuration from datapath.
 +     * Apply overrides from 'cfg'. */
 +    ofproto_get_netflow_ids(br->ofproto, &opts.engine_type, &opts.engine_id);
 +    if (cfg->engine_type) {
 +        opts.engine_type = *cfg->engine_type;
 +    }
 +    if (cfg->engine_id) {
 +        opts.engine_id = *cfg->engine_id;
 +    }
  
 -            oso.polling_interval = SFL_DEFAULT_POLLING_INTERVAL;
 -            if (sflow_cfg->polling) {
 -                oso.polling_interval = *sflow_cfg->polling;
 -            }
 +    /* Configure active timeout interval. */
 +    opts.active_timeout = cfg->active_timeout;
 +    if (!opts.active_timeout) {
 +        opts.active_timeout = -1;
 +    } else if (opts.active_timeout < 0) {
 +        VLOG_WARN("bridge %s: active timeout interval set to negative "
 +                  "value, using default instead (%d seconds)", br->name,
 +                  NF_ACTIVE_TIMEOUT_DEFAULT);
 +        opts.active_timeout = -1;
 +    }
  
 -            oso.header_len = SFL_DEFAULT_HEADER_SIZE;
 -            if (sflow_cfg->header) {
 -                oso.header_len = *sflow_cfg->header;
 -            }
 +    /* Add engine ID to interface number to disambiguate bridgs? */
 +    opts.add_id_to_iface = cfg->add_id_to_interface;
 +    if (opts.add_id_to_iface) {
 +        if (opts.engine_id > 0x7f) {
 +            VLOG_WARN("bridge %s: NetFlow port mangling may conflict with "
 +                      "another vswitch, choose an engine id less than 128",
 +                      br->name);
 +        }
 +        if (hmap_count(&br->ports) > 508) {
 +            VLOG_WARN("bridge %s: NetFlow port mangling will conflict with "
 +                      "another port when more than 508 ports are used",
 +                      br->name);
 +        }
 +    }
  
 -            oso.sub_id = sflow_bridge_number++;
 -            oso.agent_device = sflow_cfg->agent;
 +    /* Collectors. */
 +    sset_init(&opts.collectors);
 +    sset_add_array(&opts.collectors, cfg->targets, cfg->n_targets);
  
 -            oso.control_ip = NULL;
 -            n_controllers = bridge_get_controllers(br, &controllers);
 -            for (i = 0; i < n_controllers; i++) {
 -                if (controllers[i]->local_ip) {
 -                    oso.control_ip = controllers[i]->local_ip;
 -                    break;
 -                }
 -            }
 -            ofproto_set_sflow(br->ofproto, &oso);
 +    /* Configure. */
 +    if (ofproto_set_netflow(br->ofproto, &opts)) {
 +        VLOG_ERR("bridge %s: problem setting netflow collectors", br->name);
 +    }
 +    sset_destroy(&opts.collectors);
 +}
  
 -            sset_destroy(&oso.targets);
 -        } else {
 -            ofproto_set_sflow(br->ofproto, NULL);
 -        }
 +/* Set sFlow configuration on 'br'. */
 +static void
 +bridge_configure_sflow(struct bridge *br, int *sflow_bridge_number)
 +{
 +    const struct ovsrec_sflow *cfg = br->cfg->sflow;
 +    struct ovsrec_controller **controllers;
 +    struct ofproto_sflow_options oso;
 +    size_t n_controllers;
 +    size_t i;
  
 -        /* Update the controller and related settings.  It would be more
 -         * straightforward to call this from bridge_reconfigure_one(), but we
 -         * can't do it there for two reasons.  First, and most importantly, at
 -         * that point we don't know the dp_ifidx of any interfaces that have
 -         * been added to the bridge (because we haven't actually added them to
 -         * the datapath).  Second, at that point we haven't set the datapath ID
 -         * yet; when a controller is configured, resetting the datapath ID will
 -         * immediately disconnect from the controller, so it's better to set
 -         * the datapath ID before the controller. */
 -        bridge_reconfigure_remotes(br, managers, n_managers);
 +    if (!cfg) {
 +        ofproto_set_sflow(br->ofproto, NULL);
 +        return;
      }
 -    LIST_FOR_EACH (br, node, &all_bridges) {
 -        struct port *port;
  
 -        br->has_bonded_ports = false;
 -        HMAP_FOR_EACH (port, hmap_node, &br->ports) {
 -            struct iface *iface;
 +    memset(&oso, 0, sizeof oso);
  
 -            port_reconfigure_lacp(port);
 -            port_reconfigure_bond(port);
 +    sset_init(&oso.targets);
 +    sset_add_array(&oso.targets, cfg->targets, cfg->n_targets);
  
 -            LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
 -                iface_update_qos(iface, port->cfg->qos);
 -            }
 -        }
 +    oso.sampling_rate = SFL_DEFAULT_SAMPLING_RATE;
 +    if (cfg->sampling) {
 +        oso.sampling_rate = *cfg->sampling;
      }
 -    LIST_FOR_EACH (br, node, &all_bridges) {
 -        iterate_and_prune_ifaces(br, set_iface_properties, NULL);
 +
 +    oso.polling_interval = SFL_DEFAULT_POLLING_INTERVAL;
 +    if (cfg->polling) {
 +        oso.polling_interval = *cfg->polling;
      }
  
 -    /* Some reconfiguration operations require the bridge to have been run at
 -     * least once.  */
 -    LIST_FOR_EACH (br, node, &all_bridges) {
 -        struct iface *iface;
 +    oso.header_len = SFL_DEFAULT_HEADER_SIZE;
 +    if (cfg->header) {
 +        oso.header_len = *cfg->header;
 +    }
  
 -        bridge_run_one(br);
 +    oso.sub_id = (*sflow_bridge_number)++;
 +    oso.agent_device = cfg->agent;
  
 -        HMAP_FOR_EACH (iface, dp_ifidx_node, &br->ifaces) {
 -            iface_update_cfm(iface);
 +    oso.control_ip = NULL;
 +    n_controllers = bridge_get_controllers(br, &controllers);
 +    for (i = 0; i < n_controllers; i++) {
 +        if (controllers[i]->local_ip) {
 +            oso.control_ip = controllers[i]->local_ip;
 +            break;
          }
      }
 +    ofproto_set_sflow(br->ofproto, &oso);
  
 -    free(managers);
 +    sset_destroy(&oso.targets);
 +}
  
 -    /* ovs-vswitchd has completed initialization, so allow the process that
 -     * forked us to exit successfully. */
 -    daemonize_complete();
 +static bool
 +bridge_has_bond_fake_iface(const struct bridge *br, const char *name)
 +{
 +    const struct port *port = port_lookup(br, name);
 +    return port && port_is_bond_fake_iface(port);
  }
  
 -static const char *
 -get_ovsrec_key_value(const struct ovsdb_idl_row *row,
 -                     const struct ovsdb_idl_column *column,
 -                     const char *key)
 +static bool
 +port_is_bond_fake_iface(const struct port *port)
  {
 -    const struct ovsdb_datum *datum;
 -    union ovsdb_atom atom;
 -    unsigned int idx;
 +    return port->cfg->bond_fake_iface && !list_is_short(&port->ifaces);
 +}
  
 -    datum = ovsdb_idl_get(row, column, OVSDB_TYPE_STRING, OVSDB_TYPE_STRING);
 +static void
 +add_del_bridges(const struct ovsrec_open_vswitch *cfg)
 +{
 +    struct bridge *br, *next;
 +    struct shash new_br;
 +    size_t i;
 +
 +    /* Collect new bridges' names and types. */
 +    shash_init(&new_br);
 +    for (i = 0; i < cfg->n_bridges; i++) {
 +        const struct ovsrec_bridge *br_cfg = cfg->bridges[i];
 +        if (!shash_add_once(&new_br, br_cfg->name, br_cfg)) {
 +            VLOG_WARN("bridge %s specified twice", br_cfg->name);
 +        }
 +    }
 +
 +    /* Get rid of deleted bridges or those whose types have changed.
 +     * Update 'cfg' of bridges that still exist. */
 +    HMAP_FOR_EACH_SAFE (br, next, node, &all_bridges) {
 +        br->cfg = shash_find_data(&new_br, br->name);
 +        if (!br->cfg || strcmp(br->type, ofproto_normalize_type(
 +                                   br->cfg->datapath_type))) {
 +            bridge_destroy(br);
 +        }
 +    }
 +
 +    /* Add new bridges. */
 +    for (i = 0; i < cfg->n_bridges; i++) {
 +        const struct ovsrec_bridge *br_cfg = cfg->bridges[i];
 +        struct bridge *br = bridge_lookup(br_cfg->name);
 +        if (!br) {
 +            bridge_create(br_cfg);
 +        }
 +    }
 +
 +    shash_destroy(&new_br);
 +}
 +
 +/* Delete each ofproto port on 'br' that doesn't have a corresponding "struct
 + * iface".
 + *
 + * 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. */
 +static void
 +bridge_del_ofproto_ports(struct bridge *br)
 +{
 +    struct ofproto_port_dump dump;
 +    struct ofproto_port ofproto_port;
 +
 +    OFPROTO_PORT_FOR_EACH (&ofproto_port, &dump, br->ofproto) {
 +        const char *name = ofproto_port.name;
 +        struct iface *iface;
 +        const char *type;
 +        int error;
 +
 +        /* Ignore the local port.  We can't change it anyhow. */
 +        if (!strcmp(name, br->name)) {
 +            continue;
 +        }
 +
 +        /* Get the type that 'ofproto_port' should have (ordinarily the
 +         * type of its corresponding iface) or NULL if it should be
 +         * deleted. */
 +        iface = iface_lookup(br, name);
 +        type = (iface ? iface->type
 +                : bridge_has_bond_fake_iface(br, name) ? "internal"
 +                : NULL);
 +
 +        /* If it's the wrong type then delete the ofproto port. */
 +        if (type
 +            && !strcmp(ofproto_port.type, type)
 +            && (!iface || !iface->netdev
 +                || !strcmp(netdev_get_type(iface->netdev), type))) {
 +            continue;
 +        }
 +        error = ofproto_port_del(br->ofproto, ofproto_port.ofp_port);
 +        if (error) {
 +            VLOG_WARN("bridge %s: failed to remove %s interface (%s)",
 +                      br->name, name, strerror(error));
 +        }
 +        if (iface) {
 +            netdev_close(iface->netdev);
 +            iface->netdev = NULL;
 +        }
 +    }
 +}
 +
 +static void
 +iface_set_ofp_port(struct iface *iface, int ofp_port)
 +{
 +    struct bridge *br = iface->port->bridge;
 +
 +    assert(iface->ofp_port < 0 && ofp_port >= 0);
 +    iface->ofp_port = ofp_port;
 +    hmap_insert(&br->ifaces, &iface->ofp_port_node, hash_int(ofp_port, 0));
 +
 +}
 +
 +static void
 +bridge_refresh_ofp_port(struct bridge *br)
 +{
 +    struct ofproto_port_dump dump;
 +    struct ofproto_port ofproto_port;
 +    struct port *port;
 +
 +    /* Clear all the "ofp_port"es. */
 +    hmap_clear(&br->ifaces);
 +    HMAP_FOR_EACH (port, hmap_node, &br->ports) {
 +        struct iface *iface;
 +
 +        LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
 +            iface->ofp_port = -1;
 +        }
 +    }
 +
 +    /* Obtain the correct "ofp_port"s from ofproto. */
 +    OFPROTO_PORT_FOR_EACH (&ofproto_port, &dump, br->ofproto) {
 +        struct iface *iface = iface_lookup(br, ofproto_port.name);
 +        if (iface) {
 +            if (iface->ofp_port >= 0) {
 +                VLOG_WARN("bridge %s: interface %s reported twice",
 +                          br->name, ofproto_port.name);
 +            } else if (iface_from_ofp_port(br, ofproto_port.ofp_port)) {
 +                VLOG_WARN("bridge %s: interface %"PRIu16" reported twice",
 +                          br->name, ofproto_port.ofp_port);
 +            } else {
 +                iface_set_ofp_port(iface, ofproto_port.ofp_port);
 +            }
 +        }
 +    }
 +}
 +
 +/* Add an ofproto port for any "struct iface" that doesn't have one.
 + * Delete any "struct iface" for which this fails.
 + * Delete any "struct port" that thereby ends up with no ifaces. */
 +static void
 +bridge_add_ofproto_ports(struct bridge *br)
 +{
 +    struct port *port, *next_port;
 +
 +    HMAP_FOR_EACH_SAFE (port, next_port, hmap_node, &br->ports) {
 +        struct iface *iface, *next_iface;
 +        struct ofproto_port ofproto_port;
 +
 +        LIST_FOR_EACH_SAFE (iface, next_iface, port_elem, &port->ifaces) {
 +            struct shash args;
 +            int error;
 +
 +            /* Open the netdev or reconfigure it. */
 +            shash_init(&args);
 +            shash_from_ovs_idl_map(iface->cfg->key_options,
 +                                   iface->cfg->value_options,
 +                                   iface->cfg->n_options, &args);
 +            if (!iface->netdev) {
 +                struct netdev_options options;
 +                options.name = iface->name;
 +                options.type = iface->type;
 +                options.args = &args;
 +                options.ethertype = NETDEV_ETH_TYPE_NONE;
 +                error = netdev_open(&options, &iface->netdev);
 +            } else {
 +                error = netdev_set_config(iface->netdev, &args);
 +            }
 +            shash_destroy(&args);
 +            if (error) {
 +                VLOG_WARN("could not %s network device %s (%s)",
 +                          iface->netdev ? "reconfigure" : "open",
 +                          iface->name, strerror(error));
 +            }
 +
 +            /* Add the port, if necessary. */
 +            if (iface->netdev && iface->ofp_port < 0) {
 +                uint16_t ofp_port;
 +                int error;
 +
 +                error = ofproto_port_add(br->ofproto, iface->netdev,
 +                                         &ofp_port);
 +                if (!error) {
 +                    iface_set_ofp_port(iface, ofp_port);
 +                } else {
 +                    netdev_close(iface->netdev);
 +                    iface->netdev = NULL;
 +                }
 +            }
 +
 +            /* Delete the iface if  */
 +            if (iface->netdev && iface->ofp_port >= 0) {
 +                VLOG_DBG("bridge %s: interface %s is on port %d",
 +                         br->name, iface->name, iface->ofp_port);
 +            } else {
 +                if (iface->netdev) {
 +                    VLOG_ERR("bridge %s: missing %s interface, dropping",
 +                             br->name, iface->name);
 +                } else {
 +                    /* We already reported a related error, don't bother
 +                     * duplicating it. */
 +                }
 +                iface_set_ofport(iface->cfg, -1);
 +                iface_destroy(iface);
 +            }
 +        }
 +        if (list_is_empty(&port->ifaces)) {
 +            VLOG_WARN("%s port has no interfaces, dropping", port->name);
 +            port_destroy(port);
 +            continue;
 +        }
 +
 +        /* Add bond fake iface if necessary. */
 +        if (port_is_bond_fake_iface(port)) {
 +            if (ofproto_port_query_by_name(br->ofproto, port->name,
 +                                           &ofproto_port)) {
 +                struct netdev_options options;
 +                struct netdev *netdev;
 +                int error;
 +
 +                options.name = port->name;
 +                options.type = "internal";
 +                options.args = NULL;
 +                options.ethertype = NETDEV_ETH_TYPE_NONE;
 +                error = netdev_open(&options, &netdev);
 +                if (!error) {
 +                    ofproto_port_add(br->ofproto, netdev, NULL);
 +                    netdev_close(netdev);
 +                } else {
 +                    VLOG_WARN("could not open network device %s (%s)",
 +                              port->name, strerror(error));
 +                }
 +            } else {
 +                /* Already exists, nothing to do. */
 +                ofproto_port_destroy(&ofproto_port);
 +            }
 +            ofproto_port_destroy(&ofproto_port);
 +        }
 +    }
 +}
 +
 +static const char *
 +get_ovsrec_key_value(const struct ovsdb_idl_row *row,
 +                     const struct ovsdb_idl_column *column,
 +                     const char *key)
 +{
 +    const struct ovsdb_datum *datum;
 +    union ovsdb_atom atom;
 +    unsigned int idx;
 +
 +    datum = ovsdb_idl_get(row, column, OVSDB_TYPE_STRING, OVSDB_TYPE_STRING);
      atom.string = (char *) key;
      idx = ovsdb_datum_find_key(datum, &atom, OVSDB_TYPE_STRING);
      return idx == UINT_MAX ? NULL : datum->values[idx].string;
@@@ -982,7 -983,7 +982,7 @@@ bridge_pick_local_hw_addr(struct bridg
          struct iface *iface;
  
          /* Mirror output ports don't participate. */
 -        if (port->is_mirror_output_port) {
 +        if (ofproto_is_mirror_output_bundle(br->ofproto, port)) {
              continue;
          }
  
  
              /* The local port doesn't count (since we're trying to choose its
               * MAC address anyway). */
 -            if (iface->dp_ifidx == ODPP_LOCAL) {
 +            if (iface->ofp_port == OFPP_LOCAL) {
                  continue;
              }
  
@@@ -1195,6 -1196,7 +1195,6 @@@ iface_refresh_status(struct iface *ifac
          ovsrec_interface_set_link_speed(iface->cfg, NULL, 0);
      }
  
 -
      ovsrec_interface_set_link_state(iface->cfg,
                                      iface_get_carrier(iface) ? "up" : "down");
  
@@@ -1219,7 -1221,7 +1219,7 @@@ iface_refresh_cfm_stats(struct iface *i
      size_t i;
  
      mon = iface->cfg->monitor;
 -    cfm = ofproto_iface_get_cfm(iface->port->bridge->ofproto, iface->dp_ifidx);
 +    cfm = ofproto_port_get_cfm(iface->port->bridge->ofproto, iface->ofp_port);
  
      if (!cfm || !mon) {
          return false;
  static bool
  iface_refresh_lacp_stats(struct iface *iface)
  {
 -    bool *db_current = iface->cfg->lacp_current;
 -    bool changed = false;
 -
 -    if (iface->port->lacp) {
 -        bool current = lacp_slave_is_current(iface->port->lacp, iface);
 +    struct ofproto *ofproto = iface->port->bridge->ofproto;
 +    int old = iface->cfg->lacp_current ? *iface->cfg->lacp_current : -1;
 +    int new = ofproto_port_is_lacp_current(ofproto, iface->ofp_port);
  
 -        if (!db_current || *db_current != current) {
 -            changed = true;
 -            ovsrec_interface_set_lacp_current(iface->cfg, &current, 1);
 -        }
 -    } else if (db_current) {
 -        changed = true;
 -        ovsrec_interface_set_lacp_current(iface->cfg, NULL, 0);
 +    if (old != new) {
 +        bool current = new;
 +        ovsrec_interface_set_lacp_current(iface->cfg, &current, new >= 0);
      }
 -
 -    return changed;
 +    return old != new;
  }
  
  static void
@@@ -1379,8 -1388,8 +1379,8 @@@ bridge_run(void
  
      /* Let each bridge do the work that it needs to do. */
      datapath_destroyed = false;
 -    LIST_FOR_EACH (br, node, &all_bridges) {
 -        int error = bridge_run_one(br);
 +    HMAP_FOR_EACH (br, node, &all_bridges) {
 +        int error = ofproto_run(br->ofproto);
          if (error) {
              static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
              VLOG_ERR_RL(&rl, "bridge %s: datapath was destroyed externally, "
      /* (Re)configure if necessary. */
      database_changed = ovsdb_idl_run(idl);
      cfg = ovsrec_open_vswitch_first(idl);
- #ifdef HAVE_OPENSSL
      /* Re-configure SSL.  We do this on every trip through the main loop,
       * instead of just when the database changes, because the contents of the
       * key and certificate files can change without the database changing.
          stream_ssl_set_key_and_cert(ssl->private_key, ssl->certificate);
          stream_ssl_set_ca_cert_file(ssl->ca_cert, ssl->bootstrap_ca_cert);
      }
- #endif
      if (database_changed || datapath_destroyed) {
          if (cfg) {
              struct ovsdb_idl_txn *txn = ovsdb_idl_txn_create(idl);
  
 -            bridge_configure_once(cfg);
              bridge_reconfigure(cfg);
  
              ovsrec_open_vswitch_set_cur_cfg(cfg, cfg->next_cfg);
              struct ovsdb_idl_txn *txn;
  
              txn = ovsdb_idl_txn_create(idl);
 -            LIST_FOR_EACH (br, node, &all_bridges) {
 +            HMAP_FOR_EACH (br, node, &all_bridges) {
                  struct port *port;
  
                  HMAP_FOR_EACH (port, hmap_node, &br->ports) {
          bool changed = false;
  
          txn = ovsdb_idl_txn_create(idl);
 -        LIST_FOR_EACH (br, node, &all_bridges) {
 +        HMAP_FOR_EACH (br, node, &all_bridges) {
              struct port *port;
  
              HMAP_FOR_EACH (port, hmap_node, &br->ports) {
@@@ -1483,8 -1493,14 +1483,8 @@@ bridge_wait(void
  {
      struct bridge *br;
  
 -    LIST_FOR_EACH (br, node, &all_bridges) {
 -        struct port *port;
 -
 +    HMAP_FOR_EACH (br, node, &all_bridges) {
          ofproto_wait(br->ofproto);
 -        mac_learning_wait(br->ml);
 -        HMAP_FOR_EACH (port, hmap_node, &br->ports) {
 -            port_wait(port);
 -        }
      }
      ovsdb_idl_wait(idl);
      poll_timer_wait_until(stats_timer);
          poll_timer_wait_until(db_limiter);
      }
  }
 -
 -/* Forces 'br' to revalidate all of its flows.  This is appropriate when 'br''s
 - * configuration changes.  */
 -static void
 -bridge_flush(struct bridge *br)
 -{
 -    COVERAGE_INC(bridge_flush);
 -    br->flush = true;
 -}
 -\f
 -/* Bridge unixctl user interface functions. */
 -static void
 -bridge_unixctl_fdb_show(struct unixctl_conn *conn,
 -                        const char *args, void *aux OVS_UNUSED)
 -{
 -    struct ds ds = DS_EMPTY_INITIALIZER;
 -    const struct bridge *br;
 -    const struct mac_entry *e;
 -
 -    br = bridge_lookup(args);
 -    if (!br) {
 -        unixctl_command_reply(conn, 501, "no such bridge");
 -        return;
 -    }
 -
 -    ds_put_cstr(&ds, " port  VLAN  MAC                Age\n");
 -    LIST_FOR_EACH (e, lru_node, &br->ml->lrus) {
 -        struct port *port = e->port.p;
 -        ds_put_format(&ds, "%5d  %4d  "ETH_ADDR_FMT"  %3d\n",
 -                      port_get_an_iface(port)->dp_ifidx,
 -                      e->vlan, ETH_ADDR_ARGS(e->mac), mac_entry_age(e));
 -    }
 -    unixctl_command_reply(conn, 200, ds_cstr(&ds));
 -    ds_destroy(&ds);
 -}
  \f
  /* CFM unixctl user interface functions. */
  static void
@@@ -1509,7 -1560,7 +1509,7 @@@ cfm_unixctl_show(struct unixctl_conn *c
          return;
      }
  
 -    cfm = ofproto_iface_get_cfm(iface->port->bridge->ofproto, iface->dp_ifidx);
 +    cfm = ofproto_port_get_cfm(iface->port->bridge->ofproto, iface->ofp_port);
  
      if (!cfm) {
          unixctl_command_reply(conn, 501, "CFM not enabled");
@@@ -1615,48 -1666,79 +1615,48 @@@ qos_unixctl_show(struct unixctl_conn *c
  }
  \f
  /* Bridge reconfiguration functions. */
 -static struct bridge *
 +static void
  bridge_create(const struct ovsrec_bridge *br_cfg)
  {
      struct bridge *br;
 -    int error;
  
      assert(!bridge_lookup(br_cfg->name));
      br = xzalloc(sizeof *br);
  
 -    error = dpif_create_and_open(br_cfg->name, br_cfg->datapath_type,
 -                                 &br->dpif);
 -    if (error) {
 -        free(br);
 -        return NULL;
 -    }
 -
 -    error = ofproto_create(br_cfg->name, br_cfg->datapath_type, &bridge_ofhooks,
 -                           br, &br->ofproto);
 -    if (error) {
 -        VLOG_ERR("failed to create switch %s: %s", br_cfg->name,
 -                 strerror(error));
 -        dpif_delete(br->dpif);
 -        dpif_close(br->dpif);
 -        free(br);
 -        return NULL;
 -    }
 -
      br->name = xstrdup(br_cfg->name);
 +    br->type = xstrdup(ofproto_normalize_type(br_cfg->datapath_type));
      br->cfg = br_cfg;
 -    br->ml = mac_learning_create();
      eth_addr_nicira_random(br->default_ea);
  
      hmap_init(&br->ports);
      hmap_init(&br->ifaces);
 -    shash_init(&br->iface_by_name);
 -
 -    br->flush = false;
 -
 -    list_push_back(&all_bridges, &br->node);
 -
 -    VLOG_INFO("created bridge %s on %s", br->name, dpif_name(br->dpif));
 +    hmap_init(&br->iface_by_name);
 +    hmap_init(&br->mirrors);
  
 -    return br;
 +    hmap_insert(&all_bridges, &br->node, hash_string(br->name, 0));
  }
  
  static void
  bridge_destroy(struct bridge *br)
  {
      if (br) {
 -        struct port *port, *next;
 -        int error;
 -        int i;
 +        struct mirror *mirror, *next_mirror;
 +        struct port *port, *next_port;
  
 -        HMAP_FOR_EACH_SAFE (port, next, hmap_node, &br->ports) {
 +        HMAP_FOR_EACH_SAFE (port, next_port, hmap_node, &br->ports) {
              port_destroy(port);
          }
 -        for (i = 0; i < MAX_MIRRORS; i++) {
 -            mirror_destroy(br->mirrors[i]);
 +        HMAP_FOR_EACH_SAFE (mirror, next_mirror, hmap_node, &br->mirrors) {
 +            mirror_destroy(mirror);
          }
 -        list_remove(&br->node);
 +        hmap_remove(&all_bridges, &br->node);
          ofproto_destroy(br->ofproto);
 -        error = dpif_delete(br->dpif);
 -        if (error && error != ENOENT) {
 -            VLOG_ERR("failed to delete %s: %s",
 -                     dpif_name(br->dpif), strerror(error));
 -        }
 -        dpif_close(br->dpif);
 -        mac_learning_destroy(br->ml);
          hmap_destroy(&br->ifaces);
          hmap_destroy(&br->ports);
 -        shash_destroy(&br->iface_by_name);
 -        free(br->synth_local_iface.type);
 +        hmap_destroy(&br->iface_by_name);
 +        hmap_destroy(&br->mirrors);
          free(br->name);
 +        free(br->type);
          free(br);
      }
  }
@@@ -1666,7 -1748,7 +1666,7 @@@ bridge_lookup(const char *name
  {
      struct bridge *br;
  
 -    LIST_FOR_EACH (br, node, &all_bridges) {
 +    HMAP_FOR_EACH_WITH_HASH (br, node, hash_string(name, 0), &all_bridges) {
          if (!strcmp(br->name, name)) {
              return br;
          }
@@@ -1712,13 -1794,36 +1712,13 @@@ bridge_unixctl_reconnect(struct unixctl
          }
          ofproto_reconnect_controllers(br->ofproto);
      } else {
 -        LIST_FOR_EACH (br, node, &all_bridges) {
 +        HMAP_FOR_EACH (br, node, &all_bridges) {
              ofproto_reconnect_controllers(br->ofproto);
          }
      }
      unixctl_command_reply(conn, 200, NULL);
  }
  
 -static int
 -bridge_run_one(struct bridge *br)
 -{
 -    struct port *port;
 -    int error;
 -
 -    error = ofproto_run1(br->ofproto);
 -    if (error) {
 -        return error;
 -    }
 -
 -    mac_learning_run(br->ml, ofproto_get_revalidate_set(br->ofproto));
 -
 -    HMAP_FOR_EACH (port, hmap_node, &br->ports) {
 -        port_run(port);
 -    }
 -
 -    error = ofproto_run2(br->ofproto, br->flush);
 -    br->flush = false;
 -
 -    return error;
 -}
 -
  static size_t
  bridge_get_controllers(const struct bridge *br,
                         struct ovsrec_controller ***controllersp)
      return n_controllers;
  }
  
 +/* Adds and deletes "struct port"s and "struct iface"s under 'br' to match
 + * those configured in 'br->cfg'. */
  static void
 -bridge_reconfigure_one(struct bridge *br)
 +bridge_add_del_ports(struct bridge *br)
  {
 -    enum ofproto_fail_mode fail_mode;
      struct port *port, *next;
      struct shash_node *node;
      struct shash new_ports;
                        br->name, name);
          }
      }
 -    if (!shash_find(&new_ports, br->name)) {
 -        struct dpif_port dpif_port;
 -        char *type;
 -
 +    if (bridge_get_controllers(br, NULL)
 +        && !shash_find(&new_ports, br->name)) {
          VLOG_WARN("bridge %s: no port named %s, synthesizing one",
                    br->name, br->name);
  
 -        dpif_port_query_by_number(br->dpif, ODPP_LOCAL, &dpif_port);
 -        type = xstrdup(dpif_port.type ? dpif_port.type : "internal");
 -        dpif_port_destroy(&dpif_port);
 -
          br->synth_local_port.interfaces = &br->synth_local_ifacep;
          br->synth_local_port.n_interfaces = 1;
          br->synth_local_port.name = br->name;
  
          br->synth_local_iface.name = br->name;
 -        free(br->synth_local_iface.type);
 -        br->synth_local_iface.type = type;
 +        br->synth_local_iface.type = "internal";
  
          br->synth_local_ifacep = &br->synth_local_iface;
  
      }
  
      /* Get rid of deleted ports.
 -     * Get rid of deleted interfaces on ports that still exist. */
 +     * Get rid of deleted interfaces on ports that still exist.
 +     * Update 'cfg' of ports that still exist. */
      HMAP_FOR_EACH_SAFE (port, next, hmap_node, &br->ports) {
 -        const struct ovsrec_port *port_cfg;
 -
 -        port_cfg = shash_find_data(&new_ports, port->name);
 -        if (!port_cfg) {
 +        port->cfg = shash_find_data(&new_ports, port->name);
 +        if (!port->cfg) {
              port_destroy(port);
          } else {
 -            port_del_ifaces(port, port_cfg);
 +            port_del_ifaces(port);
          }
      }
  
      /* Create new ports.
 -     * Add new interfaces to existing ports.
 -     * Reconfigure existing ports. */
 +     * Add new interfaces to existing ports. */
      SHASH_FOR_EACH (node, &new_ports) {
          struct port *port = port_lookup(br, node->name);
          if (!port) {
 -            port = port_create(br, node->name);
 +            struct ovsrec_port *cfg = node->data;
 +            port = port_create(br, cfg);
          }
 -
 -        port_reconfigure(port, node->data);
 +        port_add_ifaces(port);
          if (list_is_empty(&port->ifaces)) {
              VLOG_WARN("bridge %s: port %s has no interfaces, dropping",
                        br->name, port->name);
          }
      }
      shash_destroy(&new_ports);
 -
 -    /* Set the fail-mode */
 -    fail_mode = !br->cfg->fail_mode
 -                || !strcmp(br->cfg->fail_mode, "standalone")
 -                    ? OFPROTO_FAIL_STANDALONE
 -                    : OFPROTO_FAIL_SECURE;
 -    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);
 -
 -    /* Delete all flows if we're switching from connected to standalone or vice
 -     * versa.  (XXX Should we delete all flows if we are switching from one
 -     * controller to another?) */
 -
 -    /* Configure OpenFlow controller connection snooping. */
 -    if (!ofproto_has_snoops(br->ofproto)) {
 -        struct sset snoops;
 -
 -        sset_init(&snoops);
 -        sset_add_and_free(&snoops, xasprintf("punix:%s/%s.snoop",
 -                                             ovs_rundir(), br->name));
 -        ofproto_set_snoops(br->ofproto, &snoops);
 -        sset_destroy(&snoops);
 -    }
 -
 -    mirror_reconfigure(br);
  }
  
  /* Initializes 'oc' appropriately as a management service controller for
@@@ -1850,7 -1991,7 +1850,7 @@@ bridge_configure_local_iface_netdev(str
      struct in_addr ip;
  
      /* If there's no local interface or no IP address, give up. */
 -    local_iface = iface_from_dp_ifidx(br, ODPP_LOCAL);
 +    local_iface = iface_from_ofp_port(br, OFPP_LOCAL);
      if (!local_iface || !c->local_ip || !inet_aton(c->local_ip, &ip)) {
          return;
      }
  }
  
  static void
 -bridge_reconfigure_remotes(struct bridge *br,
 -                           const struct sockaddr_in *managers,
 -                           size_t n_managers)
 +bridge_configure_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;
  
      struct ovsrec_controller **controllers;
      size_t n_controllers;
 -    bool had_primary;
 +
 +    enum ofproto_fail_mode fail_mode;
  
      struct ofproto_controller *ocs;
      size_t n_ocs;
      } 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);
  
  
              /* 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;
 -        }
 -
 -        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;
 -        }
 -        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 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);
 -        cls_rule_init_catchall(&rule, 0);
 -        ofproto_add_flow(br->ofproto, &rule, &action, 1);
 -    }
 -}
 -
 -static void
 -bridge_get_all_ifaces(const struct bridge *br, struct shash *ifaces)
 -{
 -    struct port *port;
 -
 -    shash_init(ifaces);
 -    HMAP_FOR_EACH (port, hmap_node, &br->ports) {
 -        struct iface *iface;
 -
 -        LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
 -            shash_add_once(ifaces, iface->name, iface);
 -        }
 -        if (!list_is_short(&port->ifaces) && port->cfg->bond_fake_iface) {
 -            shash_add_once(ifaces, port->name, NULL);
 -        }
 -    }
 -}
 -
 -/* For robustness, in case the administrator moves around datapath ports behind
 - * our back, we re-check all the datapath port numbers here.
 - *
 - * This function will set the 'dp_ifidx' members of interfaces that have
 - * disappeared to -1, so only call this function from a context where those
 - * 'struct iface's will be removed from the bridge.  Otherwise, the -1
 - * 'dp_ifidx'es will cause trouble later when we try to send them to the
 - * datapath, which doesn't support UINT16_MAX+1 ports. */
 -static void
 -bridge_fetch_dp_ifaces(struct bridge *br)
 -{
 -    struct dpif_port_dump dump;
 -    struct dpif_port dpif_port;
 -    struct port *port;
 -
 -    /* Reset all interface numbers. */
 -    HMAP_FOR_EACH (port, hmap_node, &br->ports) {
 -        struct iface *iface;
 -
 -        LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
 -            iface->dp_ifidx = -1;
 -        }
 -    }
 -    hmap_clear(&br->ifaces);
 -
 -    DPIF_PORT_FOR_EACH (&dpif_port, &dump, br->dpif) {
 -        struct iface *iface = iface_lookup(br, dpif_port.name);
 -        if (iface) {
 -            if (iface->dp_ifidx >= 0) {
 -                VLOG_WARN("%s reported interface %s twice",
 -                          dpif_name(br->dpif), dpif_port.name);
 -            } else if (iface_from_dp_ifidx(br, dpif_port.port_no)) {
 -                VLOG_WARN("%s reported interface %"PRIu16" twice",
 -                          dpif_name(br->dpif), dpif_port.port_no);
 -            } else {
 -                iface->dp_ifidx = dpif_port.port_no;
 -                hmap_insert(&br->ifaces, &iface->dp_ifidx_node,
 -                            hash_int(iface->dp_ifidx, 0));
 -            }
 -
 -            iface_set_ofport(iface->cfg,
 -                             (iface->dp_ifidx >= 0
 -                              ? odp_port_to_ofp_port(iface->dp_ifidx)
 -                              : -1));
 -        }
 -    }
 -}
 -\f
 -/* Bridge packet processing functions. */
 -
 -static bool
 -set_dst(struct dst *dst, const struct flow *flow,
 -        const struct port *in_port, const struct port *out_port,
 -        tag_type *tags)
 -{
 -    dst->vlan = (out_port->vlan >= 0 ? OFP_VLAN_NONE
 -                 : in_port->vlan >= 0 ? in_port->vlan
 -                 : flow->vlan_tci == 0 ? OFP_VLAN_NONE
 -                 : vlan_tci_to_vid(flow->vlan_tci));
 -
 -    dst->iface = (!out_port->bond
 -                  ? port_get_an_iface(out_port)
 -                  : bond_choose_output_slave(out_port->bond, flow,
 -                                             dst->vlan, tags));
 -
 -    return dst->iface != NULL;
 -}
 -
 -static int
 -mirror_mask_ffs(mirror_mask_t mask)
 -{
 -    BUILD_ASSERT_DECL(sizeof(unsigned int) >= sizeof(mask));
 -    return ffs(mask);
 -}
 -
 -static void
 -dst_set_init(struct dst_set *set)
 -{
 -    set->dsts = set->builtin;
 -    set->n = 0;
 -    set->allocated = ARRAY_SIZE(set->builtin);
 -}
 -
 -static void
 -dst_set_add(struct dst_set *set, const struct dst *dst)
 -{
 -    if (set->n >= set->allocated) {
 -        size_t new_allocated;
 -        struct dst *new_dsts;
 -
 -        new_allocated = set->allocated * 2;
 -        new_dsts = xmalloc(new_allocated * sizeof *new_dsts);
 -        memcpy(new_dsts, set->dsts, set->n * sizeof *new_dsts);
 -
 -        dst_set_free(set);
 -
 -        set->dsts = new_dsts;
 -        set->allocated = new_allocated;
 -    }
 -    set->dsts[set->n++] = *dst;
 -}
 -
 -static void
 -dst_set_free(struct dst_set *set)
 -{
 -    if (set->dsts != set->builtin) {
 -        free(set->dsts);
 -    }
 -}
 -
 -static bool
 -dst_is_duplicate(const struct dst_set *set, const struct dst *test)
 -{
 -    size_t i;
 -    for (i = 0; i < set->n; i++) {
 -        if (set->dsts[i].vlan == test->vlan
 -            && set->dsts[i].iface == test->iface) {
 -            return true;
 -        }
 -    }
 -    return false;
 -}
 -
 -static bool
 -port_trunks_vlan(const struct port *port, uint16_t vlan)
 -{
 -    return (port->vlan < 0
 -            && (!port->trunks || bitmap_is_set(port->trunks, vlan)));
 -}
 -
 -static bool
 -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)
 -{
 -    struct iface *iface;
 -
 -    LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
 -        if (!ofproto_port_is_floodable(port->bridge->ofproto,
 -                                       iface->dp_ifidx)) {
 -            return false;
 -        }
 -    }
 -    return true;
 -}
 -
 -/* Returns an arbitrary interface within 'port'. */
 -static struct iface *
 -port_get_an_iface(const struct port *port)
 -{
 -    return CONTAINER_OF(list_front(&port->ifaces), struct iface, port_elem);
 -}
 -
 -static void
 -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_set *set, tag_type *tags, uint16_t *nf_output_iface)
 -{
 -    struct dst dst;
 -
 -    if (out_port == FLOOD_PORT) {
 -        struct port *port;
 -
 -        HMAP_FOR_EACH (port, hmap_node, &br->ports) {
 -            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)) {
 -                dst_set_add(set, &dst);
 -            }
 -        }
 -        *nf_output_iface = NF_OUT_FLOOD;
 -    } else if (out_port && set_dst(&dst, flow, in_port, out_port, tags)) {
 -        dst_set_add(set, &dst);
 -        *nf_output_iface = dst.iface->dp_ifidx;
 -    }
 -}
 -
 -static void
 -compose_mirror_dsts(const struct bridge *br, const struct flow *flow,
 -                    uint16_t vlan, const struct port *in_port,
 -                    struct dst_set *set, tag_type *tags)
 -{
 -    mirror_mask_t mirrors;
 -    int flow_vlan;
 -    size_t i;
 -
 -    mirrors = in_port->src_mirrors;
 -    for (i = 0; i < set->n; i++) {
 -        mirrors |= set->dsts[i].iface->port->dst_mirrors;
 -    }
 -
 -    if (!mirrors) {
 -        return;
 -    }
 -
 -    flow_vlan = vlan_tci_to_vid(flow->vlan_tci);
 -    if (flow_vlan == 0) {
 -        flow_vlan = OFP_VLAN_NONE;
 -    }
 -
 -    while (mirrors) {
 -        struct mirror *m = br->mirrors[mirror_mask_ffs(mirrors) - 1];
 -        if (!m->n_vlans || vlan_is_mirrored(m, vlan)) {
 -            struct dst dst;
 -
 -            if (m->out_port) {
 -                if (set_dst(&dst, flow, in_port, m->out_port, tags)
 -                    && !dst_is_duplicate(set, &dst)) {
 -                    dst_set_add(set, &dst);
 -                }
 -            } else {
 -                struct port *port;
 -
 -                HMAP_FOR_EACH (port, hmap_node, &br->ports) {
 -                    if (port_includes_vlan(port, m->out_vlan)
 -                        && set_dst(&dst, flow, in_port, port, tags))
 -                    {
 -                        if (port->vlan < 0) {
 -                            dst.vlan = m->out_vlan;
 -                        }
 -                        if (dst_is_duplicate(set, &dst)) {
 -                            continue;
 -                        }
 -
 -                        /* Use the vlan tag on the original flow instead of
 -                         * the one passed in the vlan parameter.  This ensures
 -                         * that we compare the vlan from before any implicit
 -                         * tagging tags place. This is necessary because
 -                         * dst->vlan is the final vlan, after removing implicit
 -                         * tags. */
 -                        if (port == in_port && dst.vlan == flow_vlan) {
 -                            /* Don't send out input port on same VLAN. */
 -                            continue;
 -                        }
 -                        dst_set_add(set, &dst);
 -                    }
 -                }
 -            }
 -        }
 -        mirrors &= mirrors - 1;
 -    }
 -}
 -
 -static void
 -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 ofpbuf *actions,
 -                uint16_t *nf_output_iface)
 -{
 -    uint16_t initial_vlan, cur_vlan;
 -    const struct dst *dst;
 -    struct dst_set set;
 -
 -    dst_set_init(&set);
 -    compose_dsts(br, flow, vlan, in_port, out_port, &set, tags,
 -                 nf_output_iface);
 -    compose_mirror_dsts(br, flow, vlan, in_port, &set, tags);
 -
 -    /* Output all the packets we can without having to change the VLAN. */
 -    initial_vlan = vlan_tci_to_vid(flow->vlan_tci);
 -    if (initial_vlan == 0) {
 -        initial_vlan = OFP_VLAN_NONE;
 -    }
 -    for (dst = set.dsts; dst < &set.dsts[set.n]; dst++) {
 -        if (dst->vlan != initial_vlan) {
 -            continue;
 -        }
 -        nl_msg_put_u32(actions, ODP_ACTION_ATTR_OUTPUT, dst->iface->dp_ifidx);
 -    }
 -
 -    /* Then output the rest. */
 -    cur_vlan = initial_vlan;
 -    for (dst = set.dsts; dst < &set.dsts[set.n]; dst++) {
 -        if (dst->vlan == initial_vlan) {
 -            continue;
 -        }
 -        if (dst->vlan != cur_vlan) {
 -            if (dst->vlan == OFP_VLAN_NONE) {
 -                nl_msg_put_flag(actions, ODP_ACTION_ATTR_STRIP_VLAN);
 -            } else {
 -                ovs_be16 tci;
 -                tci = htons(dst->vlan & VLAN_VID_MASK);
 -                tci |= flow->vlan_tci & htons(VLAN_PCP_MASK);
 -                nl_msg_put_be16(actions, ODP_ACTION_ATTR_SET_DL_TCI, tci);
 -            }
 -            cur_vlan = dst->vlan;
 -        }
 -        nl_msg_put_u32(actions, ODP_ACTION_ATTR_OUTPUT, dst->iface->dp_ifidx);
 -    }
 -
 -    dst_set_free(&set);
 -}
 -
 -/* Returns the effective vlan of a packet, taking into account both the
 - * 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 struct flow *flow,
 -                         struct port *in_port, bool have_packet)
 -{
 -    int vlan = vlan_tci_to_vid(flow->vlan_tci);
 -    if (in_port->vlan >= 0) {
 -        if (vlan) {
 -            if (have_packet) {
 -                static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
 -                VLOG_WARN_RL(&rl, "bridge %s: dropping VLAN %d tagged "
 -                             "packet received on port %s configured with "
 -                             "implicit VLAN %"PRIu16,
 -                             br->name, vlan, in_port->name, in_port->vlan);
 -            }
 -            return -1;
 -        }
 -        vlan = in_port->vlan;
 -    } else {
 -        if (!port_includes_vlan(in_port, vlan)) {
 -            if (have_packet) {
 -                static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
 -                VLOG_WARN_RL(&rl, "bridge %s: dropping VLAN %d tagged "
 -                             "packet received on port %s not configured for "
 -                             "trunking VLAN %d",
 -                             br->name, vlan, in_port->name, vlan);
 -            }
 -            return -1;
 -        }
 -    }
 -
 -    return vlan;
 -}
 -
 -/* A VM broadcasts a gratuitous ARP to indicate that it has resumed after
 - * 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 struct flow *flow)
 -{
 -    return (flow->dl_type == htons(ETH_TYPE_ARP)
 -            && eth_addr_is_broadcast(flow->dl_dst)
 -            && (flow->nw_proto == ARP_OP_REPLY
 -                || (flow->nw_proto == ARP_OP_REQUEST
 -                    && flow->nw_src == flow->nw_dst)));
 -}
 -
 -static void
 -update_learning_table(struct bridge *br, const struct flow *flow, int vlan,
 -                      struct port *in_port)
 -{
 -    struct mac_entry *mac;
 -
 -    if (!mac_learning_may_learn(br->ml, flow->dl_src, vlan)) {
 -        return;
 -    }
 -
 -    mac = mac_learning_insert(br->ml, flow->dl_src, vlan);
 -    if (is_gratuitous_arp(flow)) {
 -        /* We don't want to learn from gratuitous ARP packets that are
 -         * reflected back over bond slaves so we lock the learning table. */
 -        if (!in_port->bond) {
 -            mac_entry_set_grat_arp_lock(mac);
 -        } else if (mac_entry_is_grat_arp_locked(mac)) {
 -            return;
 -        }
 -    }
 -
 -    if (mac_entry_is_new(mac) || mac->port.p != in_port) {
 -        /* The log messages here could actually be useful in debugging,
 -         * so keep the rate limit relatively high. */
 -        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(30, 300);
 -        VLOG_DBG_RL(&rl, "bridge %s: learned that "ETH_ADDR_FMT" is "
 -                    "on port %s in VLAN %d",
 -                    br->name, ETH_ADDR_ARGS(flow->dl_src),
 -                    in_port->name, vlan);
 -
 -        mac->port.p = in_port;
 -        ofproto_revalidate(br->ofproto, mac_learning_changed(br->ml, mac));
 -    }
 -}
 -
 -/* Determines whether packets in 'flow' within 'br' should be forwarded or
 - * dropped.  Returns true if they may be forwarded, false if they should be
 - * dropped.
 - *
 - * If 'have_packet' is true, it indicates that the caller is processing a
 - * received packet.  If 'have_packet' is false, then the caller is just
 - * revalidating an existing flow because configuration has changed.  Either
 - * way, 'have_packet' only affects logging (there is no point in logging errors
 - * during revalidation).
 - *
 - * Sets '*in_portp' to the input port.  This will be a null pointer if
 - * flow->in_port does not designate a known input port (in which case
 - * is_admissible() returns false).
 - *
 - * When returning true, sets '*vlanp' to the effective VLAN of the input
 - * packet, as returned by flow_get_vlan().
 - *
 - * May also add tags to '*tags', although the current implementation only does
 - * so in one special case.
 - */
 -static bool
 -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;
 -    struct port *in_port;
 -    int vlan;
 -
 -    /* Find the interface and port structure for the received packet. */
 -    in_iface = iface_from_dp_ifidx(br, flow->in_port);
 -    if (!in_iface) {
 -        /* No interface?  Something fishy... */
 -        if (have_packet) {
 -            /* Odd.  A few possible reasons here:
 -             *
 -             * - We deleted an interface but there are still a few packets
 -             *   queued up from it.
 -             *
 -             * - Someone externally added an interface (e.g. with "ovs-dpctl
 -             *   add-if") that we don't know about.
 -             *
 -             * - Packet arrived on the local port but the local port is not
 -             *   one of our bridge ports.
 -             */
 -            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);
 -        }
 -
 -        *in_portp = NULL;
 -        return false;
 -    }
 -    *in_portp = in_port = in_iface->port;
 -    *vlanp = vlan = flow_get_vlan(br, flow, in_port, have_packet);
 -    if (vlan < 0) {
 -        return false;
 -    }
 -
 -    /* Drop frames for reserved multicast addresses. */
 -    if (eth_addr_is_reserved(flow->dl_dst)) {
 -        return false;
 -    }
 -
 -    /* Drop frames on ports reserved for mirroring. */
 -    if (in_port->is_mirror_output_port) {
 -        if (have_packet) {
 -            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
 -            VLOG_WARN_RL(&rl, "bridge %s: dropping packet received on port "
 -                         "%s, which is reserved exclusively for mirroring",
 -                         br->name, in_port->name);
 -        }
 -        return false;
 -    }
 -
 -    if (in_port->bond) {
 -        struct mac_entry *mac;
 -
 -        switch (bond_check_admissibility(in_port->bond, in_iface,
 -                                         flow->dl_dst, tags)) {
 -        case BV_ACCEPT:
 -            break;
 -
 -        case BV_DROP:
 -            return false;
 -
 -        case BV_DROP_IF_MOVED:
 -            mac = mac_learning_lookup(br->ml, flow->dl_src, vlan, NULL);
 -            if (mac && mac->port.p != in_port &&
 -                (!is_gratuitous_arp(flow)
 -                 || mac_entry_is_grat_arp_locked(mac))) {
 -                return false;
 -            }
 -            break;
 -        }
 -    }
 -
 -    return true;
 -}
 -
 -/* If the composed actions may be applied to any packet in the given 'flow',
 - * returns true.  Otherwise, the actions should only be applied to 'packet', or
 - * not at all, if 'packet' was NULL. */
 -static bool
 -process_flow(struct bridge *br, const struct flow *flow,
 -             const struct ofpbuf *packet, struct ofpbuf *actions,
 -             tag_type *tags, uint16_t *nf_output_iface)
 -{
 -    struct port *in_port;
 -    struct port *out_port;
 -    struct mac_entry *mac;
 -    int vlan;
 -
 -    /* Check whether we should drop packets in this flow. */
 -    if (!is_admissible(br, flow, packet != NULL, tags, &vlan, &in_port)) {
 -        out_port = NULL;
 -        goto done;
 -    }
 -
 -    /* Learn source MAC (but don't try to learn from revalidation). */
 -    if (packet) {
 -        update_learning_table(br, flow, vlan, in_port);
 -    }
 -
 -    /* Determine output port. */
 -    mac = mac_learning_lookup(br->ml, flow->dl_dst, vlan, tags);
 -    if (mac) {
 -        out_port = mac->port.p;
 -    } else if (!packet && !eth_addr_is_multicast(flow->dl_dst)) {
 -        /* If we are revalidating but don't have a learning entry then
 -         * eject the flow.  Installing a flow that floods packets opens
 -         * up a window of time where we could learn from a packet reflected
 -         * on a bond and blackhole packets before the learning table is
 -         * updated to reflect the correct port. */
 -        return false;
 -    } else {
 -        out_port = FLOOD_PORT;
 -    }
 -
 -    /* Don't send packets out their input ports. */
 -    if (in_port == out_port) {
 -        out_port = NULL;
 -    }
 -
 -done:
 -    if (in_port) {
 -        compose_actions(br, flow, vlan, in_port, out_port, tags, actions,
 -                        nf_output_iface);
 -    }
 -
 -    return true;
 -}
 -
 -static bool
 -bridge_normal_ofhook_cb(const struct flow *flow, const struct ofpbuf *packet,
 -                        struct ofpbuf *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 bool
 -bridge_special_ofhook_cb(const struct flow *flow,
 -                         const struct ofpbuf *packet, void *br_)
 -{
 -    struct iface *iface;
 -    struct bridge *br = br_;
 -
 -    iface = iface_from_dp_ifidx(br, flow->in_port);
 -
 -    if (flow->dl_type == htons(ETH_TYPE_LACP)) {
 -        if (iface && iface->port->lacp && packet) {
 -            const struct lacp_pdu *pdu = parse_lacp_packet(packet);
 -            if (pdu) {
 -                lacp_process_pdu(iface->port->lacp, iface, pdu);
 -            }
 -        }
 -        return false;
 -    }
 -
 -    return true;
 -}
 -
 -static void
 -bridge_account_flow_ofhook_cb(const struct flow *flow, tag_type tags,
 -                              const struct nlattr *actions,
 -                              size_t actions_len,
 -                              uint64_t n_bytes, void *br_)
 -{
 -    struct bridge *br = br_;
 -    const struct nlattr *a;
 -    struct port *in_port;
 -    tag_type dummy = 0;
 -    unsigned int left;
 -    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.
 -     *
 -     * 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;
 -    }
 -    NL_ATTR_FOR_EACH_UNSAFE (a, left, actions, actions_len) {
 -        if (nl_attr_type(a) == ODP_ACTION_ATTR_OUTPUT) {
 -            struct port *out_port = port_from_dp_ifidx(br, nl_attr_get_u32(a));
 -            if (out_port && out_port->bond) {
 -                uint16_t vlan = (flow->vlan_tci
 -                                 ? vlan_tci_to_vid(flow->vlan_tci)
 -                                 : OFP_VLAN_NONE);
 -                bond_account(out_port->bond, flow, vlan, n_bytes);
 -            }
 -        }
 -    }
 -}
 -
 -static void
 -bridge_account_checkpoint_ofhook_cb(void *br_)
 -{
 -    struct bridge *br = br_;
 -    struct port *port;
 -
 -    HMAP_FOR_EACH (port, hmap_node, &br->ports) {
 -        if (port->bond) {
 -            bond_rebalance(port->bond,
 -                           ofproto_get_revalidate_set(br->ofproto));
 -        }
 -    }
 -}
 -
 -static uint16_t
 -bridge_autopath_ofhook_cb(const struct flow *flow, uint32_t ofp_port,
 -                          tag_type *tags, void *br_)
 -{
 -    struct bridge *br = br_;
 -    uint16_t odp_port = ofp_port_to_odp_port(ofp_port);
 -    struct port *port = port_from_dp_ifidx(br, odp_port);
 -    uint16_t ret;
 -
 -    if (!port) {
 -        ret = ODPP_NONE;
 -    } else if (list_is_short(&port->ifaces)) {
 -        ret = odp_port;
 -    } else {
 -        struct iface *iface;
 -
 -        /* Autopath does not support VLAN hashing. */
 -        iface = bond_choose_output_slave(port->bond, flow,
 -                                         OFP_VLAN_NONE, tags);
 -        ret = iface ? iface->dp_ifidx : ODPP_NONE;
 -    }
 -
 -    return odp_port_to_ofp_port(ret);
 -}
 -
 -static struct ofhooks bridge_ofhooks = {
 -    bridge_normal_ofhook_cb,
 -    bridge_special_ofhook_cb,
 -    bridge_account_flow_ofhook_cb,
 -    bridge_account_checkpoint_ofhook_cb,
 -    bridge_autopath_ofhook_cb,
 -};
 -\f
 -/* Port functions. */
 -
 -static void
 -lacp_send_pdu_cb(void *iface_, const struct lacp_pdu *pdu)
 -{
 -    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 10);
 -    struct iface *iface = iface_;
 -    uint8_t ea[ETH_ADDR_LEN];
 -    int error;
 -
 -    error = netdev_get_etheraddr(iface->netdev, ea);
 -    if (!error) {
 -        struct lacp_pdu *packet_pdu;
 -        struct ofpbuf packet;
 -
 -        ofpbuf_init(&packet, 0);
 -        packet_pdu = eth_compose(&packet, eth_addr_lacp, ea, ETH_TYPE_LACP,
 -                                 sizeof *packet_pdu);
 -        *packet_pdu = *pdu;
 -        error = netdev_send(iface->netdev, &packet);
 -        if (error) {
 -            VLOG_WARN_RL(&rl, "port %s: sending LACP PDU on iface %s failed "
 -                         "(%s)", iface->port->name, iface->name,
 -                         strerror(error));
 -        }
 -        ofpbuf_uninit(&packet);
 -    } else {
 -        VLOG_ERR_RL(&rl, "port %s: cannot obtain Ethernet address of iface "
 -                    "%s (%s)", iface->port->name, iface->name,
 -                    strerror(error));
 -    }
 -}
 -
 -static void
 -port_run(struct port *port)
 -{
 -    if (port->lacp) {
 -        lacp_run(port->lacp, lacp_send_pdu_cb);
 -    }
 -
 -    if (port->bond) {
 -        struct iface *iface;
 -
 -        LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
 -            bool may_enable = lacp_slave_may_enable(port->lacp, iface);
 -            bond_slave_set_lacp_may_enable(port->bond, iface, may_enable);
 +            VLOG_ERR_RL(&rl, "bridge %s: not adding Unix domain socket "
 +                        "controller \"%s\" due to possibility for remote "
 +                        "exploit", br->name, c->target);
 +            continue;
          }
  
 -        bond_run(port->bond,
 -                 ofproto_get_revalidate_set(port->bridge->ofproto),
 -                 lacp_negotiated(port->lacp));
 -        if (bond_should_send_learning_packets(port->bond)) {
 -            port_send_learning_packets(port);
 +        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;
          }
 +        n_ocs++;
      }
 -}
  
 -static void
 -port_wait(struct port *port)
 -{
 -    if (port->lacp) {
 -        lacp_wait(port->lacp);
 -    }
 +    ofproto_set_controllers(br->ofproto, ocs, n_ocs);
 +    free(ocs[0].target); /* From bridge_ofproto_controller_for_mgmt(). */
 +    free(ocs);
 +
 +    /* Set the fail-mode. */
 +    fail_mode = !br->cfg->fail_mode
 +                || !strcmp(br->cfg->fail_mode, "standalone")
 +                    ? OFPROTO_FAIL_STANDALONE
 +                    : OFPROTO_FAIL_SECURE;
 +    ofproto_set_fail_mode(br->ofproto, fail_mode);
 +
 +    /* Configure OpenFlow controller connection snooping. */
 +    if (!ofproto_has_snoops(br->ofproto)) {
 +        struct sset snoops;
  
 -    if (port->bond) {
 -        bond_wait(port->bond);
 +        sset_init(&snoops);
 +        sset_add_and_free(&snoops, xasprintf("punix:%s/%s.snoop",
 +                                             ovs_rundir(), br->name));
 +        ofproto_set_snoops(br->ofproto, &snoops);
 +        sset_destroy(&snoops);
      }
  }
 +\f
 +/* Port functions. */
  
  static struct port *
 -port_create(struct bridge *br, const char *name)
 +port_create(struct bridge *br, const struct ovsrec_port *cfg)
  {
      struct port *port;
  
      port = xzalloc(sizeof *port);
      port->bridge = br;
 -    port->vlan = -1;
 -    port->trunks = NULL;
 -    port->name = xstrdup(name);
 +    port->name = xstrdup(cfg->name);
 +    port->cfg = cfg;
      list_init(&port->ifaces);
  
      hmap_insert(&br->ports, &port->hmap_node, hash_string(port->name, 0));
  
      VLOG_INFO("created port %s on bridge %s", port->name, br->name);
 -    bridge_flush(br);
  
      return port;
  }
@@@ -2009,9 -2899,8 +2009,9 @@@ get_interface_other_config(const struc
      return value ? value : default_value;
  }
  
 +/* Deletes interfaces from 'port' that are no longer configured for it. */
  static void
 -port_del_ifaces(struct port *port, const struct ovsrec_port *cfg)
 +port_del_ifaces(struct port *port)
  {
      struct iface *iface, *next;
      struct sset new_ifaces;
  
      /* Collect list of new interfaces. */
      sset_init(&new_ifaces);
 -    for (i = 0; i < cfg->n_interfaces; i++) {
 -        const char *name = cfg->interfaces[i]->name;
 -        sset_add(&new_ifaces, name);
 +    for (i = 0; i < port->cfg->n_interfaces; i++) {
 +        const char *name = port->cfg->interfaces[i]->name;
-         sset_add(&new_ifaces, name);
++        const char *type = port->cfg->interfaces[i]->name;
++        if (strcmp(type, "null")) {
++            sset_add(&new_ifaces, name);
++        }
      }
  
      /* Get rid of deleted interfaces. */
      sset_destroy(&new_ifaces);
  }
  
 -/* Expires all MAC learning entries associated with 'port' and forces ofproto
 - * to revalidate every flow. */
 +/* Adds new interfaces to 'port' and updates 'type' and 'cfg' members of
 + * existing ones. */
  static void
 -port_flush_macs(struct port *port)
 +port_add_ifaces(struct port *port)
  {
 -    struct bridge *br = port->bridge;
 -    struct mac_learning *ml = br->ml;
 -    struct mac_entry *mac, *next_mac;
 +    struct shash new_ifaces;
 +    struct shash_node *node;
 +    size_t i;
  
 -    bridge_flush(br);
 -    LIST_FOR_EACH_SAFE (mac, next_mac, lru_node, &ml->lrus) {
 -        if (mac->port.p == port) {
 -            mac_learning_expire(ml, mac);
 +    /* Collect new ifaces. */
 +    shash_init(&new_ifaces);
 +    for (i = 0; i < port->cfg->n_interfaces; i++) {
 +        const struct ovsrec_interface *cfg = port->cfg->interfaces[i];
-         if (!shash_add_once(&new_ifaces, cfg->name, cfg)) {
++        if (strcmp(cfg->type, "null")
++            && !shash_add_once(&new_ifaces, cfg->name, cfg)) {
 +            VLOG_WARN("port %s: %s specified twice as port interface",
 +                      port->name, cfg->name);
 +            iface_set_ofport(cfg, -1);
          }
      }
 -}
 -
 -static void
 -port_reconfigure(struct port *port, const struct ovsrec_port *cfg)
 -{
 -    struct sset new_ifaces;
 -    bool need_flush = false;
 -    unsigned long *trunks;
 -    int vlan;
 -    size_t i;
 -
 -    port->cfg = cfg;
  
 -    /* Add new interfaces and update 'cfg' member of existing ones. */
 -    sset_init(&new_ifaces);
 -    for (i = 0; i < cfg->n_interfaces; i++) {
 -        const struct ovsrec_interface *if_cfg = cfg->interfaces[i];
 +    /* Create new interfaces.
 +     * Update interface types and 'cfg' members. */
 +    SHASH_FOR_EACH (node, &new_ifaces) {
 +        const struct ovsrec_interface *cfg = node->data;
 +        const char *iface_name = node->name;
          struct iface *iface;
 -        const char *type;
  
 -        if (!sset_add(&new_ifaces, if_cfg->name)) {
 -            VLOG_WARN("port %s: %s specified twice as port interface",
 -                      port->name, if_cfg->name);
 -            iface_set_ofport(if_cfg, -1);
 -            continue;
 +        iface = iface_lookup(port->bridge, iface_name);
 +        if (!iface) {
 +            iface = iface_create(port, cfg);
 +        } else {
 +            iface->cfg = 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. */
 -        type = (!strcmp(if_cfg->name, port->bridge->name) ? "internal"
 -                : if_cfg->type[0] ? if_cfg->type
 -                : "system");
 -
 -        iface = iface_lookup(port->bridge, if_cfg->name);
 -        if (!strcmp(type, "null")) {
 -            iface_destroy(iface);
 -            continue;
 -        } else if (iface) {
 -            if (iface->port != port) {
 -                VLOG_ERR("bridge %s: %s interface is on multiple ports, "
 -                         "removing from %s",
 -                         port->bridge->name, if_cfg->name, iface->port->name);
 -                continue;
 -            }
 -            iface->cfg = if_cfg;
 -        } else {
 -            iface = iface_create(port, if_cfg);
 -        }
 -
 -        iface->type = type;
 -    }
 -    sset_destroy(&new_ifaces);
 -
 -    /* Get VLAN tag. */
 -    vlan = -1;
 -    if (cfg->tag) {
 -        if (list_is_short(&port->ifaces)) {
 -            vlan = *cfg->tag;
 -            if (vlan >= 0 && vlan <= 4095) {
 -                VLOG_DBG("port %s: assigning VLAN tag %d", port->name, vlan);
 -            } else {
 -                vlan = -1;
 -            }
 -        } else {
 -            /* It's possible that bonded, VLAN-tagged ports make sense.  Maybe
 -             * they even work as-is.  But they have not been tested. */
 -            VLOG_WARN("port %s: VLAN tags not supported on bonded ports",
 -                      port->name);
 -        }
 -    }
 -    if (port->vlan != vlan) {
 -        port->vlan = vlan;
 -        need_flush = true;
 -    }
 -
 -    /* Get trunked VLANs. */
 -    trunks = NULL;
 -    if (vlan < 0 && cfg->n_trunks) {
 -        size_t n_errors;
 -
 -        trunks = bitmap_allocate(4096);
 -        n_errors = 0;
 -        for (i = 0; i < cfg->n_trunks; i++) {
 -            int trunk = cfg->trunks[i];
 -            if (trunk >= 0) {
 -                bitmap_set1(trunks, trunk);
 -            } else {
 -                n_errors++;
 -            }
 -        }
 -        if (n_errors) {
 -            VLOG_ERR("port %s: invalid values for %zu trunk VLANs",
 -                     port->name, cfg->n_trunks);
 -        }
 -        if (n_errors == cfg->n_trunks) {
 -            VLOG_ERR("port %s: no valid trunks, trunking all VLANs",
 -                     port->name);
 -            bitmap_free(trunks);
 -            trunks = NULL;
 -        }
 -    } else if (vlan >= 0 && cfg->n_trunks) {
 -        VLOG_ERR("port %s: ignoring trunks in favor of implicit vlan",
 -                 port->name);
 -    }
 -    if (trunks == NULL
 -        ? port->trunks != NULL
 -        : port->trunks == NULL || !bitmap_equal(trunks, port->trunks, 4096)) {
 -        need_flush = true;
 -    }
 -    bitmap_free(port->trunks);
 -    port->trunks = trunks;
 -
 -    if (need_flush) {
 -        port_flush_macs(port);
 +        iface->type = (!strcmp(iface_name, port->bridge->name) ? "internal"
 +                       : cfg->type[0] ? cfg->type
 +                       : "system");
      }
 +    shash_destroy(&new_ifaces);
  }
  
  static void
@@@ -2084,9 -3062,13 +2088,9 @@@ port_destroy(struct port *port
      if (port) {
          struct bridge *br = port->bridge;
          struct iface *iface, *next;
 -        int i;
  
 -        for (i = 0; i < MAX_MIRRORS; i++) {
 -            struct mirror *m = br->mirrors[i];
 -            if (m && m->out_port == port) {
 -                mirror_destroy(m);
 -            }
 +        if (br->ofproto) {
 +            ofproto_bundle_unregister(br->ofproto, port);
          }
  
          LIST_FOR_EACH_SAFE (iface, next, port_elem, &port->ifaces) {
  
          VLOG_INFO("destroyed port %s on bridge %s", port->name, br->name);
  
 -        bond_destroy(port->bond);
 -        lacp_destroy(port->lacp);
 -        port_flush_macs(port);
 -
 -        bitmap_free(port->trunks);
          free(port->name);
          free(port);
      }
  }
  
 -static struct port *
 -port_from_dp_ifidx(const struct bridge *br, uint16_t dp_ifidx)
 -{
 -    struct iface *iface = iface_from_dp_ifidx(br, dp_ifidx);
 -    return iface ? iface->port : NULL;
 -}
 -
  static struct port *
  port_lookup(const struct bridge *br, const char *name)
  {
@@@ -2138,106 -3132,200 +2142,115 @@@ enable_lacp(struct port *port, bool *ac
      }
  }
  
 -static void
 -iface_reconfigure_lacp(struct iface *iface)
 -{
 -    struct lacp_slave_settings s;
 -    int priority, portid, key;
 -
 -    portid = atoi(get_interface_other_config(iface->cfg, "lacp-port-id", "0"));
 -    priority = atoi(get_interface_other_config(iface->cfg,
 -                                               "lacp-port-priority", "0"));
 -    key = atoi(get_interface_other_config(iface->cfg, "lacp-aggregation-key",
 -                                          "0"));
 -
 -    if (portid <= 0 || portid > UINT16_MAX) {
 -        portid = iface->dp_ifidx;
 -    }
 -
 -    if (priority <= 0 || priority > UINT16_MAX) {
 -        priority = UINT16_MAX;
 -    }
 -
 -    if (key < 0 || key > UINT16_MAX) {
 -        key = 0;
 -    }
 -
 -    s.name = iface->name;
 -    s.id = portid;
 -    s.priority = priority;
 -    s.key = key;
 -    lacp_slave_register(iface->port->lacp, iface, &s);
 -}
 -
 -static void
 -port_reconfigure_lacp(struct port *port)
 +static struct lacp_settings *
 +port_configure_lacp(struct port *port, struct lacp_settings *s)
  {
 -    static struct lacp_settings s;
 -    struct iface *iface;
 -    uint8_t sysid[ETH_ADDR_LEN];
 -    const char *sysid_str;
      const char *lacp_time;
      long long int custom_time;
      int priority;
  
 -    if (!enable_lacp(port, &s.active)) {
 -        lacp_destroy(port->lacp);
 -        port->lacp = NULL;
 -        return;
 -    }
 -
 -    sysid_str = get_port_other_config(port->cfg, "lacp-system-id", NULL);
 -    if (sysid_str && eth_addr_from_string(sysid_str, sysid)) {
 -        memcpy(s.id, sysid, ETH_ADDR_LEN);
 -    } else {
 -        memcpy(s.id, port->bridge->ea, ETH_ADDR_LEN);
 +    if (!enable_lacp(port, &s->active)) {
 +        return NULL;
      }
  
 -    s.name = port->name;
 +    s->name = port->name;
 +    memcpy(s->id, port->bridge->ea, ETH_ADDR_LEN);
  
      /* Prefer bondable links if unspecified. */
      priority = atoi(get_port_other_config(port->cfg, "lacp-system-priority",
                                            "0"));
 -    s.priority = (priority > 0 && priority <= UINT16_MAX
 -                  ? priority
 -                  : UINT16_MAX - !list_is_short(&port->ifaces));
 +    s->priority = (priority > 0 && priority <= UINT16_MAX
 +                   ? priority
 +                   : UINT16_MAX - !list_is_short(&port->ifaces));
 +
-     s->strict = !strcmp(get_port_other_config(port->cfg, "lacp-strict",
-                                               "false"),
-                         "true");
++    s->heartbeat = !strcmp(get_port_other_config(port->cfg,
++                                                 "lacp-heartbeat",
++                                                 "false"), "true");
 -    s.heartbeat = !strcmp(get_port_other_config(port->cfg,
 -                                                "lacp-heartbeat",
 -                                                "false"), "true");
  
      lacp_time = get_port_other_config(port->cfg, "lacp-time", "slow");
      custom_time = atoi(lacp_time);
      if (!strcmp(lacp_time, "fast")) {
 -        s.lacp_time = LACP_TIME_FAST;
 +        s->lacp_time = LACP_TIME_FAST;
      } else if (!strcmp(lacp_time, "slow")) {
 -        s.lacp_time = LACP_TIME_SLOW;
 +        s->lacp_time = LACP_TIME_SLOW;
      } else if (custom_time > 0) {
 -        s.lacp_time = LACP_TIME_CUSTOM;
 -        s.custom_time = custom_time;
 +        s->lacp_time = LACP_TIME_CUSTOM;
 +        s->custom_time = custom_time;
      } else {
 -        s.lacp_time = LACP_TIME_SLOW;
 +        s->lacp_time = LACP_TIME_SLOW;
      }
  
 -    if (!port->lacp) {
 -        port->lacp = lacp_create();
 +    return s;
 +}
 +
 +static void
 +iface_configure_lacp(struct iface *iface, struct lacp_slave_settings *s)
 +{
-     int priority, portid;
++    int priority, portid, key;
 +
 +    portid = atoi(get_interface_other_config(iface->cfg, "lacp-port-id", "0"));
 +    priority = atoi(get_interface_other_config(iface->cfg,
 +                                               "lacp-port-priority", "0"));
++    key = atoi(get_interface_other_config(iface->cfg, "lacp-aggregation-key",
++                                          "0"));
 +
 +    if (portid <= 0 || portid > UINT16_MAX) {
 +        portid = iface->ofp_port;
      }
  
 -    lacp_configure(port->lacp, &s);
 +    if (priority <= 0 || priority > UINT16_MAX) {
 +        priority = UINT16_MAX;
 +    }
  
 -    LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
 -        iface_reconfigure_lacp(iface);
++    if (key < 0 || key > UINT16_MAX) {
++        key = 0;
+     }
++
 +    s->name = iface->name;
 +    s->id = portid;
 +    s->priority = priority;
++    s->key = key;
  }
  
  static void
 -port_reconfigure_bond(struct port *port)
 +port_configure_bond(struct port *port, struct bond_settings *s)
  {
 -    struct bond_settings s;
      const char *detect_s;
 -    struct iface *iface;
  
 -    if (list_is_short(&port->ifaces)) {
 -        /* Not a bonded port. */
 -        bond_destroy(port->bond);
 -        port->bond = NULL;
 -        return;
 -    }
 -
 -    port->bridge->has_bonded_ports = true;
 -
 -    s.name = port->name;
 -    s.balance = BM_SLB;
 +    s->name = port->name;
 +    s->balance = BM_SLB;
      if (port->cfg->bond_mode
 -        && !bond_mode_from_string(&s.balance, port->cfg->bond_mode)) {
 +        && !bond_mode_from_string(&s->balance, port->cfg->bond_mode)) {
          VLOG_WARN("port %s: unknown bond_mode %s, defaulting to %s",
                    port->name, port->cfg->bond_mode,
 -                  bond_mode_to_string(s.balance));
 +                  bond_mode_to_string(s->balance));
      }
  
 -    s.detect = BLSM_CARRIER;
 +    s->detect = BLSM_CARRIER;
      detect_s = get_port_other_config(port->cfg, "bond-detect-mode", NULL);
 -    if (detect_s && !bond_detect_mode_from_string(&s.detect, detect_s)) {
 +    if (detect_s && !bond_detect_mode_from_string(&s->detect, detect_s)) {
          VLOG_WARN("port %s: unsupported bond-detect-mode %s, "
                    "defaulting to %s",
 -                  port->name, detect_s, bond_detect_mode_to_string(s.detect));
 +                  port->name, detect_s, bond_detect_mode_to_string(s->detect));
      }
  
 -    s.miimon_interval = atoi(
 +    s->miimon_interval = atoi(
          get_port_other_config(port->cfg, "bond-miimon-interval", "200"));
 -    if (s.miimon_interval < 100) {
 -        s.miimon_interval = 100;
 +    if (s->miimon_interval < 100) {
 +        s->miimon_interval = 100;
      }
  
 -    s.up_delay = MAX(0, port->cfg->bond_updelay);
 -    s.down_delay = MAX(0, port->cfg->bond_downdelay);
 -    s.basis = atoi(get_port_other_config(port->cfg, "bond-hash-basis", "0"));
 -
 -    s.rebalance_interval = atoi(
 +    s->up_delay = MAX(0, port->cfg->bond_updelay);
 +    s->down_delay = MAX(0, port->cfg->bond_downdelay);
++    s->basis = atoi(get_port_other_config(port->cfg, "bond-hash-basis", "0"));
 +    s->rebalance_interval = atoi(
          get_port_other_config(port->cfg, "bond-rebalance-interval", "10000"));
 -    if (s.rebalance_interval < 1000) {
 -        s.rebalance_interval = 1000;
 -    }
 -
 -    s.fake_iface = port->cfg->bond_fake_iface;
 -
 -    if (!port->bond) {
 -        port->bond = bond_create(&s);
 -    } else {
 -        if (bond_reconfigure(port->bond, &s)) {
 -            bridge_flush(port->bridge);
 -        }
 -    }
 -
 -    LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
 -        long long stable_id;
 -
 -        stable_id = atoll(get_interface_other_config(iface->cfg,
 -                                                     "bond-stable-id", "0"));
 -
 -        if (stable_id <= 0 || stable_id >= UINT32_MAX) {
 -            stable_id = odp_port_to_ofp_port(iface->dp_ifidx);
 -        }
 -
 -        bond_slave_register(iface->port->bond, iface, stable_id,
 -                            iface->netdev);
 -    }
 -}
 -
 -static void
 -port_send_learning_packets(struct port *port)
 -{
 -    struct bridge *br = port->bridge;
 -    int error, n_packets, n_errors;
 -    struct mac_entry *e;
 -
 -    error = n_packets = n_errors = 0;
 -    LIST_FOR_EACH (e, lru_node, &br->ml->lrus) {
 -        if (e->port.p != port) {
 -            int ret = bond_send_learning_packet(port->bond, e->mac, e->vlan);
 -            if (ret) {
 -                error = ret;
 -                n_errors++;
 -            }
 -            n_packets++;
 -        }
 +    if (s->rebalance_interval < 1000) {
 +        s->rebalance_interval = 1000;
      }
  
 -    if (n_errors) {
 -        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
 -        VLOG_WARN_RL(&rl, "bond %s: %d errors sending %d gratuitous learning "
 -                     "packets, last error was: %s",
 -                     port->name, n_errors, n_packets, strerror(error));
 -    } else {
 -        VLOG_DBG("bond %s: sent %d gratuitous learning packets",
 -                 port->name, n_packets);
 -    }
 +    s->fake_iface = port->cfg->bond_fake_iface;
  }
  \f
  /* Interface functions. */
@@@ -2252,17 -3340,19 +2265,17 @@@ iface_create(struct port *port, const s
      iface = xzalloc(sizeof *iface);
      iface->port = port;
      iface->name = xstrdup(name);
 -    iface->dp_ifidx = -1;
 +    iface->ofp_port = -1;
      iface->tag = tag_create_random();
      iface->netdev = NULL;
      iface->cfg = if_cfg;
  
 -    shash_add_assert(&br->iface_by_name, iface->name, iface);
 +    hmap_insert(&br->iface_by_name, &iface->name_node, hash_string(name, 0));
  
      list_push_back(&port->ifaces, &iface->port_elem);
  
      VLOG_DBG("attached network device %s to port %s", iface->name, port->name);
  
 -    bridge_flush(br);
 -
      return iface;
  }
  
@@@ -2273,37 -3363,35 +2286,37 @@@ iface_destroy(struct iface *iface
          struct port *port = iface->port;
          struct bridge *br = port->bridge;
  
 -        if (port->bond) {
 -            bond_slave_unregister(port->bond, iface);
 -        }
 -
 -        if (port->lacp) {
 -            lacp_slave_unregister(port->lacp, iface);
 +        if (br->ofproto && iface->ofp_port >= 0) {
 +            ofproto_port_unregister(br->ofproto, iface->ofp_port);
          }
  
 -        shash_find_and_delete_assert(&br->iface_by_name, iface->name);
 -
 -        if (iface->dp_ifidx >= 0) {
 -            hmap_remove(&br->ifaces, &iface->dp_ifidx_node);
 +        if (iface->ofp_port >= 0) {
 +            hmap_remove(&br->ifaces, &iface->ofp_port_node);
          }
  
          list_remove(&iface->port_elem);
 +        hmap_remove(&br->iface_by_name, &iface->name_node);
  
          netdev_close(iface->netdev);
  
          free(iface->name);
          free(iface);
 -
 -        bridge_flush(port->bridge);
      }
  }
  
  static struct iface *
  iface_lookup(const struct bridge *br, const char *name)
  {
 -    return shash_find_data(&br->iface_by_name, name);
 +    struct iface *iface;
 +
 +    HMAP_FOR_EACH_WITH_HASH (iface, name_node, hash_string(name, 0),
 +                             &br->iface_by_name) {
 +        if (!strcmp(iface->name, name)) {
 +            return iface;
 +        }
 +    }
 +
 +    return NULL;
  }
  
  static struct iface *
@@@ -2311,7 -3399,7 +2324,7 @@@ iface_find(const char *name
  {
      const struct bridge *br;
  
 -    LIST_FOR_EACH (br, node, &all_bridges) {
 +    HMAP_FOR_EACH (br, node, &all_bridges) {
          struct iface *iface = iface_lookup(br, name);
  
          if (iface) {
  }
  
  static struct iface *
 -iface_from_dp_ifidx(const struct bridge *br, uint16_t dp_ifidx)
 +iface_from_ofp_port(const struct bridge *br, uint16_t ofp_port)
  {
      struct iface *iface;
  
 -    HMAP_FOR_EACH_IN_BUCKET (iface, dp_ifidx_node,
 -                             hash_int(dp_ifidx, 0), &br->ifaces) {
 -        if (iface->dp_ifidx == dp_ifidx) {
 +    HMAP_FOR_EACH_IN_BUCKET (iface, ofp_port_node,
 +                             hash_int(ofp_port, 0), &br->ifaces) {
 +        if (iface->ofp_port == ofp_port) {
              return iface;
          }
      }
@@@ -2344,7 -3432,7 +2357,7 @@@ iface_set_mac(struct iface *iface
  
      if (!strcmp(iface->type, "internal")
          && iface->cfg->mac && eth_addr_from_string(iface->cfg->mac, ea)) {
 -        if (iface->dp_ifidx == ODPP_LOCAL) {
 +        if (iface->ofp_port == OFPP_LOCAL) {
              VLOG_ERR("interface %s: ignoring mac in Interface record "
                       "(use Bridge record to set local port's mac)",
                       iface->name);
@@@ -2444,9 -3532,9 +2457,9 @@@ iface_delete_queues(unsigned int queue_
  }
  
  static void
 -iface_update_qos(struct iface *iface, const struct ovsrec_qos *qos)
 +iface_configure_qos(struct iface *iface, const struct ovsrec_qos *qos)
  {
-     if (!qos || qos->type[0] == '\0') {
+     if (!qos || qos->type[0] == '\0' || qos->n_queues < 1) {
          netdev_set_qos(iface->netdev, NULL, NULL);
      } else {
          struct iface_delete_queues_cbdata cbdata;
              shash_destroy(&details);
          }
      }
 +
 +    netdev_set_policing(iface->netdev,
 +                        iface->cfg->ingress_policing_rate,
 +                        iface->cfg->ingress_policing_burst);
  }
  
  static void
 -iface_update_cfm(struct iface *iface)
 +iface_configure_cfm(struct iface *iface)
  {
      size_t i;
      struct cfm cfm;
      mon = iface->cfg->monitor;
  
      if (!mon) {
 -        ofproto_iface_clear_cfm(iface->port->bridge->ofproto, iface->dp_ifidx);
 +        ofproto_port_clear_cfm(iface->port->bridge->ofproto, iface->ofp_port);
          return;
      }
  
          remote_mps[i] = mon->remote_mps[i]->mpid;
      }
  
 -    ofproto_iface_set_cfm(iface->port->bridge->ofproto, iface->dp_ifidx,
 -                          &cfm, remote_mps, mon->n_remote_mps);
 +    ofproto_port_set_cfm(iface->port->bridge->ofproto, iface->ofp_port,
 +                         &cfm, remote_mps, mon->n_remote_mps);
      free(remote_mps);
  }
  
@@@ -2543,10 -3627,11 +2556,10 @@@ iface_is_synthetic(const struct iface *
  static struct mirror *
  mirror_find_by_uuid(struct bridge *br, const struct uuid *uuid)
  {
 -    int i;
 +    struct mirror *m;
  
 -    for (i = 0; i < MAX_MIRRORS; i++) {
 -        struct mirror *m = br->mirrors[i];
 -        if (m && uuid_equals(uuid, &m->uuid)) {
 +    HMAP_FOR_EACH_IN_BUCKET (m, hmap_node, uuid_hash(uuid), &br->mirrors) {
 +        if (uuid_equals(uuid, &m->uuid)) {
              return m;
          }
      }
  }
  
  static void
 -mirror_reconfigure(struct bridge *br)
 +bridge_configure_mirrors(struct bridge *br)
  {
 -    unsigned long *rspan_vlans;
 -    struct port *port;
 -    int i;
 +    const struct ovsdb_datum *mc;
 +    unsigned long *flood_vlans;
 +    struct mirror *m, *next;
 +    size_t i;
  
      /* Get rid of deleted mirrors. */
 -    for (i = 0; i < MAX_MIRRORS; i++) {
 -        struct mirror *m = br->mirrors[i];
 -        if (m) {
 -            const struct ovsdb_datum *mc;
 -            union ovsdb_atom atom;
 -
 -            mc = ovsrec_bridge_get_mirrors(br->cfg, OVSDB_TYPE_UUID);
 -            atom.uuid = br->mirrors[i]->uuid;
 -            if (ovsdb_datum_find_key(mc, &atom, OVSDB_TYPE_UUID) == UINT_MAX) {
 -                mirror_destroy(m);
 -            }
 +    mc = ovsrec_bridge_get_mirrors(br->cfg, OVSDB_TYPE_UUID);
 +    HMAP_FOR_EACH_SAFE (m, next, hmap_node, &br->mirrors) {
 +        union ovsdb_atom atom;
 +
 +        atom.uuid = m->uuid;
 +        if (ovsdb_datum_find_key(mc, &atom, OVSDB_TYPE_UUID) == UINT_MAX) {
 +            mirror_destroy(m);
          }
      }
  
      /* Add new mirrors and reconfigure existing ones. */
      for (i = 0; i < br->cfg->n_mirrors; i++) {
 -        struct ovsrec_mirror *cfg = br->cfg->mirrors[i];
 +        const struct ovsrec_mirror *cfg = br->cfg->mirrors[i];
          struct mirror *m = mirror_find_by_uuid(br, &cfg->header_.uuid);
 -        if (m) {
 -            mirror_reconfigure_one(m, cfg);
 -        } else {
 -            mirror_create(br, cfg);
 +        if (!m) {
 +            m = mirror_create(br, cfg);
          }
 -    }
 -
 -    /* Update port reserved status. */
 -    HMAP_FOR_EACH (port, hmap_node, &br->ports) {
 -        port->is_mirror_output_port = false;
 -    }
 -    for (i = 0; i < MAX_MIRRORS; i++) {
 -        struct mirror *m = br->mirrors[i];
 -        if (m && m->out_port) {
 -            m->out_port->is_mirror_output_port = true;
 +        if (!mirror_configure(m, cfg)) {
 +            mirror_destroy(m);
          }
      }
  
      /* Update flooded vlans (for RSPAN). */
 -    rspan_vlans = NULL;
 -    if (br->cfg->n_flood_vlans) {
 -        rspan_vlans = bitmap_allocate(4096);
 -
 -        for (i = 0; i < br->cfg->n_flood_vlans; i++) {
 -            int64_t vlan = br->cfg->flood_vlans[i];
 -            if (vlan >= 0 && vlan < 4096) {
 -                bitmap_set1(rspan_vlans, vlan);
 -                VLOG_INFO("bridge %s: disabling learning on vlan %"PRId64,
 -                          br->name, vlan);
 -            } else {
 -                VLOG_ERR("bridge %s: invalid value %"PRId64 "for flood VLAN",
 -                         br->name, vlan);
 -            }
 -        }
 -    }
 -    if (mac_learning_set_flood_vlans(br->ml, rspan_vlans)) {
 -        bridge_flush(br);
 -        mac_learning_flush(br->ml);
 -    }
 +    flood_vlans = vlan_bitmap_from_array(br->cfg->flood_vlans,
 +                                         br->cfg->n_flood_vlans);
 +    ofproto_set_flood_vlans(br->ofproto, flood_vlans);
 +    bitmap_free(flood_vlans);
  }
  
 -static void
 -mirror_create(struct bridge *br, struct ovsrec_mirror *cfg)
 +static struct mirror *
 +mirror_create(struct bridge *br, const struct ovsrec_mirror *cfg)
  {
      struct mirror *m;
 -    size_t i;
  
 -    for (i = 0; ; i++) {
 -        if (i >= MAX_MIRRORS) {
 -            VLOG_WARN("bridge %s: maximum of %d port mirrors reached, "
 -                      "cannot create %s", br->name, MAX_MIRRORS, cfg->name);
 -            return;
 -        }
 -        if (!br->mirrors[i]) {
 -            break;
 -        }
 -    }
 -
 -    VLOG_INFO("created port mirror %s on bridge %s", cfg->name, br->name);
 -    bridge_flush(br);
 -    mac_learning_flush(br->ml);
 -
 -    br->mirrors[i] = m = xzalloc(sizeof *m);
 +    m = xzalloc(sizeof *m);
      m->uuid = cfg->header_.uuid;
 +    hmap_insert(&br->mirrors, &m->hmap_node, uuid_hash(&m->uuid));
      m->bridge = br;
 -    m->idx = i;
      m->name = xstrdup(cfg->name);
 -    sset_init(&m->src_ports);
 -    sset_init(&m->dst_ports);
 -    m->vlans = NULL;
 -    m->n_vlans = 0;
 -    m->out_vlan = -1;
 -    m->out_port = NULL;
  
 -    mirror_reconfigure_one(m, cfg);
 +    return m;
  }
  
  static void
@@@ -2610,61 -3746,104 +2623,61 @@@ mirror_destroy(struct mirror *m
  {
      if (m) {
          struct bridge *br = m->bridge;
 -        struct port *port;
  
 -        HMAP_FOR_EACH (port, hmap_node, &br->ports) {
 -            port->src_mirrors &= ~(MIRROR_MASK_C(1) << m->idx);
 -            port->dst_mirrors &= ~(MIRROR_MASK_C(1) << m->idx);
 +        if (br->ofproto) {
 +            ofproto_mirror_unregister(br->ofproto, m);
          }
  
 -        sset_destroy(&m->src_ports);
 -        sset_destroy(&m->dst_ports);
 -        free(m->vlans);
 -
 -        m->bridge->mirrors[m->idx] = NULL;
 +        hmap_remove(&br->mirrors, &m->hmap_node);
          free(m->name);
          free(m);
 -
 -        bridge_flush(br);
 -        mac_learning_flush(br->ml);
      }
  }
  
  static void
 -mirror_collect_ports(struct mirror *m, struct ovsrec_port **ports, int n_ports,
 -                     struct sset *names)
 +mirror_collect_ports(struct mirror *m,
 +                     struct ovsrec_port **in_ports, int n_in_ports,
 +                     void ***out_portsp, size_t *n_out_portsp)
  {
 +    void **out_ports = xmalloc(n_in_ports * sizeof *out_ports);
 +    size_t n_out_ports = 0;
      size_t i;
  
 -    for (i = 0; i < n_ports; i++) {
 -        const char *name = ports[i]->name;
 -        if (port_lookup(m->bridge, name)) {
 -            sset_add(names, name);
 +    for (i = 0; i < n_in_ports; i++) {
 +        const char *name = in_ports[i]->name;
 +        struct port *port = port_lookup(m->bridge, name);
 +        if (port) {
 +            out_ports[n_out_ports++] = port;
          } else {
              VLOG_WARN("bridge %s: mirror %s cannot match on nonexistent "
                        "port %s", m->bridge->name, m->name, name);
          }
      }
 -}
 -
 -static size_t
 -mirror_collect_vlans(struct mirror *m, const struct ovsrec_mirror *cfg,
 -                     int **vlans)
 -{
 -    size_t n_vlans;
 -    size_t i;
 -
 -    *vlans = xmalloc(sizeof **vlans * cfg->n_select_vlan);
 -    n_vlans = 0;
 -    for (i = 0; i < cfg->n_select_vlan; i++) {
 -        int64_t vlan = cfg->select_vlan[i];
 -        if (vlan < 0 || vlan > 4095) {
 -            VLOG_WARN("bridge %s: mirror %s selects invalid VLAN %"PRId64,
 -                      m->bridge->name, m->name, vlan);
 -        } else {
 -            (*vlans)[n_vlans++] = vlan;
 -        }
 -    }
 -    return n_vlans;
 +    *out_portsp = out_ports;
 +    *n_out_portsp = n_out_ports;
  }
  
  static bool
 -vlan_is_mirrored(const struct mirror *m, int vlan)
 -{
 -    size_t i;
 -
 -    for (i = 0; i < m->n_vlans; i++) {
 -        if (m->vlans[i] == vlan) {
 -            return true;
 -        }
 -    }
 -    return false;
 -}
 -
 -static void
 -mirror_reconfigure_one(struct mirror *m, struct ovsrec_mirror *cfg)
 +mirror_configure(struct mirror *m, const struct ovsrec_mirror *cfg)
  {
 -    struct sset src_ports, dst_ports;
 -    mirror_mask_t mirror_bit;
 -    struct port *out_port;
 -    struct port *port;
 -    int out_vlan;
 -    size_t n_vlans;
 -    int *vlans;
 +    struct ofproto_mirror_settings s;
  
      /* Set name. */
      if (strcmp(cfg->name, m->name)) {
          free(m->name);
          m->name = xstrdup(cfg->name);
      }
 +    s.name = m->name;
  
 -    /* Get output port. */
 +    /* Get output port or VLAN. */
      if (cfg->output_port) {
 -        out_port = port_lookup(m->bridge, cfg->output_port->name);
 -        if (!out_port) {
 +        s.out_bundle = port_lookup(m->bridge, cfg->output_port->name);
 +        if (!s.out_bundle) {
              VLOG_ERR("bridge %s: mirror %s outputs to port not on bridge",
                       m->bridge->name, m->name);
 -            mirror_destroy(m);
 -            return;
 +            return false;
          }
 -        out_vlan = -1;
 +        s.out_vlan = UINT16_MAX;
  
          if (cfg->output_vlan) {
              VLOG_ERR("bridge %s: mirror %s specifies both output port and "
                       m->bridge->name, m->name);
          }
      } else if (cfg->output_vlan) {
 -        out_port = NULL;
 -        out_vlan = *cfg->output_vlan;
 +        /* The database should prevent invalid VLAN values. */
 +        s.out_bundle = NULL;
 +        s.out_vlan = *cfg->output_vlan;
      } else {
          VLOG_ERR("bridge %s: mirror %s does not specify output; ignoring",
                   m->bridge->name, m->name);
 -        mirror_destroy(m);
 -        return;
 +        return false;
      }
  
 -    sset_init(&src_ports);
 -    sset_init(&dst_ports);
 +    /* Get port selection. */
      if (cfg->select_all) {
 +        size_t n_ports = hmap_count(&m->bridge->ports);
 +        void **ports = xmalloc(n_ports * sizeof *ports);
 +        struct port *port;
 +        size_t i;
 +
 +        i = 0;
          HMAP_FOR_EACH (port, hmap_node, &m->bridge->ports) {
 -            sset_add(&src_ports, port->name);
 -            sset_add(&dst_ports, port->name);
 +            ports[i++] = port;
          }
 -        vlans = NULL;
 -        n_vlans = 0;
 +
 +        s.srcs = ports;
 +        s.n_srcs = n_ports;
 +
 +        s.dsts = ports;
 +        s.n_dsts = n_ports;
      } else {
 -        /* Get ports, and drop duplicates and ports that don't exist. */
 +        /* Get ports, dropping ports that don't exist.
 +         * The IDL ensures that there are no duplicates. */
          mirror_collect_ports(m, cfg->select_src_port, cfg->n_select_src_port,
 -                             &src_ports);
 +                             &s.srcs, &s.n_srcs);
          mirror_collect_ports(m, cfg->select_dst_port, cfg->n_select_dst_port,
 -                             &dst_ports);
 -
 -        /* Get all the vlans, and drop duplicate and invalid vlans. */
 -        n_vlans = mirror_collect_vlans(m, cfg, &vlans);
 -    }
 -
 -    /* Update mirror data. */
 -    if (!sset_equals(&m->src_ports, &src_ports)
 -        || !sset_equals(&m->dst_ports, &dst_ports)
 -        || m->n_vlans != n_vlans
 -        || memcmp(m->vlans, vlans, sizeof *vlans * n_vlans)
 -        || m->out_port != out_port
 -        || m->out_vlan != out_vlan) {
 -        bridge_flush(m->bridge);
 -        mac_learning_flush(m->bridge->ml);
 -    }
 -    sset_swap(&m->src_ports, &src_ports);
 -    sset_swap(&m->dst_ports, &dst_ports);
 -    free(m->vlans);
 -    m->vlans = vlans;
 -    m->n_vlans = n_vlans;
 -    m->out_port = out_port;
 -    m->out_vlan = out_vlan;
 -
 -    /* Update ports. */
 -    mirror_bit = MIRROR_MASK_C(1) << m->idx;
 -    HMAP_FOR_EACH (port, hmap_node, &m->bridge->ports) {
 -        if (sset_contains(&m->src_ports, port->name)) {
 -            port->src_mirrors |= mirror_bit;
 -        } else {
 -            port->src_mirrors &= ~mirror_bit;
 -        }
 +                             &s.dsts, &s.n_dsts);
  
 -        if (sset_contains(&m->dst_ports, port->name)) {
 -            port->dst_mirrors |= mirror_bit;
 -        } else {
 -            port->dst_mirrors &= ~mirror_bit;
 -        }
      }
  
 +    /* Get VLAN selection. */
 +    s.src_vlans = vlan_bitmap_from_array(cfg->select_vlan, cfg->n_select_vlan);
 +
 +    /* Configure. */
 +    ofproto_mirror_register(m->bridge->ofproto, m, &s);
 +
      /* Clean up. */
 -    sset_destroy(&src_ports);
 -    sset_destroy(&dst_ports);
 +    if (s.srcs != s.dsts) {
 +        free(s.dsts);
 +    }
 +    free(s.srcs);
 +    free(s.src_vlans);
 +
 +    return true;
  }
diff --combined vswitchd/ovs-vswitchd.c
@@@ -30,6 -30,7 +30,6 @@@
  #include "command-line.h"
  #include "compiler.h"
  #include "daemon.h"
 -#include "dpif.h"
  #include "dummy.h"
  #include "leak-checker.h"
  #include "netdev.h"
@@@ -89,11 -90,13 +89,11 @@@ main(int argc, char *argv[]
          }
          bridge_run();
          unixctl_server_run(unixctl);
 -        dp_run();
          netdev_run();
  
          signal_wait(sighup);
          bridge_wait();
          unixctl_server_wait(unixctl);
 -        dp_wait();
          netdev_wait();
          if (exiting) {
              poll_immediate_wake();
@@@ -125,11 -128,9 +125,9 @@@ parse_options(int argc, char *argv[]
          DAEMON_LONG_OPTIONS,
          VLOG_LONG_OPTIONS,
          LEAK_CHECKER_LONG_OPTIONS,
- #ifdef HAVE_OPENSSL
-         STREAM_SSL_LONG_OPTIONS
+         STREAM_SSL_LONG_OPTIONS,
          {"peer-ca-cert", required_argument, 0, OPT_PEER_CA_CERT},
          {"bootstrap-ca-cert", required_argument, 0, OPT_BOOTSTRAP_CA_CERT},
- #endif
          {"enable-dummy", no_argument, 0, OPT_ENABLE_DUMMY},
          {0, 0, 0, 0},
      };
          VLOG_OPTION_HANDLERS
          DAEMON_OPTION_HANDLERS
          LEAK_CHECKER_OPTION_HANDLERS
- #ifdef HAVE_OPENSSL
          STREAM_SSL_OPTION_HANDLERS
  
          case OPT_PEER_CA_CERT:
          case OPT_BOOTSTRAP_CA_CERT:
              stream_ssl_set_ca_cert_file(optarg, true);
              break;
- #endif
  
          case OPT_ENABLE_DUMMY:
              dummy_enable();