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.

19 files changed:
1  2 
lib/bond.c
lib/dpif-linux.c
lib/dpif-netdev.c
lib/flow.c
lib/flow.h
lib/netdev-linux.c
lib/nx-match.c
lib/ofp-print.c
lib/ofp-util.c
lib/ofp-util.h
ofproto/ofproto-dpif.c
ofproto/ofproto.c
tests/ofproto-macros.at
tests/ovs-ofctl.at
utilities/ovs-controller.c
utilities/ovs-ofctl.c
utilities/ovs-openflowd.c
vswitchd/bridge.c
vswitchd/ovs-vswitchd.c

diff --combined lib/bond.c
@@@ -337,6 -337,21 +337,21 @@@ bond_reconfigure(struct bond *bond, con
      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 +391,26 @@@ bond_slave_register(struct bond *bond, 
          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.
   *
@@@ -466,10 -487,6 +487,10 @@@ bond_run(struct bond *bond, struct tag_
          bond->miimon_next_update = time_msec() + bond->miimon_interval;
      }
  
 +    if (bond->monitor) {
 +        netdev_monitor_flush(bond->monitor);
 +    }
 +
      /* Enable slaves based on link status and LACP feedback. */
      HMAP_FOR_EACH (slave, hmap_node, &bond->slaves) {
          bond_link_status_update(slave, tags);
@@@ -1388,7 -1405,7 +1409,7 @@@ static unsigned in
  bond_hash_tcp(const struct flow *flow, uint16_t vlan, uint32_t basis)
  {
      struct flow hash_flow = *flow;
 -    hash_flow.vlan_tci = vlan;
 +    hash_flow.vlan_tci = htons(vlan);
  
      /* The symmetric quality of this hash function is not required, but
       * flow_hash_symmetric_l4 already exists, and is sufficient for our
diff --combined lib/dpif-linux.c
@@@ -1028,12 -1028,12 +1028,12 @@@ dpif_linux_recv_purge(struct dpif *dpif
  
  const struct dpif_class dpif_linux_class = {
      "system",
-     NULL,                       /* run */
-     NULL,                       /* wait */
      dpif_linux_enumerate,
      dpif_linux_open,
      dpif_linux_close,
      dpif_linux_destroy,
+     NULL,                       /* run */
+     NULL,                       /* wait */
      dpif_linux_get_stats,
      dpif_linux_get_drop_frags,
      dpif_linux_set_drop_frags,
@@@ -1473,7 -1473,7 +1473,7 @@@ dpif_linux_dp_to_ofpbuf(const struct dp
  }
  
  /* Clears 'dp' to "empty" values. */
 -void
 +static void
  dpif_linux_dp_init(struct dpif_linux_dp *dp)
  {
      memset(dp, 0, sizeof *dp);
@@@ -1500,7 -1500,7 +1500,7 @@@ dpif_linux_dp_dump_start(struct nl_dum
   * result of the command is expected to be of the same form, which is decoded
   * and stored in '*reply' and '*bufp'.  The caller must free '*bufp' when the
   * reply is no longer needed ('reply' will contain pointers into '*bufp'). */
 -int
 +static int
  dpif_linux_dp_transact(const struct dpif_linux_dp *request,
                         struct dpif_linux_dp *reply, struct ofpbuf **bufp)
  {
  /* Obtains information about 'dpif_' and stores it into '*reply' and '*bufp'.
   * The caller must free '*bufp' when the reply is no longer needed ('reply'
   * will contain pointers into '*bufp').  */
 -int
 +static int
  dpif_linux_dp_get(const struct dpif *dpif_, struct dpif_linux_dp *reply,
                    struct ofpbuf **bufp)
  {
@@@ -1640,7 -1640,7 +1640,7 @@@ dpif_linux_flow_to_ofpbuf(const struct 
  }
  
  /* Clears 'flow' to "empty" values. */
 -void
 +static void
  dpif_linux_flow_init(struct dpif_linux_flow *flow)
  {
      memset(flow, 0, sizeof *flow);
   * result of the command is expected to be a flow also, which is decoded and
   * stored in '*reply' and '*bufp'.  The caller must free '*bufp' when the reply
   * is no longer needed ('reply' will contain pointers into '*bufp'). */
 -int
 +static int
  dpif_linux_flow_transact(const struct dpif_linux_flow *request,
                           struct dpif_linux_flow *reply, struct ofpbuf **bufp)
  {
diff --combined lib/dpif-netdev.c
@@@ -114,7 -114,7 +114,7 @@@ struct dp_netdev_flow 
      long long int used;         /* Last used time, in monotonic msecs. */
      long long int packet_count; /* Number of packets matched. */
      long long int byte_count;   /* Number of bytes matched. */
 -    uint16_t tcp_ctl;           /* Bitwise-OR of seen tcp_ctl values. */
 +    ovs_be16 tcp_ctl;           /* Bitwise-OR of seen tcp_ctl values. */
  
      /* Actions. */
      struct nlattr *actions;
@@@ -1090,52 -1090,44 +1090,44 @@@ dp_netdev_port_input(struct dp_netdev *
  }
  
  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)
  {
@@@ -1182,7 -1174,7 +1174,7 @@@ dp_netdev_set_nw_addr(struct ofpbuf *pa
          struct ip_header *nh = packet->l3;
          ovs_be32 ip = nl_attr_get_be32(a);
          uint16_t type = nl_attr_type(a);
 -        uint32_t *field;
 +        ovs_be32 *field;
  
          field = type == ODP_ACTION_ATTR_SET_NW_SRC ? &nh->ip_src : &nh->ip_dst;
          if (key->nw_proto == IPPROTO_TCP && packet->l7) {
              if (uh->udp_csum) {
                  uh->udp_csum = recalc_csum32(uh->udp_csum, *field, ip);
                  if (!uh->udp_csum) {
 -                    uh->udp_csum = 0xffff;
 +                    uh->udp_csum = htons(0xffff);
                  }
              }
          }
@@@ -1226,7 -1218,7 +1218,7 @@@ dp_netdev_set_tp_port(struct ofpbuf *pa
        if (is_ip(packet, key)) {
          uint16_t type = nl_attr_type(a);
          ovs_be16 port = nl_attr_get_be16(a);
 -        uint16_t *field;
 +        ovs_be16 *field;
  
          if (key->nw_proto == IPPROTO_TCP && packet->l7) {
              struct tcp_header *th = packet->l4;
@@@ -1378,12 -1370,12 +1370,12 @@@ dp_netdev_execute_actions(struct dp_net
  
  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,
diff --combined lib/flow.c
@@@ -306,7 -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.
   *      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;
  
      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;
@@@ -556,7 -556,7 +556,7 @@@ flow_wildcards_is_exact(const struct fl
      }
  
      for (i = 0; i < FLOW_N_REGS; i++) {
 -        if (wc->reg_masks[i] != htonl(UINT32_MAX)) {
 +        if (wc->reg_masks[i] != UINT32_MAX) {
              return false;
          }
      }
diff --combined lib/flow.h
@@@ -45,7 -45,7 +45,7 @@@ struct flow 
      uint32_t regs[FLOW_N_REGS]; /* Registers. */
      ovs_be32 nw_src;            /* IPv4 source address. */
      ovs_be32 nw_dst;            /* IPv4 destination address. */
-     uint16_t in_port;           /* Input switch port. */
+     uint16_t in_port;           /* OpenFlow port number of input port. */
      ovs_be16 vlan_tci;          /* If 802.1Q, TCI | VLAN_CFI; otherwise 0. */
      ovs_be16 dl_type;           /* Ethernet frame type. */
      ovs_be16 tp_src;            /* TCP/UDP source port. */
@@@ -70,7 -70,7 +70,7 @@@ BUILD_ASSERT_DECL(offsetof(struct flow
  BUILD_ASSERT_DECL(sizeof(((struct flow *)0)->nd_target) == 16);
  BUILD_ASSERT_DECL(sizeof(struct flow) == FLOW_SIG_SIZE + FLOW_PAD_SIZE);
  
 -int flow_extract(struct ofpbuf *, uint64_t tun_id, uint16_t in_port,
 +int flow_extract(struct ofpbuf *, ovs_be64 tun_id, uint16_t in_port,
                   struct flow *);
  void flow_extract_stats(const struct flow *flow, struct ofpbuf *packet,
                          struct dpif_flow_stats *);
@@@ -118,7 -118,7 +118,7 @@@ typedef unsigned int OVS_BITWISE flow_w
  #define FWW_TP_DST      ((OVS_FORCE flow_wildcards_t) (1 << 7))
  /* Same meanings as corresponding OFPFW_* bits, but differ in value. */
  #define FWW_NW_TOS      ((OVS_FORCE flow_wildcards_t) (1 << 1))
- /* No corresponding OFPFW_* or OVSFW_* bits. */
+ /* No corresponding OFPFW_* bits. */
  #define FWW_ETH_MCAST   ((OVS_FORCE flow_wildcards_t) (1 << 8))
                                                         /* multicast bit only */
  #define FWW_ARP_SHA     ((OVS_FORCE flow_wildcards_t) (1 << 9))
diff --combined lib/netdev-linux.c
@@@ -373,6 -373,7 +373,6 @@@ struct netdev_linux 
  
  /* Sockets used for ioctl operations. */
  static int af_inet_sock = -1;   /* AF_INET, SOCK_DGRAM. */
 -static int af_packet_sock = -1; /* AF_PACKET, SOCK_RAW. */
  
  /* A Netlink routing socket that is not subscribed to any multicast groups. */
  static struct nl_sock *rtnl_sock;
@@@ -410,7 -411,6 +410,7 @@@ static int set_etheraddr(const char *ne
                           const uint8_t[ETH_ADDR_LEN]);
  static int get_stats_via_netlink(int ifindex, struct netdev_stats *stats);
  static int get_stats_via_proc(const char *netdev_name, struct netdev_stats *stats);
 +static int af_packet_sock(void);
  
  static bool
  is_netdev_linux_class(const struct netdev_class *netdev_class)
@@@ -447,6 -447,15 +447,6 @@@ netdev_linux_init(void
          status = af_inet_sock >= 0 ? 0 : errno;
          if (status) {
              VLOG_ERR("failed to create inet socket: %s", strerror(status));
 -        } else {
 -            /* Create AF_PACKET socket. */
 -            af_packet_sock = socket(AF_PACKET, SOCK_RAW, 0);
 -            status = af_packet_sock >= 0 ? 0 : errno;
 -            if (status) {
 -                VLOG_ERR("failed to create packet socket: %s",
 -                         strerror(status));
 -            }
 -            set_nonblocking(af_packet_sock);
          }
  
          /* Create rtnetlink socket. */
@@@ -673,8 -682,7 +673,8 @@@ netdev_linux_open(struct netdev_dev *ne
          protocol = (ethertype == NETDEV_ETH_TYPE_ANY ? ETH_P_ALL
                      : ethertype == NETDEV_ETH_TYPE_802_2 ? ETH_P_802_2
                      : ethertype);
 -        netdev->fd = socket(PF_PACKET, SOCK_RAW, htons(protocol));
 +        netdev->fd = socket(PF_PACKET, SOCK_RAW,
 +                            (OVS_FORCE int) htons(protocol));
          if (netdev->fd < 0) {
              error = errno;
              goto error;
@@@ -835,12 -843,6 +835,12 @@@ netdev_linux_send(struct netdev *netdev
              struct iovec iov;
              int ifindex;
              int error;
 +            int sock;
 +
 +            sock = af_packet_sock();
 +            if (sock < 0) {
 +                return sock;
 +            }
  
              error = get_ifindex(netdev_, &ifindex);
              if (error) {
              msg.msg_controllen = 0;
              msg.msg_flags = 0;
  
 -            retval = sendmsg(af_packet_sock, &msg, 0);
 +            retval = sendmsg(sock, &msg, 0);
          } else {
              /* Use the netdev's own fd to send to this device.  This is
               * essential for tap devices, because packets sent to a tap device
@@@ -3974,6 -3976,8 +3974,8 @@@ netdev_stats_to_rtnl_link_stats64(struc
                                    const struct netdev_stats *src)
  {
      COPY_NETDEV_STATS;
+     dst->rx_compressed = 0;
+     dst->tx_compressed = 0;
  }
  \f
  /* Utility functions. */
@@@ -4236,22 -4240,3 +4238,22 @@@ netdev_linux_get_ipv4(const struct netd
      }
      return error;
  }
 +
 +/* Returns an AF_PACKET raw socket or a negative errno value. */
 +static int
 +af_packet_sock(void)
 +{
 +    static int sock = INT_MIN;
 +
 +    if (sock == INT_MIN) {
 +        sock = socket(AF_PACKET, SOCK_RAW, 0);
 +        if (sock >= 0) {
 +            set_nonblocking(sock);
 +        } else {
 +            sock = -errno;
 +            VLOG_ERR("failed to create packet socket: %s", strerror(errno));
 +        }
 +    }
 +
 +    return sock;
 +}
diff --combined lib/nx-match.c
@@@ -176,9 -176,6 +176,6 @@@ parse_nxm_entry(struct cls_rule *rule, 
          /* 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. */
@@@ -680,7 -677,7 +677,7 @@@ nxm_put_eth(struct ofpbuf *b, uint32_t 
  
  static void
  nxm_put_eth_dst(struct ofpbuf *b,
 -                uint32_t wc, const uint8_t value[ETH_ADDR_LEN])
 +                flow_wildcards_t wc, const uint8_t value[ETH_ADDR_LEN])
  {
      switch (wc & (FWW_DL_DST | FWW_ETH_MCAST)) {
      case FWW_DL_DST | FWW_ETH_MCAST:
@@@ -739,9 -736,6 +736,6 @@@ nx_put_match(struct ofpbuf *b, const st
      /* 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 +1266,7 @@@ nxm_read_field(const struct nxm_field *
  {
      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);
diff --combined lib/ofp-print.c
@@@ -123,7 -123,7 +123,7 @@@ ofp_print_packet_in(struct ds *string, 
  
      data_len = len - offsetof(struct ofp_packet_in, data);
      ds_put_format(string, " data_len=%zu", data_len);
 -    if (htonl(op->buffer_id) == UINT32_MAX) {
 +    if (op->buffer_id == htonl(UINT32_MAX)) {
          ds_put_format(string, " (unbuffered)");
          if (ntohs(op->total_len) != data_len)
              ds_put_format(string, " (***total_len != data_len***)");
@@@ -834,7 -834,7 +834,7 @@@ ofp_print_flow_mod(struct ds *s, const 
      bool need_priority;
      int error;
  
-     error = ofputil_decode_flow_mod(&fm, oh);
+     error = ofputil_decode_flow_mod(&fm, oh, true);
      if (error) {
          ofp_print_error(s, error);
          return;
      default:
          ds_put_format(s, "cmd:%d", fm.command);
      }
+     if (fm.table_id != 0) {
+         ds_put_format(s, " table_id:%d", fm.table_id);
+     }
  
      ds_put_char(s, ' ');
      if (verbosity >= 3 && code == OFPUTIL_OFPT_FLOW_MOD) {
@@@ -1346,6 -1349,13 +1349,13 @@@ ofp_print_nxt_role_message(struct ds *s
      }
  }
  
+ 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 +1512,10 @@@ ofp_to_string__(const struct ofp_heade
          ofp_print_nxt_role_message(string, msg);
          break;
  
+     case OFPUTIL_NXT_FLOW_MOD_TABLE_ID:
+         ofp_print_nxt_flow_mod_table_id(string, msg);
+         break;
      case OFPUTIL_NXT_SET_FLOW_FORMAT:
          ofp_print_nxt_set_flow_format(string, msg);
          break;
diff --combined lib/ofp-util.c
@@@ -36,8 -36,6 +36,8 @@@
  
  VLOG_DEFINE_THIS_MODULE(ofp_util);
  
 +static ovs_be32 normalize_wildcards(const struct ofp_match *);
 +
  /* Rate limit for OpenFlow message parse errors.  These always indicate a bug
   * in the peer and so there's not much point in showing a lot of them. */
  static struct vlog_rate_limit bad_ofmsg_rl = VLOG_RATE_LIMIT_INIT(1, 5);
@@@ -96,11 -94,12 +96,11 @@@ ofputil_netmask_to_wcbits(ovs_be32 netm
  
  /* WC_INVARIANTS is the invariant bits (as defined on WC_INVARIANT_LIST) all
   * OR'd together. */
 -enum {
 -    WC_INVARIANTS = 0
 +static const flow_wildcards_t WC_INVARIANTS = 0
  #define WC_INVARIANT_BIT(NAME) | FWW_##NAME
      WC_INVARIANT_LIST
  #undef WC_INVARIANT_BIT
 -};
 +;
  
  /* Converts the ofp_match in 'match' into a cls_rule in 'rule', with the given
   * 'priority'. */
@@@ -109,7 -108,7 +109,7 @@@ ofputil_cls_rule_from_match(const struc
                              unsigned int priority, struct cls_rule *rule)
  {
      struct flow_wildcards *wc = &rule->wc;
 -    unsigned int ofpfw;
 +    uint32_t ofpfw;
      ovs_be16 vid, pcp;
  
      /* Initialize rule->priority. */
  
      /* Initialize most of rule->wc. */
      flow_wildcards_init_catchall(wc);
 -    wc->wildcards = ofpfw & WC_INVARIANTS;
 +    wc->wildcards = (OVS_FORCE flow_wildcards_t) ofpfw & WC_INVARIANTS;
  
      /* Wildcard fields that aren't defined by ofp_match or tun_id. */
      wc->wildcards |= (FWW_ARP_SHA | FWW_ARP_THA | FWW_ND_TARGET);
      /* 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;
@@@ -201,10 -199,10 +200,10 @@@ voi
  ofputil_cls_rule_to_match(const struct cls_rule *rule, struct ofp_match *match)
  {
      const struct flow_wildcards *wc = &rule->wc;
 -    unsigned int ofpfw;
 +    uint32_t ofpfw;
  
      /* Figure out most OpenFlow wildcards. */
 -    ofpfw = wc->wildcards & WC_INVARIANTS;
 +    ofpfw = (OVS_FORCE uint32_t) (wc->wildcards & WC_INVARIANTS);
      ofpfw |= ofputil_netmask_to_wcbits(wc->nw_src_mask) << OFPFW_NW_SRC_SHIFT;
      ofpfw |= ofputil_netmask_to_wcbits(wc->nw_dst_mask) << OFPFW_NW_DST_SHIFT;
      if (wc->wildcards & FWW_NW_TOS) {
  
      /* 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 -359,9 +360,9 @@@ static in
  ofputil_decode_vendor(const struct ofp_header *oh,
                        const struct ofputil_msg_type **typep)
  {
+     BUILD_ASSERT_DECL(sizeof(struct nxt_set_flow_format)
+                       != sizeof(struct nxt_flow_mod_table_id));
      static const struct ofputil_msg_type nxt_messages[] = {
          { OFPUTIL_NXT_ROLE_REQUEST,
            NXT_ROLE_REQUEST, "NXT_ROLE_REQUEST",
      }
  
      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 -868,33 +869,33 @@@ ofputil_make_set_flow_format(enum nx_fl
      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));
      ofputil_decode_msg_type(oh, &type);
      if (ofputil_msg_type_code(type) == OFPUTIL_OFPT_FLOW_MOD) {
          /* Standard OpenFlow flow_mod. */
 -        struct ofp_match match, orig_match;
          const struct ofp_flow_mod *ofm;
 +        uint16_t priority;
 +        ovs_be32 wc;
          int error;
  
          /* Dissect the message. */
              return error;
          }
  
 +        /* Set priority based on original wildcards.  Normally we'd allow
 +         * ofputil_cls_rule_from_match() to do this for us, but
 +         * normalize_wildcards() can put wildcards where the original flow
 +         * didn't have them. */
 +        priority = ntohs(ofm->priority);
 +        if (!(ofm->match.wildcards & htonl(OFPFW_ALL))) {
 +            priority = UINT16_MAX;
 +        }
 +
          /* Normalize ofm->match.  If normalization actually changes anything,
           * then log the differences. */
 -        match = ofm->match;
 -        match.pad1[0] = match.pad2[0] = 0;
 -        orig_match = match;
 -        normalize_match(&match);
 -        if (memcmp(&match, &orig_match, sizeof orig_match)) {
 +        wc = normalize_wildcards(&ofm->match);
 +        if (wc == ofm->match.wildcards) {
 +            ofputil_cls_rule_from_match(&ofm->match, priority, &fm->cr);
 +        } else {
 +            struct ofp_match match = ofm->match;
 +            match.wildcards = wc;
 +            ofputil_cls_rule_from_match(&match, priority, &fm->cr);
 +
              if (!VLOG_DROP_INFO(&bad_ofmsg_rl)) {
 -                char *old = ofp_match_to_literal_string(&orig_match);
 -                char *new = ofp_match_to_literal_string(&match);
 +                char *pre = ofp_match_to_string(&ofm->match, 1);
 +                char *post = ofp_match_to_string(&match, 1);
                  VLOG_INFO("normalization changed ofp_match, details:");
 -                VLOG_INFO(" pre: %s", old);
 -                VLOG_INFO("post: %s", new);
 -                free(old);
 -                free(new);
 +                VLOG_INFO(" pre: %s", pre);
 +                VLOG_INFO("post: %s", post);
 +                free(pre);
 +                free(post);
              }
          }
  
          /* Translate the message. */
 -        ofputil_cls_rule_from_match(&match, ntohs(ofm->priority), &fm->cr);
          fm->cookie = ofm->cookie;
-         fm->command = ntohs(ofm->command);
+         command = ntohs(ofm->command);
          fm->idle_timeout = ntohs(ofm->idle_timeout);
          fm->hard_timeout = ntohs(ofm->hard_timeout);
          fm->buffer_id = ntohl(ofm->buffer_id);
  
          /* Translate the message. */
          fm->cookie = nfm->cookie;
-         fm->command = ntohs(nfm->command);
+         command = ntohs(nfm->command);
          fm->idle_timeout = ntohs(nfm->idle_timeout);
          fm->hard_timeout = ntohs(nfm->hard_timeout);
          fm->buffer_id = ntohl(nfm->buffer_id);
          NOT_REACHED();
      }
  
+     if (flow_mod_table_id) {
+         fm->command = command & 0xff;
+         fm->table_id = command >> 8;
+     } else {
+         fm->command = command;
+         fm->table_id = 0xff;
+     }
      return 0;
  }
  
  /* Converts 'fm' into an OFPT_FLOW_MOD or NXT_FLOW_MOD message according to
-  * 'flow_format' and returns the message. */
+  * 'flow_format' and returns the message.
+  *
+  * 'flow_mod_table_id' should be true if the NXT_FLOW_MOD_TABLE_ID extension is
+  * enabled, false otherwise. */
  struct ofpbuf *
  ofputil_encode_flow_mod(const struct flow_mod *fm,
-                         enum nx_flow_format flow_format)
+                         enum nx_flow_format flow_format,
+                         bool flow_mod_table_id)
  {
      size_t actions_len = fm->n_actions * sizeof *fm->actions;
      struct ofpbuf *msg;
+     uint16_t command;
+     command = (flow_mod_table_id
+                ? (fm->command & 0xff) | (fm->table_id << 8)
+                : fm->command);
  
      if (flow_format == NXFF_OPENFLOW10) {
          struct ofp_flow_mod *ofm;
  
          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);
@@@ -2027,29 -2065,123 +2078,29 @@@ actions_next(struct actions_iterator *i
      }
  }
  
 -void
 -normalize_match(struct ofp_match *m)
 +static ovs_be32
 +normalize_wildcards(const struct ofp_match *m)
  {
 -    enum { OFPFW_NW = (OFPFW_NW_SRC_MASK | OFPFW_NW_DST_MASK | OFPFW_NW_PROTO
 +    enum { OFPFW_NW = (OFPFW_NW_SRC_ALL | OFPFW_NW_DST_ALL | OFPFW_NW_PROTO
                         | OFPFW_NW_TOS) };
      enum { OFPFW_TP = OFPFW_TP_SRC | OFPFW_TP_DST };
 -    uint32_t wc;
 -
 -    wc = ntohl(m->wildcards) & OFPFW_ALL;
 -    if (wc & OFPFW_DL_TYPE) {
 -        m->dl_type = 0;
 +    ovs_be32 wc;
  
 -        /* Can't sensibly match on network or transport headers if the
 -         * data link type is unknown. */
 -        wc |= OFPFW_NW | OFPFW_TP;
 -        m->nw_src = m->nw_dst = m->nw_proto = m->nw_tos = 0;
 -        m->tp_src = m->tp_dst = 0;
 +    wc = m->wildcards;
 +    if (wc & htonl(OFPFW_DL_TYPE)) {
 +        wc |= htonl(OFPFW_NW | OFPFW_TP);
      } else if (m->dl_type == htons(ETH_TYPE_IP)) {
 -        if (wc & OFPFW_NW_PROTO) {
 -            m->nw_proto = 0;
 -
 -            /* Can't sensibly match on transport headers if the network
 -             * protocol is unknown. */
 -            wc |= OFPFW_TP;
 -            m->tp_src = m->tp_dst = 0;
 -        } else if (m->nw_proto == IPPROTO_TCP ||
 -                   m->nw_proto == IPPROTO_UDP ||
 -                   m->nw_proto == IPPROTO_ICMP) {
 -            if (wc & OFPFW_TP_SRC) {
 -                m->tp_src = 0;
 -            }
 -            if (wc & OFPFW_TP_DST) {
 -                m->tp_dst = 0;
 -            }
 -        } else {
 -            /* Transport layer fields will always be extracted as zeros, so we
 -             * can do an exact-match on those values.  */
 -            wc &= ~OFPFW_TP;
 -            m->tp_src = m->tp_dst = 0;
 -        }
 -        if (wc & OFPFW_NW_SRC_MASK) {
 -            m->nw_src &= ofputil_wcbits_to_netmask(wc >> OFPFW_NW_SRC_SHIFT);
 -        }
 -        if (wc & OFPFW_NW_DST_MASK) {
 -            m->nw_dst &= ofputil_wcbits_to_netmask(wc >> OFPFW_NW_DST_SHIFT);
 -        }
 -        if (wc & OFPFW_NW_TOS) {
 -            m->nw_tos = 0;
 -        } else {
 -            m->nw_tos &= IP_DSCP_MASK;
 +        if (wc & htonl(OFPFW_NW_PROTO) || (m->nw_proto != IPPROTO_TCP &&
 +                                           m->nw_proto != IPPROTO_UDP &&
 +                                           m->nw_proto != IPPROTO_ICMP)) {
 +            wc |= htonl(OFPFW_TP);
          }
      } else if (m->dl_type == htons(ETH_TYPE_ARP)) {
 -        if (wc & OFPFW_NW_PROTO) {
 -            m->nw_proto = 0;
 -        }
 -        if (wc & OFPFW_NW_SRC_MASK) {
 -            m->nw_src &= ofputil_wcbits_to_netmask(wc >> OFPFW_NW_SRC_SHIFT);
 -        }
 -        if (wc & OFPFW_NW_DST_MASK) {
 -            m->nw_dst &= ofputil_wcbits_to_netmask(wc >> OFPFW_NW_DST_SHIFT);
 -        }
 -        m->tp_src = m->tp_dst = m->nw_tos = 0;
 -    } else if (m->dl_type == htons(ETH_TYPE_IPV6)) {
 -        /* Don't normalize IPv6 traffic, since OpenFlow doesn't have a
 -         * way to express it. */
 +        wc |= htonl(OFPFW_TP);
      } else {
 -        /* Network and transport layer fields will always be extracted as
 -         * zeros, so we can do an exact-match on those values. */
 -        wc &= ~(OFPFW_NW | OFPFW_TP);
 -        m->nw_proto = m->nw_src = m->nw_dst = m->nw_tos = 0;
 -        m->tp_src = m->tp_dst = 0;
 -    }
 -    if (wc & OFPFW_DL_SRC) {
 -        memset(m->dl_src, 0, sizeof m->dl_src);
 -    }
 -    if (wc & OFPFW_DL_DST) {
 -        memset(m->dl_dst, 0, sizeof m->dl_dst);
 +        wc |= htonl(OFPFW_NW | OFPFW_TP);
      }
 -    m->wildcards = htonl(wc);
 -}
 -
 -/* Returns a string that describes 'match' in a very literal way, without
 - * interpreting its contents except in a very basic fashion.  The returned
 - * string is intended to be fixed-length, so that it is easy to see differences
 - * between two such strings if one is put above another.  This is useful for
 - * describing changes made by normalize_match().
 - *
 - * The caller must free the returned string (with free()). */
 -char *
 -ofp_match_to_literal_string(const struct ofp_match *match)
 -{
 -    return xasprintf("wildcards=%#10"PRIx32" "
 -                     " in_port=%5"PRId16" "
 -                     " dl_src="ETH_ADDR_FMT" "
 -                     " dl_dst="ETH_ADDR_FMT" "
 -                     " dl_vlan=%5"PRId16" "
 -                     " dl_vlan_pcp=%3"PRId8" "
 -                     " dl_type=%#6"PRIx16" "
 -                     " nw_tos=%#4"PRIx8" "
 -                     " nw_proto=%#4"PRIx16" "
 -                     " nw_src=%#10"PRIx32" "
 -                     " nw_dst=%#10"PRIx32" "
 -                     " tp_src=%5"PRId16" "
 -                     " tp_dst=%5"PRId16,
 -                     ntohl(match->wildcards),
 -                     ntohs(match->in_port),
 -                     ETH_ADDR_ARGS(match->dl_src),
 -                     ETH_ADDR_ARGS(match->dl_dst),
 -                     ntohs(match->dl_vlan),
 -                     match->dl_vlan_pcp,
 -                     ntohs(match->dl_type),
 -                     match->nw_tos,
 -                     match->nw_proto,
 -                     ntohl(match->nw_src),
 -                     ntohl(match->nw_dst),
 -                     ntohs(match->tp_src),
 -                     ntohs(match->tp_dst));
 +    return wc;
  }
  
  static uint32_t
diff --combined lib/ofp-util.h
@@@ -74,6 -74,7 +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,
  
@@@ -101,6 -102,8 +102,6 @@@ int ofputil_netmask_to_wcbits(ovs_be32 
  void ofputil_cls_rule_from_match(const struct ofp_match *,
                                   unsigned int priority, struct cls_rule *);
  void ofputil_cls_rule_to_match(const struct cls_rule *, struct ofp_match *);
 -void normalize_match(struct ofp_match *);
 -char *ofp_match_to_literal_string(const struct ofp_match *match);
  
  /* dl_type translation between OpenFlow and 'struct flow' format. */
  ovs_be16 ofputil_dl_type_to_openflow(ovs_be16 flow_dl_type);
@@@ -114,10 -117,14 +115,14 @@@ enum nx_flow_format ofputil_min_flow_fo
  
  struct ofpbuf *ofputil_make_set_flow_format(enum nx_flow_format);
  
+ /* NXT_FLOW_MOD_TABLE_ID extension. */
+ struct ofpbuf *ofputil_make_flow_mod_table_id(bool flow_mod_table_id);
  /* Flow format independent flow_mod. */
  struct flow_mod {
      struct cls_rule cr;
      ovs_be64 cookie;
+     uint8_t table_id;
      uint16_t command;
      uint16_t idle_timeout;
      uint16_t hard_timeout;
      size_t n_actions;
  };
  
- int ofputil_decode_flow_mod(struct flow_mod *, const struct ofp_header *);
+ int ofputil_decode_flow_mod(struct flow_mod *, const struct ofp_header *,
+                             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 --combined ofproto/ofproto-dpif.c
index 0000000,26a0fbf..36e7213
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,3939 +1,3931 @@@
 -static int send_packet(struct ofproto_dpif *,
 -                       uint32_t odp_port, uint16_t vlan_tci,
+ /*
+  * 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. */
 -                        ofport->odp_port, 0, &packet);
++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),
 -        send_packet(ofproto, OFPP_LOCAL, 0, upcall->packet);
++                        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)) {
 -/* Sends 'packet' out of port 'odp_port' within 'ofproto'.  If 'vlan_tci' is
 - * zero the packet will not have any 802.1Q hader; if it is nonzero, then the
 - * packet will be sent with the VLAN TCI specified by 'vlan_tci & ~VLAN_CFI'.
 - *
++        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
 -send_packet(struct ofproto_dpif *ofproto, uint32_t odp_port, uint16_t vlan_tci,
++/* Sends 'packet' out of port 'odp_port' within 'p'.
+  * Returns 0 if successful, otherwise a positive errno value. */
+ static int
 -    if (vlan_tci != 0) {
 -        nl_msg_put_u32(&odp_actions, ODP_ACTION_ATTR_SET_DL_TCI,
 -                       ntohs(vlan_tci & ~VLAN_CFI));
 -    }
++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,
+ };
diff --combined ofproto/ofproto.c
  #include "ofproto.h"
  #include <errno.h>
  #include <inttypes.h>
- #include <sys/socket.h>
- #include <net/if.h>
- #include <netinet/in.h>
  #include <stdbool.h>
  #include <stdlib.h>
- #include "autopath.h"
  #include "byte-order.h"
- #include "cfm.h"
  #include "classifier.h"
  #include "connmgr.h"
  #include "coverage.h"
- #include "dpif.h"
  #include "dynamic-string.h"
- #include "fail-open.h"
  #include "hash.h"
  #include "hmap.h"
- #include "in-band.h"
- #include "mac-learning.h"
- #include "multipath.h"
  #include "netdev.h"
- #include "netflow.h"
- #include "netlink.h"
  #include "nx-match.h"
- #include "odp-util.h"
  #include "ofp-print.h"
  #include "ofp-util.h"
- #include "ofproto-sflow.h"
  #include "ofpbuf.h"
  #include "openflow/nicira-ext.h"
  #include "openflow/openflow.h"
- #include "openvswitch/datapath-protocol.h"
  #include "packets.h"
  #include "pinsched.h"
  #include "pktbuf.h"
  #include "poll-loop.h"
- #include "rconn.h"
+ #include "private.h"
  #include "shash.h"
  #include "sset.h"
- #include "stream-ssl.h"
- #include "tag.h"
- #include "timer.h"
  #include "timeval.h"
  #include "unaligned.h"
  #include "unixctl.h"
- #include "vconn.h"
  #include "vlog.h"
  
  VLOG_DEFINE_THIS_MODULE(ofproto);
  
- COVERAGE_DEFINE(facet_changed_rule);
- COVERAGE_DEFINE(facet_revalidate);
- COVERAGE_DEFINE(odp_overflow);
  COVERAGE_DEFINE(ofproto_agg_request);
- COVERAGE_DEFINE(ofproto_costly_flags);
- COVERAGE_DEFINE(ofproto_ctlr_action);
- COVERAGE_DEFINE(ofproto_del_rule);
  COVERAGE_DEFINE(ofproto_error);
- COVERAGE_DEFINE(ofproto_expiration);
- COVERAGE_DEFINE(ofproto_expired);
  COVERAGE_DEFINE(ofproto_flows_req);
  COVERAGE_DEFINE(ofproto_flush);
- COVERAGE_DEFINE(ofproto_invalidated);
  COVERAGE_DEFINE(ofproto_no_packet_in);
- COVERAGE_DEFINE(ofproto_ofp2odp);
- COVERAGE_DEFINE(ofproto_packet_in);
  COVERAGE_DEFINE(ofproto_packet_out);
  COVERAGE_DEFINE(ofproto_queue_req);
  COVERAGE_DEFINE(ofproto_recv_openflow);
  COVERAGE_DEFINE(ofproto_reinit_ports);
- COVERAGE_DEFINE(ofproto_unexpected_rule);
  COVERAGE_DEFINE(ofproto_uninstallable);
  COVERAGE_DEFINE(ofproto_update_port);
  
- /* Maximum depth of flow table recursion (due to NXAST_RESUBMIT actions) in a
-  * flow translation. */
- #define MAX_RESUBMIT_RECURSION 16
+ static void ofport_destroy__(struct ofport *);
+ static void ofport_destroy(struct ofport *);
  
- struct rule;
+ static int rule_create(struct ofproto *, const struct cls_rule *,
+                        const union ofp_action *, size_t n_actions,
+                        uint16_t idle_timeout, uint16_t hard_timeout,
+                        ovs_be64 flow_cookie, bool send_flow_removed,
+                        struct rule **rulep);
  
- struct ofport {
-     struct hmap_node hmap_node; /* In struct ofproto's "ports" hmap. */
-     struct netdev *netdev;
-     struct ofp_phy_port opp;
-     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;
  }
  
  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 +587,32 @@@ ofproto_get_snoops(const struct ofprot
      connmgr_get_snoops(ofproto->connmgr, snoops);
  }
  
+ static void
+ ofproto_destroy__(struct ofproto *ofproto)
+ {
+     size_t i;
+     connmgr_destroy(ofproto->connmgr);
+     hmap_remove(&all_ofprotos, &ofproto->hmap_node);
+     free(ofproto->name);
+     free(ofproto->mfr_desc);
+     free(ofproto->hw_desc);
+     free(ofproto->sw_desc);
+     free(ofproto->serial_desc);
+     free(ofproto->dp_desc);
+     netdev_monitor_destroy(ofproto->netdev_monitor);
+     hmap_destroy(&ofproto->ports);
+     shash_destroy(&ofproto->port_by_name);
+     for (i = 0; i < ofproto->n_tables; i++) {
+         classifier_destroy(&ofproto->tables[i]);
+     }
+     free(ofproto->tables);
+     ofproto->ofproto_class->dealloc(ofproto);
+ }
  void
  ofproto_destroy(struct ofproto *p)
  {
          return;
      }
  
-     shash_find_and_delete(&all_ofprotos, dpif_name(p->dpif));
      ofproto_flush_flows__(p);
-     connmgr_destroy(p->connmgr);
-     classifier_destroy(&p->cls);
-     hmap_destroy(&p->facets);
-     dpif_close(p->dpif);
-     netdev_monitor_destroy(p->netdev_monitor);
      HMAP_FOR_EACH_SAFE (ofport, next_ofport, hmap_node, &p->ports) {
-         hmap_remove(&p->ports, &ofport->hmap_node);
-         ofport_free(ofport);
+         ofport_destroy(ofport);
      }
-     shash_destroy(&p->port_by_name);
-     netflow_destroy(p->netflow);
-     ofproto_sflow_destroy(p->sflow);
  
-     mac_learning_destroy(p->ml);
-     free(p->mfr_desc);
-     free(p->hw_desc);
-     free(p->sw_desc);
-     free(p->serial_desc);
-     free(p->dp_desc);
-     hmap_destroy(&p->ports);
-     free(p);
+     p->ofproto_class->destruct(p);
+     ofproto_destroy__(p);
  }
  
+ /* Destroys the datapath with the respective 'name' and 'type'.  With the Linux
+  * kernel datapath, for example, this destroys the datapath in the kernel, and
+  * with the netdev-based datapath, it tears down the data structures that
+  * represent the datapath.
+  *
+  * The datapath should not be currently open as an ofproto. */
  int
- ofproto_run(struct ofproto *p)
+ ofproto_delete(const char *name, const char *type)
  {
-     int error = ofproto_run1(p);
-     if (!error) {
-         error = ofproto_run2(p, false);
-     }
-     return error;
+     const struct ofproto_class *class = ofproto_class_find__(type);
+     return (!class ? EAFNOSUPPORT
+             : !class->del ? EACCES
+             : class->del(type, name));
  }
  
  static void
@@@ -744,134 -658,48 +658,48 @@@ process_port_change(struct ofproto *ofp
  }
  
  int
- ofproto_run1(struct ofproto *p)
+ ofproto_run(struct ofproto *p)
  {
-     struct ofport *ofport;
      char *devname;
      int error;
-     int i;
  
-     if (shash_is_empty(&p->port_by_name)) {
-         init_ports(p);
+     error = p->ofproto_class->run(p);
+     if (error == ENODEV) {
+         /* Someone destroyed the datapath behind our back.  The caller
+          * better destroy us and give up, because we're just going to
+          * spin from here on out. */
+         static struct vlog_rate_limit rl2 = VLOG_RATE_LIMIT_INIT(1, 5);
+         VLOG_ERR_RL(&rl2, "%s: datapath was destroyed externally",
+                     p->name);
+         return ENODEV;
      }
  
-     for (i = 0; i < 50; i++) {
-         struct dpif_upcall packet;
-         error = dpif_recv(p->dpif, &packet);
-         if (error) {
-             if (error == ENODEV) {
-                 /* Someone destroyed the datapath behind our back.  The caller
-                  * better destroy us and give up, because we're just going to
-                  * spin from here on out. */
-                 static struct vlog_rate_limit rl2 = VLOG_RATE_LIMIT_INIT(1, 5);
-                 VLOG_ERR_RL(&rl2, "%s: datapath was destroyed externally",
-                             dpif_name(p->dpif));
-                 return ENODEV;
-             }
-             break;
+     if (p->ofproto_class->port_poll) {
+         while ((error = p->ofproto_class->port_poll(p, &devname)) != EAGAIN) {
+             process_port_change(p, error, devname);
          }
-         handle_upcall(p, &packet);
-     }
-     while ((error = dpif_port_poll(p->dpif, &devname)) != EAGAIN) {
-         process_port_change(p, error, devname);
      }
      while ((error = netdev_monitor_poll(p->netdev_monitor,
                                          &devname)) != EAGAIN) {
          process_port_change(p, error, devname);
      }
  
-     HMAP_FOR_EACH (ofport, hmap_node, &p->ports) {
-         ofport_run(p, ofport);
-     }
      connmgr_run(p->connmgr, handle_openflow);
  
-     if (timer_expired(&p->next_expiration)) {
-         int delay = ofproto_expire(p);
-         timer_set_duration(&p->next_expiration, delay);
-         COVERAGE_INC(ofproto_expiration);
-     }
-     if (p->netflow) {
-         netflow_run(p->netflow);
-     }
-     if (p->sflow) {
-         ofproto_sflow_run(p->sflow);
-     }
-     return 0;
- }
- int
- ofproto_run2(struct ofproto *p, bool revalidate_all)
- {
-     /* Figure out what we need to revalidate now, if anything. */
-     struct tag_set revalidate_set = p->revalidate_set;
-     if (p->need_revalidate) {
-         revalidate_all = true;
-     }
-     /* Clear the revalidation flags. */
-     tag_set_init(&p->revalidate_set);
-     p->need_revalidate = false;
-     /* Now revalidate if there's anything to do. */
-     if (revalidate_all || !tag_set_is_empty(&revalidate_set)) {
-         struct facet *facet, *next;
-         HMAP_FOR_EACH_SAFE (facet, next, hmap_node, &p->facets) {
-             if (revalidate_all
-                 || tag_set_intersects(&revalidate_set, facet->tags)) {
-                 facet_revalidate(p, facet);
-             }
-         }
-     }
      return 0;
  }
  
  void
  ofproto_wait(struct ofproto *p)
  {
-     struct ofport *ofport;
-     HMAP_FOR_EACH (ofport, hmap_node, &p->ports) {
-         ofport_wait(ofport);
+     p->ofproto_class->wait(p);
+     if (p->ofproto_class->port_poll_wait) {
+         p->ofproto_class->port_poll_wait(p);
      }
-     dpif_recv_wait(p->dpif);
-     dpif_port_poll_wait(p->dpif);
      netdev_monitor_poll_wait(p->netdev_monitor);
-     if (p->sflow) {
-         ofproto_sflow_wait(p->sflow);
-     }
-     if (!tag_set_is_empty(&p->revalidate_set)) {
-         poll_immediate_wake();
-     }
-     if (p->need_revalidate) {
-         /* Shouldn't happen, but if it does just go around again. */
-         VLOG_DBG_RL(&rl, "need revalidate in ofproto_wait_cb()");
-         poll_immediate_wake();
-     } else {
-         timer_wait(&p->next_expiration);
-     }
      connmgr_wait(p->connmgr);
  }
  
- void
- ofproto_revalidate(struct ofproto *ofproto, tag_type tag)
- {
-     tag_set_add(&ofproto->revalidate_set, tag);
- }
- struct tag_set *
- ofproto_get_revalidate_set(struct ofproto *ofproto)
- {
-     return &ofproto->revalidate_set;
- }
  bool
  ofproto_is_alive(const struct ofproto *p)
  {
@@@ -900,72 -728,149 +728,149 @@@ ofproto_free_ofproto_controller_info(st
      shash_destroy(info);
  }
  
- /* Deletes port number 'odp_port' from the datapath for 'ofproto'.
+ /* Makes a deep copy of 'old' into 'port'. */
+ void
+ ofproto_port_clone(struct ofproto_port *port, const struct ofproto_port *old)
+ {
+     port->name = xstrdup(old->name);
+     port->type = xstrdup(old->type);
+     port->ofp_port = old->ofp_port;
+ }
+ /* Frees memory allocated to members of 'ofproto_port'.
   *
-  * This is almost the same as calling dpif_port_del() directly on the
-  * datapath, but it also makes 'ofproto' close its open netdev for the port
-  * (if any).  This makes it possible to create a new netdev of a different
-  * type under the same name, which otherwise the netdev library would refuse
-  * to do because of the conflict.  (The netdev would eventually get closed on
-  * the next trip through ofproto_run(), but this interface is more direct.)
+  * Do not call this function on an ofproto_port obtained from
+  * ofproto_port_dump_next(): that function retains ownership of the data in the
+  * ofproto_port. */
+ void
+ ofproto_port_destroy(struct ofproto_port *ofproto_port)
+ {
+     free(ofproto_port->name);
+     free(ofproto_port->type);
+ }
+ /* Initializes 'dump' to begin dumping the ports in an ofproto.
   *
-  * Returns 0 if successful, otherwise a positive errno. */
+  * 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.
   *
   * (0...65535, inclusive) then the flow will be visible to OpenFlow
   * controllers; otherwise, it will be hidden.
   *
-  * The caller retains ownership of 'cls_rule' and 'actions'. */
+  * The caller retains ownership of 'cls_rule' and 'actions'.
+  *
+  * This is a helper function for in-band control and fail-open. */
  void
  ofproto_add_flow(struct ofproto *p, const struct cls_rule *cls_rule,
                   const union ofp_action *actions, size_t n_actions)
  {
      struct rule *rule;
-     rule = rule_create(cls_rule, actions, n_actions, 0, 0, 0, false);
-     rule_insert(p, rule);
+     rule_create(p, cls_rule, actions, n_actions, 0, 0, 0, false, &rule);
  }
  
+ /* Searches for a rule with matching criteria exactly equal to 'target' in
+  * ofproto's table 0 and, if it finds one, deletes it.
+  *
+  * This is a helper function for in-band control and fail-open. */
  void
  ofproto_delete_flow(struct ofproto *ofproto, const struct cls_rule *target)
  {
      struct rule *rule;
  
-     rule = rule_from_cls_rule(classifier_find_rule_exactly(&ofproto->cls,
-                                                            target));
-     if (rule) {
-         rule_remove(ofproto, rule);
-     }
+     rule = rule_from_cls_rule(classifier_find_rule_exactly(
+                                   &ofproto->tables[0], target));
+     ofproto_rule_destroy(rule);
  }
  
  static void
  ofproto_flush_flows__(struct ofproto *ofproto)
  {
-     struct facet *facet, *next_facet;
-     struct rule *rule, *next_rule;
-     struct cls_cursor cursor;
+     size_t i;
  
      COVERAGE_INC(ofproto_flush);
  
-     HMAP_FOR_EACH_SAFE (facet, next_facet, hmap_node, &ofproto->facets) {
-         /* Mark the facet as not installed so that facet_remove() doesn't
-          * bother trying to uninstall it.  There is no point in uninstalling it
-          * individually since we are about to blow away all the facets with
-          * dpif_flow_flush(). */
-         facet->installed = false;
-         facet->dp_packet_count = 0;
-         facet->dp_byte_count = 0;
-         facet_remove(ofproto, facet);
+     if (ofproto->ofproto_class->flush) {
+         ofproto->ofproto_class->flush(ofproto);
      }
  
-     cls_cursor_init(&cursor, &ofproto->cls, NULL);
-     CLS_CURSOR_FOR_EACH_SAFE (rule, next_rule, cr, &cursor) {
-         rule_remove(ofproto, rule);
-     }
+     for (i = 0; i < ofproto->n_tables; i++) {
+         struct rule *rule, *next_rule;
+         struct cls_cursor cursor;
  
-     dpif_flow_flush(ofproto->dpif);
+         cls_cursor_init(&cursor, &ofproto->tables[i], NULL);
+         CLS_CURSOR_FOR_EACH_SAFE (rule, next_rule, cr, &cursor) {
+             ofproto_rule_destroy(rule);
+         }
+     }
  }
  
+ /* Deletes all of the flows from all of ofproto's flow tables, then
+  * reintroduces rules required by in-band control and fail open. */
  void
  ofproto_flush_flows(struct ofproto *ofproto)
  {
  static void
  reinit_ports(struct ofproto *p)
  {
-     struct dpif_port_dump dump;
+     struct ofproto_port_dump dump;
      struct sset devnames;
      struct ofport *ofport;
-     struct dpif_port dpif_port;
+     struct ofproto_port ofproto_port;
      const char *devname;
  
      COVERAGE_INC(ofproto_reinit_ports);
      HMAP_FOR_EACH (ofport, hmap_node, &p->ports) {
          sset_add(&devnames, netdev_get_name(ofport->netdev));
      }
-     DPIF_PORT_FOR_EACH (&dpif_port, &dump, p->dpif) {
-         sset_add(&devnames, dpif_port.name);
+     OFPROTO_PORT_FOR_EACH (&ofproto_port, &dump, p) {
+         sset_add(&devnames, ofproto_port.name);
      }
  
      SSET_FOR_EACH (devname, &devnames) {
      sset_destroy(&devnames);
  }
  
- /* Opens and returns a netdev for 'dpif_port', or a null pointer if the netdev
-  * cannot be opened.  On success, also fills in 'opp'. */
+ /* 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;
      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);
      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 +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 +1077,7 @@@ ofport_remove_with_name(struct ofproto 
  {
      struct ofport *port = shash_find_data(&ofproto->port_by_name, name);
      if (port) {
-         ofport_remove(ofproto, port);
+         ofport_remove(port);
      }
  }
  
   * Does not handle a name or port number change.  The caller must implement
   * such a change as a delete followed by an add.  */
  static void
- ofport_modified(struct ofproto *ofproto, struct ofport *port,
-                 struct netdev *netdev, struct ofp_phy_port *opp)
+ ofport_modified(struct ofport *port, struct ofp_phy_port *opp)
  {
      memcpy(port->opp.hw_addr, opp->hw_addr, ETH_ADDR_LEN);
      port->opp.config = ((port->opp.config & ~htonl(OFPPC_PORT_DOWN))
      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;
          }
      }
  static void
  update_port(struct ofproto *ofproto, const char *name)
  {
-     struct dpif_port dpif_port;
+     struct ofproto_port ofproto_port;
      struct ofp_phy_port opp;
      struct netdev *netdev;
      struct ofport *port;
      COVERAGE_INC(ofproto_update_port);
  
      /* Fetch 'name''s location and properties from the datapath. */
-     netdev = (!dpif_port_query_by_name(ofproto->dpif, name, &dpif_port)
-               ? ofport_open(&dpif_port, &opp)
+     netdev = (!ofproto_port_query_by_name(ofproto, name, &ofproto_port)
+               ? ofport_open(&ofproto_port, &opp)
                : NULL);
      if (netdev) {
-         port = get_port(ofproto, dpif_port.port_no);
+         port = ofproto_get_port(ofproto, ofproto_port.ofp_port);
          if (port && !strcmp(netdev_get_name(port->netdev), name)) {
+             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);
          /* Any port named 'name' is gone now. */
          ofport_remove_with_name(ofproto, name);
      }
-     dpif_port_destroy(&dpif_port);
+     ofproto_port_destroy(&ofproto_port);
  }
  
  static int
  init_ports(struct ofproto *p)
  {
-     struct dpif_port_dump dump;
-     struct dpif_port dpif_port;
-     DPIF_PORT_FOR_EACH (&dpif_port, &dump, p->dpif) {
-         if (!ofport_conflicts(p, &dpif_port)) {
+     struct ofproto_port_dump dump;
+     struct ofproto_port ofproto_port;
+     OFPROTO_PORT_FOR_EACH (&ofproto_port, &dump, p) {
+         uint16_t ofp_port = ofproto_port.ofp_port;
+         if (ofproto_get_port(p, ofp_port)) {
+             VLOG_WARN_RL(&rl, "ignoring duplicate port %"PRIu16" in datapath",
+                          ofp_port);
+         } else if (shash_find(&p->port_by_name, ofproto_port.name)) {
+             VLOG_WARN_RL(&rl, "ignoring duplicate device %s in datapath",
+                          ofproto_port.name);
+         } else {
              struct ofp_phy_port opp;
              struct netdev *netdev;
  
-             netdev = ofport_open(&dpif_port, &opp);
+             netdev = ofport_open(&ofproto_port, &opp);
              if (netdev) {
                  ofport_install(p, netdev, &opp);
              }
      return 0;
  }
  \f
- /* Returns true if 'rule' should be hidden from the controller.
-  *
-  * Rules with priority higher than UINT16_MAX are set up by ofproto itself
-  * (e.g. by in-band control) and are intentionally hidden from the
-  * controller. */
- static bool
- rule_is_hidden(const struct rule *rule)
- {
-     return rule->cr.priority > UINT16_MAX;
- }
- /* Creates and returns a new rule initialized as specified.
-  *
-  * The caller is responsible for inserting the rule into the classifier (with
-  * rule_insert()). */
- static struct rule *
- rule_create(const struct cls_rule *cls_rule,
+ /* Creates a new rule initialized as specified, inserts it into 'ofproto''s
+  * flow table, and stores the new rule into '*rulep'.  Returns 0 on success,
+  * otherwise a positive errno value or OpenFlow error code. */
+ static int
+ rule_create(struct ofproto *ofproto, const struct cls_rule *cls_rule,
              const union ofp_action *actions, size_t n_actions,
              uint16_t idle_timeout, uint16_t hard_timeout,
-             ovs_be64 flow_cookie, bool send_flow_removed)
+             ovs_be64 flow_cookie, bool send_flow_removed,
+             struct rule **rulep)
  {
-     struct rule *rule = xzalloc(sizeof *rule);
+     struct rule *rule;
+     int error;
+     rule = ofproto->ofproto_class->rule_alloc();
+     if (!rule) {
+         error = ENOMEM;
+         goto error;
+     }
+     rule->ofproto = ofproto;
      rule->cr = *cls_rule;
+     rule->flow_cookie = flow_cookie;
+     rule->created = time_msec();
      rule->idle_timeout = idle_timeout;
      rule->hard_timeout = hard_timeout;
-     rule->flow_cookie = flow_cookie;
-     rule->used = rule->created = time_msec();
      rule->send_flow_removed = send_flow_removed;
-     list_init(&rule->facets);
      if (n_actions > 0) {
-         rule->n_actions = n_actions;
          rule->actions = xmemdup(actions, n_actions * sizeof *actions);
+     } else {
+         rule->actions = NULL;
      }
+     rule->n_actions = n_actions;
  
-     return rule;
- }
+     error = ofproto->ofproto_class->rule_construct(rule);
+     if (error) {
+         ofproto_rule_destroy__(rule);
+         goto error;
+     }
  
- static struct rule *
- rule_from_cls_rule(const struct cls_rule *cls_rule)
- {
-     return cls_rule ? CONTAINER_OF(cls_rule, struct rule, cr) : NULL;
+     *rulep = rule;
+     return 0;
+ error:
+     VLOG_WARN_RL(&rl, "%s: failed to create rule (%s)",
+                  ofproto->name, strerror(error));
+     *rulep = NULL;
+     return error;
  }
  
  static void
rule_free(struct rule *rule)
ofproto_rule_destroy__(struct rule *rule)
  {
      free(rule->actions);
-     free(rule);
+     rule->ofproto->ofproto_class->rule_dealloc(rule);
  }
  
- /* Destroys 'rule' and iterates through all of its facets and revalidates them,
-  * destroying any that no longer has a rule (which is probably all of them).
-  *
-  * The caller must have already removed 'rule' from the classifier. */
- static void
- rule_destroy(struct ofproto *ofproto, struct rule *rule)
+ /* Destroys 'rule' and removes it from the flow table and the datapath. */
+ void
+ ofproto_rule_destroy(struct rule *rule)
  {
-     struct facet *facet, *next_facet;
-     LIST_FOR_EACH_SAFE (facet, next_facet, list_node, &rule->facets) {
-         facet_revalidate(ofproto, facet);
+     if (rule) {
+         rule->ofproto->ofproto_class->rule_destruct(rule);
+         ofproto_rule_destroy__(rule);
      }
-     rule_free(rule);
  }
  
  /* Returns true if 'rule' has an OpenFlow OFPAT_OUTPUT or OFPAT_ENQUEUE action
@@@ -1409,1128 -1320,133 +1320,133 @@@ rule_has_out_port(const struct rule *ru
      return false;
  }
  
- /* Executes, within 'ofproto', the 'n_actions' actions in 'actions' on
-  * 'packet', which arrived on 'in_port'.
-  *
-  * Takes ownership of 'packet'. */
- static bool
- execute_odp_actions(struct ofproto *ofproto, const struct flow *flow,
-                     const struct nlattr *odp_actions, size_t actions_len,
-                     struct ofpbuf *packet)
- {
-     if (actions_len == NLA_ALIGN(NLA_HDRLEN + sizeof(uint64_t))
-         && odp_actions->nla_type == ODP_ACTION_ATTR_CONTROLLER) {
-         /* As an optimization, avoid a round-trip from userspace to kernel to
-          * userspace.  This also avoids possibly filling up kernel packet
-          * buffers along the way. */
-         struct dpif_upcall upcall;
-         upcall.type = DPIF_UC_ACTION;
-         upcall.packet = packet;
-         upcall.key = NULL;
-         upcall.key_len = 0;
-         upcall.userdata = nl_attr_get_u64(odp_actions);
-         upcall.sample_pool = 0;
-         upcall.actions = NULL;
-         upcall.actions_len = 0;
-         send_packet_in(ofproto, &upcall, flow, false);
-         return true;
-     } else {
-         int error;
-         error = dpif_execute(ofproto->dpif, odp_actions, actions_len, packet);
-         ofpbuf_delete(packet);
-         return !error;
-     }
- }
- /* Executes the actions indicated by 'facet' on 'packet' and credits 'facet''s
+ /* Executes the actions indicated by 'rule' on 'packet' and credits 'rule''s
   * statistics appropriately.  'packet' must have at least sizeof(struct
   * ofp_packet_in) bytes of headroom.
   *
-  * For correct results, 'packet' must actually be in 'facet''s flow; that is,
-  * applying flow_extract() to 'packet' would yield the same flow as
-  * 'facet->flow'.
-  *
-  * 'facet' must have accurately composed ODP actions; that is, it must not be
-  * in need of revalidation.
-  *
-  * Takes ownership of 'packet'. */
- static void
- facet_execute(struct ofproto *ofproto, struct facet *facet,
-               struct ofpbuf *packet)
- {
-     struct dpif_flow_stats stats;
-     assert(ofpbuf_headroom(packet) >= sizeof(struct ofp_packet_in));
-     flow_extract_stats(&facet->flow, packet, &stats);
-     stats.used = time_msec();
-     if (execute_odp_actions(ofproto, &facet->flow,
-                             facet->actions, facet->actions_len, packet)) {
-         facet_update_stats(ofproto, facet, &stats);
-     }
- }
- /* Executes the actions indicated by 'rule' on 'packet' and credits 'rule''s
-  * statistics (or the statistics for one of its facets) appropriately.
-  * 'packet' must have at least sizeof(struct ofp_packet_in) bytes of headroom.
-  *
   * 'packet' doesn't necessarily have to match 'rule'.  'rule' will be credited
   * with statistics for 'packet' either way.
   *
   * Takes ownership of 'packet'. */
- static void
- rule_execute(struct ofproto *ofproto, struct rule *rule, uint16_t in_port,
-              struct ofpbuf *packet)
- {
-     struct action_xlate_ctx ctx;
-     struct ofpbuf *odp_actions;
-     struct facet *facet;
-     struct flow flow;
-     size_t size;
-     assert(ofpbuf_headroom(packet) >= sizeof(struct ofp_packet_in));
-     flow_extract(packet, 0, in_port, &flow);
-     /* First look for a related facet.  If we find one, account it to that. */
-     facet = facet_lookup_valid(ofproto, &flow);
-     if (facet && facet->rule == rule) {
-         facet_execute(ofproto, facet, packet);
-         return;
-     }
-     /* Otherwise, if 'rule' is in fact the correct rule for 'packet', then
-      * create a new facet for it and use that. */
-     if (rule_lookup(ofproto, &flow) == rule) {
-         facet = facet_create(ofproto, rule, &flow, packet);
-         facet_execute(ofproto, facet, packet);
-         facet_install(ofproto, facet, true);
-         return;
-     }
-     /* We can't account anything to a facet.  If we were to try, then that
-      * facet would have a non-matching rule, busting our invariants. */
-     action_xlate_ctx_init(&ctx, ofproto, &flow, packet);
-     odp_actions = xlate_actions(&ctx, rule->actions, rule->n_actions);
-     size = packet->size;
-     if (execute_odp_actions(ofproto, &flow, odp_actions->data,
-                             odp_actions->size, packet)) {
-         rule->used = time_msec();
-         rule->packet_count++;
-         rule->byte_count += size;
-         flow_push_stats(ofproto, rule, &flow, 1, size, rule->used);
-     }
-     ofpbuf_delete(odp_actions);
- }
- /* Inserts 'rule' into 'p''s flow table. */
- static void
- rule_insert(struct ofproto *p, struct rule *rule)
- {
-     struct rule *displaced_rule;
-     displaced_rule = rule_from_cls_rule(classifier_insert(&p->cls, &rule->cr));
-     if (displaced_rule) {
-         rule_destroy(p, displaced_rule);
-     }
-     p->need_revalidate = true;
- }
- /* Creates and returns a new facet within 'ofproto' owned by 'rule', given a
-  * 'flow' and an example 'packet' within that flow.
-  *
-  * The caller must already have determined that no facet with an identical
-  * 'flow' exists in 'ofproto' and that 'flow' is the best match for 'rule' in
-  * 'ofproto''s classifier table. */
- static struct facet *
- facet_create(struct ofproto *ofproto, struct rule *rule,
-              const struct flow *flow, const struct ofpbuf *packet)
- {
-     struct facet *facet;
-     facet = xzalloc(sizeof *facet);
-     facet->used = time_msec();
-     hmap_insert(&ofproto->facets, &facet->hmap_node, flow_hash(flow, 0));
-     list_push_back(&rule->facets, &facet->list_node);
-     facet->rule = rule;
-     facet->flow = *flow;
-     netflow_flow_init(&facet->nf_flow);
-     netflow_flow_update_time(ofproto->netflow, &facet->nf_flow, facet->used);
-     facet_make_actions(ofproto, facet, packet);
-     return facet;
- }
- static void
- facet_free(struct facet *facet)
- {
-     free(facet->actions);
-     free(facet);
- }
- /* Remove 'rule' from 'ofproto' and free up the associated memory:
-  *
-  *   - Removes 'rule' from the classifier.
-  *
-  *   - If 'rule' has facets, revalidates them (and possibly uninstalls and
-  *     destroys them), via rule_destroy().
-  */
- static void
- rule_remove(struct ofproto *ofproto, struct rule *rule)
- {
-     COVERAGE_INC(ofproto_del_rule);
-     ofproto->need_revalidate = true;
-     classifier_remove(&ofproto->cls, &rule->cr);
-     rule_destroy(ofproto, rule);
- }
- /* Remove 'facet' from 'ofproto' and free up the associated memory:
-  *
-  *   - If 'facet' was installed in the datapath, uninstalls it and updates its
-  *     rule's statistics, via facet_uninstall().
-  *
-  *   - Removes 'facet' from its rule and from ofproto->facets.
-  */
- static void
- facet_remove(struct ofproto *ofproto, struct facet *facet)
- {
-     facet_uninstall(ofproto, facet);
-     facet_flush_stats(ofproto, facet);
-     hmap_remove(&ofproto->facets, &facet->hmap_node);
-     list_remove(&facet->list_node);
-     facet_free(facet);
- }
- /* Composes the ODP actions for 'facet' based on its rule's actions. */
- static void
- facet_make_actions(struct ofproto *p, struct facet *facet,
-                    const struct ofpbuf *packet)
- {
-     const struct rule *rule = facet->rule;
-     struct ofpbuf *odp_actions;
-     struct action_xlate_ctx ctx;
-     action_xlate_ctx_init(&ctx, p, &facet->flow, packet);
-     odp_actions = xlate_actions(&ctx, rule->actions, rule->n_actions);
-     facet->tags = ctx.tags;
-     facet->may_install = ctx.may_set_up_flow;
-     facet->nf_flow.output_iface = ctx.nf_output_iface;
-     if (facet->actions_len != odp_actions->size
-         || memcmp(facet->actions, odp_actions->data, odp_actions->size)) {
-         free(facet->actions);
-         facet->actions_len = odp_actions->size;
-         facet->actions = xmemdup(odp_actions->data, odp_actions->size);
-     }
-     ofpbuf_delete(odp_actions);
- }
- /* Updates 'facet''s flow in the datapath setting its actions to 'actions_len'
-  * bytes of actions in 'actions'.  If 'stats' is non-null, statistics counters
-  * in the datapath will be zeroed and 'stats' will be updated with traffic new
-  * since 'facet' was last updated.
-  *
-  * Returns 0 if successful, otherwise a positive errno value.*/
- static int
- facet_put__(struct ofproto *ofproto, struct facet *facet,
-             const struct nlattr *actions, size_t actions_len,
-             struct dpif_flow_stats *stats)
- {
-     struct odputil_keybuf keybuf;
-     enum dpif_flow_put_flags flags;
-     struct ofpbuf key;
-     int ret;
-     flags = DPIF_FP_CREATE | DPIF_FP_MODIFY;
-     if (stats) {
-         flags |= DPIF_FP_ZERO_STATS;
-     }
-     ofpbuf_use_stack(&key, &keybuf, sizeof keybuf);
-     odp_flow_key_from_flow(&key, &facet->flow);
-     ret = dpif_flow_put(ofproto->dpif, flags, key.data, key.size,
-                         actions, actions_len, stats);
-     if (stats) {
-         facet_reset_dp_stats(facet, stats);
-     }
-     return ret;
- }
- /* If 'facet' is installable, inserts or re-inserts it into 'p''s datapath.  If
-  * 'zero_stats' is true, clears any existing statistics from the datapath for
-  * 'facet'. */
- static void
- facet_install(struct ofproto *p, struct facet *facet, bool zero_stats)
- {
-     struct dpif_flow_stats stats;
-     if (facet->may_install
-         && !facet_put__(p, facet, facet->actions, facet->actions_len,
-                         zero_stats ? &stats : NULL)) {
-         facet->installed = true;
-     }
- }
- /* Ensures that the bytes in 'facet', plus 'extra_bytes', have been passed up
-  * to the accounting hook function in the ofhooks structure. */
- static void
- facet_account(struct ofproto *ofproto,
-               struct facet *facet, uint64_t extra_bytes)
- {
-     uint64_t total_bytes = facet->byte_count + extra_bytes;
-     if (ofproto->ofhooks->account_flow_cb
-         && total_bytes > facet->accounted_bytes)
-     {
-         ofproto->ofhooks->account_flow_cb(
-             &facet->flow, facet->tags, facet->actions, facet->actions_len,
-             total_bytes - facet->accounted_bytes, ofproto->aux);
-         facet->accounted_bytes = total_bytes;
-     }
- }
- /* If 'rule' is installed in the datapath, uninstalls it. */
- static void
- facet_uninstall(struct ofproto *p, struct facet *facet)
- {
-     if (facet->installed) {
-         struct odputil_keybuf keybuf;
-         struct dpif_flow_stats stats;
-         struct ofpbuf key;
-         int error;
-         ofpbuf_use_stack(&key, &keybuf, sizeof keybuf);
-         odp_flow_key_from_flow(&key, &facet->flow);
-         error = dpif_flow_del(p->dpif, key.data, key.size, &stats);
-         facet_reset_dp_stats(facet, &stats);
-         if (!error) {
-             facet_update_stats(p, facet, &stats);
-         }
-         facet->installed = false;
-     } else {
-         assert(facet->dp_packet_count == 0);
-         assert(facet->dp_byte_count == 0);
-     }
- }
- /* Returns true if the only action for 'facet' is to send to the controller.
-  * (We don't report NetFlow expiration messages for such facets because they
-  * are just part of the control logic for the network, not real traffic). */
- static bool
- facet_is_controller_flow(struct facet *facet)
- {
-     return (facet
-             && facet->rule->n_actions == 1
-             && action_outputs_to_port(&facet->rule->actions[0],
-                                       htons(OFPP_CONTROLLER)));
- }
- /* Resets 'facet''s datapath statistics counters.  This should be called when
-  * 'facet''s statistics are cleared in the datapath.  If 'stats' is non-null,
-  * it should contain the statistics returned by dpif when 'facet' was reset in
-  * the datapath.  'stats' will be modified to only included statistics new
-  * since 'facet' was last updated. */
- static void
- facet_reset_dp_stats(struct facet *facet, struct dpif_flow_stats *stats)
- {
-     if (stats && facet->dp_packet_count <= stats->n_packets
-         && facet->dp_byte_count <= stats->n_bytes) {
-         stats->n_packets -= facet->dp_packet_count;
-         stats->n_bytes -= facet->dp_byte_count;
-     }
-     facet->dp_packet_count = 0;
-     facet->dp_byte_count = 0;
- }
- /* Folds all of 'facet''s statistics into its rule.  Also updates the
-  * accounting ofhook and emits a NetFlow expiration if appropriate.  All of
-  * 'facet''s statistics in the datapath should have been zeroed and folded into
-  * its packet and byte counts before this function is called. */
- static void
- facet_flush_stats(struct ofproto *ofproto, struct facet *facet)
- {
-     assert(!facet->dp_byte_count);
-     assert(!facet->dp_packet_count);
-     facet_push_stats(ofproto, facet);
-     facet_account(ofproto, facet, 0);
-     if (ofproto->netflow && !facet_is_controller_flow(facet)) {
-         struct ofexpired expired;
-         expired.flow = facet->flow;
-         expired.packet_count = facet->packet_count;
-         expired.byte_count = facet->byte_count;
-         expired.used = facet->used;
-         netflow_expire(ofproto->netflow, &facet->nf_flow, &expired);
-     }
-     facet->rule->packet_count += facet->packet_count;
-     facet->rule->byte_count += facet->byte_count;
-     /* Reset counters to prevent double counting if 'facet' ever gets
-      * reinstalled. */
-     facet->packet_count = 0;
-     facet->byte_count = 0;
-     facet->rs_packet_count = 0;
-     facet->rs_byte_count = 0;
-     facet->accounted_bytes = 0;
-     netflow_flow_clear(&facet->nf_flow);
- }
- /* Searches 'ofproto''s table of facets for one exactly equal to 'flow'.
-  * Returns it if found, otherwise a null pointer.
-  *
-  * The returned facet might need revalidation; use facet_lookup_valid()
-  * instead if that is important. */
- static struct facet *
- facet_find(struct ofproto *ofproto, const struct flow *flow)
- {
-     struct facet *facet;
-     HMAP_FOR_EACH_WITH_HASH (facet, hmap_node, flow_hash(flow, 0),
-                              &ofproto->facets) {
-         if (flow_equal(flow, &facet->flow)) {
-             return facet;
-         }
-     }
-     return NULL;
- }
- /* Searches 'ofproto''s table of facets for one exactly equal to 'flow'.
-  * Returns it if found, otherwise a null pointer.
-  *
-  * The returned facet is guaranteed to be valid. */
- static struct facet *
- facet_lookup_valid(struct ofproto *ofproto, const struct flow *flow)
- {
-     struct facet *facet = facet_find(ofproto, flow);
-     /* The facet we found might not be valid, since we could be in need of
-      * revalidation.  If it is not valid, don't return it. */
-     if (facet
-         && ofproto->need_revalidate
-         && !facet_revalidate(ofproto, facet)) {
-         COVERAGE_INC(ofproto_invalidated);
-         return NULL;
-     }
-     return facet;
- }
- /* Re-searches 'ofproto''s classifier for a rule matching 'facet':
-  *
-  *   - If the rule found is different from 'facet''s current rule, moves
-  *     'facet' to the new rule and recompiles its actions.
-  *
-  *   - If the rule found is the same as 'facet''s current rule, leaves 'facet'
-  *     where it is and recompiles its actions anyway.
-  *
-  *   - If there is none, destroys 'facet'.
-  *
-  * Returns true if 'facet' still exists, false if it has been destroyed. */
- static bool
- facet_revalidate(struct ofproto *ofproto, struct facet *facet)
- {
-     struct action_xlate_ctx ctx;
-     struct ofpbuf *odp_actions;
-     struct rule *new_rule;
-     bool actions_changed;
-     COVERAGE_INC(facet_revalidate);
-     /* Determine the new rule. */
-     new_rule = rule_lookup(ofproto, &facet->flow);
-     if (!new_rule) {
-         /* No new rule, so delete the facet. */
-         facet_remove(ofproto, facet);
-         return false;
-     }
-     /* Calculate new ODP actions.
-      *
-      * We do not modify any 'facet' state yet, because we might need to, e.g.,
-      * emit a NetFlow expiration and, if so, we need to have the old state
-      * around to properly compose it. */
-     action_xlate_ctx_init(&ctx, ofproto, &facet->flow, NULL);
-     odp_actions = xlate_actions(&ctx, new_rule->actions, new_rule->n_actions);
-     actions_changed = (facet->actions_len != odp_actions->size
-                        || memcmp(facet->actions, odp_actions->data,
-                                  facet->actions_len));
-     /* If the ODP actions changed or the installability changed, then we need
-      * to talk to the datapath. */
-     if (actions_changed || ctx.may_set_up_flow != facet->installed) {
-         if (ctx.may_set_up_flow) {
-             struct dpif_flow_stats stats;
-             facet_put__(ofproto, facet,
-                         odp_actions->data, odp_actions->size, &stats);
-             facet_update_stats(ofproto, facet, &stats);
-         } else {
-             facet_uninstall(ofproto, facet);
-         }
-         /* The datapath flow is gone or has zeroed stats, so push stats out of
-          * 'facet' into 'rule'. */
-         facet_flush_stats(ofproto, facet);
-     }
-     /* Update 'facet' now that we've taken care of all the old state. */
-     facet->tags = ctx.tags;
-     facet->nf_flow.output_iface = ctx.nf_output_iface;
-     facet->may_install = ctx.may_set_up_flow;
-     if (actions_changed) {
-         free(facet->actions);
-         facet->actions_len = odp_actions->size;
-         facet->actions = xmemdup(odp_actions->data, odp_actions->size);
-     }
-     if (facet->rule != new_rule) {
-         COVERAGE_INC(facet_changed_rule);
-         list_remove(&facet->list_node);
-         list_push_back(&new_rule->facets, &facet->list_node);
-         facet->rule = new_rule;
-         facet->used = new_rule->created;
-         facet->rs_used = facet->used;
-     }
-     ofpbuf_delete(odp_actions);
-     return true;
- }
\f
- static void
- send_error_oh(const struct ofconn *ofconn, const struct ofp_header *oh,
-               int error)
- {
-     struct ofpbuf *buf = ofputil_encode_error_msg(error, oh);
-     if (buf) {
-         COVERAGE_INC(ofproto_error);
-         ofconn_send_reply(ofconn, buf);
-     }
- }
- static int
- handle_echo_request(struct ofconn *ofconn, const struct ofp_header *oh)
- {
-     ofconn_send_reply(ofconn, make_echo_reply(oh));
-     return 0;
- }
- static int
- handle_features_request(struct ofconn *ofconn, const struct ofp_header *oh)
- {
-     struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
-     struct ofp_switch_features *osf;
-     struct ofpbuf *buf;
-     struct ofport *port;
-     osf = make_openflow_xid(sizeof *osf, OFPT_FEATURES_REPLY, oh->xid, &buf);
-     osf->datapath_id = htonll(ofproto->datapath_id);
-     osf->n_buffers = htonl(pktbuf_capacity());
-     osf->n_tables = 2;
-     osf->capabilities = htonl(OFPC_FLOW_STATS | OFPC_TABLE_STATS |
-                               OFPC_PORT_STATS | OFPC_ARP_MATCH_IP);
-     osf->actions = htonl((1u << OFPAT_OUTPUT) |
-                          (1u << OFPAT_SET_VLAN_VID) |
-                          (1u << OFPAT_SET_VLAN_PCP) |
-                          (1u << OFPAT_STRIP_VLAN) |
-                          (1u << OFPAT_SET_DL_SRC) |
-                          (1u << OFPAT_SET_DL_DST) |
-                          (1u << OFPAT_SET_NW_SRC) |
-                          (1u << OFPAT_SET_NW_DST) |
-                          (1u << OFPAT_SET_NW_TOS) |
-                          (1u << OFPAT_SET_TP_SRC) |
-                          (1u << OFPAT_SET_TP_DST) |
-                          (1u << OFPAT_ENQUEUE));
-     HMAP_FOR_EACH (port, hmap_node, &ofproto->ports) {
-         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
   *
   * The log message mentions 'msg_type'. */
  static int
 -reject_slave_controller(struct ofconn *ofconn, const const char *msg_type)
 +reject_slave_controller(struct ofconn *ofconn, const char *msg_type)
  {
      if (ofconn_get_type(ofconn) == OFCONN_PRIMARY
          && ofconn_get_role(ofconn) == NX_ROLE_SLAVE) {
@@@ -2560,8 -1476,6 +1476,6 @@@ handle_packet_out(struct ofconn *ofconn
      struct ofp_packet_out *opo;
      struct ofpbuf payload, *buffer;
      union ofp_action *ofp_actions;
-     struct action_xlate_ctx ctx;
-     struct ofpbuf *odp_actions;
      struct ofpbuf request;
      struct flow flow;
      size_t n_ofp_actions;
          buffer = NULL;
      }
  
-     /* Extract flow, check actions. */
-     flow_extract(&payload, 0, ofp_port_to_odp_port(ntohs(opo->in_port)),
-                  &flow);
-     error = validate_actions(ofp_actions, n_ofp_actions, &flow, p->max_ports);
-     if (error) {
-         goto exit;
-     }
-     /* Send. */
-     action_xlate_ctx_init(&ctx, p, &flow, &payload);
-     odp_actions = xlate_actions(&ctx, ofp_actions, n_ofp_actions);
-     dpif_execute(p->dpif, odp_actions->data, odp_actions->size, &payload);
-     ofpbuf_delete(odp_actions);
- exit:
+     /* Send out packet. */
+     flow_extract(&payload, 0, ntohs(opo->in_port), &flow);
+     error = p->ofproto_class->packet_out(p, &payload, &flow,
+                                          ofp_actions, n_ofp_actions);
      ofpbuf_delete(buffer);
-     return 0;
+     return error;
  }
  
  static void
- update_port_config(struct ofproto *p, struct ofport *port,
-                    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)) {
              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 +1557,13 @@@ handle_port_mod(struct ofconn *ofconn, 
          return error;
      }
  
-     port = get_port(p, ofp_port_to_odp_port(ntohs(opm->port_no)));
+     port = ofproto_get_port(p, ntohs(opm->port_no));
      if (!port) {
          return ofp_mkerr(OFPET_PORT_MOD_FAILED, OFPPMFC_BAD_PORT);
      } else if (memcmp(port->opp.hw_addr, opm->hw_addr, OFP_ETH_ALEN)) {
          return ofp_mkerr(OFPET_PORT_MOD_FAILED, OFPPMFC_BAD_HW_ADDR);
      } else {
-         update_port_config(p, port, 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 +1671,20 @@@ handle_table_stats_request(struct ofcon
      struct ofproto *p = ofconn_get_ofproto(ofconn);
      struct ofp_table_stats *ots;
      struct ofpbuf *msg;
+     size_t i;
  
-     msg = start_ofp_stats_reply(request, sizeof *ots * 2);
+     msg = start_ofp_stats_reply(request, sizeof *ots * p->n_tables);
  
-     /* Classifier table. */
-     ots = append_ofp_stats_reply(sizeof *ots, ofconn, &msg);
-     memset(ots, 0, sizeof *ots);
-     strcpy(ots->name, "classifier");
-     ots->wildcards = htonl(OFPFW_ALL);
-     ots->max_entries = htonl(1024 * 1024); /* An arbitrary big number. */
-     ots->active_count = htonl(classifier_count(&p->cls));
-     put_32aligned_be64(&ots->lookup_count, htonll(0));  /* XXX */
-     put_32aligned_be64(&ots->matched_count, htonll(0)); /* XXX */
+     ots = ofpbuf_put_zeros(msg, sizeof *ots * p->n_tables);
+     for (i = 0; i < p->n_tables; i++) {
+         ots[i].table_id = i;
+         sprintf(ots[i].name, "table%d", i);
+         ots[i].wildcards = htonl(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 +1730,7 @@@ handle_port_stats_request(struct ofcon
  
      msg = start_ofp_stats_reply(oh, sizeof *ops * 16);
      if (psr->port_no != htons(OFPP_NONE)) {
-         port = get_port(p, ofp_port_to_odp_port(ntohs(psr->port_no)));
+         port = ofproto_get_port(p, ntohs(psr->port_no));
          if (port) {
              append_port_stat(port, ofconn, &msg);
          }
@@@ -2863,6 -1766,7 +1766,7 @@@ static voi
  put_ofp_flow_stats(struct ofconn *ofconn, struct rule *rule,
                     ovs_be16 out_port, struct ofpbuf **replyp)
  {
+     struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
      struct ofp_flow_stats *ofs;
      uint64_t packet_count, byte_count;
      size_t act_len, len;
      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);
      }
  }
  
- 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 +1882,8 @@@ put_nx_flow_stats(struct ofconn *ofconn
          return;
      }
  
-     rule_get_stats(rule, &packet_count, &byte_count);
+     rule->ofproto->ofproto_class->rule_get_stats(rule,
+                                                  &packet_count, &byte_count);
  
      act_len = sizeof *rule->actions * rule->n_actions;
  
@@@ -2978,6 -1914,7 +1914,7 @@@ handle_nxst_flow(struct ofconn *ofconn
  {
      struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
      struct nx_flow_stats_request *nfsr;
+     struct classifier *cls;
      struct cls_rule target;
      struct ofpbuf *reply;
      struct ofpbuf b;
  
      COVERAGE_INC(ofproto_flows_req);
      reply = start_nxstats_reply(&nfsr->nsm, 1024);
-     if (is_valid_table(nfsr->table_id)) {
+     FOR_EACH_MATCHING_TABLE (cls, nfsr->table_id, ofproto) {
          struct cls_cursor cursor;
          struct rule *rule;
  
-         cls_cursor_init(&cursor, &ofproto->cls, &target);
+         cls_cursor_init(&cursor, cls, &target);
          CLS_CURSOR_FOR_EACH (rule, cr, &cursor) {
              put_nx_flow_stats(ofconn, rule, nfsr->out_port, &reply);
          }
@@@ -3017,11 -1954,14 +1954,14 @@@ flow_stats_ds(struct rule *rule, struc
      uint64_t packet_count, byte_count;
      size_t act_len = sizeof *rule->actions * rule->n_actions;
  
-     rule_get_stats(rule, &packet_count, &byte_count);
+     rule->ofproto->ofproto_class->rule_get_stats(rule,
+                                                  &packet_count, &byte_count);
  
+     if (rule->table_id != 0) {
+         ds_put_format(results, "table_id=%"PRIu8", ", rule->table_id);
+     }
      ds_put_format(results, "duration=%llds, ",
                    (time_msec() - rule->created) / 1000);
-     ds_put_format(results, "idle=%.3fs, ", (time_msec() - rule->used) / 1000.0);
      ds_put_format(results, "priority=%u, ", rule->cr.priority);
      ds_put_format(results, "n_packets=%"PRIu64", ", packet_count);
      ds_put_format(results, "n_bytes=%"PRIu64", ", byte_count);
  void
  ofproto_get_all_flows(struct ofproto *p, struct ds *results)
  {
-     struct cls_cursor cursor;
-     struct rule *rule;
+     struct classifier *cls;
+     for (cls = &p->tables[0]; cls < &p->tables[p->n_tables]; cls++) {
+         struct cls_cursor cursor;
+         struct rule *rule;
  
-     cls_cursor_init(&cursor, &p->cls, NULL);
-     CLS_CURSOR_FOR_EACH (rule, cr, &cursor) {
-         flow_stats_ds(rule, results);
+         cls_cursor_init(&cursor, cls, NULL);
+         CLS_CURSOR_FOR_EACH (rule, cr, &cursor) {
+             flow_stats_ds(rule, results);
+         }
      }
  }
  
+ /* Obtains the NetFlow engine type and engine ID for 'ofproto' into
+  * '*engine_type' and '*engine_id', respectively. */
+ void
+ ofproto_get_netflow_ids(const struct ofproto *ofproto,
+                         uint8_t *engine_type, uint8_t *engine_id)
+ {
+     ofproto->ofproto_class->get_netflow_ids(ofproto, engine_type, engine_id);
+ }
  static void
  query_aggregate_stats(struct ofproto *ofproto, struct cls_rule *target,
                        ovs_be16 out_port, uint8_t table_id,
  {
      uint64_t total_packets = 0;
      uint64_t total_bytes = 0;
+     struct classifier *cls;
      int n_flows = 0;
  
      COVERAGE_INC(ofproto_agg_request);
  
-     if (is_valid_table(table_id)) {
+     FOR_EACH_MATCHING_TABLE (cls, table_id, ofproto) {
          struct cls_cursor cursor;
          struct rule *rule;
  
-         cls_cursor_init(&cursor, &ofproto->cls, target);
+         cls_cursor_init(&cursor, cls, target);
          CLS_CURSOR_FOR_EACH (rule, cr, &cursor) {
              if (!rule_is_hidden(rule) && rule_has_out_port(rule, out_port)) {
                  uint64_t packet_count;
                  uint64_t byte_count;
  
-                 rule_get_stats(rule, &packet_count, &byte_count);
+                 ofproto->ofproto_class->rule_get_stats(rule, &packet_count,
+                                                        &byte_count);
  
                  total_packets += packet_count;
                  total_bytes += byte_count;
@@@ -3213,8 -2168,8 +2168,8 @@@ handle_queue_stats_request(struct ofcon
          HMAP_FOR_EACH (port, hmap_node, &ofproto->ports) {
              handle_queue_stats_for_port(port, queue_id, &cbdata);
          }
-     } else if (port_no < ofproto->max_ports) {
-         port = get_port(ofproto, ofp_port_to_odp_port(port_no));
+     } else if (port_no < OFPP_MAX) {
+         port = ofproto_get_port(ofproto, port_no);
          if (port) {
              handle_queue_stats_for_port(port, queue_id, &cbdata);
          }
      return 0;
  }
  
- /* Updates 'facet''s used time.  Caller is responsible for calling
-  * facet_push_stats() to update the flows which 'facet' resubmits into. */
- static void
- facet_update_time(struct ofproto *ofproto, struct facet *facet,
-                   long long int used)
- {
-     if (used > facet->used) {
-         facet->used = used;
-         if (used > facet->rule->used) {
-             facet->rule->used = used;
-         }
-         netflow_flow_update_time(ofproto->netflow, &facet->nf_flow, used);
-     }
- }
- /* Folds the statistics from 'stats' into the counters in 'facet'.
-  *
-  * Because of the meaning of a facet's counters, it only makes sense to do this
-  * if 'stats' are not tracked in the datapath, that is, if 'stats' represents a
-  * packet that was sent by hand or if it represents statistics that have been
-  * cleared out of the datapath. */
- static void
- facet_update_stats(struct ofproto *ofproto, struct facet *facet,
-                    const struct dpif_flow_stats *stats)
- {
-     if (stats->n_packets || stats->used > facet->used) {
-         facet_update_time(ofproto, facet, stats->used);
-         facet->packet_count += stats->n_packets;
-         facet->byte_count += stats->n_bytes;
-         facet_push_stats(ofproto, facet);
-         netflow_flow_update_flags(&facet->nf_flow, stats->tcp_flags);
-     }
- }
- static void
- facet_push_stats(struct ofproto *ofproto, struct facet *facet)
- {
-     uint64_t rs_packets, rs_bytes;
-     assert(facet->packet_count >= facet->rs_packet_count);
-     assert(facet->byte_count >= facet->rs_byte_count);
-     assert(facet->used >= facet->rs_used);
-     rs_packets = facet->packet_count - facet->rs_packet_count;
-     rs_bytes = facet->byte_count - facet->rs_byte_count;
-     if (rs_packets || rs_bytes || facet->used > facet->rs_used) {
-         facet->rs_packet_count = facet->packet_count;
-         facet->rs_byte_count = facet->byte_count;
-         facet->rs_used = facet->used;
-         flow_push_stats(ofproto, facet->rule, &facet->flow,
-                         rs_packets, rs_bytes, facet->used);
-     }
- }
- struct ofproto_push {
-     struct action_xlate_ctx ctx;
-     uint64_t packets;
-     uint64_t bytes;
-     long long int used;
- };
- static void
- push_resubmit(struct action_xlate_ctx *ctx, struct rule *rule)
- {
-     struct ofproto_push *push = CONTAINER_OF(ctx, struct ofproto_push, ctx);
-     if (rule) {
-         rule->packet_count += push->packets;
-         rule->byte_count += push->bytes;
-         rule->used = MAX(push->used, rule->used);
-     }
- }
- /* Pushes flow statistics to the rules which 'flow' resubmits into given
-  * 'rule''s actions. */
- static void
- flow_push_stats(struct ofproto *ofproto, const struct rule *rule,
-                 struct flow *flow, uint64_t packets, uint64_t bytes,
-                 long long int used)
- {
-     struct ofproto_push push;
-     push.packets = packets;
-     push.bytes = bytes;
-     push.used = used;
-     action_xlate_ctx_init(&push.ctx, ofproto, flow, NULL);
-     push.ctx.resubmit_hook = push_resubmit;
-     ofpbuf_delete(xlate_actions(&push.ctx, rule->actions, rule->n_actions));
- }
  /* Implements OFPFC_ADD and the cases for OFPFC_MODIFY and OFPFC_MODIFY_STRICT
   * in which no matching flow already exists in the flow table.
   *
@@@ -3336,43 -2198,70 +2198,70 @@@ add_flow(struct ofconn *ofconn, struct 
      struct ofpbuf *packet;
      struct rule *rule;
      uint16_t in_port;
+     int buf_err;
      int error;
  
-     if (fm->flags & OFPFF_CHECK_OVERLAP
-         && classifier_rule_overlaps(&p->cls, &fm->cr)) {
-         return ofp_mkerr(OFPET_FLOW_MOD_FAILED, OFPFMFC_OVERLAP);
+     if (fm->flags & OFPFF_CHECK_OVERLAP) {
+         struct classifier *cls;
+         FOR_EACH_MATCHING_TABLE (cls, fm->table_id, p) {
+             if (classifier_rule_overlaps(cls, &fm->cr)) {
+                 return ofp_mkerr(OFPET_FLOW_MOD_FAILED, OFPFMFC_OVERLAP);
+             }
+         }
      }
  
-     error = 0;
-     if (fm->buffer_id != UINT32_MAX) {
-         error = ofconn_pktbuf_retrieve(ofconn, fm->buffer_id,
-                                        &packet, &in_port);
-     } else {
-         packet = NULL;
-         in_port = UINT16_MAX;
+     buf_err = ofconn_pktbuf_retrieve(ofconn, fm->buffer_id, &packet, &in_port);
+     error = rule_create(p, &fm->cr, fm->actions, fm->n_actions,
+                         fm->idle_timeout, fm->hard_timeout, fm->cookie,
+                         fm->flags & OFPFF_SEND_FLOW_REM, &rule);
+     if (error) {
+         ofpbuf_delete(packet);
+         return error;
      }
  
-     rule = rule_create(&fm->cr, fm->actions, fm->n_actions,
-                        fm->idle_timeout, fm->hard_timeout, fm->cookie,
-                        fm->flags & OFPFF_SEND_FLOW_REM);
-     rule_insert(p, rule);
      if (packet) {
-         rule_execute(p, rule, in_port, packet);
+         assert(!buf_err);
+         return rule_execute(rule, in_port, packet);
      }
-     return error;
+     return buf_err;
  }
  
- static struct rule *
- find_flow_strict(struct ofproto *p, const struct flow_mod *fm)
+ /* Searches 'p' for an exact match for 'fm', in the table or tables indicated
+  * by fm->table_id.  Returns 0 if no match was found, 1 if exactly one match
+  * was found, 2 if more than one match was found.  If exactly one match is
+  * found, sets '*rulep' to the match, otherwise to NULL.
+  *
+  * This implements the rules for "strict" matching explained in the comment on
+  * struct nxt_flow_mod_table_id in nicira-ext.h.
+  *
+  * Ignores hidden rules. */
+ static int
+ find_flow_strict(struct ofproto *p, const struct flow_mod *fm,
+                  struct rule **rulep)
  {
-     return rule_from_cls_rule(classifier_find_rule_exactly(&p->cls, &fm->cr));
+     struct classifier *cls;
+     *rulep = NULL;
+     FOR_EACH_MATCHING_TABLE (cls, fm->table_id, p) {
+         struct rule *rule;
+         rule = rule_from_cls_rule(classifier_find_rule_exactly(cls, &fm->cr));
+         if (rule && !rule_is_hidden(rule)) {
+             if (*rulep) {
+                 *rulep = NULL;
+                 return 2;
+             }
+             *rulep = rule;
+         }
+     }
+     return *rulep != NULL;
  }
  
  static int
  send_buffered_packet(struct ofconn *ofconn,
                       struct rule *rule, uint32_t buffer_id)
  {
-     struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
      struct ofpbuf *packet;
      uint16_t in_port;
      int error;
          return error;
      }
  
-     rule_execute(ofproto, rule, in_port, packet);
-     return 0;
+     return rule_execute(rule, in_port, packet);
  }
  \f
  /* OFPFC_MODIFY and OFPFC_MODIFY_STRICT. */
@@@ -3399,8 -2286,7 +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 +2298,30 @@@ modify_flows_loose(struct ofconn *ofcon
  {
      struct ofproto *p = ofconn_get_ofproto(ofconn);
      struct rule *match = NULL;
-     struct cls_cursor cursor;
-     struct rule *rule;
+     struct classifier *cls;
+     int error;
+     error = 0;
+     FOR_EACH_MATCHING_TABLE (cls, fm->table_id, p) {
+         struct cls_cursor cursor;
+         struct rule *rule;
  
-     cls_cursor_init(&cursor, &p->cls, &fm->cr);
-     CLS_CURSOR_FOR_EACH (rule, cr, &cursor) {
-         if (!rule_is_hidden(rule)) {
-             match = rule;
-             modify_flow(p, fm, rule);
+         cls_cursor_init(&cursor, cls, &fm->cr);
+         CLS_CURSOR_FOR_EACH (rule, cr, &cursor) {
+             if (!rule_is_hidden(rule)) {
+                 int retval = modify_flow(fm, rule);
+                 if (!retval) {
+                     match = rule;
+                 } else {
+                     error = retval;
+                 }
+             }
          }
      }
  
-     if (match) {
+     if (error) {
+         return error;
+     } else if (match) {
          /* This credits the packet to whichever flow happened to match last.
           * That's weird.  Maybe we should do a lookup for the flow that
           * actually matches the packet?  Who knows. */
@@@ -3443,57 -2341,79 +2341,79 @@@ static in
  modify_flow_strict(struct ofconn *ofconn, struct flow_mod *fm)
  {
      struct ofproto *p = ofconn_get_ofproto(ofconn);
-     struct rule *rule = find_flow_strict(p, fm);
-     if (rule && !rule_is_hidden(rule)) {
-         modify_flow(p, fm, rule);
-         return send_buffered_packet(ofconn, rule, fm->buffer_id);
-     } else {
+     struct rule *rule;
+     int error;
+     switch (find_flow_strict(p, fm, &rule)) {
+     case 0:
          return add_flow(ofconn, fm);
+     case 1:
+         error = modify_flow(fm, rule);
+         if (!error) {
+             error = send_buffered_packet(ofconn, rule, fm->buffer_id);
+         }
+         return error;
+     case 2:
+         return 0;
+     default:
+         NOT_REACHED();
      }
  }
  
  /* Implements core of OFPFC_MODIFY and OFPFC_MODIFY_STRICT where 'rule' has
-  * been identified as a flow in 'p''s flow table to be modified, by changing
-  * the rule's actions to match those in 'ofm' (which is followed by 'n_actions'
-  * ofp_action[] structures). */
+  * been identified as a flow to be modified, by changing the rule's actions to
+  * match those in 'ofm' (which is followed by 'n_actions' ofp_action[]
+  * structures). */
  static int
- modify_flow(struct ofproto *p, const struct flow_mod *fm, struct rule *rule)
+ modify_flow(const struct flow_mod *fm, struct rule *rule)
  {
      size_t actions_len = fm->n_actions * sizeof *rule->actions;
+     int error;
  
-     rule->flow_cookie = fm->cookie;
-     /* If the actions are the same, do nothing. */
      if (fm->n_actions == rule->n_actions
          && (!fm->n_actions
              || !memcmp(fm->actions, rule->actions, actions_len))) {
-         return 0;
+         error = 0;
+     } else {
+         error = rule->ofproto->ofproto_class->rule_modify_actions(
+             rule, fm->actions, fm->n_actions);
+         if (!error) {
+             free(rule->actions);
+             rule->actions = (fm->n_actions
+                              ? xmemdup(fm->actions, actions_len)
+                              : NULL);
+             rule->n_actions = fm->n_actions;
+         }
      }
  
-     /* Replace actions. */
-     free(rule->actions);
-     rule->actions = fm->n_actions ? xmemdup(fm->actions, actions_len) : NULL;
-     rule->n_actions = fm->n_actions;
-     p->need_revalidate = true;
+     if (!error) {
+         rule->flow_cookie = fm->cookie;
+     }
  
-     return 0;
+     return error;
  }
  \f
  /* OFPFC_DELETE implementation. */
  
- static void delete_flow(struct ofproto *, struct rule *, ovs_be16 out_port);
+ static void delete_flow(struct rule *, ovs_be16 out_port);
  
  /* Implements OFPFC_DELETE. */
  static void
  delete_flows_loose(struct ofproto *p, const struct flow_mod *fm)
  {
-     struct rule *rule, *next_rule;
-     struct cls_cursor cursor;
+     struct classifier *cls;
+     FOR_EACH_MATCHING_TABLE (cls, fm->table_id, p) {
+         struct rule *rule, *next_rule;
+         struct cls_cursor cursor;
  
-     cls_cursor_init(&cursor, &p->cls, &fm->cr);
-     CLS_CURSOR_FOR_EACH_SAFE (rule, next_rule, cr, &cursor) {
-         delete_flow(p, rule, htons(fm->out_port));
+         cls_cursor_init(&cursor, cls, &fm->cr);
+         CLS_CURSOR_FOR_EACH_SAFE (rule, next_rule, cr, &cursor) {
+             delete_flow(rule, htons(fm->out_port));
+         }
      }
  }
  
  static void
  delete_flow_strict(struct ofproto *p, struct flow_mod *fm)
  {
-     struct rule *rule = find_flow_strict(p, fm);
-     if (rule) {
-         delete_flow(p, rule, htons(fm->out_port));
+     struct rule *rule;
+     if (find_flow_strict(p, fm, &rule) == 1) {
+         delete_flow(rule, htons(fm->out_port));
      }
  }
  
   * 'out_port' is htons(OFPP_NONE) or if 'rule' actually outputs to the
   * specified 'out_port'. */
  static void
- delete_flow(struct ofproto *p, struct rule *rule, ovs_be16 out_port)
+ delete_flow(struct rule *rule, ovs_be16 out_port)
  {
      if (rule_is_hidden(rule)) {
          return;
          return;
      }
  
-     rule_send_removed(p, rule, OFPRR_DELETE);
-     rule_remove(p, rule);
+     ofproto_rule_send_removed(rule, OFPRR_DELETE);
+     ofproto_rule_destroy(rule);
+ }
+ static void
+ ofproto_rule_send_removed(struct rule *rule, uint8_t reason)
+ {
+     struct ofputil_flow_removed fr;
+     if (rule_is_hidden(rule) || !rule->send_flow_removed) {
+         return;
+     }
+     fr.rule = rule->cr;
+     fr.cookie = rule->flow_cookie;
+     fr.reason = reason;
+     calc_flow_duration__(rule->created, &fr.duration_sec, &fr.duration_nsec);
+     fr.idle_timeout = rule->idle_timeout;
+     rule->ofproto->ofproto_class->rule_get_stats(rule, &fr.packet_count,
+                                                  &fr.byte_count);
+     connmgr_send_flow_removed(rule->ofproto->connmgr, &fr);
+ }
+ /* Sends an OpenFlow "flow removed" message with the given 'reason' (either
+  * OFPRR_HARD_TIMEOUT or OFPRR_IDLE_TIMEOUT), and then removes 'rule' from its
+  * ofproto.
+  *
+  * ofproto implementation ->run() functions should use this function to expire
+  * OpenFlow flows. */
+ void
+ ofproto_rule_expire(struct rule *rule, uint8_t reason)
+ {
+     assert(reason == OFPRR_HARD_TIMEOUT || reason == OFPRR_IDLE_TIMEOUT);
+     ofproto_rule_send_removed(rule, reason);
+     ofproto_rule_destroy(rule);
  }
  \f
  static int
@@@ -3542,7 -2496,8 +2496,8 @@@ handle_flow_mod(struct ofconn *ofconn, 
          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;
      }
          return ofp_mkerr(OFPET_FLOW_MOD_FAILED, OFPFMFC_ALL_TABLES_FULL);
      }
  
-     error = validate_actions(fm.actions, fm.n_actions,
-                              &fm.cr.flow, p->max_ports);
-     if (error) {
-         return error;
-     }
      switch (fm.command) {
      case OFPFC_ADD:
          return add_flow(ofconn, &fm);
          return 0;
  
      default:
+         if (fm.command > 0xff) {
+             VLOG_WARN_RL(&rl, "flow_mod has explicit table_id but "
+                          "flow_mod_table_id extension is not enabled");
+         }
          return ofp_mkerr(OFPET_FLOW_MOD_FAILED, OFPFMFC_BAD_COMMAND);
      }
  }
@@@ -3615,6 -2568,17 +2568,17 @@@ handle_role_request(struct ofconn *ofco
      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 +2655,9 @@@ handle_openflow__(struct ofconn *ofconn
      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 +2735,12 @@@ handle_openflow(struct ofconn *ofconn, 
      COVERAGE_INC(ofproto_recv_openflow);
  }
  \f
- static void
- handle_miss_upcall(struct ofproto *p, struct dpif_upcall *upcall)
- {
-     struct facet *facet;
-     struct flow flow;
-     /* Obtain in_port and tun_id, at least. */
-     odp_flow_key_to_flow(upcall->key, upcall->key_len, &flow);
-     /* Set header pointers in 'flow'. */
-     flow_extract(upcall->packet, flow.tun_id, flow.in_port, &flow);
-     if (cfm_should_process_flow(&flow)) {
-         ofproto_process_cfm(p, &flow, upcall->packet);
-         ofpbuf_delete(upcall->packet);
-         return;
-     } else if (p->ofhooks->special_cb
-                && !p->ofhooks->special_cb(&flow, upcall->packet, p->aux)) {
-         ofpbuf_delete(upcall->packet);
-         return;
-     }
-     /* Check with in-band control to see if this packet should be sent
-      * to the local port regardless of the flow table. */
-     if (connmgr_msg_in_hook(p->connmgr, &flow, upcall->packet)) {
-         ofproto_send_packet(p, ODPP_LOCAL, 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 +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 +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
- };
diff --combined tests/ofproto-macros.at
@@@ -7,14 -7,8 +7,14 @@@ 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], [ignore], [ignore])
 +     [0], [], [stderr])
 +   AT_CHECK([[sed < stderr '
 +/vlog|INFO|opened log file/d
 +/openflowd|INFO|Open vSwitch version/d
 +/openflowd|INFO|OpenFlow protocol version/d
 +/ofproto|INFO|using datapath ID/d
 +/ofproto|INFO|datapath ID changed to fedcba9876543210/d']])
  ])
  
  m4_define([OFPROTO_STOP],
diff --combined tests/ovs-ofctl.at
@@@ -14,11 -14,11 +14,11 @@@ actions=note:41.42.43,note:00.01.02.03.
  tun_id=0x1234,cookie=0x5678,actions=flood
  actions=set_tunnel:0x1234,set_tunnel64:0x9876,set_tunnel:0x123456789
  actions=multipath(eth_src, 50, hrw, 12, 0, NXM_NX_REG0[0..3]),multipath(symmetric_l4, 1024, iter_hash, 5000, 5050, NXM_NX_REG0[0..12])
- actions=drop
table=1,actions=drop
  tun_id=0x1234000056780000/0xffff0000ffff0000,actions=drop
  ]])
  AT_CHECK([ovs-ofctl parse-flows flows.txt
 -], [0], [stdout], [stderr])
 +], [0], [stdout])
  AT_CHECK([[sed 's/ (xid=0x[0-9a-fA-F]*)//' stdout]], [0], 
  [[OFPT_FLOW_MOD: ADD tcp,tp_src=123 actions=FLOOD
  OFPT_FLOW_MOD: ADD in_port=65534,dl_vlan=9,dl_src=00:0a:e4:25:6b:b0 actions=drop
@@@ -32,9 -32,21 +32,10 @@@ NXT_SET_FLOW_FORMAT: format=nx
  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_CHECK([sed 's/.*|//' stderr], [0], [dnl
 -normalization changed ofp_match, details:
 - pre: wildcards=  0x3820f8  in_port=65534  dl_src=00:0a:e4:25:6b:b0  dl_dst=00:00:00:00:00:00  dl_vlan=    9  dl_vlan_pcp=  0  dl_type=     0  nw_tos=   0  nw_proto=   0  nw_src=         0  nw_dst=         0  tp_src=    0  tp_dst=    0
 -post: wildcards=  0x3ffff8  in_port=65534  dl_src=00:0a:e4:25:6b:b0  dl_dst=00:00:00:00:00:00  dl_vlan=    9  dl_vlan_pcp=  0  dl_type=     0  nw_tos=   0  nw_proto=   0  nw_src=         0  nw_dst=         0  tp_src=    0  tp_dst=    0
 -normalization changed ofp_match, details:
 - pre: wildcards=  0x3820ff  in_port=    0  dl_src=00:00:00:00:00:00  dl_dst=00:00:00:00:00:00  dl_vlan=    0  dl_vlan_pcp=  0  dl_type=     0  nw_tos=   0  nw_proto=   0  nw_src=         0  nw_dst=         0  tp_src=    0  tp_dst=    0
 -post: wildcards=  0x3fffff  in_port=    0  dl_src=00:00:00:00:00:00  dl_dst=00:00:00:00:00:00  dl_vlan=    0  dl_vlan_pcp=  0  dl_type=     0  nw_tos=   0  nw_proto=   0  nw_src=         0  nw_dst=         0  tp_src=    0  tp_dst=    0
 -normalization changed ofp_match, details:
 - pre: wildcards=  0x3820ff  in_port=    0  dl_src=00:00:00:00:00:00  dl_dst=00:00:00:00:00:00  dl_vlan=    0  dl_vlan_pcp=  0  dl_type=     0  nw_tos=   0  nw_proto=   0  nw_src=         0  nw_dst=         0  tp_src=    0  tp_dst=    0
 -post: wildcards=  0x3fffff  in_port=    0  dl_src=00:00:00:00:00:00  dl_dst=00:00:00:00:00:00  dl_vlan=    0  dl_vlan_pcp=  0  dl_type=     0  nw_tos=   0  nw_proto=   0  nw_src=         0  nw_dst=         0  tp_src=    0  tp_dst=    0
 -])
  AT_CLEANUP
  
  AT_SETUP([ovs-ofctl -F nxm parse-flows])
@@@ -297,7 -309,7 +298,7 @@@ AT_CHECK([ovs-ofctl parse-nx-match < nx
  <any>
  
  # in port
- NXM_OF_IN_PORT(fffe)
+ NXM_OF_IN_PORT(0000)
  NXM_OF_IN_PORT(fffe)
  
  # eth dst
@@@ -227,6 -227,7 +227,7 @@@ new_switch(struct switch_ *sw, struct v
      cfg.mode = (action_normal ? LSW_NORMAL
                  : learn_macs ? LSW_LEARN
                  : LSW_FLOOD);
+     cfg.exact_flows = exact_flows;
      cfg.max_idle = set_up_flows ? max_idle : -1;
      cfg.default_flows = &default_flows;
      cfg.default_queue = default_queue;
@@@ -260,6 -261,7 +261,7 @@@ static voi
  read_flow_file(const char *name)
  {
      enum nx_flow_format flow_format;
+     bool flow_mod_table_id;
      FILE *stream;
  
      stream = fopen(optarg, "r");
      }
  
      flow_format = NXFF_OPENFLOW10;
-     while (parse_ofp_flow_mod_file(&default_flows, &flow_format, stream,
-                                    OFPFC_ADD)) {
+     flow_mod_table_id = false;
+     while (parse_ofp_flow_mod_file(&default_flows,
+                                    &flow_format, &flow_mod_table_id,
+                                    stream, OFPFC_ADD)) {
          continue;
      }
  
@@@ -310,23 -314,23 +314,23 @@@ parse_options(int argc, char *argv[]
          DAEMON_OPTION_ENUMS
      };
      static struct option long_options[] = {
 -        {"hub",         no_argument, 0, 'H'},
 -        {"noflow",      no_argument, 0, 'n'},
 -        {"normal",      no_argument, 0, 'N'},
 -        {"wildcard",    no_argument, 0, 'w'},
 -        {"max-idle",    required_argument, 0, OPT_MAX_IDLE},
 -        {"mute",        no_argument, 0, OPT_MUTE},
 -        {"queue",       required_argument, 0, 'q'},
 -        {"port-queue",  required_argument, 0, 'Q'},
 -        {"with-flows",  required_argument, 0, OPT_WITH_FLOWS},
 -        {"unixctl",     required_argument, 0, OPT_UNIXCTL},
 -        {"help",        no_argument, 0, 'h'},
 -        {"version",     no_argument, 0, 'V'},
 +        {"hub",         no_argument, NULL, 'H'},
 +        {"noflow",      no_argument, NULL, 'n'},
 +        {"normal",      no_argument, NULL, 'N'},
 +        {"wildcard",    no_argument, NULL, 'w'},
 +        {"max-idle",    required_argument, NULL, OPT_MAX_IDLE},
 +        {"mute",        no_argument, NULL, OPT_MUTE},
 +        {"queue",       required_argument, NULL, 'q'},
 +        {"port-queue",  required_argument, NULL, 'Q'},
 +        {"with-flows",  required_argument, NULL, OPT_WITH_FLOWS},
 +        {"unixctl",     required_argument, NULL, OPT_UNIXCTL},
 +        {"help",        no_argument, NULL, 'h'},
 +        {"version",     no_argument, NULL, 'V'},
          DAEMON_LONG_OPTIONS,
          VLOG_LONG_OPTIONS,
          STREAM_SSL_LONG_OPTIONS,
 -        {"peer-ca-cert", required_argument, 0, OPT_PEER_CA_CERT},
 -        {0, 0, 0, 0},
 +        {"peer-ca-cert", required_argument, NULL, OPT_PEER_CA_CERT},
 +        {NULL, 0, NULL, 0},
      };
      char *short_options = long_options_to_short_options(long_options);
  
diff --combined utilities/ovs-ofctl.c
@@@ -32,7 -32,6 +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 +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"
@@@ -85,15 -85,15 +85,15 @@@ parse_options(int argc, char *argv[]
          VLOG_OPTION_ENUMS
      };
      static struct option long_options[] = {
 -        {"timeout", required_argument, 0, 't'},
 -        {"strict", no_argument, 0, OPT_STRICT},
 -        {"flow-format", required_argument, 0, 'F'},
 -        {"more", no_argument, 0, 'm'},
 -        {"help", no_argument, 0, 'h'},
 -        {"version", no_argument, 0, 'V'},
 +        {"timeout", required_argument, NULL, 't'},
 +        {"strict", no_argument, NULL, OPT_STRICT},
 +        {"flow-format", required_argument, NULL, 'F'},
 +        {"more", no_argument, NULL, 'm'},
 +        {"help", no_argument, NULL, 'h'},
 +        {"version", no_argument, NULL, 'V'},
          VLOG_LONG_OPTIONS,
          STREAM_SSL_LONG_OPTIONS,
 -        {0, 0, 0, 0},
 +        {NULL, 0, NULL, 0},
      };
      char *short_options = long_options_to_short_options(long_options);
  
@@@ -220,12 -220,17 +220,17 @@@ static voi
  open_vconn__(const char *name, const char *default_suffix,
               struct vconn **vconnp)
  {
-     struct dpif *dpif;
+     char *datapath_name, *datapath_type, *socket_name;
+     char *bridge_path;
      struct stat s;
-     char *bridge_path, *datapath_name, *datapath_type;
  
      bridge_path = xasprintf("%s/%s.%s", ovs_rundir(), name, default_suffix);
-     dp_parse_name(name, &datapath_name, &datapath_type);
+     ofproto_parse_name(name, &datapath_name, &datapath_type);
+     socket_name = xasprintf("%s/%s.%s",
+                             ovs_rundir(), datapath_name, default_suffix);
+     free(datapath_name);
+     free(datapath_type);
  
      if (strstr(name, ":")) {
          run(vconn_open_block(name, OFP_VERSION, vconnp),
          open_vconn_socket(name, vconnp);
      } else if (!stat(bridge_path, &s) && S_ISSOCK(s.st_mode)) {
          open_vconn_socket(bridge_path, vconnp);
-     } else if (!dpif_open(datapath_name, datapath_type, &dpif)) {
-         char dpif_name[IF_NAMESIZE + 1];
-         char *socket_name;
-         run(dpif_port_get_name(dpif, ODPP_LOCAL, dpif_name, sizeof dpif_name),
-             "obtaining name of %s", dpif_name);
-         dpif_close(dpif);
-         if (strcmp(dpif_name, name)) {
-             VLOG_DBG("datapath %s is named %s", name, dpif_name);
-         }
-         socket_name = xasprintf("%s/%s.%s",
-                                 ovs_rundir(), dpif_name, default_suffix);
-         if (stat(socket_name, &s)) {
-             ovs_fatal(errno, "cannot connect to %s: stat failed on %s",
-                       name, socket_name);
-         } else if (!S_ISSOCK(s.st_mode)) {
+     } else if (!stat(socket_name, &s)) {
+         if (!S_ISSOCK(s.st_mode)) {
              ovs_fatal(0, "cannot connect to %s: %s is not a socket",
                        name, socket_name);
          }
          open_vconn_socket(socket_name, vconnp);
-         free(socket_name);
      } else {
          ovs_fatal(0, "%s is not a valid connection method", name);
      }
  
-     free(datapath_name);
-     free(datapath_type);
      free(bridge_path);
+     free(socket_name);
  }
  
  static void
@@@ -626,6 -613,7 +613,7 @@@ static voi
  do_flow_mod_file__(int argc OVS_UNUSED, char *argv[], uint16_t command)
  {
      enum nx_flow_format flow_format;
+     bool flow_mod_table_id;
      struct list requests;
      struct vconn *vconn;
      FILE *file;
  
      list_init(&requests);
      flow_format = set_initial_format_for_flow_mod(&requests);
+     flow_mod_table_id = false;
  
      open_vconn(argv[1], &vconn);
-     while (parse_ofp_flow_mod_file(&requests, &flow_format, file, command)) {
+     while (parse_ofp_flow_mod_file(&requests, &flow_format, &flow_mod_table_id,
+                                    file, command)) {
          check_final_format_for_flow_mod(flow_format);
          transact_multiple_noreply(vconn, &requests);
      }
@@@ -654,6 -644,7 +644,7 @@@ static voi
  do_flow_mod__(int argc, char *argv[], uint16_t command)
  {
      enum nx_flow_format flow_format;
+     bool flow_mod_table_id;
      struct list requests;
      struct vconn *vconn;
  
  
      list_init(&requests);
      flow_format = set_initial_format_for_flow_mod(&requests);
+     flow_mod_table_id = false;
  
-     parse_ofp_flow_mod_str(&requests, &flow_format, argc > 2 ? argv[2] : "",
-                            command);
+     parse_ofp_flow_mod_str(&requests, &flow_format, &flow_mod_table_id,
+                            argc > 2 ? argv[2] : "", command);
      check_final_format_for_flow_mod(flow_format);
  
      open_vconn(argv[1], &vconn);
@@@ -1011,7 -1003,7 +1003,7 @@@ fte_insert(struct classifier *cls, cons
      fte->rule = *rule;
      fte->versions[index] = version;
  
-     old = fte_from_cls_rule(classifier_insert(cls, &fte->rule));
+     old = fte_from_cls_rule(classifier_replace(cls, &fte->rule));
      if (old) {
          fte_version_free(old->versions[index]);
          fte->versions[!index] = old->versions[!index];
@@@ -1041,10 -1033,9 +1033,9 @@@ read_flows_from_file(const char *filena
          enum nx_flow_format min_ff;
          struct ofpbuf actions;
          struct flow_mod fm;
-         uint8_t table_idx;
  
          ofpbuf_init(&actions, 64);
-         parse_ofp_str(&fm, &table_idx, &actions, ds_cstr(&s));
+         parse_ofp_str(&fm, &actions, ds_cstr(&s));
  
          version = xmalloc(sizeof *version);
          version->cookie = fm.cookie;
@@@ -1157,6 -1148,7 +1148,7 @@@ fte_make_flow_mod(const struct fte *fte
  
      fm.cr = fte->rule;
      fm.cookie = version->cookie;
+     fm.table_id = 0xff;
      fm.command = command;
      fm.idle_timeout = version->idle_timeout;
      fm.hard_timeout = version->hard_timeout;
          fm.n_actions = 0;
      }
  
-     ofm = ofputil_encode_flow_mod(&fm, flow_format);
+     ofm = ofputil_encode_flow_mod(&fm, flow_format, false);
      list_push_back(packets, &ofm->list_node);
  }
  
@@@ -1305,15 -1297,18 +1297,18 @@@ static voi
  do_parse_flow(int argc OVS_UNUSED, char *argv[])
  {
      enum nx_flow_format flow_format;
+     bool flow_mod_table_id;
      struct list packets;
  
      flow_format = NXFF_OPENFLOW10;
      if (preferred_flow_format > 0) {
          flow_format = preferred_flow_format;
      }
+     flow_mod_table_id = false;
  
      list_init(&packets);
-     parse_ofp_flow_mod_str(&packets, &flow_format, argv[1], OFPFC_ADD);
+     parse_ofp_flow_mod_str(&packets, &flow_format, &flow_mod_table_id,
+                            argv[1], OFPFC_ADD);
      print_packet_list(&packets);
  }
  
@@@ -1323,6 -1318,7 +1318,7 @@@ static voi
  do_parse_flows(int argc OVS_UNUSED, char *argv[])
  {
      enum nx_flow_format flow_format;
+     bool flow_mod_table_id;
      struct list packets;
      FILE *file;
  
      if (preferred_flow_format > 0) {
          flow_format = preferred_flow_format;
      }
+     flow_mod_table_id = false;
  
      list_init(&packets);
-     while (parse_ofp_flow_mod_file(&packets, &flow_format, file, OFPFC_ADD)) {
+     while (parse_ofp_flow_mod_file(&packets, &flow_format, &flow_mod_table_id,
+                                    file, OFPFC_ADD)) {
          print_packet_list(&packets);
      }
      fclose(file);
@@@ -28,7 -28,6 +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 +92,6 @@@ main(int argc, char *argv[]
      struct ofproto *ofproto;
      struct ofsettings s;
      int error;
-     struct dpif *dpif;
      struct netflow_options nf_options;
      const char *port;
      bool exiting;
      VLOG_INFO("Open vSwitch version %s", VERSION BUILDNR);
      VLOG_INFO("OpenFlow protocol version 0x%02x", OFP_VERSION);
  
-     error = dpif_create_and_open(s.dp_name, s.dp_type, &dpif);
+     error = ofproto_create(s.dp_name, s.dp_type, &ofproto);
      if (error) {
-         VLOG_FATAL("could not create datapath (%s)", strerror(error));
+         VLOG_FATAL("could not initialize OpenFlow switch (%s)",
+                    strerror(error));
      }
  
      /* Add ports to the datapath if requested by the user. */
                         port, strerror(error));
          }
  
-         error = dpif_port_add(dpif, netdev, NULL);
+         error = ofproto_port_add(ofproto, netdev, NULL);
          if (error) {
              VLOG_FATAL("failed to add %s as a port (%s)",
                         port, strerror(error));
          netdev_close(netdev);
      }
  
-     /* Start OpenFlow processing. */
-     error = ofproto_create(s.dp_name, s.dp_type, NULL, NULL, &ofproto);
-     if (error) {
-         VLOG_FATAL("could not initialize openflow switch (%s)",
-                    strerror(error));
-     }
+     /* Configure OpenFlow switch. */
      if (s.datapath_id) {
          ofproto_set_datapath_id(ofproto, s.datapath_id);
      }
              VLOG_FATAL("unrecoverable datapath error (%s)", strerror(error));
          }
          unixctl_server_run(unixctl);
-         dp_run();
          netdev_run();
  
          ofproto_wait(ofproto);
          unixctl_server_wait(unixctl);
-         dp_wait();
          netdev_wait();
          if (exiting) {
              poll_immediate_wake();
          poll_block();
      }
  
-     dpif_close(dpif);
+     ofproto_destroy(ofproto);
  
      return 0;
  }
@@@ -249,37 -241,37 +241,37 @@@ parse_options(int argc, char *argv[], s
          DAEMON_OPTION_ENUMS
      };
      static struct option long_options[] = {
 -        {"datapath-id", required_argument, 0, OPT_DATAPATH_ID},
 -        {"mfr-desc", required_argument, 0, OPT_MFR_DESC},
 -        {"hw-desc", required_argument, 0, OPT_HW_DESC},
 -        {"sw-desc", required_argument, 0, OPT_SW_DESC},
 -        {"serial-desc", required_argument, 0, OPT_SERIAL_DESC},
 -        {"dp-desc", required_argument, 0, OPT_DP_DESC},
 -        {"config",      required_argument, 0, 'F'},
 -        {"br-name",     required_argument, 0, OPT_BR_NAME},
 -        {"fail",        required_argument, 0, OPT_FAIL_MODE},
 -        {"inactivity-probe", required_argument, 0, OPT_INACTIVITY_PROBE},
 -        {"max-idle",    required_argument, 0, OPT_MAX_IDLE},
 -        {"max-backoff", required_argument, 0, OPT_MAX_BACKOFF},
 -        {"listen",      required_argument, 0, 'l'},
 -        {"snoop",      required_argument, 0, OPT_SNOOP},
 -        {"rate-limit",  optional_argument, 0, OPT_RATE_LIMIT},
 -        {"burst-limit", required_argument, 0, OPT_BURST_LIMIT},
 -        {"out-of-band", no_argument, 0, OPT_OUT_OF_BAND},
 -        {"in-band",     no_argument, 0, OPT_IN_BAND},
 -        {"netflow",     required_argument, 0, OPT_NETFLOW},
 -        {"ports",       required_argument, 0, OPT_PORTS},
 -        {"unixctl",     required_argument, 0, OPT_UNIXCTL},
 -        {"enable-dummy", no_argument, 0, OPT_ENABLE_DUMMY},
 -        {"verbose",     optional_argument, 0, 'v'},
 -        {"help",        no_argument, 0, 'h'},
 -        {"version",     no_argument, 0, 'V'},
 +        {"datapath-id", required_argument, NULL, OPT_DATAPATH_ID},
 +        {"mfr-desc", required_argument, NULL, OPT_MFR_DESC},
 +        {"hw-desc", required_argument, NULL, OPT_HW_DESC},
 +        {"sw-desc", required_argument, NULL, OPT_SW_DESC},
 +        {"serial-desc", required_argument, NULL, OPT_SERIAL_DESC},
 +        {"dp-desc", required_argument, NULL, OPT_DP_DESC},
 +        {"config",      required_argument, NULL, 'F'},
 +        {"br-name",     required_argument, NULL, OPT_BR_NAME},
 +        {"fail",        required_argument, NULL, OPT_FAIL_MODE},
 +        {"inactivity-probe", required_argument, NULL, OPT_INACTIVITY_PROBE},
 +        {"max-idle",    required_argument, NULL, OPT_MAX_IDLE},
 +        {"max-backoff", required_argument, NULL, OPT_MAX_BACKOFF},
 +        {"listen",      required_argument, NULL, 'l'},
 +        {"snoop",      required_argument, NULL, OPT_SNOOP},
 +        {"rate-limit",  optional_argument, NULL, OPT_RATE_LIMIT},
 +        {"burst-limit", required_argument, NULL, OPT_BURST_LIMIT},
 +        {"out-of-band", no_argument, NULL, OPT_OUT_OF_BAND},
 +        {"in-band",     no_argument, NULL, OPT_IN_BAND},
 +        {"netflow",     required_argument, NULL, OPT_NETFLOW},
 +        {"ports",       required_argument, NULL, OPT_PORTS},
 +        {"unixctl",     required_argument, NULL, OPT_UNIXCTL},
 +        {"enable-dummy", no_argument, NULL, OPT_ENABLE_DUMMY},
 +        {"verbose",     optional_argument, NULL, 'v'},
 +        {"help",        no_argument, NULL, 'h'},
 +        {"version",     no_argument, NULL, 'V'},
          DAEMON_LONG_OPTIONS,
          VLOG_LONG_OPTIONS,
          LEAK_CHECKER_LONG_OPTIONS,
          STREAM_SSL_LONG_OPTIONS,
 -        {"bootstrap-ca-cert", required_argument, 0, OPT_BOOTSTRAP_CA_CERT},
 -        {0, 0, 0, 0},
 +        {"bootstrap-ca-cert", required_argument, NULL, OPT_BOOTSTRAP_CA_CERT},
 +        {NULL, 0, NULL, 0},
      };
      char *short_options = long_options_to_short_options(long_options);
      struct ofproto_controller controller_opts;
      }
  
      /* Local vconns. */
-     dp_parse_name(argv[0], &s->dp_name, &s->dp_type);
+     ofproto_parse_name(argv[0], &s->dp_name, &s->dp_type);
  
      /* Figure out controller names. */
      s->run_forever = false;
diff --combined vswitchd/bridge.c
  
  #include <config.h>
  #include "bridge.h"
- #include "byte-order.h"
  #include <assert.h>
  #include <errno.h>
- #include <arpa/inet.h>
- #include <ctype.h>
  #include <inttypes.h>
- #include <sys/socket.h>
- #include <net/if.h>
- #include <openflow/openflow.h>
- #include <signal.h>
  #include <stdlib.h>
- #include <strings.h>
- #include <sys/stat.h>
- #include <sys/socket.h>
- #include <sys/types.h>
- #include <unistd.h>
  #include "bitmap.h"
  #include "bond.h"
  #include "cfm.h"
- #include "classifier.h"
  #include "coverage.h"
  #include "daemon.h"
  #include "dirs.h"
- #include "dpif.h"
  #include "dynamic-string.h"
- #include "flow.h"
  #include "hash.h"
  #include "hmap.h"
  #include "jsonrpc.h"
  #include "lacp.h"
  #include "list.h"
- #include "mac-learning.h"
  #include "netdev.h"
- #include "netlink.h"
- #include "odp-util.h"
  #include "ofp-print.h"
  #include "ofpbuf.h"
- #include "ofproto/netflow.h"
  #include "ofproto/ofproto.h"
- #include "ovsdb-data.h"
- #include "packets.h"
  #include "poll-loop.h"
- #include "process.h"
  #include "sha1.h"
  #include "shash.h"
  #include "socket-util.h"
  #include "stream-ssl.h"
  #include "sset.h"
- #include "svec.h"
  #include "system-stats.h"
  #include "timeval.h"
  #include "util.h"
  #include "unixctl.h"
- #include "vconn.h"
  #include "vswitchd/vswitch-idl.h"
  #include "xenserver.h"
  #include "vlog.h"
  #include "sflow_api.h"
+ #include "vlan-bitmap.h"
  
  VLOG_DEFINE_THIS_MODULE(bridge);
  
- COVERAGE_DEFINE(bridge_flush);
- COVERAGE_DEFINE(bridge_process_flow);
  COVERAGE_DEFINE(bridge_reconfigure);
  
- struct dst {
-     struct iface *iface;
-     uint16_t vlan;
- };
- struct dst_set {
-     struct dst builtin[32];
-     struct dst *dsts;
-     size_t n, allocated;
- };
- static void dst_set_init(struct dst_set *);
- static void dst_set_add(struct dst_set *, const struct dst *);
- static void dst_set_free(struct dst_set *);
  struct iface {
      /* These members are always valid. */
      struct list port_elem;      /* Element in struct port's "ifaces" list. */
+     struct hmap_node name_node; /* In struct bridge's "iface_by_name" hmap. */
      struct port *port;          /* Containing port. */
      char *name;                 /* Host network device name. */
      tag_type tag;               /* Tag associated with this interface. */
  
      /* These members are valid only after bridge_reconfigure() causes them to
       * be initialized. */
-     struct hmap_node dp_ifidx_node; /* In struct bridge's "ifaces" hmap. */
-     int dp_ifidx;               /* Index within kernel datapath. */
+     struct hmap_node ofp_port_node; /* In struct bridge's "ifaces" hmap. */
+     int ofp_port;               /* OpenFlow port number, -1 if unknown. */
      struct netdev *netdev;      /* Network device. */
      const char *type;           /* Usually same as cfg->type. */
      const struct ovsrec_interface *cfg;
  };
  
- #define MAX_MIRRORS 32
- typedef uint32_t mirror_mask_t;
- #define MIRROR_MASK_C(X) UINT32_C(X)
- BUILD_ASSERT_DECL(sizeof(mirror_mask_t) * CHAR_BIT >= MAX_MIRRORS);
  struct mirror {
+     struct uuid uuid;           /* UUID of this "mirror" record in database. */
+     struct hmap_node hmap_node; /* In struct bridge's "mirrors" hmap. */
      struct bridge *bridge;
-     size_t idx;
      char *name;
-     struct uuid uuid;           /* UUID of this "mirror" record in database. */
-     /* Selection criteria. */
-     struct sset src_ports;      /* Source port names. */
-     struct sset dst_ports;      /* Destination port names. */
-     int *vlans;
-     size_t n_vlans;
-     /* Output. */
-     struct port *out_port;
-     int out_vlan;
  };
  
- #define FLOOD_PORT ((struct port *) 1) /* The 'flood' output port. */
  struct port {
      struct bridge *bridge;
      struct hmap_node hmap_node; /* Element in struct bridge's "ports" hmap. */
      char *name;
  
-     int vlan;                   /* -1=trunk port, else a 12-bit VLAN ID. */
-     unsigned long *trunks;      /* Bitmap of trunked VLANs, if 'vlan' == -1.
-                                  * NULL if all VLANs are trunked. */
      const struct ovsrec_port *cfg;
  
      /* An ordinary bridge port has 1 interface.
       * A bridge port for bonding has at least 2 interfaces. */
      struct list ifaces;         /* List of "struct iface"s. */
-     struct lacp *lacp;          /* NULL if LACP is not enabled. */
-     /* Bonding info. */
-     struct bond *bond;
-     /* Port mirroring info. */
-     mirror_mask_t src_mirrors;  /* Mirrors triggered when packet received. */
-     mirror_mask_t dst_mirrors;  /* Mirrors triggered when packet sent. */
-     bool is_mirror_output_port; /* Does port mirroring send frames here? */
  };
  
  struct bridge {
-     struct list node;           /* Node in global list of bridges. */
+     struct hmap_node node;      /* In 'all_bridges'. */
      char *name;                 /* User-specified arbitrary name. */
-     struct mac_learning *ml;    /* MAC learning table. */
+     char *type;                 /* Datapath type. */
      uint8_t ea[ETH_ADDR_LEN];   /* Bridge Ethernet Address. */
      uint8_t default_ea[ETH_ADDR_LEN]; /* Default MAC. */
      const struct ovsrec_bridge *cfg;
      /* OpenFlow switch processing. */
      struct ofproto *ofproto;    /* OpenFlow switch. */
  
-     /* Kernel datapath information. */
-     struct dpif *dpif;          /* Datapath. */
-     struct hmap ifaces;         /* "struct iface"s indexed by dp_ifidx. */
      /* Bridge ports. */
      struct hmap ports;          /* "struct port"s indexed by name. */
-     struct shash iface_by_name; /* "struct iface"s indexed by name. */
-     /* Bonding. */
-     bool has_bonded_ports;
-     /* Flow tracking. */
-     bool flush;
+     struct hmap ifaces;         /* "struct iface"s indexed by ofp_port. */
+     struct hmap iface_by_name;  /* "struct iface"s indexed by name. */
  
      /* Port mirroring. */
-     struct mirror *mirrors[MAX_MIRRORS];
+     struct hmap mirrors;        /* "struct mirror" indexed by UUID. */
  
      /* Synthetic local port if necessary. */
      struct ovsrec_port synth_local_port;
      struct ovsrec_interface *synth_local_ifacep;
  };
  
- /* List of all bridges. */
- static struct list all_bridges = LIST_INITIALIZER(&all_bridges);
+ /* All bridges, indexed by name. */
+ static struct hmap all_bridges = HMAP_INITIALIZER(&all_bridges);
  
  /* OVSDB IDL used to obtain configuration. */
  static struct ovsdb_idl *idl;
@@@ -209,21 -133,26 +133,26 @@@ static long long int stats_timer = LLON
  #define DB_LIMIT_INTERVAL (1 * 1000) /* In milliseconds. */
  static long long int db_limiter = LLONG_MIN;
  
- static struct bridge *bridge_create(const struct ovsrec_bridge *br_cfg);
+ static void add_del_bridges(const struct ovsrec_open_vswitch *);
+ static void bridge_del_ofprotos(void);
+ static bool bridge_add_ofprotos(struct bridge *);
+ static void bridge_create(const struct ovsrec_bridge *);
  static void bridge_destroy(struct bridge *);
  static struct bridge *bridge_lookup(const char *name);
  static unixctl_cb_func bridge_unixctl_dump_flows;
  static unixctl_cb_func bridge_unixctl_reconnect;
- static int bridge_run_one(struct bridge *);
  static size_t bridge_get_controllers(const struct bridge *br,
                                       struct ovsrec_controller ***controllersp);
- static void bridge_reconfigure_one(struct bridge *);
- static void bridge_reconfigure_remotes(struct bridge *,
-                                        const struct sockaddr_in *managers,
-                                        size_t n_managers);
- static void bridge_get_all_ifaces(const struct bridge *, struct shash *ifaces);
- static void bridge_fetch_dp_ifaces(struct bridge *);
- static void bridge_flush(struct bridge *);
+ static void bridge_add_del_ports(struct bridge *);
+ static void bridge_add_ofproto_ports(struct bridge *);
+ static void bridge_del_ofproto_ports(struct bridge *);
+ static void bridge_refresh_ofp_port(struct bridge *);
+ static void bridge_configure_datapath_id(struct bridge *);
+ static void bridge_configure_netflow(struct bridge *);
+ static void bridge_configure_sflow(struct bridge *, int *sflow_bridge_number);
+ static void bridge_configure_remotes(struct bridge *,
+                                      const struct sockaddr_in *managers,
+                                      size_t n_managers);
  static void bridge_pick_local_hw_addr(struct bridge *,
                                        uint8_t ea[ETH_ADDR_LEN],
                                        struct iface **hw_addr_iface);
@@@ -231,42 -160,42 +160,42 @@@ static uint64_t bridge_pick_datapath_id
                                          const uint8_t bridge_ea[ETH_ADDR_LEN],
                                          struct iface *hw_addr_iface);
  static uint64_t dpid_from_hash(const void *, size_t nbytes);
+ static bool bridge_has_bond_fake_iface(const struct bridge *,
+                                        const char *name);
+ static bool port_is_bond_fake_iface(const struct port *);
  
- static unixctl_cb_func bridge_unixctl_fdb_show;
  static unixctl_cb_func cfm_unixctl_show;
  static unixctl_cb_func qos_unixctl_show;
  
- static void port_run(struct port *);
- static void port_wait(struct port *);
- static struct port *port_create(struct bridge *, const char *name);
- static void port_reconfigure(struct port *, const struct ovsrec_port *);
- static void port_del_ifaces(struct port *, const struct ovsrec_port *);
+ static struct port *port_create(struct bridge *, const struct ovsrec_port *);
+ static void port_add_ifaces(struct port *);
+ static void port_del_ifaces(struct port *);
  static void port_destroy(struct port *);
  static struct port *port_lookup(const struct bridge *, const char *name);
- static struct iface *port_get_an_iface(const struct port *);
- static struct port *port_from_dp_ifidx(const struct bridge *,
-                                        uint16_t dp_ifidx);
- static void port_reconfigure_lacp(struct port *);
- static void port_reconfigure_bond(struct port *);
- static void port_send_learning_packets(struct port *);
- static void mirror_create(struct bridge *, struct ovsrec_mirror *);
+ static void port_configure(struct port *);
+ static struct lacp_settings *port_configure_lacp(struct port *,
+                                                  struct lacp_settings *);
+ static void port_configure_bond(struct port *, struct bond_settings *,
+                                 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 +204,6 @@@ static void shash_from_ovs_idl_map(cha
                                     struct shash *);
  static void shash_to_ovs_idl_map(struct shash *,
                                   char ***keys, char ***values, size_t *n);
- /* Hooks into ofproto processing. */
- static struct ofhooks bridge_ofhooks;
  \f
  /* Public functions. */
  
@@@ -342,7 -268,6 +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 +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 +348,599 @@@ collect_in_band_managers(const struct o
  static void
  bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg)
  {
-     struct shash old_br, new_br;
-     struct shash_node *node;
-     struct bridge *br, *next;
      struct sockaddr_in *managers;
-     size_t n_managers;
-     size_t i;
+     struct bridge *br, *next;
      int sflow_bridge_number;
+     size_t n_managers;
  
      COVERAGE_INC(bridge_reconfigure);
  
-     collect_in_band_managers(ovs_cfg, &managers, &n_managers);
-     /* Collect old and new bridges. */
-     shash_init(&old_br);
-     shash_init(&new_br);
-     LIST_FOR_EACH (br, node, &all_bridges) {
-         shash_add(&old_br, br->name, br);
+     /* Create and destroy "struct bridge"s, "struct port"s, and "struct
+      * iface"s according to 'ovs_cfg', with only very minimal configuration
+      * otherwise.
+      *
+      * This is purely an update to bridge data structures.  Nothing is pushed
+      * down to ofproto or lower layers. */
+     add_del_bridges(ovs_cfg);
+     HMAP_FOR_EACH (br, node, &all_bridges) {
+         bridge_add_del_ports(br);
      }
-     for (i = 0; i < ovs_cfg->n_bridges; i++) {
-         const struct ovsrec_bridge *br_cfg = ovs_cfg->bridges[i];
-         if (!shash_add_once(&new_br, br_cfg->name, br_cfg)) {
-             VLOG_WARN("more than one bridge named %s", br_cfg->name);
+     /* Delete all datapaths and datapath ports that are no longer configured.
+      *
+      * The kernel will reject any attempt to add a given port to a datapath if
+      * that port already belongs to a different datapath, so we must do all
+      * port deletions before any port additions.  A datapath always has a
+      * "local port" so we must delete not-configured datapaths too. */
+     bridge_del_ofprotos();
+     HMAP_FOR_EACH (br, node, &all_bridges) {
+         if (br->ofproto) {
+             bridge_del_ofproto_ports(br);
          }
      }
  
-     /* Get rid of deleted bridges and add new bridges. */
-     LIST_FOR_EACH_SAFE (br, next, node, &all_bridges) {
-         struct ovsrec_bridge *br_cfg = shash_find_data(&new_br, br->name);
-         if (br_cfg) {
-             br->cfg = br_cfg;
-         } else {
+     /* Create datapaths and datapath ports that are missing.
+      *
+      * After this is done, we have our final set of bridges, ports, and
+      * interfaces.  Every "struct bridge" has an ofproto, every "struct port"
+      * has at least one iface, every "struct iface" has a valid ofp_port and
+      * netdev. */
+     HMAP_FOR_EACH_SAFE (br, next, node, &all_bridges) {
+         if (!br->ofproto && !bridge_add_ofprotos(br)) {
              bridge_destroy(br);
          }
      }
-     SHASH_FOR_EACH (node, &new_br) {
-         const char *br_name = node->name;
-         const struct ovsrec_bridge *br_cfg = node->data;
-         br = shash_find_data(&old_br, br_name);
-         if (br) {
-             /* If the bridge datapath type has changed, we need to tear it
-              * down and recreate. */
-             if (strcmp(br->cfg->datapath_type, br_cfg->datapath_type)) {
-                 bridge_destroy(br);
-                 bridge_create(br_cfg);
-             }
-         } else {
-             bridge_create(br_cfg);
-         }
+     HMAP_FOR_EACH (br, node, &all_bridges) {
+         bridge_refresh_ofp_port(br);
+         bridge_add_ofproto_ports(br);
      }
-     shash_destroy(&old_br);
-     shash_destroy(&new_br);
  
-     /* Reconfigure all bridges. */
-     LIST_FOR_EACH (br, node, &all_bridges) {
-         bridge_reconfigure_one(br);
-     }
+     /* Complete the configuration. */
+     sflow_bridge_number = 0;
+     collect_in_band_managers(ovs_cfg, &managers, &n_managers);
+     HMAP_FOR_EACH (br, node, &all_bridges) {
+         struct port *port;
  
-     /* Add and delete ports on all datapaths.
-      *
-      * The kernel will reject any attempt to add a given port to a datapath if
-      * that port already belongs to a different datapath, so we must do all
-      * port deletions before any port additions. */
-     LIST_FOR_EACH (br, node, &all_bridges) {
-         struct dpif_port_dump dump;
-         struct shash want_ifaces;
-         struct dpif_port dpif_port;
-         bridge_get_all_ifaces(br, &want_ifaces);
-         DPIF_PORT_FOR_EACH (&dpif_port, &dump, br->dpif) {
-             if (!shash_find(&want_ifaces, dpif_port.name)
-                 && strcmp(dpif_port.name, br->name)) {
-                 int retval = dpif_port_del(br->dpif, dpif_port.port_no);
-                 if (retval) {
-                     VLOG_WARN("failed to remove %s interface from %s: %s",
-                               dpif_port.name, dpif_name(br->dpif),
-                               strerror(retval));
-                 }
+         HMAP_FOR_EACH (port, hmap_node, &br->ports) {
+             struct iface *iface;
+             port_configure(port);
+             HMAP_FOR_EACH (iface, ofp_port_node, &br->ifaces) {
+                 iface_configure_cfm(iface);
+                 iface_configure_qos(iface, port->cfg->qos);
+                 iface_set_mac(iface);
              }
          }
-         shash_destroy(&want_ifaces);
+         bridge_configure_mirrors(br);
+         bridge_configure_datapath_id(br);
+         bridge_configure_remotes(br, managers, n_managers);
+         bridge_configure_netflow(br);
+         bridge_configure_sflow(br, &sflow_bridge_number);
      }
-     LIST_FOR_EACH (br, node, &all_bridges) {
-         struct shash cur_ifaces, want_ifaces;
-         struct dpif_port_dump dump;
-         struct dpif_port dpif_port;
+     free(managers);
  
-         /* Get the set of interfaces currently in this datapath. */
-         shash_init(&cur_ifaces);
-         DPIF_PORT_FOR_EACH (&dpif_port, &dump, br->dpif) {
-             struct dpif_port *port_info = xmalloc(sizeof *port_info);
-             dpif_port_clone(port_info, &dpif_port);
-             shash_add(&cur_ifaces, dpif_port.name, port_info);
-         }
+     /* ovs-vswitchd has completed initialization, so allow the process that
+      * forked us to exit successfully. */
+     daemonize_complete();
+ }
  
-         /* Get the set of interfaces we want on this datapath. */
-         bridge_get_all_ifaces(br, &want_ifaces);
+ /* Iterate over all ofprotos and delete any of them that do not have a
+  * configured bridge or that are the wrong type. */
+ static void
+ bridge_del_ofprotos(void)
+ {
+     struct sset names;
+     struct sset types;
+     const char *type;
  
-         hmap_clear(&br->ifaces);
-         SHASH_FOR_EACH (node, &want_ifaces) {
-             const char *if_name = node->name;
-             struct iface *iface = node->data;
-             struct dpif_port *dpif_port;
-             const char *type;
-             int error;
+     sset_init(&names);
+     sset_init(&types);
+     ofproto_enumerate_types(&types);
+     SSET_FOR_EACH (type, &types) {
+         const char *name;
  
-             type = iface ? iface->type : "internal";
-             dpif_port = shash_find_data(&cur_ifaces, if_name);
-             /* If we have a port or a netdev already, and it's not the type we
-              * want, then delete the port (if any) and close the netdev (if
-              * any). */
-             if ((dpif_port && strcmp(dpif_port->type, type))
-                 || (iface && iface->netdev
-                     && strcmp(type, netdev_get_type(iface->netdev)))) {
-                 if (dpif_port) {
-                     error = ofproto_port_del(br->ofproto, dpif_port->port_no);
-                     if (error) {
-                         continue;
-                     }
-                     dpif_port = NULL;
-                 }
-                 if (iface) {
-                     if (iface->port->bond) {
-                         /* The bond has a pointer to the netdev, so remove it
-                          * from the bond before closing the netdev.  The slave
-                          * will get added back to the bond later, after a new
-                          * netdev is available. */
-                         bond_slave_unregister(iface->port->bond, iface);
-                     }
-                     netdev_close(iface->netdev);
-                     iface->netdev = NULL;
-                 }
+         ofproto_enumerate_names(type, &names);
+         SSET_FOR_EACH (name, &names) {
+             struct bridge *br = bridge_lookup(name);
+             if (!br || strcmp(type, br->type)) {
+                 ofproto_delete(name, type);
              }
+         }
+     }
+     sset_destroy(&names);
+     sset_destroy(&types);
+ }
  
-             /* If the port doesn't exist or we don't have the netdev open,
-              * we need to do more work. */
-             if (!dpif_port || (iface && !iface->netdev)) {
-                 struct netdev_options options;
-                 struct netdev *netdev;
-                 struct shash args;
-                 /* First open the network device. */
-                 options.name = if_name;
-                 options.type = type;
-                 options.args = &args;
-                 options.ethertype = NETDEV_ETH_TYPE_NONE;
+ static bool
+ bridge_add_ofprotos(struct bridge *br)
+ {
+     int error = ofproto_create(br->name, br->type, &br->ofproto);
+     if (error) {
+         VLOG_ERR("failed to create bridge %s: %s", br->name, strerror(error));
+         return false;
+     }
+     return true;
+ }
  
-                 shash_init(&args);
-                 if (iface) {
-                     shash_from_ovs_idl_map(iface->cfg->key_options,
-                                            iface->cfg->value_options,
-                                            iface->cfg->n_options, &args);
-                 }
-                 error = netdev_open(&options, &netdev);
-                 shash_destroy(&args);
+ static void
+ port_configure(struct port *port)
+ {
+     const struct ovsrec_port *cfg = port->cfg;
+     struct bond_settings bond_settings;
+     struct lacp_settings lacp_settings;
+     struct ofproto_bundle_settings s;
+     struct iface *iface;
  
-                 if (error) {
-                     VLOG_WARN("could not open network device %s (%s)",
-                               if_name, strerror(error));
-                     continue;
-                 }
+     /* Get name. */
+     s.name = port->name;
  
-                 /* Then add the port if we haven't already. */
-                 if (!dpif_port) {
-                     error = dpif_port_add(br->dpif, netdev, NULL);
-                     if (error) {
-                         netdev_close(netdev);
-                         if (error == EFBIG) {
-                             VLOG_ERR("ran out of valid port numbers on %s",
-                                      dpif_name(br->dpif));
-                             break;
-                         } else {
-                             VLOG_WARN("failed to add %s interface to %s: %s",
-                                       if_name, dpif_name(br->dpif),
-                                       strerror(error));
-                             continue;
-                         }
-                     }
-                 }
+     /* Get slaves. */
+     s.n_slaves = 0;
+     s.slaves = xmalloc(list_size(&port->ifaces) * sizeof *s.slaves);
+     LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
+         s.slaves[s.n_slaves++] = iface->ofp_port;
+     }
  
-                 /* Update 'iface'. */
-                 if (iface) {
-                     iface->netdev = netdev;
-                 }
-             } else if (iface && iface->netdev) {
-                 struct shash args;
-                 shash_init(&args);
-                 shash_from_ovs_idl_map(iface->cfg->key_options,
-                                        iface->cfg->value_options,
-                                        iface->cfg->n_options, &args);
-                 netdev_set_config(iface->netdev, &args);
-                 shash_destroy(&args);
+     /* Get VLAN tag. */
+     s.vlan = -1;
+     if (cfg->tag) {
+         if (list_is_short(&port->ifaces)) {
+             if (*cfg->tag >= 0 && *cfg->tag <= 4095) {
+                 s.vlan = *cfg->tag;
+                 VLOG_DBG("port %s: assigning VLAN tag %d", port->name, s.vlan);
              }
+         } else {
+             /* It's possible that bonded, VLAN-tagged ports make sense.  Maybe
+              * they even work as-is.  But they have not been tested. */
+             VLOG_WARN("port %s: VLAN tags not supported on bonded ports",
+                       port->name);
          }
-         shash_destroy(&want_ifaces);
+     }
  
-         SHASH_FOR_EACH (node, &cur_ifaces) {
-             struct dpif_port *port_info = node->data;
-             dpif_port_destroy(port_info);
-             free(port_info);
-         }
-         shash_destroy(&cur_ifaces);
+     /* Get VLAN trunks. */
+     s.trunks = NULL;
+     if (s.vlan < 0 && cfg->n_trunks) {
+         s.trunks = vlan_bitmap_from_array(cfg->trunks, cfg->n_trunks);
+     } else if (s.vlan >= 0 && cfg->n_trunks) {
+         VLOG_ERR("port %s: ignoring trunks in favor of implicit vlan",
+                  port->name);
      }
-     sflow_bridge_number = 0;
-     LIST_FOR_EACH (br, node, &all_bridges) {
-         uint8_t ea[ETH_ADDR_LEN];
-         uint64_t dpid;
-         struct iface *local_iface;
-         struct iface *hw_addr_iface;
-         char *dpid_string;
  
-         bridge_fetch_dp_ifaces(br);
+     /* Get LACP settings. */
+     s.lacp = port_configure_lacp(port, &lacp_settings);
+     if (s.lacp) {
+         size_t i = 0;
  
-         /* Delete interfaces that cannot be opened.
-          *
-          * From this point forward we are guaranteed that every "struct iface"
-          * has nonnull 'netdev' and correct 'dp_ifidx'. */
-         iterate_and_prune_ifaces(br, check_iface, NULL);
-         /* Pick local port hardware address, datapath ID. */
-         bridge_pick_local_hw_addr(br, ea, &hw_addr_iface);
-         local_iface = iface_from_dp_ifidx(br, ODPP_LOCAL);
-         if (local_iface) {
-             int error = netdev_set_etheraddr(local_iface->netdev, ea);
-             if (error) {
-                 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
-                 VLOG_ERR_RL(&rl, "bridge %s: failed to set bridge "
-                             "Ethernet address: %s",
-                             br->name, strerror(error));
-             }
+         s.lacp_slaves = xmalloc(s.n_slaves * sizeof *s.lacp_slaves);
+         LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
+             iface_configure_lacp(iface, &s.lacp_slaves[i++]);
          }
-         memcpy(br->ea, ea, ETH_ADDR_LEN);
-         dpid = bridge_pick_datapath_id(br, ea, hw_addr_iface);
-         ofproto_set_datapath_id(br->ofproto, dpid);
+     } else {
+         s.lacp_slaves = NULL;
+     }
  
-         dpid_string = xasprintf("%016"PRIx64, dpid);
-         ovsrec_bridge_set_datapath_id(br->cfg, dpid_string);
-         free(dpid_string);
+     /* Get bond settings. */
+     if (s.n_slaves > 1) {
+         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 +985,7 @@@ bridge_pick_local_hw_addr(struct bridg
          struct iface *iface;
  
          /* Mirror output ports don't participate. */
-         if (port->is_mirror_output_port) {
+         if (ofproto_is_mirror_output_bundle(br->ofproto, port)) {
              continue;
          }
  
  
              /* The local port doesn't count (since we're trying to choose its
               * MAC address anyway). */
-             if (iface->dp_ifidx == ODPP_LOCAL) {
+             if (iface->ofp_port == OFPP_LOCAL) {
                  continue;
              }
  
@@@ -1196,7 -1198,6 +1198,6 @@@ iface_refresh_status(struct iface *ifac
          ovsrec_interface_set_link_speed(iface->cfg, NULL, 0);
      }
  
      ovsrec_interface_set_link_state(iface->cfg,
                                      iface_get_carrier(iface) ? "up" : "down");
  
@@@ -1221,7 -1222,7 +1222,7 @@@ iface_refresh_cfm_stats(struct iface *i
      size_t i;
  
      mon = iface->cfg->monitor;
-     cfm = ofproto_iface_get_cfm(iface->port->bridge->ofproto, iface->dp_ifidx);
+     cfm = ofproto_port_get_cfm(iface->port->bridge->ofproto, iface->ofp_port);
  
      if (!cfm || !mon) {
          return false;
  static bool
  iface_refresh_lacp_stats(struct iface *iface)
  {
-     bool *db_current = iface->cfg->lacp_current;
-     bool changed = false;
+     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 +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, "
          if (cfg) {
              struct ovsdb_idl_txn *txn = ovsdb_idl_txn_create(idl);
  
-             bridge_configure_once(cfg);
              bridge_reconfigure(cfg);
  
              ovsrec_open_vswitch_set_cur_cfg(cfg, cfg->next_cfg);
              struct ovsdb_idl_txn *txn;
  
              txn = ovsdb_idl_txn_create(idl);
-             LIST_FOR_EACH (br, node, &all_bridges) {
+             HMAP_FOR_EACH (br, node, &all_bridges) {
                  struct port *port;
  
                  HMAP_FOR_EACH (port, hmap_node, &br->ports) {
          bool changed = false;
  
          txn = ovsdb_idl_txn_create(idl);
-         LIST_FOR_EACH (br, node, &all_bridges) {
+         HMAP_FOR_EACH (br, node, &all_bridges) {
              struct port *port;
  
              HMAP_FOR_EACH (port, hmap_node, &br->ports) {
@@@ -1493,14 -1486,8 +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);
          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 +1512,7 @@@ cfm_unixctl_show(struct unixctl_conn *c
          return;
      }
  
-     cfm = ofproto_iface_get_cfm(iface->port->bridge->ofproto, iface->dp_ifidx);
+     cfm = ofproto_port_get_cfm(iface->port->bridge->ofproto, iface->ofp_port);
  
      if (!cfm) {
          unixctl_command_reply(conn, 501, "CFM not enabled");
@@@ -1666,83 -1618,48 +1618,52 @@@ qos_unixctl_show(struct unixctl_conn *c
  }
  \f
  /* Bridge reconfiguration functions. */
- static struct bridge *
+ static void
  bridge_create(const struct ovsrec_bridge *br_cfg)
  {
      struct bridge *br;
-     int error;
  
      assert(!bridge_lookup(br_cfg->name));
      br = xzalloc(sizeof *br);
  
-     error = dpif_create_and_open(br_cfg->name, br_cfg->datapath_type,
-                                  &br->dpif);
-     if (error) {
-         free(br);
-         return NULL;
-     }
-     error = ofproto_create(br_cfg->name, br_cfg->datapath_type, &bridge_ofhooks,
-                            br, &br->ofproto);
-     if (error) {
-         VLOG_ERR("failed to create switch %s: %s", br_cfg->name,
-                  strerror(error));
-         dpif_delete(br->dpif);
-         dpif_close(br->dpif);
-         free(br);
-         return NULL;
-     }
      br->name = xstrdup(br_cfg->name);
+     br->type = xstrdup(ofproto_normalize_type(br_cfg->datapath_type));
      br->cfg = br_cfg;
-     br->ml = mac_learning_create();
 -    eth_addr_nicira_random(br->default_ea);
 +
 +    /* Derive the default Ethernet address from the bridge's UUID.  This should
 +     * be unique and it will be stable between ovs-vswitchd runs.  */
 +    memcpy(br->default_ea, &br_cfg->header_.uuid, ETH_ADDR_LEN);
 +    eth_addr_mark_random(br->default_ea);
  
      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 -1669,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 -1715,13 +1719,13 @@@ bridge_unixctl_reconnect(struct unixctl
          }
          ofproto_reconnect_controllers(br->ofproto);
      } else {
-         LIST_FOR_EACH (br, node, &all_bridges) {
+         HMAP_FOR_EACH (br, node, &all_bridges) {
              ofproto_reconnect_controllers(br->ofproto);
          }
      }
      unixctl_command_reply(conn, 200, NULL);
  }
  
- static int
- bridge_run_one(struct bridge *br)
- {
-     struct port *port;
-     int error;
-     error = ofproto_run1(br->ofproto);
-     if (error) {
-         return error;
-     }
-     mac_learning_run(br->ml, ofproto_get_revalidate_set(br->ofproto));
-     HMAP_FOR_EACH (port, hmap_node, &br->ports) {
-         port_run(port);
-     }
-     error = ofproto_run2(br->ofproto, br->flush);
-     br->flush = false;
-     return error;
- }
  static size_t
  bridge_get_controllers(const struct bridge *br,
                         struct ovsrec_controller ***controllersp)
      return n_controllers;
  }
  
+ /* Adds and deletes "struct port"s and "struct iface"s under 'br' to match
+  * those configured in 'br->cfg'. */
  static void
- bridge_reconfigure_one(struct bridge *br)
+ bridge_add_del_ports(struct bridge *br)
  {
-     enum ofproto_fail_mode fail_mode;
      struct port *port, *next;
      struct shash_node *node;
      struct shash new_ports;
                        br->name, name);
          }
      }
-     if (!shash_find(&new_ports, br->name)) {
-         struct dpif_port dpif_port;
-         char *type;
+     if (bridge_get_controllers(br, NULL)
+         && !shash_find(&new_ports, br->name)) {
          VLOG_WARN("bridge %s: no port named %s, synthesizing one",
                    br->name, br->name);
  
-         dpif_port_query_by_number(br->dpif, ODPP_LOCAL, &dpif_port);
-         type = xstrdup(dpif_port.type ? dpif_port.type : "internal");
-         dpif_port_destroy(&dpif_port);
          br->synth_local_port.interfaces = &br->synth_local_ifacep;
          br->synth_local_port.n_interfaces = 1;
          br->synth_local_port.name = br->name;
  
          br->synth_local_iface.name = br->name;
-         free(br->synth_local_iface.type);
-         br->synth_local_iface.type = type;
+         br->synth_local_iface.type = "internal";
  
          br->synth_local_ifacep = &br->synth_local_iface;
  
      }
  
      /* Get rid of deleted ports.
-      * Get rid of deleted interfaces on ports that still exist. */
+      * Get rid of deleted interfaces on ports that still exist.
+      * Update 'cfg' of ports that still exist. */
      HMAP_FOR_EACH_SAFE (port, next, hmap_node, &br->ports) {
-         const struct ovsrec_port *port_cfg;
-         port_cfg = shash_find_data(&new_ports, port->name);
-         if (!port_cfg) {
+         port->cfg = shash_find_data(&new_ports, port->name);
+         if (!port->cfg) {
              port_destroy(port);
          } else {
-             port_del_ifaces(port, port_cfg);
+             port_del_ifaces(port);
          }
      }
  
      /* Create new ports.
-      * Add new interfaces to existing ports.
-      * Reconfigure existing ports. */
+      * Add new interfaces to existing ports. */
      SHASH_FOR_EACH (node, &new_ports) {
          struct port *port = port_lookup(br, node->name);
          if (!port) {
-             port = port_create(br, node->name);
+             struct ovsrec_port *cfg = node->data;
+             port = port_create(br, cfg);
          }
-         port_reconfigure(port, node->data);
+         port_add_ifaces(port);
          if (list_is_empty(&port->ifaces)) {
              VLOG_WARN("bridge %s: port %s has no interfaces, dropping",
                        br->name, port->name);
          }
      }
      shash_destroy(&new_ports);
-     /* Set the fail-mode */
-     fail_mode = !br->cfg->fail_mode
-                 || !strcmp(br->cfg->fail_mode, "standalone")
-                     ? OFPROTO_FAIL_STANDALONE
-                     : OFPROTO_FAIL_SECURE;
-     if (ofproto_get_fail_mode(br->ofproto) != fail_mode
-         && !ofproto_has_primary_controller(br->ofproto)) {
-         ofproto_flush_flows(br->ofproto);
-     }
-     ofproto_set_fail_mode(br->ofproto, fail_mode);
-     /* Delete all flows if we're switching from connected to standalone or vice
-      * versa.  (XXX Should we delete all flows if we are switching from one
-      * controller to another?) */
-     /* Configure OpenFlow controller connection snooping. */
-     if (!ofproto_has_snoops(br->ofproto)) {
-         struct sset snoops;
-         sset_init(&snoops);
-         sset_add_and_free(&snoops, xasprintf("punix:%s/%s.snoop",
-                                              ovs_rundir(), br->name));
-         ofproto_set_snoops(br->ofproto, &snoops);
-         sset_destroy(&snoops);
-     }
-     mirror_reconfigure(br);
  }
  
  /* Initializes 'oc' appropriately as a management service controller for
@@@ -1995,7 -1853,7 +1857,7 @@@ bridge_configure_local_iface_netdev(str
      struct in_addr ip;
  
      /* If there's no local interface or no IP address, give up. */
-     local_iface = iface_from_dp_ifidx(br, ODPP_LOCAL);
+     local_iface = iface_from_ofp_port(br, OFPP_LOCAL);
      if (!local_iface || !c->local_ip || !inet_aton(c->local_ip, &ip)) {
          return;
      }
  }
  
  static void
- bridge_reconfigure_remotes(struct bridge *br,
-                            const struct sockaddr_in *managers,
-                            size_t n_managers)
+ bridge_configure_remotes(struct bridge *br,
+                          const struct sockaddr_in *managers, size_t n_managers)
  {
      const char *disable_ib_str, *queue_id_str;
      bool disable_in_band = false;
  
      struct ovsrec_controller **controllers;
      size_t n_controllers;
-     bool had_primary;
+     enum ofproto_fail_mode fail_mode;
  
      struct ofproto_controller *ocs;
      size_t n_ocs;
      } else {
          ofproto_set_extra_in_band_remotes(br->ofproto, managers, n_managers);
      }
-     had_primary = ofproto_has_primary_controller(br->ofproto);
  
      n_controllers = bridge_get_controllers(br, &controllers);
  
  
          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 -2012,9 +2016,9 @@@ get_interface_other_config(const struc
      return value ? value : default_value;
  }
  
+ /* Deletes interfaces from 'port' that are no longer configured for it. */
  static void
- port_del_ifaces(struct port *port, const struct ovsrec_port *cfg)
+ port_del_ifaces(struct port *port)
  {
      struct iface *iface, *next;
      struct sset new_ifaces;
  
      /* Collect list of new interfaces. */
      sset_init(&new_ifaces);
-     for (i = 0; i < cfg->n_interfaces; i++) {
-         const char *name = cfg->interfaces[i]->name;
-         sset_add(&new_ifaces, name);
+     for (i = 0; i < port->cfg->n_interfaces; i++) {
+         const char *name = port->cfg->interfaces[i]->name;
+         const char *type = port->cfg->interfaces[i]->name;
+         if (strcmp(type, "null")) {
+             sset_add(&new_ifaces, name);
+         }
      }
  
      /* Get rid of deleted interfaces. */
      sset_destroy(&new_ifaces);
  }
  
- /* Expires all MAC learning entries associated with 'port' and forces ofproto
-  * to revalidate every flow. */
+ /* Adds new interfaces to 'port' and updates 'type' and 'cfg' members of
+  * existing ones. */
  static void
- port_flush_macs(struct port *port)
+ port_add_ifaces(struct port *port)
  {
-     struct bridge *br = port->bridge;
-     struct mac_learning *ml = br->ml;
-     struct mac_entry *mac, *next_mac;
+     struct shash new_ifaces;
+     struct shash_node *node;
+     size_t i;
  
-     bridge_flush(br);
-     LIST_FOR_EACH_SAFE (mac, next_mac, lru_node, &ml->lrus) {
-         if (mac->port.p == port) {
-             mac_learning_expire(ml, mac);
+     /* Collect new ifaces. */
+     shash_init(&new_ifaces);
+     for (i = 0; i < port->cfg->n_interfaces; i++) {
+         const struct ovsrec_interface *cfg = port->cfg->interfaces[i];
+         if (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 -2091,9 +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) {
  
          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 -2145,129 +2149,129 @@@ enable_lacp(struct port *port, bool *ac
      }
  }
  
- static void
- iface_reconfigure_lacp(struct iface *iface)
- {
-     struct lacp_slave_settings s;
-     int priority, portid, key;
-     portid = atoi(get_interface_other_config(iface->cfg, "lacp-port-id", "0"));
-     priority = atoi(get_interface_other_config(iface->cfg,
-                                                "lacp-port-priority", "0"));
-     key = atoi(get_interface_other_config(iface->cfg, "lacp-aggregation-key",
-                                           "0"));
-     if (portid <= 0 || portid > UINT16_MAX) {
-         portid = iface->dp_ifidx;
-     }
-     if (priority <= 0 || priority > UINT16_MAX) {
-         priority = UINT16_MAX;
-     }
-     if (key < 0 || key > UINT16_MAX) {
-         key = 0;
-     }
-     s.name = iface->name;
-     s.id = portid;
-     s.priority = priority;
-     s.key = key;
-     lacp_slave_register(iface->port->lacp, iface, &s);
- }
- static void
- port_reconfigure_lacp(struct port *port)
+ static struct lacp_settings *
+ port_configure_lacp(struct port *port, struct lacp_settings *s)
  {
-     static struct lacp_settings s;
-     struct iface *iface;
-     uint8_t sysid[ETH_ADDR_LEN];
-     const char *sysid_str;
      const char *lacp_time;
      long long int custom_time;
      int priority;
  
-     if (!enable_lacp(port, &s.active)) {
-         lacp_destroy(port->lacp);
-         port->lacp = NULL;
-         return;
-     }
-     sysid_str = get_port_other_config(port->cfg, "lacp-system-id", NULL);
-     if (sysid_str && eth_addr_from_string(sysid_str, sysid)) {
-         memcpy(s.id, sysid, ETH_ADDR_LEN);
-     } else {
-         memcpy(s.id, port->bridge->ea, ETH_ADDR_LEN);
+     if (!enable_lacp(port, &s->active)) {
+         return NULL;
      }
  
-     s.name = port->name;
+     s->name = port->name;
+     memcpy(s->id, port->bridge->ea, ETH_ADDR_LEN);
  
      /* Prefer bondable links if unspecified. */
      priority = atoi(get_port_other_config(port->cfg, "lacp-system-priority",
                                            "0"));
-     s.priority = (priority > 0 && priority <= UINT16_MAX
-                   ? priority
-                   : UINT16_MAX - !list_is_short(&port->ifaces));
+     s->priority = (priority > 0 && priority <= UINT16_MAX
+                    ? priority
+                    : UINT16_MAX - !list_is_short(&port->ifaces));
+     s->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 -2283,17 +2287,17 @@@ iface_create(struct port *port, const s
      iface = xzalloc(sizeof *iface);
      iface->port = port;
      iface->name = xstrdup(name);
-     iface->dp_ifidx = -1;
+     iface->ofp_port = -1;
      iface->tag = tag_create_random();
      iface->netdev = NULL;
      iface->cfg = if_cfg;
  
-     shash_add_assert(&br->iface_by_name, iface->name, iface);
+     hmap_insert(&br->iface_by_name, &iface->name_node, hash_string(name, 0));
  
      list_push_back(&port->ifaces, &iface->port_elem);
  
      VLOG_DBG("attached network device %s to port %s", iface->name, port->name);
  
-     bridge_flush(br);
      return iface;
  }
  
@@@ -3367,35 -2304,37 +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 -2342,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) {
  }
  
  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 -2375,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 -2475,7 +2479,7 @@@ iface_delete_queues(unsigned int queue_
  }
  
  static void
- iface_update_qos(struct iface *iface, const struct ovsrec_qos *qos)
+ iface_configure_qos(struct iface *iface, const struct ovsrec_qos *qos)
  {
      if (!qos || qos->type[0] == '\0' || qos->n_queues < 1) {
          netdev_set_qos(iface->netdev, NULL, NULL);
              shash_destroy(&details);
          }
      }
+     netdev_set_policing(iface->netdev,
+                         iface->cfg->ingress_policing_rate,
+                         iface->cfg->ingress_policing_burst);
  }
  
  static void
- iface_update_cfm(struct iface *iface)
+ iface_configure_cfm(struct iface *iface)
  {
      size_t i;
      struct cfm cfm;
      mon = iface->cfg->monitor;
  
      if (!mon) {
-         ofproto_iface_clear_cfm(iface->port->bridge->ofproto, iface->dp_ifidx);
+         ofproto_port_clear_cfm(iface->port->bridge->ofproto, iface->ofp_port);
          return;
      }
  
          remote_mps[i] = mon->remote_mps[i]->mpid;
      }
  
-     ofproto_iface_set_cfm(iface->port->bridge->ofproto, iface->dp_ifidx,
-                           &cfm, remote_mps, mon->n_remote_mps);
+     ofproto_port_set_cfm(iface->port->bridge->ofproto, iface->ofp_port,
+                          &cfm, remote_mps, mon->n_remote_mps);
      free(remote_mps);
  }
  
@@@ -3631,11 -2574,10 +2578,10 @@@ iface_is_synthetic(const struct iface *
  static struct mirror *
  mirror_find_by_uuid(struct bridge *br, const struct uuid *uuid)
  {
-     int i;
+     struct mirror *m;
  
-     for (i = 0; i < MAX_MIRRORS; i++) {
-         struct mirror *m = br->mirrors[i];
-         if (m && uuid_equals(uuid, &m->uuid)) {
+     HMAP_FOR_EACH_IN_BUCKET (m, hmap_node, uuid_hash(uuid), &br->mirrors) {
+         if (uuid_equals(uuid, &m->uuid)) {
              return m;
          }
      }
  }
  
  static void
mirror_reconfigure(struct bridge *br)
bridge_configure_mirrors(struct bridge *br)
  {
-     unsigned long *rspan_vlans;
-     struct port *port;
-     int i;
+     const struct ovsdb_datum *mc;
+     unsigned long *flood_vlans;
+     struct mirror *m, *next;
+     size_t i;
  
      /* Get rid of deleted mirrors. */
-     for (i = 0; i < MAX_MIRRORS; i++) {
-         struct mirror *m = br->mirrors[i];
-         if (m) {
-             const struct ovsdb_datum *mc;
-             union ovsdb_atom atom;
-             mc = ovsrec_bridge_get_mirrors(br->cfg, OVSDB_TYPE_UUID);
-             atom.uuid = br->mirrors[i]->uuid;
-             if (ovsdb_datum_find_key(mc, &atom, OVSDB_TYPE_UUID) == UINT_MAX) {
-                 mirror_destroy(m);
-             }
+     mc = ovsrec_bridge_get_mirrors(br->cfg, OVSDB_TYPE_UUID);
+     HMAP_FOR_EACH_SAFE (m, next, hmap_node, &br->mirrors) {
+         union ovsdb_atom atom;
+         atom.uuid = m->uuid;
+         if (ovsdb_datum_find_key(mc, &atom, OVSDB_TYPE_UUID) == UINT_MAX) {
+             mirror_destroy(m);
          }
      }
  
      /* Add new mirrors and reconfigure existing ones. */
      for (i = 0; i < br->cfg->n_mirrors; i++) {
-         struct ovsrec_mirror *cfg = br->cfg->mirrors[i];
+         const struct ovsrec_mirror *cfg = br->cfg->mirrors[i];
          struct mirror *m = mirror_find_by_uuid(br, &cfg->header_.uuid);
-         if (m) {
-             mirror_reconfigure_one(m, cfg);
-         } else {
-             mirror_create(br, cfg);
+         if (!m) {
+             m = mirror_create(br, cfg);
          }
-     }
-     /* Update port reserved status. */
-     HMAP_FOR_EACH (port, hmap_node, &br->ports) {
-         port->is_mirror_output_port = false;
-     }
-     for (i = 0; i < MAX_MIRRORS; i++) {
-         struct mirror *m = br->mirrors[i];
-         if (m && m->out_port) {
-             m->out_port->is_mirror_output_port = true;
+         if (!mirror_configure(m, cfg)) {
+             mirror_destroy(m);
          }
      }
  
      /* Update flooded vlans (for RSPAN). */
-     rspan_vlans = NULL;
-     if (br->cfg->n_flood_vlans) {
-         rspan_vlans = bitmap_allocate(4096);
-         for (i = 0; i < br->cfg->n_flood_vlans; i++) {
-             int64_t vlan = br->cfg->flood_vlans[i];
-             if (vlan >= 0 && vlan < 4096) {
-                 bitmap_set1(rspan_vlans, vlan);
-                 VLOG_INFO("bridge %s: disabling learning on vlan %"PRId64,
-                           br->name, vlan);
-             } else {
-                 VLOG_ERR("bridge %s: invalid value %"PRId64 "for flood VLAN",
-                          br->name, vlan);
-             }
-         }
-     }
-     if (mac_learning_set_flood_vlans(br->ml, rspan_vlans)) {
-         bridge_flush(br);
-         mac_learning_flush(br->ml);
-     }
+     flood_vlans = vlan_bitmap_from_array(br->cfg->flood_vlans,
+                                          br->cfg->n_flood_vlans);
+     ofproto_set_flood_vlans(br->ofproto, flood_vlans);
+     bitmap_free(flood_vlans);
  }
  
- static void
- mirror_create(struct bridge *br, struct ovsrec_mirror *cfg)
+ static struct mirror *
+ mirror_create(struct bridge *br, const struct ovsrec_mirror *cfg)
  {
      struct mirror *m;
-     size_t i;
  
-     for (i = 0; ; i++) {
-         if (i >= MAX_MIRRORS) {
-             VLOG_WARN("bridge %s: maximum of %d port mirrors reached, "
-                       "cannot create %s", br->name, MAX_MIRRORS, cfg->name);
-             return;
-         }
-         if (!br->mirrors[i]) {
-             break;
-         }
-     }
-     VLOG_INFO("created port mirror %s on bridge %s", cfg->name, br->name);
-     bridge_flush(br);
-     mac_learning_flush(br->ml);
-     br->mirrors[i] = m = xzalloc(sizeof *m);
+     m = xzalloc(sizeof *m);
      m->uuid = cfg->header_.uuid;
+     hmap_insert(&br->mirrors, &m->hmap_node, uuid_hash(&m->uuid));
      m->bridge = br;
-     m->idx = i;
      m->name = xstrdup(cfg->name);
-     sset_init(&m->src_ports);
-     sset_init(&m->dst_ports);
-     m->vlans = NULL;
-     m->n_vlans = 0;
-     m->out_vlan = -1;
-     m->out_port = NULL;
  
-     mirror_reconfigure_one(m, cfg);
+     return m;
  }
  
  static void
@@@ -3750,104 -2641,61 +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 "
                       m->bridge->name, m->name);
          }
      } else if (cfg->output_vlan) {
-         out_port = NULL;
-         out_vlan = *cfg->output_vlan;
+         /* The database should prevent invalid VLAN values. */
+         s.out_bundle = NULL;
+         s.out_vlan = *cfg->output_vlan;
      } else {
          VLOG_ERR("bridge %s: mirror %s does not specify output; ignoring",
                   m->bridge->name, m->name);
-         mirror_destroy(m);
-         return;
+         return false;
      }
  
-     sset_init(&src_ports);
-     sset_init(&dst_ports);
+     /* Get port selection. */
      if (cfg->select_all) {
+         size_t n_ports = hmap_count(&m->bridge->ports);
+         void **ports = xmalloc(n_ports * sizeof *ports);
+         struct port *port;
+         size_t i;
+         i = 0;
          HMAP_FOR_EACH (port, hmap_node, &m->bridge->ports) {
-             sset_add(&src_ports, port->name);
-             sset_add(&dst_ports, port->name);
+             ports[i++] = port;
          }
-         vlans = NULL;
-         n_vlans = 0;
+         s.srcs = ports;
+         s.n_srcs = n_ports;
+         s.dsts = ports;
+         s.n_dsts = n_ports;
      } else {
-         /* Get ports, and drop duplicates and ports that don't exist. */
+         /* Get ports, dropping ports that don't exist.
+          * The IDL ensures that there are no duplicates. */
          mirror_collect_ports(m, cfg->select_src_port, cfg->n_select_src_port,
-                              &src_ports);
+                              &s.srcs, &s.n_srcs);
          mirror_collect_ports(m, cfg->select_dst_port, cfg->n_select_dst_port,
-                              &dst_ports);
-         /* Get all the vlans, and drop duplicate and invalid vlans. */
-         n_vlans = mirror_collect_vlans(m, cfg, &vlans);
-     }
-     /* Update mirror data. */
-     if (!sset_equals(&m->src_ports, &src_ports)
-         || !sset_equals(&m->dst_ports, &dst_ports)
-         || m->n_vlans != n_vlans
-         || memcmp(m->vlans, vlans, sizeof *vlans * n_vlans)
-         || m->out_port != out_port
-         || m->out_vlan != out_vlan) {
-         bridge_flush(m->bridge);
-         mac_learning_flush(m->bridge->ml);
-     }
-     sset_swap(&m->src_ports, &src_ports);
-     sset_swap(&m->dst_ports, &dst_ports);
-     free(m->vlans);
-     m->vlans = vlans;
-     m->n_vlans = n_vlans;
-     m->out_port = out_port;
-     m->out_vlan = out_vlan;
-     /* Update ports. */
-     mirror_bit = MIRROR_MASK_C(1) << m->idx;
-     HMAP_FOR_EACH (port, hmap_node, &m->bridge->ports) {
-         if (sset_contains(&m->src_ports, port->name)) {
-             port->src_mirrors |= mirror_bit;
-         } else {
-             port->src_mirrors &= ~mirror_bit;
-         }
+                              &s.dsts, &s.n_dsts);
  
-         if (sset_contains(&m->dst_ports, port->name)) {
-             port->dst_mirrors |= mirror_bit;
-         } else {
-             port->dst_mirrors &= ~mirror_bit;
-         }
      }
  
+     /* Get VLAN selection. */
+     s.src_vlans = vlan_bitmap_from_array(cfg->select_vlan, cfg->n_select_vlan);
+     /* Configure. */
+     ofproto_mirror_register(m->bridge->ofproto, m, &s);
      /* Clean up. */
-     sset_destroy(&src_ports);
-     sset_destroy(&dst_ports);
+     if (s.srcs != s.dsts) {
+         free(s.dsts);
+     }
+     free(s.srcs);
+     free(s.src_vlans);
+     return true;
  }
diff --combined vswitchd/ovs-vswitchd.c
@@@ -30,7 -30,6 +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 +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();
@@@ -122,17 -119,17 +119,17 @@@ parse_options(int argc, char *argv[]
          DAEMON_OPTION_ENUMS
      };
      static struct option long_options[] = {
 -        {"help",        no_argument, 0, 'h'},
 -        {"version",     no_argument, 0, 'V'},
 -        {"mlockall",    no_argument, 0, OPT_MLOCKALL},
 +        {"help",        no_argument, NULL, 'h'},
 +        {"version",     no_argument, NULL, 'V'},
 +        {"mlockall",    no_argument, NULL, OPT_MLOCKALL},
          DAEMON_LONG_OPTIONS,
          VLOG_LONG_OPTIONS,
          LEAK_CHECKER_LONG_OPTIONS,
          STREAM_SSL_LONG_OPTIONS,
 -        {"peer-ca-cert", required_argument, 0, OPT_PEER_CA_CERT},
 -        {"bootstrap-ca-cert", required_argument, 0, OPT_BOOTSTRAP_CA_CERT},
 -        {"enable-dummy", no_argument, 0, OPT_ENABLE_DUMMY},
 -        {0, 0, 0, 0},
 +        {"peer-ca-cert", required_argument, NULL, OPT_PEER_CA_CERT},
 +        {"bootstrap-ca-cert", required_argument, NULL, OPT_BOOTSTRAP_CA_CERT},
 +        {"enable-dummy", no_argument, NULL, OPT_ENABLE_DUMMY},
 +        {NULL, 0, NULL, 0},
      };
      char *short_options = long_options_to_short_options(long_options);