Merge citrix into master.
authorBen Pfaff <blp@nicira.com>
Wed, 19 Aug 2009 20:03:46 +0000 (13:03 -0700)
committerBen Pfaff <blp@nicira.com>
Wed, 19 Aug 2009 20:03:46 +0000 (13:03 -0700)
This was a somewhat difficult merge since there was a fair amount of
superficially divergent development on the two branches, especially in the
datapath.

This has been build-tested against XenServer 5.5.0 and XenServer 5.7.0
build 15122.  It has been booted and connected to XenCenter on 5.5.0.

The merge revealed a couple of outstanding bugs, which will be fixed on
citrix and then merged back into master.

27 files changed:
1  2 
datapath/datapath.c
datapath/datapath.h
datapath/dp_dev.c
datapath/dp_sysfs.h
datapath/dp_sysfs_dp.c
datapath/dp_sysfs_if.c
datapath/linux-2.6/compat-2.6/include/linux/kobject.h
debian/openvswitch-switch.template
include/openvswitch/datapath-protocol.h
lib/dhcp-client.c
lib/rconn.c
ofproto/fail-open.c
ofproto/netflow.c
ofproto/ofproto.c
ofproto/ofproto.h
utilities/ovs-discover.8.in
utilities/ovs-discover.c
utilities/ovs-dpctl.8.in
utilities/ovs-dpctl.c
utilities/ovs-openflowd.8.in
utilities/ovs-openflowd.c
vswitchd/bridge.c
vswitchd/ovs-brcompatd.c
vswitchd/ovs-vswitchd.c
vswitchd/ovs-vswitchd.conf.5.in
xenserver/opt_xensource_libexec_interface-reconfigure
xenserver/vswitch-xen.spec

diff --combined datapath/datapath.c
  int (*dp_ioctl_hook)(struct net_device *dev, struct ifreq *rq, int cmd);
  EXPORT_SYMBOL(dp_ioctl_hook);
  
 -int (*dp_add_dp_hook)(struct datapath *dp);
 -EXPORT_SYMBOL(dp_add_dp_hook);
 -
 -int (*dp_del_dp_hook)(struct datapath *dp);
 -EXPORT_SYMBOL(dp_del_dp_hook);
 -
 -int (*dp_add_if_hook)(struct net_bridge_port *p);
 -EXPORT_SYMBOL(dp_add_if_hook);
 -
 -int (*dp_del_if_hook)(struct net_bridge_port *p);
 -EXPORT_SYMBOL(dp_del_if_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
@@@ -165,22 -177,13 +165,23 @@@ static void dp_ifinfo_notify(int event
                kfree_skb(skb);
                goto errout;
        }
-       err = rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, GFP_KERNEL);
+       rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, GFP_KERNEL);
+       return;
  errout:
        if (err < 0)
                rtnl_set_sk_err(net, RTNLGRP_LINK, err);
  }
  
 +static void release_dp(struct kobject *kobj)
 +{
 +      struct datapath *dp = container_of(kobj, struct datapath, ifobj);
 +      kfree(dp);
 +}
 +
 +struct kobj_type dp_ktype = {
 +      .release = release_dp
 +};
 +
  static int create_dp(int dp_idx, const char __user *devnamep)
  {
        struct net_device *dp_dev;
                skb_queue_head_init(&dp->queues[i]);
        init_waitqueue_head(&dp->waitqueue);
  
-       kobject_set_name(&dp->ifobj, SYSFS_BRIDGE_PORT_SUBDIR); /* "bridge" */
 +      /* Initialize kobject for bridge.  This will be added as
 +       * /sys/class/net/<devname>/bridge later, if sysfs is enabled. */
-       dp->ifobj.parent = NULL;
 +      dp->ifobj.kset = NULL;
 +      kobject_init(&dp->ifobj, &dp_ktype);
 +
        /* Allocate table. */
        err = -ENOMEM;
        rcu_assign_pointer(dp->table, dp_table_create(DP_L1_SIZE));
        mutex_unlock(&dp_mutex);
        rtnl_unlock();
  
- #ifdef SUPPORT_SYSFS
 -      if (dp_add_dp_hook)
 -              dp_add_dp_hook(dp);
 +      dp_sysfs_add_dp(dp);
- #endif
  
        return 0;
  
@@@ -286,9 -281,8 +283,7 @@@ static void do_destroy_dp(struct datapa
                if (p->port_no != ODPP_LOCAL)
                        dp_del_port(p);
  
- #ifdef SUPPORT_SYSFS
 -      if (dp_del_dp_hook)
 -              dp_del_dp_hook(dp);
 +      dp_sysfs_del_dp(dp);
- #endif
  
        rcu_assign_pointer(dps[dp->dp_idx], NULL);
  
        for (i = 0; i < DP_MAX_GROUPS; i++)
                kfree(dp->groups[i]);
        free_percpu(dp->stats_percpu);
 -      kfree(dp);
 +      kobject_put(&dp->ifobj);
        module_put(THIS_MODULE);
  }
  
@@@ -326,19 -320,6 +321,19 @@@ err_unlock
        return err;
  }
  
