Merge "citrix" into "master".
authorBen Pfaff <blp@nicira.com>
Fri, 23 Oct 2009 00:43:28 +0000 (17:43 -0700)
committerBen Pfaff <blp@nicira.com>
Fri, 23 Oct 2009 00:43:28 +0000 (17:43 -0700)
This merge took a little bit of care due to two issues:

    - Crossport of "interface-reconfigure" fixes from master back to
      citrix that had happened and needed to be canceled out of the merge.

    - New script "refresh-xs-network-uuids" added on citrix branch that
      needed to be moved from /root/vswitch/scripts to
      /usr/share/vswitch/scripts.

12 files changed:
1  2 
acinclude.m4
configure.ac
datapath/datapath.c
datapath/datapath.h
datapath/dp_dev.c
ofproto/ofproto.c
utilities/ovs-ofctl.c
vswitchd/bridge.c
xenserver/README
xenserver/automake.mk
xenserver/usr_share_vswitch_scripts_refresh-xs-network-uuids
xenserver/vswitch-xen.spec

diff --combined acinclude.m4
  # See the License for the specific language governing permissions and
  # limitations under the License.
  
 -dnl Checks for --disable-userspace.
 -AC_DEFUN([OVS_CHECK_USERSPACE],
 -  [AC_ARG_ENABLE(
 -     [userspace],
 -     [AC_HELP_STRING([--disable-userspace], 
 -                     [Disable building userspace components.])],
 -     [case "${enableval}" in
 -        (yes) build_userspace=true ;;
 -        (no)  build_userspace=false ;;
 -        (*) AC_MSG_ERROR([bad value ${enableval} for --enable-userspace]) ;;
 -      esac],
 -     [build_userspace=true])
 -   AM_CONDITIONAL([ENABLE_USERSPACE], [$build_userspace])])
 -
  dnl OVS_CHECK_LINUX(OPTION, VERSION, VARIABLE, CONDITIONAL)
  dnl
  dnl Configure linux kernel source tree 
@@@ -119,6 -133,10 +119,10 @@@ AC_DEFUN([OVS_CHECK_LINUX26_COMPAT], 
                    [OVS_DEFINE([HAVE_NLA_NUL_STRING])])
    OVS_GREP_IFELSE([$KSRC26/include/linux/err.h], [ERR_CAST],
                    [OVS_DEFINE([HAVE_ERR_CAST])])
+   OVS_GREP_IFELSE([$KSRC26/include/net/checksum.h], [csum_unfold],
+                   [OVS_DEFINE([HAVE_CSUM_UNFOLD])])
+   OVS_GREP_IFELSE([$KSRC26/include/linux/skbuff.h], [skb_cow],
+                   [OVS_DEFINE([HAVE_SKB_COW])])
    OVS_CHECK_LOG2_H
    OVS_CHECK_VETH
    if cmp -s datapath/linux-2.6/kcompat.h.new \
diff --combined configure.ac
  # See the License for the specific language governing permissions and
  # limitations under the License.
  
 -AC_PREREQ(2.60)
 +AC_PREREQ(2.63)
- AC_INIT(openvswitch, 0.90.5, ovs-bugs@openvswitch.org)
+ AC_INIT(openvswitch, 0.90.6, ovs-bugs@openvswitch.org)
  NX_BUILDNR
  AC_CONFIG_SRCDIR([datapath/datapath.c])
  AC_CONFIG_MACRO_DIR([m4])
  AC_CONFIG_AUX_DIR([build-aux])
  AC_CONFIG_HEADERS([config.h])
 +AC_CONFIG_TESTDIR([tests])
  AM_INIT_AUTOMAKE
  
  AC_PROG_CC
@@@ -38,7 -37,7 +38,7 @@@ AC_USE_SYSTEM_EXTENSION
  AC_C_BIGENDIAN
  AC_SYS_LARGEFILE
  
 -OVS_CHECK_USERSPACE
 +OVS_CHECK_COVERAGE
  OVS_CHECK_NDEBUG
  OVS_CHECK_NETLINK
  OVS_CHECK_OPENSSL
@@@ -46,35 -45,36 +46,35 @@@ OVS_CHECK_LOGDI
  OVS_CHECK_CURSES
  OVS_CHECK_LINUX_VT_H
  OVS_CHECK_PCRE
 +OVS_CHECK_PYTHON
  OVS_CHECK_IF_PACKET
  OVS_CHECK_STRTOK_R
  
 -if $build_userspace; then
 -    OVS_CHECK_PKIDIR
 -    OVS_CHECK_RUNDIR
 -    OVS_CHECK_MALLOC_HOOKS
 -    OVS_CHECK_VALGRIND
 -    OVS_CHECK_TTY_LOCK_DIR
 -    OVS_CHECK_SOCKET_LIBS
 -    OVS_CHECK_FAULT_LIBS
 +OVS_CHECK_PKIDIR
 +OVS_CHECK_RUNDIR
 +OVS_CHECK_MALLOC_HOOKS
 +OVS_CHECK_VALGRIND
 +OVS_CHECK_TTY_LOCK_DIR
 +OVS_CHECK_SOCKET_LIBS
 +OVS_CHECK_FAULT_LIBS
  
 -    AC_CHECK_FUNCS([strsignal])
 +AC_CHECK_FUNCS([strsignal])
  
 -    OVS_ENABLE_OPTION([-Wall])
 -    OVS_ENABLE_OPTION([-Wno-sign-compare])
 -    OVS_ENABLE_OPTION([-Wpointer-arith])
 -    OVS_ENABLE_OPTION([-Wdeclaration-after-statement])
 -    OVS_ENABLE_OPTION([-Wformat-security])
 -    OVS_ENABLE_OPTION([-Wswitch-enum])
 -    OVS_ENABLE_OPTION([-Wunused-parameter])
 -    OVS_ENABLE_OPTION([-Wstrict-aliasing])
 -    OVS_ENABLE_OPTION([-Wbad-function-cast])
 -    OVS_ENABLE_OPTION([-Wcast-align])
 -    OVS_ENABLE_OPTION([-Wstrict-prototypes])
 -    OVS_ENABLE_OPTION([-Wold-style-definition])
 -    OVS_ENABLE_OPTION([-Wmissing-prototypes])
 -    OVS_ENABLE_OPTION([-Wmissing-field-initializers])
 -    OVS_ENABLE_OPTION([-Wno-override-init])
 -fi
 +OVS_ENABLE_OPTION([-Wall])
 +OVS_ENABLE_OPTION([-Wno-sign-compare])
 +OVS_ENABLE_OPTION([-Wpointer-arith])
 +OVS_ENABLE_OPTION([-Wdeclaration-after-statement])
 +OVS_ENABLE_OPTION([-Wformat-security])
 +OVS_ENABLE_OPTION([-Wswitch-enum])
 +OVS_ENABLE_OPTION([-Wunused-parameter])
 +OVS_ENABLE_OPTION([-Wstrict-aliasing])
 +OVS_ENABLE_OPTION([-Wbad-function-cast])
 +OVS_ENABLE_OPTION([-Wcast-align])
 +OVS_ENABLE_OPTION([-Wstrict-prototypes])
 +OVS_ENABLE_OPTION([-Wold-style-definition])
 +OVS_ENABLE_OPTION([-Wmissing-prototypes])
 +OVS_ENABLE_OPTION([-Wmissing-field-initializers])
 +OVS_ENABLE_OPTION([-Wno-override-init])
  
  AC_ARG_VAR(KARCH, [Kernel Architecture String])
  AC_SUBST(KARCH)
@@@ -84,7 -84,6 +84,7 @@@ AC_CONFIG_FILES([Makefil
  datapath/Makefile 
  datapath/linux-2.6/Kbuild
  datapath/linux-2.6/Makefile
 -datapath/linux-2.6/Makefile.main])
 +datapath/linux-2.6/Makefile.main
 +tests/atlocal])
  
  AC_OUTPUT
