Merge 'next' into 'master'.
authorBen Pfaff <blp@nicira.com>
Wed, 18 May 2011 21:01:13 +0000 (14:01 -0700)
committerBen Pfaff <blp@nicira.com>
Wed, 18 May 2011 21:01:13 +0000 (14:01 -0700)
I know already that this breaks the statsfixes that were implemented by the
following commits:

827ab71c97f "ofproto: Datapath statistics accounted twice."
6f1435fc8f7 "ofproto: Resubmit statistics improperly account during..."

These were already broken in a previous merge.  I will work on a fix.

53 files changed:
PORTING
include/openflow/nicira-ext.h
lib/automake.mk
lib/bitmap.h
lib/bond.c
lib/bond.h
lib/classifier.c
lib/classifier.h
lib/dpif-linux.c
lib/dpif-netdev.c
lib/dpif-provider.h
lib/dpif.c
lib/dpif.h
lib/flow.c
lib/flow.h
lib/hmapx.c [new file with mode: 0644]
lib/hmapx.h [new file with mode: 0644]
lib/learning-switch.c
lib/mac-learning.c
lib/mac-learning.h
lib/netdev-linux.c
lib/nx-match.c
lib/odp-util.c
lib/ofp-parse.c
lib/ofp-parse.h
lib/ofp-print.c
lib/ofp-util.c
lib/ofp-util.h
lib/vlan-bitmap.c [new file with mode: 0644]
lib/vlan-bitmap.h [new file with mode: 0644]
ofproto/automake.mk
ofproto/connmgr.c
ofproto/connmgr.h
ofproto/fail-open.c
ofproto/in-band.c
ofproto/names.c [new file with mode: 0644]
ofproto/ofproto-dpif.c [new file with mode: 0644]
ofproto/ofproto.c
ofproto/ofproto.h
ofproto/pinsched.c
ofproto/pktbuf.c
ofproto/private.h [new file with mode: 0644]
tests/ofproto-macros.at
tests/ofproto.at
tests/ovs-ofctl.at
tests/test-classifier.c
utilities/automake.mk
utilities/ovs-controller.c
utilities/ovs-ofctl.8.in
utilities/ovs-ofctl.c
utilities/ovs-openflowd.c
vswitchd/bridge.c
vswitchd/ovs-vswitchd.c

diff --git a/PORTING b/PORTING
index 911761e..09458ee 100644 (file)
--- a/PORTING
+++ b/PORTING
@@ -7,167 +7,85 @@ are most likely to be necessary in porting OVS to Unix-like platforms.
 (Porting OVS to other kinds of platforms is likely to be more
 difficult.)
 
-Open vSwitch Architectural Overview
------------------------------------
 
-The following diagram shows the conceptual architecture of Open
-vSwitch from a porter's perspective.
-                _                         _
-               |   +-------------------+   |
-               |   |   ovs-vswitchd    |   |Generic
-               |   +-------------------+   |code
-     userspace |   |      ofproto      |  _|
-               |   +---------+---------+  _
-               |   | netdev  |dpif/wdp |   |
-               |_  +---||----+----||---+   |Code that
-                _      ||         ||       |may need
-               |   +---||-----+---||---+   |porting
-               |   |          |datapath|  _|
-        kernel |   |          +--------+
-               |   |                   |
-               |_  +-------||----------+
-                           ||
-                        physical
-                          NIC
-
-Some of the components are generic.  Modulo bugs, these components
-should not need to be modified as part of a port:
-
-    - Near the top of the diagram, "ofproto" is the library in Open vSwitch
-      that contains the core OpenFlow protocol implementation and switching
-      functionality.  It is built from source files in the "ofproto"
-      directory.
-
-    - Above ofproto, "ovs-vswitchd", the main Open vSwitch userspace
-      program, is the primary client for ofproto.  It is built
-      from source files in the "vswitchd" directory of the Open
-      vSwitch distribution.
-
-      ovs-vswitchd is the most sophisticated of ofproto's clients, but
-      ofproto can have other clients as well.  Notably, ovs-openflowd,
-      in the utilities directory, is much simpler (though less
-      capable) than ovs-vswitchd, and it may be easier to get up and
-      running as part of a port.
-
-The other components require attention during a port:
-
-    - "dpif" or "wdp" is what ofproto uses to directly monitor and
-      control a "datapath", which is the term used in OVS for a
-      collection of physical or virtual ports that are exposed over
-      OpenFlow as a single switch.  A datapath implements a flow
-      table.
-
-    - "netdev" is the interface to "network devices", e.g. eth0 on
-      Linux.  ofproto expects that every port exposed by a datapath
-      has a corresponding netdev that it can open with netdev_open().
+Vocabulary
+----------
 
-The following sections talk about these components in more detail.
+For historical reasons, different words are used for essentially the
+same concept in different areas of the Open vSwitch source tree.  Here
+is a concordance, indexed by the area of the source tree:
 
-Which Branch?
--------------
-
-The architectural diagram shows "dpif" and "wdp" as alternatives.
-These alternatives correspond to the "master" and "wdp" branches,
-respectively, of the Open vSwitch Git repository at
-git://openvswitch.org/openvswitch.  Both of these branches currently
-represent reasonable porting targets for different purposes:
-
-    - The "master" branch is more mature and better tested.  Open
-      vSwitch releases are made from this branch, and most OVS
-      development and testing occurs on this branch.
-
-    - The "wdp" branch has a software architecture that can take
-      advantage of hardware with support for wildcards (e.g. TCAMs or
-      similar).  This branch has known important bugs, but is the basis
-      of a few ongoing hardware projects, so we expect the quality to
-      improve rapidly.
-
-Since its architecture is better, in the medium to long term we will
-fix the problems in the "wdp" branch and merge it into "master".
-
-In porting OVS, the major difference between the two branches is the
-form of the flow table in the datapath:
-
-    - On "master", the "dpif" datapath interface maintains a simple
-      flow table, one that does not support any kind of wildcards.
-      This flow table essentially acts as a cache.  When a packet
-      arrives on an interface, the datapath looks for it in this
-      exact-match table.  If there is a match, then it performs the
-      associated actions.  If there is no match, the datapath passes
-      the packet up to "ofproto", which maintains a flow table that
-      supports wildcards.  If the packet matches in this flow table,
-      then ofproto executes its actions and inserts a new exact-match
-      entry into the dpif flow table.  (Otherwise, ofproto sends the
-      packet to the OpenFlow controller, if one is configured.)
-
-      Thus, on the "master" branch, the datapath has little
-      opportunity to take advantage of hardware support for wildcards,
-      since it is only ever presented with exact-match flow entries.
-
-    - On "wdp", the "wdp" datapath interface maintains a flow table
-      similar to that of OpenFlow, one that supports wildcards.  Thus,
-      a wdp datapath can take advantage of hardware support for
-      wildcards, since it is free to implement the flow table any way
-      it likes.
-      
-The following sections describe the two datapath interfaces in a
-little more detail.
-
-dpif: The "master" Branch Datapath
-----------------------------------
-
-struct dpif_class, in lib/dpif-provider.h, defines the
-interfaces required to implement a dpif for new hardware or
-software.  That structure contains many function pointers, each
-of which has a comment that is meant to describe its behavior in
-detail.  If the requirements are unclear, please report this as
-a bug and we will clarify.
+        datapath/       vport           ---
+        vswitchd/       iface           port
+        ofproto/        port            bundle
+        lib/bond.c      slave           bond
+        lib/lacp.c      slave           lacp
+        lib/netdev.c    netdev          ---
+        database        Interface       Port
 
-There are two existing dpif implementations that may serve as
-useful examples during a port:
 
-    * lib/dpif-linux.c is a Linux-specific dpif implementation that
-      talks to an Open vSwitch-specific kernel module (whose sources
-      are in the "datapath" directory).  The kernel module performs
-      all of the switching work, passing packets that do not match any
-      flow table entry up to userspace.  This dpif implementation is
-      essentially a wrapper around calls to "ioctl".
+Open vSwitch Architectural Overview
+-----------------------------------
 
-    * lib/dpif-netdev.c is a generic dpif implementation that performs
-      all switching internally.  It delegates most of its work to the
-      "netdev" library (described below).  Using dpif-netdev, instead
-      of writing a new dpif, can be a simple way to get OVS up and
-      running on new platforms, but other solutions are likely to
-      yield higher performance.
+The following diagram shows the very high-level architecture of Open
+vSwitch from a porter's perspective.
 
-"wdp": The "wdp" Branch Datapath
---------------------------------
+                   +-------------------+
+                   |    ovs-vswitchd   |<-->ovsdb-server
+                   +-------------------+
+                   |      ofproto      |<-->OpenFlow controllers
+                   +--------+-+--------+
+                   | netdev | | ofproto|
+                   +--------+ |provider|
+                   | netdev | +--------+
+                   |provider|
+                   +--------+
+
+Some of the components are generic.  Modulo bugs or inadequacies,
+these components should not need to be modified as part of a port:
+
+    - "ovs-vswitchd" is the main Open vSwitch userspace program, in
+      vswitchd/.  It reads the desired Open vSwitch configuration from
+      the ovsdb-server program over an IPC channel and passes this
+      configuration down to the "ofproto" library.  It also passes
+      certain status and statistical information from ofproto back
+      into the database.
+
+    - "ofproto" is the Open vSwitch library, in ofproto/, that
+      implements an OpenFlow switch.  It talks to OpenFlow controllers
+      over the network and to switch hardware or software to an
+      "ofproto provider", explained further below.
+
+    - "netdev" is the Open vSwitch library, in lib/netdev.c, that
+      abstracts interacting with network devices, that is, Ethernet
+      interfaces.  The netdev library is a thin layer over "netdev
+      provider" code, explained further below.
+
+The other components may need attention during a port.  You will
+almost certainly have to implement a "netdev provider".  Depending on
+the type of port you are doing and the desired performance, you may
+also have to implement an "ofproto provider" or a lower-level
+component called a "dpif" provider.
 
-struct wdp_class, in ofproto/wdp-provider.h, defines the interfaces
-required to implement a wdp ("wildcarded datapath") for new hardware
-or software.  That structure contains many function pointers, each of
-which has a comment that is meant to describe its behavior in detail.
-If the requirements are unclear, please report this as a bug and we
-will clarify.
+The following sections talk about these components in more detail.
 
-The wdp interface is preliminary.  Please let us know if it seems
-unsuitable for your purpose.  We will try to improve it.
 
-There is currently only one wdp implementation:
+Writing a netdev Provider
+-------------------------
 
