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

@@@ -222,13 -225,6 +223,11 @@@ static int create_dp(int dp_idx, const 
                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);
  
@@@ -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;
@@@ -430,9 -402,10 +421,9 @@@ got_port_no
        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--;
@@@ -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. */
Simple merge
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 */
 +
index 9699a07,0000000..fafd9a9
mode 100644,000000..100644
--- /dev/null
@@@ -1,525 -1,0 +1,507 @@@
- #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.
 + */
 +
 +#include <linux/version.h>
 +
 +/*
 + *    Sysfs attributes of bridge for Open vSwitch
 + *
 + *  This has been shamelessly copied from the kernel sources.
 + */
 +
 +#include <linux/capability.h>
 +#include <linux/device.h>
 +#include <linux/kernel.h>
 +#include <linux/netdevice.h>
 +#include <linux/if_bridge.h>
 +#include <linux/rtnetlink.h>
 +#include <linux/spinlock.h>
 +#include <linux/times.h>
 +#include <linux/version.h>
 +
 +#include "dp_sysfs.h"
 +#include "datapath.h"
 +#include "dp_dev.h"
 +
- #define to_kobj(d) &(d)->class_dev.kobj
++#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)->dev.kobj
 +#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
- static ssize_t store_bridge_parm(struct class_device *d,
 +#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 show_forward_delay(struct class_device *d,
-                                 char *buf)
++static ssize_t store_bridge_parm(DEVICE_PARAMS,
 +                               const char *buf, size_t len,
 +                               void (*set)(struct datapath *, unsigned long))
 +{
 +      struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
 +      char *endp;
 +      unsigned long val;
 +
 +      if (!capable(CAP_NET_ADMIN))
 +              return -EPERM;
 +
 +      val = simple_strtoul(buf, &endp, 0);
 +      if (endp == buf)
 +              return -EINVAL;
 +
 +#if 0
 +      spin_lock_bh(&br->lock);
 +      (*set)(br, val);
 +      spin_unlock_bh(&br->lock);
 +#else
 +      /* xxx We use a default value of 0 for all fields.  If the caller is
 +       * xxx attempting to set the value to our default, just silently
 +       * xxx ignore the request. 
 +       */
 +      if (val != 0) {
 +              printk("%s: xxx writing dp parms not supported yet!\n", 
 +                     dp_name(dp));
 +      }
 +#endif
 +      return len;
 +}
 +
 +
- static ssize_t store_forward_delay(struct class_device *d,
++static ssize_t show_forward_delay(DEVICE_PARAMS, char *buf)
 +{
 +#if 0
 +      struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
 +      return sprintf(buf, "%lu\n", jiffies_to_clock_t(br->forward_delay));
 +#else
 +      return sprintf(buf, "%d\n", 0);
 +#endif
 +}
 +
 +static void set_forward_delay(struct datapath *dp, unsigned long val)
 +{
 +#if 0
 +      unsigned long delay = clock_t_to_jiffies(val);
 +      br->forward_delay = delay;
 +      if (br_is_root_bridge(br))
 +              br->bridge_forward_delay = delay;
 +#else
 +      printk("%s: xxx attempt to set_forward_delay()\n", dp_name(dp));
 +#endif
 +}
 +
-       return store_bridge_parm(d, buf, len, set_forward_delay);
++static ssize_t store_forward_delay(DEVICE_PARAMS,
 +                                 const char *buf, size_t len)
 +{
- static ssize_t show_hello_time(struct class_device *d, char *buf)
++      return store_bridge_parm(DEVICE_ARGS, buf, len, set_forward_delay);
 +}
 +static DP_DEVICE_ATTR(forward_delay, S_IRUGO | S_IWUSR,
 +                 show_forward_delay, store_forward_delay);
 +
- static ssize_t store_hello_time(struct class_device *d,
++static ssize_t show_hello_time(DEVICE_PARAMS, char *buf)
 +{
 +#if 0
 +      return sprintf(buf, "%lu\n",
 +                     jiffies_to_clock_t(to_bridge(d)->hello_time));
 +#else
 +      return sprintf(buf, "%d\n", 0);
 +#endif
 +}
 +
 +static void set_hello_time(struct datapath *dp, unsigned long val)
 +{
 +#if 0
 +      unsigned long t = clock_t_to_jiffies(val);
 +      br->hello_time = t;
 +      if (br_is_root_bridge(br))
 +              br->bridge_hello_time = t;
 +#else
 +      printk("%s: xxx attempt to set_hello_time()\n", dp_name(dp));
 +#endif
 +}
 +
-       return store_bridge_parm(d, buf, len, set_hello_time);
++static ssize_t store_hello_time(DEVICE_PARAMS,
 +                              const char *buf,
 +                              size_t len)
 +{
- static ssize_t show_max_age(struct class_device *d, 
-                           char *buf)
++      return store_bridge_parm(DEVICE_ARGS, buf, len, set_hello_time);
 +}
 +static DP_DEVICE_ATTR(hello_time, S_IRUGO | S_IWUSR, show_hello_time,
 +                 store_hello_time);
 +
- static ssize_t store_max_age(struct class_device *d, 
++static ssize_t show_max_age(DEVICE_PARAMS, char *buf)
 +{
 +#if 0
 +      return sprintf(buf, "%lu\n",
 +                     jiffies_to_clock_t(to_bridge(d)->max_age));
 +#else
 +      return sprintf(buf, "%d\n", 0);
 +#endif
 +}
 +
 +static void set_max_age(struct datapath *dp, unsigned long val)
 +{
 +#if 0
 +      unsigned long t = clock_t_to_jiffies(val);
 +      br->max_age = t;
 +      if (br_is_root_bridge(br))
 +              br->bridge_max_age = t;
 +#else
 +      printk("%s: xxx attempt to set_max_age()\n", dp_name(dp));
 +#endif
 +}
 +
-       return store_bridge_parm(d, buf, len, set_max_age);
++static ssize_t store_max_age(DEVICE_PARAMS,
 +                           const char *buf, size_t len)
 +{
- static ssize_t show_ageing_time(struct class_device *d,
-                               char *buf)
++      return store_bridge_parm(DEVICE_ARGS, buf, len, set_max_age);
 +}
 +static DP_DEVICE_ATTR(max_age, S_IRUGO | S_IWUSR, show_max_age, store_max_age);
 +
- static ssize_t store_ageing_time(struct class_device *d,
++static ssize_t show_ageing_time(DEVICE_PARAMS, char *buf)
 +{
 +#if 0
 +      struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
 +      return sprintf(buf, "%lu\n", jiffies_to_clock_t(br->ageing_time));
 +#else
 +      return sprintf(buf, "%d\n", 0);
 +#endif
 +}
 +
 +static void set_ageing_time(struct datapath *dp, unsigned long val)
 +{
 +#if 0
 +      br->ageing_time = clock_t_to_jiffies(val);
 +#else
 +      printk("%s: xxx attempt to set_ageing_time()\n", dp_name(dp));
 +#endif
 +}
 +
-       return store_bridge_parm(d, buf, len, set_ageing_time);
++static ssize_t store_ageing_time(DEVICE_PARAMS,
 +                               const char *buf, size_t len)
 +{
- static ssize_t show_stp_state(struct class_device *d,
-                             char *buf)
++      return store_bridge_parm(DEVICE_ARGS, buf, len, set_ageing_time);
 +}
 +static DP_DEVICE_ATTR(ageing_time, S_IRUGO | S_IWUSR, show_ageing_time,
 +                 store_ageing_time);
 +
- static ssize_t store_stp_state(struct class_device *d,
++static ssize_t show_stp_state(DEVICE_PARAMS, char *buf)
 +{
 +#if 0
 +      struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
 +      return sprintf(buf, "%d\n", br->stp_enabled);
 +#else
 +      return sprintf(buf, "%d\n", 0);
 +#endif
 +}
 +
 +
- static ssize_t show_priority(struct class_device *d, 
-                            char *buf)
++static ssize_t store_stp_state(DEVICE_PARAMS,
 +                             const char *buf,
 +                             size_t len)
 +{
 +      struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
 +#if 0
 +      char *endp;
 +      unsigned long val;
 +
 +      if (!capable(CAP_NET_ADMIN))
 +              return -EPERM;
 +
 +      val = simple_strtoul(buf, &endp, 0);
 +      if (endp == buf)
 +              return -EINVAL;
 +
 +      rtnl_lock();
 +      br_stp_set_enabled(br, val);
 +      rtnl_unlock();
 +#else
 +      printk("%s: xxx attempt to set_stp_state()\n", dp_name(dp));
 +#endif
 +
 +      return len;
 +}
 +static DP_DEVICE_ATTR(stp_state, S_IRUGO | S_IWUSR, show_stp_state,
 +                 store_stp_state);
 +
- static ssize_t store_priority(struct class_device *d, 
++static ssize_t show_priority(DEVICE_PARAMS, char *buf)
 +{
 +#if 0
 +      struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
 +      return sprintf(buf, "%d\n",
 +                     (br->bridge_id.prio[0] << 8) | br->bridge_id.prio[1]);
 +#else
 +      return sprintf(buf, "%d\n", 0);
 +#endif
 +}
 +
 +static void set_priority(struct datapath *dp, unsigned long val)
 +{
 +#if 0
 +      br_stp_set_bridge_priority(br, (u16) val);
 +#else
 +      printk("%s: xxx attempt to set_priority()\n", dp_name(dp));
 +#endif
 +}
 +
-       return store_bridge_parm(d, buf, len, set_priority);
++static ssize_t store_priority(DEVICE_PARAMS,
 +                             const char *buf, size_t len)
 +{
- static ssize_t show_root_id(struct class_device *d, 
-                           char *buf)
++      return store_bridge_parm(DEVICE_ARGS, buf, len, set_priority);
 +}
 +static DP_DEVICE_ATTR(priority, S_IRUGO | S_IWUSR, show_priority, store_priority);
 +
- static ssize_t show_bridge_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);
 +#else
 +      return sprintf(buf, "0000.010203040506\n");
 +#endif
 +}
 +static DP_DEVICE_ATTR(root_id, S_IRUGO, show_root_id, NULL);
 +
- static ssize_t show_root_port(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;
 +
 +      /* xxx Do we need a lock of some sort? */
 +      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 DP_DEVICE_ATTR(bridge_id, S_IRUGO, show_bridge_id, NULL);
 +
- static ssize_t show_root_path_cost(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);
 +#else
 +      return sprintf(buf, "%d\n", 0);
 +#endif
 +}
 +static DP_DEVICE_ATTR(root_port, S_IRUGO, show_root_port, NULL);
 +
- static ssize_t show_topology_change(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);
 +#else
 +      return sprintf(buf, "%d\n", 0);
 +#endif
 +}
 +static DP_DEVICE_ATTR(root_path_cost, S_IRUGO, show_root_path_cost, NULL);
 +
- static ssize_t show_topology_change_detected(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);
 +#else
 +      return sprintf(buf, "%d\n", 0);
 +#endif
 +}
 +static DP_DEVICE_ATTR(topology_change, S_IRUGO, show_topology_change, NULL);
 +
- static ssize_t show_hello_timer(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", br->topology_change_detected);
 +#else
 +      return sprintf(buf, "%d\n", 0);
 +#endif
 +}
 +static DP_DEVICE_ATTR(topology_change_detected, S_IRUGO,
 +                 show_topology_change_detected, NULL);
 +
- static ssize_t show_tcn_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, "%ld\n", br_timer_value(&br->hello_timer));
 +#else
 +      return sprintf(buf, "%d\n", 0);
 +#endif
 +}
 +static DP_DEVICE_ATTR(hello_timer, S_IRUGO, show_hello_timer, NULL);
 +
- static ssize_t show_topology_change_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, "%ld\n", br_timer_value(&br->tcn_timer));
 +#else
 +      return sprintf(buf, "%d\n", 0);
 +#endif
 +}
 +static DP_DEVICE_ATTR(tcn_timer, S_IRUGO, show_tcn_timer, NULL);
 +
- static ssize_t show_gc_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, "%ld\n", br_timer_value(&br->topology_change_timer));
 +#else
 +      return sprintf(buf, "%d\n", 0);
 +#endif
 +}
 +static DP_DEVICE_ATTR(topology_change_timer, S_IRUGO, show_topology_change_timer,
 +                 NULL);
 +
- static ssize_t show_group_addr(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, "%ld\n", br_timer_value(&br->gc_timer));
 +#else
 +      return sprintf(buf, "%d\n", 0);
 +#endif
 +}
 +static DP_DEVICE_ATTR(gc_timer, S_IRUGO, show_gc_timer, NULL);
 +
- static ssize_t store_group_addr(struct class_device *d,
++static ssize_t show_group_addr(DEVICE_PARAMS, char *buf)
 +{
 +#if 0
 +      struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
 +      return sprintf(buf, "%x:%x:%x:%x:%x:%x\n",
 +                     br->group_addr[0], br->group_addr[1],
 +                     br->group_addr[2], br->group_addr[3],
 +                     br->group_addr[4], br->group_addr[5]);
 +#else
 +      return sprintf(buf, "00:01:02:03:04:05\n");
 +#endif
 +}
 +
-       &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,
++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));
 +#if 0
 +      unsigned new_addr[6];
 +      int i;
 +
 +      if (!capable(CAP_NET_ADMIN))
 +              return -EPERM;
 +
 +      if (sscanf(buf, "%x:%x:%x:%x:%x:%x",
 +                 &new_addr[0], &new_addr[1], &new_addr[2],
 +                 &new_addr[3], &new_addr[4], &new_addr[5]) != 6)
 +              return -EINVAL;
 +
 +      /* Must be 01:80:c2:00:00:0X */
 +      for (i = 0; i < 5; i++)
 +              if (new_addr[i] != br_group_address[i])
 +                      return -EINVAL;
 +
 +      if (new_addr[5] & ~0xf)
 +              return -EINVAL;
 +
 +      if (new_addr[5] == 1    /* 802.3x Pause address */
 +          || new_addr[5] == 2 /* 802.3ad Slow protocols */
 +          || new_addr[5] == 3) /* 802.1X PAE address */
 +              return -EINVAL;
 +
 +      spin_lock_bh(&br->lock);
 +      for (i = 0; i < 6; i++)
 +              br->group_addr[i] = new_addr[i];
 +      spin_unlock_bh(&br->lock);
 +#else
 +      printk("%s: xxx attempt to store_group_addr()\n", dp_name(dp));
 +#endif
 +      return len;
 +}
 +
 +static DP_DEVICE_ATTR(group_addr, S_IRUGO | S_IWUSR,
 +                 show_group_addr, store_group_addr);
 +
 +static struct attribute *bridge_attrs[] = {
-       struct kobject *kobj = to_kobj(dp->ports[ODPP_LOCAL]->dev);
++      &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
 +};
 +
 +static struct attribute_group bridge_group = {
 +      .name = SYSFS_BRIDGE_ATTR,
 +      .attrs = bridge_attrs,
 +};
 +
 +/*
 + * Add entries in sysfs onto the existing network class device
 + * for the bridge.
 + *   Adds a attribute group "bridge" containing tuning parameters.
 + *   Sub directory to hold links to interfaces.
 + *
 + * Note: the ifobj exists only to be a subdirectory
 + *   to hold links.  The ifobj exists in the same data structure
 + *   as its parent the bridge so reference counting works.
 + */
 +int dp_sysfs_add_dp(struct datapath *dp)
 +{
-       /* Create /sys/class/net/<devname>/bridge directory. */
-       dp->ifobj.parent = kobj;
-       err = kobject_add(&dp->ifobj);
++      struct kobject *kobj = &dp->ports[ODPP_LOCAL]->dev->NETDEV_DEV_MEMBER.kobj;
 +      int err;
 +
 +      err = sysfs_create_group(kobj, &bridge_group);
 +      if (err) {
 +              pr_info("%s: can't create group %s/%s\n",
 +                      __func__, dp_name(dp), bridge_group.name);
 +              goto out1;
 +      }
 +
-                               __FUNCTION__, dp_name(dp), dp->ifobj.name);
++      /* 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",
-       struct kobject *kobj = to_kobj(dp->ports[ODPP_LOCAL]->dev);
++                      __FUNCTION__, dp_name(dp), kobject_name(&dp->ifobj));
 +              goto out2;
 +      }
 +      kobject_uevent(&dp->ifobj, KOBJ_ADD);
 +      return 0;
 +
 + out2:
 +      sysfs_remove_group(kobj, &bridge_group);
 + out1:
 +      return err;
 +}
 +
 +int dp_sysfs_del_dp(struct datapath *dp)
 +{
- #else /* !SUPPORT_SYSFS */
++      struct kobject *kobj = &dp->ports[ODPP_LOCAL]->dev->NETDEV_DEV_MEMBER.kobj;
 +
 +      kobject_del(&dp->ifobj);
 +      sysfs_remove_group(kobj, &bridge_group);
 +
 +      return 0;
 +}
- int dp_sysfs_del_if(struct net_bridge_port *p)
- {
-       dev_put(p->dev);
-       kfree(p);
-       return 0;
- }
- #endif /* !SUPPORT_SYSFS */
++#else /* !CONFIG_SYSFS */
 +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) { return 0; }
++#endif /* !CONFIG_SYSFS */
index ab928f6,0000000..95c26dc
mode 100644,000000..100644
--- /dev/null
@@@ -1,333 -1,0 +1,329 @@@
- #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.
 + */
 +
 +/*
 + *    Sysfs attributes of bridge ports for Open vSwitch
 + *
 + *  This has been shamelessly copied from the kernel sources.
 + */
 +
 +#include <linux/capability.h>
 +#include <linux/kernel.h>
 +#include <linux/netdevice.h>
 +#include <linux/if_bridge.h>
 +#include <linux/rtnetlink.h>
 +#include <linux/spinlock.h>
 +#include "dp_sysfs.h"
 +#include "datapath.h"
 +
-       err = kobject_add(&p->kobj);
++#ifdef CONFIG_SYSFS
 +
 +struct brport_attribute {
 +      struct attribute        attr;
 +      ssize_t (*show)(struct net_bridge_port *, char *);
 +      ssize_t (*store)(struct net_bridge_port *, unsigned long);
 +};
 +
 +#define BRPORT_ATTR(_name,_mode,_show,_store)                 \
 +struct brport_attribute brport_attr_##_name = {               \
 +      .attr = {.name = __stringify(_name),                    \
 +               .mode = _mode,                                 \
 +               .owner = THIS_MODULE, },                       \
 +      .show   = _show,                                        \
 +      .store  = _store,                                       \
 +};
 +
 +static ssize_t show_path_cost(struct net_bridge_port *p, char *buf)
 +{
 +#if 0
 +      return sprintf(buf, "%d\n", p->path_cost);
 +#else
 +      return sprintf(buf, "%d\n", 0);
 +#endif
 +}
 +static ssize_t store_path_cost(struct net_bridge_port *p, unsigned long v)
 +{
 +#if 0
 +      br_stp_set_path_cost(p, v);
 +#endif
 +      return 0;
 +}
 +static BRPORT_ATTR(path_cost, S_IRUGO | S_IWUSR,
 +                 show_path_cost, store_path_cost);
 +
 +static ssize_t show_priority(struct net_bridge_port *p, char *buf)
 +{
 +#if 0
 +      return sprintf(buf, "%d\n", p->priority);
 +#else
 +      return sprintf(buf, "%d\n", 0);
 +#endif
 +}
 +static ssize_t store_priority(struct net_bridge_port *p, unsigned long v)
 +{
 +#if 0
 +      if (v >= (1<<(16-BR_PORT_BITS)))
 +              return -ERANGE;
 +      br_stp_set_port_priority(p, v);
 +#endif
 +      return 0;
 +}
 +static BRPORT_ATTR(priority, S_IRUGO | S_IWUSR,
 +                       show_priority, store_priority);
 +
 +static ssize_t show_designated_root(struct net_bridge_port *p, char *buf)
 +{
 +#if 0
 +      return br_show_bridge_id(buf, &p->designated_root);
 +#else
 +      return sprintf(buf, "0000.010203040506\n");
 +#endif
 +}
 +static BRPORT_ATTR(designated_root, S_IRUGO, show_designated_root, NULL);
 +
 +static ssize_t show_designated_bridge(struct net_bridge_port *p, char *buf)
 +{
 +#if 0
 +      return br_show_bridge_id(buf, &p->designated_bridge);
 +#else
 +      return sprintf(buf, "0000.060504030201\n");
 +#endif
 +}
 +static BRPORT_ATTR(designated_bridge, S_IRUGO, show_designated_bridge, NULL);
 +
 +static ssize_t show_designated_port(struct net_bridge_port *p, char *buf)
 +{
 +#if 0
 +      return sprintf(buf, "%d\n", p->designated_port);
 +#else
 +      return sprintf(buf, "%d\n", 0);
 +#endif
 +}
 +static BRPORT_ATTR(designated_port, S_IRUGO, show_designated_port, NULL);
 +
 +static ssize_t show_designated_cost(struct net_bridge_port *p, char *buf)
 +{
 +#if 0
 +      return sprintf(buf, "%d\n", p->designated_cost);
 +#else
 +      return sprintf(buf, "%d\n", 0);
 +#endif
 +}
 +static BRPORT_ATTR(designated_cost, S_IRUGO, show_designated_cost, NULL);
 +
 +static ssize_t show_port_id(struct net_bridge_port *p, char *buf)
 +{
 +#if 0
 +      return sprintf(buf, "0x%x\n", p->port_id);
 +#else
 +      return sprintf(buf, "0x%x\n", 0);
 +#endif
 +}
 +static BRPORT_ATTR(port_id, S_IRUGO, show_port_id, NULL);
 +
 +static ssize_t show_port_no(struct net_bridge_port *p, char *buf)
 +{
 +      return sprintf(buf, "0x%x\n", p->port_no);
 +}
 +
 +static BRPORT_ATTR(port_no, S_IRUGO, show_port_no, NULL);
 +
 +static ssize_t show_change_ack(struct net_bridge_port *p, char *buf)
 +{
 +#if 0
 +      return sprintf(buf, "%d\n", p->topology_change_ack);
 +#else
 +      return sprintf(buf, "%d\n", 0);
 +#endif
 +}
 +static BRPORT_ATTR(change_ack, S_IRUGO, show_change_ack, NULL);
 +
 +static ssize_t show_config_pending(struct net_bridge_port *p, char *buf)
 +{
 +#if 0
 +      return sprintf(buf, "%d\n", p->config_pending);
 +#else
 +      return sprintf(buf, "%d\n", 0);
 +#endif
 +}
 +static BRPORT_ATTR(config_pending, S_IRUGO, show_config_pending, NULL);
 +
 +static ssize_t show_port_state(struct net_bridge_port *p, char *buf)
 +{
 +#if 0
 +      return sprintf(buf, "%d\n", p->state);
 +#else
 +      return sprintf(buf, "%d\n", 0);
 +#endif
 +}
 +static BRPORT_ATTR(state, S_IRUGO, show_port_state, NULL);
 +
 +static ssize_t show_message_age_timer(struct net_bridge_port *p,
 +                                          char *buf)
 +{
 +#if 0
 +      return sprintf(buf, "%ld\n", br_timer_value(&p->message_age_timer));
 +#else
 +      return sprintf(buf, "%d\n", 0);
 +#endif
 +}
 +static BRPORT_ATTR(message_age_timer, S_IRUGO, show_message_age_timer, NULL);
 +
 +static ssize_t show_forward_delay_timer(struct net_bridge_port *p,
 +                                          char *buf)
 +{
 +#if 0
 +      return sprintf(buf, "%ld\n", br_timer_value(&p->forward_delay_timer));
 +#else
 +      return sprintf(buf, "%d\n", 0);
 +#endif
 +}
 +static BRPORT_ATTR(forward_delay_timer, S_IRUGO, show_forward_delay_timer, NULL);
 +
 +static ssize_t show_hold_timer(struct net_bridge_port *p,
 +                                          char *buf)
 +{
 +#if 0
 +      return sprintf(buf, "%ld\n", br_timer_value(&p->hold_timer));
 +#else
 +      return sprintf(buf, "%d\n", 0);
 +#endif
 +}
 +static BRPORT_ATTR(hold_timer, S_IRUGO, show_hold_timer, NULL);
 +
 +static struct brport_attribute *brport_attrs[] = {
 +      &brport_attr_path_cost,
 +      &brport_attr_priority,
 +      &brport_attr_port_id,
 +      &brport_attr_port_no,
 +      &brport_attr_designated_root,
 +      &brport_attr_designated_bridge,
 +      &brport_attr_designated_port,
 +      &brport_attr_designated_cost,
 +      &brport_attr_state,
 +      &brport_attr_change_ack,
 +      &brport_attr_config_pending,
 +      &brport_attr_message_age_timer,
 +      &brport_attr_forward_delay_timer,
 +      &brport_attr_hold_timer,
 +      NULL
 +};
 +
 +#define to_brport_attr(_at) container_of(_at, struct brport_attribute, attr)
 +#define to_brport(obj)        container_of(obj, struct net_bridge_port, kobj)
 +
 +static ssize_t brport_show(struct kobject * kobj,
 +                         struct attribute * attr, char * buf)
 +{
 +      struct brport_attribute * brport_attr = to_brport_attr(attr);
 +      struct net_bridge_port * p = to_brport(kobj);
 +
 +      return brport_attr->show(p, buf);
 +}
 +
 +static ssize_t brport_store(struct kobject * kobj,
 +                          struct attribute * attr,
 +                          const char * buf, size_t count)
 +{
 +      struct net_bridge_port * p = to_brport(kobj);
 +#if 0
 +      struct brport_attribute * brport_attr = to_brport_attr(attr);
 +      char *endp;
 +      unsigned long val;
 +#endif
 +      ssize_t ret = -EINVAL;
 +
 +      if (!capable(CAP_NET_ADMIN))
 +              return -EPERM;
 +
 +#if 0
 +      val = simple_strtoul(buf, &endp, 0);
 +      if (endp != buf) {
 +              rtnl_lock();
 +              if (p->dev && p->br && brport_attr->store) {
 +                      spin_lock_bh(&p->br->lock);
 +                      ret = brport_attr->store(p, val);
 +                      spin_unlock_bh(&p->br->lock);
 +                      if (ret == 0)
 +                              ret = count;
 +              }
 +              rtnl_unlock();
 +      }
 +#else
 +      printk("%s: xxx writing port parms not supported yet!\n", 
 +             dp_name(p->dp));
 +#endif
 +      return ret;
 +}
 +
 +struct sysfs_ops brport_sysfs_ops = {
 +      .show = brport_show,
 +      .store = brport_store,
 +};
 +
 +/*
 + * 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 dp_sysfs_add_if(struct net_bridge_port *p)
 +{
 +      struct datapath *dp = p->dp;
 +      struct brport_attribute **a;
 +      int err;
 +
 +      /* Create /sys/class/net/<devname>/brport directory. */