diff --combined datapath/datapath.c
@@@ -56,7 -56,9 +56,7 @@@ int (*dp_ioctl_hook)(struct net_device 
  EXPORT_SYMBOL(dp_ioctl_hook);
  
  /* Datapaths.  Protected on the read side by rcu_read_lock, on the write side
 - * by dp_mutex.  dp_mutex is almost completely redundant with genl_mutex
 - * maintained by the Generic Netlink code, but the timeout path needs mutual
 - * exclusion too.
 + * by dp_mutex.
   *
   * dp_mutex nests inside the RTNL lock: if you need both you must take the RTNL
   * lock first.
@@@ -223,7 -225,9 +223,7 @@@ static int create_dp(int dp_idx, const 
  
        /* Initialize kobject for bridge.  This will be added as
         * /sys/class/net/<devname>/brif later, if sysfs is enabled. */
 -      kobject_set_name(&dp->ifobj, SYSFS_BRIDGE_PORT_SUBDIR); /* "brif" */
        dp->ifobj.kset = NULL;
 -      dp->ifobj.parent = NULL;
        kobject_init(&dp->ifobj, &dp_ktype);
  
        /* Allocate table. */
        mutex_unlock(&dp_mutex);
        rtnl_unlock();
  
 -#ifdef SUPPORT_SYSFS
        dp_sysfs_add_dp(dp);
 -#endif
  
        return 0;
  
@@@ -281,7 -287,9 +281,7 @@@ static void do_destroy_dp(struct datapa
                if (p->port_no != ODPP_LOCAL)
                        dp_del_port(p);
  
 -#ifdef SUPPORT_SYSFS
        dp_sysfs_del_dp(dp);
 -#endif
  
        rcu_assign_pointer(dps[dp->dp_idx], NULL);
  
@@@ -326,7 -334,7 +326,7 @@@ static void release_nbp(struct kobject 
  }
  
  struct kobj_type brport_ktype = {
 -#ifdef SUPPORT_SYSFS
 +#ifdef CONFIG_SYSFS
        .sysfs_ops = &brport_sysfs_ops,
  #endif
        .release = release_nbp
@@@ -363,7 -371,9 +363,7 @@@ static int new_nbp(struct datapath *dp
  
        /* Initialize kobject for bridge.  This will be added as
         * /sys/class/net/<devname>/brport later, if sysfs is enabled. */
 -      kobject_set_name(&p->kobj, SYSFS_BRIDGE_PORT_ATTR); /* "brport" */
        p->kobj.kset = NULL;
 -      p->kobj.parent = &p->dev->NETDEV_DEV_MEMBER.kobj;
        kobject_init(&p->kobj, &brport_ktype);
  
        dp_ifinfo_notify(RTM_NEWLINK, p);
@@@ -383,6 -393,11 +383,6 @@@ static int add_port(int dp_idx, struct 
        if (copy_from_user(&port, portp, sizeof port))
                goto out;
        port.devname[IFNAMSIZ - 1] = '\0';
 -      port_no = port.port;
 -
 -      err = -EINVAL;
 -      if (port_no < 0 || port_no >= DP_MAX_PORTS)
 -              goto out;
  
        rtnl_lock();
        dp = get_dp_locked(dp_idx);
        if (!dp)
                goto out_unlock_rtnl;
  
 -      err = -EEXIST;
 -      if (dp->ports[port_no])
 -              goto out_unlock_dp;
 +      for (port_no = 1; port_no < DP_MAX_PORTS; port_no++)
 +              if (!dp->ports[port_no])
 +                      goto got_port_no;
 +      err = -EFBIG;
 +      goto out_unlock_dp;
  
 +got_port_no:
        if (!(port.flags & ODP_PORT_INTERNAL)) {
                err = -ENODEV;
                dev = dev_get_by_name(&init_net, port.devname);
        if (err)
                goto out_put;
  
 -#ifdef SUPPORT_SYSFS
        dp_sysfs_add_if(dp->ports[port_no]);
 -#endif
 +
 +      err = __put_user(port_no, &port.port);
  
  out_put:
        dev_put(dev);
@@@ -437,8 -449,10 +437,8 @@@ int dp_del_port(struct net_bridge_port 
  {
        ASSERT_RTNL();
  
 -#ifdef SUPPORT_SYSFS
        if (p->port_no != ODPP_LOCAL)
                dp_sysfs_del_if(p);
 -#endif
        dp_ifinfo_notify(RTM_DELLINK, p);
  
        p->dp->n_ports--;
@@@ -574,7 -588,7 +574,7 @@@ static int dp_frame_hook(struct net_bri
  #error
  #endif
  
 -#ifdef CONFIG_XEN
 +#if defined(CONFIG_XEN) && LINUX_VERSION_CODE == KERNEL_VERSION(2,6,18)
  /* This code is copied verbatim from net/dev/core.c in Xen's
   * linux-2.6.18-92.1.10.el5.xs5.0.0.394.644.  We can't call those functions
   * directly because they aren't exported. */
@@@ -590,7 -604,7 +590,7 @@@ static int skb_pull_up_to(struct sk_buf
        }
  }
  
 -int skb_checksum_setup(struct sk_buff *skb)
 +int vswitch_skb_checksum_setup(struct sk_buff *skb)
  {
        if (skb->proto_csum_blank) {
                if (skb->protocol != htons(ETH_P_IP))
  out:
        return -EPROTO;
  }
 +#else
 +int vswitch_skb_checksum_setup(struct sk_buff *skb) { return 0; }
 +#endif /* CONFIG_XEN && linux == 2.6.18 */
 +
 +/* Append each packet in 'skb' list to 'queue'.  There will be only one packet
 + * unless we broke up a GSO packet. */
 +static int
 +queue_control_packets(struct sk_buff *skb, struct sk_buff_head *queue,
 +                    int queue_no, u32 arg)
 +{
 +      struct sk_buff *nskb;
 +      int port_no;
 +      int err;
 +
 +      port_no = ODPP_LOCAL;
 +      if (skb->dev) {
 +              if (skb->dev->br_port)
 +                      port_no = skb->dev->br_port->port_no;
 +              else if (is_dp_dev(skb->dev))
 +                      port_no = dp_dev_priv(skb->dev)->port_no;
 +      }
 +
 +      do {
 +              struct odp_msg *header;
 +
 +              nskb = skb->next;
 +              skb->next = NULL;
 +
 +              /* If a checksum-deferred packet is forwarded to the
 +               * controller, correct the pointers and checksum.  This happens
 +               * on a regular basis only on Xen, on which VMs can pass up
 +               * packets that do not have their checksum computed.
 +               */
 +              err = vswitch_skb_checksum_setup(skb);
 +              if (err)
 +                      goto err_kfree_skbs;
 +#ifndef CHECKSUM_HW
 +              if (skb->ip_summed == CHECKSUM_PARTIAL) {
 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
 +                      /* Until 2.6.22, the start of the transport header was
 +                       * also the start of data to be checksummed.  Linux
 +                       * 2.6.22 introduced the csum_start field for this
 +                       * purpose, but we should point the transport header to
 +                       * it anyway for backward compatibility, as
 +                       * dev_queue_xmit() does even in 2.6.28. */
 +                      skb_set_transport_header(skb, skb->csum_start -
 +                                               skb_headroom(skb));
 +#endif
 +                      err = skb_checksum_help(skb);
 +                      if (err)
 +                              goto err_kfree_skbs;
 +              }
 +#else
 +              if (skb->ip_summed == CHECKSUM_HW) {
 +                      err = skb_checksum_help(skb, 0);
 +                      if (err)
 +                              goto err_kfree_skbs;
 +              }
  #endif
  
 +              err = skb_cow(skb, sizeof *header);
 +              if (err)
 +                      goto err_kfree_skbs;
 +
 +              header = (struct odp_msg*)__skb_push(skb, sizeof *header);
 +              header->type = queue_no;
 +              header->length = skb->len;
 +              header->port = port_no;
 +              header->reserved = 0;
 +              header->arg = arg;
 +              skb_queue_tail(queue, skb);
 +
 +              skb = nskb;
 +      } while (skb);
 +      return 0;
 +
 +err_kfree_skbs:
 +      kfree_skb(skb);
 +      while ((skb = nskb) != NULL) {
 +              nskb = skb->next;
 +              kfree_skb(skb);
 +      }
 +      return err;
 +}
 +
  int
  dp_output_control(struct datapath *dp, struct sk_buff *skb, int queue_no,
                  u32 arg)
  {
        struct dp_stats_percpu *stats;
        struct sk_buff_head *queue;
 -      int port_no;
        int err;
  
        WARN_ON_ONCE(skb_shared(skb));
        if (skb_queue_len(queue) >= DP_MAX_QUEUE_LEN)
                goto err_kfree_skb;
  
 -      /* If a checksum-deferred packet is forwarded to the controller,
 -       * correct the pointers and checksum.  This happens on a regular basis
 -       * only on Xen (the CHECKSUM_HW case), on which VMs can pass up packets
 -       * that do not have their checksum computed.  We also implement it for
 -       * the non-Xen case, but it is difficult to trigger or test this case
 -       * there, hence the WARN_ON_ONCE().
 -       */
 -      err = skb_checksum_setup(skb);
 -      if (err)
 -              goto err_kfree_skb;
 -#ifndef CHECKSUM_HW
 -      if (skb->ip_summed == CHECKSUM_PARTIAL) {
 -              WARN_ON_ONCE(1);
 -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
 -              /* Until 2.6.22, the start of the transport header was also the
 -               * start of data to be checksummed.  Linux 2.6.22 introduced
 -               * the csum_start field for this purpose, but we should point
 -               * the transport header to it anyway for backward
 -               * compatibility, as dev_queue_xmit() does even in 2.6.28. */
 -              skb_set_transport_header(skb, skb->csum_start -
 -                                            skb_headroom(skb));
 -#endif
 -              err = skb_checksum_help(skb);
 -              if (err)
 -                      goto err_kfree_skb;
 -      }
 -#else
 -      if (skb->ip_summed == CHECKSUM_HW) {
 -              err = skb_checksum_help(skb, 0);
 -              if (err)
 -                      goto err_kfree_skb;
 -      }
 -#endif
 -
        /* Break apart GSO packets into their component pieces.  Otherwise
         * userspace may try to stuff a 64kB packet into a 1500-byte MTU. */
        if (skb_is_gso(skb)) {
                }
        }
  
 -      /* Figure out port number. */
 -      port_no = ODPP_LOCAL;
 -      if (skb->dev) {
 -              if (skb->dev->br_port)
 -                      port_no = skb->dev->br_port->port_no;
 -              else if (is_dp_dev(skb->dev))
 -                      port_no = dp_dev_priv(skb->dev)->port_no;
 -      }
 -
 -      /* Append each packet to queue.  There will be only one packet unless
 -       * we broke up a GSO packet above. */
 -      do {
 -              struct odp_msg *header;
 -              struct sk_buff *nskb = skb->next;
 -              skb->next = NULL;
 -
 -              err = skb_cow(skb, sizeof *header);
 -              if (err) {
 -                      while (nskb) {
 -                              kfree_skb(skb);
 -                              skb = nskb;
 -                              nskb = skb->next;
 -                      }
 -                      goto err_kfree_skb;
 -              }
 -
 -              header = (struct odp_msg*)__skb_push(skb, sizeof *header);
 -              header->type = queue_no;
 -              header->length = skb->len;
 -              header->port = port_no;
 -              header->reserved = 0;
 -              header->arg = arg;
 -              skb_queue_tail(queue, skb);
 -
 -              skb = nskb;
 -      } while (skb);
 -
 +      err = queue_control_packets(skb, queue, queue_no, arg);
        wake_up_interruptible(&dp->waitqueue);
 -      return 0;
 +      return err;
  
  err_kfree_skb:
        kfree_skb(skb);
@@@ -782,7 -784,8 +782,8 @@@ static int validate_actions(const struc
                        break;
  
                case ODPAT_SET_VLAN_PCP:
-                       if (a->vlan_pcp.vlan_pcp & ~VLAN_PCP_MASK)
+                       if (a->vlan_pcp.vlan_pcp
+                           & ~(VLAN_PCP_MASK >> VLAN_PCP_SHIFT))
                                return -EINVAL;
                        break;
  
@@@ -835,7 -838,6 +836,7 @@@ static void get_stats(struct sw_flow *f
        stats->n_bytes = flow->byte_count;
        stats->ip_tos = flow->ip_tos;
        stats->tcp_flags = flow->tcp_flags;
 +      stats->error = 0;
  }
  
  static void clear_stats(struct sw_flow *flow)
@@@ -963,6 -965,8 +964,6 @@@ static int put_actions(const struct sw_
  
        if (!n_actions)
                return 0;
 -      if (ufp->n_actions > INT_MAX / sizeof(union odp_action))
 -              return -EINVAL;
  
        sf_acts = rcu_dereference(flow->sf_acts);
        if (__put_user(sf_acts->n_actions, &ufp->n_actions) ||
@@@ -988,7 -992,9 +989,7 @@@ static int answer_query(struct sw_flow 
        return put_actions(flow, ufp);
  }
  
 -static int del_or_query_flow(struct datapath *dp,
 -                           struct odp_flow __user *ufp,
 -                           unsigned int cmd)
 +static int del_flow(struct datapath *dp, struct odp_flow __user *ufp)
  {
        struct dp_table *table = rcu_dereference(dp->table);
        struct odp_flow uf;
        if (!flow)
                goto error;
  
 -      if (cmd == ODP_FLOW_DEL) {
 -              /* XXX redundant lookup */
 -              error = dp_table_delete(table, flow);
 -              if (error)
 -                      goto error;
 +      /* XXX redundant lookup */
 +      error = dp_table_delete(table, flow);
 +      if (error)
 +              goto error;
  
 -              /* XXX These statistics might lose a few packets, since other
 -               * CPUs can be using this flow.  We used to synchronize_rcu()
 -               * to make sure that we get completely accurate stats, but that
 -               * blows our performance, badly. */
 -              dp->n_flows--;
 -              error = answer_query(flow, ufp);
 -              flow_deferred_free(flow);
 -      } else {
 -              error = answer_query(flow, ufp);
 -      }
 +      /* XXX These statistics might lose a few packets, since other CPUs can
 +       * be using this flow.  We used to synchronize_rcu() to make sure that
 +       * we get completely accurate stats, but that blows our performance,
 +       * badly. */
 +      dp->n_flows--;
 +      error = answer_query(flow, ufp);
 +      flow_deferred_free(flow);
  
  error:
        return error;
  }
  
 -static int query_multiple_flows(struct datapath *dp,
 -                              const struct odp_flowvec *flowvec)
 +static int query_flows(struct datapath *dp, const struct odp_flowvec *flowvec)
  {
        struct dp_table *table = rcu_dereference(dp->table);
        int i;
  
                flow = dp_table_lookup(table, &uf.key);
                if (!flow)
 -                      error = __clear_user(&ufp->stats, sizeof ufp->stats);
 +                      error = __put_user(ENOENT, &ufp->stats.error);
                else
                        error = answer_query(flow, ufp);
                if (error)
@@@ -1183,7 -1194,8 +1184,7 @@@ error
        return err;
  }
  
 -static int
 -get_dp_stats(struct datapath *dp, struct odp_stats __user *statsp)
 +static int get_dp_stats(struct datapath *dp, struct odp_stats __user *statsp)
  {
        struct odp_stats stats;
        int i;
@@@ -1298,7 -1310,7 +1299,7 @@@ list_ports(struct datapath *dp, struct 
                                break;
                }
        }
 -      return put_user(idx, &pvp->n_ports);
 +      return put_user(dp->n_ports, &pvp->n_ports);
  }
  
  /* RCU callback for freeing a dp_port_group */
@@@ -1371,6 -1383,16 +1372,16 @@@ get_port_group(struct datapath *dp, str
        return 0;
  }
  
+ static int get_listen_mask(const struct file *f)
+ {
+       return (long)f->private_data;
+ }
+ static void set_listen_mask(struct file *f, int listen_mask)
+ {
+       f->private_data = (void*)(long)listen_mask;
+ }
  static long openvswitch_ioctl(struct file *f, unsigned int cmd,
                           unsigned long argp)
  {
        /* Handle commands with special locking requirements up front. */
        switch (cmd) {
        case ODP_DP_CREATE:
 -              return create_dp(dp_idx, (char __user *)argp);
 +              err = create_dp(dp_idx, (char __user *)argp);
 +              goto exit;
  
        case ODP_DP_DESTROY:
 -              return destroy_dp(dp_idx);
 +              err = destroy_dp(dp_idx);
 +              goto exit;
  
        case ODP_PORT_ADD:
 -              return add_port(dp_idx, (struct odp_port __user *)argp);
 +              err = add_port(dp_idx, (struct odp_port __user *)argp);
 +              goto exit;
  
        case ODP_PORT_DEL:
                err = get_user(port_no, (int __user *)argp);
 -              if (err)
 -                      break;
 -              return del_port(dp_idx, port_no);
 +              if (!err)
 +                      err = del_port(dp_idx, port_no);
 +              goto exit;
        }
  
        dp = get_dp_locked(dp_idx);
 +      err = -ENODEV;
        if (!dp)
 -              return -ENODEV;
 +              goto exit;
  
        switch (cmd) {
        case ODP_DP_STATS:
                break;
  
        case ODP_GET_LISTEN_MASK:
-               err = put_user((int)f->private_data, (int __user *)argp);
+               err = put_user(get_listen_mask(f), (int __user *)argp);
                break;
  
        case ODP_SET_LISTEN_MASK:
                if (listeners & ~ODPL_ALL)
                        break;
                err = 0;
-               f->private_data = (void*)listeners;
+               set_listen_mask(f, listeners);
                break;
  
        case ODP_PORT_QUERY:
                break;
  
        case ODP_FLOW_DEL:
 -      case ODP_FLOW_GET:
 -              err = del_or_query_flow(dp, (struct odp_flow __user *)argp,
 -                                      cmd);
 +              err = del_flow(dp, (struct odp_flow __user *)argp);
                break;
  
 -      case ODP_FLOW_GET_MULTIPLE:
 -              err = do_flowvec_ioctl(dp, argp, query_multiple_flows);
 +      case ODP_FLOW_GET:
 +              err = do_flowvec_ioctl(dp, argp, query_flows);
                break;
  
        case ODP_FLOW_LIST:
                break;
        }
        mutex_unlock(&dp->mutex);
 +exit:
        return err;
  }
  
@@@ -1503,7 -1522,7 +1514,7 @@@ ssize_t openvswitch_read(struct file *f
                      loff_t *ppos)
  {
        /* XXX is there sufficient synchronization here? */
-       int listeners = (int) f->private_data;
+       int listeners = get_listen_mask(f);
        int dp_idx = iminor(f->f_dentry->d_inode);
        struct datapath *dp = get_dp(dp_idx);
        struct sk_buff *skb;
                }
        }
  success:
-       copy_bytes = min(skb->len, nbytes);
+       copy_bytes = min_t(size_t, skb->len, nbytes);
        iov.iov_base = buf;
        iov.iov_len = copy_bytes;
        retval = skb_copy_datagram_iovec(skb, 0, &iov, iov.iov_len);
@@@ -1565,7 -1584,7 +1576,7 @@@ static unsigned int openvswitch_poll(st
        if (dp) {
                mask = 0;
                poll_wait(file, &dp->waitqueue, wait);
-               if (dp_has_packet_of_interest(dp, (int)file->private_data))
+               if (dp_has_packet_of_interest(dp, get_listen_mask(file)))
                        mask |= POLLIN | POLLRDNORM;
        } else {
                mask = POLLIN | POLLRDNORM | POLLHUP;
@@@ -1582,8 -1601,6 +1593,8 @@@ struct file_operations openvswitch_fop
  };
  
  static int major;
 +
 +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27)
  static struct llc_sap *dp_stp_sap;
  
  static int dp_stp_rcv(struct sk_buff *skb, struct net_device *dev,
        return 0;
  }
  
 -static int __init dp_init(void)
 +static int dp_avoid_bridge_init(void)
  {
 -      int err;
 -
 -      printk("Open vSwitch %s, built "__DATE__" "__TIME__"\n", VERSION BUILDNR);
 -
        /* Register to receive STP packets because the bridge module also
         * attempts to do so.  Since there can only be a single listener for a
         * given protocol, this provides mutual exclusion against the bridge
                printk(KERN_ERR "openvswitch: can't register sap for STP (probably the bridge module is loaded)\n");
                return -EADDRINUSE;
        }
 +      return 0;
 +}
 +
 +static void dp_avoid_bridge_exit(void)
 +{
 +      llc_sap_put(dp_stp_sap);
 +}
 +#else  /* Linux 2.6.27 or later. */
 +static int dp_avoid_bridge_init(void)
 +{
 +      /* Linux 2.6.27 introduces a way for multiple clients to register for
 +       * STP packets, which interferes with what we try to do above.
 +       * Instead, just check whether there's a bridge hook defined.  This is
 +       * not as safe--the bridge module is willing to load over the top of
 +       * us--but it provides a little bit of protection. */
 +      if (br_handle_frame_hook) {
 +              printk(KERN_ERR "openvswitch: bridge module is loaded, cannot load over it\n");
 +              return -EADDRINUSE;
 +      }
 +      return 0;
 +}
 +
 +static void dp_avoid_bridge_exit(void)
 +{
 +      /* Nothing to do. */
 +}
 +#endif        /* Linux 2.6.27 or later */
 +
 +static int __init dp_init(void)
 +{
 +      int err;
 +
 +      printk("Open vSwitch %s, built "__DATE__" "__TIME__"\n", VERSION BUILDNR);
 +
 +      err = dp_avoid_bridge_init();
 +      if (err)
 +              return err;
  
        err = flow_init();
        if (err)
@@@ -1679,7 -1663,7 +1690,7 @@@ static void dp_cleanup(void
        unregister_netdevice_notifier(&dp_device_notifier);
        flow_exit();
        br_handle_frame_hook = NULL;
 -      llc_sap_put(dp_stp_sap);
 +      dp_avoid_bridge_exit();
  }
  
  module_init(dp_init);
diff --combined datapath/datapath.h
  #include <asm/page.h>
  #include <linux/kernel.h>
  #include <linux/mutex.h>
 -#include <linux/netlink.h>
  #include <linux/netdevice.h>
  #include <linux/workqueue.h>
  #include <linux/skbuff.h>
 +#include <linux/version.h>
  #include "flow.h"
  #include "dp_sysfs.h"
  
  /* Mask for the priority bits in a vlan header.  If we ever merge upstream
   * then this should go into include/linux/if_vlan.h. */
  #define VLAN_PCP_MASK 0xe000
+ #define VLAN_PCP_SHIFT 13
  
  #define DP_MAX_PORTS 1024
  #define DP_MAX_GROUPS 16
@@@ -167,6 -168,4 +168,6 @@@ static inline int skb_checksum_setup(st
  }
  #endif
  
 +int vswitch_skb_checksum_setup(struct sk_buff *skb);
 +
  #endif /* datapath.h */
diff --combined datapath/dp_dev.c
@@@ -118,7 -118,7 +118,7 @@@ static void dp_getinfo(struct net_devic
  {
        struct dp_dev *dp_dev = dp_dev_priv(netdev);
        strcpy(info->driver, "openvswitch");
 -      sprintf(info->bus_info, "%d", dp_dev->dp->dp_idx);
 +      sprintf(info->bus_info, "%d.%d", dp_dev->dp->dp_idx, dp_dev->port_no);
  }
  
  static struct ethtool_ops dp_ethtool_ops = {
@@@ -157,22 -157,47 +157,47 @@@ static void dp_dev_free(struct net_devi
        free_netdev(netdev);
  }
  
+ static int dp_dev_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+ {
+       if (dp_ioctl_hook)
+               return dp_ioctl_hook(dev, ifr, cmd);
+       return -EOPNOTSUPP;
+ }
+ #ifdef HAVE_NET_DEVICE_OPS
+ static const struct net_device_ops dp_dev_netdev_ops = {
+       .ndo_init = dp_dev_init,
+       .ndo_open = dp_dev_open,
+       .ndo_stop = dp_dev_stop,
+       .ndo_start_xmit = dp_dev_xmit,
+       .ndo_set_mac_address = dp_dev_mac_addr,
+       .ndo_do_ioctl = dp_dev_do_ioctl,
+       .ndo_change_mtu = dp_dev_change_mtu,
+       .ndo_get_stats = dp_dev_get_stats,
+ };
+ #endif
  static void
  do_setup(struct net_device *netdev)
  {
        ether_setup(netdev);
  
-       netdev->do_ioctl = dp_ioctl_hook;
+ #ifdef HAVE_NET_DEVICE_OPS
+       netdev->netdev_ops = &dp_dev_netdev_ops;
+ #else
+       netdev->do_ioctl = dp_dev_do_ioctl;
        netdev->get_stats = dp_dev_get_stats;
        netdev->hard_start_xmit = dp_dev_xmit;
        netdev->open = dp_dev_open;
-       SET_ETHTOOL_OPS(netdev, &dp_ethtool_ops);
        netdev->stop = dp_dev_stop;
-       netdev->tx_queue_len = 0;
        netdev->set_mac_address = dp_dev_mac_addr;
        netdev->change_mtu = dp_dev_change_mtu;
        netdev->init = dp_dev_init;
+ #endif
        netdev->destructor = dp_dev_free;
+       SET_ETHTOOL_OPS(netdev, &dp_ethtool_ops);
+       netdev->tx_queue_len = 0;
  
        netdev->flags = IFF_BROADCAST | IFF_MULTICAST;
        netdev->features = NETIF_F_LLTX; /* XXX other features? */
@@@ -233,5 -258,9 +258,9 @@@ void dp_dev_destroy(struct net_device *
  
  int is_dp_dev(struct net_device *netdev) 
  {
+ #ifdef HAVE_NET_DEVICE_OPS
+       return netdev->netdev_ops == &dp_dev_netdev_ops;
+ #else
        return netdev->open == dp_dev_open;
+ #endif
  }
diff --combined ofproto/ofproto.c
@@@ -135,7 -135,7 +135,7 @@@ rule_is_hidden(const struct rule *rule
          return true;
      }
  
 -    /* Rules with priority higher than UINT16_MAX are set up by secchan itself
 +    /* 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. */
      if (rule->cr.priority > UINT16_MAX) {
@@@ -194,8 -194,8 +194,8 @@@ struct ofproto 
      char *serial;               /* Serial number. */
  
      /* Datapath. */
 -    struct dpif dpif;
 -    struct dpifmon *dpifmon;
 +    struct dpif *dpif;
 +    struct netdev_monitor *netdev_monitor;
      struct port_array ports;    /* Index is ODP port nr; ofport->opp.port_no is
                                   * OFP port nr. */
      struct shash port_by_name;
@@@ -237,7 -237,7 +237,7 @@@ static struct vlog_rate_limit rl = VLOG
  
  static const struct ofhooks default_ofhooks;
  
 -static uint64_t pick_datapath_id(struct dpif *, uint64_t fallback_dpid);
 +static uint64_t pick_datapath_id(const struct ofproto *);
  static uint64_t pick_fallback_dpid(void);
  static void send_packet_in_miss(struct ofpbuf *, void *ofproto);
  static void send_packet_in_action(struct ofpbuf *, void *ofproto);
@@@ -261,9 -261,10 +261,9 @@@ in
  ofproto_create(const char *datapath, const struct ofhooks *ofhooks, void *aux,
                 struct ofproto **ofprotop)
  {
 -    struct dpifmon *dpifmon;
      struct odp_stats stats;
      struct ofproto *p;
 -    struct dpif dpif;
 +    struct dpif *dpif;
      int error;
  
      *ofprotop = NULL;
          VLOG_ERR("failed to open datapath %s: %s", datapath, strerror(error));
          return error;
      }
 -    error = dpif_get_dp_stats(&dpif, &stats);
 +    error = dpif_get_dp_stats(dpif, &stats);
      if (error) {
          VLOG_ERR("failed to obtain stats for datapath %s: %s",
                   datapath, strerror(error));
 -        dpif_close(&dpif);
 +        dpif_close(dpif);
          return error;
      }
 -    error = dpif_set_listen_mask(&dpif, ODPL_MISS | ODPL_ACTION);
 +    error = dpif_recv_set_mask(dpif, ODPL_MISS | ODPL_ACTION);
      if (error) {
          VLOG_ERR("failed to listen on datapath %s: %s",
                   datapath, strerror(error));
 -        dpif_close(&dpif);
 -        return error;
 -    }
 -    dpif_flow_flush(&dpif);
 -    dpif_purge(&dpif);
 -
 -    /* Start monitoring datapath ports for status changes. */
 -    error = dpifmon_create(datapath, &dpifmon);
 -    if (error) {
 -        VLOG_ERR("failed to starting monitoring datapath %s: %s",
 -                 datapath, strerror(error));
 -        dpif_close(&dpif);
 +        dpif_close(dpif);
          return error;
      }
 +    dpif_flow_flush(dpif);
 +    dpif_recv_purge(dpif);
  
      /* Initialize settings. */
      p = xcalloc(1, sizeof *p);
      p->fallback_dpid = pick_fallback_dpid();
 -    p->datapath_id = pick_datapath_id(&dpif, p->fallback_dpid);
 -    VLOG_INFO("using datapath ID %012"PRIx64, p->datapath_id);
 +    p->datapath_id = p->fallback_dpid;
      p->manufacturer = xstrdup("Nicira Networks, Inc.");
      p->hardware = xstrdup("Reference Implementation");
      p->software = xstrdup(VERSION BUILDNR);
  
      /* Initialize datapath. */
      p->dpif = dpif;
 -    p->dpifmon = dpifmon;
 +    p->netdev_monitor = netdev_monitor_create();
      port_array_init(&p->ports);
      shash_init(&p->port_by_name);
      p->max_ports = stats.max_ports;
          return error;
      }
  
 +    /* Pick final datapath ID. */
 +    p->datapath_id = pick_datapath_id(p);
 +    VLOG_INFO("using datapath ID %012"PRIx64, p->datapath_id);
 +
      *ofprotop = p;
      return 0;
  }
@@@ -366,7 -373,9 +366,7 @@@ voi
  ofproto_set_datapath_id(struct ofproto *p, uint64_t datapath_id)
  {
      uint64_t old_dpid = p->datapath_id;
 -    p->datapath_id = (datapath_id
 -                      ? datapath_id
 -                      : pick_datapath_id(&p->dpif, p->fallback_dpid));
 +    p->datapath_id = datapath_id ? datapath_id : pick_datapath_id(p);
      if (p->datapath_id != old_dpid) {
          VLOG_INFO("datapath ID changed to %012"PRIx64, p->datapath_id);
          rconn_reconnect(p->controller->rconn);
@@@ -424,8 -433,9 +424,8 @@@ ofproto_set_in_band(struct ofproto *p, 
  {
      if (in_band != (p->in_band != NULL)) {
          if (in_band) {
 -            in_band_create(p, &p->dpif, p->switch_status, 
 -                           p->controller->rconn, &p->in_band);
 -            return 0;
 +            return in_band_create(p, p->dpif, p->switch_status,
 +                                  p->controller->rconn, &p->in_band);
          } else {
              ofproto_set_discovery(p, false, NULL, true);
              in_band_destroy(p->in_band);
@@@ -447,7 -457,7 +447,7 @@@ ofproto_set_discovery(struct ofproto *p
                  return error;
              }
              error = discovery_create(re, update_resolv_conf,
 -                                     &p->dpif, p->switch_status,
 +                                     p->dpif, p->switch_status,
                                       &p->discovery);
              if (error) {
                  return error;
@@@ -704,8 -714,8 +704,8 @@@ ofproto_destroy(struct ofproto *p
          ofconn_destroy(ofconn, p);
      }
  
 -    dpif_close(&p->dpif);
 -    dpifmon_destroy(p->dpifmon);
 +    dpif_close(p->dpif);
 +    netdev_monitor_destroy(p->netdev_monitor);
      PORT_ARRAY_FOR_EACH (ofport, &p->ports, port_no) {
          ofport_free(ofport);
      }
@@@ -747,17 -757,6 +747,17 @@@ ofproto_run(struct ofproto *p
      return error;
  }
  
 +static void
 +process_port_change(struct ofproto *ofproto, int error, char *devname)
 +{
 +    if (error == ENOBUFS) {
 +        reinit_ports(ofproto);
 +    } else if (!error) {
 +        update_port(ofproto, devname);
 +        free(devname);
 +    }
 +}
 +
  int
  ofproto_run1(struct ofproto *p)
  {
          struct ofpbuf *buf;
          int error;
  
 -        error = dpif_recv(&p->dpif, &buf);
 +        error = dpif_recv(p->dpif, &buf);
          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 rl = VLOG_RATE_LIMIT_INIT(1, 5);
 -                VLOG_ERR_RL(&rl, "dp%u: datapath was destroyed externally",
 -                            dpif_id(&p->dpif));
 +                VLOG_ERR_RL(&rl, "%s: datapath was destroyed externally",
 +                            dpif_name(p->dpif));
                  return ENODEV;
              }
              break;
          handle_odp_msg(p, buf);
      }
  
 -    while ((error = dpifmon_poll(p->dpifmon, &devname)) != EAGAIN) {
 -        if (error == ENOBUFS) {
 -            reinit_ports(p);
 -        } else if (!error) {
 -            update_port(p, devname);
 -            free(devname);
 -        }
 +    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);
      }
  
      if (p->in_band) {
@@@ -907,9 -907,8 +907,9 @@@ ofproto_wait(struct ofproto *p
      struct ofconn *ofconn;
      size_t i;
  
 -    dpif_recv_wait(&p->dpif);
 -    dpifmon_wait(p->dpifmon);
 +    dpif_recv_wait(p->dpif);
 +    dpif_port_poll_wait(p->dpif);
 +    netdev_monitor_poll_wait(p->netdev_monitor);
      LIST_FOR_EACH (ofconn, struct ofconn, node, &p->all_conns) {
          ofconn_wait(ofconn);
      }
@@@ -979,7 -978,7 +979,7 @@@ ofproto_send_packet(struct ofproto *p, 
  
      /* XXX Should we translate the dpif_execute() errno value into an OpenFlow
       * error code? */
 -    dpif_execute(&p->dpif, flow->in_port, odp_actions.actions,
 +    dpif_execute(p->dpif, flow->in_port, odp_actions.actions,
                   odp_actions.n_actions, packet);
      return 0;
  }
@@@ -1031,7 -1030,7 +1031,7 @@@ ofproto_flush_flows(struct ofproto *ofp
  {
      COVERAGE_INC(ofproto_flush);
      classifier_for_each(&ofproto->cls, CLS_INC_ALL, destroy_rule, ofproto);
 -    dpif_flow_flush(&ofproto->dpif);
 +    dpif_flow_flush(ofproto->dpif);
      if (ofproto->in_band) {
          in_band_flushed(ofproto->in_band);
      }
@@@ -1054,7 -1053,7 +1054,7 @@@ reinit_ports(struct ofproto *p
      PORT_ARRAY_FOR_EACH (ofport, &p->ports, port_no) {
          svec_add (&devnames, (char *) ofport->opp.name);
      }
 -    dpif_port_list(&p->dpif, &odp_ports, &n_odp_ports);
 +    dpif_port_list(p->dpif, &odp_ports, &n_odp_ports);
      for (i = 0; i < n_odp_ports; i++) {
          svec_add (&devnames, odp_ports[i].devname);
      }
@@@ -1084,7 -1083,7 +1084,7 @@@ refresh_port_group(struct ofproto *p, u
              ports[n_ports++] = port_no;
          }
      }
 -    dpif_port_group_set(&p->dpif, group, ports, n_ports);
 +    dpif_port_group_set(p->dpif, group, ports, n_ports);
      free(ports);
  }
  
@@@ -1116,7 -1115,7 +1116,7 @@@ make_ofport(const struct odp_port *odp_
      ofport = xmalloc(sizeof *ofport);
      ofport->netdev = netdev;
      ofport->opp.port_no = odp_port_to_ofp_port(odp_port->port);
 -    memcpy(ofport->opp.hw_addr, netdev_get_etheraddr(netdev), ETH_ALEN);
 +    netdev_get_etheraddr(netdev, ofport->opp.hw_addr);
      memcpy(ofport->opp.name, odp_port->devname,
             MIN(sizeof ofport->opp.name, sizeof odp_port->devname));
      ofport->opp.name[sizeof ofport->opp.name - 1] = '\0';
@@@ -1191,7 -1190,6 +1191,7 @@@ send_port_status(struct ofproto *p, con
  static void
  ofport_install(struct ofproto *p, struct ofport *ofport)
  {
 +    netdev_monitor_add(p->netdev_monitor, ofport->netdev);
      port_array_set(&p->ports, ofp_port_to_odp_port(ofport->opp.port_no),
                     ofport);
      shash_add(&p->port_by_name, (char *) ofport->opp.name, ofport);
  static void
  ofport_remove(struct ofproto *p, struct ofport *ofport)
  {
 +    netdev_monitor_remove(p->netdev_monitor, ofport->netdev);
      port_array_set(&p->ports, ofp_port_to_odp_port(ofport->opp.port_no), NULL);
      shash_delete(&p->port_by_name,
                   shash_find(&p->port_by_name, (char *) ofport->opp.name));
@@@ -1226,7 -1223,7 +1226,7 @@@ update_port(struct ofproto *p, const ch
      COVERAGE_INC(ofproto_update_port);
  
      /* Query the datapath for port information. */
 -    error = dpif_port_query_by_name(&p->dpif, devname, &odp_port);
 +    error = dpif_port_query_by_name(p->dpif, devname, &odp_port);
  
      /* Find the old ofport. */
      old_ofport = shash_find_data(&p->port_by_name, devname);
@@@ -1295,7 -1292,7 +1295,7 @@@ init_ports(struct ofproto *p
      size_t i;
      int error;
  
 -    error = dpif_port_list(&p->dpif, &ports, &n_ports);
 +    error = dpif_port_list(p->dpif, &ports, &n_ports);
      if (error) {
          return error;
      }
@@@ -1500,7 -1497,7 +1500,7 @@@ rule_execute(struct ofproto *ofproto, s
      }
  
      /* Execute the ODP actions. */
 -    if (!dpif_execute(&ofproto->dpif, flow->in_port,
 +    if (!dpif_execute(ofproto->dpif, flow->in_port,
                        actions, n_actions, packet)) {
          struct odp_flow_stats stats;
          flow_extract_stats(flow, packet, &stats);
@@@ -1609,7 -1606,7 +1609,7 @@@ do_put_flow(struct ofproto *ofproto, st
      put->flow.actions = rule->odp_actions;
      put->flow.n_actions = rule->n_odp_actions;
      put->flags = flags;
 -    return dpif_flow_put(&ofproto->dpif, put);
 +    return dpif_flow_put(ofproto->dpif, put);
  }
  
  static void
@@@ -1690,7 -1687,7 +1690,7 @@@ rule_uninstall(struct ofproto *p, struc
          odp_flow.key = rule->cr.flow;
          odp_flow.actions = NULL;
          odp_flow.n_actions = 0;
 -        if (!dpif_flow_del(&p->dpif, &odp_flow)) {
 +        if (!dpif_flow_del(p->dpif, &odp_flow)) {
              update_stats(rule, &odp_flow.stats);
          }
          rule->installed = false;
@@@ -1838,7 -1835,7 +1838,7 @@@ handle_get_config_request(struct ofprot
      bool drop_frags;
  
      /* Figure out flags. */
 -    dpif_get_drop_frags(&p->dpif, &drop_frags);
 +    dpif_get_drop_frags(p->dpif, &drop_frags);
      flags = drop_frags ? OFPC_FRAG_DROP : OFPC_FRAG_NORMAL;
      if (ofconn->send_flow_exp) {
          flags |= OFPC_SEND_FLOW_EXP;
@@@ -1871,10 -1868,10 +1871,10 @@@ handle_set_config(struct ofproto *p, st
      if (ofconn == p->controller) {
          switch (flags & OFPC_FRAG_MASK) {
          case OFPC_FRAG_NORMAL:
 -            dpif_set_drop_frags(&p->dpif, false);
 +            dpif_set_drop_frags(p->dpif, false);
              break;
          case OFPC_FRAG_DROP:
 -            dpif_set_drop_frags(&p->dpif, true);
 +            dpif_set_drop_frags(p->dpif, true);
              break;
          default:
              VLOG_WARN_RL(&rl, "requested bad fragment mode (flags=%"PRIx16")",
@@@ -2198,7 -2195,7 +2198,7 @@@ handle_packet_out(struct ofproto *p, st
          return error;
      }
  
 -    dpif_execute(&p->dpif, flow.in_port, actions.actions, actions.n_actions,
 +    dpif_execute(p->dpif, flow.in_port, actions.actions, actions.n_actions,
                   &payload);
      ofpbuf_delete(buffer);
  
@@@ -2341,7 -2338,7 +2341,7 @@@ handle_table_stats_request(struct ofpro
      n_wild = classifier_count(&p->cls) - classifier_count_exact(&p->cls);
  
      /* Hash table. */
 -    dpif_get_dp_stats(&p->dpif, &dpstats);
 +    dpif_get_dp_stats(p->dpif, &dpstats);
      ots = append_stats_reply(sizeof *ots, ofconn, &msg);
      memset(ots, 0, sizeof *ots);
      ots->table_id = TABLEID_HASH;
@@@ -2423,20 -2420,23 +2423,25 @@@ query_stats(struct ofproto *p, struct r
      struct odp_flow *odp_flows;
      size_t n_odp_flows;
  
+     packet_count = rule->packet_count;
+     byte_count = rule->byte_count;
      n_odp_flows = rule->cr.wc.wildcards ? list_size(&rule->list) : 1;
      odp_flows = xcalloc(1, n_odp_flows * sizeof *odp_flows);
      if (rule->cr.wc.wildcards) {
          size_t i = 0;
          LIST_FOR_EACH (subrule, struct rule, list, &rule->list) {
              odp_flows[i++].key = subrule->cr.flow;
+             packet_count += subrule->packet_count;
+             byte_count += subrule->byte_count;
          }
      } else {
          odp_flows[0].key = rule->cr.flow;
      }
  
 -    if (!dpif_flow_get_multiple(&p->dpif, odp_flows, n_odp_flows)) {
 +    packet_count = rule->packet_count;
 +    byte_count = rule->byte_count;
 +    if (!dpif_flow_get_multiple(p->dpif, odp_flows, n_odp_flows)) {
          size_t i;
          for (i = 0; i < n_odp_flows; i++) {
              struct odp_flow *odp_flow = &odp_flows[i];
@@@ -3060,7 -3060,7 +3065,7 @@@ handle_odp_msg(struct ofproto *p, struc
          memset(&action, 0, sizeof(action));
          action.output.type = ODPAT_OUTPUT;
          action.output.port = ODPP_LOCAL;
 -        dpif_execute(&p->dpif, flow.in_port, &action, 1, &payload);
 +        dpif_execute(p->dpif, flow.in_port, &action, 1, &payload);
      }
  
      rule = lookup_valid_rule(p, &flow);
@@@ -3182,7 -3182,7 +3187,7 @@@ send_flow_exp(struct ofproto *p, struc
  {
      struct ofconn *ofconn;
      struct ofconn *prev;
 -    struct ofpbuf *buf;
 +    struct ofpbuf *buf = NULL;
  
      /* We limit the maximum number of queued flow expirations it by accounting
       * them under the counter for replies.  That works because preventing
@@@ -3274,7 -3274,7 +3279,7 @@@ update_used(struct ofproto *p
      size_t i;
      int error;
  
 -    error = dpif_flow_list_all(&p->dpif, &flows, &n_flows);
 +    error = dpif_flow_list_all(p->dpif, &flows, &n_flows);
      if (error) {
          return;
      }
              classifier_find_rule_exactly(&p->cls, &f->key, 0, UINT16_MAX));
          if (!rule || !rule->installed) {
              COVERAGE_INC(ofproto_unexpected_rule);
 -            dpif_flow_del(&p->dpif, f);
 +            dpif_flow_del(p->dpif, f);
              continue;
          }
  
@@@ -3362,23 -3362,23 +3367,23 @@@ send_packet_in_miss(struct ofpbuf *pack
  }
  
  static uint64_t
 -pick_datapath_id(struct dpif *dpif, uint64_t fallback_dpid)
 +pick_datapath_id(const struct ofproto *ofproto)
  {
 -    char local_name[IF_NAMESIZE];
 -    uint8_t ea[ETH_ADDR_LEN];
 -    int error;
 +    const struct ofport *port;
  
 -    error = dpif_get_name(dpif, local_name, sizeof local_name);
 -    if (!error) {
 -        error = netdev_nodev_get_etheraddr(local_name, ea);
 +    port = port_array_get(&ofproto->ports, ODPP_LOCAL);
 +    if (port) {
 +        uint8_t ea[ETH_ADDR_LEN];
 +        int error;
 +
 +        error = netdev_get_etheraddr(port->netdev, ea);
          if (!error) {
              return eth_addr_to_uint64(ea);
          }
          VLOG_WARN("could not get MAC address for %s (%s)",
 -                  local_name, strerror(error));
 +                  netdev_get_name(port->netdev), strerror(error));
      }
 -
 -    return fallback_dpid;
 +    return ofproto->fallback_dpid;
  }
  
  static uint64_t
diff --combined utilities/ovs-ofctl.c
@@@ -251,7 -251,7 +251,7 @@@ static void run(int retval, const char 
  static void
  open_vconn(const char *name, struct vconn **vconnp)
  {
 -    struct dpif dpif;
 +    struct dpif *dpif;
      struct stat s;
  
      if (strstr(name, ":")) {
          char *socket_name;
          char *vconn_name;
  
 -        run(dpif_get_name(&dpif, dpif_name, sizeof dpif_name),
 +        run(dpif_port_get_name(dpif, ODPP_LOCAL, dpif_name, sizeof dpif_name),
              "obtaining name of %s", dpif_name);
 -        dpif_close(&dpif);
 +        dpif_close(dpif);
          if (strcmp(dpif_name, name)) {
              VLOG_INFO("datapath %s is named %s", name, dpif_name);
          }
@@@ -617,8 -617,6 +617,8 @@@ str_to_action(char *str, struct ofpbuf 
               * packet to the controller. */
              if (arg && (strspn(act, "0123456789") == strlen(act))) {
                 oao->max_len = htons(str_to_u32(arg));
 +            } else {
 +                oao->max_len = htons(UINT16_MAX);
              }
          } else if (parse_port_name(act, &port)) {
              put_output_action(b, port);
@@@ -918,11 -916,15 +918,15 @@@ do_mod_flows(const struct settings *s, 
      struct vconn *vconn;
      struct ofpbuf *buffer;
      struct ofp_flow_mod *ofm;
+     struct ofp_match match;
  
-     /* Parse and send. */
-     ofm = make_openflow(sizeof *ofm, OFPT_FLOW_MOD, &buffer);
-     str_to_flow(argv[2], &ofm->match, buffer,
+     /* Parse and send.  str_to_flow() will expand and reallocate the data in
+      * 'buffer', so we can't keep pointers to across the str_to_flow() call. */
+     make_openflow(sizeof *ofm, OFPT_FLOW_MOD, &buffer);
+     str_to_flow(argv[2], &match, buffer,
                  NULL, NULL, &priority, &idle_timeout, &hard_timeout);
+     ofm = buffer->data;
+     ofm->match = match;
      if (s->strict) {
          ofm->command = htons(OFPFC_MODIFY_STRICT);
      } else {
diff --combined vswitchd/bridge.c
  #include "odp-util.h"
  #include "ofp-print.h"
  #include "ofpbuf.h"
 +#include "ofproto/ofproto.h"
  #include "packets.h"
  #include "poll-loop.h"
  #include "port-array.h"
  #include "proc-net-compat.h"
  #include "process.h"
 -#include "secchan/ofproto.h"
  #include "socket-util.h"
  #include "stp.h"
  #include "svec.h"
@@@ -71,18 -71,17 +71,18 @@@ struct dst 
  extern uint64_t mgmt_id;
  
  struct iface {
 +    /* These members are always valid. */
      struct port *port;          /* Containing port. */
      size_t port_ifidx;          /* Index within containing port. */
 -
      char *name;                 /* Host network device name. */
 -    int dp_ifidx;               /* Index within kernel datapath. */
 -
 -    uint8_t mac[ETH_ADDR_LEN];  /* Ethernet address (all zeros if unknowns). */
 -
      tag_type tag;               /* Tag associated with this interface. */
 -    bool enabled;               /* May be chosen for flows? */
      long long delay_expires;    /* Time after which 'enabled' may change. */
 +
 +    /* These members are valid only after bridge_reconfigure() causes them to
 +     * be initialized.*/
 +    int dp_ifidx;               /* Index within kernel datapath. */
 +    struct netdev *netdev;      /* Network device. */
 +    bool enabled;               /* May be chosen for flows? */
  };
  
  #define BOND_MASK 0xff
@@@ -160,7 -159,7 +160,7 @@@ struct bridge 
      struct ofproto *ofproto;    /* OpenFlow switch. */
  
      /* Kernel datapath information. */
 -    struct dpif dpif;           /* Kernel datapath. */
 +    struct dpif *dpif;          /* Datapath. */
      struct port_array ifaces;   /* Indexed by kernel datapath port number. */
  
      /* Bridge ports. */
@@@ -203,11 -202,10 +203,11 @@@ static void bridge_fetch_dp_ifaces(stru
  static void bridge_flush(struct bridge *);
  static void bridge_pick_local_hw_addr(struct bridge *,
                                        uint8_t ea[ETH_ADDR_LEN],
 -                                      const char **devname);
 +                                      struct iface **hw_addr_iface);
  static uint64_t bridge_pick_datapath_id(struct bridge *,
                                          const uint8_t bridge_ea[ETH_ADDR_LEN],
 -                                        const char *devname);
 +                                        struct iface *hw_addr_iface);
 +static struct iface *bridge_get_local_iface(struct bridge *);
  static uint64_t dpid_from_hash(const void *, size_t nbytes);
  
  static void bridge_unixctl_fdb_show(struct unixctl_conn *, const char *args);
@@@ -227,7 -225,6 +227,7 @@@ static struct port *port_from_dp_ifidx(
                                         uint16_t dp_ifidx);
  static void port_update_bond_compat(struct port *);
  static void port_update_vlan_compat(struct port *);
 +static void port_update_bonding(struct port *);
  
  static void mirror_create(struct bridge *, const char *name);
  static void mirror_destroy(struct mirror *);
@@@ -268,8 -265,8 +268,8 @@@ bridge_get_ifaces(struct svec *svec
              for (j = 0; j < port->n_ifaces; j++) {
                  struct iface *iface = port->ifaces[j];
                  if (iface->dp_ifidx < 0) {
 -                    VLOG_ERR("%s interface not in dp%u, ignoring",
 -                             iface->name, dpif_id(&br->dpif));
 +                    VLOG_ERR("%s interface not in datapath %s, ignoring",
 +                             iface->name, dpif_name(br->dpif));
                  } else {
                      if (iface->dp_ifidx != ODPP_LOCAL) {
                          svec_add(svec, iface->name);
  void
  bridge_init(void)
  {
 -    int retval;
 -    int i;
 -
 -    bond_init();
 +    struct svec dpif_names;
 +    size_t i;
  
      unixctl_command_register("fdb/show", bridge_unixctl_fdb_show);
  
 -    for (i = 0; i < DP_MAX; i++) {
 -        struct dpif dpif;
 -        char devname[16];
 +    svec_init(&dpif_names);
 +    dp_enumerate(&dpif_names);
 +    for (i = 0; i < dpif_names.n; i++) {
 +        const char *dpif_name = dpif_names.names[i];
 +        struct dpif *dpif;
 +        int retval;
  
 -        sprintf(devname, "dp%d", i);
 -        retval = dpif_open(devname, &dpif);
 +        retval = dpif_open(dpif_name, &dpif);
          if (!retval) {
 -            char dpif_name[IF_NAMESIZE];
 -            if (dpif_get_name(&dpif, dpif_name, sizeof dpif_name)
 -                || !cfg_has("bridge.%s.port", dpif_name)) {
 -                dpif_delete(&dpif);
 +            struct svec all_names;
 +            size_t j;
 +
 +            svec_init(&all_names);
 +            dpif_get_all_names(dpif, &all_names);
 +            for (j = 0; j < all_names.n; j++) {
 +                if (cfg_has("bridge.%s.port", all_names.names[j])) {
 +                    goto found;
 +                }
              }
 -            dpif_close(&dpif);
 -        } else if (retval != ENODEV) {
 -            VLOG_ERR("failed to delete datapath dp%d: %s",
 -                     i, strerror(retval));
 +            dpif_delete(dpif);
 +        found:
 +            svec_destroy(&all_names);
 +            dpif_close(dpif);
          }
      }
 +    svec_destroy(&dpif_names);
  
      unixctl_command_register("bridge/dump-flows", bridge_unixctl_dump_flows);
  
 +    bond_init();
      bridge_reconfigure();
  }
  
@@@ -360,116 -350,43 +360,116 @@@ bridge_configure_ssl(void
       * the old certificate will still be trusted until vSwitch is
       * restarted.  We may want to address this in vconn's SSL library. */
      if (config_string_change("ssl.ca-cert", &cacert_file)
 -            || (stat(cacert_file, &s) && errno == ENOENT)) {
 +        || (cacert_file && stat(cacert_file, &s) && errno == ENOENT)) {
          vconn_ssl_set_ca_cert_file(cacert_file,
                                     cfg_get_bool(0, "ssl.bootstrap-ca-cert"));
      }
  }
  #endif
  
 +/* iterate_and_prune_ifaces() callback function that opens the network device
 + * for 'iface', if it is not already open, and retrieves the interface's MAC
 + * address and carrier status. */
 +static bool
 +init_iface_netdev(struct bridge *br UNUSED, struct iface *iface,
 +                  void *aux UNUSED)
 +{
 +    if (iface->netdev) {
 +        return true;
 +    } else if (!netdev_open(iface->name, NETDEV_ETH_TYPE_NONE,
 +                            &iface->netdev)) {
 +        netdev_get_carrier(iface->netdev, &iface->enabled);
 +        return true;
 +    } else {
 +        /* If the network device can't be opened, then we're not going to try
 +         * to do anything with this interface. */
 +        return false;
 +    }
 +}
 +
 +static bool
 +check_iface_dp_ifidx(struct bridge *br, struct iface *iface, void *aux UNUSED)
 +{
 +    if (iface->dp_ifidx >= 0) {
 +        VLOG_DBG("%s has interface %s on port %d",
 +                 dpif_name(br->dpif),
 +                 iface->name, iface->dp_ifidx);
 +        return true;
 +    } else {
 +        VLOG_ERR("%s interface not in %s, dropping",
 +                 iface->name, dpif_name(br->dpif));
 +        return false;
 +    }
 +}
 +
 +static bool
 +set_iface_properties(struct bridge *br UNUSED, struct iface *iface,
 +                   void *aux UNUSED)
 +{
 +    int rate, burst;
 +
 +    /* Set policing attributes. */
 +    rate = cfg_get_int(0, "port.%s.ingress.policing-rate", iface->name);
 +    burst = cfg_get_int(0, "port.%s.ingress.policing-burst", iface->name);
 +    netdev_set_policing(iface->netdev, rate, burst);
 +
 +    /* Set MAC address of internal interfaces other than the local
 +     * interface. */
 +    if (iface->dp_ifidx != ODPP_LOCAL
 +        && iface_is_internal(br, iface->name)) {
 +        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)
 +{
 +    size_t i, j;
 +
 +    for (i = 0; i < br->n_ports; ) {
 +        struct port *port = br->ports[i];
 +        for (j = 0; j < port->n_ifaces; ) {
 +            struct iface *iface = port->ifaces[j];
 +            if (cb(br, iface, aux)) {
 +                j++;
 +            } else {
 +                iface_destroy(iface);
 +            }
 +        }
 +
 +        if (port->n_ifaces) {
 +            i++;
 +        } else  {
 +            VLOG_ERR("%s port has no interfaces, dropping", port->name);
 +            port_destroy(port);
 +        }
 +    }
 +}
 +
  void
  bridge_reconfigure(void)
  {
 -    struct svec old_br, new_br, raw_new_br;
 +    struct svec old_br, new_br;
      struct bridge *br, *next;
 -    size_t i, j;
 +    size_t i;
  
      COVERAGE_INC(bridge_reconfigure);
  
 -    /* Collect old bridges. */
 +    /* Collect old and new bridges. */
      svec_init(&old_br);
 +    svec_init(&new_br);
      LIST_FOR_EACH (br, struct bridge, node, &all_bridges) {
          svec_add(&old_br, br->name);
      }
 -
 -    /* Collect new bridges. */
 -    svec_init(&raw_new_br);
 -    cfg_get_subsections(&raw_new_br, "bridge");
 -    svec_init(&new_br);
 -    for (i = 0; i < raw_new_br.n; i++) {
 -        const char *name = raw_new_br.names[i];
 -        if ((!strncmp(name, "dp", 2) && isdigit(name[2])) ||
 -            (!strncmp(name, "nl:", 3) && isdigit(name[3]))) {
 -            VLOG_ERR("%s is not a valid bridge name (bridges may not be "
 -                     "named \"dp\" or \"nl:\" followed by a digit)", name);
 -        } else {
 -            svec_add(&new_br, name);
 -        }
 -    }
 -    svec_destroy(&raw_new_br);
 +    cfg_get_subsections(&new_br, "bridge");
  
      /* Get rid of deleted bridges and add new bridges. */
      svec_sort(&old_br);
          size_t n_dpif_ports;
          struct svec want_ifaces;
  
 -        dpif_port_list(&br->dpif, &dpif_ports, &n_dpif_ports);
 +        dpif_port_list(br->dpif, &dpif_ports, &n_dpif_ports);
          bridge_get_all_ifaces(br, &want_ifaces);
          for (i = 0; i < n_dpif_ports; i++) {
              const struct odp_port *p = &dpif_ports[i];
              if (!svec_contains(&want_ifaces, p->devname)
                  && strcmp(p->devname, br->name)) {
 -                int retval = dpif_port_del(&br->dpif, p->port);
 +                int retval = dpif_port_del(br->dpif, p->port);
                  if (retval) {
 -                    VLOG_ERR("failed to remove %s interface from dp%u: %s",
 -                             p->devname, dpif_id(&br->dpif), strerror(retval));
 +                    VLOG_ERR("failed to remove %s interface from %s: %s",
 +                             p->devname, dpif_name(br->dpif),
 +                             strerror(retval));
                  }
              }
          }
          struct odp_port *dpif_ports;
          size_t n_dpif_ports;
          struct svec cur_ifaces, want_ifaces, add_ifaces;
 -        int next_port_no;
  
 -        dpif_port_list(&br->dpif, &dpif_ports, &n_dpif_ports);
 +        dpif_port_list(br->dpif, &dpif_ports, &n_dpif_ports);
          svec_init(&cur_ifaces);
          for (i = 0; i < n_dpif_ports; i++) {
              svec_add(&cur_ifaces, dpif_ports[i].devname);
          bridge_get_all_ifaces(br, &want_ifaces);
          svec_diff(&want_ifaces, &cur_ifaces, &add_ifaces, NULL, NULL);
  
 -        next_port_no = 1;
          for (i = 0; i < add_ifaces.n; i++) {
              const char *if_name = add_ifaces.names[i];
 -            for (;;) {
 -                bool internal;
 -                int error;
 -
 -                /* Add to datapath. */
 -                internal = iface_is_internal(br, if_name);
 -                error = dpif_port_add(&br->dpif, if_name, next_port_no++,
 -                                      internal ? ODP_PORT_INTERNAL : 0);
 -                if (error != EEXIST) {
 -                    if (next_port_no >= 256) {
 -                        VLOG_ERR("ran out of valid port numbers on dp%u",
 -                                 dpif_id(&br->dpif));
 -                        goto out;
 -                    }
 -                    if (error) {
 -                        VLOG_ERR("failed to add %s interface to dp%u: %s",
 -                                 if_name, dpif_id(&br->dpif), strerror(error));
 -                    }
 -                    break;
 -                }
 +            bool internal;
 +            int error;
 +
 +            /* Add to datapath. */
 +            internal = iface_is_internal(br, if_name);
 +            error = dpif_port_add(br->dpif, if_name,
 +                                  internal ? ODP_PORT_INTERNAL : 0, NULL);
 +            if (error == EFBIG) {
 +                VLOG_ERR("ran out of valid port numbers on %s",
 +                         dpif_name(br->dpif));
 +                break;
 +            } else if (error) {
 +                VLOG_ERR("failed to add %s interface to %s: %s",
 +                         if_name, dpif_name(br->dpif), strerror(error));
              }
          }
 -    out:
          svec_destroy(&cur_ifaces);
          svec_destroy(&want_ifaces);
          svec_destroy(&add_ifaces);
      LIST_FOR_EACH (br, struct bridge, node, &all_bridges) {
          uint8_t ea[8];
          uint64_t dpid;
 -        struct iface *local_iface = NULL;
 -        const char *devname;
 -        uint8_t engine_type = br->dpif.minor;
 -        uint8_t engine_id = br->dpif.minor;
 +        struct iface *local_iface;
 +        struct iface *hw_addr_iface;
 +        uint8_t engine_type, engine_id;
          bool add_id_to_iface = false;
          struct svec nf_hosts;
  
          bridge_fetch_dp_ifaces(br);
 -        for (i = 0; i < br->n_ports; ) {
 -            struct port *port = br->ports[i];
 +        iterate_and_prune_ifaces(br, init_iface_netdev, NULL);
  
 -            for (j = 0; j < port->n_ifaces; ) {
 -                struct iface *iface = port->ifaces[j];
 -                if (iface->dp_ifidx < 0) {
 -                    VLOG_ERR("%s interface not in dp%u, dropping",
 -                             iface->name, dpif_id(&br->dpif));
 -                    iface_destroy(iface);
 -                } else {
 -                    if (iface->dp_ifidx == ODPP_LOCAL) {
 -                        local_iface = iface;
 -                    }
 -                    VLOG_DBG("dp%u has interface %s on port %d",
 -                             dpif_id(&br->dpif), iface->name, iface->dp_ifidx);
 -                    j++;
 -                }
 -            }
 -            if (!port->n_ifaces) {
 -                VLOG_ERR("%s port has no interfaces, dropping", port->name);
 -                port_destroy(port);
 -                continue;
 -            }
 -            i++;
 -        }
 +        iterate_and_prune_ifaces(br, check_iface_dp_ifidx, NULL);
  
          /* Pick local port hardware address, datapath ID. */
 -        bridge_pick_local_hw_addr(br, ea, &devname);
 +        bridge_pick_local_hw_addr(br, ea, &hw_addr_iface);
 +        local_iface = bridge_get_local_iface(br);
          if (local_iface) {
 -            int error = netdev_nodev_set_etheraddr(local_iface->name, ea);
 +            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 "
              }
          }
  
 -        dpid = bridge_pick_datapath_id(br, ea, devname);
 +        dpid = bridge_pick_datapath_id(br, ea, hw_addr_iface);
          ofproto_set_datapath_id(br->ofproto, dpid);
  
          /* Set NetFlow configuration on this bridge. */
 +        dpif_get_netflow_ids(br->dpif, &engine_type, &engine_id);
          if (cfg_has("netflow.%s.engine-type", br->name)) {
              engine_type = cfg_get_int(0, "netflow.%s.engine-type", 
                      br->name);
              struct port *port = br->ports[i];
  
              port_update_vlan_compat(port);
 -
 -            for (j = 0; j < port->n_ifaces; j++) {
 -                struct iface *iface = port->ifaces[j];
 -                if (iface->dp_ifidx != ODPP_LOCAL
 -                    && iface_is_internal(br, iface->name)) {
 -                    iface_set_mac(iface);
 -                }
 -            }
 +            port_update_bonding(port);
          }
      }
      LIST_FOR_EACH (br, struct bridge, node, &all_bridges) {
          brstp_reconfigure(br);
 +        iterate_and_prune_ifaces(br, set_iface_properties, NULL);
      }
  }
  
  static void
  bridge_pick_local_hw_addr(struct bridge *br, uint8_t ea[ETH_ADDR_LEN],
 -                          const char **devname)
 +                          struct iface **hw_addr_iface)
  {
      uint64_t requested_ea;
      size_t i, j;
      int error;
  
 -    *devname = NULL;
 +    *hw_addr_iface = NULL;
  
      /* Did the user request a particular MAC? */
      requested_ea = cfg_get_mac(0, "bridge.%s.mac", br->name);
              for (j = 0; j < port->n_ifaces; j++) {
                  struct iface *candidate = port->ifaces[j];
                  uint8_t candidate_ea[ETH_ADDR_LEN];
 -                if (!netdev_nodev_get_etheraddr(candidate->name, candidate_ea)
 +                if (!netdev_get_etheraddr(candidate->netdev, candidate_ea)
                      && eth_addr_equals(iface_ea, candidate_ea)) {
                      iface = candidate;
                  }
              }
  
              /* Grab MAC. */
 -            error = netdev_nodev_get_etheraddr(iface->name, iface_ea);
 +            error = netdev_get_etheraddr(iface->netdev, iface_ea);
              if (error) {
                  static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
                  VLOG_ERR_RL(&rl, "failed to obtain Ethernet address of %s: %s",
              memcmp(iface_ea, ea, ETH_ADDR_LEN) < 0)
          {
              memcpy(ea, iface_ea, ETH_ADDR_LEN);
 -            *devname = iface ? iface->name : NULL;
 +            *hw_addr_iface = iface;
          }
      }
      if (eth_addr_is_multicast(ea) || eth_addr_is_vif(ea)) {
          memcpy(ea, br->default_ea, ETH_ADDR_LEN);
 -        *devname = NULL;
 +        *hw_addr_iface = NULL;
          VLOG_WARN("bridge %s: using default bridge Ethernet "
                    "address "ETH_ADDR_FMT, br->name, ETH_ADDR_ARGS(ea));
      } else {
  
  /* Choose and returns the datapath ID for bridge 'br' given that the bridge
   * Ethernet address is 'bridge_ea'.  If 'bridge_ea' is the Ethernet address of
 - * a network device, then that network device's name must be passed in as
 - * 'devname'; if 'bridge_ea' was derived some other way, then 'devname' must be
 - * passed in as a null pointer. */
 + * an interface on 'br', then that interface must be passed in as
 + * 'hw_addr_iface'; if 'bridge_ea' was derived some other way, then
 + * 'hw_addr_iface' must be passed in as a null pointer. */
  static uint64_t
  bridge_pick_datapath_id(struct bridge *br,
                          const uint8_t bridge_ea[ETH_ADDR_LEN],
 -                        const char *devname)
 +                        struct iface *hw_addr_iface)
  {
      /*
       * The procedure for choosing a bridge MAC address will, in the most
          return dpid;
      }
  
 -    if (devname) {
 +    if (hw_addr_iface) {
          int vlan;
 -        if (!netdev_get_vlan_vid(devname, &vlan)) {
 +        if (!netdev_get_vlan_vid(hw_addr_iface->netdev, &vlan)) {
              /*
               * A bridge whose MAC address is taken from a VLAN network device
               * (that is, a network device created with vconfig(8) or similar
@@@ -903,26 -855,6 +903,26 @@@ bridge_flush(struct bridge *br
          mac_learning_flush(br->ml);
      }
  }
 +
 +/* Returns the 'br' interface for the ODPP_LOCAL port, or null if 'br' has no
 + * such interface. */
 +static struct iface *
 +bridge_get_local_iface(struct bridge *br)
 +{
 +    size_t i, j;
 +
 +    for (i = 0; i < br->n_ports; i++) {
 +        struct port *port = br->ports[i];
 +        for (j = 0; j < port->n_ifaces; j++) {
 +            struct iface *iface = port->ifaces[j];
 +            if (iface->dp_ifidx == ODPP_LOCAL) {
 +                return iface;
 +            }
 +        }
 +    }
 +
 +    return NULL;
 +}
  \f
  /* Bridge unixctl user interface functions. */
  static void
@@@ -965,7 -897,7 +965,7 @@@ bridge_create(const char *name
      br = xcalloc(1, sizeof *br);
  
      error = dpif_create(name, &br->dpif);
 -    if (error == EEXIST) {
 +    if (error == EEXIST || error == EBUSY) {
          error = dpif_open(name, &br->dpif);
          if (error) {
              VLOG_ERR("datapath %s already exists but cannot be opened: %s",
              free(br);
              return NULL;
          }
 -        dpif_flow_flush(&br->dpif);
 +        dpif_flow_flush(br->dpif);
      } else if (error) {
          VLOG_ERR("failed to create datapath %s: %s", name, strerror(error));
          free(br);
      error = ofproto_create(name, &bridge_ofhooks, br, &br->ofproto);
      if (error) {
          VLOG_ERR("failed to create switch %s: %s", name, strerror(error));
 -        dpif_delete(&br->dpif);
 -        dpif_close(&br->dpif);
 +        dpif_delete(br->dpif);
 +        dpif_close(br->dpif);
          free(br);
          return NULL;
      }
  
      list_push_back(&all_bridges, &br->node);
  
 -    VLOG_INFO("created bridge %s on dp%u", br->name, dpif_id(&br->dpif));
 +    VLOG_INFO("created bridge %s on %s", br->name, dpif_name(br->dpif));
  
      return br;
  }
@@@ -1016,12 -948,12 +1016,12 @@@ bridge_destroy(struct bridge *br
              port_destroy(br->ports[br->n_ports - 1]);
          }
          list_remove(&br->node);
 -        error = dpif_delete(&br->dpif);
 +        error = dpif_delete(br->dpif);
          if (error && error != ENOENT) {
 -            VLOG_ERR("failed to delete dp%u: %s",
 -                     dpif_id(&br->dpif), strerror(error));
 +            VLOG_ERR("failed to delete %s: %s",
 +                     dpif_name(br->dpif), strerror(error));
          }
 -        dpif_close(&br->dpif);
 +        dpif_close(br->dpif);
          ofproto_destroy(br->ofproto);
          free(br->controller);
          mac_learning_destroy(br->ml);
@@@ -1113,29 -1045,13 +1113,29 @@@ bridge_get_controller(const struct brid
      return controller && controller[0] ? controller : NULL;
  }
  
 +static bool
 +check_duplicate_ifaces(struct bridge *br, struct iface *iface, void *ifaces_)
 +{
 +    struct svec *ifaces = ifaces_;
 +    if (!svec_contains(ifaces, iface->name)) {
 +        svec_add(ifaces, iface->name);
 +        svec_sort(ifaces);
 +        return true;
 +    } else {
 +        VLOG_ERR("bridge %s: %s interface is on multiple ports, "
 +                 "removing from %s",
 +                 br->name, iface->name, iface->port->name);
 +        return false;
 +    }
 +}
 +
  static void
  bridge_reconfigure_one(struct bridge *br)
  {
      struct svec old_ports, new_ports, ifaces;
      struct svec listeners, old_listeners;
      struct svec snoops, old_snoops;
 -    size_t i, j;
 +    size_t i;
  
      /* Collect old ports. */
      svec_init(&old_ports);
      svec_init(&new_ports);
      cfg_get_all_keys(&new_ports, "bridge.%s.port", br->name);
      svec_sort(&new_ports);
 -    if (bridge_get_controller(br) && !svec_contains(&new_ports, br->name)) {
 -        svec_add(&new_ports, br->name);
 -        svec_sort(&new_ports);
 +    if (bridge_get_controller(br)) {
 +        char local_name[IF_NAMESIZE];
 +        int error;
 +
 +        error = dpif_port_get_name(br->dpif, ODPP_LOCAL,
 +                                   local_name, sizeof local_name);
 +        if (!error && !svec_contains(&new_ports, local_name)) {
 +            svec_add(&new_ports, local_name);
 +            svec_sort(&new_ports);
 +        }
      }
      if (!svec_is_unique(&new_ports)) {
          VLOG_WARN("bridge %s: %s specified twice as bridge port",
  
      /* Check and delete duplicate interfaces. */
      svec_init(&ifaces);
 -    for (i = 0; i < br->n_ports; ) {
 -        struct port *port = br->ports[i];
 -        for (j = 0; j < port->n_ifaces; ) {
 -            struct iface *iface = port->ifaces[j];
 -            if (svec_contains(&ifaces, iface->name)) {
 -                VLOG_ERR("bridge %s: %s interface is on multiple ports, "
 -                         "removing from %s",
 -                         br->name, iface->name, port->name);
 -                iface_destroy(iface);
 -            } else {
 -                svec_add(&ifaces, iface->name);
 -                svec_sort(&ifaces);
 -                j++;
 -            }
 -        }
 -        if (!port->n_ifaces) {
 -            VLOG_ERR("%s port has no interfaces, dropping", port->name);
 -            port_destroy(port);
 -        } else {
 -            i++;
 -        }
 -    }
 +    iterate_and_prune_ifaces(br, check_duplicate_ifaces, &ifaces);
      svec_destroy(&ifaces);
  
      /* Delete all flows if we're switching from connected to standalone or vice
@@@ -1274,8 -1204,9 +1274,8 @@@ bridge_reconfigure_controller(struct br
                                    cfg_get_string(0, "%s.accept-regex", pfx),
                                    update_resolv_conf);
          } else {
 -            struct netdev *netdev;
 +            struct iface *local_iface;
              bool in_band;
 -            int error;
  
              in_band = (!cfg_is_valid(CFG_BOOL | CFG_REQUIRED,
                                       "%s.in-band", pfx)
              ofproto_set_discovery(br->ofproto, false, NULL, NULL);
              ofproto_set_in_band(br->ofproto, in_band);
  
 -            error = netdev_open(br->name, NETDEV_ETH_TYPE_NONE, &netdev);
 -            if (!error) {
 -                if (cfg_is_valid(CFG_IP | CFG_REQUIRED, "%s.ip", pfx)) {
 -                    struct in_addr ip, mask, gateway;
 -                    ip.s_addr = cfg_get_ip(0, "%s.ip", pfx);
 -                    mask.s_addr = cfg_get_ip(0, "%s.netmask", pfx);
 -                    gateway.s_addr = cfg_get_ip(0, "%s.gateway", pfx);
 -
 -                    netdev_turn_flags_on(netdev, NETDEV_UP, true);
 -                    if (!mask.s_addr) {
 -                        mask.s_addr = guess_netmask(ip.s_addr);
 -                    }
 -                    if (!netdev_set_in4(netdev, ip, mask)) {
 -                        VLOG_INFO("bridge %s: configured IP address "IP_FMT", "
 -                                  "netmask "IP_FMT,
 -                                  br->name, IP_ARGS(&ip.s_addr),
 -                                  IP_ARGS(&mask.s_addr));
 -                    }
 +            local_iface = bridge_get_local_iface(br);
 +            if (local_iface
 +                && cfg_is_valid(CFG_IP | CFG_REQUIRED, "%s.ip", pfx)) {
 +                struct netdev *netdev = local_iface->netdev;
 +                struct in_addr ip, mask, gateway;
 +                ip.s_addr = cfg_get_ip(0, "%s.ip", pfx);
 +                mask.s_addr = cfg_get_ip(0, "%s.netmask", pfx);
 +                gateway.s_addr = cfg_get_ip(0, "%s.gateway", pfx);
 +
 +                netdev_turn_flags_on(netdev, NETDEV_UP, true);
 +                if (!mask.s_addr) {
 +                    mask.s_addr = guess_netmask(ip.s_addr);
 +                }
 +                if (!netdev_set_in4(netdev, ip, mask)) {
 +                    VLOG_INFO("bridge %s: configured IP address "IP_FMT", "
 +                              "netmask "IP_FMT,
 +                              br->name, IP_ARGS(&ip.s_addr),
 +                              IP_ARGS(&mask.s_addr));
 +                }
  
 -                    if (gateway.s_addr) {
 -                        if (!netdev_add_router(gateway)) {
 -                            VLOG_INFO("bridge %s: configured gateway "IP_FMT,
 -                                      br->name, IP_ARGS(&gateway.s_addr));
 -                        }
 +                if (gateway.s_addr) {
 +                    if (!netdev_add_router(netdev, gateway)) {
 +                        VLOG_INFO("bridge %s: configured gateway "IP_FMT,
 +                                  br->name, IP_ARGS(&gateway.s_addr));
                      }
                  }
 -                netdev_close(netdev);
              }
          }
  
@@@ -1438,17 -1370,17 +1438,17 @@@ bridge_fetch_dp_ifaces(struct bridge *b
      }
      port_array_clear(&br->ifaces);
  
 -    dpif_port_list(&br->dpif, &dpif_ports, &n_dpif_ports);
 +    dpif_port_list(br->dpif, &dpif_ports, &n_dpif_ports);
      for (i = 0; i < n_dpif_ports; i++) {
          struct odp_port *p = &dpif_ports[i];
          struct iface *iface = iface_lookup(br, p->devname);
          if (iface) {
              if (iface->dp_ifidx >= 0) {
 -                VLOG_WARN("dp%u reported interface %s twice",
 -                          dpif_id(&br->dpif), p->devname);
 +                VLOG_WARN("%s reported interface %s twice",
 +                          dpif_name(br->dpif), p->devname);
              } else if (iface_from_dp_ifidx(br, p->port)) {
 -                VLOG_WARN("dp%u reported interface %"PRIu16" twice",
 -                          dpif_id(&br->dpif), p->port);
 +                VLOG_WARN("%s reported interface %"PRIu16" twice",
 +                          dpif_name(br->dpif), p->port);
              } else {
                  port_array_set(&br->ifaces, p->port, iface);
                  iface->dp_ifidx = p->port;
@@@ -1783,14 -1715,26 +1783,26 @@@ compose_dsts(const struct bridge *br, c
                      if (port_includes_vlan(port, m->out_vlan)
                          && set_dst(dst, flow, in_port, port, tags))
                      {
+                         int flow_vlan;
                          if (port->vlan < 0) {
                              dst->vlan = m->out_vlan;
                          }
                          if (dst_is_duplicate(dsts, dst - dsts, dst)) {
                              continue;
                          }
-                         if (dst->dp_ifidx == flow->in_port
-                             && dst->vlan == vlan) {
+                         /* 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. */
+                         flow_vlan = ntohs(flow->dl_vlan);
+                         if (flow_vlan == 0) {
+                             flow_vlan = OFP_VLAN_NONE;
+                         }
+                         if (port == in_port && dst->vlan == flow_vlan) {
                              /* Don't send out input port on same VLAN. */
                              continue;
                          }
@@@ -1953,33 -1897,27 +1965,27 @@@ process_flow(struct bridge *br, const f
          goto done;
      }
  
-     /* Multicast (and broadcast) packets on bonds need special attention, to
-      * avoid receiving duplicates. */
-     if (in_port->n_ifaces > 1 && eth_addr_is_multicast(flow->dl_dst)) {
-         *tags |= in_port->active_iface_tag;
-         if (in_port->active_iface != in_iface->port_ifidx) {
-             /* Drop all multicast packets on inactive slaves. */
-             goto done;
-         } else {
-             /* Drop all multicast packets for which we have learned a different
-              * input port, because we probably sent the packet on one slave
-              * and got it back on the active slave.  Broadcast ARP replies are
-              * an exception to this rule: the host has moved to another
-              * switch. */
-             int src_idx = mac_learning_lookup(br->ml, flow->dl_src, vlan);
-             if (src_idx != -1 && src_idx != in_port->port_idx) {
-                 if (packet) {
-                     if (!is_bcast_arp_reply(flow, packet)) {
-                         goto done;
-                     }
-                 } else {
-                     /* No way to know whether it's an ARP reply, because the
-                      * flow entry doesn't include enough information and we
-                      * don't have a packet.  Punt. */
-                     return false;
-                 }
+     /* Packets received on bonds need special attention to avoid duplicates. */
+     if (in_port->n_ifaces > 1) {
+         int src_idx;
+         if (eth_addr_is_multicast(flow->dl_dst)) {
+             *tags |= in_port->active_iface_tag;
+             if (in_port->active_iface != in_iface->port_ifidx) {
+                 /* Drop all multicast packets on inactive slaves. */
+                 goto done;
              }
          }
+         /* Drop all packets for which we have learned a different input
+          * port, because we probably sent the packet on one slave and got
+          * it back on the other.  Broadcast ARP replies are an exception
+          * to this rule: the host has moved to another switch. */
+         src_idx = mac_learning_lookup(br->ml, flow->dl_src, vlan);
+         if (src_idx != -1 && src_idx != in_port->port_idx &&
+             (!packet || !is_bcast_arp_reply(flow, packet))) {
+                 goto done;
+         }
      }
  
      /* MAC learning. */
                                                 tags);
          if (out_port_idx >= 0 && out_port_idx < br->n_ports) {
              out_port = br->ports[out_port_idx];
+         } else if (!packet) {
+             /* If we are revalidating but don't have a learning entry then
+              * eject the flow.  Installing a flow that floods packets will
+              * prevent us from seeing future packets and learning properly. */
+             return false;
          }
      }
  
@@@ -2061,6 -2004,7 +2072,6 @@@ bridge_port_changed_ofhook_cb(enum ofp_
  
          bridge_flush(br);
      } else {
 -        memcpy(iface->mac, opp->hw_addr, ETH_ADDR_LEN);
          if (port->n_ifaces > 1) {
              bool up = !(opp->state & OFPPS_LINK_DOWN);
              bond_link_status_update(iface, up);
@@@ -2259,8 -2203,9 +2270,9 @@@ log_bals(const struct slave_balance *ba
  /* Shifts 'hash' from 'from' to 'to' within 'port'. */
  static void
  bond_shift_load(struct slave_balance *from, struct slave_balance *to,
-                 struct bond_entry *hash)
+                 int hash_idx)
  {
+     struct bond_entry *hash = from->hashes[hash_idx];
      struct port *port = from->iface->port;
      uint64_t delta = hash->tx_bytes;
  
       * it require more work, the only purpose it would be to allow that hash to
       * be migrated to another slave in this rebalancing run, and there is no
       * point in doing that.  */
-     if (from->hashes[0] == hash) {
+     if (hash_idx == 0) {
          from->hashes++;
      } else {
-         int i = hash - from->hashes[0];
-         memmove(from->hashes + i, from->hashes + i + 1,
-                 (from->n_hashes - (i + 1)) * sizeof *from->hashes);
+         memmove(from->hashes + hash_idx, from->hashes + hash_idx + 1,
+                 (from->n_hashes - (hash_idx + 1)) * sizeof *from->hashes);
      }
      from->n_hashes--;
  
@@@ -2368,22 -2312,60 +2379,60 @@@ bond_rebalance_port(struct port *port
              /* 'from' is carrying significantly more load than 'to', and that
               * load is split across at least two different hashes.  Pick a hash
               * to migrate to 'to' (the least-loaded slave), given that doing so
-              * must not cause 'to''s load to exceed 'from''s load.
+              * must decrease the ratio of the load on the two slaves by at
+              * least 0.1.
               *
               * The sort order we use means that we prefer to shift away the
               * smallest hashes instead of the biggest ones.  There is little
               * reason behind this decision; we could use the opposite sort
               * order to shift away big hashes ahead of small ones. */
              size_t i;
+             bool order_swapped;
  
              for (i = 0; i < from->n_hashes; i++) {
+                 double old_ratio, new_ratio;
                  uint64_t delta = from->hashes[i]->tx_bytes;
-                 if (to->tx_bytes + delta < from->tx_bytes - delta) {
+                 if (delta == 0 || from->tx_bytes - delta == 0) {
+                     /* Pointless move. */
+                     continue;
+                 }
+                 order_swapped = from->tx_bytes - delta < to->tx_bytes + delta;
+                 if (to->tx_bytes == 0) {
+                     /* Nothing on the new slave, move it. */
+                     break;
+                 }
+                 old_ratio = (double)from->tx_bytes / to->tx_bytes;
+                 new_ratio = (double)(from->tx_bytes - delta) /
+                             (to->tx_bytes + delta);
+                 if (new_ratio == 0) {
+                     /* Should already be covered but check to prevent division
+                      * by zero. */
+                     continue;
+                 }
+                 if (new_ratio < 1) {
+                     new_ratio = 1 / new_ratio;
+                 }
+                 if (old_ratio - new_ratio > 0.1) {
+                     /* Would decrease the ratio, move it. */
                      break;
                  }
              }
              if (i < from->n_hashes) {
-                 bond_shift_load(from, to, from->hashes[i]);
+                 bond_shift_load(from, to, i);
+                 port->bond_compat_is_stale = true;
+                 /* If the result of the migration changed the relative order of
+                  * 'from' and 'to' swap them back to maintain invariants. */
+                 if (order_swapped) {
+                     swap_bals(from, to);
+                 }
  
                  /* Re-sort 'bals'.  Note that this may make 'from' and 'to'
                   * point to different slave_balance structures.  It is only
              } else {
                  from++;
              }
-             port->bond_compat_is_stale = true;
          }
      }
  
@@@ -2739,25 -2720,6 +2787,25 @@@ bond_unixctl_disable_slave(struct unixc
      enable_slave(conn, args, false);
  }
  
 +static void
 +bond_unixctl_hash(struct unixctl_conn *conn, const char *args)
 +{
 +      uint8_t mac[ETH_ADDR_LEN];
 +      uint8_t hash;
 +      char *hash_cstr;
 +
 +      if (sscanf(args, ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(mac))
 +          == ETH_ADDR_SCAN_COUNT) {
 +              hash = bond_hash(mac);
 +
 +              hash_cstr = xasprintf("%u", hash);
 +              unixctl_command_reply(conn, 200, hash_cstr);
 +              free(hash_cstr);
 +      } else {
 +              unixctl_command_reply(conn, 501, "invalid mac");
 +      }
 +}
 +
  static void
  bond_init(void)
  {
                               bond_unixctl_set_active_slave);
      unixctl_command_register("bond/enable-slave", bond_unixctl_enable_slave);
      unixctl_command_register("bond/disable-slave", bond_unixctl_disable_slave);
 +    unixctl_command_register("bond/hash", bond_unixctl_hash);
  }
  \f
  /* Port functions. */
@@@ -3067,7 -3028,7 +3115,7 @@@ port_update_bond_compat(struct port *po
          if (slave->up) {
              bond.up = true;
          }
 -        memcpy(slave->mac, iface->mac, ETH_ADDR_LEN);
 +        netdev_get_etheraddr(iface->netdev, slave->mac);
      }
  
      if (cfg_get_bool(0, "bonding.%s.fake-iface", port->name)) {
@@@ -3111,8 -3072,7 +3159,8 @@@ port_update_vlan_compat(struct port *po
                  && p->n_ifaces
                  && (!vlandev_name || strcmp(p->name, vlandev_name) <= 0))
              {
 -                const uint8_t *ea = p->ifaces[0]->mac;
 +                uint8_t ea[ETH_ADDR_LEN];
 +                netdev_get_etheraddr(p->ifaces[0]->netdev, ea);
                  if (!eth_addr_is_multicast(ea) &&
                      !eth_addr_is_reserved(ea) &&
                      !eth_addr_is_zero(ea)) {
@@@ -3138,7 -3098,18 +3186,7 @@@ iface_create(struct port *port, const c
      iface->dp_ifidx = -1;
      iface->tag = tag_create_random();
      iface->delay_expires = LLONG_MAX;
 -
 -    if (!cfg_get_bool(0, "iface.%s.internal", iface->name)) {
 -        netdev_nodev_get_etheraddr(name, iface->mac);
 -        netdev_nodev_get_carrier(name, &iface->enabled);
 -    } else {
 -        /* Internal interfaces are created later by the call to dpif_port_add()
 -         * in bridge_reconfigure().  Until then, we can't obtain any
 -         * information about them.  (There's no real value in doing so, anyway,
 -         * because the 'mac' and 'enabled' values are only used for interfaces
 -         * that are bond slaves, and it doesn't normally make sense to bond an
 -         * internal interface.) */
 -    }
 +    iface->netdev = NULL;
  
      if (port->n_ifaces >= port->allocated_ifaces) {
          port->ifaces = x2nrealloc(port->ifaces, &port->allocated_ifaces,
  
      VLOG_DBG("attached network device %s to port %s", iface->name, port->name);
  
 -    port_update_bonding(port);
      bridge_flush(port->bridge);
  }
  
@@@ -3170,7 -3142,6 +3218,7 @@@ iface_destroy(struct iface *iface
          del = port->ifaces[iface->port_ifidx] = port->ifaces[--port->n_ifaces];
          del->port_ifidx = iface->port_ifidx;
  
 +        netdev_close(iface->netdev);
          free(iface->name);
          free(iface);
  
              bond_send_learning_packets(port);
          }
  
 -        port_update_bonding(port);
          bridge_flush(port->bridge);
      }
  }
@@@ -3252,7 -3224,7 +3300,7 @@@ iface_set_mac(struct iface *iface
              VLOG_ERR("ignoring iface.%s.mac; use bridge.%s.mac instead",
                       iface->name, iface->name);
          } else {
 -            int error = netdev_nodev_set_etheraddr(iface->name, ea);
 +            int error = netdev_set_etheraddr(iface->netdev, ea);
              if (error) {
                  VLOG_ERR("interface %s: setting MAC failed (%s)",
                           iface->name, strerror(error));
@@@ -3581,25 -3553,23 +3629,25 @@@ brstp_send_bpdu(struct ofpbuf *pkt, in
      if (!iface) {
          VLOG_WARN_RL(&rl, "%s: cannot send BPDU on unknown port %d",
                       br->name, port_no);
 -    } else if (eth_addr_is_zero(iface->mac)) {
 -        VLOG_WARN_RL(&rl, "%s: cannot send BPDU on port %d with unknown MAC",
 -                     br->name, port_no);
      } else {
 -        union ofp_action action;
          struct eth_header *eth = pkt->l2;
 -        flow_t flow;
  
 -        memcpy(eth->eth_src, iface->mac, ETH_ADDR_LEN);
 +        netdev_get_etheraddr(iface->netdev, eth->eth_src);
 +        if (eth_addr_is_zero(eth->eth_src)) {
 +            VLOG_WARN_RL(&rl, "%s: cannot send BPDU on port %d "
 +                         "with unknown MAC", br->name, port_no);
 +        } else {
 +            union ofp_action action;
 +            flow_t flow;
  
 -        memset(&action, 0, sizeof action);
 -        action.type = htons(OFPAT_OUTPUT);
 -        action.output.len = htons(sizeof action);
 -        action.output.port = htons(port_no);
 +            memset(&action, 0, sizeof action);
 +            action.type = htons(OFPAT_OUTPUT);
 +            action.output.len = htons(sizeof action);
 +            action.output.port = htons(port_no);
  
 -        flow_extract(pkt, ODPP_NONE, &flow);
 -        ofproto_send_packet(br->ofproto, &flow, &action, 1, pkt);
 +            flow_extract(pkt, ODPP_NONE, &flow);
 +            ofproto_send_packet(br->ofproto, &flow, &action, 1, pkt);
 +        }
      }
      ofpbuf_delete(pkt);
  }
diff --combined xenserver/README
@@@ -50,6 -50,12 +50,12 @@@ files are
          needed by the controller.  This is called by the "vif" script,
          which is run when virtual interfaces are added and removed.
  
 -    root_vswitch_scripts_refresh-xs-network-uuids
++    usr_share_vswitch_scripts_refresh-xs-network-uuids
+         Script to refresh bridge.<bridge>.xs-network-uuids keys, which
+         can get out-of-sync following a pool join.  Running this script
+         is an alternative to rebooting the host.
      root_vswitch_scripts_sysconfig.template
  
          Template for vswitch's /etc/sysconfig/vswitch configuration
diff --combined xenserver/automake.mk
@@@ -14,9 -14,10 +14,10 @@@ EXTRA_DIST += 
        xenserver/etc_xapi.d_plugins_vswitch-cfg-update \
        xenserver/etc_xensource_scripts_vif \
        xenserver/opt_xensource_libexec_interface-reconfigure \
 -      xenserver/root_vswitch_scripts_sysconfig.template \
 -      xenserver/root_vswitch_scripts_dump-vif-details \
 -      xenserver/root_vswitch_scripts_refresh-xs-network-uuids \
        xenserver/usr_lib_xsconsole_plugins-base_XSFeatureVSwitch.py \
        xenserver/usr_sbin_brctl \
        xenserver/usr_sbin_xen-bugtool \
 +      xenserver/usr_share_vswitch_scripts_sysconfig.template \
 +      xenserver/usr_share_vswitch_scripts_dump-vif-details \
++      xenserver/usr_share_vswitch_scripts_refresh-xs-network-uuids \
        xenserver/vswitch-xen.spec
index 0000000,34fe1e7..34fe1e7
mode 000000,100755..100755
--- /dev/null
@@@ -1,0 -1,12 +1,12 @@@
+ #! /bin/sh
+ . /etc/xensource-inventory
+ for pif in $(xe pif-list --minimal host-uuid=${INSTALLATION_UUID} currently-attached=true VLAN=-1 | sed 's/,/ /g'); do
+     printf "Refreshing PIF %s...  " $pif
+     if /opt/xensource/libexec/interface-reconfigure --pif-uuid=$pif up; then
+         printf "done\n"
+     else
+         printf "error!\n"
+     fi
+ done
@@@ -14,6 -14,7 +14,6 @@@
  #    rpmbuild -D "vswitch_version 0.8.9~1+build123" -D "xen_version 2.6.18-128.1.1.el5.xs5.1.0.483.1000xen" -D "build_number --with-build-number=123" -bb /usr/src/redhat/SPECS/vswitch-xen.spec
  #
  %define version %{vswitch_version}-%{xen_version}
 -%define _prefix /root/vswitch
  
  Name: vswitch
  Summary: Virtual switch
@@@ -27,13 -28,6 +27,13 @@@ Release: 
  Source: openvswitch-%{vswitch_version}.tar.gz
  Buildroot: /tmp/vswitch-xen-rpm
  Requires: kernel-xen = %(echo '%{xen_version}' | sed 's/xen$//')
 +# The following Conflicts prevents the "vswitch" package generated by
 +# this spec file from installing at the same time as the "openvswitch"
 +# package shipped with XenServer 5.5.900.  In fact, the packages
 +# contain some files with identical names anyhow, so they will not
 +# coexist, but adding an explicit Conflicts makes this conflict more
 +# obvious.
 +Conflicts: openvswitch
  
  %description
  The vswitch provides standard network bridging functions augmented with
@@@ -44,12 -38,12 +44,12 @@@ traffic
  %setup -q -n openvswitch-%{vswitch_version}
  
  %build
 -./configure --prefix=%{_prefix} --localstatedir=%{_localstatedir} --with-l26=/lib/modules/%{xen_version}/build --enable-ssl %{build_number}
 +./configure --prefix=/usr --sysconfdir=/etc --localstatedir=%{_localstatedir} --with-l26=/lib/modules/%{xen_version}/build --enable-ssl %{build_number}
  make %{_smp_mflags}
  
  %install
  rm -rf $RPM_BUILD_ROOT
 -make install DESTDIR=$RPM_BUILD_ROOT prefix=%{_prefix}
 +make install DESTDIR=$RPM_BUILD_ROOT
  install -d -m 755 $RPM_BUILD_ROOT/etc
  install -d -m 755 $RPM_BUILD_ROOT/etc/init.d
  install -m 755 xenserver/etc_init.d_vswitch \
@@@ -66,45 -60,45 +66,47 @@@ install -m 755 xenserver/etc_profile.d_
  install -d -m 755 $RPM_BUILD_ROOT/etc/xapi.d/plugins
  install -m 755 xenserver/etc_xapi.d_plugins_vswitch-cfg-update \
           $RPM_BUILD_ROOT/etc/xapi.d/plugins/vswitch-cfg-update
 -install -d -m 755 $RPM_BUILD_ROOT%{_prefix}/scripts
 +install -d -m 755 $RPM_BUILD_ROOT/usr/share/vswitch/scripts
  install -m 755 xenserver/opt_xensource_libexec_interface-reconfigure \
 -             $RPM_BUILD_ROOT%{_prefix}/scripts/interface-reconfigure
 +             $RPM_BUILD_ROOT/usr/share/vswitch/scripts/interface-reconfigure
  install -m 755 xenserver/etc_xensource_scripts_vif \
 -             $RPM_BUILD_ROOT%{_prefix}/scripts/vif
 -install -m 755 xenserver/root_vswitch_scripts_dump-vif-details \
 -               $RPM_BUILD_ROOT%{_prefix}/scripts/dump-vif-details
 -install -m 755 xenserver/root_vswitch_scripts_refresh-xs-network-uuids \
 -               $RPM_BUILD_ROOT%{_prefix}/scripts/refresh-xs-network-uuids
 +             $RPM_BUILD_ROOT/usr/share/vswitch/scripts/vif
 +install -m 755 xenserver/usr_share_vswitch_scripts_dump-vif-details \
 +               $RPM_BUILD_ROOT/usr/share/vswitch/scripts/dump-vif-details
++install -m 755 xenserver/usr_share_vswitch_scripts_refresh-xs-network-uuids \
++               $RPM_BUILD_ROOT/usr/share/vswitch/scripts/refresh-xs-network-uuids
  install -m 755 xenserver/usr_sbin_xen-bugtool \
 -             $RPM_BUILD_ROOT%{_prefix}/scripts/xen-bugtool
 +             $RPM_BUILD_ROOT/usr/share/vswitch/scripts/xen-bugtool
  install -m 755 xenserver/usr_sbin_brctl \
 -             $RPM_BUILD_ROOT%{_prefix}/scripts/brctl
 -install -m 755 xenserver/root_vswitch_scripts_sysconfig.template \
 -         $RPM_BUILD_ROOT/root/vswitch/scripts/sysconfig.template
 +             $RPM_BUILD_ROOT/usr/share/vswitch/scripts/brctl
 +install -m 755 xenserver/usr_share_vswitch_scripts_sysconfig.template \
 +         $RPM_BUILD_ROOT/usr/share/vswitch/scripts/sysconfig.template
  install -m 644 \
          xenserver/usr_lib_xsconsole_plugins-base_XSFeatureVSwitch.py \
 -               $RPM_BUILD_ROOT%{_prefix}/scripts/XSFeatureVSwitch.py
 +               $RPM_BUILD_ROOT/usr/share/vswitch/scripts/XSFeatureVSwitch.py
  
 -install -d -m 755 $RPM_BUILD_ROOT%{_prefix}/kernel_modules
 -find datapath/linux-2.6 -name *.ko -exec install -m 755  \{\} $RPM_BUILD_ROOT%{_prefix}/kernel_modules/ \;
 +install -d -m 755 $RPM_BUILD_ROOT/lib/modules/%{xen_version}/kernel/net/vswitch
 +find datapath/linux-2.6 -name *.ko -exec install -m 755  \{\} $RPM_BUILD_ROOT/lib/modules/%{xen_version}/kernel/net/vswitch \;
  
  # Get rid of stuff we don't want to make RPM happy.
 -rm -rf \
 -    $RPM_BUILD_ROOT/root/vswitch/bin/ezio-term \
 -    $RPM_BUILD_ROOT/root/vswitch/bin/ovs-controller \
 -    $RPM_BUILD_ROOT/root/vswitch/bin/ovs-discover \
 -    $RPM_BUILD_ROOT/root/vswitch/bin/ovs-kill \
 -    $RPM_BUILD_ROOT/root/vswitch/bin/ovs-pki \
 -    $RPM_BUILD_ROOT/root/vswitch/bin/ovs-switchui \
 -    $RPM_BUILD_ROOT/root/vswitch/bin/ovs-wdt \
 -    $RPM_BUILD_ROOT/root/vswitch/bin/secchan \
 -    $RPM_BUILD_ROOT/root/vswitch/sbin/ovs-monitor \
 -    $RPM_BUILD_ROOT/root/vswitch/share/man/man8/ovs-controller.8 \
 -    $RPM_BUILD_ROOT/root/vswitch/share/man/man8/ovs-discover.8 \
 -    $RPM_BUILD_ROOT/root/vswitch/share/man/man8/ovs-kill.8 \
 -    $RPM_BUILD_ROOT/root/vswitch/share/man/man8/ovs-pki.8 \
 -    $RPM_BUILD_ROOT/root/vswitch/share/man/man8/secchan.8 \
 -    $RPM_BUILD_ROOT/root/vswitch/share/openvswitch
 +rm \
 +    $RPM_BUILD_ROOT/usr/bin/ovs-controller \
 +    $RPM_BUILD_ROOT/usr/bin/ovs-discover \
 +    $RPM_BUILD_ROOT/usr/bin/ovs-kill \
 +    $RPM_BUILD_ROOT/usr/bin/ovs-openflowd \
 +    $RPM_BUILD_ROOT/usr/bin/ovs-pki \
 +    $RPM_BUILD_ROOT/usr/bin/ovs-wdt \
 +    $RPM_BUILD_ROOT/usr/sbin/ovs-monitor \
 +    $RPM_BUILD_ROOT/usr/share/man/man8/ovs-controller.8 \
 +    $RPM_BUILD_ROOT/usr/share/man/man8/ovs-discover.8 \
 +    $RPM_BUILD_ROOT/usr/share/man/man8/ovs-kill.8 \
 +    $RPM_BUILD_ROOT/usr/share/man/man8/ovs-openflowd.8 \
 +    $RPM_BUILD_ROOT/usr/share/man/man8/ovs-pki.8
 +rm -f $RPM_BUILD_ROOT/lib/modules/%{xen_version}/kernel/net/vswitch/veth_mod.ko
 +rm -r \
 +    $RPM_BUILD_ROOT/usr/share/openvswitch/commands
 +
 +install -d -m 755 $RPM_BUILD_ROOT/var/lib/openvswitch
  
  %clean
  rm -rf $RPM_BUILD_ROOT
@@@ -116,31 -110,34 +118,31 @@@ if [ ! -f /etc/xensource-inventory ]; t
  fi
  
  if [ "$1" = "1" ]; then
 -    if ! md5sum -c --status <<EOF
 +    if md5sum -c --status <<EOF
 +ca141d60061dcfdade73e75abc6529b5  /usr/sbin/brctl
  b8e9835862ef1a9cec2a3f477d26c989  /etc/xensource/scripts/vif
  51970ad613a3996d5997e18e44db47da  /opt/xensource/libexec/interface-reconfigure
  5654c8c36699fcc8744ca9cd5b855414  /usr/sbin/xen-bugtool
  EOF
      then
 -        printf "\nThe original XenServer scripts replaced by this package\n"
 -        printf "are different than expected.  This could lead to unexpected\n"
 -        printf "behavior of your server.  Unless you are sure you know what\n"
 -        printf "you are doing, it is highly recommended that you remove this\n"
 -        printf "package immediately after the install completes, which\n"
 -        printf "will restore the XenServer scripts that you were previously\n"
 -        printf "using.\n\n"
 -    fi
 -    if test "`/usr/sbin/brctl --version`" != "bridge-utils, 1.1"; then
 +        printf "\nVerified host scripts from XenServer 5.5.0.\n\n"
 +    elif md5sum -c --status <<EOF
 +ca141d60061dcfdade73e75abc6529b5  /usr/sbin/brctl
 +b8e9835862ef1a9cec2a3f477d26c989  /etc/xensource/scripts/vif
 +ce451d3c985fd1db6497a363f0d9dedb  /opt/xensource/libexec/interface-reconfigure
 +2b53f500431fcba5276c896e9e4281b9  /usr/sbin/xen-bugtool
 +EOF
 +    then
 +        printf "\nVerified host scripts from XenServer 5.5.900.\n\n"
 +    else
  cat <<EOF
  
 -/usr/sbin/brctl replaced by this package reports the following version:
 -
 -`/usr/sbin/brctl --version`
 -
 -The expected version was:
 -
 -bridge-utils, 1.1
 -
 -Unless you are sure you know what you are doing, it is highly recommended that
 -you remove this package immediately after the install completes, which will
 -restore the original /usr/sbin/brctl.
 +The original XenServer scripts replaced by this package are not those
 +of any supported version of XenServer.  This could lead to unexpected
 +behavior of your server.  Unless you are sure you know what you are
 +doing, it is highly recommended that you remove this package
 +immediately after the install completes, which will restore the
 +XenServer scripts that you were previously using.
  
  EOF
      fi
@@@ -155,7 -152,8 +157,7 @@@ if test ! -e /var/lib/openvswitch/dbcac
          printf "Re-creating xapi database cache...  "
      fi
  
 -    mkdir -p /var/lib/openvswitch
 -    if /root/vswitch/scripts/interface-reconfigure rewrite; then
 +    if /usr/share/vswitch/scripts/interface-reconfigure rewrite; then
          printf "done.\n"
      else
          printf "FAILED\n"
      fi
  fi
  
 +# Ensure that modprobe will find our modules.
 +depmod %{xen_version}
 +
  if grep -F net.ipv4.conf.all.arp_filter /etc/sysctl.conf >/dev/null 2>&1; then :; else
      cat >>/etc/sysctl.conf <<EOF
  # This works around an issue in xhad, which binds to a particular
@@@ -189,7 -184,7 +191,7 @@@ touch /etc/ovs-vswitchd.con
  
  # Create default or update existing /etc/sysconfig/vswitch.
  SYSCONFIG=/etc/sysconfig/vswitch
 -TEMPLATE=/root/vswitch/scripts/sysconfig.template
 +TEMPLATE=/usr/share/vswitch/scripts/sysconfig.template
  if [ ! -e $SYSCONFIG ]; then
      cp $TEMPLATE $SYSCONFIG
  else
  fi
  
  # Replace XenServer files by our versions.
 -mkdir -p %{_prefix}/xs-original \
 +mkdir -p /usr/lib/vswitch/xs-original \
      || printf "Could not create script backup directory.\n"
  for f in \
      /opt/xensource/libexec/interface-reconfigure \
  do
      s=$(basename "$f")
      t=$(readlink "$f")
 -    if [ "$t" != "%{_prefix}/scripts/$s" ]; then
 -        mv "$f" %{_prefix}/xs-original/ \
 +    if [ "$t" != "/usr/share/vswitch/scripts/$s" ]; then
 +        mv "$f" /usr/lib/vswitch/xs-original/ \
              || printf "Could not save original XenServer $s script\n"
 -        ln -s "%{_prefix}/scripts/$s" "$f" \
 +        ln -s "/usr/share/vswitch/scripts/$s" "$f" \
              || printf "Could not link to vSwitch $s script\n"
      fi
  done
  
  # Install xsconsole plugin
  plugin=$(readlink /usr/lib/xsconsole/plugins-base/XSFeatureVSwitch.py)
 -if [ "$plugin" != "/root/vswitch/scripts/XSFeatureVSwitch.py" ]; then
 +if [ "$plugin" != "/usr/share/vswitch/scripts/XSFeatureVSwitch.py" ]; then
      rm -f /usr/lib/xsconsole/plugins-base/XSFeatureVSwitch.py
 -    ln -s /root/vswitch/scripts/XSFeatureVSwitch.py /usr/lib/xsconsole/plugins-base/ || printf "Could not link to vSswitch xsconsole plugin.\n"
 +    ln -s /usr/share/vswitch/scripts/XSFeatureVSwitch.py /usr/lib/xsconsole/plugins-base/ || printf "Could not link to vSswitch xsconsole plugin.\n"
  fi
  
  # Ensure all required services are set to run
@@@ -271,19 -266,23 +273,19 @@@ if [ "$1" = "0" ]; then     # $1 = 1 fo
          /usr/sbin/brctl
      do
          s=$(basename "$f")
 -        if [ ! -f "%{_prefix}/xs-original/$s" ]; then
 -            printf "Original XenServer $s script not present in %{_prefix}/xs-original\n"
 +        if [ ! -f "/usr/lib/vswitch/xs-original/$s" ]; then
 +            printf "Original XenServer $s script not present in /usr/lib/vswitch/xs-original\n"
              printf "Could not restore original XenServer script.\n"
          else
              (rm -f "$f" \
 -                && mv "%{_prefix}/xs-original/$s" "$f") \
 +                && mv "/usr/lib/vswitch/xs-original/$s" "$f") \
                  || printf "Could not restore original XenServer $s script.\n"
          fi
      done
  
 -    find  %{_prefix} -type d -depth -exec rmdir \{\} \; \
 -        || printf "Could not remove vSwitch install directory.\n"
 -
 -    # Remove all configuration and log files
 +    # Remove all configuration files
      rm -f /etc/ovs-vswitchd.conf
      rm -f /etc/sysconfig/vswitch
 -    rm -f /var/log/vswitch*
      rm -f /etc/ovs-vswitchd.cacert
      rm -f /var/lib/openvswitch/dbcache
  
  /etc/xapi.d/plugins/vswitch-cfg-update
  /etc/logrotate.d/vswitch
  /etc/profile.d/vswitch.sh
 -/root/vswitch/kernel_modules/brcompat_mod.ko
 -/root/vswitch/kernel_modules/openvswitch_mod.ko
 -/root/vswitch/kernel_modules/veth_mod.ko
 -/root/vswitch/scripts/dump-vif-details
 -/root/vswitch/scripts/refresh-xs-network-uuids
 -/root/vswitch/scripts/interface-reconfigure
 -/root/vswitch/scripts/vif
 -/root/vswitch/scripts/xen-bugtool
 -/root/vswitch/scripts/XSFeatureVSwitch.py
 -/root/vswitch/scripts/brctl
 -/root/vswitch/scripts/sysconfig.template
 +/lib/modules/%{xen_version}/kernel/net/vswitch/openvswitch_mod.ko
 +/lib/modules/%{xen_version}/kernel/net/vswitch/brcompat_mod.ko
 +/usr/share/vswitch/scripts/dump-vif-details
++/usr/share/vswitch/scripts/refresh-xs-network-uuids
 +/usr/share/vswitch/scripts/interface-reconfigure
 +/usr/share/vswitch/scripts/vif
 +/usr/share/vswitch/scripts/xen-bugtool
 +/usr/share/vswitch/scripts/XSFeatureVSwitch.py
 +/usr/share/vswitch/scripts/brctl
 +/usr/share/vswitch/scripts/sysconfig.template
  # Following two files are generated automatically by rpm.  We don't
  # really need them and they won't be used on the XenServer, but there
  # isn't an obvious place to get rid of them since they are generated
  # after the install script runs.  Since they are small, we just
  # include them.
 -/root/vswitch/scripts/XSFeatureVSwitch.pyc
 -/root/vswitch/scripts/XSFeatureVSwitch.pyo
 -/root/vswitch/sbin/ovs-brcompatd
 -/root/vswitch/sbin/ovs-vswitchd
 -/root/vswitch/bin/ovs-appctl
 -/root/vswitch/bin/ovs-cfg-mod
 -/root/vswitch/bin/ovs-dpctl
 -/root/vswitch/bin/ovs-ofctl
 -/root/vswitch/share/man/man5/ovs-vswitchd.conf.5
 -/root/vswitch/share/man/man8/ovs-appctl.8
 -/root/vswitch/share/man/man8/ovs-brcompatd.8
 -/root/vswitch/share/man/man8/ovs-cfg-mod.8
 -/root/vswitch/share/man/man8/ovs-dpctl.8
 -/root/vswitch/share/man/man8/ovs-ofctl.8
 -/root/vswitch/share/man/man8/ovs-vswitchd.8
 +/usr/share/vswitch/scripts/XSFeatureVSwitch.pyc
 +/usr/share/vswitch/scripts/XSFeatureVSwitch.pyo
 +/usr/sbin/ovs-brcompatd
 +/usr/sbin/ovs-vswitchd
 +/usr/bin/ovs-appctl
 +/usr/bin/ovs-cfg-mod
 +/usr/bin/ovs-dpctl
 +/usr/bin/ovs-ofctl
 +/usr/bin/ovs-vsctl
 +/usr/share/man/man5/ovs-vswitchd.conf.5.gz
 +/usr/share/man/man8/ovs-appctl.8.gz
 +/usr/share/man/man8/ovs-brcompatd.8.gz
 +/usr/share/man/man8/ovs-cfg-mod.8.gz
 +/usr/share/man/man8/ovs-dpctl.8.gz
 +/usr/share/man/man8/ovs-ofctl.8.gz
 +/usr/share/man/man8/ovs-vsctl.8.gz
 +/usr/share/man/man8/ovs-vswitchd.8.gz
 +/var/lib/openvswitch