-    * ofproto/wdp-xflow.c is an adaptation of "master" branch code
-      that breaks wildcarded flows up into exact-match flows in the
-      same way that ofproto always does on the "master" branch.  It
-      delegates its work to exact-match datapath implementations whose
-      interfaces are identical to "master" branch datapaths, except
-      that names have been changed from "dpif" to "xfif" ("exact-match
-      flow interface") and similar.
+A "netdev provider" implements an operating system and hardware
+specific interface to "network devices", e.g. eth0 on Linux.  Open
+vSwitch must be able to open each port on a switch as a netdev, so you
+will need to implement a "netdev provider" that works with your switch
+hardware and software.
 
-"netdev": Interface to network devices
---------------------------------------
+struct netdev_class, in lib/netdev-provider.h, defines the interfaces
+required to implement a netdev.  That structure contains many function
+pointers, each of which has a comment that is meant to describe its
+behavior in detail.  If the requirements are unclear, please report
+this as a bug.
 
-The netdev interface can be roughly divided into functionality for the
-following purposes:
+The netdev interface can be divided into a few rough categories:
 
     * Functions required to properly implement OpenFlow features.  For
       example, OpenFlow requires the ability to report the Ethernet
@@ -180,15 +98,9 @@ following purposes:
       table.  These functions must be implemented if the corresponding
       OVS features are to work, but may be omitted initially.
 
-    * Functions that may be needed in some implementations but not
-      others.  The dpif-netdev described above, for example, needs to
-      be able to send and receive packets on a netdev.
-
-struct netdev_class, in lib/netdev-provider.h, defines the interfaces
-required to implement a netdev.  That structure contains many function
-pointers, each of which has a comment that is meant to describe its
-behavior in detail.  If the requirements are unclear, please report
-this as a bug and we will clarify.
+    * Functions needed in some implementations but not in others.  For
+      example, most kinds of ports (see below) do not need
+      functionality to receive packets from a network device.
 
 The existing netdev implementations may serve as useful examples
 during a port:
@@ -197,14 +109,143 @@ during a port:
       network devices, using Linux kernel calls.  It may be a good
       place to start for full-featured netdev implementations.
 
-    * lib/netdev-vport.c provides support for "virtual ports" 
+    * lib/netdev-vport.c provides support for "virtual ports"
       implemented by the Open vSwitch datapath module for the Linux
       kernel.  This may serve as a model for minimal netdev
       implementations.
 
+    * lib/netdev-dummy.c is a fake netdev implementation useful only
+      for testing.
+
+
+Porting Strategies
+------------------
+
+After a netdev provider has been implemented for a system's network
+devices, you may choose among three basic porting strategies.
+
+The lowest-effort strategy is to use the "userspace switch"
+implementation built into Open vSwitch.  This ought to work, without
+writing any more code, as long as the netdev provider that you
+implemented supports receiving packets.  It yields poor performance,
+however, because every packet passes through the ovs-vswitchd process.
+See INSTALL.userspace for instructions on how to configure a userspace
+switch.
+
+If the userspace switch is not the right choice for your port, then
+you will have to write more code.  You may implement either an
+"ofproto provider" or a "dpif provider".  Which you should choose
+depends on a few different factors:
+
+    * Only an ofproto provider can take full advantage of hardware
+      with built-in support for wildcards (e.g. an ACL table or a
+      TCAM).
+
+    * A dpif provider can take advantage of the Open vSwitch built-in
+      implementations of bonding, LACP, 802.1ag, 802.1Q VLANs, and
+      other features.  An ofproto provider has to provide its own
+      implementations, if the hardware can support them at all.
+
+    * A dpif provider is usually easier to implement.
+
+The following sections describe how to implement each kind of port.
+
+
+ofproto Providers
+-----------------
+
+An "ofproto provider" is what ofproto uses to directly monitor and
+control an OpenFlow-capable switch.  struct ofproto_class, in
+ofproto/private.h, defines the interfaces to implement a ofproto
+provider for new hardware or software.  That structure contains many
+function pointers, each of which has a comment that is meant to
+describe its behavior in detail.  If the requirements are unclear,
+please report this as a bug.
+
+The ofproto provider interface is preliminary.  Please let us know if
+it seems unsuitable for your purpose.  We will try to improve it.
+
+
+Writing a dpif Provider
+-----------------------
+
+Open vSwitch has a built-in ofproto provider named "ofproto-dpif",
+which is built on top of a library for manipulating datapaths, called
+"dpif".  A "datapath" is a simple flow table, one that supports only
+exact-match flows, that is, flows without wildcards.  When a packet
+arrives on a network device, the datapath looks for it in this
+exact-match table.  If there is a match, then it performs the
+associated actions.  If there is no match, the datapath passes the
+packet up to ofproto-dpif, which maintains an OpenFlow flow table
+(that supports wildcards).  If the packet matches in this flow table,
+then ofproto-dpif executes its actions and inserts a new exact-match
+entry into the dpif flow table.  (Otherwise, ofproto-dpif passes the
+packet up to ofproto to send the packet to the OpenFlow controller, if
+one is configured.)
+
+The "dpif" library in turn delegates much of its functionality to a
+"dpif provider".  The following diagram shows how dpif providers fit
+into the Open vSwitch architecture:
+
+                _
+               |   +-------------------+
+               |   |    ovs-vswitchd   |<-->ovsdb-server
+               |   +-------------------+
+               |   |      ofproto      |<-->OpenFlow controllers
+               |   +--------+-+--------+
+               |   | netdev | |ofproto-|
+     userspace |   +--------+ |  dpif  |
+               |   | netdev | +--------+
+               |   |provider| |  dpif  |
+               |   +---||---+ +--------+
+               |       ||     |  dpif  |
+               |       ||     |provider|
+               |_      ||     +---||---+
+                       ||         ||
+                _  +---||-----+---||---+
+               |   |          |datapath|
+        kernel |   |          +--------+
+               |   |                   |
+               |_  +--------||---------+
+                            ||
+                         physical
+                           NIC
+
+struct dpif_class, in lib/dpif-provider.h, defines the interfaces
+required to implement a dpif provider for new hardware or software.
+That structure contains many function pointers, each of which has a
+comment that is meant to describe its behavior in detail.  If the
+requirements are unclear, please report this as a bug.
+
+There are two existing dpif implementations that may serve as
+useful examples during a port:
+
+    * lib/dpif-linux.c is a Linux-specific dpif implementation that
+      talks to an Open vSwitch-specific kernel module (whose sources
+      are in the "datapath" directory).  The kernel module performs
+      all of the switching work, passing packets that do not match any
+      flow table entry up to userspace.  This dpif implementation is
+      essentially a wrapper around calls into the kernel module.
+
+    * lib/dpif-netdev.c is a generic dpif implementation that performs
+      all switching internally.  This is how the Open vSwitch
+      userspace switch is implemented.
+
+
 Miscellaneous Notes
 -------------------
 
+Open vSwitch source code uses uint16_t, uint32_t, and uint64_t as
+fixed-width types in host byte order, and ovs_be16, ovs_be32, and
+ovs_be64 as fixed-width types in network byte order.  Each of the
+latter is equivalent to the one of the former, but the difference in
+name makes the intended use obvious.
+
+ovs-vswitchd is the most sophisticated of ofproto's clients, but
+ofproto can have other clients as well.  ovs-openflowd, in the
+utilities directory, is much simpler than ovs-vswitchd.  It may be
+easier to initially bring up ovs-openflowd as part of a port.
+
 lib/entropy.c assumes that it can obtain high-quality random number
 seeds at startup by reading from /dev/urandom.  You will need to
 modify it if this is not true on your platform.
@@ -212,6 +253,7 @@ modify it if this is not true on your platform.
 vswitchd/system-stats.c only knows how to obtain some statistics on
 Linux.  Optionally you may implement them for your platform as well.
 
+
 Questions
 ---------
 
index 241cfe9..738fd90 100644 (file)
@@ -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
@@ -143,8 +141,17 @@ enum nicira_type {
     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. */
 };
@@ -167,6 +174,53 @@ enum nicira_stats_type {
     NXST_AGGREGATE              /* Analogous to OFPST_AGGREGATE. */
 };
 
+/* 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
index efc1fd7..8c97100 100644 (file)
@@ -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 \
@@ -169,6 +171,8 @@ lib_libopenvswitch_a_SOURCES = \
        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 = \
index fd05d3d..f6feff0 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010 Nicira Networks.
+ * Copyright (c) 2008, 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.
@@ -53,6 +53,12 @@ bitmap_allocate(size_t n_bits)
     return xzalloc(bitmap_n_bytes(n_bits));
 }
 
+static inline unsigned long *
+bitmap_clone(const unsigned long *bitmap, size_t n_bits)
+{
+    return xmemdup(bitmap, bitmap_n_bytes(n_bits));
+}
+
 static inline void
 bitmap_free(unsigned long *bitmap)
 {
index cf1d057..9caa3f2 100644 (file)
@@ -337,6 +337,21 @@ bond_reconfigure(struct bond *bond, const struct bond_settings *s)
     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
@@ -376,20 +391,26 @@ bond_slave_register(struct bond *bond, void *slave_, uint32_t stb_id,
         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.
  *
index c2efb8b..fe58792 100644 (file)
@@ -76,6 +76,7 @@ void bond_destroy(struct bond *);
 bool bond_reconfigure(struct bond *, const struct bond_settings *);
 void bond_slave_register(struct bond *, void *slave_,
                          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);
index 71d26e9..edaceb4 100644 (file)
@@ -142,10 +142,10 @@ cls_rule_set_tun_id_masked(struct cls_rule *rule,
 }
 
 void
-cls_rule_set_in_port(struct cls_rule *rule, uint16_t odp_port)
+cls_rule_set_in_port(struct cls_rule *rule, uint16_t ofp_port)
 {
     rule->wc.wildcards &= ~FWW_IN_PORT;
-    rule->flow.in_port = odp_port;
+    rule->flow.in_port = ofp_port;
 }
 
 void
@@ -506,8 +506,7 @@ cls_rule_format(const struct cls_rule *rule, struct ds *s)
         break;
     }
     if (!(w & FWW_IN_PORT)) {
-        ds_put_format(s, "in_port=%"PRIu16",",
-                      odp_port_to_ofp_port(f->in_port));
+        ds_put_format(s, "in_port=%"PRIu16",", f->in_port);
     }
     if (wc->vlan_tci_mask) {
         ovs_be16 vid_mask = wc->vlan_tci_mask & htons(VLAN_VID_MASK);
@@ -693,7 +692,7 @@ classifier_count(const struct classifier *cls)
  * rule, even rules that cannot have any effect because the new rule matches a
  * superset of their flows and has higher priority. */
 struct cls_rule *
-classifier_insert(struct classifier *cls, struct cls_rule *rule)
+classifier_replace(struct classifier *cls, struct cls_rule *rule)
 {
     struct cls_rule *old_rule;
     struct cls_table *table;
@@ -711,6 +710,19 @@ classifier_insert(struct classifier *cls, struct cls_rule *rule)
     return old_rule;
 }
 
+/* Inserts 'rule' into 'cls'.  Until 'rule' is removed from 'cls', the caller
+ * must not modify or free it.
+ *
+ * 'cls' must not contain an identical rule (including wildcards, values of
+ * fixed fields, and priority).  Use classifier_find_rule_exactly() to find
+ * such a rule. */
+void
+classifier_insert(struct classifier *cls, struct cls_rule *rule)
+{
+    struct cls_rule *displaced_rule = classifier_replace(cls, rule);
+    assert(!displaced_rule);
+}
+
 /* Removes 'rule' from 'cls'.  It is the caller's responsibility to free
  * 'rule', if this is desirable. */
 void
@@ -761,10 +773,7 @@ classifier_lookup(const struct classifier *cls, const struct flow *flow)
 
 /* Finds and returns a rule in 'cls' with exactly the same priority and
  * matching criteria as 'target'.  Returns a null pointer if 'cls' doesn't
- * contain an exact match.
- *
- * Priority is ignored for exact-match rules (because OpenFlow 1.0 always
- * treats exact-match rules as highest priority). */
+ * contain an exact match. */
 struct cls_rule *
 classifier_find_rule_exactly(const struct classifier *cls,
                              const struct cls_rule *target)
@@ -778,9 +787,6 @@ classifier_find_rule_exactly(const struct classifier *cls,
     }
 
     head = find_equal(table, &target->flow, flow_hash(&target->flow, 0));
-    if (flow_wildcards_is_exact(&target->wc)) {
-        return head;
-    }
     FOR_EACH_RULE_IN_LIST (rule, head) {
         if (target->priority >= rule->priority) {
             return target->priority == rule->priority ? rule : NULL;
index d3121bf..08e2c0d 100644 (file)
@@ -121,7 +121,8 @@ void classifier_init(struct classifier *);
 void classifier_destroy(struct classifier *);
 bool classifier_is_empty(const struct classifier *);
 int classifier_count(const struct classifier *);
-struct cls_rule *classifier_insert(struct classifier *, struct cls_rule *);
+void classifier_insert(struct classifier *, struct cls_rule *);
+struct cls_rule *classifier_replace(struct classifier *, struct cls_rule *);
 void classifier_remove(struct classifier *, struct cls_rule *);
 struct cls_rule *classifier_lookup(const struct classifier *,
                                    const struct flow *);
index 24a81bd..d84b5fa 100644 (file)
@@ -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,
index cc898fb..53574aa 100644 (file)
@@ -1090,52 +1090,44 @@ dp_netdev_port_input(struct dp_netdev *dp, struct dp_netdev_port *port,
 }
 
 static void
-dp_netdev_run(void)
+dpif_netdev_run(struct dpif *dpif)
 {
-    struct shash_node *node;
+    struct dp_netdev *dp = get_dp_netdev(dpif);
+    struct dp_netdev_port *port;
     struct ofpbuf packet;
 
     ofpbuf_init(&packet, DP_NETDEV_HEADROOM + VLAN_ETH_HEADER_LEN + max_mtu);
-    SHASH_FOR_EACH (node, &dp_netdevs) {
-        struct dp_netdev *dp = node->data;
-        struct dp_netdev_port *port;
-
-        LIST_FOR_EACH (port, node, &dp->port_list) {
-            int error;
-
-            /* Reset packet contents. */
-            ofpbuf_clear(&packet);
-            ofpbuf_reserve(&packet, DP_NETDEV_HEADROOM);
 
-            error = netdev_recv(port->netdev, &packet);
-            if (!error) {
-                dp_netdev_port_input(dp, port, &packet);
-            } else if (error != EAGAIN && error != EOPNOTSUPP) {
-                static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
-                VLOG_ERR_RL(&rl, "error receiving data from %s: %s",
-                            netdev_get_name(port->netdev), strerror(error));
-            }
+    LIST_FOR_EACH (port, node, &dp->port_list) {
+        int error;
+
+        /* Reset packet contents. */
+        ofpbuf_clear(&packet);
+        ofpbuf_reserve(&packet, DP_NETDEV_HEADROOM);
+
+        error = netdev_recv(port->netdev, &packet);
+        if (!error) {
+            dp_netdev_port_input(dp, port, &packet);
+        } else if (error != EAGAIN && error != EOPNOTSUPP) {
+            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+            VLOG_ERR_RL(&rl, "error receiving data from %s: %s",
+                        netdev_get_name(port->netdev), strerror(error));
         }
     }
     ofpbuf_uninit(&packet);
 }
 
 static void
-dp_netdev_wait(void)
+dpif_netdev_wait(struct dpif *dpif)
 {
-    struct shash_node *node;
-
-    SHASH_FOR_EACH (node, &dp_netdevs) {
-        struct dp_netdev *dp = node->data;
-        struct dp_netdev_port *port;
+    struct dp_netdev *dp = get_dp_netdev(dpif);
+    struct dp_netdev_port *port;
 
-        LIST_FOR_EACH (port, node, &dp->port_list) {
-            netdev_recv_wait(port->netdev);
-        }
+    LIST_FOR_EACH (port, node, &dp->port_list) {
+        netdev_recv_wait(port->netdev);
     }
 }
 
-
 static void
 dp_netdev_strip_vlan(struct ofpbuf *packet)
 {
@@ -1378,12 +1370,12 @@ dp_netdev_execute_actions(struct dp_netdev *dp,
 
 const struct dpif_class dpif_netdev_class = {
     "netdev",
-    dp_netdev_run,
-    dp_netdev_wait,
     NULL,                       /* enumerate */
     dpif_netdev_open,
     dpif_netdev_close,
     dpif_netdev_destroy,
+    dpif_netdev_run,
+    dpif_netdev_wait,
     dpif_netdev_get_stats,
     dpif_netdev_get_drop_frags,
     dpif_netdev_set_drop_frags,
index 4d36753..8670906 100644 (file)
@@ -69,14 +69,6 @@ struct dpif_class {
      * the type assumed if no type is specified when opening a dpif. */
     const char *type;
 
-    /* Performs periodic work needed by dpifs of this class, if any is
-     * necessary. */
-    void (*run)(void);
-
-    /* Arranges for poll_block() to wake up if the "run" member function needs
-     * to be called. */
-    void (*wait)(void);
-
     /* Enumerates the names of all known created datapaths, if possible, into
      * 'all_dps'.  The caller has already initialized 'all_dps' and other dpif
      * classes might already have added names to it.
@@ -108,6 +100,13 @@ struct dpif_class {
      * the 'close' member function. */
     int (*destroy)(struct dpif *dpif);
 
+    /* Performs periodic work needed by 'dpif', if any is necessary. */
+    void (*run)(struct dpif *dpif);
+
+    /* Arranges for poll_block() to wake up if the "run" member function needs
+     * to be called for 'dpif'. */
+    void (*wait)(struct dpif *dpif);
+
     /* Retrieves statistics for 'dpif' into 'stats'. */
     int (*get_stats)(const struct dpif *dpif, struct odp_stats *stats);
 
index 630bcad..74e9856 100644 (file)
@@ -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
@@ -232,8 +200,10 @@ dp_enumerate_names(const char *type, struct sset *names)
     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)
 {
@@ -244,10 +214,10 @@ 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));
     }
 }
 
@@ -260,9 +230,7 @@ do_open(const char *name, const char *type, bool create, struct dpif **dpifp)
 
     dp_initialize();
 
-    if (!type || *type == '\0') {
-        type = "system";
-    }
+    type = dpif_normalize_type(type);
 
     registered_class = shash_find_data(&dpif_classes, type);
     if (!registered_class) {
@@ -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 *
@@ -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(). */
@@ -526,11 +523,14 @@ dpif_port_query_by_name(const struct dpif *dpif, const char *devname,
     } 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;
 }
index a039f11..8452349 100644 (file)
@@ -37,12 +37,10 @@ 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);
@@ -52,6 +50,9 @@ int dpif_create(const char *name, const char *type, struct dpif **);
 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 *);
 
index 42e80ab..0a0e9f5 100644 (file)
@@ -306,7 +306,7 @@ invalid:
 
 }
 
-/* Initializes 'flow' members from 'packet', 'tun_id', and 'in_port.
+/* Initializes 'flow' members from 'packet', 'tun_id', and 'ofp_in_port'.
  * Initializes 'packet' header pointers as follows:
  *
  *    - packet->l2 to the start of the Ethernet header.
@@ -322,7 +322,7 @@ invalid:
  *      present and has a correct length, and otherwise NULL.
  */
 int
-flow_extract(struct ofpbuf *packet, ovs_be64 tun_id, uint16_t in_port,
+flow_extract(struct ofpbuf *packet, ovs_be64 tun_id, uint16_t ofp_in_port,
              struct flow *flow)
 {
     struct ofpbuf b = *packet;
@@ -333,7 +333,7 @@ flow_extract(struct ofpbuf *packet, ovs_be64 tun_id, uint16_t in_port,
 
     memset(flow, 0, sizeof *flow);
     flow->tun_id = tun_id;
-    flow->in_port = in_port;
+    flow->in_port = ofp_in_port;
 
     packet->l2 = b.data;
     packet->l3 = NULL;
index c596f7d..a83987b 100644 (file)
@@ -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 @@ typedef unsigned int OVS_BITWISE flow_wildcards_t;
 #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 --git a/lib/hmapx.c b/lib/hmapx.c
new file mode 100644 (file)
index 0000000..bcdd7a8
--- /dev/null
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 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 "hmapx.h"
+
+#include <assert.h>
+
+#include "hash.h"
+
+static struct hmapx_node *
+hmapx_find__(const struct hmapx *map, const void *data, size_t hash)
+{
+    struct hmapx_node *node;
+
+    HMAP_FOR_EACH_IN_BUCKET (node, hmap_node, hash, &map->map) {
+        if (node->data == data) {
+            return node;
+        }
+    }
+    return NULL;
+}
+
+static struct hmapx_node *
+hmapx_add__(struct hmapx *map, void *data, size_t hash)
+{
+    struct hmapx_node *node = xmalloc(sizeof *node);
+    node->data = data;
+    hmap_insert(&map->map, &node->hmap_node, hash);
+    return node;
+}
+
+/* Initializes 'map' as an empty set of pointers. */
+void
+hmapx_init(struct hmapx *map)
+{
+    hmap_init(&map->map);
+}
+
+/* Destroys 'map'. */
+void
+hmapx_destroy(struct hmapx *map)
+{
+    if (map) {
+        hmapx_clear(map);
+        hmap_destroy(&map->map);
+    }
+}
+
+/* Initializes 'map' to contain the same pointers as 'orig'. */
+void
+hmapx_clone(struct hmapx *map, const struct hmapx *orig)
+{
+    struct hmapx_node *node;
+
+    hmapx_init(map);
+    HMAP_FOR_EACH (node, hmap_node, &orig->map) {
+        hmapx_add__(map, node->data, node->hmap_node.hash);
+    }
+}
+
+/* Exchanges the contents of 'a' and 'b'. */
+void
+hmapx_swap(struct hmapx *a, struct hmapx *b)
+{
+    hmap_swap(&a->map, &b->map);
+}
+
+/* Adjusts 'map' so that it is still valid after it has been moved around in
+ * memory (e.g. due to realloc()). */
+void
+hmapx_moved(struct hmapx *map)
+{
+    hmap_moved(&map->map);
+}
+
+/* Returns true if 'map' contains no nodes, false if it contains at least one
+ * node. */
+bool
+hmapx_is_empty(const struct hmapx *map)
+{
+    return hmap_is_empty(&map->map);
+}
+
+/* Returns the number of nodes in 'map'. */
+size_t
+hmapx_count(const struct hmapx *map)
+{
+    return hmap_count(&map->map);
+}
+
+/* Adds 'data' to 'map'.  If 'data' is new, returns the new hmapx_node;
+ * otherwise (if a 'data' already existed in 'map'), returns NULL. */
+struct hmapx_node *
+hmapx_add(struct hmapx *map, void *data)
+{
+    uint32_t hash = hash_pointer(data, 0);
+    return (hmapx_find__(map, data, hash)
+            ? NULL
+            : hmapx_add__(map, data, hash));
+}
+
+/* Adds 'data' to 'map'.  Assert-fails if 'data' was already in 'map'. */
+void
+hmapx_add_assert(struct hmapx *map, void *data)
+{
+    bool added OVS_UNUSED = hmapx_add(map, data);
+    assert(added);
+}
+
+/* Removes all of the nodes from 'map'. */
+void
+hmapx_clear(struct hmapx *map)
+{
+    struct hmapx_node *node, *next;
+
+    HMAPX_FOR_EACH_SAFE (node, next, map) {
+        hmapx_delete(map, node);
+    }
+}
+
+/* Deletes 'node' from 'map' and frees 'node'. */
+void
+hmapx_delete(struct hmapx *map, struct hmapx_node *node)
+{
+    hmap_remove(&map->map, &node->hmap_node);
+    free(node);
+}
+
+/* Searches for 'data' in 'map'.  If found, deletes it and returns true.  If
+ * not found, returns false without modifying 'map'. */
+bool
+hmapx_find_and_delete(struct hmapx *map, const void *data)
+{
+    struct hmapx_node *node = hmapx_find(map, data);
+    if (node) {
+        hmapx_delete(map, node);
+    }
+    return node != NULL;
+}
+
+/* Searches for 'data' in 'map' and deletes it.  Assert-fails if 'data' is not
+ * in 'map'. */
+void
+hmapx_find_and_delete_assert(struct hmapx *map, const void *data)
+{
+    bool deleted OVS_UNUSED = hmapx_find_and_delete(map, data);
+    assert(deleted);
+}
+
+/* Searches for 'data' in 'map'.  Returns its node, if found, otherwise a null
+ * pointer. */
+struct hmapx_node *
+hmapx_find(const struct hmapx *map, const void *data)
+{
+    return hmapx_find__(map, data, hash_pointer(data, 0));
+}
+
+/* Returns true if 'map' contains 'data', false otherwise. */
+bool
+hmapx_contains(const struct hmapx *map, const void *data)
+{
+    return hmapx_find(map, data) != NULL;
+}
+
+/* Returns true if 'a' and 'b' contain the same pointers, false otherwise. */
+bool
+hmapx_equals(const struct hmapx *a, const struct hmapx *b)
+{
+    struct hmapx_node *node;
+
+    if (hmapx_count(a) != hmapx_count(b)) {
+        return false;
+    }
+
+    HMAP_FOR_EACH (node, hmap_node, &a->map) {
+        if (!hmapx_find__(b, node->data, node->hmap_node.hash)) {
+            return false;
+        }
+    }
+
+    return true;
+}
diff --git a/lib/hmapx.h b/lib/hmapx.h
new file mode 100644 (file)
index 0000000..226255d
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 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 HMAPX_H
+#define HMAPX_H
+
+#include "hmap.h"
+
+struct hmapx_node {
+    struct hmap_node hmap_node;
+    void *data;
+};
+
+/* A set of "void *" pointers. */
+struct hmapx {
+    struct hmap map;
+};
+
+#define HMAPX_INITIALIZER(HMAPX) { HMAP_INITIALIZER(&(HMAPX)->map) }
+
+/* Basics. */
+void hmapx_init(struct hmapx *);
+void hmapx_destroy(struct hmapx *);
+void hmapx_clone(struct hmapx *, const struct hmapx *);
+void hmapx_swap(struct hmapx *, struct hmapx *);
+void hmapx_moved(struct hmapx *);
+
+/* Count. */
+bool hmapx_is_empty(const struct hmapx *);
+size_t hmapx_count(const struct hmapx *);
+
+/* Insertion. */
+struct hmapx_node *hmapx_add(struct hmapx *, void *);
+void hmapx_add_assert(struct hmapx *, void *);
+
+/* Deletion. */
+void hmapx_clear(struct hmapx *);
+void hmapx_delete(struct hmapx *, struct hmapx_node *);
+bool hmapx_find_and_delete(struct hmapx *, const void *);
+void hmapx_find_and_delete_assert(struct hmapx *, const void *);
+
+/* Search. */
+struct hmapx_node *hmapx_find(const struct hmapx *, const void *);
+bool hmapx_contains(const struct hmapx *, const void *);
+bool hmapx_equals(const struct hmapx *, const struct hmapx *);
+
+/* Iteration. */
+
+/* Iterates through every hmapx_node in HMAPX. */
+#define HMAPX_FOR_EACH(NODE, HMAPX)             \
+    HMAP_FOR_EACH(NODE, hmap_node, &(HMAPX)->map)
+
+/* Safe when NODE may be freed (not needed when NODE may be removed from the
+ * hash map but its members remain accessible and intact). */
+#define HMAPX_FOR_EACH_SAFE(NODE, NEXT, HMAPX)                  \
+    HMAP_FOR_EACH_SAFE(NODE, NEXT, hmap_node, &(HMAPX)->map)
+
+#endif /* hmapx.h */
index e003b8f..6bd2286 100644 (file)
@@ -240,6 +240,7 @@ lswitch_process_packet(struct lswitch *sw, struct rconn *rconn,
     case OFPUTIL_OFPST_AGGREGATE_REPLY:
     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:
index 42864ce..f9f4db3 100644 (file)
@@ -29,6 +29,7 @@
 #include "tag.h"
 #include "timeval.h"
 #include "util.h"
+#include "vlan-bitmap.h"
 #include "vlog.h"
 
 VLOG_DEFINE_THIS_MODULE(mac_learning);
@@ -133,31 +134,30 @@ mac_learning_destroy(struct mac_learning *ml)
 {
     if (ml) {
         bitmap_free(ml->flood_vlans);
+        free(ml);
     }
-    free(ml);
 }
 
 /* Provides a bitmap of VLANs which have learning disabled, that is, VLANs on
- * which all packets are flooded.  It takes ownership of the bitmap.  Returns
- * true if the set has changed from the previous value. */
+ * which all packets are flooded.  Returns true if the set has changed from the
+ * previous value. */
 bool
-mac_learning_set_flood_vlans(struct mac_learning *ml, unsigned long *bitmap)
+mac_learning_set_flood_vlans(struct mac_learning *ml,
+                             const unsigned long *bitmap)
 {
-    bool ret = (bitmap == NULL
-                ? ml->flood_vlans != NULL
-                : (ml->flood_vlans == NULL
-                   || !bitmap_equal(bitmap, ml->flood_vlans, 4096)));
-
-    bitmap_free(ml->flood_vlans);
-    ml->flood_vlans = bitmap;
-
-    return ret;
+    if (vlan_bitmap_equal(ml->flood_vlans, bitmap)) {
+        return false;
+    } else {
+        bitmap_free(ml->flood_vlans);
+        ml->flood_vlans = vlan_bitmap_clone(bitmap);
+        return true;
+    }
 }
 
 static bool
 is_learning_vlan(const struct mac_learning *ml, uint16_t vlan)
 {
-    return !(ml->flood_vlans && bitmap_is_set(ml->flood_vlans, vlan));
+    return vlan_bitmap_contains(ml->flood_vlans, vlan);
 }
 
 /* Returns true if 'src_mac' may be learned on 'vlan' for 'ml'.
index 51a7ac7..d9fa433 100644 (file)
@@ -96,7 +96,7 @@ void mac_learning_wait(struct mac_learning *);
 
 /* Configuration. */
 bool mac_learning_set_flood_vlans(struct mac_learning *,
-                                  unsigned long *bitmap);
+                                  const unsigned long *bitmap);
 
 /* Learning. */
 bool mac_learning_may_learn(const struct mac_learning *,
index fde686c..a401c60 100644 (file)
@@ -3974,6 +3974,8 @@ netdev_stats_to_rtnl_link_stats64(struct rtnl_link_stats64 *dst,
                                   const struct netdev_stats *src)
 {
     COPY_NETDEV_STATS;
+    dst->rx_compressed = 0;
+    dst->tx_compressed = 0;
 }
 \f
 /* Utility functions. */
index 7c2aa7a..f5d62de 100644 (file)
@@ -176,9 +176,6 @@ parse_nxm_entry(struct cls_rule *rule, const struct nxm_field *f,
         /* Metadata. */
     case NFI_NXM_OF_IN_PORT:
         flow->in_port = ntohs(get_unaligned_be16(value));
-        if (flow->in_port == OFPP_LOCAL) {
-            flow->in_port = ODPP_LOCAL;
-        }
         return 0;
 
         /* Ethernet header. */
@@ -739,9 +736,6 @@ nx_put_match(struct ofpbuf *b, const struct cls_rule *cr)
     /* Metadata. */
     if (!(wc & FWW_IN_PORT)) {
         uint16_t in_port = flow->in_port;
-        if (in_port == ODPP_LOCAL) {
-            in_port = OFPP_LOCAL;
-        }
         nxm_put_16(b, NXM_OF_IN_PORT, htons(in_port));
     }
 
@@ -1272,7 +1266,7 @@ nxm_read_field(const struct nxm_field *src, const struct flow *flow)
 {
     switch (src->index) {
     case NFI_NXM_OF_IN_PORT:
-        return flow->in_port == ODPP_LOCAL ? OFPP_LOCAL : flow->in_port;
+        return flow->in_port;
 
     case NFI_NXM_OF_ETH_DST:
         return eth_addr_to_uint64(flow->dl_dst);
index e82006b..79f4bfc 100644 (file)
@@ -403,7 +403,8 @@ odp_flow_key_from_flow(struct ofpbuf *buf, const struct flow *flow)
         nl_msg_put_be64(buf, ODP_KEY_ATTR_TUN_ID, flow->tun_id);
     }
 
-    nl_msg_put_u32(buf, ODP_KEY_ATTR_IN_PORT, flow->in_port);
+    nl_msg_put_u32(buf, ODP_KEY_ATTR_IN_PORT,
+                   ofp_port_to_odp_port(flow->in_port));
 
     eth_key = nl_msg_put_unspec_uninit(buf, ODP_KEY_ATTR_ETHERNET,
                                        sizeof *eth_key);
@@ -551,7 +552,7 @@ odp_flow_key_to_flow(const struct nlattr *key, size_t key_len,
             if (nl_attr_get_u32(nla) >= UINT16_MAX) {
                 return EINVAL;
             }
-            flow->in_port = nl_attr_get_u32(nla);
+            flow->in_port = odp_port_to_ofp_port(nl_attr_get_u32(nla));
             break;
 
         case TRANSITION(ODP_KEY_ATTR_IN_PORT, ODP_KEY_ATTR_ETHERNET):
index f45c450..29137ac 100644 (file)
@@ -608,9 +608,6 @@ parse_field_value(struct cls_rule *rule, enum field_index index,
         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;
 
@@ -732,17 +729,14 @@ parse_reg_value(struct cls_rule *rule, int reg_idx, const char *value)
  * 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;
@@ -788,8 +782,8 @@ parse_ofp_str(struct flow_mod *fm, uint8_t *table_idx,
                 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")) {
@@ -847,7 +841,7 @@ parse_ofp_str(struct flow_mod *fm, uint8_t *table_idx,
  * 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;
@@ -856,7 +850,7 @@ parse_ofp_flow_mod_str(struct list *packets, enum nx_flow_format *cur_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);
@@ -867,7 +861,13 @@ parse_ofp_flow_mod_str(struct list *packets, enum nx_flow_format *cur_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);
@@ -877,7 +877,8 @@ parse_ofp_flow_mod_str(struct list *packets, enum nx_flow_format *cur_format,
  * '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;
@@ -886,7 +887,8 @@ parse_ofp_flow_mod_file(struct list *packets, enum nx_flow_format *cur,
     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);
 
@@ -898,11 +900,10 @@ parse_ofp_flow_stats_request_str(struct flow_stats_request *fsr,
                                  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;
 }
index c8cdade..8209298 100644 (file)
@@ -29,12 +29,13 @@ struct flow_stats_request;
 struct list;
 struct ofpbuf;
 
-void parse_ofp_str(struct flow_mod *, uint8_t *table_idx,
-                   struct ofpbuf *actions, char *string);
+void parse_ofp_str(struct flow_mod *, struct ofpbuf *actions, char *string);
 
-void parse_ofp_flow_mod_str(struct list *packets, enum nx_flow_format *cur,
+void parse_ofp_flow_mod_str(struct list *packets,
+                            enum nx_flow_format *cur, bool *flow_mod_table_id,
                             char *string, uint16_t command);
-bool parse_ofp_flow_mod_file(struct list *packets, enum nx_flow_format *cur,
+bool parse_ofp_flow_mod_file(struct list *packets,
+                             enum nx_flow_format *cur, bool *flow_mod_table_id,
                              FILE *, uint16_t command);
 
 void parse_ofp_flow_stats_request_str(struct flow_stats_request *,
index 2525866..89656cc 100644 (file)
@@ -834,7 +834,7 @@ ofp_print_flow_mod(struct ds *s, const struct ofp_header *oh,
     bool need_priority;
     int error;
 
-    error = ofputil_decode_flow_mod(&fm, oh);
+    error = ofputil_decode_flow_mod(&fm, oh, true);
     if (error) {
         ofp_print_error(s, error);
         return;
@@ -860,6 +860,9 @@ ofp_print_flow_mod(struct ds *s, const struct ofp_header *oh,
     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) {
@@ -1346,6 +1349,13 @@ ofp_print_nxt_role_message(struct ds *string,
     }
 }
 
+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)
@@ -1502,6 +1512,10 @@ ofp_to_string__(const struct ofp_header *oh,
         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;
index 8a8a7b5..4618567 100644 (file)
@@ -139,8 +139,7 @@ ofputil_cls_rule_from_match(const struct ofp_match *match,
     /* 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;
@@ -235,8 +234,7 @@ ofputil_cls_rule_to_match(const struct cls_rule *rule, struct ofp_match *match)
 
     /* 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);
@@ -362,6 +360,9 @@ static int
 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_ROLE_REQUEST,
           NXT_ROLE_REQUEST, "NXT_ROLE_REQUEST",
@@ -408,6 +409,21 @@ ofputil_decode_vendor(const struct ofp_header *oh,
     }
 
     nh = (const struct nicira_header *) oh;
+
+    if (nh->subtype == htonl(NXT_FLOW_MOD_TABLE_ID)
+        && oh->length == htons(sizeof(struct nxt_flow_mod_table_id))) {
+        /* NXT_SET_FLOW_FORMAT and NXT_FLOW_MOD_TABLE_ID accidentally have the
+         * same value but different lengths.  ofputil_lookup_openflow_message()
+         * doesn't support this case, so special case it here. */
+        static const struct ofputil_msg_type nxt_flow_mod_table_id =
+            { OFPUTIL_NXT_FLOW_MOD_TABLE_ID,
+              NXT_FLOW_MOD_TABLE_ID, "NXT_FLOW_MOD_TABLE_ID",
+              sizeof(struct nxt_flow_mod_table_id), 0 };
+
+        *typep = &nxt_flow_mod_table_id;
+        return 0;
+    }
+
     return ofputil_lookup_openflow_message(&nxt_category, ntohl(nh->subtype),
                                            ntohs(oh->length), typep);
 }
@@ -853,15 +869,33 @@ ofputil_make_set_flow_format(enum nx_flow_format 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.
  *
+ * '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,
+                        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));
@@ -913,7 +947,7 @@ ofputil_decode_flow_mod(struct flow_mod *fm, const struct ofp_header *oh)
 
         /* Translate the message. */
         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);
@@ -938,7 +972,7 @@ ofputil_decode_flow_mod(struct flow_mod *fm, const struct ofp_header *oh)
 
         /* 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);
@@ -948,17 +982,34 @@ ofputil_decode_flow_mod(struct flow_mod *fm, const struct ofp_header *oh)
         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) {
         struct ofp_flow_mod *ofm;
@@ -984,7 +1035,7 @@ ofputil_encode_flow_mod(const struct flow_mod *fm,
 
         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);
index f4f71cb..975d8d2 100644 (file)
@@ -74,6 +74,7 @@ enum ofputil_msg_code {
     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,
 
@@ -114,10 +115,14 @@ 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;
@@ -128,9 +133,11 @@ struct flow_mod {
     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 *,
+                            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 {
diff --git a/lib/vlan-bitmap.c b/lib/vlan-bitmap.c
new file mode 100644 (file)
index 0000000..94059c7
--- /dev/null
@@ -0,0 +1,60 @@
+/* Copyright (c) 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 "vlan-bitmap.h"
+
+/* Allocates and returns a new 4096-bit bitmap that has 1-bit in positions in
+ * the 'n_vlans' bits indicated in 'vlans' and 0-bits everywhere else.  Returns
+ * a null pointer if there are no (valid) VLANs in 'vlans'. */
+unsigned long *
+vlan_bitmap_from_array(const int64_t *vlans, size_t n_vlans)
+{
+    unsigned long *b;
+    size_t i, n;
+
+    if (!n_vlans) {
+        return NULL;
+    }
+
+    b = bitmap_allocate(4096);
+    n = 0;
+    for (i = 0; i < n_vlans; i++) {
+        int64_t vlan = vlans[i];
+
+        if (vlan >= 0 && vlan < 4096) {
+            bitmap_set1(b, vlan);
+            n++;
+        }
+    }
+
+    if (!n) {
+        free(b);
+        return NULL;
+    }
+
+    return b;
+}
+
+/* Returns true if 'a' and 'b' are the same: either both null or both the same
+ * 4096-bit bitmap.
+ *
+ * (We assume that a nonnull bitmap is not all 0-bits.) */
+bool
+vlan_bitmap_equal(const unsigned long *a, const unsigned long *b)
+{
+    return (!a && !b) || (a && b && bitmap_equal(a, b, 4096));
+}
diff --git a/lib/vlan-bitmap.h b/lib/vlan-bitmap.h
new file mode 100644 (file)
index 0000000..6d74d40
--- /dev/null
@@ -0,0 +1,47 @@
+/* Copyright (c) 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 VLAN_BITMAP_H
+#define VLAN_BITMAP_H 1
+
+#include <stdbool.h>
+#include <stdint.h>
+#include "bitmap.h"
+
+/* A "VLAN bitmap" is a 4096-bit bitmap that represents a set.  A 1-bit
+ * indicates that the respective VLAN is a member of the set, a 0-bit indicates
+ * that it is not.  There is one wrinkle: NULL indicates that every VLAN is a
+ * member of the set.
+ *
+ * This is empirically a useful data structure. */
+
+unsigned long *vlan_bitmap_from_array(const int64_t *vlans, size_t n_vlans);
+bool vlan_bitmap_equal(const unsigned long *a, const unsigned long *b);
+
+/* Returns true if 'vid', in the range [0,4095], is a member of 'vlans'. */
+static inline bool
+vlan_bitmap_contains(const unsigned long *vlans, uint16_t vid)
+{
+    return !vlans || bitmap_is_set(vlans, vid);
+}
+
+/* Returns a new copy of 'vlans'. */
+static inline unsigned long *
+vlan_bitmap_clone(const unsigned long *vlans)
+{
+    return vlans ? bitmap_clone(vlans, 4096) : NULL;
+}
+
+#endif /* lib/vlan-bitmap.h */
index 18aa7e6..0279802 100644 (file)
@@ -15,15 +15,18 @@ ofproto_libofproto_a_SOURCES = \
        ofproto/fail-open.h \
        ofproto/in-band.c \
        ofproto/in-band.h \
+       ofproto/names.c \
        ofproto/netflow.c \
        ofproto/netflow.h \
        ofproto/ofproto.c \
        ofproto/ofproto.h \
+       ofproto/ofproto-dpif.c \
        ofproto/ofproto-sflow.c \
        ofproto/ofproto-sflow.h \
        ofproto/pktbuf.c \
        ofproto/pktbuf.h \
        ofproto/pinsched.c \
-       ofproto/pinsched.h
+       ofproto/pinsched.h \
+       ofproto/private.h
 
 EXTRA_DIST += ofproto/ofproto-unixctl.man
index 26831fe..7e7aaae 100644 (file)
@@ -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"
@@ -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,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'. */
@@ -390,6 +391,7 @@ connmgr_set_controllers(struct connmgr *mgr,
                         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;
@@ -449,6 +451,9 @@ connmgr_set_controllers(struct connmgr *mgr,
 
     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
@@ -718,6 +723,24 @@ ofconn_set_flow_format(struct ofconn *ofconn, enum nx_flow_format flow_format)
     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)
@@ -767,6 +790,7 @@ ofconn_create(struct connmgr *mgr, struct rconn *rconn, enum ofconn_type type)
     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;
@@ -919,7 +943,7 @@ ofconn_send(const struct ofconn *ofconn, struct ofpbuf *msg,
 \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
@@ -974,15 +998,15 @@ connmgr_send_flow_removed(struct connmgr *mgr,
     }
 }
 
-/* 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;
@@ -991,13 +1015,13 @@ connmgr_send_packet_in(struct connmgr *mgr, const struct dpif_upcall *upcall,
     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);
     }
@@ -1013,50 +1037,45 @@ do_send_packet_in(struct ofpbuf *ofp_packet_in, void *ofconn_)
                           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);
 }
@@ -1076,8 +1095,13 @@ connmgr_get_fail_mode(const struct connmgr *mgr)
 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. */
@@ -1260,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,
index 6ee9cb4..46d2f5d 100644 (file)
@@ -23,9 +23,9 @@
 #include "openflow/nicira-ext.h"
 #include "openvswitch/types.h"
 
-struct dpif_upcall;
 struct ofconn;
 struct ofputil_flow_removed;
+struct ofputil_packet_in;
 struct sset;
 
 /* ofproto supports two kinds of OpenFlow connections:
@@ -80,6 +80,9 @@ void ofconn_set_role(struct ofconn *, enum nx_role);
 enum nx_flow_format ofconn_get_flow_format(struct ofconn *);
 void ofconn_set_flow_format(struct ofconn *, enum nx_flow_format);
 
+bool ofconn_get_flow_mod_table_id(const struct ofconn *);
+void ofconn_set_flow_mod_table_id(struct ofconn *, bool enable);
+
 int ofconn_get_miss_send_len(const struct ofconn *);
 void ofconn_set_miss_send_len(struct ofconn *, int miss_send_len);
 
@@ -93,7 +96,7 @@ void connmgr_send_port_status(struct connmgr *, const struct ofp_phy_port *,
                               uint8_t reason);
 void connmgr_send_flow_removed(struct connmgr *,
                                const struct ofputil_flow_removed *);
-void connmgr_send_packet_in(struct connmgr *, const struct dpif_upcall *,
+void connmgr_send_packet_in(struct connmgr *, const struct ofputil_packet_in *,
                             const struct flow *, struct ofpbuf *rw_packet);
 
 /* Fail-open settings. */
index bf57b9f..197e518 100644 (file)
@@ -28,6 +28,7 @@
 #include "ofproto.h"
 #include "pktbuf.h"
 #include "poll-loop.h"
+#include "private.h"
 #include "rconn.h"
 #include "timeval.h"
 #include "vconn.h"
index 9aa03af..e04ae60 100644 (file)
@@ -35,6 +35,7 @@
 #include "openflow/openflow.h"
 #include "packets.h"
 #include "poll-loop.h"
+#include "private.h"
 #include "timeval.h"
 #include "vlog.h"
 
diff --git a/ofproto/names.c b/ofproto/names.c
new file mode 100644 (file)
index 0000000..724b184
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 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/ofproto.h"
+
+#include "dpif.h"
+
+/* This function is in a separate file because it is the only ofproto function
+ * required by ovs-ofctl, which allows ovs-ofctl to avoid linking in extra
+ * baggage. */
+
+/* Parses 'ofproto_name', which is of the form [type@]name into component
+ * pieces that are suitable for passing to ofproto_create().  The caller must
+ * free 'name' and 'type'. */
+void
+ofproto_parse_name(const char *ofproto_name, char **name, char **type)
+{
+    dp_parse_name(ofproto_name, name, type);
+}
+
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
new file mode 100644 (file)
index 0000000..36e7213
--- /dev/null
@@ -0,0 +1,3931 @@
+/*
+ * 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,
+                       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;
+
+    bundle->ofproto->need_revalidate = true;
+
+    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,
+                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) {
+        bundle->ofproto->need_revalidate = true;
+        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],
+                             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] == port->up.ofp_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);
+            ofproto->need_revalidate = true;
+        }
+
+        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)
+{
+    struct ofport *ofport = ofproto_get_port(&ofproto->up, ofp_port);
+    return ofport ? ofport_dpif_cast(ofport) : NULL;
+}
+
+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, &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, 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 'p'.
+ * Returns 0 if successful, otherwise a positive errno value. */
+static int
+send_packet(struct ofproto_dpif *ofproto, uint32_t odp_port,
+            const struct ofpbuf *packet)
+{
+    struct ofpbuf odp_actions;
+    int error;
+
+    ofpbuf_init(&odp_actions, 32);
+    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 ? in_port->bundle : NULL;
+    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,
+};
index ee59cd0..beab99c 100644 (file)
 #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;
-    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);
-
-    shash_add_once(&all_ofprotos, dpif_name(p->dpif), p);
+    ofproto->datapath_id = pick_datapath_id(ofproto);
+    VLOG_INFO("using datapath ID %016"PRIx64, ofproto->datapath_id);
+    init_ports(ofproto);
 
-    /* Initialize OpenFlow connections. */
-    p->connmgr = connmgr_create(p, datapath, local_name);
-
-    *ofprotop = p;
+    *ofprotop = ofproto;
     return 0;
 }
 
@@ -555,121 +398,183 @@ int
 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)
+{
+    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)
 {
-    struct ofport *ofport = get_port(ofproto, port_no);
-    return ofport ? ofport->cfm : NULL;
+    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)
 {
@@ -682,6 +587,32 @@ ofproto_get_snoops(const struct ofproto *ofproto, struct sset *snoops)
     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)
 {
@@ -691,45 +622,28 @@ 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
@@ -744,134 +658,48 @@ process_port_change(struct ofproto *ofproto, int error, char *devname)
 }
 
 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)
 {
@@ -900,72 +728,149 @@ ofproto_free_ofproto_controller_info(struct shash *info)
     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. */
+ * 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)
+{
+    dump->ofproto = ofproto;
+    dump->error = ofproto->ofproto_class->port_dump_start(ofproto,
+                                                          &dump->state);
+}
+
+/* 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;
+    }
+
+    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;
+}
+
+/* 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_del(struct ofproto *ofproto, uint16_t odp_port)
+ofproto_port_dump_done(struct ofproto_port_dump *dump)
 {
-    struct ofport *ofport = get_port(ofproto, odp_port);
-    const char *name = ofport ? netdev_get_name(ofport->netdev) : "<unknown>";
+    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;
+}
+
+/* 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_port_add(struct ofproto *ofproto, struct netdev *netdev,
+                 uint16_t *ofp_portp)
+{
+    uint16_t ofp_port;
     int error;
 
-    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);
+    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;
     }
     return error;
 }
 
-/* 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)
+/* 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)
 {
-    struct ofport *ofport = get_port(ofproto, odp_port);
-    return ofport && !(ofport->opp.config & htonl(OFPPC_NO_FLOOD));
+    int error;
+
+    error = ofproto->ofproto_class->port_query_by_name(ofproto, devname, port);
+    if (error) {
+        memset(port, 0, sizeof *port);
+    }
+    return error;
 }
 
-/* Sends 'packet' out of port 'port_no' within 'p'.
- *
- * Returns 0 if successful, otherwise a positive errno value. */
+/* Deletes port number 'ofp_port' from the datapath for 'ofproto'.
+ * Returns 0 if successful, otherwise a positive errno. */
 int
-ofproto_send_packet(struct ofproto *ofproto,
-                    uint32_t port_no, const struct ofpbuf *packet)
+ofproto_port_del(struct ofproto *ofproto, uint16_t ofp_port)
 {
-    struct ofpbuf odp_actions;
+    struct ofport *ofport = ofproto_get_port(ofproto, ofp_port);
+    const char *name = ofport ? netdev_get_name(ofport->netdev) : "<unknown>";
     int error;
 
-    ofpbuf_init(&odp_actions, 32);
-    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);
-
-    if (error) {
-        VLOG_WARN_RL(&rl, "%s: failed to send packet on port %"PRIu32" (%s)",
-                     dpif_name(ofproto->dpif), port_no, strerror(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.
  *
@@ -973,56 +878,55 @@ ofproto_send_packet(struct ofproto *ofproto,
  * (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)
 {
@@ -1033,10 +937,10 @@ 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);
@@ -1045,8 +949,8 @@ reinit_ports(struct ofproto *p)
     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) {
@@ -1055,10 +959,10 @@ reinit_ports(struct ofproto *p)
     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'. */
+/* 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;
@@ -1067,25 +971,25 @@ ofport_open(const struct dpif_port *dpif_port, struct ofp_phy_port *opp)
     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 = htons(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);
+    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);
@@ -1096,22 +1000,6 @@ ofport_open(const struct dpif_port *dpif_port, struct ofp_phy_port *opp)
     return netdev;
 }
 
-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;
-    }
-}
-
 /* Returns true if most fields of 'a' and 'b' are equal.  Differences in name,
  * port number, and 'config' bits other than OFPPC_PORT_DOWN are
  * disregarded. */
@@ -1137,41 +1025,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(ntohs(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
@@ -1181,7 +1077,7 @@ ofport_remove_with_name(struct ofproto *ofproto, const char *name)
 {
     struct ofport *port = shash_find_data(&ofproto->port_by_name, name);
     if (port) {
-        ofport_remove(ofproto, port);
+        ofport_remove(port);
     }
 }
 
@@ -1190,8 +1086,7 @@ ofport_remove_with_name(struct ofproto *ofproto, const char *name)
  * 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 & ~htonl(OFPPC_PORT_DOWN))
@@ -1202,61 +1097,55 @@ ofport_modified(struct ofproto *ofproto, struct ofport *port,
     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, &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;
         }
     }
@@ -1266,7 +1155,7 @@ get_port(const struct ofproto *ofproto, uint16_t odp_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;
@@ -1274,24 +1163,37 @@ update_port(struct ofproto *ofproto, const char *name)
     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)) {
+            struct netdev *old_netdev = port->netdev;
+
             /* '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.
+             * Don't close the old netdev yet in case port_modified has to
+             * remove a retained reference to it.*/
+            netdev_monitor_remove(ofproto->netdev_monitor, port->netdev);
+            netdev_monitor_add(ofproto->netdev_monitor, netdev);
+            port->netdev = netdev;
+
+            if (port->ofproto->ofproto_class->port_modified) {
+                port->ofproto->ofproto_class->port_modified(port);
             }
+
+            netdev_close(old_netdev);
         } 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);
@@ -1300,21 +1202,28 @@ update_port(struct ofproto *ofproto, const char *name)
         /* 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);
             }
@@ -1324,68 +1233,70 @@ init_ports(struct ofproto *p)
     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
@@ -1409,1128 +1320,133 @@ rule_has_out_port(const struct rule *rule, ovs_be16 out_port)
     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) {
-        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 & 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, 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, ovs_be32 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, 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, 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)
+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 & htonl(OFPPC_NO_RECV | OFPPC_NO_RECV_STP) &&
-        port->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;
+    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
@@ -2560,8 +1476,6 @@ handle_packet_out(struct ofconn *ofconn, const struct ofp_header *oh)
     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;
@@ -2599,29 +1513,20 @@ handle_packet_out(struct ofconn *ofconn, const struct ofp_header *oh)
         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,
-                   ovs_be32 config, ovs_be32 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 & htonl(OFPPC_PORT_DOWN)) {
         if (config & htonl(OFPPC_PORT_DOWN)) {
@@ -2630,16 +1535,12 @@ update_port_config(struct ofproto *p, struct ofport *port,
             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 & htonl(REVALIDATE_BITS)) {
-        COVERAGE_INC(ofproto_costly_flags);
-        port->opp.config ^= mask & htonl(REVALIDATE_BITS);
-        p->need_revalidate = true;
-    }
-#undef REVALIDATE_BITS
-    if (mask & htonl(OFPPC_NO_PACKET_IN)) {
-        port->opp.config ^= htonl(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);
     }
 }
 
@@ -2656,13 +1557,13 @@ handle_port_mod(struct ofconn *ofconn, const struct ofp_header *oh)
         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, opm->config, opm->mask);
+        update_port_config(port, opm->config, opm->mask);
         if (opm->advertise) {
             netdev_set_advertisements(port->netdev, ntohl(opm->advertise));
         }
@@ -2770,18 +1671,20 @@ handle_table_stats_request(struct ofconn *ofconn,
     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(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);
 
     ofconn_send_reply(ofconn, msg);
     return 0;
@@ -2827,7 +1730,7 @@ handle_port_stats_request(struct ofconn *ofconn, const struct ofp_header *oh)
 
     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);
         }
@@ -2863,6 +1766,7 @@ static void
 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;
     size_t act_len, len;
@@ -2874,11 +1778,11 @@ put_ofp_flow_stats(struct ofconn *ofconn, struct rule *rule,
     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, &ofs->match);
     put_32aligned_be64(&ofs->cookie, rule->flow_cookie);
@@ -2894,37 +1798,68 @@ put_ofp_flow_stats(struct ofconn *ofconn, struct rule *rule,
     }
 }
 
-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);
-    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);
         }
@@ -2947,7 +1882,8 @@ put_nx_flow_stats(struct ofconn *ofconn, struct rule *rule,
         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;
 
@@ -2978,6 +1914,7 @@ handle_nxst_flow(struct ofconn *ofconn, const struct ofp_header *oh)
 {
     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;
@@ -2997,11 +1934,11 @@ handle_nxst_flow(struct ofconn *ofconn, const struct ofp_header *oh)
 
     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);
         }
@@ -3017,11 +1954,14 @@ flow_stats_ds(struct rule *rule, struct ds *results)
     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);
@@ -3040,15 +1980,28 @@ flow_stats_ds(struct rule *rule, struct ds *results)
 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,
@@ -3056,21 +2009,23 @@ query_aggregate_stats(struct ofproto *ofproto, struct cls_rule *target,
 {
     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;
@@ -3213,8 +2168,8 @@ handle_queue_stats_request(struct ofconn *ofconn, const struct ofp_header *oh)
         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);
         }
@@ -3227,99 +2182,6 @@ handle_queue_stats_request(struct ofconn *ofconn, const struct ofp_header *oh)
     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.
  *
@@ -3336,43 +2198,70 @@ add_flow(struct ofconn *ofconn, struct flow_mod *fm)
     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;
@@ -3386,9 +2275,7 @@ send_buffered_packet(struct ofconn *ofconn,
         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. */
@@ -3399,8 +2286,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.
@@ -3412,18 +2298,30 @@ modify_flows_loose(struct ofconn *ofconn, struct flow_mod *fm)
 {
     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. */
@@ -3443,57 +2341,79 @@ static int
 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));
+        }
     }
 }
 
@@ -3501,9 +2421,9 @@ delete_flows_loose(struct ofproto *p, const struct flow_mod *fm)
 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));
     }
 }
 
@@ -3516,7 +2436,7 @@ delete_flow_strict(struct ofproto *p, struct flow_mod *fm)
  * '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;
@@ -3526,8 +2446,42 @@ delete_flow(struct ofproto *p, struct rule *rule, ovs_be16 out_port)
         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
@@ -3542,7 +2496,8 @@ handle_flow_mod(struct ofconn *ofconn, const struct ofp_header *oh)
         return error;
     }
 
-    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;
     }
@@ -3555,12 +2510,6 @@ handle_flow_mod(struct ofconn *ofconn, const struct ofp_header *oh)
         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);
@@ -3580,6 +2529,10 @@ handle_flow_mod(struct ofconn *ofconn, const struct ofp_header *oh)
         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);
     }
 }
@@ -3615,6 +2568,17 @@ 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)
 {
@@ -3691,6 +2655,9 @@ handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg)
     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);
 