-               goto err_put;
++      err = kobject_add(&p->kobj, &p->dev->NETDEV_DEV_MEMBER.kobj,
++                        SYSFS_BRIDGE_PORT_ATTR);
 +      if (err)
-       return err;
++              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); /* "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;
 +      }
 +
 +      /* 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);
 +      if (err)
 +              goto err_del;
 +      strcpy(p->linkname, p->dev->name);
 +
 +      kobject_uevent(&p->kobj, KOBJ_ADD);
 +
- err_put:
-       kobject_put(&p->kobj);
-       /* Ensure that dp_sysfs_del_if becomes a no-op. */
-       p->kobj.dentry = NULL;
++      return 0;
 +
 +err_del:
 +      kobject_del(&p->kobj);
-               p->linkname[0] = '\0';
-       }
-       if (p->kobj.dentry) {
++err:
++      p->linkname[0] = 0;
 +      return err;
 +}
 +
 +int dp_sysfs_del_if(struct net_bridge_port *p)
 +{
 +      if (p->linkname[0]) {
 +              sysfs_remove_link(&p->dp->ifobj, p->linkname);
- #endif /* SUPPORT_SYSFS */
 +              kobject_uevent(&p->kobj, KOBJ_REMOVE);
 +              kobject_del(&p->kobj);
++              p->linkname[0] = '\0';
 +      }
 +      return 0;
 +}
++#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 */
@@@ -133,17 -133,17 +133,17 @@@ MGMT_VCONNS="punix:/var/run/ovs-openflo
  #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.
@@@ -923,10 -927,9 +927,9 @@@ do_receive_msg(struct dhclient *cli, st
          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;
          }
  
diff --cc lib/rconn.c
Simple merge
index 0000000,0e88729..60890d4
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,140 +1,141 @@@
 -            VLOG_WARN("Could not connect to controller for %d seconds, "
 -                      "failing open", disconn_secs);
+ /*
+  * Copyright (c) 2008, 2009 Nicira Networks.
+  *
+  * Licensed under the Apache License, Version 2.0 (the "License");
+  * you may not use this file except in compliance with the License.
+  * You may obtain a copy of the License at:
+  *
+  *     http://www.apache.org/licenses/LICENSE-2.0
+  *
+  * Unless required by applicable law or agreed to in writing, software
+  * distributed under the License is distributed on an "AS IS" BASIS,
+  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  * See the License for the specific language governing permissions and
+  * limitations under the License.
+  */
+ #include <config.h>
+ #include "fail-open.h"
+ #include <inttypes.h>
+ #include <stdlib.h>
+ #include "flow.h"
+ #include "mac-learning.h"
+ #include "odp-util.h"
+ #include "ofproto.h"
+ #include "rconn.h"
+ #include "status.h"
+ #include "timeval.h"
+ #define THIS_MODULE VLM_fail_open
+ #include "vlog.h"
+ struct fail_open {
+     struct ofproto *ofproto;
+     struct rconn *controller;
+     int trigger_duration;
+     int last_disconn_secs;
+     struct status_category *ss_cat;
+ };
+ /* Causes the switch to enter or leave fail-open mode, if appropriate. */
+ void
+ fail_open_run(struct fail_open *fo)
+ {
+     int disconn_secs = rconn_failure_duration(fo->controller);
+     bool open = disconn_secs >= fo->trigger_duration;
+     if (open != (fo->last_disconn_secs != 0)) {
+         if (!open) {
+             flow_t flow;
+             VLOG_WARN("No longer in fail-open mode");
+             fo->last_disconn_secs = 0;
+             memset(&flow, 0, sizeof flow);
+             ofproto_delete_flow(fo->ofproto, &flow, OFPFW_ALL, 70000);
+         } else {
++            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
+              * fail-open rule from fail_open_flushed() when
+              * ofproto_flush_flows() calls back to us. */
+             ofproto_flush_flows(fo->ofproto);
+         }
+     } else if (open && disconn_secs > fo->last_disconn_secs + 60) {
+         VLOG_INFO("Still in fail-open mode after %d seconds disconnected "
+                   "from controller", disconn_secs);
+         fo->last_disconn_secs = disconn_secs;
+     }
+ }
+ void
+ fail_open_wait(struct fail_open *fo UNUSED)
+ {
+     /* Nothing to do. */
+ }
+ void
+ fail_open_flushed(struct fail_open *fo)
+ {
+     int disconn_secs = rconn_failure_duration(fo->controller);
+     bool open = disconn_secs >= fo->trigger_duration;
+     if (open) {
+         union ofp_action action;
+         flow_t flow;
+         /* Set up a flow that matches every packet and directs them to
+          * OFPP_NORMAL. */
+         memset(&action, 0, sizeof action);
+         action.type = htons(OFPAT_OUTPUT);
+         action.output.len = htons(sizeof action);
+         action.output.port = htons(OFPP_NORMAL);
+         memset(&flow, 0, sizeof flow);
+         ofproto_add_flow(fo->ofproto, &flow, OFPFW_ALL, 70000,
+                          &action, 1, 0);
+     }
+ }
+ static void
+ fail_open_status_cb(struct status_reply *sr, void *fo_)
+ {
+     struct fail_open *fo = fo_;
+     int cur_duration = rconn_failure_duration(fo->controller);
+     status_reply_put(sr, "trigger-duration=%d", fo->trigger_duration);
+     status_reply_put(sr, "current-duration=%d", cur_duration);
+     status_reply_put(sr, "triggered=%s",
+                      cur_duration >= fo->trigger_duration ? "true" : "false");
+ }
+ struct fail_open *
+ fail_open_create(struct ofproto *ofproto,
+                  int trigger_duration, struct switch_status *switch_status,
+                  struct rconn *controller)
+ {
+     struct fail_open *fo = xmalloc(sizeof *fo);
+     fo->ofproto = ofproto;
+     fo->controller = controller;
+     fo->trigger_duration = trigger_duration;
+     fo->last_disconn_secs = 0;
+     fo->ss_cat = switch_status_register(switch_status, "fail-open",
+                                         fail_open_status_cb, fo);
+     return fo;
+ }
+ void
+ fail_open_set_trigger_duration(struct fail_open *fo, int trigger_duration)
+ {
+     fo->trigger_duration = trigger_duration;
+ }
+ void
+ fail_open_destroy(struct fail_open *fo)
+ {
+     if (fo) {
+         /* We don't own fo->controller. */
+         switch_status_unregister(fo->ss_cat);
+         free(fo);
+     }
+ }
index 0000000,e867c0e..0162c45
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,328 +1,328 @@@
 -    char *save_ptr;
+ /*
+  * Copyright (c) 2008, 2009 Nicira Networks.
+  *
+  * Licensed under the Apache License, Version 2.0 (the "License");
+  * you may not use this file except in compliance with the License.
+  * You may obtain a copy of the License at:
+  *
+  *     http://www.apache.org/licenses/LICENSE-2.0
+  *
+  * Unless required by applicable law or agreed to in writing, software
+  * distributed under the License is distributed on an "AS IS" BASIS,
+  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  * See the License for the specific language governing permissions and
+  * limitations under the License.
+  */
+ #include <config.h>
+ #include "netflow.h"
+ #include <arpa/inet.h>
+ #include <errno.h>
+ #include <stdlib.h>
+ #include <unistd.h>
+ #include "cfg.h"
+ #include "flow.h"
+ #include "netflow.h"
+ #include "ofpbuf.h"
+ #include "ofproto.h"
+ #include "packets.h"
+ #include "socket-util.h"
+ #include "svec.h"
+ #include "timeval.h"
+ #include "util.h"
+ #include "xtoxll.h"
+ #define THIS_MODULE VLM_netflow
+ #include "vlog.h"
+ #define NETFLOW_V5_VERSION 5
+ /* Every NetFlow v5 message contains the header that follows.  This is
+  * followed by up to thirty records that describe a terminating flow.
+  * We only send a single record per NetFlow message.
+  */
+ struct netflow_v5_header {
+     uint16_t version;              /* NetFlow version is 5. */
+     uint16_t count;                /* Number of records in this message. */
+     uint32_t sysuptime;            /* System uptime in milliseconds. */
+     uint32_t unix_secs;            /* Number of seconds since Unix epoch. */
+     uint32_t unix_nsecs;           /* Number of residual nanoseconds
+                                       after epoch seconds. */
+     uint32_t flow_seq;             /* Number of flows since sending
+                                       messages began. */
+     uint8_t  engine_type;          /* Engine type. */
+     uint8_t  engine_id;            /* Engine id. */
+     uint16_t sampling_interval;    /* Set to zero. */
+ };
+ BUILD_ASSERT_DECL(sizeof(struct netflow_v5_header) == 24);
+ /* A NetFlow v5 description of a terminating flow.  It is preceded by a
+  * NetFlow v5 header.
+  */
+ struct netflow_v5_record {
+     uint32_t src_addr;             /* Source IP address. */
+     uint32_t dst_addr;             /* Destination IP address. */
+     uint32_t nexthop;              /* IP address of next hop.  Set to 0. */
+     uint16_t input;                /* Input interface index. */
+     uint16_t output;               /* Output interface index. */
+     uint32_t packet_count;         /* Number of packets. */
+     uint32_t byte_count;           /* Number of bytes. */
+     uint32_t init_time;            /* Value of sysuptime on first packet. */
+     uint32_t used_time;            /* Value of sysuptime on last packet. */
+     /* The 'src_port' and 'dst_port' identify the source and destination
+      * port, respectively, for TCP and UDP.  For ICMP, the high-order
+      * byte identifies the type and low-order byte identifies the code
+      * in the 'dst_port' field. */
+     uint16_t src_port;
+     uint16_t dst_port;
+     uint8_t  pad1;
+     uint8_t  tcp_flags;            /* Union of seen TCP flags. */
+     uint8_t  ip_proto;             /* IP protocol. */
+     uint8_t  ip_tos;               /* IP TOS value. */
+     uint16_t src_as;               /* Source AS ID.  Set to 0. */
+     uint16_t dst_as;               /* Destination AS ID.  Set to 0. */
+     uint8_t  src_mask;             /* Source mask bits.  Set to 0. */
+     uint8_t  dst_mask;             /* Destination mask bits.  Set to 0. */
+     uint8_t  pad[2];
+ };
+ BUILD_ASSERT_DECL(sizeof(struct netflow_v5_record) == 48);
+ struct netflow {
+     uint8_t engine_type;          /* Value of engine_type to use. */
+     uint8_t engine_id;            /* Value of engine_id to use. */
+     long long int boot_time;      /* Time when netflow_create() was called. */
+     int *fds;                     /* Sockets for NetFlow collectors. */
+     size_t n_fds;                 /* Number of Netflow collectors. */
+     bool add_id_to_iface;         /* Put the 7 least signficiant bits of 
+                                    * 'engine_id' into the most signficant 
+                                    * bits of the interface fields. */
+     uint32_t netflow_cnt;         /* Flow sequence number for NetFlow. */
+     struct ofpbuf packet;         /* NetFlow packet being accumulated. */
+ };
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+ static int
+ open_collector(char *dst)
+ {
++    char *save_ptr = NULL;
+     const char *host_name;
+     const char *port_string;
+     struct sockaddr_in sin;
+     int retval;
+     int fd;
+     /* Glibc 2.7 has a bug in strtok_r when compiling with optimization that
+      * can cause segfaults here:
+      * http://sources.redhat.com/bugzilla/show_bug.cgi?id=5614.
+      * Using "::" instead of the obvious ":" works around it. */
+     host_name = strtok_r(dst, ":", &save_ptr);
+     port_string = strtok_r(NULL, ":", &save_ptr);
+     if (!host_name) {
+         ovs_error(0, "%s: bad peer name format", dst);
+         return -EAFNOSUPPORT;
+     }
+     if (!port_string) {
+         ovs_error(0, "%s: bad port format", dst);
+         return -EAFNOSUPPORT;
+     }
+     memset(&sin, 0, sizeof sin);
+     sin.sin_family = AF_INET;
+     if (lookup_ip(host_name, &sin.sin_addr)) {
+         return -ENOENT;
+     }
+     sin.sin_port = htons(atoi(port_string));
+     fd = socket(AF_INET, SOCK_DGRAM, 0);
+     if (fd < 0) {
+         VLOG_ERR("%s: socket: %s", dst, strerror(errno));
+         return -errno;
+     }
+     retval = set_nonblocking(fd);
+     if (retval) {
+         close(fd);
+         return -retval;
+     }
+     retval = connect(fd, (struct sockaddr *) &sin, sizeof sin);
+     if (retval < 0) {
+         int error = errno;
+         VLOG_ERR("%s: connect: %s", dst, strerror(error));
+         close(fd);
+         return -error;
+     }
+     return fd;
+ }
+ void
+ netflow_expire(struct netflow *nf, const struct ofexpired *expired)
+ {
+     struct netflow_v5_header *nf_hdr;
+     struct netflow_v5_record *nf_rec;
+     struct timeval now;
+     /* NetFlow only reports on IP packets. */
+     if (expired->flow.dl_type != htons(ETH_TYPE_IP)) {
+         return;
+     }
+     time_timeval(&now);
+     if (!nf->packet.size) {
+         nf_hdr = ofpbuf_put_zeros(&nf->packet, sizeof *nf_hdr);
+         nf_hdr->version = htons(NETFLOW_V5_VERSION);
+         nf_hdr->count = htons(0);
+         nf_hdr->sysuptime = htonl(time_msec() - nf->boot_time);
+         nf_hdr->unix_secs = htonl(now.tv_sec);
+         nf_hdr->unix_nsecs = htonl(now.tv_usec * 1000);
+         nf_hdr->flow_seq = htonl(nf->netflow_cnt++);
+         nf_hdr->engine_type = nf->engine_type;
+         nf_hdr->engine_id = nf->engine_id;
+         nf_hdr->sampling_interval = htons(0);
+     }
+     nf_hdr = nf->packet.data;
+     nf_hdr->count = htons(ntohs(nf_hdr->count) + 1);
+     nf_rec = ofpbuf_put_zeros(&nf->packet, sizeof *nf_rec);
+     nf_rec->src_addr = expired->flow.nw_src;
+     nf_rec->dst_addr = expired->flow.nw_dst;
+     nf_rec->nexthop = htons(0);
+     if (nf->add_id_to_iface) {
+         uint16_t iface = (nf->engine_id & 0x7f) << 9;
+         nf_rec->input = htons(iface | (expired->flow.in_port & 0x1ff));
+         nf_rec->output = htons(iface);
+         printf("input: %x\n", ntohs(nf_rec->input));
+     } else {
+         nf_rec->input = htons(expired->flow.in_port);
+         nf_rec->output = htons(0);
+     }
+     nf_rec->packet_count = htonl(MIN(expired->packet_count, UINT32_MAX));
+     nf_rec->byte_count = htonl(MIN(expired->byte_count, UINT32_MAX));
+     nf_rec->init_time = htonl(expired->created - nf->boot_time);
+     nf_rec->used_time = htonl(MAX(expired->created, expired->used)
+                              - nf->boot_time);
+     if (expired->flow.nw_proto == IP_TYPE_ICMP) {
+         /* In NetFlow, the ICMP type and code are concatenated and
+          * placed in the 'dst_port' field. */
+         uint8_t type = ntohs(expired->flow.tp_src);
+         uint8_t code = ntohs(expired->flow.tp_dst);
+         nf_rec->src_port = htons(0);
+         nf_rec->dst_port = htons((type << 8) | code);
+     } else {
+         nf_rec->src_port = expired->flow.tp_src;
+         nf_rec->dst_port = expired->flow.tp_dst;
+     }
+     nf_rec->tcp_flags = expired->tcp_flags;
+     nf_rec->ip_proto = expired->flow.nw_proto;
+     nf_rec->ip_tos = expired->ip_tos;
+     /* NetFlow messages are limited to 30 records.  A length of 1400
+      * bytes guarantees that the limit is not exceeded.  */
+     if (nf->packet.size >= 1400) {
+         netflow_run(nf);
+     }
+ }
+ void
+ netflow_run(struct netflow *nf)
+ {
+     size_t i;
+     if (!nf->packet.size) {
+         return;
+     }
+     for (i = 0; i < nf->n_fds; i++) {
+         if (send(nf->fds[i], nf->packet.data, nf->packet.size, 0) == -1) {
+             VLOG_WARN_RL(&rl, "netflow message send failed: %s",
+                          strerror(errno));
+         }
+     }
+     nf->packet.size = 0;
+ }
+ static void
+ clear_collectors(struct netflow *nf)
+ {
+     size_t i;
+     for (i = 0; i < nf->n_fds; i++) {
+         close(nf->fds[i]);
+     }
+     free(nf->fds);
+     nf->fds = NULL;
+     nf->n_fds = 0;
+ }
+ int
+ netflow_set_collectors(struct netflow *nf, const struct svec *collectors_)
+ {
+     struct svec collectors;
+     int error = 0;
+     size_t i;
+     clear_collectors(nf);
+     svec_clone(&collectors, collectors_);
+     svec_sort_unique(&collectors);
+     nf->fds = xmalloc(sizeof *nf->fds * collectors.n);
+     for (i = 0; i < collectors.n; i++) {
+         const char *name = collectors.names[i];
+         char *tmpname = xstrdup(name);
+         int fd = open_collector(tmpname);
+         free(tmpname);
+         if (fd >= 0) {
+             nf->fds[nf->n_fds++] = fd;
+         } else {
+             VLOG_WARN("couldn't open connection to collector (%s), "
+                       "ignoring %s\n", strerror(-fd), name);
+             if (!error) {
+                 error = -fd;
+             }
+         }
+     }
+     svec_destroy(&collectors);
+     return error;
+ }
+ void 
+ netflow_set_engine(struct netflow *nf, uint8_t engine_type, 
+         uint8_t engine_id, bool add_id_to_iface)
+ {
+     nf->engine_type = engine_type;
+     nf->engine_id = engine_id;
+     nf->add_id_to_iface = add_id_to_iface;
+ }
+ struct netflow *
+ netflow_create(void)
+ {
+     struct netflow *nf = xmalloc(sizeof *nf);
+     nf->engine_type = 0;
+     nf->engine_id = 0;
+     nf->boot_time = time_msec();
+     nf->fds = NULL;
+     nf->n_fds = 0;
+     nf->add_id_to_iface = false;
+     nf->netflow_cnt = 0;
+     ofpbuf_init(&nf->packet, 1500);
+     return nf;
+ }
+ void
+ netflow_destroy(struct netflow *nf)
+ {
+     if (nf) {
+         ofpbuf_uninit(&nf->packet);
+         clear_collectors(nf);
+         free(nf);
+     }
+ }
index 0000000,55eb2c2..c26cdc2
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,3316 +1,3389 @@@
 -    p->controller = ofconn_create(p, rconn_create(15, 15));