- #ifdef SUPPORT_SYSFS
 +static void release_nbp(struct kobject *kobj)
 +{
 +      struct net_bridge_port *p = container_of(kobj, struct net_bridge_port, kobj);
 +      kfree(p);
 +}
 +
 +struct kobj_type brport_ktype = {
++#ifdef CONFIG_SYSFS
 +      .sysfs_ops = &brport_sysfs_ops,
 +#endif
 +      .release = release_nbp
 +};
 +
  /* Called with RTNL lock and dp_mutex. */
  static int new_nbp(struct datapath *dp, struct net_device *dev, int port_no)
  {
        list_add_rcu(&p->node, &dp->port_list);
        dp->n_ports++;
  
-       kobject_set_name(&p->kobj, SYSFS_BRIDGE_PORT_ATTR); /* "brport" */
 +      /* Initialize kobject for bridge.  This will be added as
 +       * /sys/class/net/<devname>/brport later, if sysfs is enabled. */
-       p->kobj.parent = &p->dev->NETDEV_DEV_MEMBER.kobj;
 +      p->kobj.kset = NULL;
 +      kobject_init(&p->kobj, &brport_ktype);
 +
        dp_ifinfo_notify(RTM_NEWLINK, p);
  
        return 0;
@@@ -392,11 -366,6 +385,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 = -EXFULL;
+       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
 -      if (dp_add_if_hook)
 -              dp_add_if_hook(dp->ports[port_no]);
 +      dp_sysfs_add_if(dp->ports[port_no]);
- #endif
+       err = __put_user(port_no, &port.port);
  
  out_put:
        dev_put(dev);
@@@ -448,10 -421,13 +439,8 @@@ int dp_del_port(struct net_bridge_port 
  {
        ASSERT_RTNL();
  
- #ifdef SUPPORT_SYSFS
 -      if (p->port_no != ODPP_LOCAL && dp_del_if_hook) {
 -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
 -              sysfs_remove_link(&p->dp->ifobj, p->dev->name);
 -#else
 -              sysfs_remove_link(p->dp->ifobj, p->dev->name);
 -#endif
 -      }
 +      if (p->port_no != ODPP_LOCAL)
 +              dp_sysfs_del_if(p);
- #endif
        dp_ifinfo_notify(RTM_DELLINK, p);
  
        p->dp->n_ports--;
        /* Then wait until no one is still using it, and destroy it. */
        synchronize_rcu();
  
 -      if (is_dp_dev(p->dev)) {
 +      if (is_dp_dev(p->dev))
                dp_dev_destroy(p->dev);
 -      }
 -      if (p->port_no != ODPP_LOCAL && dp_del_if_hook) {
 -              dp_del_if_hook(p);
 -      } else {
 -              dev_put(p->dev);
 -              kfree(p);
 -      }
 +      dev_put(p->dev);
 +      kobject_put(&p->kobj);
  
        return 0;
  }
@@@ -587,7 -568,7 +576,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. */
@@@ -603,7 -584,7 +592,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;
  }
- #endif
+ #else
+ int vswitch_skb_checksum_setup(struct sk_buff *skb) { return 0; }
+ #endif /* CONFIG_XEN && linux == 2.6.18 */
  
  int
  dp_output_control(struct datapath *dp, struct sk_buff *skb, int queue_no,
         * 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);
+       err = vswitch_skb_checksum_setup(skb);
        if (err)
                goto err_kfree_skb;
  #ifndef CHECKSUM_HW
@@@ -836,6 -819,7 +827,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)
@@@ -964,8 -948,6 +956,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) ||
@@@ -991,9 -973,7 +981,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)
@@@ -1181,8 -1156,7 +1164,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;
        return copy_to_user(statsp, &stats, sizeof stats) ? -EFAULT : 0;
  }
  
 +/* MTU of the dp pseudo-device: ETH_DATA_LEN or the minimum of the ports */
 +int dp_min_mtu(const struct datapath *dp)
 +{
 +      struct net_bridge_port *p;
 +      int mtu = 0;
 +
 +      ASSERT_RTNL();
 +
 +      list_for_each_entry_rcu (p, &dp->port_list, node) {
 +              struct net_device *dev = p->dev;
 +
 +              /* Skip any internal ports, since that's what we're trying to
 +               * set. */
 +              if (is_dp_dev(dev))
 +                      continue;
 +
 +              if (!mtu || dev->mtu < mtu)
 +                      mtu = dev->mtu;
 +      }
 +
 +      return mtu ? mtu : ETH_DATA_LEN;
 +}
 +
  static int
  put_port(const struct net_bridge_port *p, struct odp_port __user *uop)
  {
@@@ -1297,7 -1248,7 +1279,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 */
@@@ -1381,24 -1332,28 +1363,28 @@@ static long openvswitch_ioctl(struct fi
        /* 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_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;
  }
  
diff --combined datapath/datapath.h
@@@ -18,8 -18,9 +18,9 @@@
  #include <linux/netdevice.h>
  #include <linux/workqueue.h>
  #include <linux/skbuff.h>
+ #include <linux/version.h>
  #include "flow.h"
 -#include "brc_sysfs.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. */
@@@ -64,7 -65,13 +65,7 @@@ struct datapath 
        struct mutex mutex;
        int dp_idx;
  
 -#ifdef CONFIG_SYSFS
 -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
        struct kobject ifobj;
 -#else
 -      struct kobject *ifobj;
 -#endif
 -#endif
  
        int drop_frags;
  
@@@ -92,13 -99,18 +93,13 @@@ struct net_bridge_port 
        u16 port_no;
        struct datapath *dp;
        struct net_device *dev;
 -#ifdef CONFIG_SYSFS
        struct kobject kobj;
 -#endif
 +      char linkname[IFNAMSIZ];
        struct list_head node;   /* Element in datapath.ports. */
  };
  
  extern struct notifier_block dp_device_notifier;
  extern int (*dp_ioctl_hook)(struct net_device *dev, struct ifreq *rq, int cmd);
 -extern int (*dp_add_dp_hook)(struct datapath *dp);
 -extern int (*dp_del_dp_hook)(struct datapath *dp);
 -extern int (*dp_add_if_hook)(struct net_bridge_port *p);
 -extern int (*dp_del_if_hook)(struct net_bridge_port *p);
  
  /* Flow table. */
  struct dp_table *dp_table_create(unsigned int n_buckets);
@@@ -115,7 -127,6 +116,7 @@@ int dp_table_foreach(struct dp_table *t
  void dp_process_received_packet(struct sk_buff *, struct net_bridge_port *);
  int dp_del_port(struct net_bridge_port *);
  int dp_output_control(struct datapath *, struct sk_buff *, int, u32 arg);
 +int dp_min_mtu(const struct datapath *dp);
  
  struct datapath *get_dp(int dp_idx);
  
@@@ -133,4 -144,6 +134,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
@@@ -28,6 -28,7 +28,6 @@@ struct datapath *dp_dev_get_dp(struct n
  {
        return dp_dev_priv(netdev)->dp;
  }
 -EXPORT_SYMBOL(dp_dev_get_dp);
  
  static struct net_device_stats *dp_dev_get_stats(struct net_device *netdev)
  {
@@@ -118,7 -119,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 = {
        .get_tso = ethtool_op_get_tso,
  };
  
 +static int dp_dev_change_mtu(struct net_device *dev, int new_mtu)
 +{
 +      if (new_mtu < 68 || new_mtu > dp_min_mtu(dp_dev_get_dp(dev)))
 +              return -EINVAL;
 +
 +      dev->mtu = new_mtu;
 +      return 0;
 +}
 +
  static int dp_dev_init(struct net_device *netdev)
  {
        struct dp_dev *dp_dev = dp_dev_priv(netdev);
@@@ -170,7 -162,6 +170,7 @@@ do_setup(struct net_device *netdev
        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;
        netdev->destructor = dp_dev_free;
  
@@@ -234,3 -225,4 +234,3 @@@ int is_dp_dev(struct net_device *netdev
  {
        return netdev->open == dp_dev_open;
  }
 -EXPORT_SYMBOL(is_dp_dev);
diff --combined datapath/dp_sysfs.h
index c0ac01b,0000000..be044ea
mode 100644,000000..100644
--- /dev/null
@@@ -1,37 -1,0 +1,28 @@@
- #include <linux/version.h>
- #if LINUX_VERSION_CODE == KERNEL_VERSION(2,6,18)
- #define SUPPORT_SYSFS 1
- #else
- /* We only support sysfs on Linux 2.6.18 because that's the only place we
-  * really need it (on Xen, for brcompat) and it's a big pain to try to support
-  * multiple versions. */
- #endif
- #ifdef SUPPORT_SYSFS
 +/*
 + * Copyright (c) 2009 Nicira Networks.
 + * Distributed under the terms of the GNU GPL version 2.
 + *
 + * Significant portions of this file may be copied from parts of the Linux
 + * kernel, by Linus Torvalds and others.
 + */
 +
 +#ifndef DP_SYSFS_H
 +#define DP_SYSFS_H 1
 +
 +struct datapath;
 +struct net_bridge_port;
 +
 +/* dp_sysfs_dp.c */
 +int dp_sysfs_add_dp(struct datapath *dp);
 +int dp_sysfs_del_dp(struct datapath *dp);
 +
 +/* dp_sysfs_if.c */
 +int dp_sysfs_add_if(struct net_bridge_port *p);
 +int dp_sysfs_del_if(struct net_bridge_port *p);
 +
++#ifdef CONFIG_SYSFS
 +extern struct sysfs_ops brport_sysfs_ops;
 +#endif
 +
 +#endif /* dp_sysfs.h */
 +
diff --combined datapath/dp_sysfs_dp.c
  #include <linux/times.h>
  #include <linux/version.h>
  
 -#include "brc_sysfs.h"
 +#include "dp_sysfs.h"
  #include "datapath.h"
  #include "dp_dev.h"
  
- #ifdef SUPPORT_SYSFS
+ #ifdef CONFIG_SYSFS
  #define to_dev(obj)   container_of(obj, struct device, kobj)
  
  /* Hack to attempt to build on more platforms. */
  #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)
- #define to_kobj(d) &(d)->class_dev.kobj
 -#define BRC_DEVICE_ATTR CLASS_DEVICE_ATTR
 +#define DP_DEVICE_ATTR CLASS_DEVICE_ATTR
+ #define DEVICE_PARAMS struct class_device *d
+ #define DEVICE_ARGS d
+ #define DEV_ATTR(NAME) class_device_attr_##NAME
  #else
- #define to_kobj(d) &(d)->dev.kobj
 -#define BRC_DEVICE_ATTR DEVICE_ATTR
 +#define DP_DEVICE_ATTR DEVICE_ATTR
+ #define DEVICE_PARAMS struct device *d, struct device_attribute *attr
+ #define DEVICE_ARGS d, attr
+ #define DEV_ATTR(NAME) dev_attr_##NAME
  #endif
  
  /*
   * Common code for storing bridge parameters.
   */
- static ssize_t store_bridge_parm(struct class_device *d,
+ static ssize_t store_bridge_parm(DEVICE_PARAMS,
                                 const char *buf, size_t len,
                                 void (*set)(struct datapath *, unsigned long))
  {
@@@ -76,8 -80,7 +80,7 @@@
  }
  
  
- static ssize_t show_forward_delay(struct class_device *d,
-                                 char *buf)
+ static ssize_t show_forward_delay(DEVICE_PARAMS, char *buf)
  {
  #if 0
        struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
@@@ -99,15 -102,15 +102,15 @@@ static void set_forward_delay(struct da
  #endif
  }
  
- static ssize_t store_forward_delay(struct class_device *d,
+ static ssize_t store_forward_delay(DEVICE_PARAMS,
                                   const char *buf, size_t len)
  {
-       return store_bridge_parm(d, buf, len, set_forward_delay);
+       return store_bridge_parm(DEVICE_ARGS, buf, len, set_forward_delay);
  }
 -static BRC_DEVICE_ATTR(forward_delay, S_IRUGO | S_IWUSR,
 +static DP_DEVICE_ATTR(forward_delay, S_IRUGO | S_IWUSR,
                   show_forward_delay, store_forward_delay);
  
- static ssize_t show_hello_time(struct class_device *d, char *buf)
+ static ssize_t show_hello_time(DEVICE_PARAMS, char *buf)
  {
  #if 0
        return sprintf(buf, "%lu\n",
@@@ -129,17 -132,16 +132,16 @@@ static void set_hello_time(struct datap
  #endif
  }
  
- static ssize_t store_hello_time(struct class_device *d,
+ static ssize_t store_hello_time(DEVICE_PARAMS,
                                const char *buf,
                                size_t len)
  {
-       return store_bridge_parm(d, buf, len, set_hello_time);
+       return store_bridge_parm(DEVICE_ARGS, buf, len, set_hello_time);
  }
 -static BRC_DEVICE_ATTR(hello_time, S_IRUGO | S_IWUSR, show_hello_time,
 +static DP_DEVICE_ATTR(hello_time, S_IRUGO | S_IWUSR, show_hello_time,
                   store_hello_time);
  
- static ssize_t show_max_age(struct class_device *d, 
-                           char *buf)
+ static ssize_t show_max_age(DEVICE_PARAMS, char *buf)
  {
  #if 0
        return sprintf(buf, "%lu\n",
@@@ -161,15 -163,14 +163,14 @@@ static void set_max_age(struct datapat
  #endif
  }
  
- static ssize_t store_max_age(struct class_device *d, 
+ static ssize_t store_max_age(DEVICE_PARAMS,
                             const char *buf, size_t len)
  {
-       return store_bridge_parm(d, buf, len, set_max_age);
+       return store_bridge_parm(DEVICE_ARGS, buf, len, set_max_age);
  }
 -static BRC_DEVICE_ATTR(max_age, S_IRUGO | S_IWUSR, show_max_age, store_max_age);
 +static DP_DEVICE_ATTR(max_age, S_IRUGO | S_IWUSR, show_max_age, store_max_age);
  
- static ssize_t show_ageing_time(struct class_device *d,
-                               char *buf)
+ static ssize_t show_ageing_time(DEVICE_PARAMS, char *buf)
  {
  #if 0
        struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
@@@ -188,16 -189,15 +189,15 @@@ static void set_ageing_time(struct data
  #endif
  }
  
- static ssize_t store_ageing_time(struct class_device *d,
+ static ssize_t store_ageing_time(DEVICE_PARAMS,
                                 const char *buf, size_t len)
  {
-       return store_bridge_parm(d, buf, len, set_ageing_time);
+       return store_bridge_parm(DEVICE_ARGS, buf, len, set_ageing_time);
  }
 -static BRC_DEVICE_ATTR(ageing_time, S_IRUGO | S_IWUSR, show_ageing_time,
 +static DP_DEVICE_ATTR(ageing_time, S_IRUGO | S_IWUSR, show_ageing_time,
                   store_ageing_time);
  
- static ssize_t show_stp_state(struct class_device *d,
-                             char *buf)
+ static ssize_t show_stp_state(DEVICE_PARAMS, char *buf)
  {
  #if 0
        struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
  }
  
  
- static ssize_t store_stp_state(struct class_device *d,
+ static ssize_t store_stp_state(DEVICE_PARAMS,
                               const char *buf,
                               size_t len)
  {
  
        return len;
  }
 -static BRC_DEVICE_ATTR(stp_state, S_IRUGO | S_IWUSR, show_stp_state,
 +static DP_DEVICE_ATTR(stp_state, S_IRUGO | S_IWUSR, show_stp_state,
                   store_stp_state);
  
- static ssize_t show_priority(struct class_device *d, 
-                            char *buf)
+ static ssize_t show_priority(DEVICE_PARAMS, char *buf)
  {
  #if 0
        struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
@@@ -257,15 -256,14 +256,14 @@@ static void set_priority(struct datapat
  #endif
  }
  
- static ssize_t store_priority(struct class_device *d, 
+ static ssize_t store_priority(DEVICE_PARAMS,
                               const char *buf, size_t len)
  {
-       return store_bridge_parm(d, buf, len, set_priority);
+       return store_bridge_parm(DEVICE_ARGS, buf, len, set_priority);
  }
 -static BRC_DEVICE_ATTR(priority, S_IRUGO | S_IWUSR, show_priority, store_priority);
 +static DP_DEVICE_ATTR(priority, S_IRUGO | S_IWUSR, show_priority, store_priority);
  
- static ssize_t show_root_id(struct class_device *d, 
-                           char *buf)
+ static ssize_t show_root_id(DEVICE_PARAMS, char *buf)
  {
  #if 0
        return br_show_bridge_id(buf, &to_bridge(d)->designated_root);
        return sprintf(buf, "0000.010203040506\n");
  #endif
  }
 -static BRC_DEVICE_ATTR(root_id, S_IRUGO, show_root_id, NULL);
 +static DP_DEVICE_ATTR(root_id, S_IRUGO, show_root_id, NULL);
  
- static ssize_t show_bridge_id(struct class_device *d, 
-                             char *buf)
+ static ssize_t show_bridge_id(DEVICE_PARAMS, char *buf)
  {
        struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
        const unsigned char *addr = dp->ports[ODPP_LOCAL]->dev->dev_addr;
        return sprintf(buf, "%.2x%.2x.%.2x%.2x%.2x%.2x%.2x%.2x\n",
                        0, 0, addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
  }
 -static BRC_DEVICE_ATTR(bridge_id, S_IRUGO, show_bridge_id, NULL);
 +static DP_DEVICE_ATTR(bridge_id, S_IRUGO, show_bridge_id, NULL);
  
- static ssize_t show_root_port(struct class_device *d, 
-                             char *buf)
+ static ssize_t show_root_port(DEVICE_PARAMS, char *buf)
  {
  #if 0
        return sprintf(buf, "%d\n", to_bridge(d)->root_port);
        return sprintf(buf, "%d\n", 0);
  #endif
  }
 -static BRC_DEVICE_ATTR(root_port, S_IRUGO, show_root_port, NULL);
 +static DP_DEVICE_ATTR(root_port, S_IRUGO, show_root_port, NULL);
  
- static ssize_t show_root_path_cost(struct class_device *d,
-                                  char *buf)
+ static ssize_t show_root_path_cost(DEVICE_PARAMS, char *buf)
  {
  #if 0
        return sprintf(buf, "%d\n", to_bridge(d)->root_path_cost);
        return sprintf(buf, "%d\n", 0);
  #endif
  }
 -static BRC_DEVICE_ATTR(root_path_cost, S_IRUGO, show_root_path_cost, NULL);
 +static DP_DEVICE_ATTR(root_path_cost, S_IRUGO, show_root_path_cost, NULL);
  
- static ssize_t show_topology_change(struct class_device *d,
-                                   char *buf)
+ static ssize_t show_topology_change(DEVICE_PARAMS, char *buf)
  {
  #if 0
        return sprintf(buf, "%d\n", to_bridge(d)->topology_change);
        return sprintf(buf, "%d\n", 0);
  #endif
  }
 -static BRC_DEVICE_ATTR(topology_change, S_IRUGO, show_topology_change, NULL);
 +static DP_DEVICE_ATTR(topology_change, S_IRUGO, show_topology_change, NULL);
  
- static ssize_t show_topology_change_detected(struct class_device *d,
-                                            char *buf)
+ static ssize_t show_topology_change_detected(DEVICE_PARAMS, char *buf)
  {
  #if 0
        struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
        return sprintf(buf, "%d\n", 0);
  #endif
  }
 -static BRC_DEVICE_ATTR(topology_change_detected, S_IRUGO,
 +static DP_DEVICE_ATTR(topology_change_detected, S_IRUGO,
                   show_topology_change_detected, NULL);
  
- static ssize_t show_hello_timer(struct class_device *d,
-                               char *buf)
+ static ssize_t show_hello_timer(DEVICE_PARAMS, char *buf)
  {
  #if 0
        struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
        return sprintf(buf, "%d\n", 0);
  #endif
  }
 -static BRC_DEVICE_ATTR(hello_timer, S_IRUGO, show_hello_timer, NULL);
 +static DP_DEVICE_ATTR(hello_timer, S_IRUGO, show_hello_timer, NULL);
  
- static ssize_t show_tcn_timer(struct class_device *d, 
-                             char *buf)
+ static ssize_t show_tcn_timer(DEVICE_PARAMS, char *buf)
  {
  #if 0
        struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
        return sprintf(buf, "%d\n", 0);
  #endif
  }
 -static BRC_DEVICE_ATTR(tcn_timer, S_IRUGO, show_tcn_timer, NULL);
 +static DP_DEVICE_ATTR(tcn_timer, S_IRUGO, show_tcn_timer, NULL);
  
- static ssize_t show_topology_change_timer(struct class_device *d,
-                                         char *buf)
+ static ssize_t show_topology_change_timer(DEVICE_PARAMS, char *buf)
  {
  #if 0
        struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
        return sprintf(buf, "%d\n", 0);
  #endif
  }
 -static BRC_DEVICE_ATTR(topology_change_timer, S_IRUGO, show_topology_change_timer,
 +static DP_DEVICE_ATTR(topology_change_timer, S_IRUGO, show_topology_change_timer,
                   NULL);
  
- static ssize_t show_gc_timer(struct class_device *d, 
-                            char *buf)
+ static ssize_t show_gc_timer(DEVICE_PARAMS, char *buf)
  {
  #if 0
        struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
        return sprintf(buf, "%d\n", 0);
  #endif
  }
 -static BRC_DEVICE_ATTR(gc_timer, S_IRUGO, show_gc_timer, NULL);
 +static DP_DEVICE_ATTR(gc_timer, S_IRUGO, show_gc_timer, NULL);
  
- static ssize_t show_group_addr(struct class_device *d,
-                              char *buf)
+ static ssize_t show_group_addr(DEVICE_PARAMS, char *buf)
  {
  #if 0
        struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
  #endif
  }
  
- static ssize_t store_group_addr(struct class_device *d,
+ static ssize_t store_group_addr(DEVICE_PARAMS,
                                const char *buf, size_t len)
  {
        struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
        return len;
  }
  
 -static BRC_DEVICE_ATTR(group_addr, S_IRUGO | S_IWUSR,
 +static DP_DEVICE_ATTR(group_addr, S_IRUGO | S_IWUSR,
                   show_group_addr, store_group_addr);
  
  static struct attribute *bridge_attrs[] = {
-       &class_device_attr_forward_delay.attr,
-       &class_device_attr_hello_time.attr,
-       &class_device_attr_max_age.attr,
-       &class_device_attr_ageing_time.attr,
-       &class_device_attr_stp_state.attr,
-       &class_device_attr_priority.attr,
-       &class_device_attr_bridge_id.attr,
-       &class_device_attr_root_id.attr,
-       &class_device_attr_root_path_cost.attr,
-       &class_device_attr_root_port.attr,
-       &class_device_attr_topology_change.attr,
-       &class_device_attr_topology_change_detected.attr,
-       &class_device_attr_hello_timer.attr,
-       &class_device_attr_tcn_timer.attr,
-       &class_device_attr_topology_change_timer.attr,
-       &class_device_attr_gc_timer.attr,
-       &class_device_attr_group_addr.attr,
+       &DEV_ATTR(forward_delay).attr,
+       &DEV_ATTR(hello_time).attr,
+       &DEV_ATTR(max_age).attr,
+       &DEV_ATTR(ageing_time).attr,
+       &DEV_ATTR(stp_state).attr,
+       &DEV_ATTR(priority).attr,
+       &DEV_ATTR(bridge_id).attr,
+       &DEV_ATTR(root_id).attr,
+       &DEV_ATTR(root_path_cost).attr,
+       &DEV_ATTR(root_port).attr,
+       &DEV_ATTR(topology_change).attr,
+       &DEV_ATTR(topology_change_detected).attr,
+       &DEV_ATTR(hello_timer).attr,
+       &DEV_ATTR(tcn_timer).attr,
+       &DEV_ATTR(topology_change_timer).attr,
+       &DEV_ATTR(gc_timer).attr,
+       &DEV_ATTR(group_addr).attr,
        NULL
  };
  
@@@ -474,9 -462,9 +462,9 @@@ static struct attribute_group bridge_gr
   *   to hold links.  The ifobj exists in the same data structure
   *   as its parent the bridge so reference counting works.
   */
 -int brc_sysfs_add_dp(struct datapath *dp)
 +int dp_sysfs_add_dp(struct datapath *dp)
  {
-       struct kobject *kobj = to_kobj(dp->ports[ODPP_LOCAL]->dev);
+       struct kobject *kobj = &dp->ports[ODPP_LOCAL]->dev->NETDEV_DEV_MEMBER.kobj;
        int err;
  
        err = sysfs_create_group(kobj, &bridge_group);
                goto out1;
        }
  
-       /* Create /sys/class/net/<devname>/bridge directory. */
 -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
 -      kobject_set_name(&dp->ifobj, SYSFS_BRIDGE_PORT_SUBDIR);
 -      dp->ifobj.ktype = NULL;
 -      dp->ifobj.kset = NULL;
--      dp->ifobj.parent = kobj;
-       err = kobject_add(&dp->ifobj);
 -
 -      err = kobject_register(&dp->ifobj);
++      /* Create /sys/class/net/<devname>/brif directory. */
++      err = kobject_add(&dp->ifobj, kobj, SYSFS_BRIDGE_PORT_SUBDIR);
        if (err) {
                pr_info("%s: can't add kobject (directory) %s/%s\n",
-                               __FUNCTION__, dp_name(dp), dp->ifobj.name);
+                       __FUNCTION__, dp_name(dp), kobject_name(&dp->ifobj));
                goto out2;
        }
 -#else
 -      dp->ifobj = kobject_create_and_add(SYSFS_BRIDGE_PORT_SUBDIR, kobj);
 -      if (!dp->ifobj) {
 -              pr_info("%s: can't add kobject (directory) %s/%s\n",
 -                      __func__, dp_name(dp), SYSFS_BRIDGE_PORT_SUBDIR);
 -              goto out2;
 -      }
 -#endif
 +      kobject_uevent(&dp->ifobj, KOBJ_ADD);
        return 0;
  
   out2:
        return err;
  }
  
 -int brc_sysfs_del_dp(struct datapath *dp)
 +int dp_sysfs_del_dp(struct datapath *dp)
  {
-       struct kobject *kobj = to_kobj(dp->ports[ODPP_LOCAL]->dev);
+       struct kobject *kobj = &dp->ports[ODPP_LOCAL]->dev->NETDEV_DEV_MEMBER.kobj;
  
 -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
 -      kobject_unregister(&dp->ifobj);
 -#else 
 -      kobject_put(dp->ifobj);
 -#endif
 +      kobject_del(&dp->ifobj);
        sysfs_remove_group(kobj, &bridge_group);
  
        return 0;
  }
- #else /* !SUPPORT_SYSFS */
+ #else /* !CONFIG_SYSFS */
 -int brc_sysfs_add_dp(struct datapath *dp) { return 0; }
 -int brc_sysfs_del_dp(struct datapath *dp) { return 0; }
 -int brc_sysfs_add_if(struct net_bridge_port *p) { return 0; }
 -int brc_sysfs_del_if(struct net_bridge_port *p)
 -{
 -      dev_put(p->dev);
 -      kfree(p);
 -      return 0;
 -}
 +int dp_sysfs_add_dp(struct datapath *dp) { return 0; }
 +int dp_sysfs_del_dp(struct datapath *dp) { return 0; }
 +int dp_sysfs_add_if(struct net_bridge_port *p) { return 0; }
- int dp_sysfs_del_if(struct net_bridge_port *p)
- {
-       dev_put(p->dev);
-       kfree(p);
-       return 0;
- }
- #endif /* !SUPPORT_SYSFS */
++int dp_sysfs_del_if(struct net_bridge_port *p) { return 0; }
+ #endif /* !CONFIG_SYSFS */
diff --combined datapath/dp_sysfs_if.c
  #include <linux/if_bridge.h>
  #include <linux/rtnetlink.h>
  #include <linux/spinlock.h>
 -#include "brc_sysfs.h"
 +#include "dp_sysfs.h"
  #include "datapath.h"
  
- #ifdef SUPPORT_SYSFS
+ #ifdef CONFIG_SYSFS
  
  struct brport_attribute {
        struct attribute        attr;
@@@ -266,68 -266,77 +266,64 @@@ struct sysfs_ops brport_sysfs_ops = 
        .store = brport_store,
  };
  
 -static void release_nbp(struct kobject *kobj)
 -{
 -      struct net_bridge_port *p
 -              = container_of(kobj, struct net_bridge_port, kobj);
 -      kfree(p);
 -}
 -
 -struct kobj_type brport_ktype = {
 -      .sysfs_ops = &brport_sysfs_ops,
 -      .release = release_nbp
 -};
 -
  /*
   * Add sysfs entries to ethernet device added to a bridge.
   * Creates a brport subdirectory with bridge attributes.
   * Puts symlink in bridge's brport subdirectory
   */
 -int brc_sysfs_add_if(struct net_bridge_port *p)
 +int dp_sysfs_add_if(struct net_bridge_port *p)
  {
        struct datapath *dp = p->dp;
        struct brport_attribute **a;
        int err;
  
 -      err = kobject_init_and_add(&p->kobj, &brport_ktype,
 -                                 &(p->dev->NETDEV_DEV_MEMBER.kobj),
 -                                 SYSFS_BRIDGE_PORT_ATTR);
 +      /* Create /sys/class/net/<devname>/brport directory. */
-       err = kobject_add(&p->kobj);
++      err = kobject_add(&p->kobj, &p->dev->NETDEV_DEV_MEMBER.kobj,
++                        SYSFS_BRIDGE_PORT_ATTR);
        if (err)
-               goto err_put;
+               goto err;
  
 +      /* Create symlink from /sys/class/net/<devname>/brport/bridge to
 +       * /sys/class/net/<bridgename>. */
        err = sysfs_create_link(&p->kobj,
                                &dp->ports[ODPP_LOCAL]->dev->NETDEV_DEV_MEMBER.kobj,
 -                              SYSFS_BRIDGE_PORT_LINK);
 +                              SYSFS_BRIDGE_PORT_LINK); /* "bridge" */
        if (err)
                goto err_del;
  
 +      /* Populate /sys/class/net/<devname>/brport directory with files. */
        for (a = brport_attrs; *a; ++a) {
                err = sysfs_create_file(&p->kobj, &((*a)->attr));
                if (err)
                        goto err_del;
        }
  
 -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
 +      /* Create symlink from /sys/class/net/<bridgename>/brif/<devname> to
 +       * /sys/class/net/<devname>/brport.  */
        err = sysfs_create_link(&dp->ifobj, &p->kobj, p->dev->name);
 -#else
 -      err = sysfs_create_link(dp->ifobj, &p->kobj, p->dev->name);
 -#endif
        if (err)
                goto err_del;
 +      strcpy(p->linkname, p->dev->name);
  
        kobject_uevent(&p->kobj, KOBJ_ADD);
  
--      return err;
++      return 0;
  
  err_del:
        kobject_del(&p->kobj);
- err_put:
--      kobject_put(&p->kobj);
-       /* Ensure that dp_sysfs_del_if becomes a no-op. */
-       p->kobj.dentry = NULL;
+ err:
++      p->linkname[0] = 0;
        return err;
  }
  
 -int brc_sysfs_del_if(struct net_bridge_port *p)
 +int dp_sysfs_del_if(struct net_bridge_port *p)
  {
 -      struct net_device *dev = p->dev;
 -
 -      kobject_uevent(&p->kobj, KOBJ_REMOVE);
 -      kobject_del(&p->kobj);
 -
 -      dev_put(dev);
 -
 -      kobject_put(&p->kobj);
 -
 +      if (p->linkname[0]) {
 +              sysfs_remove_link(&p->dp->ifobj, p->linkname);
-               p->linkname[0] = '\0';
-       }
-       if (p->kobj.dentry) {
 +              kobject_uevent(&p->kobj, KOBJ_REMOVE);
 +              kobject_del(&p->kobj);
++              p->linkname[0] = '\0';
 +      }
        return 0;
  }
- #endif /* SUPPORT_SYSFS */
+ #endif /* CONFIG_SYSFS */
@@@ -4,13 -4,20 +4,27 @@@
  #include_next <linux/kobject.h>
  
  #include <linux/version.h>
++
  #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
 -static inline int kobject_init_and_add(struct kobject *kobj,
 -                                      struct kobj_type *ktype,
 -                                      struct kobject *parent,
 -                                      const char *name)
 +#define kobject_init(kobj, ktype) rpl_kobject_init(kobj, ktype)
 +static inline void rpl_kobject_init(struct kobject *kobj, struct kobj_type *ktype)
  {
 -      kobject_init(kobj);
 -      kobject_set_name(kobj, "%s", name);
        kobj->ktype = ktype;
 -      kobj->kset = NULL;
 -      kobj->parent = parent;
 +      (kobject_init)(kobj);
 +}
 -      return kobject_add(kobj);
++#define kobject_add(kobj, parent, name) rpl_kobject_add(kobj, parent, name)
++static inline int rpl_kobject_add(struct kobject *kobj,
++                                struct kobject *parent,
++                                const char *name)
++{
++      int err = kobject_set_name(kobj, "%s", name);
++      if (err)
++              return err;
++      kobj->parent = parent;
++      return (kobject_add)(kobj);
+ }
  #endif
  
++
  #endif /* linux/kobject.h wrapper */
@@@ -1,7 -1,7 +1,7 @@@
  # This is a POSIX shell fragment                -*- sh -*-
  
- # To configure the secure channel, fill in the following properly and
- # uncomment them.  Afterward, the secure channel will come up
+ # To configure the OpenFlow switch, fill in the following properly and
+ # uncomment them.  Afterward, the switch will come up
  # automatically at boot time.  It can be started immediately with
  #       /etc/init.d/openvswitch-switch start
  # Alternatively, use the ovs-switch-setup program (from the
@@@ -101,12 -101,12 +101,12 @@@ SWITCH_IP=dhc
  # Set CACERT_MODE to 'secure' or 'bootstrap' for these respective cases.
  #CACERT_MODE=secure
  
- # MGMT_VCONNS: List of vconns (space-separated) on which secchan
+ # MGMT_VCONNS: List of vconns (space-separated) on which ovs-openflowd
  # should listen for management connections from ovs-ofctl, etc.
  # openvswitch-switchui by default connects to
- # unix:/var/run/secchan.mgmt, so do not disable this if you want to
+ # unix:/var/run/ovs-openflowd.mgmt, so do not disable this if you want to
  # use openvswitch-switchui.
- MGMT_VCONNS="punix:/var/run/secchan.mgmt"
+ MGMT_VCONNS="punix:/var/run/ovs-openflowd.mgmt"
  
  # COMMANDS: Access control list for the commands that can be executed
  # remotely over the OpenFlow protocol, as a comma-separated list of
  #DISCONNECTED_MODE=switch
  
  # STP: Enable or disabled 802.1D-1998 Spanning Tree Protocol.  Set to
- # 'yes' to enable STP, 'no' to disable it.  If unset, secchan's
+ # 'yes' to enable STP, 'no' to disable it.  If unset, ovs-openflowd's
  # current default is 'no' (but this may change in the future).
  #STP=no
  
  #RATE_LIMIT=1000
  
  # INACTIVITY_PROBE: The maximum number of seconds of inactivity on the
- # controller connection before secchan sends an inactivity probe
+ # controller connection before ovs-openflowd sends an inactivity probe
  # message to the controller.  The valid range is 5 and up.  If unset,
- # secchan defaults to 5 seconds.
 -# ovs-openflowd defaults to 15 seconds.
++# ovs-openflowd defaults to 5 seconds.
  #INACTIVITY_PROBE=5
  
- # MAX_BACKOFF: The maximum time that secchan will wait between
+ # MAX_BACKOFF: The maximum time that ovs-openflowd will wait between
  # attempts to connect to the controller.  The valid range is 1 and up.
- # If unset, secchan defaults to 8 seconds.
 -# If unset, ovs-openflowd defaults to 15 seconds.
 -#MAX_BACKOFF=15
++# If unset, ovs-openflowd defaults to 8 seconds.
 +#MAX_BACKOFF=8
  
- # DAEMON_OPTS: Additional options to pass to secchan, e.g. "--fail=open"
+ # DAEMON_OPTS: Additional options to pass to ovs-openflowd, e.g. "--fail=open"
  DAEMON_OPTS=""
  
  # CORE_LIMIT: Maximum size for core dumps.
@@@ -37,7 -37,7 +37,7 @@@
   * ----------------------------------------------------------------------
   */
  
- /* Protocol between secchan and datapath. */
+ /* Protocol between userspace and kernel datapath. */
  
  #ifndef OPENVSWITCH_DATAPATH_PROTOCOL_H
  #define OPENVSWITCH_DATAPATH_PROTOCOL_H 1
  #define ODP_PORT_GROUP_GET      _IOWR('O', 12, struct odp_port_group)
  
  #define ODP_FLOW_GET            _IOWR('O', 13, struct odp_flow)
- #define ODP_FLOW_GET_MULTIPLE   _IOWR('O', 14, struct odp_flowvec)
+ #define ODP_FLOW_PUT            _IOWR('O', 14, struct odp_flow)
  #define ODP_FLOW_LIST           _IOWR('O', 15, struct odp_flowvec)
  #define ODP_FLOW_FLUSH          _IO('O', 16)
- #define ODP_FLOW_PUT            _IOWR('O', 17, struct odp_flow)
- #define ODP_FLOW_DEL            _IOWR('O', 18, struct odp_flow)
+ #define ODP_FLOW_DEL            _IOWR('O', 17, struct odp_flow)
  
- #define ODP_EXECUTE             _IOR('O', 19, struct odp_execute)
+ #define ODP_EXECUTE             _IOR('O', 18, struct odp_execute)
  
  struct odp_stats {
      /* Flows. */
@@@ -149,21 -147,21 +147,21 @@@ struct odp_flow_stats 
      __u32 used_nsec;
      __u8 tcp_flags;
      __u8 ip_tos;
-     __u16 reserved;
+     __u16 error;                /* Used by ODP_FLOW_GET. */
  };
  
  struct odp_flow_key {
 -      __be32 nw_src;               /* IP source address. */
 -      __be32 nw_dst;               /* IP destination address. */
 -      __u16  in_port;              /* Input switch port. */
 -      __be16 dl_vlan;              /* Input VLAN. */
 -      __be16 dl_type;              /* Ethernet frame type. */
 -      __be16 tp_src;               /* TCP/UDP source port. */
 -      __be16 tp_dst;               /* TCP/UDP destination port. */
 -      __u8   dl_src[ETH_ALEN];     /* Ethernet source address. */
 -      __u8   dl_dst[ETH_ALEN];     /* Ethernet destination address. */
 -      __u8   nw_proto;             /* IP protocol. */
 -      __u8   reserved;             /* Pad to 64 bits. */
 +    __be32 nw_src;               /* IP source address. */
 +    __be32 nw_dst;               /* IP destination address. */
 +    __u16  in_port;              /* Input switch port. */
 +    __be16 dl_vlan;              /* Input VLAN. */
 +    __be16 dl_type;              /* Ethernet frame type. */
 +    __be16 tp_src;               /* TCP/UDP source port. */
 +    __be16 tp_dst;               /* TCP/UDP destination port. */
 +    __u8   dl_src[ETH_ALEN];     /* Ethernet source address. */
 +    __u8   dl_dst[ETH_ALEN];     /* Ethernet destination address. */
 +    __u8   nw_proto;             /* IP protocol. */
 +    __u8   reserved;             /* Pad to 64 bits. */
  };
  
  struct odp_flow {
diff --combined lib/dhcp-client.c
@@@ -411,7 -411,7 +411,7 @@@ dhclient_configure_netdev(struct dhclie
      }
  
      if (!error && router.s_addr) {
-         error = netdev_add_router(router);
+         error = netdev_add_router(cli->netdev, router);
          if (error) {
              VLOG_ERR("failed to add default route to "IP_FMT" on %s: %s",
                       IP_ARGS(&router), netdev_get_name(cli->netdev),
@@@ -882,7 -882,7 +882,7 @@@ dhclient_msg_init(struct dhclient *cli
      msg->xid = cli->xid;
      msg->secs = cli->secs;
      msg->type = type;
-     memcpy(msg->chaddr, netdev_get_etheraddr(cli->netdev), ETH_ADDR_LEN);
+     netdev_get_etheraddr(cli->netdev, msg->chaddr);
  }
  
  /* If time goes backward this returns a large number, which makes it look like
@@@ -905,9 -905,13 +905,13 @@@ timeout(struct dhclient *cli, unsigned 
  static bool
  do_receive_msg(struct dhclient *cli, struct dhcp_msg *msg)
  {
+     uint8_t cli_mac[ETH_ADDR_LEN];
      struct ofpbuf b;
+     int mtu;
  
-     ofpbuf_init(&b, netdev_get_mtu(cli->netdev) + VLAN_ETH_HEADER_LEN);
+     netdev_get_mtu(cli->netdev, &mtu);
+     ofpbuf_init(&b, mtu + VLAN_ETH_HEADER_LEN);
+     netdev_get_etheraddr(cli->netdev, cli_mac);
      for (; cli->received < 50; cli->received++) {
          const struct ip_header *ip;
          const struct dhcp_header *dhcp;
          flow_extract(&b, 0, &flow);
          if (flow.dl_type != htons(ETH_TYPE_IP)
              || flow.nw_proto != IP_TYPE_UDP
 -            || flow.tp_dst != htons(68)
 +            || flow.tp_dst != htons(DHCP_CLIENT_PORT)
              || !(eth_addr_is_broadcast(flow.dl_dst)
-                  || eth_addr_equals(flow.dl_dst,
-                                     netdev_get_etheraddr(cli->netdev)))) {
+                  || eth_addr_equals(flow.dl_dst, cli_mac))) {
              continue;
          }
  
@@@ -977,7 -980,7 +980,7 @@@ do_send_msg(struct dhclient *cli, cons
  
      dhcp_assemble(msg, &b);
  
-     memcpy(eh.eth_src, netdev_get_etheraddr(cli->netdev), ETH_ADDR_LEN);
+     netdev_get_etheraddr(cli->netdev, eh.eth_src);
      memcpy(eh.eth_dst, eth_addr_broadcast, ETH_ADDR_LEN);
      eh.eth_type = htons(ETH_TYPE_IP);
  
      nh.ip_dst = INADDR_BROADCAST;
      nh.ip_csum = csum(&nh, sizeof nh);
  
 -    th.udp_src = htons(66);
 -    th.udp_dst = htons(67);
 +    th.udp_src = htons(DHCP_CLIENT_PORT);
 +    th.udp_dst = htons(DHCP_SERVER_PORT);
      th.udp_len = htons(UDP_HEADER_LEN + b.size);
      th.udp_csum = 0;
      udp_csum = csum_add32(0, nh.ip_src);
diff --combined lib/rconn.c
@@@ -89,7 -89,7 +89,7 @@@ struct rconn 
      time_t last_admitted;
  
      /* These values are simply for statistics reporting, not used directly by
-      * anything internal to the rconn (or the secchan for that matter). */
+      * anything internal to the rconn (or ofproto for that matter). */
      unsigned int packets_received;
      unsigned int n_attempted_connections, n_successful_connections;
      time_t creation_time;
@@@ -159,7 -159,7 +159,7 @@@ rconn_new_from_vconn(const char *name, 
   * 'max_backoff' is the maximum number of seconds between attempts to connect
   * to the peer.  The actual interval starts at 1 second and doubles on each
   * failure until it reaches 'max_backoff'.  If 0 is specified, the default of
 - * 60 seconds is used. */
 + * 8 seconds is used. */
  struct rconn *
  rconn_create(int probe_interval, int max_backoff)
  {
      queue_init(&rc->txq);
  
      rc->backoff = 0;
 -    rc->max_backoff = max_backoff ? max_backoff : 60;
 +    rc->max_backoff = max_backoff ? max_backoff : 8;
      rc->backoff_deadline = TIME_MIN;
      rc->last_received = time_now();
      rc->last_connected = time_now();
diff --combined ofproto/fail-open.c
@@@ -53,9 -53,8 +53,9 @@@ fail_open_run(struct fail_open *fo
              memset(&flow, 0, sizeof flow);
              ofproto_delete_flow(fo->ofproto, &flow, OFPFW_ALL, 70000);
          } else {
 -            VLOG_WARN("Could not connect to controller for %d seconds, "
 -                      "failing open", disconn_secs);
 +            VLOG_WARN("Could not connect to controller (or switch failed "
 +                      "controller's post-connection admission control "
 +                      "policy) for %d seconds, failing open", disconn_secs);
              fo->last_disconn_secs = disconn_secs;
  
              /* Flush all OpenFlow and datapath flows.  We will set up our
diff --combined ofproto/netflow.c
@@@ -107,7 -107,7 +107,7 @@@ static struct vlog_rate_limit rl = VLOG
  static int
  open_collector(char *dst)
  {
 -    char *save_ptr;
 +    char *save_ptr = NULL;
      const char *host_name;
      const char *port_string;
      struct sockaddr_in sin;
diff --combined ofproto/ofproto.c
@@@ -26,7 -26,6 +26,7 @@@
  #include "coverage.h"
  #include "discovery.h"
  #include "dpif.h"
 +#include "dynamic-string.h"
  #include "executer.h"
  #include "fail-open.h"
  #include "in-band.h"
@@@ -52,7 -51,6 +52,7 @@@
  #include "svec.h"
  #include "tag.h"
  #include "timeval.h"
 +#include "unixctl.h"
  #include "vconn.h"
  #include "vconn-ssl.h"
  #include "xtoxll.h"
@@@ -135,7 -133,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 -192,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 -235,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,10 -259,9 +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;
  
      /* Initialize OpenFlow connections. */
      list_init(&p->all_conns);
 -    p->controller = ofconn_create(p, rconn_create(15, 15));
 +    p->controller = ofconn_create(p, rconn_create(5, 8));
      p->controller->pktbuf = pktbuf_create();
      p->controller->miss_send_len = OFP_DEFAULT_MISS_SEND_LEN;
      p->listeners = NULL;
          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;
  }
@@@ -373,9 -364,7 +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);
@@@ -457,7 -446,7 +448,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;
@@@ -714,8 -703,8 +705,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);
      }
@@@ -757,6 -746,17 +748,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) {
@@@ -904,8 -903,9 +905,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);
      }
@@@ -975,7 -975,7 +977,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;
  }
@@@ -1027,7 -1027,7 +1029,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);
      }
@@@ -1050,7 -1050,7 +1052,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);
      }
@@@ -1080,7 -1080,7 +1082,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);
  }
  
@@@ -1112,7 -1112,7 +1114,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';
@@@ -1187,6 -1187,7 +1189,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));
@@@ -1213,71 -1215,53 +1217,71 @@@ static voi
  update_port(struct ofproto *p, const char *devname)
  {
      struct odp_port odp_port;
 -    struct ofport *ofport;
 +    struct ofport *old_ofport;
 +    struct ofport *new_ofport;
      int error;
  
      COVERAGE_INC(ofproto_update_port);
 -    ofport = shash_find_data(&p->port_by_name, devname);
 +
 +    /* 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);
 -    if (!error) {
 -        if (!ofport) {
 -            /* New port. */
 -            if (!ofport_conflicts(p, &odp_port)) {
 -                ofport = make_ofport(&odp_port);
 -                if (ofport) {
 -                    ofport_install(p, ofport);
 -                    send_port_status(p, ofport, OFPPR_ADD);
 -                }
 -            }
 -        } else {
 -            /* Modified port. */
 -            struct ofport *new_ofport = make_ofport(&odp_port);
 -            if (!new_ofport) {
 -                return;
 -            }
  
 -            new_ofport->opp.config &= OFPPC_PORT_DOWN;
 -            new_ofport->opp.config |= ofport->opp.config & ~OFPPC_PORT_DOWN;
 -            if (ofport_equal(ofport, new_ofport)) {
 -                /* False alarm--no change. */
 -                ofport_free(new_ofport);
 -            } else {
 -                ofport_remove(p, ofport);
 -                ofport_install(p, new_ofport);
 -                ofport_free(ofport);
 -                send_port_status(p, new_ofport, OFPPR_MODIFY);
 -            }
 -        }
 -    } else if (error == ENOENT || error == ENODEV) {
 -        /* Deleted port. */
 -        if (ofport) {
 -            send_port_status(p, ofport, OFPPR_DELETE);
 -            ofport_remove(p, ofport);
 -            ofport_free(ofport);
 +    /* Find the old ofport. */
 +    old_ofport = shash_find_data(&p->port_by_name, devname);
 +    if (!error) {
 +        if (!old_ofport) {
 +            /* There's no port named 'devname' but there might be a port with
 +             * the same port number.  This could happen if a port is deleted
 +             * and then a new one added in its place very quickly, or if a port
 +             * is renamed.  In the former case we want to send an OFPPR_DELETE
 +             * and an OFPPR_ADD, and in the latter case we want to send a
 +             * single OFPPR_MODIFY.  We can distinguish the cases by comparing
 +             * the old port's ifindex against the new port, or perhaps less
 +             * reliably but more portably by comparing the old port's MAC
 +             * against the new port's MAC.  However, this code isn't that smart
 +             * and always sends an OFPPR_MODIFY (XXX). */
 +            old_ofport = port_array_get(&p->ports, odp_port.port);
          }
 -    } else {
 +    } else if (error != ENOENT && error != ENODEV) {
          VLOG_WARN_RL(&rl, "dpif_port_query_by_name returned unexpected error "
                       "%s", strerror(error));
          return;
      }
 +
 +    /* Create a new ofport. */
 +    new_ofport = !error ? make_ofport(&odp_port) : NULL;
 +
 +    /* Eliminate a few pathological cases. */
 +    if (!old_ofport && !new_ofport) {
 +        return;
 +    } else if (old_ofport && new_ofport) {
 +        /* Most of the 'config' bits are OpenFlow soft state, but
 +         * OFPPC_PORT_DOWN is maintained the kernel.  So transfer the OpenFlow
 +         * bits from old_ofport.  (make_ofport() only sets OFPPC_PORT_DOWN and
 +         * leaves the other bits 0.)  */
 +        new_ofport->opp.config |= old_ofport->opp.config & ~OFPPC_PORT_DOWN;
 +
 +        if (ofport_equal(old_ofport, new_ofport)) {
 +            /* False alarm--no change. */
 +            ofport_free(new_ofport);
 +            return;
 +        }
 +    }
 +
 +    /* Now deal with the normal cases. */
 +    if (old_ofport) {
 +        ofport_remove(p, old_ofport);
 +    }
 +    if (new_ofport) {
 +        ofport_install(p, new_ofport);
 +    }
 +    send_port_status(p, new_ofport ? new_ofport : old_ofport,
 +                     (!old_ofport ? OFPPR_ADD
 +                      : !new_ofport ? OFPPR_DELETE
 +                      : OFPPR_MODIFY));
 +    ofport_free(old_ofport);
 +
 +    /* Update port groups. */
      refresh_port_groups(p);
  }
  
@@@ -1289,7 -1273,7 +1293,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;
      }
@@@ -1491,7 -1475,7 +1495,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);
@@@ -1600,7 -1584,7 +1604,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
@@@ -1681,7 -1665,7 +1685,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;
@@@ -1829,7 -1813,7 +1833,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;
@@@ -1862,10 -1846,10 +1866,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")",
@@@ -2170,7 -2154,7 +2174,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);
  
@@@ -2313,7 -2297,7 +2317,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;
@@@ -2408,7 -2392,7 +2412,7 @@@ query_stats(struct ofproto *p, struct r
  
      packet_count = rule->packet_count;
      byte_count = rule->byte_count;
-     if (!dpif_flow_get_multiple(&p->dpif, odp_flows, n_odp_flows)) {
+     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];
@@@ -2491,59 -2475,6 +2495,59 @@@ handle_flow_stats_request(struct ofprot
      return 0;
  }
  
 +struct flow_stats_ds_cbdata {
 +    struct ofproto *ofproto;
 +    struct ds *results;
 +};
 +
 +static void
 +flow_stats_ds_cb(struct cls_rule *rule_, void *cbdata_)
 +{
 +    struct rule *rule = rule_from_cls_rule(rule_);
 +    struct flow_stats_ds_cbdata *cbdata = cbdata_;
 +    struct ds *results = cbdata->results;
 +    struct ofp_match match;
 +    uint64_t packet_count, byte_count;
 +    size_t act_len = sizeof *rule->actions * rule->n_actions;
 +
 +    /* Don't report on subrules. */
 +    if (rule->super != NULL) {
 +        return;
 +    }
 +
 +    query_stats(cbdata->ofproto, rule, &packet_count, &byte_count);
 +    flow_to_match(&rule->cr.flow, rule->cr.wc.wildcards, &match);
 +
 +    ds_put_format(results, "duration=%llds, ",
 +                  (time_msec() - rule->created) / 1000);
 +    ds_put_format(results, "priority=%u", rule->cr.priority);
 +    ds_put_format(results, "n_packets=%"PRIu64", ", packet_count);
 +    ds_put_format(results, "n_bytes=%"PRIu64", ", byte_count);
 +    ofp_print_match(results, &match, true);
 +    ofp_print_actions(results, &rule->actions->header, act_len);
 +    ds_put_cstr(results, "\n");
 +}
 +
 +/* Adds a pretty-printed description of all flows to 'results', including 
 + * those marked hidden by secchan (e.g., by in-band control). */
 +void
 +ofproto_get_all_flows(struct ofproto *p, struct ds *results)
 +{
 +    struct ofp_match match;
 +    struct cls_rule target;
 +    struct flow_stats_ds_cbdata cbdata;
 +
 +    memset(&match, 0, sizeof match);
 +    match.wildcards = htonl(OFPFW_ALL);
 +
 +    cbdata.ofproto = p;
 +    cbdata.results = results;
 +
 +    cls_rule_from_match(&target, &match, 0);
 +    classifier_for_each_match(&p->cls, &target, CLS_INC_ALL,
 +                              flow_stats_ds_cb, &cbdata);
 +}
 +
  struct aggregate_stats_cbdata {
      struct ofproto *ofproto;
      uint16_t out_port;
@@@ -3219,7 -3150,7 +3223,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;
          }
  
@@@ -3307,23 -3238,23 +3311,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 ofproto/ofproto.h
@@@ -80,7 -80,6 +80,7 @@@ bool ofproto_get_discovery(const struc
  const char *ofproto_get_controller(const struct ofproto *);
  void ofproto_get_listeners(const struct ofproto *, struct svec *);
  void ofproto_get_snoops(const struct ofproto *, struct svec *);
 +void ofproto_get_all_flows(struct ofproto *p, struct ds *);
  
  /* Functions for use by ofproto implementation modules, not by clients. */
  int ofproto_send_packet(struct ofproto *, const flow_t *,
@@@ -17,7 -17,7 +17,7 @@@ receives an acceptable DHCP response.  
  reply that has the same vendor class identifier and includes a
  vendor-specific option with code 1 whose contents are a string
  specifying the location of the controller in the same format used on
- the \fBsecchan\fR command line (e.g. \fBssl:192.168.0.1\fR).
+ the \fBovs\-openflowd\fR command line (e.g. \fBssl:192.168.0.1\fR).
  
  When \fBovs\-discover\fR receives an acceptable response, it prints
  the details of the response on \fBstdout\fR.  Then, by default, it
@@@ -28,15 -28,16 +28,16 @@@ itself to the background
  .SH OPTIONS
  .TP
  \fB--accept-vconn=\fIregex\fR
- By default, \fBovs\-discover\fR accepts any controller location
- advertised over DHCP.  With this option, only controllers whose names
- match POSIX extended regular expression \fIregex\fR will be accepted.
- Specifying \fBssl:.*\fR for \fIregex\fR, for example, would cause only
- SSL controller connections to be accepted.
+ With this option, only controllers whose names match POSIX extended
+ regular expression \fIregex\fR will be accepted.  Specifying
+ \fBssl:.*\fR for \fIregex\fR, for example, would cause only SSL
+ controller connections to be accepted.
  
  The \fIregex\fR is implicitly anchored at the beginning of the
  controller location string, as if it begins with \fB^\fR.
  
+ When this option is not given, the default \fIregex\fR is
+ \fBtcp:.*\fR.
  .TP
  \fB--exit-without-bind\fR
  By default, \fBovs\-discover\fR binds the network device that receives
@@@ -74,7 -75,7 +75,7 @@@ This option is mutually exclusive with 
  \fB--exit-after-bind\fR.
  
  .TP
 -\fB-P\fR[\fIpidfile\fR], \fB--pidfile\fR[\fB=\fIpidfile\fR]
 +\fB--pidfile\fR[\fB=\fIpidfile\fR]
  Causes a file (by default, \fBovs\-discover.pid\fR) to be created indicating
  the PID of the running process.  If \fIpidfile\fR is not specified, or
  if it does not begin with \fB/\fR, then it is created in
@@@ -85,13 -86,14 +86,13 @@@ this this option has no effect when on
  \fB--exit-after-bind\fR, or \fB--no-detach\fR is also given.
  
  .TP
 -\fB-f\fR, \fB--force\fR
 -By default, when \fB-P\fR or \fB--pidfile\fR is specified and the
 -specified pidfile already exists and is locked by a running process,
 -\fBcontroller\fR refuses to start.  Specify \fB-f\fR or \fB--force\fR
 -to cause it to instead overwrite the pidfile.
 -
 -When \fB-P\fR or \fB--pidfile\fR is not specified, this option has no
 -effect.
 +\fB--overwrite-pidfile\fR
 +By default, when \fB--pidfile\fR is specified and the specified pidfile 
 +already exists and is locked by a running process, \fBcontroller\fR refuses 
 +to start.  Specify \fB--overwrite-pidfile\fR to cause it to instead 
 +overwrite the pidfile.
 +
 +When \fB--pidfile\fR is not specified, this option has no effect.
  
  .so lib/vlog.man
  .so lib/common.man
@@@ -113,5 -115,5 +114,5 @@@ arriving IP packets, will not
  
  .SH "SEE ALSO"
  
- .BR secchan (8),
- .BR ovs-pki (8)
+ .BR ovs\-openflowd (8),
+ .BR ovs\-pki (8)
diff --combined utilities/ovs-discover.c
@@@ -48,7 -48,7 +48,7 @@@ static int n_ifaces
  
  /* --accept-vconn: Regular expression specifying the class of controller vconns
   * that we will accept during autodiscovery. */
- static const char *accept_controller_re = ".*";
+ static const char *accept_controller_re = "tcp:.*";
  static regex_t accept_controller_regex;
  
  /* --exit-without-bind: Exit after discovering the controller, without binding
@@@ -282,7 -282,7 +282,7 @@@ parse_options(int argc, char *argv[]
          OPT_ACCEPT_VCONN = UCHAR_MAX + 1,
          OPT_EXIT_WITHOUT_BIND,
          OPT_EXIT_AFTER_BIND,
 -        OPT_NO_DETACH,
 +        OPT_NO_DETACH
      };
      static struct option long_options[] = {
          {"accept-vconn", required_argument, 0, OPT_ACCEPT_VCONN},
          {"exit-after-bind", no_argument, 0, OPT_EXIT_AFTER_BIND},
          {"no-detach",   no_argument, 0, OPT_NO_DETACH},
          {"timeout",     required_argument, 0, 't'},
 -        {"pidfile",     optional_argument, 0, 'P'},
 -        {"force",       no_argument, 0, 'f'},
 +        {"pidfile",     optional_argument, 0, OPT_PIDFILE},
 +        {"overwrite-pidfile", no_argument, 0, OPT_OVERWRITE_PIDFILE},
          {"verbose",     optional_argument, 0, 'v'},
          {"help",        no_argument, 0, 'h'},
          {"version",     no_argument, 0, 'V'},
              detach_after_bind = false;
              break;
  
 -        case 'P':
 +        case OPT_PIDFILE:
              set_pidfile(optarg);
              break;
  
 -        case 'f':
 +        case OPT_OVERWRITE_PIDFILE:
              ignore_existing_pidfile();
              break;
  
@@@ -396,9 -396,8 +396,9 @@@ usage(void
      vlog_usage();
      printf("\nOther options:\n"
             "  -t, --timeout=SECS      give up discovery after SECS seconds\n"
 -           "  -P, --pidfile[=FILE]    create pidfile (default: %s/%s.pid)\n"
 -           "  -f, --force             with -P, start even if already running\n"
 +           "  --pidfile[=FILE]        create pidfile (default: %s/%s.pid)\n"
 +           "  --overwrite-pidfile     with --pidfile, start even if already "
 +                                      "running\n"
             "  -h, --help              display this help message\n"
             "  -V, --version           display version information\n",
             ovs_rundir, program_name);
diff --combined utilities/ovs-dpctl.8.in
@@@ -1,4 -1,4 +1,4 @@@
 -.TH ovs\-dpctl 8 "March 2009" "Open vSwitch" "Open vSwitch Manual"
 +.TH ovs\-dpctl 8 "August 2009" "Open vSwitch" "Open vSwitch Manual"
  .ds PN ovs\-dpctl
  
  .SH NAME
@@@ -41,14 -41,13 +41,13 @@@ The following commands manage datapaths
  
  Creates datapath \fIdp\fR.  The name of the new datapath's local port
  depends on how \fIdp\fR is specified: if it takes the form
- \fBdp\fIN\fR, the local port will be named \fBdp\fIN\fR; if \fIdp\fR
- is \fBnl:\fI, the local port will be named \fBof\fIN\fR; otherwise,
+ \fBdp\fIN\fR, the local port will be named \fBdp\fIN\fR; otherwise,
  the local port's name will be \fIdp\fR.
  
  This will fail if the host already has 256 datapaths, if a network
  device with the same name as the new datapath's local port already
- exists, or if \fIdp\fR is given in the form \fBdp\fIN\fR or
\fBnl:\fIN\fR and a datapath numbered \fIN\fR already exists.
+ exists, or if \fIdp\fR is given in the form \fBdp\fIN\fR
+ and a datapath numbered \fIN\fR already exists.
  
  If \fInetdev\fRs are specified, \fBovs\-dpctl\fR adds them to the datapath.
  
@@@ -71,11 -70,6 +70,6 @@@ A \fInetdev\fR may be followed by a com
  The following options are currently supported:
  
  .RS
- .IP "\fBport=\fIportno\fR"
- Specifies \fIportno\fR (a number between 1 and 255) as the port number
- at which \fInetdev\fR will be attached.  By default, \fBadd\-if\fR
- automatically selects the lowest available port number.
  .IP "\fBinternal\fR"
  Instead of attaching an existing \fInetdev\fR, creates an internal
  port (analogous to the local port) with that name.
  Removes each \fInetdev\fR from the list of network devices datapath
  \fIdp\fR monitors.
  
 +.TP
 +\fBdump-dps\fR
 +Prints the name of each configured datapath on a separate line.
 +
  .TP
  \fBshow \fR[\fIdp\fR...]
  Prints a summary of configured datapaths, including their datapath
@@@ -120,9 -110,10 +114,10 @@@ up may be confused about their disappea
  .IP "\fBdump-groups \fIdp\fR"
  Prints to the console the sets of port groups maintained by datapath
  \fIdp\fR.  Ordinarily there are at least 2 port groups in a datapath
- that \fBsecchan\fR or \fBvswitch\fR is controlling: group 0 contains
+ that \fBovs\-openflowd\fR or \fBovs\-vswitch\fR is controlling: group
+ 0 contains
  all ports except those disabled by STP, and group 1 contains all
- ports.  Additional groups might be used in the future.
+ ports.  Additional or different groups might be used in the future.
  
  This command is primarily useful for debugging Open vSwitch.  OpenFlow
  does not have a concept of port groups.
@@@ -151,7 -142,7 +146,7 @@@ Creates datapath number 0
  Adds two network devices to the new datapath.
  
  .PP
- At this point one would ordinarily start \fBsecchan\fR(8) on
+ At this point one would ordinarily start \fBovs\-openflowd\fR(8) on
  \fBdp0\fR, transforming \fBdp0\fR into an OpenFlow switch.  Then, when
  the switch and the datapath is no longer needed:
  
@@@ -165,6 -156,6 +160,6 @@@ Deletes the datapath
  
  .SH "SEE ALSO"
  
- .BR secchan (8),
  .BR ovs\-appctl (8),
+ .BR ovs\-openflowd (8),
  .BR ovs\-vswitchd (8)
diff --combined utilities/ovs-dpctl.c
@@@ -36,7 -36,6 +36,7 @@@
  #include "dynamic-string.h"
  #include "netdev.h"
  #include "odp-util.h"
 +#include "svec.h"
  #include "timeval.h"
  #include "util.h"
  
@@@ -158,7 -157,6 +158,7 @@@ usage(void
             "  del-dp DP                delete local datapath DP\n"
             "  add-if DP IFACE...       add each IFACE as a port on DP\n"
             "  del-if DP IFACE...       delete each IFACE from DP\n"
 +           "  dump-dps                 display names of all datapaths\n"
             "  show                     show basic info on all datapaths\n"
             "  show DP...               show basic info on each DP\n"
             "  dump-flows DP            display flows in DP\n"
@@@ -213,9 -211,9 +213,9 @@@ static int if_up(const char *netdev_nam
  static void
  do_add_dp(int argc UNUSED, char *argv[])
  {
-     struct dpif dpif;
+     struct dpif *dpif;
      run(dpif_create(argv[1], &dpif), "add_dp");
-     dpif_close(&dpif);
+     dpif_close(dpif);
      if (argc > 2) {
          do_add_if(argc, argv);
      }
  static void
  do_del_dp(int argc UNUSED, char *argv[])
  {
-     struct dpif dpif;
+     struct dpif *dpif;
      run(dpif_open(argv[1], &dpif), "opening datapath");
-     run(dpif_delete(&dpif), "del_dp");
-     dpif_close(&dpif);
+     run(dpif_delete(dpif), "del_dp");
+     dpif_close(dpif);
  }
  
  static int
@@@ -245,41 -243,17 +245,17 @@@ query_ports(struct dpif *dpif, struct o
      qsort(*ports, *n_ports, sizeof **ports, compare_ports);
  }
  
- static uint16_t
- get_free_port(struct dpif *dpif)
- {
-     struct odp_port *ports;
-     size_t n_ports;
-     int port_no;
-     query_ports(dpif, &ports, &n_ports);
-     for (port_no = 0; port_no <= UINT16_MAX; port_no++) {
-         size_t i;
-         for (i = 0; i < n_ports; i++) {
-             if (ports[i].port == port_no) {
-                 goto next_portno;
-             }
-         }
-         free(ports);
-         return port_no;
-     next_portno: ;
-     }
-     ovs_fatal(0, "no free datapath ports");
- }
  static void
  do_add_if(int argc UNUSED, char *argv[])
  {
      bool failure = false;
-     struct dpif dpif;
+     struct dpif *dpif;
      int i;
  
      run(dpif_open(argv[1], &dpif), "opening datapath");
      for (i = 2; i < argc; i++) {
          char *save_ptr = NULL;
          char *devname, *suboptions;
-         int port = -1;
          int flags = 0;
          int error;
  
          suboptions = strtok_r(NULL, "", &save_ptr);
          if (suboptions) {
              enum {
-                 AP_PORT,
                  AP_INTERNAL
              };
              static char *options[] = {
-                 "port",
                  "internal"
              };
  
                  char *value;
  
                  switch (getsubopt(&suboptions, options, &value)) {
-                 case AP_PORT:
-                     if (!value) {
-                         ovs_error(0, "'port' suboption requires a value");
-                     }
-                     port = atoi(value);
-                     break;
                  case AP_INTERNAL:
                      flags |= ODP_PORT_INTERNAL;
                      break;
                  }
              }
          }
-         if (port < 0) {
-             port = get_free_port(&dpif);
-         }
  
-         error = dpif_port_add(&dpif, devname, port, flags);
+         error = dpif_port_add(dpif, devname, flags, NULL);
          if (error) {
-             ovs_error(error, "adding %s as port %"PRIu16" of %s failed",
-                       devname, port, argv[1]);
+             ovs_error(error, "adding %s to %s failed", devname, argv[1]);
              failure = true;
          } else if (if_up(devname)) {
              failure = true;
          }
      }
-     dpif_close(&dpif);
+     dpif_close(dpif);
      if (failure) {
          exit(EXIT_FAILURE);
      }
@@@ -364,7 -325,7 +327,7 @@@ static voi
  do_del_if(int argc UNUSED, char *argv[])
  {
      bool failure = false;
-     struct dpif dpif;
+     struct dpif *dpif;
      int i;
  
      run(dpif_open(argv[1], &dpif), "opening datapath");
  
          if (!name[strspn(name, "0123456789")]) {
              port = atoi(name);
-         } else if (!get_port_number(&dpif, name, &port)) {
+         } else if (!get_port_number(dpif, name, &port)) {
              failure = true;
              continue;
          }
  
-         error = dpif_port_del(&dpif, port);
+         error = dpif_port_del(dpif, port);
          if (error) {
              ovs_error(error, "deleting port %s from %s failed", name, argv[1]);
              failure = true;
          }
      }
-     dpif_close(&dpif);
+     dpif_close(dpif);
      if (failure) {
          exit(EXIT_FAILURE);
      }
@@@ -400,7 -361,7 +363,7 @@@ show_dpif(struct dpif *dpif
      size_t n_ports;
      size_t i;
  
-     printf("dp%u:\n", dpif_id(dpif));
+     printf("%s:\n", dpif_name(dpif));
      if (!dpif_get_dp_stats(dpif, &stats)) {
          printf("\tflows: cur:%"PRIu32", soft-max:%"PRIu32", "
                 "hard-max:%"PRIu32"\n",
  }
  
  static void
 -do_show(int argc UNUSED, char *argv[])
 +do_show(int argc, char *argv[])
  {
      bool failure = false;
      if (argc > 1) {
          int i;
          for (i = 1; i < argc; i++) {
              const char *name = argv[i];
-             struct dpif dpif;
+             struct dpif *dpif;
              int error;
  
              error = dpif_open(name, &dpif);
              if (!error) {
-                 show_dpif(&dpif);
+                 show_dpif(dpif);
              } else {
                  ovs_error(error, "opening datapath %s failed", name);
                  failure = true;
          unsigned int i;
          for (i = 0; i < ODP_MAX; i++) {
              char name[128];
-             struct dpif dpif;
+             struct dpif *dpif;
              int error;
  
              sprintf(name, "dp%u", i);
              error = dpif_open(name, &dpif);
              if (!error) {
-                 show_dpif(&dpif);
+                 show_dpif(dpif);
              } else if (error != ENODEV) {
                  ovs_error(error, "opening datapath %s failed", name);
                  failure = true;
      }
  }
  
-         struct dpif dpif;
-         char dpif_name[IF_NAMESIZE];
-         if (dpif_open(all_dps.names[i], &dpif)) {
-             continue;
-         }
-         if (!dpif_get_name(&dpif, dpif_name, sizeof dpif_name)) {
-             printf("%s\n", dpif_name);
 +static void
 +do_dump_dps(int argc UNUSED, char *argv[] UNUSED)
 +{
 +    struct svec all_dps;
 +    unsigned int i;
 +    int error;
 +
 +    svec_init(&all_dps);
 +    error = dp_enumerate(&all_dps);
 +
 +    for (i = 0; i < all_dps.n; i++) {
-         dpif_close(&dpif);
++        struct dpif *dpif;
++        if (!dpif_open(all_dps.names[i], &dpif)) {
++            printf("%s\n", dpif_name(dpif));
++            dpif_close(dpif);
 +        }
 +    }
 +
 +    svec_destroy(&all_dps);
 +    if (error) {
 +        exit(EXIT_FAILURE);
 +    }
 +}
 +
  static void
  do_dump_flows(int argc UNUSED, char *argv[])
  {
      struct odp_flow *flows;
-     struct dpif dpif;
+     struct dpif *dpif;
      size_t n_flows;
      struct ds ds;
      size_t i;
  
      run(dpif_open(argv[1], &dpif), "opening datapath");
-     run(dpif_flow_list_all(&dpif, &flows, &n_flows), "listing all flows");
+     run(dpif_flow_list_all(dpif, &flows, &n_flows), "listing all flows");
  
      ds_init(&ds);
      for (i = 0; i < n_flows; i++) {
  
          f->actions = actions;
          f->n_actions = MAX_ACTIONS;
-         dpif_flow_get(&dpif, f);
+         dpif_flow_get(dpif, f);
  
          ds_clear(&ds);
          format_odp_flow(&ds, f);
          printf("%s\n", ds_cstr(&ds));
      }
      ds_destroy(&ds);
-     dpif_close(&dpif);
+     dpif_close(dpif);
  }
  
  static void
  do_del_flows(int argc UNUSED, char *argv[])
  {
-     struct dpif dpif;
+     struct dpif *dpif;
  
      run(dpif_open(argv[1], &dpif), "opening datapath");
-     run(dpif_flow_flush(&dpif), "deleting all flows");
-     dpif_close(&dpif);
+     run(dpif_flow_flush(dpif), "deleting all flows");
+     dpif_close(dpif);
  }
  
  static void
  do_dump_groups(int argc UNUSED, char *argv[])
  {
      struct odp_stats stats;
-     struct dpif dpif;
+     struct dpif *dpif;
      unsigned int i;
  
      run(dpif_open(argv[1], &dpif), "opening datapath");
-     run(dpif_get_dp_stats(&dpif, &stats), "get datapath stats");
+     run(dpif_get_dp_stats(dpif, &stats), "get datapath stats");
      for (i = 0; i < stats.max_groups; i++) {
-         uint16_t ports[UINT16_MAX];
+         uint16_t *ports;
          size_t n_ports;
  
-         if (!dpif_port_group_get(&dpif, i, ports,
-                                  ARRAY_SIZE(ports), &n_ports) && n_ports) {
+         if (!dpif_port_group_get(dpif, i, &ports, &n_ports) && n_ports) {
              size_t j;
  
              printf("group %u:", i);
              }
              printf("\n");
          }
+         free(ports);
      }
-     dpif_close(&dpif);
+     dpif_close(dpif);
  }
  
  static void
@@@ -574,7 -506,6 +532,7 @@@ static struct command all_commands[] = 
      { "del-dp", 1, 1, do_del_dp },
      { "add-if", 2, INT_MAX, do_add_if },
      { "del-if", 2, INT_MAX, do_del_if },
 +    { "dump-dps", 0, 0, do_dump_dps },
      { "show", 0, INT_MAX, do_show },
      { "dump-flows", 1, 1, do_dump_flows },
      { "del-flows", 1, 1, do_del_flows },
@@@ -1,16 -1,16 +1,16 @@@
- .TH secchan 8 "March 2009" "Open vSwitch" "Open vSwitch Manual"
- .ds PN secchan
+ .TH ovs\-openflowd 8 "March 2009" "Open vSwitch" "Open vSwitch Manual"
+ .ds PN ovs\-openflowd
  
  .SH NAME
secchan \- OpenFlow switch implementation
ovs\-openflowd \- OpenFlow switch implementation
  
  .SH SYNOPSIS
- .B secchan
+ .B ovs\-openflowd
  [\fIoptions\fR] \fIdatapath\fR [\fIcontroller\fR]
  
  .SH DESCRIPTION
- The \fBsecchan\fR program implements an OpenFlow switch using a
- flow-based datapath.  \fBsecchan\fR connects to an OpenFlow controller
+ The \fBovs\-openflowd\fR program implements an OpenFlow switch using a
+ flow-based datapath.  \fBovs\-openflowd\fR connects to an OpenFlow controller
  over TCP or SSL.
  
  The mandatory \fIdatapath\fR argument argument specifies the local datapath
@@@ -39,7 -39,7 +39,7 @@@ The Unix domain server socket named \fI
  .RE
  
  .PP
- If \fIcontroller\fR is omitted, \fBsecchan\fR attempts to discover the
+ If \fIcontroller\fR is omitted, \fBovs\-openflowd\fR attempts to discover the
  location of the controller automatically (see below).
  
  .SS "Contacting the Controller"
@@@ -52,9 -52,9 +52,9 @@@ the data traffic that it controls, tha
  any of the network devices added to the datapath with \fBovs\-dpctl
  add\-if\fR in its communication with the controller.
  
- To use \fBsecchan\fR in a network with out-of-band control, specify
- \fB--out-of-band\fR on the \fBsecchan\fR command line.  The control
- network must be configured separately, before or after \fBsecchan\fR
+ To use \fBovs\-openflowd\fR in a network with out-of-band control, specify
+ \fB--out-of-band\fR on the \fBovs\-openflowd\fR command line.  The control
+ network must be configured separately, before or after \fBovs\-openflowd\fR
  is started.
  
  .IP in-band
@@@ -65,7 -65,7 +65,7 @@@ add\-if\fR.  This configuration is ofte
  out-of-band control, because it is not necessary to maintain two
  independent networks.
  
- In-band control is the default for \fBsecchan\fR, so no special
+ In-band control is the default for \fBovs\-openflowd\fR, so no special
  command-line option is required.
  
  With in-band control, the location of the controller can be configured
@@@ -73,23 -73,23 +73,23 @@@ manually or discovered automatically
  
  .RS
  .IP "controller discovery"
- To make \fBsecchan\fR discover the location of the controller
+ To make \fBovs\-openflowd\fR discover the location of the controller
  automatically, do not specify the location of the controller on the
- \fBsecchan\fR command line.
+ \fBovs\-openflowd\fR command line.
  
- In this mode, \fBsecchan\fR will broadcast a DHCP request with vendor
+ In this mode, \fBovs\-openflowd\fR will broadcast a DHCP request with vendor
  class identifier \fBOpenFlow\fR across the network devices added to
  the datapath with \fBovs\-dpctl add\-if\fR.  It will accept any valid DHCP
  reply that has the same vendor class identifier and includes a
  vendor-specific option with code 1 whose contents are a string
  specifying the location of the controller in the same format used on
- the \fBsecchan\fR command line (e.g. \fBssl:192.168.0.1\fR).
+ the \fBovs\-openflowd\fR command line (e.g. \fBssl:192.168.0.1\fR).
  
  The DHCP reply may also, optionally, include a vendor-specific option
  with code 2 whose contents are a string specifying the URI to the base
  of the OpenFlow PKI (e.g. \fBhttp://192.168.0.1/openflow/pki\fR).
  This URI is used only for bootstrapping the OpenFlow PKI at initial
- switch setup; \fBsecchan\fR does not use it at all.
+ switch setup; \fBovs\-openflowd\fR does not use it at all.
  
  The following ISC DHCP server configuration file assigns the IP
  address range 192.168.0.20 through 192.168.0.30 to OpenFlow switches
@@@ -143,28 -143,28 +143,28 @@@ subnet 192.168.0.0 netmask 255.255.255.
  
  .IP "manual configuration"
  To configure in-band control manually, specify the location of the
- controller on the \fBsecchan\fR command line as the \fIcontroller\fR
+ controller on the \fBovs\-openflowd\fR command line as the \fIcontroller\fR
  argument.  You must also configure the network device for the OpenFlow
- ``local port'' to allow \fBsecchan\fR to connect to that controller.
- The OpenFlow local port is a virtual network port that \fBsecchan\fR
+ ``local port'' to allow \fBovs\-openflowd\fR to connect to that controller.
+ The OpenFlow local port is a virtual network port that \fBovs\-openflowd\fR
  bridges to the physical switch ports.  The name of the local port for
  a given \fIdatapath\fR may be seen by running \fBovs\-dpctl show
  \fIdatapath\fR; the local port is listed as port 0 in \fBshow\fR's
  output.
  
  .IP
- Before \fBsecchan\fR starts, the local port network device is not
+ Before \fBovs\-openflowd\fR starts, the local port network device is not
  bridged to any physical network, so the next step depends on whether
  connectivity is required to configure the device's IP address.  If the
  switch has a static IP address, you may configure its IP address now
  with a command such as 
  .B ifconfig of0 192.168.1.1
- and then invoke \fBsecchan\fR.
+ and then invoke \fBovs\-openflowd\fR.
  
  On the other hand, if the switch does not have a static IP address,
  e.g. it obtains its IP address dynamically via DHCP, the DHCP client
- will not be able to contact the DHCP server until the secure channel
- has started up.  Thus, start \fBsecchan\fR without configuring
+ will not be able to contact the DHCP server until the OpenFlow switch
+ has started up.  Thus, start \fBovs\-openflowd\fR without configuring
  the local port network device, and start the DHCP client afterward.
  .RE
  
  .SS "Controller Discovery Options"
  .TP
  \fB--accept-vconn=\fIregex\fR
- When \fBsecchan\fR performs controller discovery (see \fBContacting
+ When \fBovs\-openflowd\fR performs controller discovery (see \fBContacting
  the Controller\fR, above, for more information about controller
  discovery), it validates the controller location obtained via DHCP
  with a POSIX extended regular expression.  Only controllers whose
@@@ -181,8 -181,8 +181,8 @@@ names match the regular expression wil
  The default regular expression is \fBssl:.*\fR (meaning that only SSL
  controller connections will be accepted) when any of the SSL
  configuration options \fB--private-key\fR, \fB--certificate\fR, or
- \fB--ca-cert\fR is specified.  The default is \fB.*\fR otherwise
- (meaning that any controller will be accepted).
+ \fB--ca-cert\fR is specified.  The default is \fB^tcp:.*\fR otherwise
+ (meaning that only TCP controller connections will be accepted).
  
  The \fIregex\fR is implicitly anchored at the beginning of the
  controller location string, as if it begins with \fB^\fR.
@@@ -191,7 -191,7 +191,7 @@@ When controller discovery is not perfor
  
  .TP
  \fB--no-resolv-conf\fR
- When \fBsecchan\fR performs controller discovery (see \fBContacting
+ When \fBovs\-openflowd\fR performs controller discovery (see \fBContacting
  the Controller\fR, above, for more information about controller
  discovery), by default it overwrites the system's
  \fB/etc/resolv.conf\fR with domain information and DNS servers
@@@ -199,10 -199,10 +199,10 @@@ obtained via DHCP.  If the location of 
  using a hostname, rather than an IP address, and the network's DNS
  servers ever change, this behavior is essential.  But because it also
  interferes with any administrator or process that manages
- \fB/etc/resolv.conf\fR, when this option is specified, \fBsecchan\fR
+ \fB/etc/resolv.conf\fR, when this option is specified, \fBovs\-openflowd\fR
  will not modify \fB/etc/resolv.conf\fR.
  
- \fBsecchan\fR will only modify \fBresolv.conf\fR if the DHCP response
+ \fBovs\-openflowd\fR will only modify \fBresolv.conf\fR if the DHCP response
  that it receives specifies one or more DNS servers.
  
  When controller discovery is not performed, this option has no effect.
@@@ -234,27 -234,27 +234,27 @@@ no new network connections can be set u
  controller stays down long enough, no packets can pass through the
  switch at all.
  
- If this option is set to \fBopen\fR (the default), \fBsecchan\fR will
+ If this option is set to \fBopen\fR (the default), \fBovs\-openflowd\fR will
  take over responsibility for setting up flows in the local datapath
  when no message has been received from the controller for three times
  the inactivity probe interval (see below), or 45 seconds by default.
- In this ``fail open'' mode, \fBsecchan\fR causes the datapath to act
- like an ordinary MAC-learning switch.  \fBsecchan\fR will continue to
+ In this ``fail open'' mode, \fBovs\-openflowd\fR causes the datapath to act
+ like an ordinary MAC-learning switch.  \fBovs\-openflowd\fR will continue to
  retry connection to the controller in the background and, when the
  connection succeeds, it discontinues its fail-open behavior.
  
- If this option is set to \fBclosed\fR, then \fBsecchan\fR will not
+ If this option is set to \fBclosed\fR, then \fBovs\-openflowd\fR will not
  set up flows on its own when the controller connection fails.
  
  .TP
  \fB--inactivity-probe=\fIsecs\fR
- When the secure channel is connected to the controller, the secure
channel waits for a message to be received from the controller for
+ When the OpenFlow switch is connected to the controller, the
switch waits for a message to be received from the controller for
  \fIsecs\fR seconds before it sends a inactivity probe to the
  controller.  After sending the inactivity probe, if no response is
- received for an additional \fIsecs\fR seconds, the secure channel
+ received for an additional \fIsecs\fR seconds, the switch
  assumes that the connection has been broken and attempts to reconnect.
 -The default is 15 seconds, and the minimum value is 5 seconds.
 +The default and the minimum value are both 5 seconds.
  
  When fail-open mode is configured, changing the inactivity probe
  interval also changes the interval before entering fail-open mode (see
@@@ -263,19 -263,19 +263,19 @@@ above)
  .TP
  \fB--max-idle=\fIsecs\fR|\fBpermanent\fR
  Sets \fIsecs\fR as the number of seconds that a flow set up by the
secure channel will remain in the switch's flow table without any
OpenFlow switch will remain in the switch's flow table without any
  matching packets being seen.  If \fBpermanent\fR is specified, which
- is not recommended, flows set up by the secure channel will never
+ is not recommended, flows set up by the switch will never
  expire.  The default is 15 seconds.
  
- Most flows are set up by the OpenFlow controller, not by the secure
channel.  This option affects only the following flows, which the
secure channel sets up itself:
+ Most flows are set up by the OpenFlow controller, not by the
switch.  This option affects only the following flows, which the
OpenFlow switch sets up itself:
  
  .RS
  .IP \(bu
- When \fB--fail=open\fR is specified, flows set up when the secure
channel has not been able to contact the controller for the configured
+ When \fB--fail=open\fR is specified, flows set up when the
switch has not been able to contact the controller for the configured
  fail-open delay.
  
  .IP \(bu
@@@ -294,7 -294,7 +294,7 @@@ Sets the maximum time between attempts 
  \fIsecs\fR, which must be at least 1.  The actual interval between
  connection attempts starts at 1 second and doubles on each failing
  attempt until it reaches the maximum.  The default maximum backoff
 -time is 15 seconds.
 +time is 8 seconds.
  
  .TP
  \fB-l\fR, \fB--listen=\fImethod\fR
@@@ -306,14 -306,20 +306,20 @@@ multiple connection methods
  
  .RS
  .TP
- \fBpssl:\fR[\fIport\fR]
+ \fBpssl:\fR[\fIport\fR][\fB:\fIip\fR]
  Listens for SSL connections on \fIport\fR (default: 6633).  The
  \fB--private-key\fR, \fB--certificate\fR, and \fB--ca-cert\fR options
  are mandatory when this form is used.
+ By default, \fB\*(PN\fR listens for connections to any local IP
+ address, but \fIip\fR may be specified to listen only for connections
+ to the given \fIip\fR.
  
  .TP
- \fBptcp:\fR[\fIport\fR]
+ \fBptcp:\fR[\fIport\fR][\fB:\fIip\fR]
  Listens for TCP connections on \fIport\fR (default: 6633).
+ By default, \fB\*(PN\fR listens for connections to any local IP
+ address, but \fIip\fR may be specified to listen only for connections
+ to the given \fIip\fR.
  
  .TP
  \fBpunix:\fIfile\fR
@@@ -336,7 -342,7 +342,7 @@@ problems
  
  .TP
  \fB--in-band\fR, \fB--out-of-band\fR
- Configures \fBsecchan\fR to operate in in-band or out-of-band control
+ Configures \fBovs\-openflowd\fR to operate in in-band or out-of-band control
  mode (see \fBContacting the Controller\fR above).  When neither option
  is given, the default is in-band control.
  
@@@ -395,7 -401,7 +401,7 @@@ Command names that include characters o
  English letters, digits, and the underscore and hyphen characters are
  unconditionally disallowed.
  
- When the whitelist and blacklist permit a command name, \fBsecchan\fR
+ When the whitelist and blacklist permit a command name, \fBovs\-openflowd\fR
  looks for a program with the same name as the command in the commands
  directory (see below).  Other directories are not searched.
  
@@@ -429,7 -435,7 +435,7 @@@ the switch is connected to a trustworth
  .TP
  \fB--bootstrap-ca-cert=\fIcacert.pem\fR
  When \fIcacert.pem\fR exists, this option has the same effect as
- \fB-C\fR or \fB--ca-cert\fR.  If it does not exist, then \fBsecchan\fR
+ \fB-C\fR or \fB--ca-cert\fR.  If it does not exist, then \fBovs\-openflowd\fR
  will attempt to obtain the CA certificate from the controller on its
  first SSL connection and save it to the named PEM file.  If it is
  successful, it will immediately drop the connection and reconnect, and
  #include "compiler.h"
  #include "daemon.h"
  #include "dirs.h"
- #include "discovery.h"
  #include "dpif.h"
- #include "fail-open.h"
  #include "fault.h"
- #include "in-band.h"
  #include "leak-checker.h"
  #include "list.h"
  #include "netdev.h"
  #include "ofpbuf.h"
- #include "ofproto.h"
+ #include "ofproto/ofproto.h"
  #include "openflow/openflow.h"
  #include "packets.h"
  #include "poll-loop.h"
  #include "rconn.h"
- #include "status.h"
  #include "svec.h"
  #include "timeval.h"
  #include "unixctl.h"
@@@ -51,7 -47,7 +47,7 @@@
  #include "vconn.h"
  
  #include "vlog.h"
- #define THIS_MODULE VLM_secchan
+ #define THIS_MODULE VLM_openflowd
  
  /* Behavior when the connection to the controller fails. */
  enum fail_mode {
@@@ -198,9 -194,13 +194,13 @@@ main(int argc, char *argv[]
              ovs_fatal(error, "unrecoverable datapath error");
          }
          unixctl_server_run(unixctl);
+         dp_run();
+         netdev_run();
  
          ofproto_wait(ofproto);
          unixctl_server_wait(unixctl);
+         dp_wait();
+         netdev_wait();
          poll_block();
      }
  
@@@ -291,7 -291,7 +291,7 @@@ parse_options(int argc, char *argv[], s
      s->fail_mode = FAIL_OPEN;
      s->max_idle = 0;
      s->probe_interval = 0;
 -    s->max_backoff = 15;
 +    s->max_backoff = 8;
      s->update_resolv_conf = true;
      s->rate_limit = 0;
      s->burst_limit = 0;
              } else if (!strcmp(optarg, "closed")) {
                  s->fail_mode = FAIL_CLOSED;
              } else {
 -                ovs_fatal(0, "-f or --fail argument must be \"open\" "
 -                          "or \"closed\"");
 +                ovs_fatal(0, "--fail argument must be \"open\" or \"closed\"");
              }
              break;
  
  
      /* Set accept_controller_regex. */
      if (!s->accept_controller_re) {
-         s->accept_controller_re = vconn_ssl_is_configured() ? "^ssl:.*" : ".*";
+         s->accept_controller_re
+             = vconn_ssl_is_configured() ? "^ssl:.*" : "^tcp:.*";
      }
  
      /* Mode of operation. */
@@@ -518,7 -520,7 +519,7 @@@ usage(void
             "usage: %s [OPTIONS] DATAPATH [CONTROLLER]\n"
             "DATAPATH is a local datapath (e.g. \"dp0\").\n"
             "CONTROLLER is an active OpenFlow connection method; if it is\n"
-            "omitted, then secchan performs controller discovery.\n",
+            "omitted, then ovs-openflowd performs controller discovery.\n",
             program_name, program_name);
      vconn_usage(true, true, true);
      printf("\nOpenFlow options:\n"
             "                            closed: drop all packets\n"
             "                            open (default): act as learning switch\n"
             "  --inactivity-probe=SECS time between inactivity probes\n"
-            "  --max-idle=SECS         max idle for flows set up by secchan\n"
+            "  --max-idle=SECS         max idle for flows set up by switch\n"
             "  --max-backoff=SECS      max time between controller connection\n"
 -           "                          attempts (default: 15 seconds)\n"
 +           "                          attempts (default: 8 seconds)\n"
             "  -l, --listen=METHOD     allow management connections on METHOD\n"
             "                          (a passive OpenFlow connection method)\n"
             "  --snoop=METHOD          allow controller snooping on METHOD\n"
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,17 -71,18 +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
@@@ -130,7 -131,6 +131,7 @@@ struct port 
      tag_type active_iface_tag;  /* Tag for bcast flows. */
      tag_type no_ifaces_tag;     /* Tag for flows when all ifaces disabled. */
      int updelay, downdelay;     /* Delay before iface goes up/down, in ms. */
 +    bool bond_compat_is_stale;  /* Need to call port_update_bond_compat()? */
  
      /* Port mirroring info. */
      mirror_mask_t src_mirrors;  /* Mirrors triggered when packet received. */
@@@ -159,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. */
@@@ -193,7 -193,6 +194,7 @@@ enum { DP_MAX = 256 }
  static struct bridge *bridge_create(const char *name);
  static void bridge_destroy(struct bridge *);
  static struct bridge *bridge_lookup(const char *name);
 +static void bridge_unixctl_dump_flows(struct unixctl_conn *, const char *);
  static int bridge_run_one(struct bridge *);
  static void bridge_reconfigure_one(struct bridge *);
  static void bridge_reconfigure_controller(struct bridge *);
@@@ -202,10 -201,11 +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);
@@@ -225,6 -225,7 +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 *);
@@@ -263,8 -264,8 +266,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);
          }
      }
  
 +    unixctl_command_register("bridge/dump-flows", bridge_unixctl_dump_flows);
 +
+     bond_init();
      bridge_reconfigure();
  }
  
@@@ -348,43 -353,105 +357,105 @@@ 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_policing(struct bridge *br UNUSED, struct iface *iface,
+                    void *aux UNUSED)
+ {
+     int rate = cfg_get_int(0, "port.%s.ingress.policing-rate", iface->name);
+     int burst = cfg_get_int(0, "port.%s.ingress.policing-burst", iface->name);
+     netdev_set_policing(iface->netdev, rate, burst);
+     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;
-                 /* It's an internal interface if it's marked that way, or if
-                  * it's a bonded interface for which we're faking up a network
-                  * device. */
-                 internal = cfg_get_bool(0, "iface.%s.internal", if_name);
-                 if (cfg_get_bool(0, "bonding.%s.fake-iface", if_name)) {
-                     struct port *port = port_lookup(br, if_name);
-                     if (port && port->n_ifaces > 1) {
-                         internal = true;
-                     }
-                 }
 -            int internal = cfg_get_bool(0, "iface.%s.internal", if_name);
 -            int flags = internal ? ODP_PORT_INTERNAL : 0;
 -            int error = dpif_port_add(br->dpif, if_name, flags, NULL);
++            bool internal;
++            int error;
 +
-                 /* Add to datapath. */
-                 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;
++            /* It's an internal interface if it's marked that way, or if
++             * it's a bonded interface for which we're faking up a network
++             * device. */
++            internal = cfg_get_bool(0, "iface.%s.internal", if_name);
++            if (cfg_get_bool(0, "bonding.%s.fake-iface", if_name)) {
++                struct port *port = port_lookup(br, if_name);
++                if (port && port->n_ifaces > 1) {
++                    internal = true;
 +                }
 +            }
++
++            /* Add to datapath. */
++            error = dpif_port_add(br->dpif, if_name,
++                                  internal ? ODP_PORT_INTERNAL : 0, NULL);
+             if (error == EXFULL) {
+                 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);
          for (i = 0; i < br->n_ports; i++) {
              struct port *port = br->ports[i];
              port_update_vlan_compat(port);
+             port_update_bonding(port);
          }
      }
      LIST_FOR_EACH (br, struct bridge, node, &all_bridges) {
          brstp_reconfigure(br);
+         iterate_and_prune_ifaces(br, set_iface_policing, 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);
      memset(ea, 0xff, sizeof ea);
      for (i = 0; i < br->n_ports; i++) {
          struct port *port = br->ports[i];
 +        uint8_t iface_ea[ETH_ADDR_LEN];
 +        uint64_t iface_ea_u64;
 +        struct iface *iface;
 +
 +        /* Mirror output ports don't participate. */
          if (port->is_mirror_output_port) {
              continue;
          }
 -        for (j = 0; j < port->n_ifaces; j++) {
 -            struct iface *iface = port->ifaces[j];
 -            uint8_t iface_ea[ETH_ADDR_LEN];
 +
 +        /* Choose the MAC address to represent the port. */
 +        iface_ea_u64 = cfg_get_mac(0, "port.%s.mac", port->name);
 +        if (iface_ea_u64) {
 +            /* User specified explicitly. */
 +            eth_addr_from_uint64(iface_ea_u64, iface_ea);
 +
 +            /* Find the interface with this Ethernet address (if any) so that
 +             * we can provide the correct devname to the caller. */
 +            iface = NULL;
 +            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;
 +                }
 +            }
 +        } else {
 +            /* Choose the interface whose MAC address will represent the port.
 +             * The Linux kernel bonding code always chooses the MAC address of
 +             * the first slave added to a bond, and the Fedora networking
 +             * scripts always add slaves to a bond in alphabetical order, so
 +             * for compatibility we choose the interface with the name that is
 +             * first in alphabetical order. */
 +            iface = port->ifaces[0];
 +            for (j = 1; j < port->n_ifaces; j++) {
 +                struct iface *candidate = port->ifaces[j];
 +                if (strcmp(candidate->name, iface->name) < 0) {
 +                    iface = candidate;
 +                }
 +            }
 +
 +            /* The local port doesn't count (since we're trying to choose its
 +             * MAC address anyway).  Other internal ports don't count because
 +             * we really want a physical MAC if we can get it, and internal
 +             * ports typically have randomly generated MACs. */
              if (iface->dp_ifidx == ODPP_LOCAL
                  || cfg_get_bool(0, "iface.%s.internal", iface->name)) {
                  continue;
              }
-             error = netdev_nodev_get_etheraddr(iface->name, iface_ea);
 +
 +            /* Grab MAC. */
 -            if (!error) {
 -                if (!eth_addr_is_multicast(iface_ea) &&
 -                    !eth_addr_is_reserved(iface_ea) &&
 -                    !eth_addr_is_zero(iface_ea) &&
 -                    memcmp(iface_ea, ea, ETH_ADDR_LEN) < 0) {
 -                    memcpy(ea, iface_ea, ETH_ADDR_LEN);
 -                    *hw_addr_iface = iface;
 -                }
 -            } else {
+             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",
                              iface->name, strerror(error));
 +                continue;
              }
          }
-             memcpy(ea, iface_ea, ETH_ADDR_LEN);
-             *devname = iface ? iface->name : NULL;
 +
 +        /* Compare against our current choice. */
 +        if (!eth_addr_is_multicast(iface_ea) &&
 +            !eth_addr_is_reserved(iface_ea) &&
 +            !eth_addr_is_zero(iface_ea) &&
 +            memcmp(iface_ea, ea, ETH_ADDR_LEN) < 0)
 +        {
++            *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
@@@ -853,6 -835,26 +896,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
@@@ -895,7 -897,7 +958,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;
  }
@@@ -946,12 -948,12 +1009,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);
@@@ -988,27 -990,6 +1051,27 @@@ bridge_get_datapathid(const char *name
      return br ? ofproto_get_datapath_id(br->ofproto) : 0;
  }
  
 +/* Handle requests for a listing of all flows known by the OpenFlow
 + * stack, including those normally hidden. */
 +static void
 +bridge_unixctl_dump_flows(struct unixctl_conn *conn, const char *args)
 +{
 +    struct bridge *br;
 +    struct ds results;
 +    
 +    br = bridge_lookup(args);
 +    if (!br) {
 +        unixctl_command_reply(conn, 501, "Unknown bridge");
 +        return;
 +    }
 +
 +    ds_init(&results);
 +    ofproto_get_all_flows(br->ofproto, &results);
 +
 +    unixctl_command_reply(conn, 200, ds_cstr(&results));
 +    ds_destroy(&results);
 +}
 +
  static int
  bridge_run_one(struct bridge *br)
  {
@@@ -1043,13 -1024,29 +1106,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
@@@ -1202,9 -1185,8 +1267,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);
              }
          }
  
          if (probe < 5) {
              probe = cfg_get_int(0, "mgmt.inactivity-probe");
              if (probe < 5) {
 -                probe = 15;
 +                probe = 5;
              }
          }
          ofproto_set_probe_interval(br->ofproto, probe);
          if (!max_backoff) {
              max_backoff = cfg_get_int(0, "mgmt.max-backoff");
              if (!max_backoff) {
 -                max_backoff = 15;
 +                max_backoff = 8;
              }
          }
          ofproto_set_max_backoff(br->ofproto, max_backoff);
@@@ -1335,12 -1316,9 +1398,12 @@@ bridge_get_all_ifaces(const struct brid
              struct iface *iface = port->ifaces[j];
              svec_add(ifaces, iface->name);
          }
 +        if (port->n_ifaces > 1
 +            && cfg_get_bool(0, "bonding.%s.fake-iface", port->name)) {
 +            svec_add(ifaces, port->name);
 +        }
      }
 -    svec_sort(ifaces);
 -    assert(svec_is_unique(ifaces));
 +    svec_sort_unique(ifaces);
  }
  
  /* For robustness, in case the administrator moves around datapath ports behind
@@@ -1368,17 -1346,17 +1431,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;
@@@ -1435,7 -1413,6 +1498,7 @@@ choose_output_iface(const struct port *
                  return false;
              }
              e->iface_tag = tag_create_random();
 +            ((struct port *) port)->bond_compat_is_stale = true;
          }
          *tags |= e->iface_tag;
          iface = port->ifaces[e->iface_idx];
@@@ -1534,12 -1511,6 +1597,12 @@@ bond_run(struct bridge *br
  
      for (i = 0; i < br->n_ports; i++) {
          struct port *port = br->ports[i];
 +
 +        if (port->bond_compat_is_stale) {
 +            port->bond_compat_is_stale = false;
 +            port_update_bond_compat(port);
 +        }
 +
          if (port->n_ifaces < 2) {
              continue;
          }
@@@ -1988,7 -1959,6 +2051,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);
@@@ -2322,7 -2292,6 +2384,7 @@@ bond_rebalance_port(struct port *port
              } else {
                  from++;
              }
 +            port->bond_compat_is_stale = true;
          }
      }
  
@@@ -2589,7 -2558,6 +2651,7 @@@ bond_unixctl_migrate(struct unixctl_con
      ofproto_revalidate(port->bridge->ofproto, entry->iface_tag);
      entry->iface_idx = iface->port_ifidx;
      entry->iface_tag = tag_create_random();
 +    port->bond_compat_is_stale = true;
      unixctl_command_reply(conn, 200, "migrated");
  }
  
@@@ -2854,7 -2822,6 +2916,7 @@@ port_destroy(struct port *port
          size_t i;
  
          proc_net_compat_update_vlan(port->name, NULL, 0);
 +        proc_net_compat_update_bond(port->name, NULL);
  
          for (i = 0; i < MAX_MIRRORS; i++) {
              struct mirror *m = br->mirrors[i];
@@@ -2921,7 -2888,7 +2983,7 @@@ port_update_bonding(struct port *port
          if (port->bond_hash) {
              free(port->bond_hash);
              port->bond_hash = NULL;
 -            proc_net_compat_update_bond(port->name, NULL);
 +            port->bond_compat_is_stale = true;
          }
      } else {
          if (!port->bond_hash) {
              port->no_ifaces_tag = tag_create_random();
              bond_choose_active_iface(port);
          }
 -        port_update_bond_compat(port);
 +        port->bond_compat_is_stale = true;
      }
  }
  
  static void
  port_update_bond_compat(struct port *port)
  {
 +    struct compat_bond_hash compat_hashes[BOND_MASK + 1];
      struct compat_bond bond;
      size_t i;
  
      if (port->n_ifaces < 2) {
 +        proc_net_compat_update_bond(port->name, NULL);
          return;
      }
  
      bond.up = false;
      bond.updelay = port->updelay;
      bond.downdelay = port->downdelay;
 +
 +    bond.n_hashes = 0;
 +    bond.hashes = compat_hashes;
 +    if (port->bond_hash) {
 +        const struct bond_entry *e;
 +        for (e = port->bond_hash; e <= &port->bond_hash[BOND_MASK]; e++) {
 +            if (e->iface_idx >= 0 && e->iface_idx < port->n_ifaces) {
 +                struct compat_bond_hash *cbh = &bond.hashes[bond.n_hashes++];
 +                cbh->hash = e - port->bond_hash;
 +                cbh->netdev_name = port->ifaces[e->iface_idx]->name;
 +            }
 +        }
 +    }
 +
      bond.n_slaves = port->n_ifaces;
      bond.slaves = xmalloc(port->n_ifaces * sizeof *bond.slaves);
      for (i = 0; i < port->n_ifaces; i++) {
          if (slave->up) {
              bond.up = true;
          }
-         memcpy(slave->mac, iface->mac, ETH_ADDR_LEN);
+         netdev_get_etheraddr(iface->netdev, slave->mac);
      }
 +
      proc_net_compat_update_bond(port->name, &bond);
      free(bond.slaves);
  }
@@@ -3011,7 -2961,8 +3073,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)) {
@@@ -3037,18 -2988,7 +3100,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);
  }
  
@@@ -3081,6 -3020,7 +3132,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);
      }
  }
@@@ -3429,23 -3368,25 +3480,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 vswitchd/ovs-brcompatd.c
@@@ -38,7 -38,6 +38,6 @@@
  #include "coverage.h"
  #include "daemon.h"
  #include "dirs.h"
- #include "dpif.h"
  #include "dynamic-string.h"
  #include "fatal-signal.h"
  #include "fault.h"
@@@ -240,14 -239,21 +239,14 @@@ rewrite_and_reload_config(void
      return 0;
  }
  
 -/* Get all the interfaces for 'bridge' as 'ifaces', breaking bonded interfaces
 - * down into their constituent parts.
 - *
 - * If 'vlan' < 0, all interfaces on 'bridge' are reported.  If 'vlan' == 0,
 - * then only interfaces for trunk ports or ports with implicit VLAN 0 are
 - * reported.  If 'vlan' > 0, only interfaces with implict VLAN 'vlan' are
 - * reported.  */
  static void
 -get_bridge_ifaces(const char *bridge, struct svec *ifaces, int vlan)
 +do_get_bridge_parts(const char *bridge, struct svec *parts, int vlan,
 +                    bool break_down_bonds)
  {
      struct svec ports;
      int i;
  
      svec_init(&ports);
 -    svec_init(ifaces);
      cfg_get_all_keys(&ports, "bridge.%s.port", bridge);
      for (i = 0; i < ports.n; i++) {
          const char *port_name = ports.names[i];
                  continue;
              }
          }
 -        if (cfg_has_section("bonding.%s", port_name)) {
 +        if (break_down_bonds && cfg_has_section("bonding.%s", port_name)) {
              struct svec slaves;
              svec_init(&slaves);
              cfg_get_all_keys(&slaves, "bonding.%s.slave", port_name);
 -            svec_append(ifaces, &slaves);
 +            svec_append(parts, &slaves);
              svec_destroy(&slaves);
          } else {
 -            svec_add(ifaces, port_name);
 +            svec_add(parts, port_name);
          }
      }
      svec_destroy(&ports);
  }
  
 +/* Add all the interfaces for 'bridge' to 'ifaces', breaking bonded interfaces
 + * down into their constituent parts.
 + *
 + * If 'vlan' < 0, all interfaces on 'bridge' are reported.  If 'vlan' == 0,
 + * then only interfaces for trunk ports or ports with implicit VLAN 0 are
 + * reported.  If 'vlan' > 0, only interfaces with implicit VLAN 'vlan' are
 + * reported.  */
 +static void
 +get_bridge_ifaces(const char *bridge, struct svec *ifaces, int vlan)
 +{
 +    do_get_bridge_parts(bridge, ifaces, vlan, true);
 +}
 +
 +/* Add all the ports for 'bridge' to 'ports'.  Bonded ports are reported under
 + * the bond name, not broken down into their constituent interfaces.
 + *
 + * If 'vlan' < 0, all ports on 'bridge' are reported.  If 'vlan' == 0, then
 + * only trunk ports or ports with implicit VLAN 0 are reported.  If 'vlan' > 0,
 + * only port with implicit VLAN 'vlan' are reported.  */
 +static void
 +get_bridge_ports(const char *bridge, struct svec *ports, int vlan)
 +{
 +    do_get_bridge_parts(bridge, ports, vlan, false);
 +}
 +
  /* Go through the configuration file and remove any ports that no longer
   * exist associated with a bridge. */
  static void
  prune_ports(void)
  {
      int i, j;
-     int error;
      struct svec bridges, delete;
  
      if (cfg_lock(NULL, 0)) {
          struct svec ifaces;
  
          /* Check that each bridge interface exists. */
 +        svec_init(&ifaces);
          get_bridge_ifaces(br_name, &ifaces, -1);
          for (j = 0; j < ifaces.n; j++) {
              const char *iface_name = ifaces.names[j];
-             enum netdev_flags flags;
  
              /* The local port and internal ports are created and destroyed by
               * ovs-vswitchd itself, so don't bother checking for them at all.
                  continue;
              }
  
-             error = netdev_nodev_get_flags(iface_name, &flags);
-             if (error == ENODEV) {
+             if (!netdev_exists(iface_name)) {
                  VLOG_INFO_RL(&rl, "removing dead interface %s from %s",
                               iface_name, br_name);
                  svec_add(&delete, iface_name);
-             } else if (error) {
-                 VLOG_INFO_RL(&rl, "unknown error %d on interface %s from %s",
-                              error, iface_name, br_name);
              }
          }
          svec_destroy(&ifaces);
      svec_destroy(&delete);
  }
  
- /* Checks whether a network device named 'name' exists and returns true if so,
-  * false otherwise.
-  *
-  * XXX it is possible that this doesn't entirely accomplish what we want in
-  * context, since ovs-vswitchd.conf may cause vswitchd to create or destroy
-  * network devices based on iface.*.internal settings.
-  *
-  * XXX may want to move this to lib/netdev.
-  *
-  * XXX why not just use netdev_nodev_get_flags() or similar function? */
- static bool
- netdev_exists(const char *name)
- {
-     struct stat s;
-     char *filename;
-     int error;
-     filename = xasprintf("/sys/class/net/%s", name);
-     error = stat(filename, &s);
-     free(filename);
-     return !error;
- }
  static int
  add_bridge(const char *br_name)
  {
@@@ -431,7 -382,7 +401,7 @@@ parse_command(struct ofpbuf *buffer, ui
                const char **port_name, uint64_t *count, uint64_t *skip)
  {
      static const struct nl_policy policy[] = {
 -        [BRC_GENL_A_DP_NAME] = { .type = NL_A_STRING },
 +        [BRC_GENL_A_DP_NAME] = { .type = NL_A_STRING, .optional = true },
          [BRC_GENL_A_PORT_NAME] = { .type = NL_A_STRING, .optional = true },
          [BRC_GENL_A_FDB_COUNT] = { .type = NL_A_U64, .optional = true },
          [BRC_GENL_A_FDB_SKIP] = { .type = NL_A_U64, .optional = true },
  
      if (!nl_policy_parse(buffer, NLMSG_HDRLEN + GENL_HDRLEN, policy,
                           attrs, ARRAY_SIZE(policy))
 +        || (br_name && !attrs[BRC_GENL_A_DP_NAME])
          || (port_name && !attrs[BRC_GENL_A_PORT_NAME])
          || (count && !attrs[BRC_GENL_A_FDB_COUNT])
          || (skip && !attrs[BRC_GENL_A_FDB_SKIP])) {
      }
  
      *seq = ((struct nlmsghdr *) buffer->data)->nlmsg_seq;
 -    *br_name = nl_attr_get_string(attrs[BRC_GENL_A_DP_NAME]);
 +    if (br_name) {
 +        *br_name = nl_attr_get_string(attrs[BRC_GENL_A_DP_NAME]);
 +    }
      if (port_name) {
          *port_name = nl_attr_get_string(attrs[BRC_GENL_A_PORT_NAME]);
      }
      return 0;
  }
  
 -static void
 -send_reply(uint32_t seq, int error, struct ofpbuf *fdb_query_data)
 +/* Composes and returns a reply to a request made by the datapath with Netlink
 + * sequence number 'seq' and error code 'error'.  The caller may add additional
 + * attributes to the message, then it may send it with send_reply(). */
 +static struct ofpbuf *
 +compose_reply(uint32_t seq, int error)
  {
 -    struct ofpbuf msg;
 -    int retval;
 -
 -    /* Compose reply. */
 -    ofpbuf_init(&msg, 0);
 -    nl_msg_put_genlmsghdr(&msg, brc_sock, 32, brc_family, NLM_F_REQUEST,
 +    struct ofpbuf *reply = ofpbuf_new(4096);
 +    nl_msg_put_genlmsghdr(reply, brc_sock, 32, brc_family, NLM_F_REQUEST,
                            BRC_GENL_C_DP_RESULT, 1);
 -    ((struct nlmsghdr *) msg.data)->nlmsg_seq = seq;
 -    nl_msg_put_u32(&msg, BRC_GENL_A_ERR_CODE, error);
 -    if (fdb_query_data) {
 -        nl_msg_put_unspec(&msg, BRC_GENL_A_FDB_DATA,
 -                          fdb_query_data->data, fdb_query_data->size);
 -    }
 +    ((struct nlmsghdr *) reply->data)->nlmsg_seq = seq;
 +    nl_msg_put_u32(reply, BRC_GENL_A_ERR_CODE, error);
 +    return reply;
 +}
  
 -    /* Send reply. */
 -    retval = nl_sock_send(brc_sock, &msg, false);
 +/* Sends 'reply' to the datapath and frees it. */
 +static void
 +send_reply(struct ofpbuf *reply)
 +{
 +    int retval = nl_sock_send(brc_sock, reply, false);
      if (retval) {
          VLOG_WARN_RL(&rl, "replying to brcompat request: %s",
                       strerror(retval));
      }
 -    ofpbuf_uninit(&msg);
 +    ofpbuf_delete(reply);
 +}
 +
 +/* Composes and sends a reply to a request made by the datapath with Netlink
 + * sequence number 'seq' and error code 'error'. */
 +static void
 +send_simple_reply(uint32_t seq, int error)
 +{
 +    send_reply(compose_reply(seq, error));
  }
  
  static int
@@@ -510,7 -450,7 +480,7 @@@ handle_bridge_cmd(struct ofpbuf *buffer
          if (!error) {
              error = rewrite_and_reload_config();
          }
 -        send_reply(seq, error, NULL);
 +        send_simple_reply(seq, error);
      }
      return error;
  }
@@@ -555,7 -495,7 +525,7 @@@ handle_port_cmd(struct ofpbuf *buffer, 
              VLOG_INFO("%s %s %s: success", cmd_name, br_name, port_name);
              error = rewrite_and_reload_config();
          }
 -        send_reply(seq, error, NULL);
 +        send_simple_reply(seq, error);
      }
  
      return error;
@@@ -582,33 -522,6 +552,33 @@@ get_bridge_containing_port(const char *
      return xmemdup0(start, end - start);
  }
  
 +static int
 +linux_bridge_to_ovs_bridge(const char *linux_bridge,
 +                           char **ovs_bridge, int *br_vlan)
 +{
 +    if (bridge_exists(linux_bridge)) {
 +        /* Bridge name is the same.  We are interested in VLAN 0. */
 +        *ovs_bridge = xstrdup(linux_bridge);
 +        *br_vlan = 0;
 +        return 0;
 +    } else {
 +        /* No such Open vSwitch bridge 'linux_bridge', but there might be an
 +         * internal port named 'linux_bridge' on some other bridge
 +         * 'ovs_bridge'.  If so then we are interested in the VLAN assigned to
 +         * port 'linux_bridge' on the bridge named 'ovs_bridge'. */
 +        const char *port_name = linux_bridge;
 +
 +        *ovs_bridge = get_bridge_containing_port(port_name);
 +        *br_vlan = cfg_get_vlan(0, "vlan.%s.tag", port_name);
 +        if (*ovs_bridge && *br_vlan >= 0) {
 +            return 0;
 +        } else {
 +            free(*ovs_bridge);
 +            return ENODEV;
 +        }
 +    }
 +}
 +
  static int
  handle_fdb_query_cmd(struct ofpbuf *buffer)
  {
      struct svec ifaces;
  
      struct ofpbuf query_data;
 +    struct ofpbuf *reply;
      char *unixctl_command;
      uint64_t count, skip;
      char *output;
  
      /* Figure out vswitchd bridge and VLAN. */
      cfg_read();
 -    if (bridge_exists(linux_bridge)) {
 -        /* Bridge name is the same.  We are interested in VLAN 0. */
 -        ovs_bridge = xstrdup(linux_bridge);
 -        br_vlan = 0;
 -    } else {
 -        /* No such Open vSwitch bridge 'linux_bridge', but there might be an
 -         * internal port named 'linux_bridge' on some other bridge
 -         * 'ovs_bridge'.  If so then we are interested in the VLAN assigned to
 -         * port 'linux_bridge' on the bridge named 'ovs_bridge'. */
 -        const char *port_name = linux_bridge;
 -
 -        ovs_bridge = get_bridge_containing_port(port_name);
 -        br_vlan = cfg_get_vlan(0, "vlan.%s.tag", port_name);
 -        if (!ovs_bridge || br_vlan < 0) {
 -            free(ovs_bridge);
 -            send_reply(seq, ENODEV, NULL);
 -            return error;
 -        }
 +    error = linux_bridge_to_ovs_bridge(linux_bridge, &ovs_bridge, &br_vlan);
 +    if (error) {
 +        send_simple_reply(seq, error);
 +        return error;
      }
  
      /* Fetch the forwarding database using ovs-appctl. */
      free(unixctl_command);
      if (error) {
          free(ovs_bridge);
 -        send_reply(seq, error, NULL);
 +        send_simple_reply(seq, error);
          return error;
      }
  
      /* Fetch the MAC address for each interface on the bridge, so that we can
       * fill in the is_local field in the response. */
 +    svec_init(&ifaces);
      get_bridge_ifaces(ovs_bridge, &ifaces, br_vlan);
      local_macs = xmalloc(ifaces.n * sizeof *local_macs);
      n_local_macs = 0;
      for (i = 0; i < ifaces.n; i++) {
          const char *iface_name = ifaces.names[i];
          struct mac *mac = &local_macs[n_local_macs];
-         if (!netdev_nodev_get_etheraddr(iface_name, mac->addr)) {
-             n_local_macs++;
+         struct netdev *netdev;
+         error = netdev_open(iface_name, NETDEV_ETH_TYPE_NONE, &netdev);
+         if (netdev) {
+             if (!netdev_get_etheraddr(netdev, mac->addr)) {
+                 n_local_macs++;
+             }
+             netdev_close(netdev);
          }
      }
      svec_destroy(&ifaces);
      }
      free(output);
  
 -    send_reply(seq, 0, &query_data);
 +    /* Compose and send reply to datapath. */
 +    reply = compose_reply(seq, 0);
 +    nl_msg_put_unspec(reply, BRC_GENL_A_FDB_DATA,
 +                      query_data.data, query_data.size);
 +    send_reply(reply);
 +
 +    /* Free memory. */
      ofpbuf_uninit(&query_data);
      free(ovs_bridge);
  
      return 0;
  }
  
 +static void
 +send_ifindex_reply(uint32_t seq, struct svec *ifaces)
 +{
 +    struct ofpbuf *reply;
 +    const char *iface;
 +    size_t n_indices;
 +    int *indices;
 +    size_t i;
 +
 +    /* Make sure that any given interface only occurs once.  This shouldn't
 +     * happen, but who knows what people put into their configuration files. */
 +    svec_sort_unique(ifaces);
 +
 +    /* Convert 'ifaces' into ifindexes. */
 +    n_indices = 0;
 +    indices = xmalloc(ifaces->n * sizeof *indices);
 +    SVEC_FOR_EACH (i, iface, ifaces) {
 +        int ifindex = if_nametoindex(iface);
 +        if (ifindex) {
 +            indices[n_indices++] = ifindex;
 +        }
 +    }
 +
 +    /* Compose and send reply. */
 +    reply = compose_reply(seq, 0);
 +    nl_msg_put_unspec(reply, BRC_GENL_A_IFINDEXES,
 +                      indices, n_indices * sizeof *indices);
 +    send_reply(reply);
 +
 +    /* Free memory. */
 +    free(indices);
 +}
 +
 +static int
 +handle_get_bridges_cmd(struct ofpbuf *buffer)
 +{
 +    struct svec bridges;
 +    const char *br_name;
 +    size_t i;
 +
 +    uint32_t seq;
 +
 +    int error;
 +
 +    /* Parse Netlink command.
 +     *
 +     * The command doesn't actually have any arguments, but we need the
 +     * sequence number to send the reply. */
 +    error = parse_command(buffer, &seq, NULL, NULL, NULL, NULL);
 +    if (error) {
 +        return error;
 +    }
 +
 +    /* Get all the real bridges and all the fake ones. */
 +    cfg_read();
 +    cfg_get_subsections(&bridges, "bridge");
 +    SVEC_FOR_EACH (i, br_name, &bridges) {
 +        const char *iface_name;
 +        struct svec ifaces;
 +        size_t j;
 +
 +        svec_init(&ifaces);
 +        get_bridge_ifaces(br_name, &ifaces, -1);
 +        SVEC_FOR_EACH (j, iface_name, &ifaces) {
 +            if (cfg_get_bool(0, "iface.%s.fake-bridge", iface_name)) {
 +                svec_add(&bridges, iface_name);
 +            }
 +        }
 +        svec_destroy(&ifaces);
 +    }
 +
 +    send_ifindex_reply(seq, &bridges);
 +    svec_destroy(&bridges);
 +
 +    return 0;
 +}
 +
 +static int
 +handle_get_ports_cmd(struct ofpbuf *buffer)
 +{
 +    uint32_t seq;
 +
 +    const char *linux_bridge;
 +    char *ovs_bridge;
 +    int br_vlan;
 +
 +    struct svec ports;
 +
 +    int error;
 +
 +    /* Parse Netlink command. */
 +    error = parse_command(buffer, &seq, &linux_bridge, NULL, NULL, NULL);
 +    if (error) {
 +        return error;
 +    }
 +
 +    cfg_read();
 +    error = linux_bridge_to_ovs_bridge(linux_bridge, &ovs_bridge, &br_vlan);
 +    if (error) {
 +        send_simple_reply(seq, error);
 +        return error;
 +    }
 +
 +    svec_init(&ports);
 +    get_bridge_ports(ovs_bridge, &ports, br_vlan);
 +    send_ifindex_reply(seq, &ports); /* XXX bonds won't show up */
 +    svec_destroy(&ports);
 +
 +    free(ovs_bridge);
 +
 +    return 0;
 +}
 +
  static int
  brc_recv_update(void)
  {
          retval = handle_fdb_query_cmd(buffer);
          break;
  
 +    case BRC_GENL_C_GET_BRIDGES:
 +        retval = handle_get_bridges_cmd(buffer);
 +        break;
 +
 +    case BRC_GENL_C_GET_PORTS:
 +        retval = handle_get_ports_cmd(buffer);
 +        break;
 +
      default:
          retval = EPROTO;
      }
@@@ -992,7 -796,6 +968,6 @@@ rtnl_recv_update(void
              char br_name[IFNAMSIZ];
              uint32_t br_idx = nl_attr_get_u32(attrs[IFLA_MASTER]);
              struct svec ports;
-             enum netdev_flags flags;
  
              if (!if_indextoname(br_idx, br_name)) {
                  ofpbuf_delete(buf);
                  return;
              }
  
-             if (netdev_nodev_get_flags(port_name, &flags) == ENODEV) {
+             if (!netdev_exists(port_name)) {
                  /* Network device is really gone. */
                  VLOG_INFO("network device %s destroyed, "
                            "removing from bridge %s", port_name, br_name);
@@@ -1102,14 -905,12 +1077,15 @@@ main(int argc, char *argv[]
          }
      }
  
 -    cfg_read();
 +    retval = cfg_read();
 +    if (retval) {
 +        ovs_fatal(retval, "could not read config file");
 +    }
  
      for (;;) {
          unixctl_server_run(unixctl);
          brc_recv_update();
+         netdev_run();
  
          /* If 'prune_timeout' is non-zero, we actively prune from the
           * config file any 'bridge.<br_name>.port' entries that are no 
  
          nl_sock_wait(brc_sock, POLLIN);
          unixctl_server_wait(unixctl);
+         netdev_wait();
          poll_block();
      }
  
@@@ -1238,7 -1040,6 +1215,7 @@@ parse_options(int argc, char *argv[]
                  "use --help for usage");
      }
  
 +    cfg_init();
      config_file = argv[0];
      error = cfg_set_file(config_file);
      if (error) {
diff --combined vswitchd/ovs-vswitchd.c
  #include "command-line.h"
  #include "compiler.h"
  #include "daemon.h"
+ #include "dpif.h"
  #include "fault.h"
  #include "leak-checker.h"
  #include "mgmt.h"
+ #include "netdev.h"
  #include "ovs-vswitchd.h"
  #include "poll-loop.h"
- #include "port.h"
  #include "proc-net-compat.h"
  #include "process.h"
  #include "signals.h"
@@@ -80,13 -81,9 +81,12 @@@ main(int argc, char *argv[]
      }
      unixctl_command_register("vswitchd/reload", reload);
  
 -    cfg_read();
 +    retval = cfg_read();
 +    if (retval) {
 +        ovs_fatal(retval, "could not read config file");
 +    }
      mgmt_init();
      bridge_init();
-     port_init();
      mgmt_reconfigure();
  
      need_reconfigure = false;
              need_reconfigure = true;
          }
          unixctl_server_run(unixctl);
+         dp_run();
+         netdev_run();
  
          if (need_reconfigure) {
              poll_immediate_wake();
          mgmt_wait();
          bridge_wait();
          unixctl_server_wait(unixctl);
+         dp_wait();
+         netdev_wait();
          poll_block();
      }
  
@@@ -133,7 -134,6 +137,6 @@@ reconfigure(void
      cfg_read();
      bridge_reconfigure();
      mgmt_reconfigure();
-     port_reconfigure();
  
      for (i = 0; i < n_conns; i++) {
          unixctl_command_reply(conns[i], 202, NULL);
@@@ -222,7 -222,6 +225,7 @@@ parse_options(int argc, char *argv[]
                  "use --help for usage");
      }
  
 +    cfg_init();
      config_file = argv[0];
      error = cfg_set_file(config_file);
      if (error) {
  static void
  usage(void)
  {
-     printf("%s: virtual switch daemon\n"
+     printf("%s: Open vSwitch daemon\n"
             "usage: %s [OPTIONS] CONFIG\n"
             "CONFIG is a configuration file in ovs-vswitchd.conf(5) format.\n",
             program_name, program_name);
  .  RE
  .  PP
  ..
- .TH ovs\-vswitchd.conf 5 "April 2009" "Open vSwitch" "OpenVSwitch Manual"
+ .TH ovs\-vswitchd.conf 5 "June 2009" "Open vSwitch" "Open vSwitch Manual"
  .
  .SH NAME
  ovs\-vswitchd.conf \- configuration file for \fBovs\-vswitchd\fR
  .
  .SH DESCRIPTION
  This manual page describes the syntax for the configuration file used 
- by \fBovs\-vswitchd\fR(8), the virtual switch daemon.
+ by \fBovs\-vswitchd\fR(8), the Open vSwitch daemon.
  .PP
  The configuration file is based on key-value pairs, which are given
  one per line in the form \fIkey\fB=\fIvalue\fR.  Each \fIkey\fR
@@@ -50,14 -50,13 +50,13 @@@ configure \fBovs\-vswitchd\fR
  .SS "Bridge Configuration"
  A bridge (switch) with a given \fIname\fR is configured by specifying
  the names of its network devices as values for key
- \fBbridge.\fIname\fB.port\fR.  (The specified \fIname\fR may not begin
- with \fBdp\fR or \fBnl:\fR followed by a digit.)
+ \fBbridge.\fIname\fB.port\fR.
  .PP
  The names given on \fBbridge.\fIname\fB.port\fR must be the names of
  existing network devices, except for ``internal ports.''  An internal
  port is a simulated network device that receives traffic only
- through the virtual switch and switches any traffic sent it through
virtual switch.  An internal port may configured with an IP address,
+ through the switch and switches any traffic sent it through the
+ switch.  An internal port may configured with an IP address,
  etc. using the usual system tools (e.g. \fBifconfig\fR, \fBip\fR).  To
  designate network device \fInetdev\fR as an internal port, add
  \fBiface.\fInetdev\fB.internal=true\fR to the configuration file.
@@@ -72,23 -71,11 +71,23 @@@ in the bridge, by specifying it as one 
  included, then its MAC address is by default the lowest-numbered MAC
  address among the other bridge ports, ignoring other internal ports
  and bridge ports that are
 -used as port mirroring destinations (see \fBPort Mirroring\fR, below).  To
 -use a specific MAC address instead, set \fBbridge.\fIname\fB.mac\fR to
 -a MAC address in the format
 +used as port mirroring destinations (see \fBPort Mirroring\fR, below).
 +For this purpose, the MAC of a bonded port (see \fBNetwork Device
 +Bonding\fR, below) is by default the MAC of its slave whose name is first in
 +alphabetical order.
 +There are two ways to modify this algorithm for selecting the MAC
 +address of the local port:
 +.IP \(bu
 +To use a specific MAC address for the local port, set
 +\fBbridge.\fIname\fB.mac\fR to a MAC address in the format
  \fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fR, where each
 -\fIx\fR is a hex digit.  If no valid MAC address can be determined
 +\fIx\fR is a hex digit.
 +.IP \(bu
 +To override the MAC of a port for the purpose of this algorithm, set
 +\fBport.\fIport\fB.mac\fR to a MAC address in the format described
 +above.
 +.PP
 +If no valid MAC address can be determined
  either of these ways, then a MAC address is randomly generated.
  .PP
  The following syntax defines a bridge named \fBmybr\fR, configured
@@@ -339,7 -326,7 +338,7 @@@ This can be overridden with the \fBnetf
  \fBnetflow.\fIbridge\fB.engine-id\fR, respectively.  Each takes a value
  between 0 and 255, inclusive. 
  
- Many NetFlow collectors do not expect multiple virtual switches to be
+ Many NetFlow collectors do not expect multiple switches to be
  sending messages from the same host, and they do not store the engine
  information which could be used to disambiguate the traffic.  To prevent
  flows from multiple switches appearing as if they came on the interface,
@@@ -376,7 -363,7 +375,7 @@@ The specified TCP \fIport\fR (default: 
  .PP
  The maximum time between attempts to connect to the controller may be
  specified in integral seconds with the \fBmgmt.max-backoff\fR key.  The
 -default maximum backoff is 15 seconds, and the minimum value is 1
 +default maximum backoff is 8 seconds, and the minimum value is 1
  second.
  
  An inactivity probe may be configured with the \fBmgmt.inactivity-probe\fR
@@@ -384,7 -371,7 +383,7 @@@ key.  If \fBovs\-vswitchd\fR does not c
  specified number of seconds, it will send a probe.  If a response is not
  received for an additional amount of that time, \fBovs\-vswitchd\fR assumes
  the connection has been broken and attempts to reconnect.  The default
 -is 15 seconds, and the minimum value is 5 seconds.
 +and minimum values are both 5 seconds.
  
  A management id may be specified with the \fBmgmt.id\fR key.  It takes
  an id in the form of exactly 12 hexadecimal digits.  If one is not
@@@ -413,7 -400,7 +412,7 @@@ switch will perform all configured brid
  .TP
  \fBdiscover\fR
  Use controller discovery to find the local OpenFlow controller.
- Refer to \fBsecchan\fR(8) for information on how to configure a DHCP
+ Refer to \fB\ovs\-openflowd\fR(8) for information on how to configure a DHCP
  server to support controller discovery.  The following additional
  options control the discovery process:
  .
@@@ -426,8 -413,8 +425,8 @@@ the regular expression will be accepted
  .IP
  The default regular expression is \fBssl:.*\fR, meaning that only SSL
  controller connections will be accepted, when SSL is configured (see
- \fBSSL Configuration\fR), and \fB.*\fR otherwise, meaning that any
controller will be accepted.
+ \fBSSL Configuration\fR), and \fBtcp:.*\fR otherwise, meaning that only
TCP controller connections will be accepted.
  .IP
  The regular expression is implicitly anchored at the beginning of the
  controller location string, as if it begins with \fB^\fR.
@@@ -472,7 -459,7 +471,7 @@@ not in use, the following additional se
  By default, or if this is set to \fBtrue\fR, \fBovs\-vswitchd\fR connects
  to the controller in-band.  If this is set to \fBfalse\fR,
  \fBovs\-vswitchd\fR connects to the controller out-of-band.  Refer to
- \fBsecchan\fR(8) for a description of in-band and out-of-band control.
+ \fBovs\-openflowd\fR(8) for a description of in-band and out-of-band control.
  .IP "\fBbridge.\fIname\fB.controller.ip=\fIip\fR"
  If specified, the IP address to configure on the bridge's local port.
  .IP "\fBbridge.\fIname\fB.controller.netmask=\fInetmask\fR"
@@@ -490,11 -477,11 +489,11 @@@ This optional setting may be set to \fI
  The minimum value of \fIsecs\fR is 5 seconds.  The default is taken
  from \fBmgmt.inactivity-probe\fR (see above).
  .IP
- When the virtual switch is connected to the controller, it waits for a
+ When the switch is connected to the controller, it waits for a
  message to be received from the controller for \fIsecs\fR seconds
  before it sends a inactivity probe to the controller.  After sending
  the inactivity probe, if no response is received for an additional
- \fIsecs\fR seconds, the secure channel assumes that the connection has
+ \fIsecs\fR seconds, \fBovs-vswitchd\fR assumes that the connection has
  been broken and attempts to reconnect.
  .IP
  Changing the inactivity probe interval also changes the interval
@@@ -502,7 -489,7 +501,7 @@@ before entering standalone mode (see be
  .IP "\fBbridge.\fIname\fB.controller.fail-mode=\fBstandalone\fR|\fBsecure\fR"
  .IQ "\fBmgmt.fail-mode=standalone\fR|\fBsecure\fR"
  When a controller is configured, it is, ordinarily, responsible for
- setting up all flows on the virtual switch.  Thus, if the connection to
+ setting up all flows on the switch.  Thus, if the connection to
  the controller fails, no new network connections can be set up.  If
  the connection to the controller stays down long enough, no packets
  can pass through the switch at all.
@@@ -527,7 -514,7 +526,7 @@@ connection attempts starts at 1 second 
  attempt until it reaches the maximum.  The default maximum backoff
  time is taken from \fBmgmt.max-backoff\fR.
  .ST "Controller Rate-Limiting"
- These settings configure how the virtual switch applies a ``token
+ These settings configure how the switch applies a ``token
  bucket'' to limit the rate at which packets in unknown flows are
  forwarded to the OpenFlow controller for flow-setup processing.  This
  feature prevents a single bridge from overwhelming a controller.
@@@ -580,24 -567,23 +579,23 @@@ When \fBovs\-vswitchd\fR is configured 
  for controller connectivity, the following settings are required:
  .TP
  \fBssl.private-key=\fIprivkey.pem\fR
- Specifies a PEM file containing the private key used as the virtual
+ Specifies a PEM file containing the private key used as the 
  switch's identity for SSL connections to the controller.
  .TP
  \fBssl.certificate=\fIcert.pem\fR
  Specifies a PEM file containing a certificate, signed by the
  certificate authority (CA) used by the controller and manager, that
- certifies the virtual switch's private key, identifying a trustworthy
+ certifies the switch's private key, identifying a trustworthy
  switch.
  .TP
  \fBssl.ca-cert=\fIcacert.pem\fR
  Specifies a PEM file containing the CA certificate used to verify that
- the virtual switch is connected to a trustworthy controller.
+ the switch is connected to a trustworthy controller.
  .PP
  These files are read only once, at \fBovs\-vswitchd\fR startup time.  If
  their contents change, \fBovs\-vswitchd\fR must be killed and restarted.
  .PP
- These SSL settings apply to all SSL connections made by the virtual
- switch.
+ These SSL settings apply to all SSL connections made by the switch.
  .ST "CA Certificate Bootstrap"
  Ordinarily, all of the files named in the SSL configuration must exist
  when \fBovs\-vswitchd\fR starts.  However, if \fBssl.bootstrap-ca-cert\fR
@@@ -635,8 -621,11 +633,11 @@@ Listens for connections on the Unix dom
  Listens for SSL connections on \fIport\fR (default: 6633).  SSL must
  be configured when this form is used (see \fBSSL Configuration\fR,
  above).
- .IP "\fBptcp:\fR[\fIport\fR]"
+ .IP "\fBptcp:\fR[\fIport\fR][\fB:\fIip\fR]"
  Listens for TCP connections on \fIport\fR (default: 6633).
+ By default, \fB\ovs\-vswitchd\fR listens for connections to any local
+ IP address, but \fIip\fR may be specified to limit connections to the
+ specified local \fIip\fR.
  .RE
  To entirely disable listening for management connections, set
  \fBbridge.\fIname\fB.openflow.listeners\fR to the single value
@@@ -1,6 -1,6 +1,6 @@@
  #!/usr/bin/python
  #
 -# Copyright (c) Citrix Systems 2008. All rights reserved.
 +# Copyright (c) 2008,2009 Citrix Systems, Inc. All rights reserved.
  # Copyright (c) 2009 Nicira Networks.
  #
  """Usage:
@@@ -62,8 -62,8 +62,9 @@@ import syslo
  import traceback
  import time
  import re
- import pickle
 +import random
+ from xml.dom.minidom import getDOMImplementation
+ from xml.dom.minidom import parse as parseXML
  
  output_directory = None
  
@@@ -249,38 -249,215 +250,242 @@@ def check_allowed(pif)
  def interface_exists(i):
      return os.path.exists("/sys/class/net/" + i)
  
 +def get_netdev_mac(device):
 +    try:
 +        return read_first_line_of_file("/sys/class/net/%s/address" % device)
 +    except:
 +        # Probably no such device.
 +        return None
 +
 +def get_netdev_tx_queue_len(device):
 +    try:
 +        return int(read_first_line_of_file("/sys/class/net/%s/tx_queue_len"
 +                                           % device))
 +    except:
 +        # Probably no such device.
 +        return None
 +
 +def get_netdev_by_mac(mac):
 +    maybe = None
 +    for device in os.listdir("/sys/class/net"):
 +        dev_mac = get_netdev_mac(device)
 +        if dev_mac and mac.lower() == dev_mac.lower():
 +            if get_netdev_tx_queue_len(device):
 +                return device
 +            if not maybe:
 +                # Probably a datapath internal port.
 +                maybe = device
 +    return maybe
 +
+ #
+ # Helper functions for encoding/decoding database attributes to/from XML.
+ #
+ def str_to_xml(xml, parent, tag, val):
+     e = xml.createElement(tag)
+     parent.appendChild(e)
+     v = xml.createTextNode(val)
+     e.appendChild(v)
+ def str_from_xml(n):
+     def getText(nodelist):
+         rc = ""
+         for node in nodelist:
+             if node.nodeType == node.TEXT_NODE:
+                 rc = rc + node.data
+         return rc
+     return getText(n.childNodes).strip()
+ def bool_to_xml(xml, parent, tag, val):
+     if val:
+         str_to_xml(xml, parent, tag, "True")
+     else:
+         str_to_xml(xml, parent, tag, "False")
+ def bool_from_xml(n):
+     s = str_from_xml(n)
+     if s == "True":
+         return True
+     elif s == "False":
+         return False
+     else:
+         raise Error("Unknown boolean value %s" % s);
+ def strlist_to_xml(xml, parent, ltag, itag, val):
+     e = xml.createElement(ltag)
+     parent.appendChild(e)
+     for v in val:
+         c = xml.createElement(itag)
+         e.appendChild(c)
+         cv = xml.createTextNode(v)
+         c.appendChild(cv)
+ def strlist_from_xml(n, ltag, itag):
+     ret = []
+     for n in n.childNodes:
+         if n.nodeName == itag:
+             ret.append(str_from_xml(n))
+     return ret
+ def otherconfig_to_xml(xml, parent, val, attrs):
+     otherconfig = xml.createElement("other_config")
+     parent.appendChild(otherconfig)
+     for n,v in val.items():
+         if not n in attrs:
+             raise Error("Unknown other-config attribute: %s" % n)
+         str_to_xml(xml, otherconfig, n, v)
+ def otherconfig_from_xml(n, attrs):
+     ret = {}
+     for n in n.childNodes:
+         if n.nodeName in attrs:
+             ret[n.nodeName] = str_from_xml(n)
+     return ret
+ #
+ # Definitions of the database objects (and their attributes) used by interface-reconfigure.
+ #
+ # Each object is defined by a dictionary mapping an attribute name in
+ # the xapi database to a tuple containing two items:
+ #  - a function which takes this attribute and encodes it as XML.
+ #  - a function which takes XML and decocdes it into a value.
+ #
+ # other-config attributes are specified as a simple array of strings
+ PIF_XML_TAG = "pif"
+ VLAN_XML_TAG = "vlan"
+ BOND_XML_TAG = "bond"
+ NETWORK_XML_TAG = "network"
+ ETHTOOL_OTHERCONFIG_ATTRS = ['ethtool-%s' % x for x in 'autoneg', 'speed', 'duplex', 'rx', 'tx', 'sg', 'tso', 'ufo', 'gso' ]
+ PIF_ATTRS = { 'uuid': (str_to_xml,str_from_xml),
+               'management': (bool_to_xml,bool_from_xml),
+               'network': (str_to_xml,str_from_xml),
+               'device': (str_to_xml,str_from_xml),
+               'bond_master_of': (lambda x, p, t, v: strlist_to_xml(x, p, 'bond_master_of', 'slave', v),
+                                  lambda n: strlist_from_xml(n, 'bond_master_of', 'slave')),
+               'bond_slave_of': (str_to_xml,str_from_xml),
+               'VLAN': (str_to_xml,str_from_xml),
+               'VLAN_master_of': (str_to_xml,str_from_xml),
+               'VLAN_slave_of': (lambda x, p, t, v: strlist_to_xml(x, p, 'VLAN_slave_of', 'master', v),
+                                 lambda n: strlist_from_xml(n, 'VLAN_slave_Of', 'master')),
+               'ip_configuration_mode': (str_to_xml,str_from_xml),
+               'IP': (str_to_xml,str_from_xml),
+               'netmask': (str_to_xml,str_from_xml),
+               'gateway': (str_to_xml,str_from_xml),
+               'DNS': (str_to_xml,str_from_xml),
+               'MAC': (str_to_xml,str_from_xml),
+               'other_config': (lambda x, p, t, v: otherconfig_to_xml(x, p, v, PIF_OTHERCONFIG_ATTRS),
+                                lambda n: otherconfig_from_xml(n, PIF_OTHERCONFIG_ATTRS)),
+             }
+ PIF_OTHERCONFIG_ATTRS = [ 'domain', 'peerdns', 'defaultroute', 'mtu', 'static-routes' ] + \
+                         [ 'bond-%s' % x for x in 'mode', 'miimon', 'downdelay', 'updelay', 'use_carrier' ] + \
+                         ETHTOOL_OTHERCONFIG_ATTRS
+ VLAN_ATTRS = { 'uuid': (str_to_xml,str_from_xml),
+                'tagged_PIF': (str_to_xml,str_from_xml),
+                'untagged_PIF': (str_to_xml,str_from_xml),
+              }
+     
+ BOND_ATTRS = { 'uuid': (str_to_xml,str_from_xml),
+                'master': (str_to_xml,str_from_xml),
+                'slaves': (lambda x, p, t, v: strlist_to_xml(x, p, 'slaves', 'slave', v),
+                           lambda n: strlist_from_xml(n, 'slaves', 'slave')),
+              }
+ NETWORK_ATTRS = { 'uuid': (str_to_xml,str_from_xml),
+                   'bridge': (str_to_xml,str_from_xml),
+                   'PIFs': (lambda x, p, t, v: strlist_to_xml(x, p, 'PIFs', 'PIF', v),
+                            lambda n: strlist_from_xml(n, 'PIFs', 'PIF')),
+                   'other_config': (lambda x, p, t, v: otherconfig_to_xml(x, p, v, NETWORK_OTHERCONFIG_ATTRS),
+                                    lambda n: otherconfig_from_xml(n, NETWORK_OTHERCONFIG_ATTRS)),
+                 }
+ NETWORK_OTHERCONFIG_ATTRS = [ 'mtu', 'static-routes' ] + ETHTOOL_OTHERCONFIG_ATTRS
  class DatabaseCache(object):
+     def __read_xensource_inventory(self):
+         filename = "/etc/xensource-inventory"
+         f = open(filename, "r")
+         lines = [x.strip("\n") for x in f.readlines()]
+         f.close()
+         defs = [ (l[:l.find("=")], l[(l.find("=") + 1):]) for l in lines ]
+         defs = [ (a, b.strip("'")) for (a,b) in defs ]
+         return dict(defs)
+     def __pif_on_host(self,pif):
+         return self.__pifs.has_key(pif)
+     def __get_pif_records_from_xapi(self, session, host):
+         self.__pifs = {}
+         for (p,rec) in session.xenapi.PIF.get_all_records().items():
+             if rec['host'] != host:
+                 continue
+             self.__pifs[p] = {}
+             for f in PIF_ATTRS:
+                 self.__pifs[p][f] = rec[f]
+             self.__pifs[p]['other_config'] = {}
+             for f in PIF_OTHERCONFIG_ATTRS:
+                 if not rec['other_config'].has_key(f): continue
+                 self.__pifs[p]['other_config'][f] = rec['other_config'][f]
+     def __get_vlan_records_from_xapi(self, session):
+         self.__vlans = {}
+         for v in session.xenapi.VLAN.get_all():
+             rec = session.xenapi.VLAN.get_record(v)
+             if not self.__pif_on_host(rec['untagged_PIF']):
+                 continue
+             self.__vlans[v] = {}
+             for f in VLAN_ATTRS:
+                 self.__vlans[v][f] = rec[f]
+     def __get_bond_records_from_xapi(self, session):
+         self.__bonds = {}
+         for b in session.xenapi.Bond.get_all():
+             rec = session.xenapi.Bond.get_record(b)
+             if not self.__pif_on_host(rec['master']):
+                 continue
+             self.__bonds[b] = {}
+             for f in BOND_ATTRS:
+                 self.__bonds[b][f] = rec[f]
+     def __get_network_records_from_xapi(self, session):
+         self.__networks = {}
+         for n in session.xenapi.network.get_all():
+             rec = session.xenapi.network.get_record(n)
+             self.__networks[n] = {}
+             for f in NETWORK_ATTRS:
+                 self.__networks[n][f] = rec[f]
+             self.__networks[n]['other_config'] = {}
+             for f in NETWORK_OTHERCONFIG_ATTRS:
+                 if not rec['other_config'].has_key(f): continue
+                 self.__networks[n]['other_config'][f] = rec['other_config'][f]
+     def __to_xml(self, xml, parent, key, ref, rec, attrs):
+         """Encode a database object as XML"""
+         e = xml.createElement(key)
+         parent.appendChild(e)
+         if ref:
+             e.setAttribute('ref', ref)
+         for n,v in rec.items():
+             if attrs.has_key(n):
+                 h,_ = attrs[n]
+                 h(xml, e, n, v)
+             else:
+                 raise Error("Unknown attribute %s" % n)
+     def __from_xml(self, e, attrs):
+         """Decode a database object from XML"""
+         ref = e.attributes['ref'].value
+         rec = {}
+         for n in e.childNodes:
+             if n.nodeName in attrs:
+                 _,h = attrs[n.nodeName]
+                 rec[n.nodeName] = h(n)
+         return (ref,rec)
+     
      def __init__(self, session_ref=None, cache_file=None):
          if session_ref and cache_file:
              raise Error("can't specify session reference and cache file")
          if cache_file == None:
              session = XenAPI.xapi_local()
  
                  session._session = session_ref
  
              try:
-                 self.__vlans = session.xenapi.VLAN.get_all_records()
-                 self.__bonds = session.xenapi.Bond.get_all_records()
-                 self.__pifs = session.xenapi.PIF.get_all_records()
-                 self.__networks = session.xenapi.network.get_all_records()
+                 
+                 inventory = self.__read_xensource_inventory()
+                 assert(inventory.has_key('INSTALLATION_UUID'))
+                 log("host uuid is %s" % inventory['INSTALLATION_UUID'])
+                 
+                 host = session.xenapi.host.get_by_uuid(inventory['INSTALLATION_UUID'])
+                 
+                 self.__get_pif_records_from_xapi(session, host)
+                 self.__get_vlan_records_from_xapi(session)
+                 self.__get_bond_records_from_xapi(session)
+                 self.__get_network_records_from_xapi(session)
              finally:
                  if not session_ref:
                      session.xenapi.session.logout()
          else:
              log("Loading xapi database cache from %s" % cache_file)
-             f = open(cache_file, 'r')
-             members = pickle.load(f)
-             self.extras = pickle.load(f)
-             f.close()
  
-             self.__vlans = members['vlans']
-             self.__bonds = members['bonds']
-             self.__pifs = members['pifs']
-             self.__networks = members['networks']
+             xml = parseXML(cache_file)
+             self.__pifs = {}
+             self.__bonds = {}
+             self.__vlans = {}
+             self.__networks = {}
  
-     def save(self, cache_file, extras):
+             assert(len(xml.childNodes) == 1)
+             toplevel = xml.childNodes[0]
+             
+             assert(toplevel.nodeName == "xenserver-network-configuration")
+             
+             for n in toplevel.childNodes:
+                 if n.nodeName == "#text":
+                     pass
+                 elif n.nodeName == PIF_XML_TAG:
+                     (ref,rec) = self.__from_xml(n, PIF_ATTRS)
+                     self.__pifs[ref] = rec
+                 elif n.nodeName == BOND_XML_TAG:
+                     (ref,rec) = self.__from_xml(n, BOND_ATTRS)
+                     self.__bonds[ref] = rec
+                 elif n.nodeName == VLAN_XML_TAG:
+                     (ref,rec) = self.__from_xml(n, VLAN_ATTRS)
+                     self.__vlans[ref] = rec
+                 elif n.nodeName == NETWORK_XML_TAG:
+                     (ref,rec) = self.__from_xml(n, NETWORK_ATTRS)
+                     self.__networks[ref] = rec
+                 else:
+                     raise Error("Unknown XML element %s" % n.nodeName)
+     def save(self, cache_file):
+         xml = getDOMImplementation().createDocument(
+             None, "xenserver-network-configuration", None)
+         for (ref,rec) in self.__pifs.items():
+             self.__to_xml(xml, xml.documentElement, PIF_XML_TAG, ref, rec, PIF_ATTRS)
+         for (ref,rec) in self.__bonds.items():
+             self.__to_xml(xml, xml.documentElement, BOND_XML_TAG, ref, rec, BOND_ATTRS)
+         for (ref,rec) in self.__vlans.items():
+             self.__to_xml(xml, xml.documentElement, VLAN_XML_TAG, ref, rec, VLAN_ATTRS)
+         for (ref,rec) in self.__networks.items():
+             self.__to_xml(xml, xml.documentElement, NETWORK_XML_TAG, ref, rec,
+                           NETWORK_ATTRS)
+             
          f = open(cache_file, 'w')
-         pickle.dump({'vlans': self.__vlans,
-                      'bonds': self.__bonds,
-                      'pifs': self.__pifs,
-                      'networks': self.__networks}, f)
-         pickle.dump(extras, f)
+         f.write(xml.toprettyxml())
          f.close()
  
      def get_pif_by_uuid(self, uuid):
  
          return pifs[0]
  
-     def get_pifs_by_record(self, record):
-         """record is partial pif record.
-         Get the pif(s) whose record matches.
-         """
-         def match(pifrec):
-             for key in record:
-                 if record[key] != pifrec[key]:
-                     return False
-             return True
-             
+     def get_pifs_by_device(self, device):
          return map(lambda (ref,rec): ref,
-                    filter(lambda (ref,rec): match(rec),
+                    filter(lambda (ref,rec): rec['device'] == device,
                            self.__pifs.items()))
  
-     def get_pif_by_record(self, record):
-         """record is partial pif record.
-         Get the pif whose record matches.
-         """
-         pifs = self.get_pifs_by_record(record)
-         if len(pifs) == 0:
-             raise Error("No matching PIF \"%s\"" % str(record))
-         elif len(pifs) > 1:
-             raise Error("Multiple matching PIFs \"%s\"" % str(record))
-         return pifs[0]
-     def get_pif_by_bridge(self, host, bridge):
+     def get_pif_by_bridge(self, bridge):
          networks = map(lambda (ref,rec): ref,
                         filter(lambda (ref,rec): rec['bridge'] == bridge,
                                self.__networks.items()))
              nwrec = self.get_network_record(network)
              for pif in nwrec['PIFs']:
                  pifrec = self.get_pif_record(pif)
-                 if pifrec['host'] != host:
-                     continue
                  if answer:
-                     raise Error("Multiple PIFs on %s for network %s" % (host, bridge))
+                     raise Error("Multiple PIFs on host for network %s" % (bridge))
                  answer = pif
          if not answer:
-             raise Error("No PIF on %s for network %s" % (host, bridge))
+             raise Error("No PIF on host for network %s" % (bridge))
          return answer
  
      def get_pif_record(self, pif):
          if self.__pifs.has_key(pif):
              return self.__pifs[pif]
-         raise Error("Unknown PIF \"%s\"" % pif)
+         raise Error("Unknown PIF \"%s\" (get_pif_record)" % pif)
      def get_all_pifs(self):
          return self.__pifs
      def pif_exists(self, pif):
          return self.__pifs.has_key(pif)
      
-     def get_management_pif(self, host):
+     def get_management_pif(self):
          """ Returns the management pif on host
          """
          all = self.get_all_pifs()
          for pif in all: 
              pifrec = self.get_pif_record(pif)
-             if pifrec['management'] and pifrec['host'] == host :
-                 return pif
+             if pifrec['management']: return pif
          return None
  
      def get_network_record(self, network):
@@@ -445,6 -636,7 +664,7 @@@ For a VLAN PIF, the datapath name is th
  use it.)
  """
  
      pifrec = db.get_pif_record(pif)
  
      if pifrec['VLAN'] == '-1':
@@@ -461,40 -653,36 +681,42 @@@ The ipdev name is the same as the bridg
      pifrec = db.get_pif_record(pif)
      return bridge_name(pif)
  
 -def physdev_names(pif):
 -    """Return the name(s) of the physical network device(s) associated with pif.
 -For a VLAN PIF, the physical devices are the VLAN slave's physical devices.
 -For a bond master PIF, the physical devices are the bond slaves.
 -For a non-VLAN, non-bond master PIF, the physical device is the PIF itself.
 +def get_physdev_pifs(pif):
 +    """Return the PIFs for the physical network device(s) associated with pif.
 +For a VLAN PIF, this is the VLAN slave's physical device PIF.
 +For a bond master PIF, these are the bond slave PIFs.
 +For a non-VLAN, non-bond master PIF, the PIF is its own physical device PIF.
  """
      pifrec = db.get_pif_record(pif)
  
      if pifrec['VLAN'] != '-1':
 -        return physdev_names(get_vlan_slave_of_pif(pif))
 +        return [get_vlan_slave_of_pif(pif)]
      elif len(pifrec['bond_master_of']) != 0:
 -        physdevs = []
 -        for slave in get_bond_slaves_of_pif(pif):
 -            physdevs += physdev_names(slave)
 -        return physdevs
 +        return get_bond_slaves_of_pif(pif)
      else:
 -        return [pifrec['device']]
 +        return [pif]
 +
 +def get_physdev_names(pif):
 +    """Return the name(s) of the physical network device(s) associated with pif.
 +For a VLAN PIF, the physical devices are the VLAN slave's physical devices.
 +For a bond master PIF, the physical devices are the bond slaves.
 +For a non-VLAN, non-bond master PIF, the physical device is the PIF itself.
 +"""
 +
 +    return [db.get_pif_record(phys)['device'] for phys in get_physdev_pifs(pif)]
  
  def log_pif_action(action, pif):
      pifrec = db.get_pif_record(pif)
-     pifrec['action'] = action
-     pifrec['interface-name'] = interface_name(pif)
+     rec = {}
+     rec['uuid'] = pifrec['uuid']
+     rec['ip_configuration_mode'] = pifrec['ip_configuration_mode']
+     rec['action'] = action
+     rec['interface-name'] = interface_name(pif)
      if action == "rewrite":
-         pifrec['message'] = "Rewrite PIF %(uuid)s configuration" % pifrec
+         rec['message'] = "Rewrite PIF %(uuid)s configuration" % rec
      else:
-         pifrec['message'] = "Bring %(action)s PIF %(uuid)s" % pifrec
-     log("%(message)s: %(interface-name)s configured as %(ip_configuration_mode)s" % pifrec)
+         rec['message'] = "Bring %(action)s PIF %(uuid)s" % rec
+     log("%(message)s: %(interface-name)s configured as %(ip_configuration_mode)s" % rec)
  
  def get_bond_masters_of_pif(pif):
      """Returns a list of PIFs which are bond masters of this PIF"""
@@@ -520,7 -708,6 +742,6 @@@ def get_bond_slaves_of_pif(pif)
      """Returns a list of PIFs which make up the given bonded pif."""
      
      pifrec = db.get_pif_record(pif)
-     host = pifrec['host']
  
      bmo = pifrec['bond_master_of']
      if len(bmo) > 1:
@@@ -577,76 -764,31 +798,76 @@@ def run_command(command)
          return False
      return True
  
 +def rename_netdev(old_name, new_name):
 +    log("Changing the name of %s to %s" % (old_name, new_name))
 +    run_command(['/sbin/ifconfig', old_name, 'down'])
 +    if not run_command(['/sbin/ip', 'link', 'set', old_name,
 +                        'name', new_name]):
 +        raise Error("Could not rename %s to %s" % (old_name, new_name))
 +
 +# Check whether 'pif' exists and has the correct MAC.
 +# If not, try to find a device with the correct MAC and rename it.
 +# 'already_renamed' is used to avoid infinite recursion.
 +def remap_pif(pif, already_renamed=[]):
 +    pifrec = db.get_pif_record(pif)
 +    device = pifrec['device']
 +    mac = pifrec['MAC']
 +
 +    # Is there a network device named 'device' at all?
 +    device_exists = interface_exists(device)
 +    if device_exists:
 +        # Yes.  Does it have MAC 'mac'?
 +        found_mac = get_netdev_mac(device)
 +        if found_mac and mac.lower() == found_mac.lower():
 +            # Yes, everything checks out the way we want.  Nothing to do.
 +            return
 +    else:
 +        log("No network device %s" % device)
 +
 +    # What device has MAC 'mac'?
 +    cur_device = get_netdev_by_mac(mac)
 +    if not cur_device:
 +        log("No network device has MAC %s" % mac)
 +        return
 +
 +    # First rename 'device', if it exists, to get it out of the way
 +    # for 'cur_device' to replace it.
 +    if device_exists:
 +        rename_netdev(device, "dev%d" % random.getrandbits(24))
 +
 +    # Rename 'cur_device' to 'device'.
 +    rename_netdev(cur_device, device)
 +
 +def read_first_line_of_file(name):
 +    file = None
 +    try:
 +        file = open(name, 'r')
 +        return file.readline().rstrip('\n')
 +    finally:
 +        if file != None:
 +            file.close()
 +
  def down_netdev(interface, deconfigure=True):
      if not interface_exists(interface):
          log("down_netdev: interface %s does not exist, ignoring" % interface)
          return
 -    argv = ["/sbin/ifconfig", interface, 'down']
      if deconfigure:
 -        argv += ['0.0.0.0']
 -
          # Kill dhclient.
          pidfile_name = '/var/run/dhclient-%s.pid' % interface
 -        pidfile = None
          try:
 -            pidfile = open(pidfile_name, 'r')
 -            os.kill(int(pidfile.readline()), signal.SIGTERM)
 +            os.kill(int(read_first_line_of_file(pidfile_name)), signal.SIGTERM)
          except:
              pass
 -        if pidfile != None:
 -            pidfile.close()
  
          # Remove dhclient pidfile.
          try:
              os.remove(pidfile_name)
          except:
              pass
 -    run_command(argv)
 +        
 +        run_command(["/sbin/ifconfig", interface, '0.0.0.0'])
 +
 +    run_command(["/sbin/ifconfig", interface, 'down'])
  
  def up_netdev(interface):
      run_command(["/sbin/ifconfig", interface, 'up'])
@@@ -661,10 -803,8 +882,8 @@@ This is because when we are called to b
  we should bring down that master."""
  
      pifrec = db.get_pif_record(pif)
-     host = pifrec['host']
  
-     pifs_on_host = [ __pif for __pif in db.get_all_pifs() if
-                      db.get_pif_record(__pif)['host'] == host and 
+     pifs = [ __pif for __pif in db.get_all_pifs() if
                       (not  __pif in get_bond_masters_of_pif(pif)) ]
  
      peerdns_pif = None
      # loop through all the pifs on this host looking for one with
      #   other-config:peerdns = true, and one with
      #   other-config:default-route=true
-     for __pif in pifs_on_host:
+     for __pif in pifs:
          __pifrec = db.get_pif_record(__pif)
          __oc = __pifrec['other_config']
          if __oc.has_key('peerdns') and __oc['peerdns'] == 'true':
  
      return peerdns_pif, defaultroute_pif
  
 -def ethtool_settings(oc):
 -    # Options for "ethtool -s"
 +def run_ethtool(device, oc):
 +    # Run "ethtool -s" if there are any settings.
      settings = []
      if oc.has_key('ethtool-speed'):
          val = oc['ethtool-speed']
              settings += ['autoneg', 'off']
          else:
              log("Invalid value for ethtool-autoneg = %s. Must be on|true|off|false." % val)
 +    if settings:
 +        run_command(['/sbin/ethtool', '-s', device] + settings)
  
 -    # Options for "ethtool -K"
 +    # Run "ethtool -K" if there are any offload settings.
      offload = []
      for opt in ("rx", "tx", "sg", "tso", "ufo", "gso"):
          if oc.has_key("ethtool-" + opt):
                  offload += [opt, 'off']
              else:
                  log("Invalid value for ethtool-%s = %s. Must be on|true|off|false." % (opt, val))
 +    if offload:
 +        run_command(['/sbin/ethtool', '-K', device] + offload)
  
 -    return settings, offload
 +def mtu_setting(oc):
 +    if oc.has_key('mtu'):
 +        try:
 +            int(oc['mtu'])      # Check that the value is an integer
 +            return ['mtu', oc['mtu']]
 +        except ValueError, x:
 +            log("Invalid value for mtu = %s" % mtu)
 +    return []
  
 -def configure_netdev(pif):
 +def configure_local_port(pif):
      pifrec = db.get_pif_record(pif)
      datapath = datapath_name(pif)
      ipdev = ipdev_name(pif)
  
-     host = pifrec['host']
      nw = pifrec['network']
      nwrec = db.get_network_record(nw)
  
 +    pif_oc = pifrec['other_config']
 +    nw_oc = nwrec['other_config']
 +
 +    # IP (except DHCP) and MTU.
      ifconfig_argv = ['/sbin/ifconfig', ipdev, 'up']
      gateway = ''
      if pifrec['ip_configuration_mode'] == "DHCP":
          pass
      else:
          raise Error("Unknown IP-configuration-mode %s" % pifrec['ip_configuration_mode'])
 -
 -    oc = {}
 -    if pifrec.has_key('other_config'):
 -        oc = pifrec['other_config']
 -        if oc.has_key('mtu'):
 -            int(oc['mtu'])      # Check that the value is an integer
 -            ifconfig_argv += ['mtu', oc['mtu']]
 -
 +    ifconfig_argv += mtu_setting(nw_oc)
      run_command(ifconfig_argv)
      
      (peerdns_pif, defaultroute_pif) = find_distinguished_pifs(pif)
  
 +    # /etc/resolv.conf
      if peerdns_pif == pif:
          f = ConfigurationFile('resolv.conf', "/etc")
 -        if oc.has_key('domain'):
 -            f.write("search %s\n" % oc['domain'])
 +        if pif_oc.has_key('domain'):
 +            f.write("search %s\n" % pif_oc['domain'])
          for dns in pifrec['DNS'].split(","): 
              f.write("nameserver %s\n" % dns)
          f.close()
          f.apply()
          f.commit()
  
 +    # Routing.
      if defaultroute_pif == pif and gateway != '':
          run_command(['/sbin/ip', 'route', 'replace', 'default',
                       'via', gateway, 'dev', ipdev])
 -    
 -    if oc.has_key('static-routes'):
 -        for line in oc['static-routes'].split(','):
 +    if nw_oc.has_key('static-routes'):
 +        for line in nw_oc['static-routes'].split(','):
              network, masklen, gateway = line.split('/')
              run_command(['/sbin/ip', 'route', 'add',
 -                         '%s/%s' % (netmask, masklen), 'via', gateway,
 +                         '%s/%s' % (network, masklen), 'via', gateway,
                           'dev', ipdev])
  
 -    settings, offload = ethtool_settings(oc)
 -    if settings:
 -        run_command(['/sbin/ethtool', '-s', ipdev] + settings)
 -    if offload:
 -        run_command(['/sbin/ethtool', '-K', ipdev] + offload)
 +    # Ethtool.
 +    run_ethtool(ipdev, nw_oc)
  
 +    # DHCP.
      if pifrec['ip_configuration_mode'] == "DHCP":
          print
          print "Determining IP information for %s..." % ipdev,
          else:
              print 'failed.'
  
 +def configure_physdev(pif):
 +    pifrec = db.get_pif_record(pif)
 +    device = pifrec['device']
 +    oc = pifrec['other_config']
 +
 +    run_command(['/sbin/ifconfig', device, 'up'] + mtu_setting(oc))
 +    run_ethtool(device, oc)
 +
  def modify_config(commands):
      run_command(['/root/vswitch/bin/ovs-cfg-mod', '-vANY:console:emer',
                   '-F', '/etc/ovs-vswitchd.conf']
@@@ -838,15 -962,11 +1056,15 @@@ def configure_bond(pif)
      interface = interface_name(pif)
      ipdev = ipdev_name(pif)
      datapath = datapath_name(pif)
 -    physdevs = physdev_names(pif)
 +    physdev_names = get_physdev_names(pif)
  
      argv = ['--del-match=bonding.%s.[!0-9]*' % interface]
      argv += ["--add=bonding.%s.slave=%s" % (interface, slave)
 -             for slave in physdevs]
 +             for slave in physdev_names]
 +    argv += ['--add=bonding.%s.fake-iface=true' % interface]
 +
 +    if pifrec['MAC'] != "":
 +        argv += ['--add=port.%s.mac=%s' % (interface, pifrec['MAC'])]
  
      # Bonding options.
      bond_options = { 
@@@ -874,8 -994,7 +1092,8 @@@ def action_up(pif)
      interface = interface_name(pif)
      ipdev = ipdev_name(pif)
      datapath = datapath_name(pif)
 -    physdevs = physdev_names(pif)
 +    physdev_pifs = get_physdev_pifs(pif)
 +    physdev_names = get_physdev_names(pif)
      vlan_slave = None
      if pifrec['VLAN'] != '-1':
          vlan_slave = get_vlan_slave_of_pif(pif)
      f.apply()
      f.commit()
  
 +    # Check the MAC address of each network device and remap if
 +    # necessary to make names match our expectations.
 +    for physdev_pif in physdev_pifs:
 +        remap_pif(physdev_pif)
 +
      # "ifconfig down" the network device and delete its IP address, etc.
      down_netdev(ipdev)
 -    for physdev in physdevs:
 -        down_netdev(physdev)
 +    for physdev_name in physdev_names:
 +        down_netdev(physdev_name)
  
      # If we are bringing up a bond, remove IP addresses from the
      # slaves (because we are implicitly being asked to take them down).
          run_command(["/sbin/ifconfig", ipdev_name(bond_pif), '0.0.0.0']) 
  
      # Remove all keys related to pif and any bond masters linked to PIF.
 -    del_ports = [ipdev] + physdevs + bond_masters
 +    del_ports = [ipdev] + physdev_names + bond_masters
      if vlan_slave and bond_master:
          del_ports += [interface_name(bond_master)]
      
      # port.
      add_ports = [ipdev, datapath]
      if not bond_master:
 -        add_ports += physdevs
 +        add_ports += physdev_names
      else:
          add_ports += [interface_name(bond_master)]
  
      #  - The bond masters for pif.  (Ordinarily pif shouldn't have any
      #    bond masters.  If it does then interface-reconfigure is
      #    implicitly being asked to take them down.)
 -    del_ports = add_ports + physdevs + bond_masters
 +    del_ports = add_ports + physdev_names + bond_masters
  
      # What networks does this datapath carry?
      #
      # - The networks corresponding to any VLANs attached to the
      #   datapath's PIF.
      network_uuids = []
-     for nwpif in db.get_pifs_by_record({'device': pifrec['device'],
-                                         'host': pifrec['host']}):
+     for nwpif in db.get_pifs_by_device({'device': pifrec['device']}):
          net = db.get_pif_record(nwpif)['network']
          network_uuids += [db.get_network_record(net)['uuid']]
  
      # enables or disables bond slaves based on whether carrier is
      # detected when they are added, and a network device that is down
      # always reports "no carrier".
 -    bond_slave_physdevs = []
 +    bond_slave_physdev_pifs = []
      for slave in bond_slaves:
 -        bond_slave_physdevs += physdev_names(slave)
 -    for slave_physdev in bond_slave_physdevs:
 -        up_netdev(slave_physdev)
 +        bond_slave_physdev_pifs += get_physdev_pifs(slave)
 +    for slave_physdev_pif in set(bond_slave_physdev_pifs):
 +        configure_physdev(slave_physdev_pif)
  
      # Now modify the ovs-vswitchd config file.
      argv = []
          argv += configure_bond(bond_master)
      modify_config(argv)
  
 -    # Configure network devices.
 -    configure_netdev(pif)
 -
      # Bring up VLAN slave, plus physical devices other than bond
      # slaves (which we brought up earlier).
      if vlan_slave:
          up_netdev(ipdev_name(vlan_slave))
 -    for physdev in set(physdevs) - set(bond_slave_physdevs):
 -        up_netdev(physdev)
 +    for physdev_pif in set(physdev_pifs) - set(bond_slave_physdev_pifs):
 +        configure_physdev(physdev_pif)
 +
 +    # Configure network device for local port.
 +    configure_local_port(pif)
  
      # Update /etc/issue (which contains the IP address of the management interface)
      os.system("/sbin/update-issue")
@@@ -1244,9 -1357,8 +1461,8 @@@ def main(argv=None)
                  action_force_rewrite(force_interface, force_rewrite_config)
              else:
                  db = DatabaseCache(cache_file=dbcache_file)
-                 host = db.extras['host']
-                 pif = db.get_pif_by_bridge(host, force_interface)
-                 management_pif = db.get_management_pif(host)
+                 pif = db.get_pif_by_bridge(force_interface)
+                 management_pif = db.get_management_pif()
  
                  if action == "up":
                      action_up(pif)
                  # pif is not going to be the management pif.
                  # Search DB cache for pif on same host with management=true
                  pifrec = db.get_pif_record(pif)
-                 host = pifrec['host']
-                 management_pif = db.get_management_pif(host)
+                 management_pif = db.get_management_pif()
  
              log_pif_action(action, pif)
  
  
              # Save cache.
              pifrec = db.get_pif_record(pif)
-             db.save(dbcache_file, {'host': pifrec['host']})
+             db.save(dbcache_file)
          
      except Usage, err:
          print >>sys.stderr, err.msg
@@@ -1455,7 -1566,6 +1670,6 @@@ def configure_network(pif, f)
      """
      
      pifrec = db.get_pif_record(pif)
-     host = pifrec['host']
      nw = pifrec['network']
      nwrec = db.get_network_record(nw)
      oc = None
      # This is because when we are called to bring up an interface with a bond master, it is implicit that
      # we should bring down that master.
      pifs_on_host = [ __pif for __pif in db.get_all_pifs() if
-                      db.get_pif_record(__pif)['host'] == host and 
-                      (not  __pif in get_bond_masters_of_pif(pif)) ]
+                      not __pif in get_bond_masters_of_pif(pif) ]
      other_pifs_on_host = [ __pif for __pif in pifs_on_host if __pif != pif ]
  
      peerdns_pif = None
@@@ -21,9 -21,7 +21,9 @@@ Summary: Virtual switc
  Group: System Environment/Daemons
  URL: http://www.openvswitch.org/
  Version: %{vswitch_version}
 -License: GPL3
 +
 +# The entire source code is ASL 2.0 except datapath/ which is GPLv2
 +License: ASL 2.0 and GPLv2
  Release: 1
  Source: openvswitch-%{vswitch_version}.tar.gz
  Buildroot: /tmp/vswitch-xen-rpm
@@@ -69,8 -67,6 +69,8 @@@ install -m 755 xenserver/etc_xensource_
               $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/usr_sbin_xen-bugtool \
 +             $RPM_BUILD_ROOT%{_prefix}/scripts/xen-bugtool
  install -m 644 \
          xenserver/usr_lib_xsconsole_plugins-base_XSFeatureVSwitch.py \
                 $RPM_BUILD_ROOT%{_prefix}/scripts/XSFeatureVSwitch.py
@@@ -84,16 -80,17 +84,17 @@@ rm -rf 
      $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-openflowd \
      $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/kernel_modules/veth_mod.ko \
      $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-openflowd.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
  
  %clean
@@@ -109,7 -106,6 +110,7 @@@ if [ "$1" = "1" ]; the
      if ! md5sum -c --status <<EOF
  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"
@@@ -170,6 -166,8 +171,6 @@@ EO
  fi
  
  %post
 -source /etc/xensource-inventory
 -
  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
@@@ -185,6 -183,10 +186,6 @@@ net.ipv4.conf.all.arp_filter = 
  EOF
  fi
  
 -xe host-param-set \
 -    "other-config:vSwitchVersion=%{version}" uuid="$INSTALLATION_UUID" ||
 -    echo "Could not set vSwitchVersion config parameter"
 -
  # Ensure ovs-vswitchd.conf exists
  touch /etc/ovs-vswitchd.conf
  
@@@ -193,8 -195,7 +194,8 @@@ mkdir -p %{_prefix}/xs-original 
      || printf "Could not create script backup directory.\n"
  for f in \
      /opt/xensource/libexec/interface-reconfigure \
 -    /etc/xensource/scripts/vif
 +    /etc/xensource/scripts/vif \
 +    /usr/sbin/xen-bugtool
  do
      s=$(basename "$f")
      t=$(readlink "$f")
@@@ -251,8 -252,7 +252,8 @@@ if [ "$1" = "0" ]; then     # $1 = 1 fo
      # Restore original XenServer scripts
      for f in \
          /opt/xensource/libexec/interface-reconfigure \
 -        /etc/xensource/scripts/vif
 +        /etc/xensource/scripts/vif \
 +        /usr/sbin/xen-bugtool
      do
          s=$(basename "$f")
          if [ ! -f "%{_prefix}/xs-original/$s" ]; then
      rm -f /var/log/vswitch*
      rm -f /etc/ovs-vswitchd.cacert
  
 -    if [ ! -f /etc/xensource-inventory ]; then
 -        printf "XenSource inventory not present in /etc/xensource-inventory\n"
 -        printf "Could not remove vSwitchVersion from XAPI database.\n"
 -        exit 1
 -    else
 -        source /etc/xensource-inventory
 -        xe host-param-remove \
 -            param-name=other-config param-key=vSwitchVersion \
 -            uuid="$INSTALLATION_UUID" ||
 -            echo "Could not clear vSwitchVersion config parameter."
 -    fi
 -
      printf "\nYou MUST reboot the server now to complete the change to\n"
      printf "standard Xen networking.  Attempts to modify networking on the\n"
      printf "server or any hosted VM will fail until after the reboot and\n"
  /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/interface-reconfigure
  /root/vswitch/scripts/vif
 +/root/vswitch/scripts/xen-bugtool
  /root/vswitch/scripts/XSFeatureVSwitch.py
  # 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