@@ -3768,452 +2735,12 @@ handle_openflow(struct ofconn *ofconn, struct ofpbuf *ofp_msg)
     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, 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 & htonl(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;
@@ -4236,159 +2763,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
@@ -4401,63 +2804,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, htonl(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
-};
index d4c3bf8..ea9f98b 100644 (file)
@@ -31,9 +31,9 @@
 extern "C" {
 #endif
 
+struct cfm;
 struct cls_rule;
-struct nlattr;
-struct ofhooks;
+struct netdev;
 struct ofproto;
 struct shash;
 
@@ -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);
@@ -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,
-                        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 *);
index 934140b..695d9b4 100644 (file)
@@ -209,9 +209,9 @@ pinsched_send(struct pinsched *ps, uint16_t port_no,
         /* Otherwise queue it up for the periodic callback to drain out. */
         struct pinqueue *q;
 
-        /* We are called with a buffer obtained from dpif_recv() that has much
-         * more allocated space than actual content most of the time.  Since
-         * we're going to store the packet for some time, free up that
+        /* We might be called with a buffer obtained from dpif_recv() that has
+         * much more allocated space than actual content most of the time.
+         * Since we're going to store the packet for some time, free up that
          * otherwise wasted space. */
         ofpbuf_trim(packet);
 
index b869802..02c590c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010 Nicira Networks.
+ * Copyright (c) 2008, 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.
@@ -173,6 +173,11 @@ pktbuf_retrieve(struct pktbuf *pb, uint32_t id, struct ofpbuf **bufferp,
     struct packet *p;
     int error;
 
+    if (id == UINT32_MAX) {
+        error = 0;
+        goto error;
+    }
+
     if (!pb) {
         VLOG_WARN_RL(&rl, "attempt to send buffered packet via connection "
                      "without buffers");
@@ -204,6 +209,7 @@ pktbuf_retrieve(struct pktbuf *pb, uint32_t id, struct ofpbuf **bufferp,
                      "if the switch was recently in fail-open mode)", id);
         error = 0;
     }
+error:
     *bufferp = NULL;
     *in_port = UINT16_MAX;
     return error;
diff --git a/ofproto/private.h b/ofproto/private.h
new file mode 100644 (file)
index 0000000..16d30d5
--- /dev/null
@@ -0,0 +1,743 @@
+/*
+ * 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 255, 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 */
index fabdb84..3927550 100644 (file)
@@ -7,7 +7,7 @@ m4_define([OFPROTO_START],
    trap 'kill `cat ovs-openflowd.pid`' 0
    AT_CAPTURE_FILE([ovs-openflowd.log])
    AT_CHECK(
-     [ovs-openflowd --detach --pidfile --enable-dummy --log-file dummy@br0 none --datapath-id=fedcba9876543210 $1],
+     [ovs-openflowd --detach --pidfile --enable-dummy --log-file --fail=closed dummy@br0 none --datapath-id=fedcba9876543210 $1],
      [0], [], [stderr])
    AT_CHECK([[sed < stderr '
 /vlog|INFO|opened log file/d
index 10080e6..bb43149 100644 (file)
@@ -11,7 +11,7 @@ OFPROTO_START
 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 @@ do
     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
@@ -49,8 +49,8 @@ AT_CHECK([ovs-ofctl dump-flows br0 | STRIP_XIDS], [0], [NXST_FLOW reply:
 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])
 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=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])
index c23945f..997cc30 100644 (file)
@@ -14,7 +14,7 @@ actions=note:41.42.43,note:00.01.02.03.04.05.06.07,note
 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
@@ -32,8 +32,9 @@ NXT_SET_FLOW_FORMAT: format=nxm
 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
+NXT_FLOW_MOD_TABLE_ID: enable
+NXT_FLOW_MOD: ADD table_id:1 actions=drop
+NXT_FLOW_MOD: ADD table_id:255 tun_id=0x1234000056780000/0xffff0000ffff0000 actions=drop
 ]])
 AT_CLEANUP
 
@@ -297,7 +298,7 @@ AT_CHECK([ovs-ofctl parse-nx-match < nx-match.txt], [0], [dnl
 <any>
 
 # in port
-NXM_OF_IN_PORT(fffe)
+NXM_OF_IN_PORT(0000)
 NXM_OF_IN_PORT(fffe)
 
 # eth dst
index 9af8aac..1cfd5cf 100644 (file)
@@ -251,7 +251,7 @@ static ovs_be32 nw_dst_values[] = { CONSTANT_HTONL(0xc0a80002),
 static ovs_be64 tun_id_values[] = {
     0,
     CONSTANT_HTONLL(UINT64_C(0xfedcba9876543210)) };
-static uint16_t in_port_values[] = { 1, ODPP_LOCAL };
+static uint16_t in_port_values[] = { 1, OFPP_LOCAL };
 static ovs_be16 vlan_tci_values[] = { CONSTANT_HTONS(101), CONSTANT_HTONS(0) };
 static ovs_be16 dl_type_values[]
             = { CONSTANT_HTONS(ETH_TYPE_IP), CONSTANT_HTONS(ETH_TYPE_ARP) };
@@ -526,7 +526,7 @@ test_single_rule(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
         tcls_init(&tcls);
 
         tcls_rule = tcls_insert(&tcls, rule);
-        assert(!classifier_insert(&cls, &rule->cls_rule));
+        classifier_insert(&cls, &rule->cls_rule);
         check_tables(&cls, 1, 1, 0);
         compare_classifiers(&cls, &tcls);
 
@@ -562,7 +562,7 @@ test_rule_replacement(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
         classifier_init(&cls);
         tcls_init(&tcls);
         tcls_insert(&tcls, rule1);
-        assert(!classifier_insert(&cls, &rule1->cls_rule));
+        classifier_insert(&cls, &rule1->cls_rule);
         check_tables(&cls, 1, 1, 0);
         compare_classifiers(&cls, &tcls);
         tcls_destroy(&tcls);
@@ -570,7 +570,7 @@ test_rule_replacement(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
         tcls_init(&tcls);
         tcls_insert(&tcls, rule2);
         assert(test_rule_from_cls_rule(
-                   classifier_insert(&cls, &rule2->cls_rule)) == rule1);
+                   classifier_replace(&cls, &rule2->cls_rule)) == rule1);
         free(rule1);
         check_tables(&cls, 1, 1, 0);
         compare_classifiers(&cls, &tcls);
@@ -681,7 +681,7 @@ test_many_rules_in_one_list (int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
 
                     tcls_rules[j] = tcls_insert(&tcls, rules[j]);
                     displaced_rule = test_rule_from_cls_rule(
-                        classifier_insert(&cls, &rules[j]->cls_rule));
+                        classifier_replace(&cls, &rules[j]->cls_rule));
                     if (pri_rules[pris[j]] >= 0) {
                         int k = pri_rules[pris[j]];
                         assert(displaced_rule != NULL);
@@ -781,7 +781,7 @@ test_many_rules_in_one_table(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
 
             rules[i] = make_rule(wcf, priority, value_pats[i]);
             tcls_rules[i] = tcls_insert(&tcls, rules[i]);
-            assert(!classifier_insert(&cls, &rules[i]->cls_rule));
+            classifier_insert(&cls, &rules[i]->cls_rule);
 
             check_tables(&cls, 1, i + 1, 0);
             compare_classifiers(&cls, &tcls);
@@ -839,7 +839,7 @@ test_many_rules_in_n_tables(int n_tables)
             int value_pat = rand() & ((1u << CLS_N_FIELDS) - 1);
             rule = make_rule(wcf, priority, value_pat);
             tcls_insert(&tcls, rule);
-            assert(!classifier_insert(&cls, &rule->cls_rule));
+            classifier_insert(&cls, &rule->cls_rule);
             check_tables(&cls, -1, i + 1, -1);
             compare_classifiers(&cls, &tcls);
         }
index fc3a19d..cb06f5a 100644 (file)
@@ -78,7 +78,10 @@ utilities_ovs_dpctl_SOURCES = utilities/ovs-dpctl.c
 utilities_ovs_dpctl_LDADD = lib/libopenvswitch.a
 
 utilities_ovs_ofctl_SOURCES = utilities/ovs-ofctl.c
-utilities_ovs_ofctl_LDADD = lib/libopenvswitch.a $(SSL_LIBS)
+utilities_ovs_ofctl_LDADD = \
+       ofproto/libofproto.a \
+       lib/libopenvswitch.a \
+       $(SSL_LIBS)
 
 utilities_ovs_openflowd_SOURCES = utilities/ovs-openflowd.c
 utilities_ovs_openflowd_LDADD = \
index 66cc7ea..f977974 100644 (file)
@@ -227,6 +227,7 @@ new_switch(struct switch_ *sw, struct vconn *vconn)
     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;
@@ -260,6 +261,7 @@ static void
 read_flow_file(const char *name)
 {
     enum nx_flow_format flow_format;
+    bool flow_mod_table_id;
     FILE *stream;
 
     stream = fopen(optarg, "r");
@@ -268,8 +270,10 @@ read_flow_file(const char *name)
     }
 
     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;
     }
 
index c44de46..06d61de 100644 (file)
@@ -365,6 +365,25 @@ specified as a decimal number between 0 and 255, inclusive.
 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:
 .
@@ -713,14 +732,6 @@ If set, a matching flow must include an output action to \fIport\fR.
 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 
index e1f365b..5f9c830 100644 (file)
@@ -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"
@@ -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"
@@ -220,12 +220,17 @@ static void
 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),
@@ -234,36 +239,18 @@ open_vconn__(const char *name, const char *default_suffix,
         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
@@ -626,6 +613,7 @@ static void
 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;
@@ -637,9 +625,11 @@ do_flow_mod_file__(int argc OVS_UNUSED, char *argv[], uint16_t command)
 
     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);
     }
@@ -654,6 +644,7 @@ static void
 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;
 
@@ -664,9 +655,10 @@ do_flow_mod__(int argc, char *argv[], uint16_t command)
 
     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);
@@ -1011,7 +1003,7 @@ fte_insert(struct classifier *cls, const struct cls_rule *rule,
     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];
@@ -1041,10 +1033,9 @@ read_flows_from_file(const char *filename, struct classifier *cls, int index)
         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;
@@ -1157,6 +1148,7 @@ fte_make_flow_mod(const struct fte *fte, int index, uint16_t command,
 
     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;
@@ -1172,7 +1164,7 @@ fte_make_flow_mod(const struct fte *fte, int index, uint16_t command,
         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);
 }
 
@@ -1305,15 +1297,18 @@ static void
 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);
 }
 