+ /*
+  * Copyright (c) 2009 Nicira Networks.
+  *
+  * Licensed under the Apache License, Version 2.0 (the "License");
+  * you may not use this file except in compliance with the License.
+  * You may obtain a copy of the License at:
+  *
+  *     http://www.apache.org/licenses/LICENSE-2.0
+  *
+  * Unless required by applicable law or agreed to in writing, software
+  * distributed under the License is distributed on an "AS IS" BASIS,
+  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  * See the License for the specific language governing permissions and
+  * limitations under the License.
+  */
+ #include <config.h>
+ #include "ofproto.h"
+ #include <errno.h>
+ #include <inttypes.h>
+ #include <net/if.h>
+ #include <netinet/in.h>
+ #include <stdbool.h>
+ #include <stdlib.h>
+ #include "classifier.h"
+ #include "coverage.h"
+ #include "discovery.h"
+ #include "dpif.h"
++#include "dynamic-string.h"
+ #include "executer.h"
+ #include "fail-open.h"
+ #include "in-band.h"
+ #include "mac-learning.h"
+ #include "netdev.h"
+ #include "netflow.h"
+ #include "odp-util.h"
+ #include "ofp-print.h"
+ #include "ofpbuf.h"
+ #include "openflow/nicira-ext.h"
+ #include "openflow/openflow.h"
+ #include "openflow/openflow-mgmt.h"
+ #include "openvswitch/datapath-protocol.h"
+ #include "packets.h"
+ #include "pinsched.h"
+ #include "pktbuf.h"
+ #include "poll-loop.h"
+ #include "port-array.h"
+ #include "rconn.h"
+ #include "shash.h"
+ #include "status.h"
+ #include "stp.h"
+ #include "svec.h"
+ #include "tag.h"
+ #include "timeval.h"
++#include "unixctl.h"
+ #include "vconn.h"
+ #include "vconn-ssl.h"
+ #include "xtoxll.h"
+ #define THIS_MODULE VLM_ofproto
+ #include "vlog.h"
+ enum {
+     DP_GROUP_FLOOD = 0,
+     DP_GROUP_ALL = 1
+ };
+ enum {
+     TABLEID_HASH = 0,
+     TABLEID_CLASSIFIER = 1
+ };
+ struct ofport {
+     struct netdev *netdev;
+     struct ofp_phy_port opp;    /* In host byte order. */
+ };
+ static void ofport_free(struct ofport *);
+ static void hton_ofp_phy_port(struct ofp_phy_port *);
+ static int xlate_actions(const union ofp_action *in, size_t n_in,
+                          const flow_t *flow, struct ofproto *ofproto,
+                          const struct ofpbuf *packet,
+                          struct odp_actions *out, tag_type *tags,
+                          bool *may_setup_flow);
+ struct rule {
+     struct cls_rule cr;
+     uint16_t idle_timeout;      /* In seconds from time of last use. */
+     uint16_t hard_timeout;      /* In seconds from time of creation. */
+     long long int used;         /* Last-used time (0 if never used). */
+     long long int created;      /* Creation time. */
+     uint64_t packet_count;      /* Number of packets received. */
+     uint64_t byte_count;        /* Number of bytes received. */
+     uint64_t accounted_bytes;   /* Number of bytes passed to account_cb. */
+     uint8_t tcp_flags;          /* Bitwise-OR of all TCP flags seen. */
+     uint8_t ip_tos;             /* Last-seen IP type-of-service. */
+     tag_type tags;              /* Tags (set only by hooks). */
+     /* If 'super' is non-NULL, this rule is a subrule, that is, it is an
+      * exact-match rule (having cr.wc.wildcards of 0) generated from the
+      * wildcard rule 'super'.  In this case, 'list' is an element of the
+      * super-rule's list.
+      *
+      * If 'super' is NULL, this rule is a super-rule, and 'list' is the head of
+      * a list of subrules.  A super-rule with no wildcards (where
+      * cr.wc.wildcards is 0) will never have any subrules. */
+     struct rule *super;
+     struct list list;
+     /* OpenFlow actions.
+      *
+      * A subrule has no actions (it uses the super-rule's actions). */
+     int n_actions;
+     union ofp_action *actions;
+     /* Datapath actions.
+      *
+      * A super-rule with wildcard fields never has ODP actions (since the
+      * datapath only supports exact-match flows). */
+     bool installed;             /* Installed in datapath? */
+     bool may_install;           /* True ordinarily; false if actions must
+                                  * be reassessed for every packet. */
+     int n_odp_actions;
+     union odp_action *odp_actions;
+ };
+ static inline bool
+ rule_is_hidden(const struct rule *rule)
+ {
+     /* Subrules are merely an implementation detail, so hide them from the
+      * controller. */
+     if (rule->super != NULL) {
+         return true;
+     }
+     /* 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) {
+         return true;
+     }
+     return false;
+ }
+ static struct rule *rule_create(struct rule *super, const union ofp_action *,
+                                 size_t n_actions, uint16_t idle_timeout,
+                                 uint16_t hard_timeout);
+ static void rule_free(struct rule *);
+ static void rule_destroy(struct ofproto *, struct rule *);
+ static struct rule *rule_from_cls_rule(const struct cls_rule *);
+ static void rule_insert(struct ofproto *, struct rule *,
+                         struct ofpbuf *packet, uint16_t in_port);
+ static void rule_remove(struct ofproto *, struct rule *);
+ static bool rule_make_actions(struct ofproto *, struct rule *,
+                               const struct ofpbuf *packet);
+ static void rule_install(struct ofproto *, struct rule *,
+                          struct rule *displaced_rule);
+ static void rule_uninstall(struct ofproto *, struct rule *);
+ static void rule_post_uninstall(struct ofproto *, struct rule *);
+ struct ofconn {
+     struct list node;
+     struct rconn *rconn;
+     struct pktbuf *pktbuf;
+     bool send_flow_exp;
+     int miss_send_len;
+     struct rconn_packet_counter *packet_in_counter;
+     /* Number of OpenFlow messages queued as replies to OpenFlow requests, and
+      * the maximum number before we stop reading OpenFlow requests.  */
+ #define OFCONN_REPLY_MAX 100
+     struct rconn_packet_counter *reply_counter;
+ };
+ static struct ofconn *ofconn_create(struct ofproto *, struct rconn *);
+ static void ofconn_destroy(struct ofconn *, struct ofproto *);
+ static void ofconn_run(struct ofconn *, struct ofproto *);
+ static void ofconn_wait(struct ofconn *);
+ static void queue_tx(struct ofpbuf *msg, const struct ofconn *ofconn,
+                      struct rconn_packet_counter *counter);
+ struct ofproto {
+     /* Settings. */
+     uint64_t datapath_id;       /* Datapath ID. */
+     uint64_t fallback_dpid;     /* Datapath ID if no better choice found. */
+     uint64_t mgmt_id;           /* Management channel identifier. */
+     char *manufacturer;         /* Manufacturer. */
+     char *hardware;             /* Hardware. */
+     char *software;             /* Software version. */
+     char *serial;               /* Serial number. */
+     /* Datapath. */
+     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;
+     uint32_t max_ports;
+     /* Configuration. */
+     struct switch_status *switch_status;
+     struct status_category *ss_cat;
+     struct in_band *in_band;
+     struct discovery *discovery;
+     struct fail_open *fail_open;
+     struct pinsched *miss_sched, *action_sched;
+     struct executer *executer;
+     struct netflow *netflow;
+     /* Flow table. */
+     struct classifier cls;
+     bool need_revalidate;
+     long long int next_expiration;
+     struct tag_set revalidate_set;
+     /* OpenFlow connections. */
+     struct list all_conns;
+     struct ofconn *controller;
+     struct pvconn **listeners;
+     size_t n_listeners;
+     struct pvconn **snoops;
+     size_t n_snoops;
+     /* Hooks for ovs-vswitchd. */
+     const struct ofhooks *ofhooks;
+     void *aux;
+     /* Used by default ofhooks. */
+     struct mac_learning *ml;
+ };
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+ static const struct ofhooks default_ofhooks;
+ 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);
+ static void update_used(struct ofproto *);
+ static void update_stats(struct rule *, const struct odp_flow_stats *);
+ static void expire_rule(struct cls_rule *, void *ofproto);
+ static bool revalidate_rule(struct ofproto *p, struct rule *rule);
+ static void revalidate_cb(struct cls_rule *rule_, void *p_);
+ static void handle_odp_msg(struct ofproto *, struct ofpbuf *);
+ static void handle_openflow(struct ofconn *, struct ofproto *,
+                             struct ofpbuf *);
+ static void refresh_port_group(struct ofproto *, unsigned int group);
+ static void update_port(struct ofproto *, const char *devname);
+ static int init_ports(struct ofproto *);
+ static void reinit_ports(struct ofproto *);
+ int
+ ofproto_create(const char *datapath, const struct ofhooks *ofhooks, void *aux,
+                struct ofproto **ofprotop)
+ {
+     struct odp_stats stats;
+     struct ofproto *p;
+     struct dpif *dpif;
+     int error;
+     *ofprotop = NULL;
+     /* Connect to datapath and start listening for messages. */
+     error = dpif_open(datapath, &dpif);
+     if (error) {
+         VLOG_ERR("failed to open datapath %s: %s", datapath, strerror(error));
+         return error;
+     }
+     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);
+         return error;
+     }
+     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_recv_purge(dpif);
+     /* Initialize settings. */
+     p = xcalloc(1, sizeof *p);
+     p->fallback_dpid = pick_fallback_dpid();
+     p->datapath_id = p->fallback_dpid;
+     p->manufacturer = xstrdup("Nicira Networks, Inc.");
+     p->hardware = xstrdup("Reference Implementation");
+     p->software = xstrdup(VERSION BUILDNR);
+     p->serial = xstrdup("None");
+     /* Initialize datapath. */
+     p->dpif = dpif;
+     p->netdev_monitor = netdev_monitor_create();
+     port_array_init(&p->ports);
+     shash_init(&p->port_by_name);
+     p->max_ports = stats.max_ports;
+     /* Initialize submodules. */
+     p->switch_status = switch_status_create(p);
+     p->in_band = NULL;
+     p->discovery = NULL;
+     p->fail_open = NULL;
+     p->miss_sched = p->action_sched = NULL;
+     p->executer = NULL;
+     p->netflow = NULL;
+     /* Initialize flow table. */
+     classifier_init(&p->cls);
+     p->need_revalidate = false;
+     p->next_expiration = time_msec() + 1000;
+     tag_set_init(&p->revalidate_set);
+     /* Initialize OpenFlow connections. */
+     list_init(&p->all_conns);
 -    struct ofport *ofport;