@@ -1323,6 +1318,7 @@ static void
 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;
 
@@ -1335,9 +1331,11 @@ do_parse_flows(int argc OVS_UNUSED, char *argv[])
     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);
index aab5a2b..4ca22e0 100644 (file)
@@ -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"
@@ -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;
@@ -116,9 +114,10 @@ main(int argc, char *argv[])
     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. */
@@ -131,7 +130,7 @@ main(int argc, char *argv[])
                        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));
@@ -140,12 +139,7 @@ main(int argc, char *argv[])
         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);
     }
@@ -175,12 +169,10 @@ main(int argc, char *argv[])
             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();
@@ -188,7 +180,7 @@ main(int argc, char *argv[])
         poll_block();
     }
 
-    dpif_close(dpif);
+    ofproto_destroy(ofproto);
 
     return 0;
 }
@@ -475,7 +467,7 @@ parse_options(int argc, char *argv[], struct ofsettings *s)
     }
 
     /* 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;
index 9accbdd..3faeee1 100644 (file)
 
 #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;
@@ -169,22 +102,13 @@ struct bridge {
     /* 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;
@@ -192,8 +116,8 @@ struct bridge {
     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;
@@ -209,21 +133,26 @@ static long long int stats_timer = LLONG_MIN;
 #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);
@@ -231,42 +160,42 @@ static uint64_t bridge_pick_datapath_id(struct bridge *,
                                         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 *,
+                                uint32_t *bond_stable_ids);
+
+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 *);
@@ -275,9 +204,6 @@ static void shash_from_ovs_idl_map(char **keys, char **values, size_t n,
                                    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. */
 
@@ -342,7 +268,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,
@@ -358,131 +283,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()).
@@ -542,403 +348,599 @@ collect_in_band_managers(const struct ovsrec_open_vswitch *ovs_cfg,
 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) {
+        s.bond = &bond_settings;
+        s.bond_stable_ids = xmalloc(s.n_slaves * sizeof *s.bond_stable_ids);
+        port_configure_bond(port, &bond_settings, s.bond_stable_ids);
+    } else {
+        s.bond = NULL;
+        s.bond_stable_ids = 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);
+    free(s.bond_stable_ids);
+}
 
-            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));
+    iface_set_ofport(iface->cfg, ofp_port);
+}
+
+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);
+            }
+        }
+    }
+}
+
+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;
@@ -983,7 +985,7 @@ bridge_pick_local_hw_addr(struct bridge *br, uint8_t ea[ETH_ADDR_LEN],
         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;
         }
 
@@ -1014,7 +1016,7 @@ bridge_pick_local_hw_addr(struct bridge *br, uint8_t ea[ETH_ADDR_LEN],
 
             /* 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;
             }
 
@@ -1196,7 +1198,6 @@ iface_refresh_status(struct iface *iface)
         ovsrec_interface_set_link_speed(iface->cfg, NULL, 0);
     }
 
-
     ovsrec_interface_set_link_state(iface->cfg,
                                     iface_get_carrier(iface) ? "up" : "down");
 
@@ -1221,7 +1222,7 @@ iface_refresh_cfm_stats(struct iface *iface)
     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;
@@ -1251,22 +1252,15 @@ iface_refresh_cfm_stats(struct iface *iface)
 static bool
 iface_refresh_lacp_stats(struct iface *iface)
 {
-    bool *db_current = iface->cfg->lacp_current;
-    bool changed = false;
+    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 (iface->port->lacp) {
-        bool current = lacp_slave_is_current(iface->port->lacp, iface);
-
-        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
@@ -1388,8 +1382,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, "
@@ -1419,7 +1413,6 @@ bridge_run(void)
         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);
@@ -1440,7 +1433,7 @@ bridge_run(void)
             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) {
@@ -1466,7 +1459,7 @@ bridge_run(void)
         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) {
@@ -1493,14 +1486,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);
@@ -1509,41 +1496,6 @@ bridge_wait(void)
         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
@@ -1560,7 +1512,7 @@ cfm_unixctl_show(struct unixctl_conn *conn,
         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");
@@ -1666,36 +1618,17 @@ qos_unixctl_show(struct unixctl_conn *conn,
 }
 \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();
 
     /* Derive the default Ethernet address from the bridge's UUID.  This should
      * be unique and it will be stable between ovs-vswitchd runs.  */
@@ -1704,45 +1637,33 @@ bridge_create(const struct ovsrec_bridge *br_cfg)
 
     hmap_init(&br->ports);
     hmap_init(&br->ifaces);
-    shash_init(&br->iface_by_name);
+    hmap_init(&br->iface_by_name);
+    hmap_init(&br->mirrors);
 
-    br->flush = false;
-
-    list_push_back(&all_bridges, &br->node);
-
-    VLOG_INFO("created bridge %s on %s", br->name, dpif_name(br->dpif));
-
-    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);
     }
 }