++    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;
+     p->n_listeners = 0;
+     p->snoops = NULL;
+     p->n_snoops = 0;
+     /* Initialize hooks. */
+     if (ofhooks) {
+         p->ofhooks = ofhooks;
+         p->aux = aux;
+         p->ml = NULL;
+     } else {
+         p->ofhooks = &default_ofhooks;
+         p->aux = p;
+         p->ml = mac_learning_create();
+     }
+     /* Register switch status category. */
+     p->ss_cat = switch_status_register(p->switch_status, "remote",
+                                        rconn_status_cb, p->controller->rconn);
+     /* Almost done... */
+     error = init_ports(p);
+     if (error) {
+         ofproto_destroy(p);
+         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;
+ }
+ void
+ 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);
+     if (p->datapath_id != old_dpid) {
+         VLOG_INFO("datapath ID changed to %012"PRIx64, p->datapath_id);
+         rconn_reconnect(p->controller->rconn);
+     }
+ }
+ void
+ ofproto_set_mgmt_id(struct ofproto *p, uint64_t mgmt_id)
+ {
+     p->mgmt_id = mgmt_id;
+ }
+ void
+ ofproto_set_probe_interval(struct ofproto *p, int probe_interval)
+ {
+     probe_interval = probe_interval ? MAX(probe_interval, 5) : 0;
+     rconn_set_probe_interval(p->controller->rconn, probe_interval);
+     if (p->fail_open) {
+         int trigger_duration = probe_interval ? probe_interval * 3 : 15;
+         fail_open_set_trigger_duration(p->fail_open, trigger_duration);
+     }
+ }
+ void
+ ofproto_set_max_backoff(struct ofproto *p, int max_backoff)
+ {
+     rconn_set_max_backoff(p->controller->rconn, max_backoff);
+ }
+ void
+ ofproto_set_desc(struct ofproto *p,
+                  const char *manufacturer, const char *hardware,
+                  const char *software, const char *serial)
+ {
+     if (manufacturer) {
+         free(p->manufacturer);
+         p->manufacturer = xstrdup(manufacturer);
+     }
+     if (hardware) {
+         free(p->hardware);
+         p->hardware = xstrdup(hardware);
+     }
+     if (software) {
+         free(p->software);
+         p->software = xstrdup(software);
+     }
+     if (serial) {
+         free(p->serial);
+         p->serial = xstrdup(serial);
+     }
+ }
+ int
+ ofproto_set_in_band(struct ofproto *p, bool in_band)
+ {
+     if (in_band != (p->in_band != NULL)) {
+         if (in_band) {
+             in_band_create(p, p->switch_status, p->controller->rconn, 
+                            &p->in_band);
+             return 0;
+         } else {
+             ofproto_set_discovery(p, false, NULL, true);
+             in_band_destroy(p->in_band);
+             p->in_band = NULL;
+         }
+         rconn_reconnect(p->controller->rconn);
+     }
+     return 0;
+ }
+ int
+ ofproto_set_discovery(struct ofproto *p, bool discovery,
+                       const char *re, bool update_resolv_conf)
+ {
+     if (discovery != (p->discovery != NULL)) {
+         if (discovery) {
+             int error = ofproto_set_in_band(p, true);
+             if (error) {
+                 return error;
+             }
+             error = discovery_create(re, update_resolv_conf,
+                                      p->dpif, p->switch_status,
+                                      &p->discovery);
+             if (error) {
+                 return error;
+             }
+         } else {
+             discovery_destroy(p->discovery);
+             p->discovery = NULL;
+         }
+         rconn_disconnect(p->controller->rconn);
+     } else if (discovery) {
+         discovery_set_update_resolv_conf(p->discovery, update_resolv_conf);
+         return discovery_set_accept_controller_re(p->discovery, re);
+     }
+     return 0;
+ }
+ int
+ ofproto_set_controller(struct ofproto *ofproto, const char *controller)
+ {
+     if (ofproto->discovery) {
+         return EINVAL;
+     } else if (controller) {
+         if (strcmp(rconn_get_name(ofproto->controller->rconn), controller)) {
+             return rconn_connect(ofproto->controller->rconn, controller);
+         } else {
+             return 0;
+         }
+     } else {
+         rconn_disconnect(ofproto->controller->rconn);
+         return 0;
+     }
+ }
+ static int
+ set_pvconns(struct pvconn ***pvconnsp, size_t *n_pvconnsp,
+             const struct svec *svec)
+ {
+     struct pvconn **pvconns = *pvconnsp;
+     size_t n_pvconns = *n_pvconnsp;
+     int retval = 0;
+     size_t i;
+     for (i = 0; i < n_pvconns; i++) {
+         pvconn_close(pvconns[i]);
+     }
+     free(pvconns);
+     pvconns = xmalloc(svec->n * sizeof *pvconns);
+     n_pvconns = 0;
+     for (i = 0; i < svec->n; i++) {
+         const char *name = svec->names[i];
+         struct pvconn *pvconn;
+         int error;
+         error = pvconn_open(name, &pvconn);
+         if (!error) {
+             pvconns[n_pvconns++] = pvconn;
+         } else {
+             VLOG_ERR("failed to listen on %s: %s", name, strerror(error));
+             if (!retval) {
+                 retval = error;
+             }
+         }
+     }
+     *pvconnsp = pvconns;
+     *n_pvconnsp = n_pvconns;
+     return retval;
+ }
+ int
+ ofproto_set_listeners(struct ofproto *ofproto, const struct svec *listeners)
+ {
+     return set_pvconns(&ofproto->listeners, &ofproto->n_listeners, listeners);
+ }
+ int
+ ofproto_set_snoops(struct ofproto *ofproto, const struct svec *snoops)
+ {
+     return set_pvconns(&ofproto->snoops, &ofproto->n_snoops, snoops);
+ }
+ int
+ ofproto_set_netflow(struct ofproto *ofproto, const struct svec *collectors,
+         uint8_t engine_type, uint8_t engine_id, bool add_id_to_iface)
+ {
+     if (collectors && collectors->n) {
+         if (!ofproto->netflow) {
+             ofproto->netflow = netflow_create();
+         }
+         netflow_set_engine(ofproto->netflow, engine_type, engine_id, 
+                 add_id_to_iface);
+         return netflow_set_collectors(ofproto->netflow, collectors);
+     } else {
+         netflow_destroy(ofproto->netflow);
+         ofproto->netflow = NULL;
+         return 0;
+     }
+ }
+ void
+ ofproto_set_failure(struct ofproto *ofproto, bool fail_open)
+ {
+     if (fail_open) {
+         struct rconn *rconn = ofproto->controller->rconn;
+         int trigger_duration = rconn_get_probe_interval(rconn) * 3;
+         if (!ofproto->fail_open) {
+             ofproto->fail_open = fail_open_create(ofproto, trigger_duration,
+                                                   ofproto->switch_status,
+                                                   rconn);
+         } else {
+             fail_open_set_trigger_duration(ofproto->fail_open,
+                                            trigger_duration);
+         }
+     } else {
+         fail_open_destroy(ofproto->fail_open);
+         ofproto->fail_open = NULL;
+     }
+ }
+ void
+ ofproto_set_rate_limit(struct ofproto *ofproto,
+                        int rate_limit, int burst_limit)
+ {
+     if (rate_limit > 0) {
+         if (!ofproto->miss_sched) {
+             ofproto->miss_sched = pinsched_create(rate_limit, burst_limit,
+                                                   ofproto->switch_status);
+             ofproto->action_sched = pinsched_create(rate_limit, burst_limit,
+                                                     NULL);
+         } else {
+             pinsched_set_limits(ofproto->miss_sched, rate_limit, burst_limit);
+             pinsched_set_limits(ofproto->action_sched,
+                                 rate_limit, burst_limit);
+         }
+     } else {
+         pinsched_destroy(ofproto->miss_sched);
+         ofproto->miss_sched = NULL;
+         pinsched_destroy(ofproto->action_sched);
+         ofproto->action_sched = NULL;
+     }
+ }
+ int
+ ofproto_set_stp(struct ofproto *ofproto UNUSED, bool enable_stp)
+ {
+     /* XXX */
+     if (enable_stp) {
+         VLOG_WARN("STP is not yet implemented");
+         return EINVAL;
+     } else {
+         return 0;
+     }
+ }
+ int
+ ofproto_set_remote_execution(struct ofproto *ofproto, const char *command_acl,
+                              const char *command_dir)
+ {
+     if (command_acl) {
+         if (!ofproto->executer) {
+             return executer_create(command_acl, command_dir,
+                                    &ofproto->executer);
+         } else {
+             executer_set_acl(ofproto->executer, command_acl, command_dir);
+         }
+     } else {
+         executer_destroy(ofproto->executer);
+         ofproto->executer = NULL;
+     }
+     return 0;
+ }
+ uint64_t
+ ofproto_get_datapath_id(const struct ofproto *ofproto)
+ {
+     return ofproto->datapath_id;
+ }
+ uint64_t
+ ofproto_get_mgmt_id(const struct ofproto *ofproto)
+ {
+     return ofproto->mgmt_id;
+ }
+ int
+ ofproto_get_probe_interval(const struct ofproto *ofproto)
+ {
+     return rconn_get_probe_interval(ofproto->controller->rconn);
+ }
+ int
+ ofproto_get_max_backoff(const struct ofproto *ofproto)
+ {
+     return rconn_get_max_backoff(ofproto->controller->rconn);
+ }
+ bool
+ ofproto_get_in_band(const struct ofproto *ofproto)
+ {
+     return ofproto->in_band != NULL;
+ }
+ bool
+ ofproto_get_discovery(const struct ofproto *ofproto)
+ {
+     return ofproto->discovery != NULL;
+ }
+ const char *
+ ofproto_get_controller(const struct ofproto *ofproto)
+ {
+     return rconn_get_name(ofproto->controller->rconn);
+ }
+ void
+ ofproto_get_listeners(const struct ofproto *ofproto, struct svec *listeners)
+ {
+     size_t i;
+     for (i = 0; i < ofproto->n_listeners; i++) {
+         svec_add(listeners, pvconn_get_name(ofproto->listeners[i]));
+     }
+ }
+ void
+ ofproto_get_snoops(const struct ofproto *ofproto, struct svec *snoops)
+ {
+     size_t i;
+     for (i = 0; i < ofproto->n_snoops; i++) {
+         svec_add(snoops, pvconn_get_name(ofproto->snoops[i]));
+     }
+ }
+ void
+ ofproto_destroy(struct ofproto *p)
+ {
+     struct ofconn *ofconn, *next_ofconn;
+     struct ofport *ofport;
+     unsigned int port_no;
+     size_t i;
+     if (!p) {
+         return;
+     }
+     ofproto_flush_flows(p);
+     classifier_destroy(&p->cls);
+     LIST_FOR_EACH_SAFE (ofconn, next_ofconn, struct ofconn, node,
+                         &p->all_conns) {
+         ofconn_destroy(ofconn, p);
+     }
+     dpif_close(p->dpif);
+     netdev_monitor_destroy(p->netdev_monitor);
+     PORT_ARRAY_FOR_EACH (ofport, &p->ports, port_no) {
+         ofport_free(ofport);
+     }
+     shash_destroy(&p->port_by_name);
+     switch_status_destroy(p->switch_status);
+     in_band_destroy(p->in_band);
+     discovery_destroy(p->discovery);
+     fail_open_destroy(p->fail_open);
+     pinsched_destroy(p->miss_sched);
+     pinsched_destroy(p->action_sched);
+     executer_destroy(p->executer);
+     netflow_destroy(p->netflow);
+     switch_status_unregister(p->ss_cat);
+     for (i = 0; i < p->n_listeners; i++) {
+         pvconn_close(p->listeners[i]);
+     }
+     free(p->listeners);
+     for (i = 0; i < p->n_snoops; i++) {
+         pvconn_close(p->snoops[i]);
+     }
+     free(p->snoops);
+     mac_learning_destroy(p->ml);
+     free(p);
+ }
+ int
+ ofproto_run(struct ofproto *p)
+ {
+     int error = ofproto_run1(p);
+     if (!error) {
+         error = ofproto_run2(p, false);
+     }
+     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 ofconn *ofconn, *next_ofconn;
+     char *devname;
+     int error;
+     int i;
+     for (i = 0; i < 50; i++) {
+         struct ofpbuf *buf;
+         int error;
+         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, "%s: datapath was destroyed externally",
+                             dpif_name(p->dpif));
+                 return ENODEV;
+             }
+             break;
+         }
+         handle_odp_msg(p, buf);
+     }
+     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) {
+         in_band_run(p->in_band);
+     }
+     if (p->discovery) {
+         char *controller_name;
+         if (rconn_is_connectivity_questionable(p->controller->rconn)) {
+             discovery_question_connectivity(p->discovery);
+         }
+         if (discovery_run(p->discovery, &controller_name)) {
+             if (controller_name) {
+                 rconn_connect(p->controller->rconn, controller_name);
+             } else {
+                 rconn_disconnect(p->controller->rconn);
+             }
+         }
+     }
+     if (p->fail_open) {
+         fail_open_run(p->fail_open);
+     }
+     pinsched_run(p->miss_sched, send_packet_in_miss, p);
+     pinsched_run(p->action_sched, send_packet_in_action, p);
+     if (p->executer) {
+         executer_run(p->executer);
+     }
+     LIST_FOR_EACH_SAFE (ofconn, next_ofconn, struct ofconn, node,
+                         &p->all_conns) {
+         ofconn_run(ofconn, p);
+     }
+     for (i = 0; i < p->n_listeners; i++) {
+         struct vconn *vconn;
+         int retval;
+         retval = pvconn_accept(p->listeners[i], OFP_VERSION, &vconn);
+         if (!retval) {
+             ofconn_create(p, rconn_new_from_vconn("passive", vconn));
+         } else if (retval != EAGAIN) {
+             VLOG_WARN_RL(&rl, "accept failed (%s)", strerror(retval));
+         }
+     }
+     for (i = 0; i < p->n_snoops; i++) {
+         struct vconn *vconn;
+         int retval;
+         retval = pvconn_accept(p->snoops[i], OFP_VERSION, &vconn);
+         if (!retval) {
+             rconn_add_monitor(p->controller->rconn, vconn);
+         } else if (retval != EAGAIN) {
+             VLOG_WARN_RL(&rl, "accept failed (%s)", strerror(retval));
+         }
+     }
+     if (time_msec() >= p->next_expiration) {
+         COVERAGE_INC(ofproto_expiration);
+         p->next_expiration = time_msec() + 1000;
+         update_used(p);
+         classifier_for_each(&p->cls, CLS_INC_ALL, expire_rule, p);
+         /* Let the hook know that we're at a stable point: all outstanding data
+          * in existing flows has been accounted to the account_cb.  Thus, the
+          * hook can now reasonably do operations that depend on having accurate
+          * flow volume accounting (currently, that's just bond rebalancing). */
+         if (p->ofhooks->account_checkpoint_cb) {
+             p->ofhooks->account_checkpoint_cb(p->aux);
+         }
+     }
+     if (p->netflow) {
+         netflow_run(p->netflow);
+     }
+     return 0;
+ }
+ struct revalidate_cbdata {
+     struct ofproto *ofproto;
+     bool revalidate_all;        /* Revalidate all exact-match rules? */
+     bool revalidate_subrules;   /* Revalidate all exact-match subrules? */
+     struct tag_set revalidate_set; /* Set of tags to revalidate. */
+ };
+ int
+ ofproto_run2(struct ofproto *p, bool revalidate_all)
+ {
+     if (p->need_revalidate || revalidate_all
+         || !tag_set_is_empty(&p->revalidate_set)) {
+         struct revalidate_cbdata cbdata;
+         cbdata.ofproto = p;
+         cbdata.revalidate_all = revalidate_all;
+         cbdata.revalidate_subrules = p->need_revalidate;
+         cbdata.revalidate_set = p->revalidate_set;
+         tag_set_init(&p->revalidate_set);
+         COVERAGE_INC(ofproto_revalidate);
+         classifier_for_each(&p->cls, CLS_INC_EXACT, revalidate_cb, &cbdata);
+         p->need_revalidate = false;
+     }
+     return 0;
+ }
+ void
+ ofproto_wait(struct ofproto *p)
+ {
+     struct ofconn *ofconn;
+     size_t i;
+     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);
+     }
+     if (p->in_band) {
+         in_band_wait(p->in_band);
+     }
+     if (p->discovery) {
+         discovery_wait(p->discovery);
+     }
+     if (p->fail_open) {
+         fail_open_wait(p->fail_open);
+     }
+     pinsched_wait(p->miss_sched);
+     pinsched_wait(p->action_sched);
+     if (p->executer) {
+         executer_wait(p->executer);
+     }
+     if (!tag_set_is_empty(&p->revalidate_set)) {
+         poll_immediate_wake();
+     }
+     if (p->need_revalidate) {
+         /* Shouldn't happen, but if it does just go around again. */
+         VLOG_DBG_RL(&rl, "need revalidate in ofproto_wait_cb()");
+         poll_immediate_wake();
+     } else if (p->next_expiration != LLONG_MAX) {
+         poll_timer_wait(p->next_expiration - time_msec());
+     }
+     for (i = 0; i < p->n_listeners; i++) {
+         pvconn_wait(p->listeners[i]);
+     }
+     for (i = 0; i < p->n_snoops; i++) {
+         pvconn_wait(p->snoops[i]);
+     }
+ }
+ void
+ ofproto_revalidate(struct ofproto *ofproto, tag_type tag)
+ {
+     tag_set_add(&ofproto->revalidate_set, tag);
+ }
+ struct tag_set *
+ ofproto_get_revalidate_set(struct ofproto *ofproto)
+ {
+     return &ofproto->revalidate_set;
+ }
+ bool
+ ofproto_is_alive(const struct ofproto *p)
+ {
+     return p->discovery || rconn_is_alive(p->controller->rconn);
+ }
+ int
+ ofproto_send_packet(struct ofproto *p, const flow_t *flow,
+                     const union ofp_action *actions, size_t n_actions,
+                     const struct ofpbuf *packet)
+ {
+     struct odp_actions odp_actions;
+     int error;
+     error = xlate_actions(actions, n_actions, flow, p, packet, &odp_actions,
+                           NULL, NULL);
+     if (error) {
+         return error;
+     }
+     /* XXX Should we translate the dpif_execute() errno value into an OpenFlow
+      * error code? */
+     dpif_execute(p->dpif, flow->in_port, odp_actions.actions,
+                  odp_actions.n_actions, packet);
+     return 0;
+ }
+ void
+ ofproto_add_flow(struct ofproto *p,
+                  const flow_t *flow, uint32_t wildcards, unsigned int priority,
+                  const union ofp_action *actions, size_t n_actions,
+                  int idle_timeout)
+ {
+     struct rule *rule;
+     rule = rule_create(NULL, actions, n_actions,
+                        idle_timeout >= 0 ? idle_timeout : 5 /* XXX */, 0);
+     cls_rule_from_flow(&rule->cr, flow, wildcards, priority);
+     rule_insert(p, rule, NULL, 0);
+ }
+ void
+ ofproto_delete_flow(struct ofproto *ofproto, const flow_t *flow,
+                     uint32_t wildcards, unsigned int priority)
+ {
+     struct rule *rule;
+     rule = rule_from_cls_rule(classifier_find_rule_exactly(&ofproto->cls,
+                                                            flow, wildcards,
+                                                            priority));
+     if (rule) {
+         rule_remove(ofproto, rule);
+     }
+ }
+ static void
+ destroy_rule(struct cls_rule *rule_, void *ofproto_)
+ {
+     struct rule *rule = rule_from_cls_rule(rule_);
+     struct ofproto *ofproto = ofproto_;
+     /* Mark the flow as not installed, even though it might really be
+      * installed, so that rule_remove() doesn't bother trying to uninstall it.
+      * There is no point in uninstalling it individually since we are about to
+      * blow away all the flows with dpif_flow_flush(). */
+     rule->installed = false;
+     rule_remove(ofproto, rule);
+ }
+ void
+ ofproto_flush_flows(struct ofproto *ofproto)
+ {
+     COVERAGE_INC(ofproto_flush);
+     classifier_for_each(&ofproto->cls, CLS_INC_ALL, destroy_rule, ofproto);
+     dpif_flow_flush(ofproto->dpif);
+     if (ofproto->in_band) {
+         in_band_flushed(ofproto->in_band);
+     }
+     if (ofproto->fail_open) {
+         fail_open_flushed(ofproto->fail_open);
+     }
+ }
\f
+ static void
+ reinit_ports(struct ofproto *p)
+ {
+     struct svec devnames;
+     struct ofport *ofport;
+     unsigned int port_no;
+     struct odp_port *odp_ports;
+     size_t n_odp_ports;
+     size_t i;
+     svec_init(&devnames);
+     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);
+     for (i = 0; i < n_odp_ports; i++) {
+         svec_add (&devnames, odp_ports[i].devname);
+     }
+     free(odp_ports);
+     svec_sort_unique(&devnames);
+     for (i = 0; i < devnames.n; i++) {
+         update_port(p, devnames.names[i]);
+     }
+     svec_destroy(&devnames);
+ }
+ static void
+ refresh_port_group(struct ofproto *p, unsigned int group)
+ {
+     uint16_t *ports;
+     size_t n_ports;
+     struct ofport *port;
+     unsigned int port_no;
+     assert(group == DP_GROUP_ALL || group == DP_GROUP_FLOOD);
+     ports = xmalloc(port_array_count(&p->ports) * sizeof *ports);
+     n_ports = 0;
+     PORT_ARRAY_FOR_EACH (port, &p->ports, port_no) {
+         if (group == DP_GROUP_ALL || !(port->opp.config & OFPPC_NO_FLOOD)) {
+             ports[n_ports++] = port_no;
+         }
+     }
+     dpif_port_group_set(p->dpif, group, ports, n_ports);
+     free(ports);
+ }
+ static void
+ refresh_port_groups(struct ofproto *p)
+ {
+     refresh_port_group(p, DP_GROUP_FLOOD);
+     refresh_port_group(p, DP_GROUP_ALL);
+ }
+ static struct ofport *
+ make_ofport(const struct odp_port *odp_port)
+ {
+     enum netdev_flags flags;
+     struct ofport *ofport;
+     struct netdev *netdev;
+     bool carrier;
+     int error;
+     error = netdev_open(odp_port->devname, NETDEV_ETH_TYPE_NONE, &netdev);
+     if (error) {
+         VLOG_WARN_RL(&rl, "ignoring port %s (%"PRIu16") because netdev %s "
+                      "cannot be opened (%s)",
+                      odp_port->devname, odp_port->port,
+                      odp_port->devname, strerror(error));
+         return NULL;
+     }
+     ofport = xmalloc(sizeof *ofport);
+     ofport->netdev = netdev;
+     ofport->opp.port_no = odp_port_to_ofp_port(odp_port->port);
+     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';
+     netdev_get_flags(netdev, &flags);
+     ofport->opp.config = flags & NETDEV_UP ? 0 : OFPPC_PORT_DOWN;
+     netdev_get_carrier(netdev, &carrier);
+     ofport->opp.state = carrier ? 0 : OFPPS_LINK_DOWN;
+     netdev_get_features(netdev,
+                         &ofport->opp.curr, &ofport->opp.advertised,
+                         &ofport->opp.supported, &ofport->opp.peer);
+     return ofport;
+ }
+ static bool
+ ofport_conflicts(const struct ofproto *p, const struct odp_port *odp_port)
+ {
+     if (port_array_get(&p->ports, odp_port->port)) {
+         VLOG_WARN_RL(&rl, "ignoring duplicate port %"PRIu16" in datapath",
+                      odp_port->port);
+         return true;
+     } else if (shash_find(&p->port_by_name, odp_port->devname)) {
+         VLOG_WARN_RL(&rl, "ignoring duplicate device %s in datapath",
+                      odp_port->devname);
+         return true;
+     } else {
+         return false;
+     }
+ }
+ static int
+ ofport_equal(const struct ofport *a_, const struct ofport *b_)
+ {
+     const struct ofp_phy_port *a = &a_->opp;
+     const struct ofp_phy_port *b = &b_->opp;
+     BUILD_ASSERT_DECL(sizeof *a == 48); /* Detect ofp_phy_port changes. */
+     return (a->port_no == b->port_no
+             && !memcmp(a->hw_addr, b->hw_addr, sizeof a->hw_addr)
+             && !strcmp((char *) a->name, (char *) b->name)
+             && a->state == b->state
+             && a->config == b->config
+             && a->curr == b->curr
+             && a->advertised == b->advertised
+             && a->supported == b->supported
+             && a->peer == b->peer);
+ }
+ static void
+ send_port_status(struct ofproto *p, const struct ofport *ofport,
+                  uint8_t reason)
+ {
+     /* XXX Should limit the number of queued port status change messages. */
+     struct ofconn *ofconn;
+     LIST_FOR_EACH (ofconn, struct ofconn, node, &p->all_conns) {
+         struct ofp_port_status *ops;
+         struct ofpbuf *b;
+         ops = make_openflow_xid(sizeof *ops, OFPT_PORT_STATUS, 0, &b);
+         ops->reason = reason;
+         ops->desc = ofport->opp;
+         hton_ofp_phy_port(&ops->desc);
+         queue_tx(b, ofconn, NULL);
+     }
+     if (p->ofhooks->port_changed_cb) {
+         p->ofhooks->port_changed_cb(reason, &ofport->opp, p->aux);
+     }
+ }
+ 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));
+ }
+ static void
+ ofport_free(struct ofport *ofport)
+ {
+     if (ofport) {
+         netdev_close(ofport->netdev);
+         free(ofport);
+     }
+ }
+ static void
+ update_port(struct ofproto *p, const char *devname)
+ {
+     struct odp_port odp_port;
 -    ofport = shash_find_data(&p->port_by_name, devname);
++    struct ofport *old_ofport;
++    struct ofport *new_ofport;
+     int error;
+     COVERAGE_INC(ofproto_update_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;
 -            }
++
++    /* Query the datapath for port information. */
+     error = dpif_port_query_by_name(p->dpif, devname, &odp_port);
 -            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);
 -    } else {
++    /* 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 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);
+ }
+ static int
+ init_ports(struct ofproto *p)
+ {
+     struct odp_port *ports;
+     size_t n_ports;
+     size_t i;
+     int error;
+     error = dpif_port_list(p->dpif, &ports, &n_ports);
+     if (error) {
+         return error;
+     }
+     for (i = 0; i < n_ports; i++) {
+         const struct odp_port *odp_port = &ports[i];
+         if (!ofport_conflicts(p, odp_port)) {
+             struct ofport *ofport = make_ofport(odp_port);
+             if (ofport) {
+                 ofport_install(p, ofport);
+             }
+         }
+     }
+     free(ports);
+     refresh_port_groups(p);
+     return 0;
+ }
\f
+ static struct ofconn *
+ ofconn_create(struct ofproto *p, struct rconn *rconn)
+ {
+     struct ofconn *ofconn = xmalloc(sizeof *ofconn);
+     list_push_back(&p->all_conns, &ofconn->node);
+     ofconn->rconn = rconn;
+     ofconn->pktbuf = NULL;
+     ofconn->send_flow_exp = false;
+     ofconn->miss_send_len = 0;
+     ofconn->packet_in_counter = rconn_packet_counter_create ();
+     ofconn->reply_counter = rconn_packet_counter_create ();
+     return ofconn;
+ }
+ static void
+ ofconn_destroy(struct ofconn *ofconn, struct ofproto *p)
+ {
+     if (p->executer) {
+         executer_rconn_closing(p->executer, ofconn->rconn);
+     }
+     list_remove(&ofconn->node);
+     rconn_destroy(ofconn->rconn);
+     rconn_packet_counter_destroy(ofconn->packet_in_counter);
+     rconn_packet_counter_destroy(ofconn->reply_counter);
+     pktbuf_destroy(ofconn->pktbuf);
+     free(ofconn);
+ }
+ static void
+ ofconn_run(struct ofconn *ofconn, struct ofproto *p)
+ {
+     int iteration;
+     rconn_run(ofconn->rconn);
+     if (rconn_packet_counter_read (ofconn->reply_counter) < OFCONN_REPLY_MAX) {
+         /* Limit the number of iterations to prevent other tasks from
+          * starving. */
+         for (iteration = 0; iteration < 50; iteration++) {
+             struct ofpbuf *of_msg = rconn_recv(ofconn->rconn);
+             if (!of_msg) {
+                 break;
+             }
+             handle_openflow(ofconn, p, of_msg);
+             ofpbuf_delete(of_msg);
+         }
+     }
+     if (ofconn != p->controller && !rconn_is_alive(ofconn->rconn)) {
+         ofconn_destroy(ofconn, p);
+     }
+ }
+ static void
+ ofconn_wait(struct ofconn *ofconn)
+ {
+     rconn_run_wait(ofconn->rconn);
+     if (rconn_packet_counter_read (ofconn->reply_counter) < OFCONN_REPLY_MAX) {
+         rconn_recv_wait(ofconn->rconn);
+     } else {
+         COVERAGE_INC(ofproto_ofconn_stuck);
+     }
+ }
\f
+ /* Caller is responsible for initializing the 'cr' member of the returned
+  * rule. */
+ static struct rule *
+ rule_create(struct rule *super,
+             const union ofp_action *actions, size_t n_actions,
+             uint16_t idle_timeout, uint16_t hard_timeout)
+ {
+     struct rule *rule = xcalloc(1, sizeof *rule);
+     rule->idle_timeout = idle_timeout;
+     rule->hard_timeout = hard_timeout;
+     rule->used = rule->created = time_msec();
+     rule->super = super;
+     if (super) {
+         list_push_back(&super->list, &rule->list);
+     } else {
+         list_init(&rule->list);
+     }
+     rule->n_actions = n_actions;
+     rule->actions = xmemdup(actions, n_actions * sizeof *actions);
+     return rule;
+ }
+ static struct rule *
+ rule_from_cls_rule(const struct cls_rule *cls_rule)
+ {
+     return cls_rule ? CONTAINER_OF(cls_rule, struct rule, cr) : NULL;
+ }
+ static void
+ rule_free(struct rule *rule)
+ {
+     free(rule->actions);
+     free(rule->odp_actions);
+     free(rule);
+ }
+ /* Destroys 'rule'.  If 'rule' is a subrule, also removes it from its
+  * super-rule's list of subrules.  If 'rule' is a super-rule, also iterates
+  * through all of its subrules and revalidates them, destroying any that no
+  * longer has a super-rule (which is probably all of them).
+  *
+  * Before calling this function, the caller must make have removed 'rule' from
+  * the classifier.  If 'rule' is an exact-match rule, the caller is also
+  * responsible for ensuring that it has been uninstalled from the datapath. */
+ static void
+ rule_destroy(struct ofproto *ofproto, struct rule *rule)
+ {
+     if (!rule->super) {
+         struct rule *subrule, *next;
+         LIST_FOR_EACH_SAFE (subrule, next, struct rule, list, &rule->list) {
+             revalidate_rule(ofproto, subrule);
+         }
+     } else {
+         list_remove(&rule->list);
+     }
+     rule_free(rule);
+ }
+ static bool
+ rule_has_out_port(const struct rule *rule, uint16_t out_port)
+ {
+     const union ofp_action *oa;
+     struct actions_iterator i;
+     if (out_port == htons(OFPP_NONE)) {
+         return true;
+     }
+     for (oa = actions_first(&i, rule->actions, rule->n_actions); oa;
+          oa = actions_next(&i)) {
+         if (oa->type == htons(OFPAT_OUTPUT) && oa->output.port == out_port) {
+             return true;
+         }
+     }
+     return false;
+ }
+ /* Executes the actions indicated by 'rule' on 'packet', which is in flow
+  * 'flow' and is considered to have arrived on ODP port 'in_port'.
+  *
+  * The flow that 'packet' actually contains does not need to actually match
+  * 'rule'; the actions in 'rule' will be applied to it either way.  Likewise,
+  * the packet and byte counters for 'rule' will be credited for the packet sent
+  * out whether or not the packet actually matches 'rule'.
+  *
+  * If 'rule' is an exact-match rule and 'flow' actually equals the rule's flow,
+  * the caller must already have accurately composed ODP actions for it given
+  * 'packet' using rule_make_actions().  If 'rule' is a wildcard rule, or if
+  * 'rule' is an exact-match rule but 'flow' is not the rule's flow, then this
+  * function will compose a set of ODP actions based on 'rule''s OpenFlow
+  * actions and apply them to 'packet'. */
+ static void
+ rule_execute(struct ofproto *ofproto, struct rule *rule,
+              struct ofpbuf *packet, const flow_t *flow)
+ {
+     const union odp_action *actions;
+     size_t n_actions;
+     struct odp_actions a;
+     /* Grab or compose the ODP actions.
+      *
+      * The special case for an exact-match 'rule' where 'flow' is not the
+      * rule's flow is important to avoid, e.g., sending a packet out its input
+      * port simply because the ODP actions were composed for the wrong
+      * scenario. */
+     if (rule->cr.wc.wildcards || !flow_equal(flow, &rule->cr.flow)) {
+         struct rule *super = rule->super ? rule->super : rule;
+         if (xlate_actions(super->actions, super->n_actions, flow, ofproto,
+                           packet, &a, NULL, 0)) {
+             return;
+         }
+         actions = a.actions;
+         n_actions = a.n_actions;
+     } else {
+         actions = rule->odp_actions;
+         n_actions = rule->n_odp_actions;
+     }
+     /* Execute the ODP actions. */
+     if (!dpif_execute(ofproto->dpif, flow->in_port,
+                       actions, n_actions, packet)) {
+         struct odp_flow_stats stats;
+         flow_extract_stats(flow, packet, &stats);
+         update_stats(rule, &stats);
+         rule->used = time_msec();
+     }
+ }
+ static void
+ rule_insert(struct ofproto *p, struct rule *rule, struct ofpbuf *packet,
+             uint16_t in_port)
+ {
+     struct rule *displaced_rule;
+     /* Insert the rule in the classifier. */
+     displaced_rule = rule_from_cls_rule(classifier_insert(&p->cls, &rule->cr));
+     if (!rule->cr.wc.wildcards) {
+         rule_make_actions(p, rule, packet);
+     }
+     /* Send the packet and credit it to the rule. */
+     if (packet) {
+         flow_t flow;
+         flow_extract(packet, in_port, &flow);
+         rule_execute(p, rule, packet, &flow);
+     }
+     /* Install the rule in the datapath only after sending the packet, to
+      * avoid packet reordering.  */
+     if (rule->cr.wc.wildcards) {
+         COVERAGE_INC(ofproto_add_wc_flow);
+         p->need_revalidate = true;
+     } else {
+         rule_install(p, rule, displaced_rule);
+     }
+     /* Free the rule that was displaced, if any. */
+     if (displaced_rule) {
+         rule_destroy(p, displaced_rule);
+     }
+ }
+ static struct rule *
+ rule_create_subrule(struct ofproto *ofproto, struct rule *rule,
+                     const flow_t *flow)
+ {
+     struct rule *subrule = rule_create(rule, NULL, 0,
+                                        rule->idle_timeout, rule->hard_timeout);
+     COVERAGE_INC(ofproto_subrule_create);
+     cls_rule_from_flow(&subrule->cr, flow, 0,
+                        (rule->cr.priority <= UINT16_MAX ? UINT16_MAX
+                         : rule->cr.priority));
+     classifier_insert_exact(&ofproto->cls, &subrule->cr);
+     return subrule;
+ }
+ static void
+ rule_remove(struct ofproto *ofproto, struct rule *rule)
+ {
+     if (rule->cr.wc.wildcards) {
+         COVERAGE_INC(ofproto_del_wc_flow);
+         ofproto->need_revalidate = true;
+     } else {
+         rule_uninstall(ofproto, rule);
+     }
+     classifier_remove(&ofproto->cls, &rule->cr);
+     rule_destroy(ofproto, rule);
+ }
+ /* Returns true if the actions changed, false otherwise. */
+ static bool
+ rule_make_actions(struct ofproto *p, struct rule *rule,
+                   const struct ofpbuf *packet)
+ {
+     const struct rule *super;
+     struct odp_actions a;
+     size_t actions_len;
+     assert(!rule->cr.wc.wildcards);
+     super = rule->super ? rule->super : rule;
+     rule->tags = 0;
+     xlate_actions(super->actions, super->n_actions, &rule->cr.flow, p,
+                   packet, &a, &rule->tags, &rule->may_install);
+     actions_len = a.n_actions * sizeof *a.actions;
+     if (rule->n_odp_actions != a.n_actions
+         || memcmp(rule->odp_actions, a.actions, actions_len)) {
+         COVERAGE_INC(ofproto_odp_unchanged);
+         free(rule->odp_actions);
+         rule->n_odp_actions = a.n_actions;
+         rule->odp_actions = xmemdup(a.actions, actions_len);
+         return true;
+     } else {
+         return false;
+     }
+ }
+ static int
+ do_put_flow(struct ofproto *ofproto, struct rule *rule, int flags,
+             struct odp_flow_put *put)
+ {
+     memset(&put->flow.stats, 0, sizeof put->flow.stats);
+     put->flow.key = rule->cr.flow;
+     put->flow.actions = rule->odp_actions;
+     put->flow.n_actions = rule->n_odp_actions;
+     put->flags = flags;
+     return dpif_flow_put(ofproto->dpif, put);
+ }
+ static void
+ rule_install(struct ofproto *p, struct rule *rule, struct rule *displaced_rule)
+ {
+     assert(!rule->cr.wc.wildcards);
+     if (rule->may_install) {
+         struct odp_flow_put put;
+         if (!do_put_flow(p, rule,
+                          ODPPF_CREATE | ODPPF_MODIFY | ODPPF_ZERO_STATS,
+                          &put)) {
+             rule->installed = true;
+             if (displaced_rule) {
+                 update_stats(rule, &put.flow.stats);
+                 rule_post_uninstall(p, displaced_rule);
+             }
+         }
+     } else if (displaced_rule) {
+         rule_uninstall(p, displaced_rule);
+     }
+ }
+ static void
+ rule_reinstall(struct ofproto *ofproto, struct rule *rule)
+ {
+     if (rule->installed) {
+         struct odp_flow_put put;
+         COVERAGE_INC(ofproto_dp_missed);
+         do_put_flow(ofproto, rule, ODPPF_CREATE | ODPPF_MODIFY, &put);
+     } else {
+         rule_install(ofproto, rule, NULL);
+     }
+ }
+ static void
+ rule_update_actions(struct ofproto *ofproto, struct rule *rule)
+ {
+     bool actions_changed = rule_make_actions(ofproto, rule, NULL);
+     if (rule->may_install) {
+         if (rule->installed) {
+             if (actions_changed) {
+                 /* XXX should really do rule_post_uninstall() for the *old* set
+                  * of actions, and distinguish the old stats from the new. */
+                 struct odp_flow_put put;
+                 do_put_flow(ofproto, rule, ODPPF_CREATE | ODPPF_MODIFY, &put);
+             }
+         } else {
+             rule_install(ofproto, rule, NULL);
+         }
+     } else {
+         rule_uninstall(ofproto, rule);
+     }
+ }
+ static void
+ rule_account(struct ofproto *ofproto, struct rule *rule, uint64_t extra_bytes)
+ {
+     uint64_t total_bytes = rule->byte_count + extra_bytes;
+     if (ofproto->ofhooks->account_flow_cb
+         && total_bytes > rule->accounted_bytes)
+     {
+         ofproto->ofhooks->account_flow_cb(
+             &rule->cr.flow, rule->odp_actions, rule->n_odp_actions,
+             total_bytes - rule->accounted_bytes, ofproto->aux);
+         rule->accounted_bytes = total_bytes;
+     }
+ }
+ static void
+ rule_uninstall(struct ofproto *p, struct rule *rule)
+ {
+     assert(!rule->cr.wc.wildcards);
+     if (rule->installed) {
+         struct odp_flow odp_flow;
+         odp_flow.key = rule->cr.flow;
+         odp_flow.actions = NULL;
+         odp_flow.n_actions = 0;
+         if (!dpif_flow_del(p->dpif, &odp_flow)) {
+             update_stats(rule, &odp_flow.stats);
+         }
+         rule->installed = false;
+         rule_post_uninstall(p, rule);
+     }
+ }
+ static void
+ rule_post_uninstall(struct ofproto *ofproto, struct rule *rule)
+ {
+     struct rule *super = rule->super;
+     rule_account(ofproto, rule, 0);
+     if (ofproto->netflow) {
+         struct ofexpired expired;
+         expired.flow = rule->cr.flow;
+         expired.packet_count = rule->packet_count;
+         expired.byte_count = rule->byte_count;
+         expired.used = rule->used;
+         expired.created = rule->created;
+         expired.tcp_flags = rule->tcp_flags;
+         expired.ip_tos = rule->ip_tos;
+         netflow_expire(ofproto->netflow, &expired);
+     }
+     if (super) {
+         super->packet_count += rule->packet_count;
+         super->byte_count += rule->byte_count;
+         super->tcp_flags |= rule->tcp_flags;
+         if (rule->packet_count) {
+             super->ip_tos = rule->ip_tos;
+         }
+     }
+     /* Reset counters to prevent double counting if the rule ever gets
+      * reinstalled. */
+     rule->packet_count = 0;
+     rule->byte_count = 0;
+     rule->accounted_bytes = 0;
+     rule->tcp_flags = 0;
+     rule->ip_tos = 0;
+ }
\f
+ static void
+ queue_tx(struct ofpbuf *msg, const struct ofconn *ofconn,
+          struct rconn_packet_counter *counter)
+ {
+     update_openflow_length(msg);
+     if (rconn_send(ofconn->rconn, msg, counter)) {
+         ofpbuf_delete(msg);
+     }
+ }
+ static void
+ send_error(const struct ofconn *ofconn, const struct ofp_header *oh,
+            int error, const void *data, size_t len)
+ {
+     struct ofpbuf *buf;
+     struct ofp_error_msg *oem;
+     if (!(error >> 16)) {
+         VLOG_WARN_RL(&rl, "not sending bad error code %d to controller",
+                      error);
+         return;
+     }
+     COVERAGE_INC(ofproto_error);
+     oem = make_openflow_xid(len + sizeof *oem, OFPT_ERROR,
+                             oh ? oh->xid : 0, &buf);
+     oem->type = htons((unsigned int) error >> 16);
+     oem->code = htons(error & 0xffff);
+     memcpy(oem->data, data, len);
+     queue_tx(buf, ofconn, ofconn->reply_counter);
+ }
+ static void
+ send_error_oh(const struct ofconn *ofconn, const struct ofp_header *oh,
+               int error)
+ {
+     size_t oh_length = ntohs(oh->length);
+     send_error(ofconn, oh, error, oh, MIN(oh_length, 64));
+ }
+ static void
+ hton_ofp_phy_port(struct ofp_phy_port *opp)
+ {
+     opp->port_no = htons(opp->port_no);
+     opp->config = htonl(opp->config);
+     opp->state = htonl(opp->state);
+     opp->curr = htonl(opp->curr);
+     opp->advertised = htonl(opp->advertised);
+     opp->supported = htonl(opp->supported);
+     opp->peer = htonl(opp->peer);
+ }
+ static int
+ handle_echo_request(struct ofconn *ofconn, struct ofp_header *oh)
+ {
+     struct ofp_header *rq = oh;
+     queue_tx(make_echo_reply(rq), ofconn, ofconn->reply_counter);
+     return 0;
+ }
+ static int
+ handle_features_request(struct ofproto *p, struct ofconn *ofconn,
+                         struct ofp_header *oh)
+ {
+     struct ofp_switch_features *osf;
+     struct ofpbuf *buf;
+     unsigned int port_no;
+     struct ofport *port;
+     osf = make_openflow_xid(sizeof *osf, OFPT_FEATURES_REPLY, oh->xid, &buf);
+     osf->datapath_id = htonll(p->datapath_id);
+     osf->n_buffers = htonl(pktbuf_capacity());
+     osf->n_tables = 2;
+     osf->capabilities = htonl(OFPC_FLOW_STATS | OFPC_TABLE_STATS |
+                               OFPC_PORT_STATS | OFPC_MULTI_PHY_TX);
+     osf->actions = htonl((1u << OFPAT_OUTPUT) |
+                          (1u << OFPAT_SET_VLAN_VID) |
+                          (1u << OFPAT_SET_VLAN_PCP) |
+                          (1u << OFPAT_STRIP_VLAN) |
+                          (1u << OFPAT_SET_DL_SRC) |
+                          (1u << OFPAT_SET_DL_DST) |
+                          (1u << OFPAT_SET_NW_SRC) |
+                          (1u << OFPAT_SET_NW_DST) |
+                          (1u << OFPAT_SET_TP_SRC) |
+                          (1u << OFPAT_SET_TP_DST));
+     PORT_ARRAY_FOR_EACH (port, &p->ports, port_no) {
+         hton_ofp_phy_port(ofpbuf_put(buf, &port->opp, sizeof port->opp));
+     }
+     queue_tx(buf, ofconn, ofconn->reply_counter);
+     return 0;
+ }
+ static int
+ handle_get_config_request(struct ofproto *p, struct ofconn *ofconn,
+                           struct ofp_header *oh)
+ {
+     struct ofpbuf *buf;
+     struct ofp_switch_config *osc;
+     uint16_t flags;
+     bool drop_frags;
+     /* Figure out flags. */
+     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;
+     }
+     /* Send reply. */
+     osc = make_openflow_xid(sizeof *osc, OFPT_GET_CONFIG_REPLY, oh->xid, &buf);
+     osc->flags = htons(flags);
+     osc->miss_send_len = htons(ofconn->miss_send_len);
+     queue_tx(buf, ofconn, ofconn->reply_counter);
+     return 0;
+ }
+ static int
+ handle_set_config(struct ofproto *p, struct ofconn *ofconn,
+                   struct ofp_switch_config *osc)
+ {
+     uint16_t flags;
+     int error;
+     error = check_ofp_message(&osc->header, OFPT_SET_CONFIG, sizeof *osc);
+     if (error) {
+         return error;
+     }
+     flags = ntohs(osc->flags);
+     ofconn->send_flow_exp = (flags & OFPC_SEND_FLOW_EXP) != 0;
+     if (ofconn == p->controller) {
+         switch (flags & OFPC_FRAG_MASK) {
+         case OFPC_FRAG_NORMAL:
+             dpif_set_drop_frags(p->dpif, false);
+             break;
+         case OFPC_FRAG_DROP:
+             dpif_set_drop_frags(p->dpif, true);
+             break;
+         default:
+             VLOG_WARN_RL(&rl, "requested bad fragment mode (flags=%"PRIx16")",
+                          osc->flags);
+             break;
+         }
+     }
+     if ((ntohs(osc->miss_send_len) != 0) != (ofconn->miss_send_len != 0)) {
+         if (ntohs(osc->miss_send_len) != 0) {
+             ofconn->pktbuf = pktbuf_create();
+         } else {
+             pktbuf_destroy(ofconn->pktbuf);
+         }
+     }
+     ofconn->miss_send_len = ntohs(osc->miss_send_len);
+     return 0;
+ }
+ static void
+ add_output_group_action(struct odp_actions *actions, uint16_t group)
+ {
+     odp_actions_add(actions, ODPAT_OUTPUT_GROUP)->output_group.group = group;
+ }
+ static void
+ add_controller_action(struct odp_actions *actions,
+                       const struct ofp_action_output *oao)
+ {
+     union odp_action *a = odp_actions_add(actions, ODPAT_CONTROLLER);
+     a->controller.arg = oao->max_len ? ntohs(oao->max_len) : UINT32_MAX;
+ }
+ struct action_xlate_ctx {
+     /* Input. */
+     const flow_t *flow;         /* Flow to which these actions correspond. */
+     int recurse;                /* Recursion level, via xlate_table_action. */
+     struct ofproto *ofproto;
+     const struct ofpbuf *packet; /* The packet corresponding to 'flow', or a
+                                   * null pointer if we are revalidating
+                                   * without a packet to refer to. */
+     /* Output. */
+     struct odp_actions *out;    /* Datapath actions. */
+     tag_type *tags;             /* Tags associated with OFPP_NORMAL actions. */
+     bool may_setup_flow;        /* True ordinarily; false if the actions must
+                                  * be reassessed for every packet. */
+ };
+ static void do_xlate_actions(const union ofp_action *in, size_t n_in,
+                              struct action_xlate_ctx *ctx);
+ static void
+ add_output_action(struct action_xlate_ctx *ctx, uint16_t port)
+ {
+     const struct ofport *ofport = port_array_get(&ctx->ofproto->ports, port);
+     if (!ofport || !(ofport->opp.config & OFPPC_NO_FWD)) {
+         odp_actions_add(ctx->out, ODPAT_OUTPUT)->output.port = port;
+     }
+ }
+ static struct rule *
+ lookup_valid_rule(struct ofproto *ofproto, const flow_t *flow)
+ {
+     struct rule *rule;
+     rule = rule_from_cls_rule(classifier_lookup(&ofproto->cls, flow));
+     /* The rule we found might not be valid, since we could be in need of
+      * revalidation.  If it is not valid, don't return it. */
+     if (rule
+         && rule->super
+         && ofproto->need_revalidate
+         && !revalidate_rule(ofproto, rule)) {
+         COVERAGE_INC(ofproto_invalidated);
+         return NULL;
+     }
+     return rule;
+ }
+ static void
+ xlate_table_action(struct action_xlate_ctx *ctx, uint16_t in_port)
+ {
+     if (!ctx->recurse) {
+         struct rule *rule;
+         flow_t flow;
+         flow = *ctx->flow;
+         flow.in_port = in_port;
+         rule = lookup_valid_rule(ctx->ofproto, &flow);
+         if (rule) {
+             if (rule->super) {
+                 rule = rule->super;
+             }
+             ctx->recurse++;
+             do_xlate_actions(rule->actions, rule->n_actions, ctx);
+             ctx->recurse--;
+         }
+     }
+ }
+ static void
+ xlate_output_action(struct action_xlate_ctx *ctx,
+                     const struct ofp_action_output *oao)
+ {
+     uint16_t odp_port;
+     switch (ntohs(oao->port)) {
+     case OFPP_IN_PORT:
+         add_output_action(ctx, ctx->flow->in_port);
+         break;
+     case OFPP_TABLE:
+         xlate_table_action(ctx, ctx->flow->in_port);
+         break;
+     case OFPP_NORMAL:
+         if (!ctx->ofproto->ofhooks->normal_cb(ctx->flow, ctx->packet,
+                                               ctx->out, ctx->tags,
+                                               ctx->ofproto->aux)) {
+             COVERAGE_INC(ofproto_uninstallable);
+             ctx->may_setup_flow = false;
+         }
+         break;
+     case OFPP_FLOOD:
+         add_output_group_action(ctx->out, DP_GROUP_FLOOD);
+         break;
+     case OFPP_ALL:
+         add_output_group_action(ctx->out, DP_GROUP_ALL);
+         break;
+     case OFPP_CONTROLLER:
+         add_controller_action(ctx->out, oao);
+         break;
+     case OFPP_LOCAL:
+         add_output_action(ctx, ODPP_LOCAL);
+         break;
+     default:
+         odp_port = ofp_port_to_odp_port(ntohs(oao->port));
+         if (odp_port != ctx->flow->in_port) {
+             add_output_action(ctx, odp_port);
+         }
+         break;
+     }
+ }
+ static void
+ xlate_nicira_action(struct action_xlate_ctx *ctx,
+                     const struct nx_action_header *nah)
+ {
+     const struct nx_action_resubmit *nar;
+     int subtype = ntohs(nah->subtype);
+     assert(nah->vendor == htonl(NX_VENDOR_ID));
+     switch (subtype) {
+     case NXAST_RESUBMIT:
+         nar = (const struct nx_action_resubmit *) nah;
+         xlate_table_action(ctx, ofp_port_to_odp_port(ntohs(nar->in_port)));
+         break;
+     default:
+         VLOG_DBG_RL(&rl, "unknown Nicira action type %"PRIu16, subtype);
+         break;
+     }
+ }
+ static void
+ do_xlate_actions(const union ofp_action *in, size_t n_in,
+                  struct action_xlate_ctx *ctx)
+ {
+     struct actions_iterator iter;
+     const union ofp_action *ia;
+     const struct ofport *port;
+     port = port_array_get(&ctx->ofproto->ports, ctx->flow->in_port);
+     if (port && port->opp.config & (OFPPC_NO_RECV | OFPPC_NO_RECV_STP) &&
+         port->opp.config & (eth_addr_equals(ctx->flow->dl_dst, stp_eth_addr)
+                             ? OFPPC_NO_RECV_STP : OFPPC_NO_RECV)) {
+         /* Drop this flow. */
+         return;
+     }
+     for (ia = actions_first(&iter, in, n_in); ia; ia = actions_next(&iter)) {
+         uint16_t type = ntohs(ia->type);
+         union odp_action *oa;
+         switch (type) {
+         case OFPAT_OUTPUT:
+             xlate_output_action(ctx, &ia->output);
+             break;
+         case OFPAT_SET_VLAN_VID:
+             oa = odp_actions_add(ctx->out, ODPAT_SET_VLAN_VID);
+             oa->vlan_vid.vlan_vid = ia->vlan_vid.vlan_vid;
+             break;
+         case OFPAT_SET_VLAN_PCP:
+             oa = odp_actions_add(ctx->out, ODPAT_SET_VLAN_PCP);
+             oa->vlan_pcp.vlan_pcp = ia->vlan_pcp.vlan_pcp;
+             break;
+         case OFPAT_STRIP_VLAN:
+             odp_actions_add(ctx->out, ODPAT_STRIP_VLAN);
+             break;
+         case OFPAT_SET_DL_SRC:
+             oa = odp_actions_add(ctx->out, ODPAT_SET_DL_SRC);
+             memcpy(oa->dl_addr.dl_addr,
+                    ((struct ofp_action_dl_addr *) ia)->dl_addr, ETH_ADDR_LEN);
+             break;
+         case OFPAT_SET_DL_DST:
+             oa = odp_actions_add(ctx->out, ODPAT_SET_DL_DST);
+             memcpy(oa->dl_addr.dl_addr,
+                    ((struct ofp_action_dl_addr *) ia)->dl_addr, ETH_ADDR_LEN);
+             break;
+         case OFPAT_SET_NW_SRC:
+             oa = odp_actions_add(ctx->out, ODPAT_SET_NW_SRC);
+             oa->nw_addr.nw_addr = ia->nw_addr.nw_addr;
+             break;
+         case OFPAT_SET_TP_SRC:
+             oa = odp_actions_add(ctx->out, ODPAT_SET_TP_SRC);
+             oa->tp_port.tp_port = ia->tp_port.tp_port;
+             break;
+         case OFPAT_VENDOR:
+             xlate_nicira_action(ctx, (const struct nx_action_header *) ia);
+             break;
+         default:
+             VLOG_DBG_RL(&rl, "unknown action type %"PRIu16, type);
+             break;
+         }
+     }
+ }
+ static int
+ xlate_actions(const union ofp_action *in, size_t n_in,
+               const flow_t *flow, struct ofproto *ofproto,
+               const struct ofpbuf *packet,
+               struct odp_actions *out, tag_type *tags, bool *may_setup_flow)
+ {
+     tag_type no_tags = 0;
+     struct action_xlate_ctx ctx;
+     COVERAGE_INC(ofproto_ofp2odp);
+     odp_actions_init(out);
+     ctx.flow = flow;
+     ctx.recurse = 0;
+     ctx.ofproto = ofproto;
+     ctx.packet = packet;
+     ctx.out = out;
+     ctx.tags = tags ? tags : &no_tags;
+     ctx.may_setup_flow = true;
+     do_xlate_actions(in, n_in, &ctx);
+     if (may_setup_flow) {
+         *may_setup_flow = ctx.may_setup_flow;
+     }
+     if (odp_actions_overflow(out)) {
+         odp_actions_init(out);
+         return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_TOO_MANY);
+     }
+     return 0;
+ }
+ static int
+ handle_packet_out(struct ofproto *p, struct ofconn *ofconn,
+                   struct ofp_header *oh)
+ {
+     struct ofp_packet_out *opo;
+     struct ofpbuf payload, *buffer;
+     struct odp_actions actions;
+     int n_actions;
+     uint16_t in_port;
+     flow_t flow;
+     int error;
+     error = check_ofp_packet_out(oh, &payload, &n_actions, p->max_ports);
+     if (error) {
+         return error;
+     }
+     opo = (struct ofp_packet_out *) oh;
+     COVERAGE_INC(ofproto_packet_out);
+     if (opo->buffer_id != htonl(UINT32_MAX)) {
+         error = pktbuf_retrieve(ofconn->pktbuf, ntohl(opo->buffer_id),
+                                 &buffer, &in_port);
+         if (error) {
+             return error;
+         }
+         payload = *buffer;
+     } else {
+         buffer = NULL;
+     }
+     flow_extract(&payload, ofp_port_to_odp_port(ntohs(opo->in_port)), &flow);
+     error = xlate_actions((const union ofp_action *) opo->actions, n_actions,
+                           &flow, p, &payload, &actions, NULL, NULL);
+     if (error) {
+         return error;
+     }
+     dpif_execute(p->dpif, flow.in_port, actions.actions, actions.n_actions,
+                  &payload);
+     ofpbuf_delete(buffer);
+     return 0;
+ }
+ static void
+ update_port_config(struct ofproto *p, struct ofport *port,
+                    uint32_t config, uint32_t mask)
+ {
+     mask &= config ^ port->opp.config;
+     if (mask & OFPPC_PORT_DOWN) {
+         if (config & OFPPC_PORT_DOWN) {
+             netdev_turn_flags_off(port->netdev, NETDEV_UP, true);
+         } else {
+             netdev_turn_flags_on(port->netdev, NETDEV_UP, true);
+         }
+     }
+ #define REVALIDATE_BITS (OFPPC_NO_RECV | OFPPC_NO_RECV_STP | OFPPC_NO_FWD)
+     if (mask & REVALIDATE_BITS) {
+         COVERAGE_INC(ofproto_costly_flags);
+         port->opp.config ^= mask & REVALIDATE_BITS;
+         p->need_revalidate = true;
+     }
+ #undef REVALIDATE_BITS
+     if (mask & OFPPC_NO_FLOOD) {
+         port->opp.config ^= OFPPC_NO_FLOOD;
+         refresh_port_group(p, DP_GROUP_FLOOD);
+     }
+     if (mask & OFPPC_NO_PACKET_IN) {
+         port->opp.config ^= OFPPC_NO_PACKET_IN;
+     }
+ }
+ static int
+ handle_port_mod(struct ofproto *p, struct ofp_header *oh)
+ {
+     const struct ofp_port_mod *opm;
+     struct ofport *port;
+     int error;
+     error = check_ofp_message(oh, OFPT_PORT_MOD, sizeof *opm);
+     if (error) {
+         return error;
+     }
+     opm = (struct ofp_port_mod *) oh;
+     port = port_array_get(&p->ports,
+                           ofp_port_to_odp_port(ntohs(opm->port_no)));
+     if (!port) {
+         return ofp_mkerr(OFPET_PORT_MOD_FAILED, OFPPMFC_BAD_PORT);
+     } else if (memcmp(port->opp.hw_addr, opm->hw_addr, OFP_ETH_ALEN)) {
+         return ofp_mkerr(OFPET_PORT_MOD_FAILED, OFPPMFC_BAD_HW_ADDR);
+     } else {
+         update_port_config(p, port, ntohl(opm->config), ntohl(opm->mask));
+         if (opm->advertise) {
+             netdev_set_advertisements(port->netdev, ntohl(opm->advertise));
+         }
+     }
+     return 0;
+ }
+ static struct ofpbuf *
+ make_stats_reply(uint32_t xid, uint16_t type, size_t body_len)
+ {
+     struct ofp_stats_reply *osr;
+     struct ofpbuf *msg;
+     msg = ofpbuf_new(MIN(sizeof *osr + body_len, UINT16_MAX));
+     osr = put_openflow_xid(sizeof *osr, OFPT_STATS_REPLY, xid, msg);
+     osr->type = type;
+     osr->flags = htons(0);
+     return msg;
+ }
+ static struct ofpbuf *
+ start_stats_reply(const struct ofp_stats_request *request, size_t body_len)
+ {
+     return make_stats_reply(request->header.xid, request->type, body_len);
+ }
+ static void *
+ append_stats_reply(size_t nbytes, struct ofconn *ofconn, struct ofpbuf **msgp)
+ {
+     struct ofpbuf *msg = *msgp;
+     assert(nbytes <= UINT16_MAX - sizeof(struct ofp_stats_reply));
+     if (nbytes + msg->size > UINT16_MAX) {
+         struct ofp_stats_reply *reply = msg->data;
+         reply->flags = htons(OFPSF_REPLY_MORE);
+         *msgp = make_stats_reply(reply->header.xid, reply->type, nbytes);
+         queue_tx(msg, ofconn, ofconn->reply_counter);
+     }
+     return ofpbuf_put_uninit(*msgp, nbytes);
+ }
+ static int
+ handle_desc_stats_request(struct ofproto *p, struct ofconn *ofconn,
+                            struct ofp_stats_request *request)
+ {
+     struct ofp_desc_stats *ods;
+     struct ofpbuf *msg;
+     msg = start_stats_reply(request, sizeof *ods);
+     ods = append_stats_reply(sizeof *ods, ofconn, &msg);
+     strncpy(ods->mfr_desc, p->manufacturer, sizeof ods->mfr_desc);
+     strncpy(ods->hw_desc, p->hardware, sizeof ods->hw_desc);
+     strncpy(ods->sw_desc, p->software, sizeof ods->sw_desc);
+     strncpy(ods->serial_num, p->serial, sizeof ods->serial_num);
+     queue_tx(msg, ofconn, ofconn->reply_counter);
+     return 0;
+ }
+ static void
+ count_subrules(struct cls_rule *cls_rule, void *n_subrules_)
+ {
+     struct rule *rule = rule_from_cls_rule(cls_rule);
+     int *n_subrules = n_subrules_;
+     if (rule->super) {
+         (*n_subrules)++;
+     }
+ }
+ static int
+ handle_table_stats_request(struct ofproto *p, struct ofconn *ofconn,
+                            struct ofp_stats_request *request)
+ {
+     struct ofp_table_stats *ots;
+     struct ofpbuf *msg;
+     struct odp_stats dpstats;
+     int n_exact, n_subrules, n_wild;
+     msg = start_stats_reply(request, sizeof *ots * 2);
+     /* Count rules of various kinds. */
+     n_subrules = 0;
+     classifier_for_each(&p->cls, CLS_INC_EXACT, count_subrules, &n_subrules);
+     n_exact = classifier_count_exact(&p->cls) - n_subrules;
+     n_wild = classifier_count(&p->cls) - classifier_count_exact(&p->cls);
+     /* Hash table. */
+     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;
+     strcpy(ots->name, "hash");
+     ots->wildcards = htonl(0);
+     ots->max_entries = htonl(dpstats.max_capacity);
+     ots->active_count = htonl(n_exact);
+     ots->lookup_count = htonll(dpstats.n_frags + dpstats.n_hit +
+                                dpstats.n_missed);
+     ots->matched_count = htonll(dpstats.n_hit); /* XXX */
+     /* Classifier table. */
+     ots = append_stats_reply(sizeof *ots, ofconn, &msg);
+     memset(ots, 0, sizeof *ots);
+     ots->table_id = TABLEID_CLASSIFIER;
+     strcpy(ots->name, "classifier");
+     ots->wildcards = htonl(OFPFW_ALL);
+     ots->max_entries = htonl(65536);
+     ots->active_count = htonl(n_wild);
+     ots->lookup_count = htonll(0);              /* XXX */
+     ots->matched_count = htonll(0);             /* XXX */
+     queue_tx(msg, ofconn, ofconn->reply_counter);
+     return 0;
+ }
+ static int
+ handle_port_stats_request(struct ofproto *p, struct ofconn *ofconn,
+                           struct ofp_stats_request *request)
+ {
+     struct ofp_port_stats *ops;
+     struct ofpbuf *msg;
+     struct ofport *port;
+     unsigned int port_no;
+     msg = start_stats_reply(request, sizeof *ops * 16);
+     PORT_ARRAY_FOR_EACH (port, &p->ports, port_no) {
+         struct netdev_stats stats;
+         /* Intentionally ignore return value, since errors will set 'stats' to
+          * all-1s, which is correct for OpenFlow, and netdev_get_stats() will
+          * log errors. */
+         netdev_get_stats(port->netdev, &stats);
+         ops = append_stats_reply(sizeof *ops, ofconn, &msg);
+         ops->port_no = htons(odp_port_to_ofp_port(port_no));
+         memset(ops->pad, 0, sizeof ops->pad);
+         ops->rx_packets = htonll(stats.rx_packets);
+         ops->tx_packets = htonll(stats.tx_packets);
+         ops->rx_bytes = htonll(stats.rx_bytes);
+         ops->tx_bytes = htonll(stats.tx_bytes);
+         ops->rx_dropped = htonll(stats.rx_dropped);
+         ops->tx_dropped = htonll(stats.tx_dropped);
+         ops->rx_errors = htonll(stats.rx_errors);
+         ops->tx_errors = htonll(stats.tx_errors);
+         ops->rx_frame_err = htonll(stats.rx_frame_errors);
+         ops->rx_over_err = htonll(stats.rx_over_errors);
+         ops->rx_crc_err = htonll(stats.rx_crc_errors);
+         ops->collisions = htonll(stats.collisions);
+     }
+     queue_tx(msg, ofconn, ofconn->reply_counter);
+     return 0;
+ }
+ struct flow_stats_cbdata {
+     struct ofproto *ofproto;
+     struct ofconn *ofconn;
+     uint16_t out_port;
+     struct ofpbuf *msg;
+ };
+ static void
+ query_stats(struct ofproto *p, struct rule *rule,
+             uint64_t *packet_countp, uint64_t *byte_countp)
+ {
+     uint64_t packet_count, byte_count;
+     struct rule *subrule;
+     struct odp_flow *odp_flows;
+     size_t n_odp_flows;
+     n_odp_flows = rule->cr.wc.wildcards ? list_size(&rule->list) : 1;
+     odp_flows = xcalloc(1, n_odp_flows * sizeof *odp_flows);
+     if (rule->cr.wc.wildcards) {
+         size_t i = 0;
+         LIST_FOR_EACH (subrule, struct rule, list, &rule->list) {
+             odp_flows[i++].key = subrule->cr.flow;
+         }
+     } else {
+         odp_flows[0].key = rule->cr.flow;
+     }
+     packet_count = rule->packet_count;
+     byte_count = rule->byte_count;
+     if (!dpif_flow_get_multiple(p->dpif, odp_flows, n_odp_flows)) {
+         size_t i;
+         for (i = 0; i < n_odp_flows; i++) {
+             struct odp_flow *odp_flow = &odp_flows[i];
+             packet_count += odp_flow->stats.n_packets;
+             byte_count += odp_flow->stats.n_bytes;
+         }
+     }
+     free(odp_flows);
+     *packet_countp = packet_count;
+     *byte_countp = byte_count;
+ }
+ static void
+ flow_stats_cb(struct cls_rule *rule_, void *cbdata_)
+ {
+     struct rule *rule = rule_from_cls_rule(rule_);
+     struct flow_stats_cbdata *cbdata = cbdata_;
+     struct ofp_flow_stats *ofs;
+     uint64_t packet_count, byte_count;
+     size_t act_len, len;
+     if (rule_is_hidden(rule) || !rule_has_out_port(rule, cbdata->out_port)) {
+         return;
+     }
+     act_len = sizeof *rule->actions * rule->n_actions;
+     len = offsetof(struct ofp_flow_stats, actions) + act_len;
+     query_stats(cbdata->ofproto, rule, &packet_count, &byte_count);
+     ofs = append_stats_reply(len, cbdata->ofconn, &cbdata->msg);
+     ofs->length = htons(len);
+     ofs->table_id = rule->cr.wc.wildcards ? TABLEID_CLASSIFIER : TABLEID_HASH;
+     ofs->pad = 0;
+     flow_to_match(&rule->cr.flow, rule->cr.wc.wildcards, &ofs->match);
+     ofs->duration = htonl((time_msec() - rule->created) / 1000);
+     ofs->priority = htons(rule->cr.priority);
+     ofs->idle_timeout = htons(rule->idle_timeout);
+     ofs->hard_timeout = htons(rule->hard_timeout);
+     memset(ofs->pad2, 0, sizeof ofs->pad2);
+     ofs->packet_count = htonll(packet_count);
+     ofs->byte_count = htonll(byte_count);
+     memcpy(ofs->actions, rule->actions, act_len);
+ }
+ static int
+ table_id_to_include(uint8_t table_id)
+ {
+     return (table_id == TABLEID_HASH ? CLS_INC_EXACT
+             : table_id == TABLEID_CLASSIFIER ? CLS_INC_WILD
+             : table_id == 0xff ? CLS_INC_ALL
+             : 0);
+ }
+ static int
+ handle_flow_stats_request(struct ofproto *p, struct ofconn *ofconn,
+                           const struct ofp_stats_request *osr,
+                           size_t arg_size)
+ {
+     struct ofp_flow_stats_request *fsr;
+     struct flow_stats_cbdata cbdata;
+     struct cls_rule target;
+     if (arg_size != sizeof *fsr) {
+         return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LENGTH);
+     }
+     fsr = (struct ofp_flow_stats_request *) osr->body;
+     COVERAGE_INC(ofproto_flows_req);
+     cbdata.ofproto = p;
+     cbdata.ofconn = ofconn;
+     cbdata.out_port = fsr->out_port;
+     cbdata.msg = start_stats_reply(osr, 1024);
+     cls_rule_from_match(&target, &fsr->match, 0);
+     classifier_for_each_match(&p->cls, &target,
+                               table_id_to_include(fsr->table_id),
+                               flow_stats_cb, &cbdata);
+     queue_tx(cbdata.msg, ofconn, ofconn->reply_counter);
+     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;
+     uint64_t packet_count;
+     uint64_t byte_count;
+     uint32_t n_flows;
+ };
+ static void
+ aggregate_stats_cb(struct cls_rule *rule_, void *cbdata_)
+ {
+     struct rule *rule = rule_from_cls_rule(rule_);
+     struct aggregate_stats_cbdata *cbdata = cbdata_;
+     uint64_t packet_count, byte_count;
+     if (rule_is_hidden(rule) || !rule_has_out_port(rule, cbdata->out_port)) {
+         return;
+     }
+     query_stats(cbdata->ofproto, rule, &packet_count, &byte_count);
+     cbdata->packet_count += packet_count;
+     cbdata->byte_count += byte_count;
+     cbdata->n_flows++;
+ }
+ static int
+ handle_aggregate_stats_request(struct ofproto *p, struct ofconn *ofconn,
+                                const struct ofp_stats_request *osr,
+                                size_t arg_size)
+ {
+     struct ofp_aggregate_stats_request *asr;
+     struct ofp_aggregate_stats_reply *reply;
+     struct aggregate_stats_cbdata cbdata;
+     struct cls_rule target;
+     struct ofpbuf *msg;
+     if (arg_size != sizeof *asr) {
+         return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LENGTH);
+     }
+     asr = (struct ofp_aggregate_stats_request *) osr->body;
+     COVERAGE_INC(ofproto_agg_request);
+     cbdata.ofproto = p;
+     cbdata.out_port = asr->out_port;
+     cbdata.packet_count = 0;
+     cbdata.byte_count = 0;
+     cbdata.n_flows = 0;
+     cls_rule_from_match(&target, &asr->match, 0);
+     classifier_for_each_match(&p->cls, &target,
+                               table_id_to_include(asr->table_id),
+                               aggregate_stats_cb, &cbdata);
+     msg = start_stats_reply(osr, sizeof *reply);
+     reply = append_stats_reply(sizeof *reply, ofconn, &msg);
+     reply->flow_count = htonl(cbdata.n_flows);
+     reply->packet_count = htonll(cbdata.packet_count);
+     reply->byte_count = htonll(cbdata.byte_count);
+     queue_tx(msg, ofconn, ofconn->reply_counter);
+     return 0;
+ }
+ static int
+ handle_stats_request(struct ofproto *p, struct ofconn *ofconn,
+                      struct ofp_header *oh)
+ {
+     struct ofp_stats_request *osr;
+     size_t arg_size;
+     int error;
+     error = check_ofp_message_array(oh, OFPT_STATS_REQUEST, sizeof *osr,
+                                     1, &arg_size);
+     if (error) {
+         return error;
+     }
+     osr = (struct ofp_stats_request *) oh;
+     switch (ntohs(osr->type)) {
+     case OFPST_DESC:
+         return handle_desc_stats_request(p, ofconn, osr);
+     case OFPST_FLOW:
+         return handle_flow_stats_request(p, ofconn, osr, arg_size);
+     case OFPST_AGGREGATE:
+         return handle_aggregate_stats_request(p, ofconn, osr, arg_size);
+     case OFPST_TABLE:
+         return handle_table_stats_request(p, ofconn, osr);
+     case OFPST_PORT:
+         return handle_port_stats_request(p, ofconn, osr);
+     case OFPST_VENDOR:
+         return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_VENDOR);
+     default:
+         return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_STAT);
+     }
+ }
+ static long long int
+ msec_from_nsec(uint64_t sec, uint32_t nsec)
+ {
+     return !sec ? 0 : sec * 1000 + nsec / 1000000;
+ }
+ static void
+ update_time(struct rule *rule, const struct odp_flow_stats *stats)
+ {
+     long long int used = msec_from_nsec(stats->used_sec, stats->used_nsec);
+     if (used > rule->used) {
+         rule->used = used;
+     }
+ }
+ static void
+ update_stats(struct rule *rule, const struct odp_flow_stats *stats)
+ {
+     update_time(rule, stats);
+     rule->packet_count += stats->n_packets;
+     rule->byte_count += stats->n_bytes;
+     rule->tcp_flags |= stats->tcp_flags;
+     if (stats->n_packets) {
+         rule->ip_tos = stats->ip_tos;
+     }
+ }
+ static int
+ add_flow(struct ofproto *p, struct ofconn *ofconn,
+          struct ofp_flow_mod *ofm, size_t n_actions)
+ {
+     struct ofpbuf *packet;
+     struct rule *rule;
+     uint16_t in_port;
+     int error;
+     rule = rule_create(NULL, (const union ofp_action *) ofm->actions,
+                        n_actions, ntohs(ofm->idle_timeout),
+                        ntohs(ofm->hard_timeout));
+     cls_rule_from_match(&rule->cr, &ofm->match, ntohs(ofm->priority));
+     packet = NULL;
+     error = 0;
+     if (ofm->buffer_id != htonl(UINT32_MAX)) {
+         error = pktbuf_retrieve(ofconn->pktbuf, ntohl(ofm->buffer_id),
+                                 &packet, &in_port);
+     }
+     rule_insert(p, rule, packet, in_port);
+     ofpbuf_delete(packet);
+     return error;
+ }
+ static int
+ modify_flow(struct ofproto *p, const struct ofp_flow_mod *ofm,
+             size_t n_actions, uint16_t command, struct rule *rule)
+ {
+     if (rule_is_hidden(rule)) {
+         return 0;
+     }
+     if (command == OFPFC_DELETE) {
+         rule_remove(p, rule);
+     } else {
+         size_t actions_len = n_actions * sizeof *rule->actions;
+         if (n_actions == rule->n_actions
+             && !memcmp(ofm->actions, rule->actions, actions_len))
+         {
+             return 0;
+         }
+         free(rule->actions);
+         rule->actions = xmemdup(ofm->actions, actions_len);
+         rule->n_actions = n_actions;
+         if (rule->cr.wc.wildcards) {
+             COVERAGE_INC(ofproto_mod_wc_flow);
+             p->need_revalidate = true;
+         } else {
+             rule_update_actions(p, rule);
+         }
+     }
+     return 0;
+ }
+ static int
+ modify_flows_strict(struct ofproto *p, const struct ofp_flow_mod *ofm,
+                     size_t n_actions, uint16_t command)
+ {
+     struct rule *rule;
+     uint32_t wildcards;
+     flow_t flow;
+     flow_from_match(&flow, &wildcards, &ofm->match);
+     rule = rule_from_cls_rule(classifier_find_rule_exactly(
+                                   &p->cls, &flow, wildcards,
+                                   ntohs(ofm->priority)));
+     if (rule) {
+         if (command == OFPFC_DELETE
+             && ofm->out_port != htons(OFPP_NONE)
+             && !rule_has_out_port(rule, ofm->out_port)) {
+             return 0;
+         }
+         modify_flow(p, ofm, n_actions, command, rule);
+     }
+     return 0;
+ }
+ struct modify_flows_cbdata {
+     struct ofproto *ofproto;
+     const struct ofp_flow_mod *ofm;
+     uint16_t out_port;
+     size_t n_actions;
+     uint16_t command;
+ };
+ static void
+ modify_flows_cb(struct cls_rule *rule_, void *cbdata_)
+ {
+     struct rule *rule = rule_from_cls_rule(rule_);
+     struct modify_flows_cbdata *cbdata = cbdata_;
+     if (cbdata->out_port != htons(OFPP_NONE)
+         && !rule_has_out_port(rule, cbdata->out_port)) {
+         return;
+     }
+     modify_flow(cbdata->ofproto, cbdata->ofm, cbdata->n_actions,
+                 cbdata->command, rule);
+ }
+ static int
+ modify_flows_loose(struct ofproto *p, const struct ofp_flow_mod *ofm,
+                    size_t n_actions, uint16_t command)
+ {
+     struct modify_flows_cbdata cbdata;
+     struct cls_rule target;
+     cbdata.ofproto = p;
+     cbdata.ofm = ofm;
+     cbdata.out_port = (command == OFPFC_DELETE ? ofm->out_port
+                        : htons(OFPP_NONE));
+     cbdata.n_actions = n_actions;
+     cbdata.command = command;
+     cls_rule_from_match(&target, &ofm->match, 0);
+     classifier_for_each_match(&p->cls, &target, CLS_INC_ALL,
+                               modify_flows_cb, &cbdata);
+     return 0;
+ }
+ static int
+ handle_flow_mod(struct ofproto *p, struct ofconn *ofconn,
+                 struct ofp_flow_mod *ofm)
+ {
+     size_t n_actions;
+     int error;
+     error = check_ofp_message_array(&ofm->header, OFPT_FLOW_MOD, sizeof *ofm,
+                                     sizeof *ofm->actions, &n_actions);
+     if (error) {
+         return error;
+     }
+     normalize_match(&ofm->match);
+     if (!ofm->match.wildcards) {
+         ofm->priority = htons(UINT16_MAX);
+     }
+     error = validate_actions((const union ofp_action *) ofm->actions,
+                              n_actions, p->max_ports);
+     if (error) {
+         return error;
+     }
+     switch (ntohs(ofm->command)) {
+     case OFPFC_ADD:
+         return add_flow(p, ofconn, ofm, n_actions);
+     case OFPFC_MODIFY:
+         return modify_flows_loose(p, ofm, n_actions, OFPFC_MODIFY);
+     case OFPFC_MODIFY_STRICT:
+         return modify_flows_strict(p, ofm, n_actions, OFPFC_MODIFY);
+     case OFPFC_DELETE:
+         return modify_flows_loose(p, ofm, n_actions, OFPFC_DELETE);
+     case OFPFC_DELETE_STRICT:
+         return modify_flows_strict(p, ofm, n_actions, OFPFC_DELETE);
+     default:
+         return ofp_mkerr(OFPET_FLOW_MOD_FAILED, OFPFMFC_BAD_COMMAND);
+     }
+ }
+ static void
+ send_capability_reply(struct ofproto *p, struct ofconn *ofconn, uint32_t xid)
+ {
+     struct ofmp_capability_reply *ocr;
+     struct ofpbuf *b;
+     char capabilities[] = "com.nicira.mgmt.manager=false\n";
+     ocr = make_openflow_xid(sizeof(*ocr), OFPT_VENDOR, xid, &b);
+     ocr->header.header.vendor = htonl(NX_VENDOR_ID);
+     ocr->header.header.subtype = htonl(NXT_MGMT);
+     ocr->header.type = htons(OFMPT_CAPABILITY_REPLY);
+     ocr->format = htonl(OFMPCOF_SIMPLE);
+     ocr->mgmt_id = htonll(p->mgmt_id);
+     ofpbuf_put(b, capabilities, strlen(capabilities));
+     queue_tx(b, ofconn, ofconn->reply_counter);
+ }
+ static int
+ handle_ofmp(struct ofproto *p, struct ofconn *ofconn, 
+             struct ofmp_header *ofmph)
+ {
+     size_t msg_len = ntohs(ofmph->header.header.length);
+     if (msg_len < sizeof(*ofmph)) {
+         VLOG_WARN_RL(&rl, "dropping short managment message: %d\n", msg_len);
+         return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LENGTH);
+     }
+     if (ofmph->type == htons(OFMPT_CAPABILITY_REQUEST)) {
+         struct ofmp_capability_request *ofmpcr;
+         if (msg_len < sizeof(struct ofmp_capability_request)) {
+             VLOG_WARN_RL(&rl, "dropping short capability request: %d\n", 
+                     msg_len);
+             return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LENGTH);
+         }
+         ofmpcr = (struct ofmp_capability_request *)ofmph;
+         if (ofmpcr->format != htonl(OFMPCAF_SIMPLE)) {
+             /* xxx Find a better type than bad subtype */
+             return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_SUBTYPE);
+         }
+         send_capability_reply(p, ofconn, ofmph->header.header.xid);
+         return 0;
+     } else {
+         return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_SUBTYPE);
+     }
+ }
+ static int
+ handle_vendor(struct ofproto *p, struct ofconn *ofconn, void *msg)
+ {
+     struct ofp_vendor_header *ovh = msg;
+     struct nicira_header *nh;
+     if (ntohs(ovh->header.length) < sizeof(struct ofp_vendor_header)) {
+         return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LENGTH);
+     }
+     if (ovh->vendor != htonl(NX_VENDOR_ID)) {
+         return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_VENDOR);
+     }
+     if (ntohs(ovh->header.length) < sizeof(struct nicira_header)) {
+         return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LENGTH);
+     }
+     nh = msg;
+     switch (ntohl(nh->subtype)) {
+     case NXT_STATUS_REQUEST:
+         return switch_status_handle_request(p->switch_status, ofconn->rconn,
+                                             msg);
+     case NXT_ACT_SET_CONFIG:
+         return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_SUBTYPE); /* XXX */
+     case NXT_ACT_GET_CONFIG:
+         return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_SUBTYPE); /* XXX */
+     case NXT_COMMAND_REQUEST:
+         if (p->executer) {
+             return executer_handle_request(p->executer, ofconn->rconn, msg);
+         }
+         break;
+     case NXT_MGMT:
+         return handle_ofmp(p, ofconn, msg);
+     }
+     return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_SUBTYPE);
+ }
+ static void
+ handle_openflow(struct ofconn *ofconn, struct ofproto *p,
+                 struct ofpbuf *ofp_msg)
+ {
+     struct ofp_header *oh = ofp_msg->data;
+     int error;
+     COVERAGE_INC(ofproto_recv_openflow);
+     switch (oh->type) {
+     case OFPT_ECHO_REQUEST:
+         error = handle_echo_request(ofconn, oh);
+         break;
+     case OFPT_ECHO_REPLY:
+         error = 0;
+         break;
+     case OFPT_FEATURES_REQUEST:
+         error = handle_features_request(p, ofconn, oh);
+         break;
+     case OFPT_GET_CONFIG_REQUEST:
+         error = handle_get_config_request(p, ofconn, oh);
+         break;
+     case OFPT_SET_CONFIG:
+         error = handle_set_config(p, ofconn, ofp_msg->data);
+         break;
+     case OFPT_PACKET_OUT:
+         error = handle_packet_out(p, ofconn, ofp_msg->data);
+         break;
+     case OFPT_PORT_MOD:
+         error = handle_port_mod(p, oh);
+         break;
+     case OFPT_FLOW_MOD:
+         error = handle_flow_mod(p, ofconn, ofp_msg->data);
+         break;
+     case OFPT_STATS_REQUEST:
+         error = handle_stats_request(p, ofconn, oh);
+         break;
+     case OFPT_VENDOR:
+         error = handle_vendor(p, ofconn, ofp_msg->data);
+         break;
+     default:
+         if (VLOG_IS_WARN_ENABLED()) {
+             char *s = ofp_to_string(oh, ntohs(oh->length), 2);
+             VLOG_DBG_RL(&rl, "OpenFlow message ignored: %s", s);
+             free(s);
+         }
+         error = ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE);
+         break;
+     }
+     if (error) {
+         send_error_oh(ofconn, ofp_msg->data, error);
+     }
+ }
\f
+ static void
+ handle_odp_msg(struct ofproto *p, struct ofpbuf *packet)
+ {
+     struct odp_msg *msg = packet->data;
+     uint16_t in_port = odp_port_to_ofp_port(msg->port);
+     struct rule *rule;
+     struct ofpbuf payload;
+     flow_t flow;
+     /* Handle controller actions. */
+     if (msg->type == _ODPL_ACTION_NR) {
+         COVERAGE_INC(ofproto_ctlr_action);
+         pinsched_send(p->action_sched, in_port, packet,
+                       send_packet_in_action, p);
+         return;
+     }
+     payload.data = msg + 1;
+     payload.size = msg->length - sizeof *msg;
+     flow_extract(&payload, msg->port, &flow);
+     rule = lookup_valid_rule(p, &flow);
+     if (!rule) {
+         /* Don't send a packet-in if OFPPC_NO_PACKET_IN asserted. */
+         struct ofport *port = port_array_get(&p->ports, msg->port);
+         if (port) {
+             if (port->opp.config & OFPPC_NO_PACKET_IN) {
+                 COVERAGE_INC(ofproto_no_packet_in);
+                 /* XXX install 'drop' flow entry */
+                 ofpbuf_delete(packet);
+                 return;
+             }
+         } else {
+             VLOG_WARN_RL(&rl, "packet-in on unknown port %"PRIu16, msg->port);
+         }
+         COVERAGE_INC(ofproto_packet_in);
+         pinsched_send(p->miss_sched, in_port, packet, send_packet_in_miss, p);
+         return;
+     }
+     if (rule->cr.wc.wildcards) {
+         rule = rule_create_subrule(p, rule, &flow);
+         rule_make_actions(p, rule, packet);
+     } else {
+         if (!rule->may_install) {
+             /* The rule is not installable, that is, we need to process every
+              * packet, so process the current packet and set its actions into
+              * 'subrule'. */
+             rule_make_actions(p, rule, packet);
+         } else {
+             /* XXX revalidate rule if it needs it */
+         }
+     }
+     rule_execute(p, rule, &payload, &flow);
+     rule_reinstall(p, rule);
+     ofpbuf_delete(packet);
+ }
\f
+ static void
+ revalidate_cb(struct cls_rule *sub_, void *cbdata_)
+ {
+     struct rule *sub = rule_from_cls_rule(sub_);
+     struct revalidate_cbdata *cbdata = cbdata_;
+     if (cbdata->revalidate_all
+         || (cbdata->revalidate_subrules && sub->super)
+         || (tag_set_intersects(&cbdata->revalidate_set, sub->tags))) {
+         revalidate_rule(cbdata->ofproto, sub);
+     }
+ }
+ static bool
+ revalidate_rule(struct ofproto *p, struct rule *rule)
+ {
+     const flow_t *flow = &rule->cr.flow;
+     COVERAGE_INC(ofproto_revalidate_rule);
+     if (rule->super) {
+         struct rule *super;
+         super = rule_from_cls_rule(classifier_lookup_wild(&p->cls, flow));
+         if (!super) {
+             rule_remove(p, rule);
+             return false;
+         } else if (super != rule->super) {
+             COVERAGE_INC(ofproto_revalidate_moved);
+             list_remove(&rule->list);
+             list_push_back(&super->list, &rule->list);
+             rule->super = super;
+             rule->hard_timeout = super->hard_timeout;
+             rule->idle_timeout = super->idle_timeout;
+             rule->created = super->created;
+             rule->used = 0;
+         }
+     }
+     rule_update_actions(p, rule);
+     return true;
+ }
+ static struct ofpbuf *
+ compose_flow_exp(const struct rule *rule, long long int now, uint8_t reason)
+ {
+     struct ofp_flow_expired *ofe;
+     struct ofpbuf *buf;
+     ofe = make_openflow(sizeof *ofe, OFPT_FLOW_EXPIRED, &buf);
+     flow_to_match(&rule->cr.flow, rule->cr.wc.wildcards, &ofe->match);
+     ofe->priority = htons(rule->cr.priority);
+     ofe->reason = reason;
+     ofe->duration = (now - rule->created) / 1000;
+     ofe->packet_count = rule->packet_count;
+     ofe->byte_count = rule->byte_count;
+     return buf;
+ }
+ static void
+ send_flow_exp(struct ofproto *p, struct rule *rule,
+               long long int now, uint8_t reason)
+ {
+     struct ofconn *ofconn;
+     struct ofconn *prev;
+     struct ofpbuf *buf;
+     /* We limit the maximum number of queued flow expirations it by accounting
+      * them under the counter for replies.  That works because preventing
+      * OpenFlow requests from being processed also prevents new flows from
+      * being added (and expiring).  (It also prevents processing OpenFlow
+      * requests that would not add new flows, so it is imperfect.) */
+     prev = NULL;
+     LIST_FOR_EACH (ofconn, struct ofconn, node, &p->all_conns) {
+         if (ofconn->send_flow_exp && rconn_is_connected(ofconn->rconn)) {
+             if (prev) {
+                 queue_tx(ofpbuf_clone(buf), prev, ofconn->reply_counter);
+             } else {
+                 buf = compose_flow_exp(rule, now, reason);
+             }
+             prev = ofconn;
+         }
+     }
+     if (prev) {
+         queue_tx(buf, prev, ofconn->reply_counter);
+     }
+ }
+ static void
+ uninstall_idle_flow(struct ofproto *ofproto, struct rule *rule)
+ {
+     assert(rule->installed);
+     assert(!rule->cr.wc.wildcards);
+     if (rule->super) {
+         rule_remove(ofproto, rule);
+     } else {
+         rule_uninstall(ofproto, rule);
+     }
+ }
+ static void
+ expire_rule(struct cls_rule *cls_rule, void *p_)
+ {
+     struct ofproto *p = p_;
+     struct rule *rule = rule_from_cls_rule(cls_rule);
+     long long int hard_expire, idle_expire, expire, now;
+     hard_expire = (rule->hard_timeout
+                    ? rule->created + rule->hard_timeout * 1000
+                    : LLONG_MAX);
+     idle_expire = (rule->idle_timeout
+                    && (rule->super || list_is_empty(&rule->list))
+                    ? rule->used + rule->idle_timeout * 1000
+                    : LLONG_MAX);
+     expire = MIN(hard_expire, idle_expire);
+     if (expire == LLONG_MAX) {
+         if (rule->installed && time_msec() >= rule->used + 5000) {
+             uninstall_idle_flow(p, rule);
+         }
+         return;
+     }
+     now = time_msec();
+     if (now < expire) {
+         if (rule->installed && now >= rule->used + 5000) {
+             uninstall_idle_flow(p, rule);
+         }
+         return;
+     }
+     COVERAGE_INC(ofproto_expired);
+     if (rule->cr.wc.wildcards) {
+         /* Update stats.  (This code will be a no-op if the rule expired
+          * due to an idle timeout, because in that case the rule has no
+          * subrules left.) */
+         struct rule *subrule, *next;
+         LIST_FOR_EACH_SAFE (subrule, next, struct rule, list, &rule->list) {
+             rule_remove(p, subrule);
+         }
+     }
+     send_flow_exp(p, rule, now,
+                   (now >= hard_expire
+                    ? OFPER_HARD_TIMEOUT : OFPER_IDLE_TIMEOUT));
+     rule_remove(p, rule);
+ }
+ static void
+ update_used(struct ofproto *p)
+ {
+     struct odp_flow *flows;
+     size_t n_flows;
+     size_t i;
+     int error;
+     error = dpif_flow_list_all(p->dpif, &flows, &n_flows);
+     if (error) {
+         return;
+     }
+     for (i = 0; i < n_flows; i++) {
+         struct odp_flow *f = &flows[i];
+         struct rule *rule;
+         rule = rule_from_cls_rule(
+             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);
+             continue;
+         }
+         update_time(rule, &f->stats);
+         rule_account(p, rule, f->stats.n_bytes);
+     }
+     free(flows);
+ }
+ static void
+ do_send_packet_in(struct ofconn *ofconn, uint32_t buffer_id,
+                   const struct ofpbuf *packet, int send_len)
+ {
+     struct ofp_packet_in *opi;
+     struct ofpbuf payload, *buf;
+     struct odp_msg *msg;
+     msg = packet->data;
+     payload.data = msg + 1;
+     payload.size = msg->length - sizeof *msg;
+     send_len = MIN(send_len, payload.size);
+     buf = ofpbuf_new(sizeof *opi + send_len);
+     opi = put_openflow_xid(offsetof(struct ofp_packet_in, data),
+                            OFPT_PACKET_IN, 0, buf);
+     opi->buffer_id = htonl(buffer_id);
+     opi->total_len = htons(payload.size);
+     opi->in_port = htons(odp_port_to_ofp_port(msg->port));
+     opi->reason = msg->type == _ODPL_ACTION_NR ? OFPR_ACTION : OFPR_NO_MATCH;
+     ofpbuf_put(buf, payload.data, MIN(send_len, payload.size));
+     update_openflow_length(buf);
+     rconn_send_with_limit(ofconn->rconn, buf, ofconn->packet_in_counter, 100);
+ }
+ static void
+ send_packet_in_action(struct ofpbuf *packet, void *p_)
+ {
+     struct ofproto *p = p_;
+     struct ofconn *ofconn;
+     struct odp_msg *msg;
+     msg = packet->data;
+     LIST_FOR_EACH (ofconn, struct ofconn, node, &p->all_conns) {
+         if (ofconn == p->controller || ofconn->miss_send_len) {
+             do_send_packet_in(ofconn, UINT32_MAX, packet, msg->arg);
+         }
+     }
+     ofpbuf_delete(packet);
+ }
+ static void
+ send_packet_in_miss(struct ofpbuf *packet, void *p_)
+ {
+     struct ofproto *p = p_;
+     struct ofconn *ofconn;
+     struct ofpbuf payload;
+     struct odp_msg *msg;
+     msg = packet->data;
+     payload.data = msg + 1;
+     payload.size = msg->length - sizeof *msg;
+     LIST_FOR_EACH (ofconn, struct ofconn, node, &p->all_conns) {
+         if (ofconn->miss_send_len) {
+             uint32_t buffer_id = pktbuf_save(ofconn->pktbuf, &payload,
+                                              msg->port);
+             int send_len = (buffer_id != UINT32_MAX ? ofconn->miss_send_len
+                             : UINT32_MAX);
+             do_send_packet_in(ofconn, buffer_id, packet, send_len);
+         }
+     }
+     ofpbuf_delete(packet);
+ }
+ static uint64_t
+ pick_datapath_id(const struct ofproto *ofproto)
+ {
+     const struct ofport *port;
+     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)",
+                   netdev_get_name(port->netdev), strerror(error));
+     }
+     return ofproto->fallback_dpid;
+ }
+ static uint64_t
+ pick_fallback_dpid(void)
+ {
+     uint8_t ea[ETH_ADDR_LEN];
+     eth_addr_random(ea);
+     ea[0] = 0x00;               /* Set Nicira OUI. */
+     ea[1] = 0x23;
+     ea[2] = 0x20;
+     return eth_addr_to_uint64(ea);
+ }
\f
+ static bool
+ default_normal_ofhook_cb(const flow_t *flow, const struct ofpbuf *packet,
+                          struct odp_actions *actions, tag_type *tags,
+                          void *ofproto_)
+ {
+     struct ofproto *ofproto = ofproto_;
+     int out_port;
+     /* Drop frames for reserved multicast addresses. */
+     if (eth_addr_is_reserved(flow->dl_dst)) {
+         return true;
+     }
+     /* Learn source MAC (but don't try to learn from revalidation). */
+     if (packet != NULL) {
+         tag_type rev_tag = mac_learning_learn(ofproto->ml, flow->dl_src,
+                                               0, flow->in_port);
+         if (rev_tag) {
+             /* The log messages here could actually be useful in debugging,
+              * so keep the rate limit relatively high. */
+             static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(30, 300);
+             VLOG_DBG_RL(&rl, "learned that "ETH_ADDR_FMT" is on port %"PRIu16,
+                         ETH_ADDR_ARGS(flow->dl_src), flow->in_port);
+             ofproto_revalidate(ofproto, rev_tag);
+         }
+     }
+     /* Determine output port. */
+     out_port = mac_learning_lookup_tag(ofproto->ml, flow->dl_dst, 0, tags);
+     if (out_port < 0) {
+         add_output_group_action(actions, DP_GROUP_FLOOD);
+     } else if (out_port != flow->in_port) {
+         odp_actions_add(actions, ODPAT_OUTPUT)->output.port = out_port;
+     } else {
+         /* Drop. */
+     }
+     return true;
+ }
+ static const struct ofhooks default_ofhooks = {
+     NULL,
+     default_normal_ofhook_cb,
+     NULL,
+     NULL
+ };
index 0000000,f4c1b40..398cac4
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,110 +1,111 @@@
+ /*
+  * Copyright (c) 2009 Nicira Networks.
+  *
+  * Licensed under the Apache License, Version 2.0 (the "License");
+  * you may not use this file except in compliance with the License.
+  * You may obtain a copy of the License at:
+  *
+  *     http://www.apache.org/licenses/LICENSE-2.0
+  *
+  * Unless required by applicable law or agreed to in writing, software
+  * distributed under the License is distributed on an "AS IS" BASIS,
+  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  * See the License for the specific language governing permissions and
+  * limitations under the License.
+  */
+ #ifndef OFPROTO_H
+ #define OFPROTO_H 1
+ #include <stdbool.h>
+ #include <stddef.h>
+ #include <stdint.h>
+ #include "flow.h"
+ #include "tag.h"
+ struct odp_actions;
+ struct ofhooks;
+ struct ofproto;
+ struct svec;
+ struct ofexpired {
+     flow_t flow;
+     uint64_t packet_count;      /* Packets from *expired* subrules. */
+     uint64_t byte_count;        /* Bytes from *expired* subrules. */
+     long long int used;         /* Last-used time (0 if never used). */
+     long long int created;      /* Creation time. */
+     uint8_t tcp_flags;          /* Bitwise-OR of all TCP flags seen. */
+     uint8_t ip_tos;             /* Last-seen IP type-of-service. */
+ };
+ int ofproto_create(const char *datapath, const struct ofhooks *, void *aux,
+                    struct ofproto **ofprotop);
+ void ofproto_destroy(struct ofproto *);
+ int ofproto_run(struct ofproto *);
+ int ofproto_run1(struct ofproto *);
+ int ofproto_run2(struct ofproto *, bool revalidate_all);
+ void ofproto_wait(struct ofproto *);
+ bool ofproto_is_alive(const struct ofproto *);
+ /* Configuration. */
+ void ofproto_set_datapath_id(struct ofproto *, uint64_t datapath_id);
+ void ofproto_set_mgmt_id(struct ofproto *, uint64_t mgmt_id);
+ void ofproto_set_probe_interval(struct ofproto *, int probe_interval);
+ void ofproto_set_max_backoff(struct ofproto *, int max_backoff);
+ void ofproto_set_desc(struct ofproto *,
+                       const char *manufacturer, const char *hardware,
+                       const char *software, const char *serial);
+ int ofproto_set_in_band(struct ofproto *, bool in_band);
+ int ofproto_set_discovery(struct ofproto *, bool discovery,
+                           const char *accept_controller_re,
+                           bool update_resolv_conf);
+ int ofproto_set_controller(struct ofproto *, const char *controller);
+ int ofproto_set_listeners(struct ofproto *, const struct svec *listeners);
+ int ofproto_set_snoops(struct ofproto *, const struct svec *snoops);
+ int ofproto_set_netflow(struct ofproto *, const struct svec *collectors,
+         uint8_t engine_type, uint8_t engine_id, bool add_id_to_iface);
+ void ofproto_set_failure(struct ofproto *, bool fail_open);
+ void ofproto_set_rate_limit(struct ofproto *, int rate_limit, int burst_limit);
+ int ofproto_set_stp(struct ofproto *, bool enable_stp);
+ int ofproto_set_remote_execution(struct ofproto *, const char *command_acl,
+                                  const char *command_dir);
+ /* Configuration querying. */
+ uint64_t ofproto_get_datapath_id(const struct ofproto *);
+ uint64_t ofproto_get_mgmt_id(const struct ofproto *);
+ int ofproto_get_probe_interval(const struct ofproto *);
+ int ofproto_get_max_backoff(const struct ofproto *);
+ bool ofproto_get_in_band(const struct ofproto *);
+ bool ofproto_get_discovery(const struct ofproto *);
+ 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 *,
+                         const union ofp_action *, size_t n_actions,
+                         const struct ofpbuf *);
+ void ofproto_add_flow(struct ofproto *, const flow_t *, uint32_t wildcards,
+                       unsigned int priority,
+                       const union ofp_action *, size_t n_actions,
+                       int idle_timeout);
+ void ofproto_delete_flow(struct ofproto *, const flow_t *, uint32_t wildcards,
+                          unsigned int priority);
+ void ofproto_flush_flows(struct ofproto *);
+ /* Hooks for ovs-vswitchd. */
+ struct ofhooks {
+     void (*port_changed_cb)(enum ofp_port_reason, const struct ofp_phy_port *,
+                             void *aux);
+     bool (*normal_cb)(const flow_t *, const struct ofpbuf *packet,
+                       struct odp_actions *, tag_type *, void *aux);
+     void (*account_flow_cb)(const flow_t *, const union odp_action *,
+                             size_t n_actions, unsigned long long int n_bytes,
+                             void *aux);
+     void (*account_checkpoint_cb)(void *aux);
+ };
+ void ofproto_revalidate(struct ofproto *, tag_type);
+ struct tag_set *ofproto_get_revalidate_set(struct ofproto *);
+ #endif /* ofproto.h */
Simple merge
Simple merge
Simple merge
@@@ -467,35 -428,6 +430,30 @@@ do_show(int argc, char *argv[]
      }
  }
  