@@ -1752,7 +1673,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;
         }
@@ -1798,36 +1719,13 @@ bridge_unixctl_reconnect(struct unixctl_conn *conn,
         }
         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)
@@ -1849,10 +1747,11 @@ bridge_get_controllers(const struct bridge *br,
     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;
@@ -1867,24 +1766,17 @@ bridge_reconfigure_one(struct bridge *br)
                       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;
 
@@ -1892,28 +1784,26 @@ bridge_reconfigure_one(struct bridge *br)
     }
 
     /* 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);
@@ -1921,34 +1811,6 @@ bridge_reconfigure_one(struct bridge *br)
         }
     }
     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
@@ -1995,7 +1857,7 @@ bridge_configure_local_iface_netdev(struct bridge *br,
     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;
     }
@@ -2027,9 +1889,8 @@ bridge_configure_local_iface_netdev(struct bridge *br,
 }
 
 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;
@@ -2037,7 +1898,8 @@ bridge_reconfigure_remotes(struct bridge *br,
 
     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;
@@ -2059,7 +1921,6 @@ bridge_reconfigure_remotes(struct bridge *br,
     } 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);
 
@@ -2072,811 +1933,63 @@ bridge_reconfigure_remotes(struct bridge *br,
 
         if (!strncmp(c->target, "punix:", 6)
             || !strncmp(c->target, "unix:", 5)) {
-            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
-
-            /* 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;
+            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
 
-        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);
+            /* Prevent remote ovsdb-server users from accessing arbitrary Unix
+             * domain sockets and overwriting arbitrary local files. */
+            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;
 }
@@ -2903,8 +2016,9 @@ get_interface_other_config(const struct ovsrec_interface *iface,
     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;
@@ -2912,9 +2026,12 @@ port_del_ifaces(struct port *port, const struct ovsrec_port *cfg)
 
     /* 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;
+        const char *type = port->cfg->interfaces[i]->name;
+        if (strcmp(type, "null")) {
+            sset_add(&new_ifaces, name);
+        }
     }
 
     /* Get rid of deleted interfaces. */
@@ -2927,137 +2044,49 @@ port_del_ifaces(struct port *port, const struct ovsrec_port *cfg)
     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 (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
@@ -3066,13 +2095,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) {
@@ -3083,23 +2108,11 @@ port_destroy(struct port *port)
 
         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)
 {
@@ -3136,199 +2149,129 @@ enable_lacp(struct port *port, bool *activep)
     }
 }
 
-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->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, 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,
+                    uint32_t *bond_stable_ids)
 {
-    struct bond_settings s;
     const char *detect_s;
     struct iface *iface;
+    size_t i;
 
-    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;
+    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);
-        }
-    }
+    s->fake_iface = port->cfg->bond_fake_iface;
 
+    i = 0;
     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);
+            stable_id = iface->ofp_port;
         }
-
-        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 (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);
+        bond_stable_ids[i++] = stable_id;
     }
 }
 \f