-         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[])
  {
index 0000000,4d1a858..3d25574
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,469 +1,469 @@@
 -The default is 15 seconds, and the minimum value is 5 seconds.
+ .TH ovs\-openflowd 8 "March 2009" "Open vSwitch" "Open vSwitch Manual"
+ .ds PN ovs\-openflowd
+ .SH NAME
+ ovs\-openflowd \- OpenFlow switch implementation
+ .SH SYNOPSIS
+ .B ovs\-openflowd
+ [\fIoptions\fR] \fIdatapath\fR [\fIcontroller\fR]
+ .SH DESCRIPTION
+ 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
+ to relay.  It takes one of the following forms:
+ .so lib/dpif.man
+ .PP
+ The optional \fIcontroller\fR argument specifies how to connect to
+ the OpenFlow controller.  It takes one of the following forms:
+ .RS
+ .IP "\fBssl:\fIip\fR[\fB:\fIport\fR]"
+ The specified SSL \fIport\fR (default: 6633) on the host at the given
+ \fIip\fR, which must be expressed as an IP address (not a DNS name).
+ The \fB--private-key\fR, \fB--certificate\fR, and \fB--ca-cert\fR
+ options are mandatory when this form is used.
+ .IP "\fBtcp:\fIip\fR[\fB:\fIport\fR]"
+ The specified TCP \fIport\fR (default: 6633) on the host at the given
+ \fIip\fR, which must be expressed as an IP address (not a DNS name).
+ .TP
+ \fBunix:\fIfile\fR
+ The Unix domain server socket named \fIfile\fR.
+ .RE
+ .PP
+ If \fIcontroller\fR is omitted, \fBovs\-openflowd\fR attempts to discover the
+ location of the controller automatically (see below).
+ .SS "Contacting the Controller"
+ The OpenFlow switch must be able to contact the OpenFlow controller
+ over the network.  It can do so in one of two ways:
+ .IP out-of-band
+ In this configuration, OpenFlow traffic uses a network separate from
+ the data traffic that it controls, that is, the switch does not use
+ any of the network devices added to the datapath with \fBovs\-dpctl
+ add\-if\fR in its communication with the controller.
+ 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
+ In this configuration, a single network is used for OpenFlow traffic
+ and other data traffic, that is, the switch contacts the controller
+ over one of the network devices added to the datapath with \fBovs\-dpctl
+ add\-if\fR.  This configuration is often more convenient than
+ out-of-band control, because it is not necessary to maintain two
+ independent networks.
+ 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
+ manually or discovered automatically:
+ .RS
+ .IP "controller discovery"
+ To make \fBovs\-openflowd\fR discover the location of the controller
+ automatically, do not specify the location of the controller on the
+ \fBovs\-openflowd\fR command line.
+ 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 \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; \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
+ that follow the switch protocol and addresses 192.168.0.1 through
+ 192.168.0.10 to all other DHCP clients:
+ default-lease-time 600;
+ .br
+ max-lease-time 7200;
+ .br
+ option space openflow;
+ .br
+ option openflow.controller-vconn code 1 = text;
+ .br
+ option openflow.pki-uri code 2 = text;
+ .br
+ class "OpenFlow" {
+ .br
+   match if option vendor-class-identifier = "OpenFlow";
+ .br
+   vendor-option-space openflow;
+ .br
+   option openflow.controller-vconn "tcp:192.168.0.10";
+ .br
+   option openflow.pki-uri "http://192.168.0.10/openflow/pki";
+ .br
+   option vendor-class-identifier "OpenFlow";
+ .br
+ }
+ .br
+ subnet 192.168.0.0 netmask 255.255.255.0 {
+ .br
+     pool {
+ .br
+         allow members of "OpenFlow";
+ .br
+         range 192.168.0.20 192.168.0.30;
+ .br
+     }
+ .br
+     pool {
+ .br
+         deny members of "OpenFlow";
+ .br
+         range 192.168.0.1 192.168.0.10;
+ .br
+     }
+ .br
+ }
+ .br
+ .IP "manual configuration"
+ To configure in-band control manually, specify the location of the
+ 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 \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 \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 \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 OpenFlow switch
+ has started up.  Thus, start \fBovs\-openflowd\fR without configuring
+ the local port network device, and start the DHCP client afterward.
+ .RE
+ .SH OPTIONS
+ .SS "Controller Discovery Options"
+ .TP
+ \fB--accept-vconn=\fIregex\fR
+ 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
+ names match the regular expression will be accepted.
+ 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^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.
+ When controller discovery is not performed, this option has no effect.
+ .TP
+ \fB--no-resolv-conf\fR
+ 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
+ obtained via DHCP.  If the location of the controller is specified
+ 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, \fBovs\-openflowd\fR
+ will not modify \fB/etc/resolv.conf\fR.
+ \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.
+ .SS "Networking Options"
+ .TP
+ \fB--datapath-id=\fIdpid\fR
+ Sets \fIdpid\fR, which must consist of exactly 12 hexadecimal digits,
+ as the datapath ID that the switch will use to identify itself to the
+ OpenFlow controller.
+ If this option is omitted, the default datapath ID is taken from the
+ Ethernet address of the datapath's local port (which is typically
+ randomly generated).
+ .TP
+ \fB--mgmt-id=\fImgmtid\fR
+ Sets \fImgmtid\fR, which must consist of exactly 12 hexadecimal
+ digits, as the switch's management ID.
+ If this option is omitted, the management ID defaults to 0, signaling
+ to the controller that management is supported but not configured.
+ .TP
+ \fB--fail=\fR[\fBopen\fR|\fBclosed\fR]
+ The controller is, ordinarily, responsible for setting up all flows on
+ the OpenFlow 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.
+ 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, \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 \fBovs\-openflowd\fR will not
+ set up flows on its own when the controller connection fails.
+ .TP
+ \fB--inactivity-probe=\fIsecs\fR
+ 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 switch
+ assumes that the connection has been broken and attempts to reconnect.
 -time is 15 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