@@ -3344,19 +2287,17 @@ iface_create(struct port *port, const struct ovsrec_interface *if_cfg)
     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;
 }
 
@@ -3367,35 +2308,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 *
@@ -3403,7 +2346,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) {
@@ -3414,13 +2357,13 @@ iface_find(const char *name)
 }
 
 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;
         }
     }
@@ -3436,7 +2379,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);
@@ -3536,7 +2479,7 @@ iface_delete_queues(unsigned int queue_id,
 }
 
 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' || qos->n_queues < 1) {
         netdev_set_qos(iface->netdev, NULL, NULL);
@@ -3569,10 +2512,14 @@ iface_update_qos(struct iface *iface, const struct ovsrec_qos *qos)
             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;
@@ -3583,7 +2530,7 @@ iface_update_cfm(struct iface *iface)
     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;
     }
 
@@ -3602,8 +2549,8 @@ iface_update_cfm(struct iface *iface)
         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);
 }
 
@@ -3631,11 +2578,10 @@ iface_is_synthetic(const struct iface *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;
         }
     }
@@ -3643,106 +2589,55 @@ mirror_find_by_uuid(struct bridge *br, const struct uuid *uuid)
 }
 
 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
@@ -3750,104 +2645,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 "
@@ -3855,70 +2707,54 @@ mirror_reconfigure_one(struct mirror *m, struct ovsrec_mirror *cfg)
                      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;
 }
index 441dfb2..d591e95 100644 (file)
@@ -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"
@@ -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();