+ above).
+ .TP
+ \fB--max-idle=\fIsecs\fR|\fBpermanent\fR
+ Sets \fIsecs\fR as the number of seconds that a flow set up by the
+ 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 switch will never
+ expire.  The default is 15 seconds.
+ 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
+ switch has not been able to contact the controller for the configured
+ fail-open delay.
+ .IP \(bu
+ When in-band control is in use, flows set up to bootstrap contacting
+ the controller (see \fBContacting the Controller\fR, above, for
+ more information about in-band control).
+ .RE
+ .IP
+ As a result, when both \fB--fail=closed\fR and \fB--out-of-band\fR are
+ specified, this option has no effect.
+ .TP
+ \fB--max-backoff=\fIsecs\fR
+ Sets the maximum time between attempts to connect to the controller to
+ \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 8 seconds.
+ .TP
+ \fB-l\fR, \fB--listen=\fImethod\fR
+ Configures the switch to additionally listen for incoming OpenFlow
+ connections for switch management with \fBovs\-ofctl\fR.  The \fImethod\fR
+ must be given as one of the passive OpenFlow connection methods listed
+ below.  This option may be specified multiple times to listen to
+ multiple connection methods.
+ .RS
+ .TP
+ \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][\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
+ Listens for connections on Unix domain server socket named \fIfile\fR.
+ .RE
+ .TP
+ \fB--snoop=\fImethod\fR
+ Configures the switch to additionally listen for incoming OpenFlow
+ connections for controller connection snooping.  The \fImethod\fR must
+ be given as one of the passive OpenFlow connection methods listed
+ under the \fB--listen\fR option above.  This option may be specified
+ multiple times to listen to multiple connection methods.
+ If \fBovs\-ofctl monitor\fR is used to connect to \fImethod\fR specified on
+ \fB--snoop\fR, it will display all the OpenFlow messages traveling
+ between the switch and its controller on the primary OpenFlow
+ connection.  This can be useful for debugging switch and controller
+ problems.
+ .TP
+ \fB--in-band\fR, \fB--out-of-band\fR
+ 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.
+ .TP
+ \fB--netflow=\fIip\fB:\fIport\fR
+ Configures the given UDP \fIport\fR on the specified IP \fIip\fR as
+ a recipient of NetFlow messages for expired flows.  The \fIip\fR must
+ be specified numerically, not as a DNS name.
+ This option may be specified multiple times to configure additional
+ NetFlow collectors.
+ .SS "Rate-Limiting Options"
+ These options configure how the switch applies a ``token bucket'' to
+ limit the rate at which packets in unknown flows are forwarded to an
+ OpenFlow controller for flow-setup processing.  This feature prevents
+ a single OpenFlow switch from overwhelming a controller.
+ .TP
+ \fB--rate-limit\fR[\fB=\fIrate\fR]
+ .
+ Limits the maximum rate at which packets will be forwarded to the
+ OpenFlow controller to \fIrate\fR packets per second.  If \fIrate\fR
+ is not specified then the default of 1,000 packets per second is used.
+ If \fB--rate-limit\fR is not used, then the switch does not limit the
+ rate at which packets are forwarded to the controller.
+ .TP
+ \fB--burst-limit=\fIburst\fR
+ .
+ Sets the maximum number of unused packet credits that the switch will
+ allow to accumulate during time in which no packets are being
+ forwarded to the OpenFlow controller to \fIburst\fR (measured in
+ packets).  The default \fIburst\fR is one-quarter of the \fIrate\fR
+ specified on \fB--rate-limit\fR.
+ This option takes effect only when \fB--rate-limit\fR is also specified.
+ .SS "Remote Command Execution Options"
+ .TP
+ \fB--command-acl=\fR[\fB!\fR]\fIglob\fR[\fB,\fR[\fB!\fR]\fIglob\fR...]
+ Configures the commands that remote OpenFlow connections are allowed
+ to invoke using (e.g.) \fBovs\-ofctl execute\fR.  The argument is a
+ comma-separated sequence of shell glob patterns.  A glob pattern
+ specified without a leading \fB!\fR is a ``whitelist'' that specifies
+ a set of commands that are that may be invoked, whereas a pattern that
+ does begin with \fB!\fR is a ``blacklist'' that specifies commands
+ that may not be invoked.  To be permitted, a command name must be
+ whitelisted and must not be blacklisted;
+ e.g. \fB--command-acl=up*,!upgrade\fR would allow any command whose name
+ begins with \fBup\fR except for the command named \fBupgrade\fR.
+ Command names that include characters other than upper- and lower-case
+ English letters, digits, and the underscore and hyphen characters are
+ unconditionally disallowed.
+ 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.
+ .TP
+ \fB--command-dir=\fIdirectory\fR
+ Sets the directory searched for remote command execution to
+ \fBdirectory\fR.  The default directory is
+ \fB@pkgdatadir@/commands\fR.
+ .SS "Daemon Options"
+ .so lib/daemon.man
+ .SS "Public Key Infrastructure Options"
+ .TP
+ \fB-p\fR, \fB--private-key=\fIprivkey.pem\fR
+ Specifies a PEM file containing the private key used as the switch's
+ identity for SSL connections to the controller.
+ .TP
+ \fB-c\fR, \fB--certificate=\fIcert.pem\fR
+ Specifies a PEM file containing a certificate, signed by the
+ controller's certificate authority (CA), that certifies the switch's
+ private key to identify a trustworthy switch.
+ .TP
+ \fB-C\fR, \fB--ca-cert=\fIcacert.pem\fR
+ Specifies a PEM file containing the CA certificate used to verify that
+ the switch is connected to a trustworthy controller.
+ .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 \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
+ from then on all SSL connections must be authenticated by a
+ certificate signed by the CA certificate thus obtained.
+ \fBThis option exposes the SSL connection to a man-in-the-middle
+ attack obtaining the initial CA certificate\fR, but it may be useful
+ for bootstrapping.
+ This option is only useful if the controller sends its CA certificate
+ as part of the SSL certificate chain.  The SSL protocol does not
+ require the controller to send the CA certificate, but
+ \fBcontroller\fR(8) can be configured to do so with the
+ \fB--peer-ca-cert\fR option.
+ .SS "Logging Options"
+ .so lib/vlog.man
+ .SS "Other Options"
+ .so lib/common.man
+ .so lib/leak-checker.man
+ .SH "SEE ALSO"
+ .BR ovs\-appctl (8),
+ .BR ovs\-controller (8),
+ .BR ovs\-discover (8),
+ .BR ovs\-dpctl (8),
+ .BR ovs\-ofctl (8),
+ .BR ovs\-pki (8),
+ .BR ovs\-vswitchd.conf (5)
index 0000000,5dd77c0..603e258
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,566 +1,565 @@@
 -    s->max_backoff = 15;
+ /*
+  * Copyright (c) 2008, 2009 Nicira Networks.
+  *
+  * Licensed under the Apache License, Version 2.0 (the "License");
+  * you may not use this file except in compliance with the License.
+  * You may obtain a copy of the License at:
+  *
+  *     http://www.apache.org/licenses/LICENSE-2.0
+  *
+  * Unless required by applicable law or agreed to in writing, software
+  * distributed under the License is distributed on an "AS IS" BASIS,
+  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  * See the License for the specific language governing permissions and
+  * limitations under the License.
+  */
+ #include <config.h>
+ #include <assert.h>
+ #include <errno.h>
+ #include <getopt.h>
+ #include <inttypes.h>
+ #include <netinet/in.h>
+ #include <stdlib.h>
+ #include <signal.h>
+ #include <string.h>
+ #include "command-line.h"
+ #include "compiler.h"
+ #include "daemon.h"
+ #include "dirs.h"
+ #include "dpif.h"
+ #include "fault.h"
+ #include "leak-checker.h"
+ #include "list.h"
+ #include "netdev.h"
+ #include "ofpbuf.h"
+ #include "ofproto/ofproto.h"
+ #include "openflow/openflow.h"
+ #include "packets.h"
+ #include "poll-loop.h"
+ #include "rconn.h"
+ #include "svec.h"
+ #include "timeval.h"
+ #include "unixctl.h"
+ #include "util.h"
+ #include "vconn-ssl.h"
+ #include "vconn.h"
+ #include "vlog.h"
+ #define THIS_MODULE VLM_openflowd
+ /* Behavior when the connection to the controller fails. */
+ enum fail_mode {
+     FAIL_OPEN,                  /* Act as learning switch. */
+     FAIL_CLOSED                 /* Drop all packets. */
+ };
+ /* Settings that may be configured by the user. */
+ struct ofsettings {
+     /* Overall mode of operation. */
+     bool discovery;           /* Discover the controller automatically? */
+     bool in_band;             /* Connect to controller in-band? */
+     /* Datapath. */
+     uint64_t datapath_id;       /* Datapath ID. */
+     const char *dp_name;        /* Name of local datapath. */
+     /* Description strings. */
+     const char *mfr_desc;       /* Manufacturer. */
+     const char *hw_desc;        /* Hardware. */
+     const char *sw_desc;        /* Software version. */
+     const char *serial_desc;    /* Serial number. */
+     /* Related vconns and network devices. */
+     const char *controller_name; /* Controller (if not discovery mode). */
+     struct svec listeners;       /* Listen for management connections. */
+     struct svec snoops;          /* Listen for controller snooping conns. */
+     /* Failure behavior. */
+     enum fail_mode fail_mode; /* Act as learning switch if no controller? */
+     int max_idle;             /* Idle time for flows in fail-open mode. */
+     int probe_interval;       /* # seconds idle before sending echo request. */
+     int max_backoff;          /* Max # seconds between connection attempts. */
+     /* Packet-in rate-limiting. */
+     int rate_limit;           /* Tokens added to bucket per second. */
+     int burst_limit;          /* Maximum number token bucket size. */
+     /* Discovery behavior. */
+     const char *accept_controller_re; /* Controller vconns to accept. */
+     bool update_resolv_conf;          /* Update /etc/resolv.conf? */
+     /* Spanning tree protocol. */
+     bool enable_stp;
+     /* Remote command execution. */
+     char *command_acl;          /* Command white/blacklist, as shell globs. */
+     char *command_dir;          /* Directory that contains commands. */
+     /* Management. */
+     uint64_t mgmt_id;           /* Management ID. */
+     /* NetFlow. */
+     struct svec netflow;        /* NetFlow targets. */
+ };
+ static void parse_options(int argc, char *argv[], struct ofsettings *);
+ static void usage(void) NO_RETURN;
+ int
+ main(int argc, char *argv[])
+ {
+     struct unixctl_server *unixctl;
+     struct ofproto *ofproto;
+     struct ofsettings s;
+     int error;
+     set_program_name(argv[0]);
+     register_fault_handlers();
+     time_init();
+     vlog_init();
+     parse_options(argc, argv, &s);
+     signal(SIGPIPE, SIG_IGN);
+     die_if_already_running();
+     daemonize();
+     /* Start listening for ovs-appctl requests. */
+     error = unixctl_server_create(NULL, &unixctl);
+     if (error) {
+         ovs_fatal(error, "Could not listen for unixctl connections");
+     }
+     VLOG_INFO("Open vSwitch version %s", VERSION BUILDNR);
+     VLOG_INFO("OpenFlow protocol version 0x%02x", OFP_VERSION);
+     /* Start OpenFlow processing. */
+     error = ofproto_create(s.dp_name, NULL, NULL, &ofproto);
+     if (error) {
+         ovs_fatal(error, "could not initialize openflow switch");
+     }
+     error = ofproto_set_in_band(ofproto, s.in_band);
+     if (error) {
+         ovs_fatal(error, "failed to configure in-band control");
+     }
+     error = ofproto_set_discovery(ofproto, s.discovery, s.accept_controller_re,
+                                   s.update_resolv_conf);
+     if (error) {
+         ovs_fatal(error, "failed to configure controller discovery");
+     }
+     if (s.datapath_id) {
+         ofproto_set_datapath_id(ofproto, s.datapath_id);
+     }
+     if (s.mgmt_id) {
+         ofproto_set_mgmt_id(ofproto, s.mgmt_id);
+     }
+     ofproto_set_desc(ofproto, s.mfr_desc, s.hw_desc, s.sw_desc, s.serial_desc);
+     error = ofproto_set_listeners(ofproto, &s.listeners);
+     if (error) {
+         ovs_fatal(error, "failed to configure management connections");
+     }
+     error = ofproto_set_snoops(ofproto, &s.snoops);
+     if (error) {
+         ovs_fatal(error,
+                   "failed to configure controller snooping connections");
+     }
+     error = ofproto_set_netflow(ofproto, &s.netflow, 0, 0, false);
+     if (error) {
+         ovs_fatal(error, "failed to configure NetFlow collectors");
+     }
+     ofproto_set_failure(ofproto, s.fail_mode == FAIL_OPEN);
+     ofproto_set_probe_interval(ofproto, s.probe_interval);
+     ofproto_set_max_backoff(ofproto, s.max_backoff);
+     ofproto_set_rate_limit(ofproto, s.rate_limit, s.burst_limit);
+     error = ofproto_set_stp(ofproto, s.enable_stp);
+     if (error) {
+         ovs_fatal(error, "failed to configure STP");
+     }
+     error = ofproto_set_remote_execution(ofproto, s.command_acl,
+                                          s.command_dir);
+     if (error) {
+         ovs_fatal(error, "failed to configure remote command execution");
+     }
+     if (!s.discovery) {
+         error = ofproto_set_controller(ofproto, s.controller_name);
+         if (error) {
+             ovs_fatal(error, "failed to configure controller");
+         }
+     }
+     while (ofproto_is_alive(ofproto)) {
+         error = ofproto_run(ofproto);
+         if (error) {
+             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();
+     }
+     return 0;
+ }
\f
+ /* User interface. */
+ static void
+ parse_options(int argc, char *argv[], struct ofsettings *s)
+ {
+     enum {
+         OPT_DATAPATH_ID = UCHAR_MAX + 1,
+         OPT_MANUFACTURER,
+         OPT_HARDWARE,
+         OPT_SOFTWARE,
+         OPT_SERIAL,
+         OPT_ACCEPT_VCONN,
+         OPT_NO_RESOLV_CONF,
+         OPT_BR_NAME,
+         OPT_FAIL_MODE,
+         OPT_INACTIVITY_PROBE,
+         OPT_MAX_IDLE,
+         OPT_MAX_BACKOFF,
+         OPT_SNOOP,
+         OPT_RATE_LIMIT,
+         OPT_BURST_LIMIT,
+         OPT_BOOTSTRAP_CA_CERT,
+         OPT_STP,
+         OPT_NO_STP,
+         OPT_OUT_OF_BAND,
+         OPT_IN_BAND,
+         OPT_COMMAND_ACL,
+         OPT_COMMAND_DIR,
+         OPT_NETFLOW,
+         OPT_MGMT_ID,
+         VLOG_OPTION_ENUMS,
+         LEAK_CHECKER_OPTION_ENUMS
+     };
+     static struct option long_options[] = {
+         {"datapath-id", required_argument, 0, OPT_DATAPATH_ID},
+         {"manufacturer", required_argument, 0, OPT_MANUFACTURER},
+         {"hardware", required_argument, 0, OPT_HARDWARE},
+         {"software", required_argument, 0, OPT_SOFTWARE},
+         {"serial", required_argument, 0, OPT_SERIAL},
+         {"accept-vconn", required_argument, 0, OPT_ACCEPT_VCONN},
+         {"no-resolv-conf", no_argument, 0, OPT_NO_RESOLV_CONF},
+         {"config",      required_argument, 0, 'F'},
+         {"br-name",     required_argument, 0, OPT_BR_NAME},
+         {"fail",        required_argument, 0, OPT_FAIL_MODE},
+         {"inactivity-probe", required_argument, 0, OPT_INACTIVITY_PROBE},
+         {"max-idle",    required_argument, 0, OPT_MAX_IDLE},
+         {"max-backoff", required_argument, 0, OPT_MAX_BACKOFF},
+         {"listen",      required_argument, 0, 'l'},
+         {"snoop",      required_argument, 0, OPT_SNOOP},
+         {"rate-limit",  optional_argument, 0, OPT_RATE_LIMIT},
+         {"burst-limit", required_argument, 0, OPT_BURST_LIMIT},
+         {"stp",         no_argument, 0, OPT_STP},
+         {"no-stp",      no_argument, 0, OPT_NO_STP},
+         {"out-of-band", no_argument, 0, OPT_OUT_OF_BAND},
+         {"in-band",     no_argument, 0, OPT_IN_BAND},
+         {"command-acl", required_argument, 0, OPT_COMMAND_ACL},
+         {"command-dir", required_argument, 0, OPT_COMMAND_DIR},
+         {"netflow",     required_argument, 0, OPT_NETFLOW},
+         {"mgmt-id",     required_argument, 0, OPT_MGMT_ID},
+         {"verbose",     optional_argument, 0, 'v'},
+         {"help",        no_argument, 0, 'h'},
+         {"version",     no_argument, 0, 'V'},
+         DAEMON_LONG_OPTIONS,
+         VLOG_LONG_OPTIONS,
+         LEAK_CHECKER_LONG_OPTIONS,
+ #ifdef HAVE_OPENSSL
+         VCONN_SSL_LONG_OPTIONS
+         {"bootstrap-ca-cert", required_argument, 0, OPT_BOOTSTRAP_CA_CERT},
+ #endif
+         {0, 0, 0, 0},
+     };
+     char *short_options = long_options_to_short_options(long_options);
+     /* Set defaults that we can figure out before parsing options. */
+     s->datapath_id = 0;
+     s->mfr_desc = NULL;
+     s->hw_desc = NULL;
+     s->sw_desc = NULL;
+     s->serial_desc = NULL;
+     svec_init(&s->listeners);
+     svec_init(&s->snoops);
+     s->fail_mode = FAIL_OPEN;
+     s->max_idle = 0;
+     s->probe_interval = 0;
 -                ovs_fatal(0, "-f or --fail argument must be \"open\" "
 -                          "or \"closed\"");
++    s->max_backoff = 8;
+     s->update_resolv_conf = true;
+     s->rate_limit = 0;
+     s->burst_limit = 0;
+     s->accept_controller_re = NULL;
+     s->enable_stp = false;
+     s->in_band = true;
+     s->command_acl = "";
+     s->command_dir = NULL;
+     svec_init(&s->netflow);
+     s->mgmt_id = 0;
+     for (;;) {
+         int c;
+         c = getopt_long(argc, argv, short_options, long_options, NULL);
+         if (c == -1) {
+             break;
+         }
+         switch (c) {
+         case OPT_DATAPATH_ID:
+             if (strlen(optarg) != 12
+                 || strspn(optarg, "0123456789abcdefABCDEF") != 12) {
+                 ovs_fatal(0, "argument to --datapath-id must be "
+                           "exactly 12 hex digits");
+             }
+             s->datapath_id = strtoll(optarg, NULL, 16);
+             if (!s->datapath_id) {
+                 ovs_fatal(0, "argument to --datapath-id must be nonzero");
+             }
+             break;
+         case OPT_MANUFACTURER:
+             s->mfr_desc = optarg;
+             break;
+         case OPT_HARDWARE:
+             s->hw_desc = optarg;
+             break;
+         case OPT_SOFTWARE:
+             s->sw_desc = optarg;
+             break;
+         case OPT_SERIAL:
+             s->serial_desc = optarg;
+             break;
+         case OPT_ACCEPT_VCONN:
+             s->accept_controller_re = optarg;
+             break;
+         case OPT_NO_RESOLV_CONF:
+             s->update_resolv_conf = false;
+             break;
+         case OPT_FAIL_MODE:
+             if (!strcmp(optarg, "open")) {
+                 s->fail_mode = FAIL_OPEN;
+             } else if (!strcmp(optarg, "closed")) {
+                 s->fail_mode = FAIL_CLOSED;
+             } else {
 -           "                          attempts (default: 15 seconds)\n"
++                ovs_fatal(0, "--fail argument must be \"open\" or \"closed\"");
+             }
+             break;
+         case OPT_INACTIVITY_PROBE:
+             s->probe_interval = atoi(optarg);
+             if (s->probe_interval < 5) {
+                 ovs_fatal(0, "--inactivity-probe argument must be at least 5");
+             }
+             break;
+         case OPT_MAX_IDLE:
+             if (!strcmp(optarg, "permanent")) {
+                 s->max_idle = OFP_FLOW_PERMANENT;
+             } else {
+                 s->max_idle = atoi(optarg);
+                 if (s->max_idle < 1 || s->max_idle > 65535) {
+                     ovs_fatal(0, "--max-idle argument must be between 1 and "
+                               "65535 or the word 'permanent'");
+                 }
+             }
+             break;
+         case OPT_MAX_BACKOFF:
+             s->max_backoff = atoi(optarg);
+             if (s->max_backoff < 1) {
+                 ovs_fatal(0, "--max-backoff argument must be at least 1");
+             } else if (s->max_backoff > 3600) {
+                 s->max_backoff = 3600;
+             }
+             break;
+         case OPT_RATE_LIMIT:
+             if (optarg) {
+                 s->rate_limit = atoi(optarg);
+                 if (s->rate_limit < 1) {
+                     ovs_fatal(0, "--rate-limit argument must be at least 1");
+                 }
+             } else {
+                 s->rate_limit = 1000;
+             }
+             break;
+         case OPT_BURST_LIMIT:
+             s->burst_limit = atoi(optarg);
+             if (s->burst_limit < 1) {
+                 ovs_fatal(0, "--burst-limit argument must be at least 1");
+             }
+             break;
+         case OPT_STP:
+             s->enable_stp = true;
+             break;
+         case OPT_NO_STP:
+             s->enable_stp = false;
+             break;
+         case OPT_OUT_OF_BAND:
+             s->in_band = false;
+             break;
+         case OPT_IN_BAND:
+             s->in_band = true;
+             break;
+         case OPT_COMMAND_ACL:
+             s->command_acl = (s->command_acl[0]
+                               ? xasprintf("%s,%s", s->command_acl, optarg)
+                               : optarg);
+             break;
+         case OPT_COMMAND_DIR:
+             s->command_dir = optarg;
+             break;
+         case OPT_NETFLOW:
+             svec_add(&s->netflow, optarg);
+             break;
+         case OPT_MGMT_ID:
+             if (strlen(optarg) != 12
+                 || strspn(optarg, "0123456789abcdefABCDEF") != 12) {
+                 ovs_fatal(0, "argument to --mgmt-id must be "
+                           "exactly 12 hex digits");
+             }
+             s->mgmt_id = strtoll(optarg, NULL, 16);
+             if (!s->mgmt_id) {
+                 ovs_fatal(0, "argument to --mgmt-id must be nonzero");
+             }
+             break;
+         case 'l':
+             svec_add(&s->listeners, optarg);
+             break;
+         case OPT_SNOOP:
+             svec_add(&s->snoops, optarg);
+             break;
+         case 'h':
+             usage();
+         case 'V':
+             OVS_PRINT_VERSION(OFP_VERSION, OFP_VERSION);
+             exit(EXIT_SUCCESS);
+         DAEMON_OPTION_HANDLERS
+         VLOG_OPTION_HANDLERS
+         LEAK_CHECKER_OPTION_HANDLERS
+ #ifdef HAVE_OPENSSL
+         VCONN_SSL_OPTION_HANDLERS
+         case OPT_BOOTSTRAP_CA_CERT:
+             vconn_ssl_set_ca_cert_file(optarg, true);
+             break;
+ #endif
+         case '?':
+             exit(EXIT_FAILURE);
+         default:
+             abort();
+         }
+     }
+     free(short_options);
+     argc -= optind;
+     argv += optind;
+     if (argc < 1 || argc > 2) {
+         ovs_fatal(0, "need one or two non-option arguments; "
+                   "use --help for usage");
+     }
+     /* Local and remote vconns. */
+     s->dp_name = argv[0];
+     s->controller_name = argc > 1 ? xstrdup(argv[1]) : NULL;
+     /* Set accept_controller_regex. */
+     if (!s->accept_controller_re) {
+         s->accept_controller_re
+             = vconn_ssl_is_configured() ? "^ssl:.*" : "^tcp:.*";
+     }
+     /* Mode of operation. */
+     s->discovery = s->controller_name == NULL;
+     if (s->discovery && !s->in_band) {
+         ovs_fatal(0, "Cannot perform discovery with out-of-band control");
+     }
+     /* Rate limiting. */
+     if (s->rate_limit && s->rate_limit < 100) {
+         VLOG_WARN("Rate limit set to unusually low value %d", s->rate_limit);
+     }
+ }
+ static void
+ usage(void)
+ {
+     printf("%s: an OpenFlow switch implementation.\n"
+            "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 ovs-openflowd performs controller discovery.\n",
+            program_name, program_name);
+     vconn_usage(true, true, true);
+     printf("\nOpenFlow options:\n"
+            "  -d, --datapath-id=ID    Use ID as the OpenFlow switch ID\n"
+            "                          (ID must consist of 12 hex digits)\n"
+            "  --mgmt-id=ID            Use ID as the management ID\n"
+            "                          (ID must consist of 12 hex digits)\n"
+            "  --manufacturer=MFR      Identify manufacturer as MFR\n"
+            "  --hardware=HW           Identify hardware as HW\n"
+            "  --software=SW           Identify software as SW\n"
+            "  --serial=SERIAL         Identify serial number as SERIAL\n"
+            "\nController discovery options:\n"
+            "  --accept-vconn=REGEX    accept matching discovered controllers\n"
+            "  --no-resolv-conf        do not update /etc/resolv.conf\n"
+            "\nNetworking options:\n"
+            "  --fail=open|closed      when controller connection fails:\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 switch\n"
+            "  --max-backoff=SECS      max time between controller connection\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"
+            "                          (a passive OpenFlow connection method)\n"
+            "  --out-of-band           controller connection is out-of-band\n"
+            "  --netflow=HOST:PORT     configure NetFlow output target\n"
+            "\nRate-limiting of \"packet-in\" messages to the controller:\n"
+            "  --rate-limit[=PACKETS]  max rate, in packets/s (default: 1000)\n"
+            "  --burst-limit=BURST     limit on packet credit for idle time\n"
+            "\nRemote command execution options:\n"
+            "  --command-acl=[!]GLOB[,[!]GLOB...] set allowed/denied commands\n"
+            "  --command-dir=DIR       set command dir (default: %s/commands)\n",
+            ovs_pkgdatadir);
+     daemon_usage();
+     vlog_usage();
+     printf("\nOther options:\n"
+            "  -h, --help              display this help message\n"
+            "  -V, --version           display version information\n");
+     leak_checker_usage();
+     exit(EXIT_SUCCESS);
+ }
@@@ -305,8 -311,7 +313,9 @@@ bridge_init(void
          }
      }
  
 +    unixctl_command_register("bridge/dump-flows", bridge_unixctl_dump_flows);
 +
+     bond_init();
      bridge_reconfigure();
  }
  
@@@ -457,42 -524,20 +528,34 @@@ bridge_reconfigure(void
          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);
@@@ -639,67 -660,28 +683,66 @@@ bridge_pick_local_hw_addr(struct bridg
          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);
@@@ -2980,9 -2931,8 +3042,9 @@@ port_update_bond_compat(struct port *po
          if (slave->up) {
              bond.up = true;
          }
-         memcpy(slave->mac, iface->mac, ETH_ADDR_LEN);
+         netdev_get_etheraddr(iface->netdev, slave->mac);
      }
 +
      proc_net_compat_update_bond(port->name, &bond);
      free(bond.slaves);
  }
Simple merge
@@@ -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;
Simple merge
@@@ -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,34 -249,212 +250,239 @@@ 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")
@@@ -461,13 -653,12 +681,12 @@@ 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':
Simple merge