Merge citrix branch into master.
authorBen Pfaff <blp@nicira.com>
Tue, 22 Sep 2009 17:17:44 +0000 (10:17 -0700)
committerBen Pfaff <blp@nicira.com>
Tue, 22 Sep 2009 17:17:44 +0000 (10:17 -0700)
17 files changed:
1  2 
configure.ac
datapath/datapath.h
lib/automake.mk
lib/netdev-linux.c
lib/rconn.c
lib/vconn.c
ofproto/fail-open.c
ofproto/fail-open.h
ofproto/in-band.c
ofproto/ofproto.c
ofproto/pktbuf.c
ofproto/pktbuf.h
utilities/ovs-appctl.8.in
utilities/ovs-controller.8.in
vswitchd/bridge.c
vswitchd/ovs-brcompatd.c
xenserver/usr_sbin_xen-bugtool

diff --combined configure.ac
  # See the License for the specific language governing permissions and
  # limitations under the License.
  
 -AC_PREREQ(2.60)
 +AC_PREREQ(2.63)
- AC_INIT(openvswitch, 0.90.3, bugs@openvswitch.org)
+ AC_INIT(openvswitch, 0.90.5, ovs-bugs@openvswitch.org)
  NX_BUILDNR
  AC_CONFIG_SRCDIR([datapath/datapath.c])
  AC_CONFIG_MACRO_DIR([m4])
  AC_CONFIG_AUX_DIR([build-aux])
  AC_CONFIG_HEADERS([config.h])
 +AC_CONFIG_TESTDIR([tests])
  AM_INIT_AUTOMAKE
  
  AC_PROG_CC
@@@ -38,7 -37,7 +38,7 @@@ AC_USE_SYSTEM_EXTENSION
  AC_C_BIGENDIAN
  AC_SYS_LARGEFILE
  
 -OVS_CHECK_USERSPACE
 +OVS_CHECK_COVERAGE
  OVS_CHECK_NDEBUG
  OVS_CHECK_NETLINK
  OVS_CHECK_OPENSSL
@@@ -46,35 -45,36 +46,35 @@@ OVS_CHECK_LOGDI
  OVS_CHECK_CURSES
  OVS_CHECK_LINUX_VT_H
  OVS_CHECK_PCRE
 +OVS_CHECK_PYTHON
  OVS_CHECK_IF_PACKET
  OVS_CHECK_STRTOK_R
  
 -if $build_userspace; then
 -    OVS_CHECK_PKIDIR
 -    OVS_CHECK_RUNDIR
 -    OVS_CHECK_MALLOC_HOOKS
 -    OVS_CHECK_VALGRIND
 -    OVS_CHECK_TTY_LOCK_DIR
 -    OVS_CHECK_SOCKET_LIBS
 -    OVS_CHECK_FAULT_LIBS
 +OVS_CHECK_PKIDIR
 +OVS_CHECK_RUNDIR
 +OVS_CHECK_MALLOC_HOOKS
 +OVS_CHECK_VALGRIND
 +OVS_CHECK_TTY_LOCK_DIR
 +OVS_CHECK_SOCKET_LIBS
 +OVS_CHECK_FAULT_LIBS
  
 -    AC_CHECK_FUNCS([strsignal])
 +AC_CHECK_FUNCS([strsignal])
  
 -    OVS_ENABLE_OPTION([-Wall])
 -    OVS_ENABLE_OPTION([-Wno-sign-compare])
 -    OVS_ENABLE_OPTION([-Wpointer-arith])
 -    OVS_ENABLE_OPTION([-Wdeclaration-after-statement])
 -    OVS_ENABLE_OPTION([-Wformat-security])
 -    OVS_ENABLE_OPTION([-Wswitch-enum])
 -    OVS_ENABLE_OPTION([-Wunused-parameter])
 -    OVS_ENABLE_OPTION([-Wstrict-aliasing])
 -    OVS_ENABLE_OPTION([-Wbad-function-cast])
 -    OVS_ENABLE_OPTION([-Wcast-align])
 -    OVS_ENABLE_OPTION([-Wstrict-prototypes])
 -    OVS_ENABLE_OPTION([-Wold-style-definition])
 -    OVS_ENABLE_OPTION([-Wmissing-prototypes])
 -    OVS_ENABLE_OPTION([-Wmissing-field-initializers])
 -    OVS_ENABLE_OPTION([-Wno-override-init])
 -fi
 +OVS_ENABLE_OPTION([-Wall])
 +OVS_ENABLE_OPTION([-Wno-sign-compare])
 +OVS_ENABLE_OPTION([-Wpointer-arith])
 +OVS_ENABLE_OPTION([-Wdeclaration-after-statement])
 +OVS_ENABLE_OPTION([-Wformat-security])
 +OVS_ENABLE_OPTION([-Wswitch-enum])
 +OVS_ENABLE_OPTION([-Wunused-parameter])
 +OVS_ENABLE_OPTION([-Wstrict-aliasing])
 +OVS_ENABLE_OPTION([-Wbad-function-cast])
 +OVS_ENABLE_OPTION([-Wcast-align])
 +OVS_ENABLE_OPTION([-Wstrict-prototypes])
 +OVS_ENABLE_OPTION([-Wold-style-definition])
 +OVS_ENABLE_OPTION([-Wmissing-prototypes])
 +OVS_ENABLE_OPTION([-Wmissing-field-initializers])
 +OVS_ENABLE_OPTION([-Wno-override-init])
  
  AC_ARG_VAR(KARCH, [Kernel Architecture String])
  AC_SUBST(KARCH)
@@@ -84,7 -84,6 +84,7 @@@ AC_CONFIG_FILES([Makefil
  datapath/Makefile 
  datapath/linux-2.6/Kbuild
  datapath/linux-2.6/Makefile
 -datapath/linux-2.6/Makefile.main])
 +datapath/linux-2.6/Makefile.main
 +tests/atlocal])
  
  AC_OUTPUT
diff --combined datapath/datapath.h
  #include <asm/page.h>
  #include <linux/kernel.h>
  #include <linux/mutex.h>
 -#include <linux/netlink.h>
  #include <linux/netdevice.h>
  #include <linux/workqueue.h>
  #include <linux/skbuff.h>
 +#include <linux/version.h>
  #include "flow.h"
  #include "dp_sysfs.h"
  
@@@ -25,7 -25,7 +25,7 @@@
   * then this should go into include/linux/if_vlan.h. */
  #define VLAN_PCP_MASK 0xe000
  
- #define DP_MAX_PORTS 256
+ #define DP_MAX_PORTS 1024
  #define DP_MAX_GROUPS 16
  
  #define DP_L2_BITS (PAGE_SHIFT - ilog2(sizeof(struct dp_bucket*)))
@@@ -167,6 -167,4 +167,6 @@@ static inline int skb_checksum_setup(st
  }
  #endif
  
 +int vswitch_skb_checksum_setup(struct sk_buff *skb);
 +
  #endif /* datapath.h */
diff --combined lib/automake.mk
@@@ -32,11 -32,6 +32,11 @@@ lib_libopenvswitch_a_SOURCES = 
        lib/dhcp.h \
        lib/dhparams.h \
        lib/dirs.h \
 +      lib/dpif-linux.c \
 +      lib/dpif-netdev.c \
 +      lib/dpif-provider.h \
 +      lib/dpif.c \
 +      lib/dpif.h \
        lib/dynamic-string.c \
        lib/dynamic-string.h \
        lib/fatal-signal.c \
@@@ -57,8 -52,6 +57,8 @@@
        lib/list.h \
        lib/mac-learning.c \
        lib/mac-learning.h \
 +      lib/netdev-linux.c \
 +      lib/netdev-provider.h \
        lib/netdev.c \
        lib/netdev.h \
        lib/odp-util.c \
@@@ -67,6 -60,7 +67,7 @@@
        lib/ofp-print.h \
        lib/ofpbuf.c \
        lib/ofpbuf.h \
+       lib/packets.c \
        lib/packets.h \
        lib/pcap.c \
        lib/pcap.h \
@@@ -82,8 -76,6 +83,8 @@@
        lib/random.h \
        lib/rconn.c \
        lib/rconn.h \
 +      lib/rtnetlink.c \
 +      lib/rtnetlink.h \
        lib/sat-math.h \
        lib/sha1.c \
        lib/sha1.h \
@@@ -126,6 -118,8 +127,6 @@@ CLEANFILES += $(nodist_lib_libopenvswit
  
  if HAVE_NETLINK
  lib_libopenvswitch_a_SOURCES += \
 -      lib/dpif.c \
 -      lib/dpif.h \
        lib/netlink-protocol.h \
        lib/netlink.c \
        lib/netlink.h
@@@ -180,19 -174,17 +181,19 @@@ COVERAGE_FILES = 
        lib/hmap.c \
        lib/mac-learning.c \
        lib/netdev.c \
 +      lib/netdev-linux.c \
        lib/netlink.c \
        lib/odp-util.c \
        lib/poll-loop.c \
        lib/process.c \
        lib/rconn.c \
 +      lib/rtnetlink.c \
        lib/timeval.c \
        lib/unixctl.c \
        lib/util.c \
        lib/vconn.c \
 -      secchan/ofproto.c \
 -      secchan/pktbuf.c \
 +      ofproto/ofproto.c \
 +      ofproto/pktbuf.c \
        vswitchd/bridge.c \
        vswitchd/mgmt.c \
        vswitchd/ovs-brcompatd.c
diff --combined lib/netdev-linux.c
index 2faffa3,0000000..5abf6e1
mode 100644,000000..100644
--- /dev/null
@@@ -1,1733 -1,0 +1,1736 @@@
 +/*
 + * 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 <assert.h>
 +#include <errno.h>
 +#include <fcntl.h>
 +#include <arpa/inet.h>
 +#include <inttypes.h>
 +#include <linux/if_tun.h>
 +#include <linux/types.h>
 +#include <linux/ethtool.h>
 +#include <linux/rtnetlink.h>
 +#include <linux/sockios.h>
 +#include <linux/version.h>
 +#include <sys/types.h>
 +#include <sys/ioctl.h>
 +#include <sys/socket.h>
 +#include <netpacket/packet.h>
 +#include <net/ethernet.h>
 +#include <net/if.h>
 +#include <net/if_arp.h>
 +#include <net/if_packet.h>
 +#include <net/route.h>
 +#include <netinet/in.h>
 +#include <poll.h>
 +#include <stdlib.h>
 +#include <string.h>
 +#include <unistd.h>
 +
 +#include "coverage.h"
 +#include "dynamic-string.h"
 +#include "fatal-signal.h"
 +#include "netdev-provider.h"
 +#include "netlink.h"
 +#include "ofpbuf.h"
 +#include "openflow/openflow.h"
 +#include "packets.h"
 +#include "poll-loop.h"
 +#include "rtnetlink.h"
 +#include "socket-util.h"
 +#include "shash.h"
 +#include "svec.h"
 +
 +#define THIS_MODULE VLM_netdev_linux
 +#include "vlog.h"
 +\f
 +/* These were introduced in Linux 2.6.14, so they might be missing if we have
 + * old headers. */
 +#ifndef ADVERTISED_Pause
 +#define ADVERTISED_Pause                (1 << 13)
 +#endif
 +#ifndef ADVERTISED_Asym_Pause
 +#define ADVERTISED_Asym_Pause           (1 << 14)
 +#endif
 +
 +struct netdev_linux {
 +    struct netdev netdev;
 +
 +    /* File descriptors.  For ordinary network devices, the two fds below are
 +     * the same; for tap devices, they differ. */
 +    int netdev_fd;              /* Network device. */
 +    int tap_fd;                 /* TAP character device, if any, otherwise the
 +                                 * network device. */
 +
 +    struct netdev_linux_cache *cache;
 +};
 +
 +enum {
 +    VALID_IFINDEX = 1 << 0,
 +    VALID_ETHERADDR = 1 << 1,
 +    VALID_IN4 = 1 << 2,
 +    VALID_IN6 = 1 << 3,
 +    VALID_MTU = 1 << 4,
 +    VALID_CARRIER = 1 << 5,
 +    VALID_IS_INTERNAL = 1 << 6
 +};
 +
 +/* Cached network device information. */
 +struct netdev_linux_cache {
 +    struct shash_node *shash_node;
 +    unsigned int valid;
 +    int ref_cnt;
 +
 +    int ifindex;
 +    uint8_t etheraddr[ETH_ADDR_LEN];
 +    struct in_addr address, netmask;
 +    struct in6_addr in6;
 +    int mtu;
 +    int carrier;
 +    bool is_internal;
 +};
 +
 +static struct shash cache_map = SHASH_INITIALIZER(&cache_map);
 +static struct rtnetlink_notifier netdev_linux_cache_notifier;
 +
 +/* An AF_INET socket (used for ioctl operations). */
 +static int af_inet_sock = -1;
 +
 +struct netdev_linux_notifier {
 +    struct netdev_notifier notifier;
 +    struct list node;
 +};
 +
 +static struct shash netdev_linux_notifiers =
 +    SHASH_INITIALIZER(&netdev_linux_notifiers);
 +static struct rtnetlink_notifier netdev_linux_poll_notifier;
 +
 +/* This is set pretty low because we probably won't learn anything from the
 + * additional log messages. */
 +static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
 +
 +static int netdev_linux_do_ethtool(struct netdev *, struct ethtool_cmd *,
 +                                   int cmd, const char *cmd_name);
 +static int netdev_linux_do_ioctl(const struct netdev *, struct ifreq *,
 +                                 int cmd, const char *cmd_name);
 +static int netdev_linux_get_ipv4(const struct netdev *, struct in_addr *,
 +                                 int cmd, const char *cmd_name);
 +static int get_flags(const struct netdev *, int *flagsp);
 +static int set_flags(struct netdev *, int flags);
 +static int do_get_ifindex(const char *netdev_name);
 +static int get_ifindex(const struct netdev *, int *ifindexp);
 +static int do_set_addr(struct netdev *netdev,
 +                       int ioctl_nr, const char *ioctl_name,
 +                       struct in_addr addr);
 +static int get_etheraddr(const char *netdev_name, uint8_t ea[ETH_ADDR_LEN]);
 +static int set_etheraddr(const char *netdev_name, int hwaddr_family,
 +                         const uint8_t[ETH_ADDR_LEN]);
 +static int get_stats_via_netlink(int ifindex, struct netdev_stats *stats);
 +static int get_stats_via_proc(const char *netdev_name, struct netdev_stats *stats);
 +
 +static struct netdev_linux *
 +netdev_linux_cast(const struct netdev *netdev)
 +{
 +    netdev_assert_class(netdev, &netdev_linux_class);
 +    return CONTAINER_OF(netdev, struct netdev_linux, netdev);
 +}
 +
 +static int
 +netdev_linux_init(void)
 +{
 +    static int status = -1;
 +    if (status < 0) {
 +        af_inet_sock = socket(AF_INET, SOCK_DGRAM, 0);
 +        status = af_inet_sock >= 0 ? 0 : errno;
 +        if (status) {
 +            VLOG_ERR("failed to create inet socket: %s", strerror(status));
 +        }
 +    }
 +    return status;
 +}
 +
 +static void
 +netdev_linux_run(void)
 +{
 +    rtnetlink_notifier_run();
 +}
 +
 +static void
 +netdev_linux_wait(void)
 +{
 +    rtnetlink_notifier_wait();
 +}
 +
 +static void
 +netdev_linux_cache_cb(const struct rtnetlink_change *change,
 +                      void *aux UNUSED)
 +{
 +    struct netdev_linux_cache *cache;
 +    if (change) {
 +        cache = shash_find_data(&cache_map, change->ifname);
 +        if (cache) {
 +            cache->valid = 0;
 +        }
 +    } else {
 +        struct shash_node *node;
 +        SHASH_FOR_EACH (node, &cache_map) {
 +            cache = node->data;
 +            cache->valid = 0;
 +        }
 +    }
 +}
 +
 +static int
 +netdev_linux_open(const char *name, char *suffix, int ethertype,
 +                  struct netdev **netdevp)
 +{
 +    struct netdev_linux *netdev;
 +    enum netdev_flags flags;
 +    int error;
 +
 +    /* Allocate network device. */
 +    netdev = xcalloc(1, sizeof *netdev);
 +    netdev_init(&netdev->netdev, suffix, &netdev_linux_class);
 +    netdev->netdev_fd = -1;
 +    netdev->tap_fd = -1;
 +    netdev->cache = shash_find_data(&cache_map, suffix);
 +    if (!netdev->cache) {
 +        if (shash_is_empty(&cache_map)) {
 +            int error = rtnetlink_notifier_register(
 +                &netdev_linux_cache_notifier, netdev_linux_cache_cb, NULL);
 +            if (error) {
 +                netdev_close(&netdev->netdev);
 +                return error;
 +            }
 +        }
 +        netdev->cache = xmalloc(sizeof *netdev->cache);
 +        netdev->cache->shash_node = shash_add(&cache_map, suffix,
 +                                              netdev->cache);
 +        netdev->cache->valid = 0;
 +        netdev->cache->ref_cnt = 0;
 +    }
 +    netdev->cache->ref_cnt++;
 +
 +    if (!strncmp(name, "tap:", 4)) {
 +        static const char tap_dev[] = "/dev/net/tun";
 +        struct ifreq ifr;
 +
 +        /* Open tap device. */
 +        netdev->tap_fd = open(tap_dev, O_RDWR);
 +        if (netdev->tap_fd < 0) {
 +            error = errno;
 +            VLOG_WARN("opening \"%s\" failed: %s", tap_dev, strerror(error));
 +            goto error;
 +        }
 +
 +        /* Create tap device. */
 +        ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
 +        error = netdev_linux_do_ioctl(&netdev->netdev, &ifr,
 +                                      TUNSETIFF, "TUNSETIFF");
 +        if (error) {
 +            goto error;
 +        }
 +
 +        /* Make non-blocking. */
 +        error = set_nonblocking(netdev->tap_fd);
 +        if (error) {
 +            goto error;
 +        }
 +    }
 +
 +    error = netdev_get_flags(&netdev->netdev, &flags);
 +    if (error == ENODEV) {
 +        goto error;
 +    }
 +
 +    if (netdev->tap_fd >= 0 || ethertype != NETDEV_ETH_TYPE_NONE) {
 +        struct sockaddr_ll sll;
 +        int protocol;
 +        int ifindex;
 +
 +        /* Create file descriptor. */
 +        protocol = (ethertype == NETDEV_ETH_TYPE_ANY ? ETH_P_ALL
 +                    : ethertype == NETDEV_ETH_TYPE_802_2 ? ETH_P_802_2
 +                    : ethertype);
 +        netdev->netdev_fd = socket(PF_PACKET, SOCK_RAW, htons(protocol));
 +        if (netdev->netdev_fd < 0) {
 +            error = errno;
 +            goto error;
 +        }
 +        if (netdev->tap_fd < 0) {
 +            netdev->tap_fd = netdev->netdev_fd;
 +        }
 +
 +        /* Set non-blocking mode. */
 +        error = set_nonblocking(netdev->netdev_fd);
 +        if (error) {
 +            goto error;
 +        }
 +
 +        /* Get ethernet device index. */
 +        error = get_ifindex(&netdev->netdev, &ifindex);
 +        if (error) {
 +            goto error;
 +        }
 +
 +        /* Bind to specific ethernet device. */
 +        memset(&sll, 0, sizeof sll);
 +        sll.sll_family = AF_PACKET;
 +        sll.sll_ifindex = ifindex;
 +        if (bind(netdev->netdev_fd,
 +                 (struct sockaddr *) &sll, sizeof sll) < 0) {
 +            error = errno;
 +            VLOG_ERR("bind to %s failed: %s", suffix, strerror(error));
 +            goto error;
 +        }
 +
 +        /* Between the socket() and bind() calls above, the socket receives all
 +         * packets of the requested type on all system interfaces.  We do not
 +         * want to receive that data, but there is no way to avoid it.  So we
 +         * must now drain out the receive queue. */
 +        error = drain_rcvbuf(netdev->netdev_fd);
 +        if (error) {
 +            goto error;
 +        }
 +    }
 +
 +    *netdevp = &netdev->netdev;
 +    return 0;
 +
 +error:
 +    netdev_close(&netdev->netdev);
 +    return error;
 +}
 +
 +/* Closes and destroys 'netdev'. */
 +static void
 +netdev_linux_close(struct netdev *netdev_)
 +{
 +    struct netdev_linux *netdev = netdev_linux_cast(netdev_);
 +
 +    if (netdev->cache && !--netdev->cache->ref_cnt) {
 +        shash_delete(&cache_map, netdev->cache->shash_node);
 +        free(netdev->cache);
 +
 +        if (shash_is_empty(&cache_map)) {
 +            rtnetlink_notifier_unregister(&netdev_linux_cache_notifier);
 +        }
 +    }
 +    if (netdev->netdev_fd >= 0) {
 +        close(netdev->netdev_fd);
 +    }
 +    if (netdev->tap_fd >= 0 && netdev->netdev_fd != netdev->tap_fd) {
 +        close(netdev->tap_fd);
 +    }
 +    free(netdev);
 +}
 +
 +/* Initializes 'svec' with a list of the names of all known network devices. */
 +static int
 +netdev_linux_enumerate(struct svec *svec)
 +{
 +    struct if_nameindex *names;
 +
 +    names = if_nameindex();
 +    if (names) {
 +        size_t i;
 +
 +        for (i = 0; names[i].if_name != NULL; i++) {
 +            svec_add(svec, names[i].if_name);
 +        }
 +        if_freenameindex(names);
 +        return 0;
 +    } else {
 +        VLOG_WARN("could not obtain list of network device names: %s",
 +                  strerror(errno));
 +        return errno;
 +    }
 +}
 +
 +static int
 +netdev_linux_recv(struct netdev *netdev_, void *data, size_t size)
 +{
 +    struct netdev_linux *netdev = netdev_linux_cast(netdev_);
 +
 +    if (netdev->tap_fd < 0) {
 +        /* Device was opened with NETDEV_ETH_TYPE_NONE. */
 +        return EAGAIN;
 +    }
 +
 +    for (;;) {
 +        ssize_t retval = read(netdev->tap_fd, data, size);
 +        if (retval >= 0) {
 +            return retval;
 +        } else if (errno != EINTR) {
 +            if (errno != EAGAIN) {
 +                VLOG_WARN_RL(&rl, "error receiving Ethernet packet on %s: %s",
 +                             strerror(errno), netdev_get_name(netdev_));
 +            }
 +            return errno;
 +        }
 +    }
 +}
 +
 +/* Registers with the poll loop to wake up from the next call to poll_block()
 + * when a packet is ready to be received with netdev_recv() on 'netdev'. */
 +static void
 +netdev_linux_recv_wait(struct netdev *netdev_)
 +{
 +    struct netdev_linux *netdev = netdev_linux_cast(netdev_);
 +    if (netdev->tap_fd >= 0) {
 +        poll_fd_wait(netdev->tap_fd, POLLIN);
 +    }
 +}
 +
 +/* Discards all packets waiting to be received from 'netdev'. */
 +static int
 +netdev_linux_drain(struct netdev *netdev_)
 +{
 +    struct netdev_linux *netdev = netdev_linux_cast(netdev_);
 +    if (netdev->tap_fd < 0 && netdev->netdev_fd < 0) {
 +        return 0;
 +    } else if (netdev->tap_fd != netdev->netdev_fd) {
 +        struct ifreq ifr;
 +        int error = netdev_linux_do_ioctl(netdev_, &ifr,
 +                                          SIOCGIFTXQLEN, "SIOCGIFTXQLEN");
 +        if (error) {
 +            return error;
 +        }
 +        drain_fd(netdev->tap_fd, ifr.ifr_qlen);
 +        return 0;
 +    } else {
 +        return drain_rcvbuf(netdev->netdev_fd);
 +    }
 +}
 +
 +/* Sends 'buffer' on 'netdev'.  Returns 0 if successful, otherwise a positive
 + * errno value.  Returns EAGAIN without blocking if the packet cannot be queued
 + * immediately.  Returns EMSGSIZE if a partial packet was transmitted or if
 + * the packet is too big or too small to transmit on the device.
 + *
 + * The caller retains ownership of 'buffer' in all cases.
 + *
 + * The kernel maintains a packet transmission queue, so the caller is not
 + * expected to do additional queuing of packets. */
 +static int
 +netdev_linux_send(struct netdev *netdev_, const void *data, size_t size)
 +{
 +    struct netdev_linux *netdev = netdev_linux_cast(netdev_);
 +
 +    /* XXX should support sending even if 'ethertype' was NETDEV_ETH_TYPE_NONE.
 +     */
 +    if (netdev->tap_fd < 0) {
 +        return EPIPE;
 +    }
 +
 +    for (;;) {
 +        ssize_t retval = write(netdev->tap_fd, data, size);
 +        if (retval < 0) {
 +            /* The Linux AF_PACKET implementation never blocks waiting for room
 +             * for packets, instead returning ENOBUFS.  Translate this into
 +             * EAGAIN for the caller. */
 +            if (errno == ENOBUFS) {
 +                return EAGAIN;
 +            } else if (errno == EINTR) {
 +                continue;
 +            } else if (errno != EAGAIN) {
 +                VLOG_WARN_RL(&rl, "error sending Ethernet packet on %s: %s",
 +                             netdev_get_name(netdev_), strerror(errno));
 +            }
 +            return errno;
 +        } else if (retval != size) {
 +            VLOG_WARN_RL(&rl, "sent partial Ethernet packet (%zd bytes of "
 +                         "%zu) on %s", retval, size, netdev_get_name(netdev_));
 +            return EMSGSIZE;
 +        } else {
 +            return 0;
 +        }
 +    }
 +}
 +
 +/* Registers with the poll loop to wake up from the next call to poll_block()
 + * when the packet transmission queue has sufficient room to transmit a packet
 + * with netdev_send().
 + *
 + * The kernel maintains a packet transmission queue, so the client is not
 + * expected to do additional queuing of packets.  Thus, this function is
 + * unlikely to ever be used.  It is included for completeness. */
 +static void
 +netdev_linux_send_wait(struct netdev *netdev_)
 +{
 +    struct netdev_linux *netdev = netdev_linux_cast(netdev_);
 +    if (netdev->tap_fd < 0 && netdev->netdev_fd < 0) {
 +        /* Nothing to do. */
 +    } else if (netdev->tap_fd == netdev->netdev_fd) {
 +        poll_fd_wait(netdev->tap_fd, POLLOUT);
 +    } else {
 +        /* TAP device always accepts packets.*/
 +        poll_immediate_wake();
 +    }
 +}
 +
 +/* Attempts to set 'netdev''s MAC address to 'mac'.  Returns 0 if successful,
 + * otherwise a positive errno value. */
 +static int
 +netdev_linux_set_etheraddr(struct netdev *netdev_,
 +                           const uint8_t mac[ETH_ADDR_LEN])
 +{
 +    struct netdev_linux *netdev = netdev_linux_cast(netdev_);
 +    int error = set_etheraddr(netdev_get_name(netdev_), ARPHRD_ETHER, mac);
 +    if (!error) {
 +        memcpy(netdev->cache->etheraddr, mac, ETH_ADDR_LEN);
 +    }
 +    return error;
 +}
 +
 +/* Returns a pointer to 'netdev''s MAC address.  The caller must not modify or
 + * free the returned buffer. */
 +static int
 +netdev_linux_get_etheraddr(const struct netdev *netdev_,
 +                           uint8_t mac[ETH_ADDR_LEN])
 +{
 +    struct netdev_linux *netdev = netdev_linux_cast(netdev_);
 +    if (!(netdev->cache->valid & VALID_ETHERADDR)) {
 +        int error = get_etheraddr(netdev_get_name(netdev_),
 +                                  netdev->cache->etheraddr);
 +        if (error) {
 +            return error;
 +        }
 +        netdev->cache->valid |= VALID_ETHERADDR;
 +    }
 +    memcpy(mac, netdev->cache->etheraddr, ETH_ADDR_LEN);
 +    return 0;
 +}
 +
 +/* Returns the maximum size of transmitted (and received) packets on 'netdev',
 + * in bytes, not including the hardware header; thus, this is typically 1500
 + * bytes for Ethernet devices. */
 +static int
 +netdev_linux_get_mtu(const struct netdev *netdev_, int *mtup)
 +{
 +    struct netdev_linux *netdev = netdev_linux_cast(netdev_);
 +    if (!(netdev->cache->valid & VALID_MTU)) {
 +        struct ifreq ifr;
 +        int error;
 +
 +        error = netdev_linux_do_ioctl(netdev_, &ifr, SIOCGIFMTU, "SIOCGIFMTU");
 +        if (error) {
 +            return error;
 +        }
 +        netdev->cache->mtu = ifr.ifr_mtu;
 +        netdev->cache->valid |= VALID_MTU;
 +    }
 +    *mtup = netdev->cache->mtu;
 +    return 0;
 +}
 +
 +static int
 +netdev_linux_get_carrier(const struct netdev *netdev_, bool *carrier)
 +{
 +    struct netdev_linux *netdev = netdev_linux_cast(netdev_);
 +    int error = 0;
 +    char *fn = NULL;
 +    int fd = -1;
 +
 +    if (!(netdev->cache->valid & VALID_CARRIER)) {
 +        char line[8];
 +        int retval;
 +
 +        fn = xasprintf("/sys/class/net/%s/carrier", netdev_get_name(netdev_));
 +        fd = open(fn, O_RDONLY);
 +        if (fd < 0) {
 +            error = errno;
 +            VLOG_WARN_RL(&rl, "%s: open failed: %s", fn, strerror(error));
 +            goto exit;
 +        }
 +
 +        retval = read(fd, line, sizeof line);
 +        if (retval < 0) {
 +            error = errno;
 +            if (error == EINVAL) {
 +                /* This is the normal return value when we try to check carrier
 +                 * if the network device is not up. */
 +            } else {
 +                VLOG_WARN_RL(&rl, "%s: read failed: %s", fn, strerror(error));
 +            }
 +            goto exit;
 +        } else if (retval == 0) {
 +            error = EPROTO;
 +            VLOG_WARN_RL(&rl, "%s: unexpected end of file", fn);
 +            goto exit;
 +        }
 +
 +        if (line[0] != '0' && line[0] != '1') {
 +            error = EPROTO;
 +            VLOG_WARN_RL(&rl, "%s: value is %c (expected 0 or 1)",
 +                         fn, line[0]);
 +            goto exit;
 +        }
 +        netdev->cache->carrier = line[0] != '0';
 +        netdev->cache->valid |= VALID_CARRIER;
 +    }
 +    *carrier = netdev->cache->carrier;
 +    error = 0;
 +
 +exit:
 +    if (fd >= 0) {
 +        close(fd);
 +    }
 +    free(fn);
 +    return error;
 +}
 +
 +/* Check whether we can we use RTM_GETLINK to get network device statistics.
 + * In pre-2.6.19 kernels, this was only available if wireless extensions were
 + * enabled. */
 +static bool
 +check_for_working_netlink_stats(void)
 +{
 +    /* Decide on the netdev_get_stats() implementation to use.  Netlink is
 +     * preferable, so if that works, we'll use it. */
 +    int ifindex = do_get_ifindex("lo");
 +    if (ifindex < 0) {
 +        VLOG_WARN("failed to get ifindex for lo, "
 +                  "obtaining netdev stats from proc");
 +        return false;
 +    } else {
 +        struct netdev_stats stats;
 +        int error = get_stats_via_netlink(ifindex, &stats);
 +        if (!error) {
 +            VLOG_DBG("obtaining netdev stats via rtnetlink");
 +            return true;
 +        } else {
 +            VLOG_INFO("RTM_GETLINK failed (%s), obtaining netdev stats "
 +                      "via proc (you are probably running a pre-2.6.19 "
 +                      "kernel)", strerror(error));
 +            return false;
 +        }
 +    }
 +}
 +
 +/* Retrieves current device stats for 'netdev'.
 + *
 + * XXX All of the members of struct netdev_stats are 64 bits wide, but on
 + * 32-bit architectures the Linux network stats are only 32 bits. */
 +static int
 +netdev_linux_get_stats(const struct netdev *netdev_, struct netdev_stats *stats)
 +{
 +    struct netdev_linux *netdev = netdev_linux_cast(netdev_);
 +    static int use_netlink_stats = -1;
 +    int error;
 +    struct netdev_stats raw_stats;
 +    struct netdev_stats *collect_stats = stats;
 +
 +    COVERAGE_INC(netdev_get_stats);
 +
 +    if (!(netdev->cache->valid & VALID_IS_INTERNAL)) {
 +        netdev->cache->is_internal = (netdev->tap_fd != -1);
 +
 +        if (!netdev->cache->is_internal) {
 +            struct ethtool_drvinfo drvinfo;
 +
 +            memset(&drvinfo, 0, sizeof drvinfo);
 +            error = netdev_linux_do_ethtool(&netdev->netdev,
 +                                            (struct ethtool_cmd *)&drvinfo,
 +                                            ETHTOOL_GDRVINFO,
 +                                            "ETHTOOL_GDRVINFO");
 +
 +            if (!error) {
 +                netdev->cache->is_internal = !strcmp(drvinfo.driver,
 +                                                     "openvswitch");
 +            }
 +        }
 +
 +        netdev->cache->valid |= VALID_IS_INTERNAL;
 +    }
 +
 +    if (netdev->cache->is_internal) {
 +        collect_stats = &raw_stats;
 +    }
 +
 +    if (use_netlink_stats < 0) {
 +        use_netlink_stats = check_for_working_netlink_stats();
 +    }
 +    if (use_netlink_stats) {
 +        int ifindex;
 +
 +        error = get_ifindex(&netdev->netdev, &ifindex);
 +        if (!error) {
 +            error = get_stats_via_netlink(ifindex, collect_stats);
 +        }
 +    } else {
 +        error = get_stats_via_proc(netdev->netdev.name, collect_stats);
 +    }
 +
 +    /* If this port is an internal port then the transmit and receive stats
 +     * will appear to be swapped relative to the other ports since we are the
 +     * one sending the data, not a remote computer.  For consistency, we swap
 +     * them back here. */
 +    if (netdev->cache->is_internal) {
 +        stats->rx_packets = raw_stats.tx_packets;
 +        stats->tx_packets = raw_stats.rx_packets;
 +        stats->rx_bytes = raw_stats.tx_bytes;
 +        stats->tx_bytes = raw_stats.rx_bytes;
 +        stats->rx_errors = raw_stats.tx_errors;
 +        stats->tx_errors = raw_stats.rx_errors;
 +        stats->rx_dropped = raw_stats.tx_dropped;
 +        stats->tx_dropped = raw_stats.rx_dropped;
 +        stats->multicast = raw_stats.multicast;
 +        stats->collisions = raw_stats.collisions;
 +        stats->rx_length_errors = 0;
 +        stats->rx_over_errors = 0;
 +        stats->rx_crc_errors = 0;
 +        stats->rx_frame_errors = 0;
 +        stats->rx_fifo_errors = 0;
 +        stats->rx_missed_errors = 0;
 +        stats->tx_aborted_errors = 0;
 +        stats->tx_carrier_errors = 0;
 +        stats->tx_fifo_errors = 0;
 +        stats->tx_heartbeat_errors = 0;
 +        stats->tx_window_errors = 0;
 +    }
 +
 +    return error;
 +}
 +
 +/* Stores the features supported by 'netdev' into each of '*current',
 + * '*advertised', '*supported', and '*peer' that are non-null.  Each value is a
 + * bitmap of "enum ofp_port_features" bits, in host byte order.  Returns 0 if
 + * successful, otherwise a positive errno value.  On failure, all of the
 + * passed-in values are set to 0. */
 +static int
 +netdev_linux_get_features(struct netdev *netdev,
 +                          uint32_t *current, uint32_t *advertised,
 +                          uint32_t *supported, uint32_t *peer)
 +{
 +    struct ethtool_cmd ecmd;
 +    int error;
 +
 +    memset(&ecmd, 0, sizeof ecmd);
 +    error = netdev_linux_do_ethtool(netdev, &ecmd,
 +                                    ETHTOOL_GSET, "ETHTOOL_GSET");
 +    if (error) {
 +        return error;
 +    }
 +
 +    /* Supported features. */
 +    *supported = 0;
 +    if (ecmd.supported & SUPPORTED_10baseT_Half) {
 +        *supported |= OFPPF_10MB_HD;
 +    }
 +    if (ecmd.supported & SUPPORTED_10baseT_Full) {
 +        *supported |= OFPPF_10MB_FD;
 +    }
 +    if (ecmd.supported & SUPPORTED_100baseT_Half)  {
 +        *supported |= OFPPF_100MB_HD;
 +    }
 +    if (ecmd.supported & SUPPORTED_100baseT_Full) {
 +        *supported |= OFPPF_100MB_FD;
 +    }
 +    if (ecmd.supported & SUPPORTED_1000baseT_Half) {
 +        *supported |= OFPPF_1GB_HD;
 +    }
 +    if (ecmd.supported & SUPPORTED_1000baseT_Full) {
 +        *supported |= OFPPF_1GB_FD;
 +    }
 +    if (ecmd.supported & SUPPORTED_10000baseT_Full) {
 +        *supported |= OFPPF_10GB_FD;
 +    }
 +    if (ecmd.supported & SUPPORTED_TP) {
 +        *supported |= OFPPF_COPPER;
 +    }
 +    if (ecmd.supported & SUPPORTED_FIBRE) {
 +        *supported |= OFPPF_FIBER;
 +    }
 +    if (ecmd.supported & SUPPORTED_Autoneg) {
 +        *supported |= OFPPF_AUTONEG;
 +    }
 +    if (ecmd.supported & SUPPORTED_Pause) {
 +        *supported |= OFPPF_PAUSE;
 +    }
 +    if (ecmd.supported & SUPPORTED_Asym_Pause) {
 +        *supported |= OFPPF_PAUSE_ASYM;
 +    }
 +
 +    /* Advertised features. */
 +    *advertised = 0;
 +    if (ecmd.advertising & ADVERTISED_10baseT_Half) {
 +        *advertised |= OFPPF_10MB_HD;
 +    }
 +    if (ecmd.advertising & ADVERTISED_10baseT_Full) {
 +        *advertised |= OFPPF_10MB_FD;
 +    }
 +    if (ecmd.advertising & ADVERTISED_100baseT_Half) {
 +        *advertised |= OFPPF_100MB_HD;
 +    }
 +    if (ecmd.advertising & ADVERTISED_100baseT_Full) {
 +        *advertised |= OFPPF_100MB_FD;
 +    }
 +    if (ecmd.advertising & ADVERTISED_1000baseT_Half) {
 +        *advertised |= OFPPF_1GB_HD;
 +    }
 +    if (ecmd.advertising & ADVERTISED_1000baseT_Full) {
 +        *advertised |= OFPPF_1GB_FD;
 +    }
 +    if (ecmd.advertising & ADVERTISED_10000baseT_Full) {
 +        *advertised |= OFPPF_10GB_FD;
 +    }
 +    if (ecmd.advertising & ADVERTISED_TP) {
 +        *advertised |= OFPPF_COPPER;
 +    }
 +    if (ecmd.advertising & ADVERTISED_FIBRE) {
 +        *advertised |= OFPPF_FIBER;
 +    }
 +    if (ecmd.advertising & ADVERTISED_Autoneg) {
 +        *advertised |= OFPPF_AUTONEG;
 +    }
 +    if (ecmd.advertising & ADVERTISED_Pause) {
 +        *advertised |= OFPPF_PAUSE;
 +    }
 +    if (ecmd.advertising & ADVERTISED_Asym_Pause) {
 +        *advertised |= OFPPF_PAUSE_ASYM;
 +    }
 +
 +    /* Current settings. */
 +    if (ecmd.speed == SPEED_10) {
 +        *current = ecmd.duplex ? OFPPF_10MB_FD : OFPPF_10MB_HD;
 +    } else if (ecmd.speed == SPEED_100) {
 +        *current = ecmd.duplex ? OFPPF_100MB_FD : OFPPF_100MB_HD;
 +    } else if (ecmd.speed == SPEED_1000) {
 +        *current = ecmd.duplex ? OFPPF_1GB_FD : OFPPF_1GB_HD;
 +    } else if (ecmd.speed == SPEED_10000) {
 +        *current = OFPPF_10GB_FD;
 +    } else {
 +        *current = 0;
 +    }
 +
 +    if (ecmd.port == PORT_TP) {
 +        *current |= OFPPF_COPPER;
 +    } else if (ecmd.port == PORT_FIBRE) {
 +        *current |= OFPPF_FIBER;
 +    }
 +
 +    if (ecmd.autoneg) {
 +        *current |= OFPPF_AUTONEG;
 +    }
 +
 +    /* Peer advertisements. */
 +    *peer = 0;                  /* XXX */
 +
 +    return 0;
 +}
 +
 +/* Set the features advertised by 'netdev' to 'advertise'. */
 +static int
 +netdev_linux_set_advertisements(struct netdev *netdev, uint32_t advertise)
 +{
 +    struct ethtool_cmd ecmd;
 +    int error;
 +
 +    memset(&ecmd, 0, sizeof ecmd);
 +    error = netdev_linux_do_ethtool(netdev, &ecmd,
 +                                    ETHTOOL_GSET, "ETHTOOL_GSET");
 +    if (error) {
 +        return error;
 +    }
 +
 +    ecmd.advertising = 0;
 +    if (advertise & OFPPF_10MB_HD) {
 +        ecmd.advertising |= ADVERTISED_10baseT_Half;
 +    }
 +    if (advertise & OFPPF_10MB_FD) {
 +        ecmd.advertising |= ADVERTISED_10baseT_Full;
 +    }
 +    if (advertise & OFPPF_100MB_HD) {
 +        ecmd.advertising |= ADVERTISED_100baseT_Half;
 +    }
 +    if (advertise & OFPPF_100MB_FD) {
 +        ecmd.advertising |= ADVERTISED_100baseT_Full;
 +    }
 +    if (advertise & OFPPF_1GB_HD) {
 +        ecmd.advertising |= ADVERTISED_1000baseT_Half;
 +    }
 +    if (advertise & OFPPF_1GB_FD) {
 +        ecmd.advertising |= ADVERTISED_1000baseT_Full;
 +    }
 +    if (advertise & OFPPF_10GB_FD) {
 +        ecmd.advertising |= ADVERTISED_10000baseT_Full;
 +    }
 +    if (advertise & OFPPF_COPPER) {
 +        ecmd.advertising |= ADVERTISED_TP;
 +    }
 +    if (advertise & OFPPF_FIBER) {
 +        ecmd.advertising |= ADVERTISED_FIBRE;
 +    }
 +    if (advertise & OFPPF_AUTONEG) {
 +        ecmd.advertising |= ADVERTISED_Autoneg;
 +    }
 +    if (advertise & OFPPF_PAUSE) {
 +        ecmd.advertising |= ADVERTISED_Pause;
 +    }
 +    if (advertise & OFPPF_PAUSE_ASYM) {
 +        ecmd.advertising |= ADVERTISED_Asym_Pause;
 +    }
 +    return netdev_linux_do_ethtool(netdev, &ecmd,
 +                                   ETHTOOL_SSET, "ETHTOOL_SSET");
 +}
 +
 +/* If 'netdev_name' is the name of a VLAN network device (e.g. one created with
 + * vconfig(8)), sets '*vlan_vid' to the VLAN VID associated with that device
 + * and returns 0.  Otherwise returns a errno value (specifically ENOENT if
 + * 'netdev_name' is the name of a network device that is not a VLAN device) and
 + * sets '*vlan_vid' to -1. */
 +static int
 +netdev_linux_get_vlan_vid(const struct netdev *netdev, int *vlan_vid)
 +{
 +    const char *netdev_name = netdev_get_name(netdev);
 +    struct ds line = DS_EMPTY_INITIALIZER;
 +    FILE *stream = NULL;
 +    int error;
 +    char *fn;
 +
 +    COVERAGE_INC(netdev_get_vlan_vid);
 +    fn = xasprintf("/proc/net/vlan/%s", netdev_name);
 +    stream = fopen(fn, "r");
 +    if (!stream) {
 +        error = errno;
 +        goto done;
 +    }
 +
 +    if (ds_get_line(&line, stream)) {
 +        if (ferror(stream)) {
 +            error = errno;
 +            VLOG_ERR_RL(&rl, "error reading \"%s\": %s", fn, strerror(errno));
 +        } else {
 +            error = EPROTO;
 +            VLOG_ERR_RL(&rl, "unexpected end of file reading \"%s\"", fn);
 +        }
 +        goto done;
 +    }
 +
 +    if (!sscanf(ds_cstr(&line), "%*s VID: %d", vlan_vid)) {
 +        error = EPROTO;
 +        VLOG_ERR_RL(&rl, "parse error reading \"%s\" line 1: \"%s\"",
 +                    fn, ds_cstr(&line));
 +        goto done;
 +    }
 +
 +    error = 0;
 +
 +done:
 +    free(fn);
 +    if (stream) {
 +        fclose(stream);
 +    }
 +    ds_destroy(&line);
 +    if (error) {
 +        *vlan_vid = -1;
 +    }
 +    return error;
 +}
 +
 +#define POLICE_ADD_CMD "/sbin/tc qdisc add dev %s handle ffff: ingress"
 +#define POLICE_CONFIG_CMD "/sbin/tc filter add dev %s parent ffff: protocol ip prio 50 u32 match ip src 0.0.0.0/0 police rate %dkbit burst %dk mtu 65535 drop flowid :1"
 +/* We redirect stderr to /dev/null because we often want to remove all
 + * traffic control configuration on a port so its in a known state.  If
 + * this done when there is no such configuration, tc complains, so we just
 + * always ignore it.
 + */
 +#define POLICE_DEL_CMD "/sbin/tc qdisc del dev %s handle ffff: ingress 2>/dev/null"
 +
 +/* Attempts to set input rate limiting (policing) policy. */
 +static int
 +netdev_linux_set_policing(struct netdev *netdev,
 +                          uint32_t kbits_rate, uint32_t kbits_burst)
 +{
 +    const char *netdev_name = netdev_get_name(netdev);
 +    char command[1024];
 +
 +    COVERAGE_INC(netdev_set_policing);
 +    if (kbits_rate) {
 +        if (!kbits_burst) {
 +            /* Default to 10 kilobits if not specified. */
 +            kbits_burst = 10;
 +        }
 +
 +        /* xxx This should be more careful about only adding if it
 +         * xxx actually exists, as opposed to always deleting it. */
 +        snprintf(command, sizeof(command), POLICE_DEL_CMD, netdev_name);
 +        if (system(command) == -1) {
 +            VLOG_WARN_RL(&rl, "%s: problem removing policing", netdev_name);
 +        }
 +
 +        snprintf(command, sizeof(command), POLICE_ADD_CMD, netdev_name);
 +        if (system(command) != 0) {
 +            VLOG_WARN_RL(&rl, "%s: problem adding policing", netdev_name);
 +            return -1;
 +        }
 +
 +        snprintf(command, sizeof(command), POLICE_CONFIG_CMD, netdev_name,
 +                kbits_rate, kbits_burst);
 +        if (system(command) != 0) {
 +            VLOG_WARN_RL(&rl, "%s: problem configuring policing",
 +                    netdev_name);
 +            return -1;
 +        }
 +    } else {
 +        snprintf(command, sizeof(command), POLICE_DEL_CMD, netdev_name);
 +        if (system(command) == -1) {
 +            VLOG_WARN_RL(&rl, "%s: problem removing policing", netdev_name);
 +        }
 +    }
 +
 +    return 0;
 +}
 +
 +static int
 +netdev_linux_get_in4(const struct netdev *netdev_,
 +                     struct in_addr *address, struct in_addr *netmask)
 +{
 +    struct netdev_linux *netdev = netdev_linux_cast(netdev_);
 +    if (!(netdev->cache->valid & VALID_IN4)) {
 +        int error;
 +
 +        error = netdev_linux_get_ipv4(netdev_, &netdev->cache->address,
 +                                      SIOCGIFADDR, "SIOCGIFADDR");
 +        if (error) {
 +            return error;
 +        }
 +
 +        error = netdev_linux_get_ipv4(netdev_, &netdev->cache->netmask,
 +                                      SIOCGIFNETMASK, "SIOCGIFNETMASK");
 +        if (error) {
 +            return error;
 +        }
 +
 +        netdev->cache->valid |= VALID_IN4;
 +    }
 +    *address = netdev->cache->address;
 +    *netmask = netdev->cache->netmask;
 +    return address->s_addr == INADDR_ANY ? EADDRNOTAVAIL : 0;
 +}
 +
 +static int
 +netdev_linux_set_in4(struct netdev *netdev_, struct in_addr address,
 +                     struct in_addr netmask)
 +{
 +    struct netdev_linux *netdev = netdev_linux_cast(netdev_);
 +    int error;
 +
 +    error = do_set_addr(netdev_, SIOCSIFADDR, "SIOCSIFADDR", address);
 +    if (!error) {
 +        netdev->cache->valid |= VALID_IN4;
 +        netdev->cache->address = address;
 +        netdev->cache->netmask = netmask;
 +        if (address.s_addr != INADDR_ANY) {
 +            error = do_set_addr(netdev_, SIOCSIFNETMASK,
 +                                "SIOCSIFNETMASK", netmask);
 +        }
 +    }
 +    return error;
 +}
 +
 +static bool
 +parse_if_inet6_line(const char *line,
 +                    struct in6_addr *in6, char ifname[16 + 1])
 +{
 +    uint8_t *s6 = in6->s6_addr;
 +#define X8 "%2"SCNx8
 +    return sscanf(line,
 +                  " "X8 X8 X8 X8 X8 X8 X8 X8 X8 X8 X8 X8 X8 X8 X8 X8
 +                  "%*x %*x %*x %*x %16s\n",
 +                  &s6[0], &s6[1], &s6[2], &s6[3],
 +                  &s6[4], &s6[5], &s6[6], &s6[7],
 +                  &s6[8], &s6[9], &s6[10], &s6[11],
 +                  &s6[12], &s6[13], &s6[14], &s6[15],
 +                  ifname) == 17;
 +}
 +
 +/* If 'netdev' has an assigned IPv6 address, sets '*in6' to that address (if
 + * 'in6' is non-null) and returns true.  Otherwise, returns false. */
 +static int
 +netdev_linux_get_in6(const struct netdev *netdev_, struct in6_addr *in6)
 +{
 +    struct netdev_linux *netdev = netdev_linux_cast(netdev_);
 +    if (!(netdev->cache->valid & VALID_IN6)) {
 +        FILE *file;
 +        char line[128];
 +
 +        netdev->cache->in6 = in6addr_any;
 +
 +        file = fopen("/proc/net/if_inet6", "r");
 +        if (file != NULL) {
 +            const char *name = netdev_get_name(netdev_);
 +            while (fgets(line, sizeof line, file)) {
 +                struct in6_addr in6;
 +                char ifname[16 + 1];
 +                if (parse_if_inet6_line(line, &in6, ifname)
 +                    && !strcmp(name, ifname))
 +                {
 +                    netdev->cache->in6 = in6;
 +                    break;
 +                }
 +            }
 +            fclose(file);
 +        }
 +        netdev->cache->valid |= VALID_IN6;
 +    }
 +    *in6 = netdev->cache->in6;
 +    return 0;
 +}
 +
 +static void
 +make_in4_sockaddr(struct sockaddr *sa, struct in_addr addr)
 +{
 +    struct sockaddr_in sin;
 +    memset(&sin, 0, sizeof sin);
 +    sin.sin_family = AF_INET;
 +    sin.sin_addr = addr;
 +    sin.sin_port = 0;
 +
 +    memset(sa, 0, sizeof *sa);
 +    memcpy(sa, &sin, sizeof sin);
 +}
 +
 +static int
 +do_set_addr(struct netdev *netdev,
 +            int ioctl_nr, const char *ioctl_name, struct in_addr addr)
 +{
 +    struct ifreq ifr;
 +    strncpy(ifr.ifr_name, netdev->name, sizeof ifr.ifr_name);
 +    make_in4_sockaddr(&ifr.ifr_addr, addr);
 +    return netdev_linux_do_ioctl(netdev, &ifr, ioctl_nr, ioctl_name);
 +}
 +
 +/* Adds 'router' as a default IP gateway. */
 +static int
 +netdev_linux_add_router(struct netdev *netdev UNUSED, struct in_addr router)
 +{
 +    struct in_addr any = { INADDR_ANY };
 +    struct rtentry rt;
 +    int error;
 +
 +    memset(&rt, 0, sizeof rt);
 +    make_in4_sockaddr(&rt.rt_dst, any);
 +    make_in4_sockaddr(&rt.rt_gateway, router);
 +    make_in4_sockaddr(&rt.rt_genmask, any);
 +    rt.rt_flags = RTF_UP | RTF_GATEWAY;
 +    COVERAGE_INC(netdev_add_router);
 +    error = ioctl(af_inet_sock, SIOCADDRT, &rt) < 0 ? errno : 0;
 +    if (error) {
 +        VLOG_WARN("ioctl(SIOCADDRT): %s", strerror(error));
 +    }
 +    return error;
 +}
 +
 +static int
 +netdev_linux_get_next_hop(const struct in_addr *host, struct in_addr *next_hop,
 +                          char **netdev_name)
 +{
 +    static const char fn[] = "/proc/net/route";
 +    FILE *stream;
 +    char line[256];
 +    int ln;
 +
 +    *netdev_name = NULL;
 +    stream = fopen(fn, "r");
 +    if (stream == NULL) {
 +        VLOG_WARN_RL(&rl, "%s: open failed: %s", fn, strerror(errno));
 +        return errno;
 +    }
 +
 +    ln = 0;
 +    while (fgets(line, sizeof line, stream)) {
 +        if (++ln >= 2) {
 +            char iface[17];
 +            uint32_t dest, gateway, mask;
 +            int refcnt, metric, mtu;
 +            unsigned int flags, use, window, irtt;
 +
 +            if (sscanf(line,
 +                       "%16s %"SCNx32" %"SCNx32" %04X %d %u %d %"SCNx32
 +                       " %d %u %u\n",
 +                       iface, &dest, &gateway, &flags, &refcnt,
 +                       &use, &metric, &mask, &mtu, &window, &irtt) != 11) {
 +
 +                VLOG_WARN_RL(&rl, "%s: could not parse line %d: %s", 
 +                        fn, ln, line);
 +                continue;
 +            }
 +            if (!(flags & RTF_UP)) {
 +                /* Skip routes that aren't up. */
 +                continue;
 +            }
 +
 +            /* The output of 'dest', 'mask', and 'gateway' were given in
 +             * network byte order, so we don't need need any endian 
 +             * conversions here. */
 +            if ((dest & mask) == (host->s_addr & mask)) {
 +                if (!gateway) {
 +                    /* The host is directly reachable. */
 +                    next_hop->s_addr = 0;
 +                } else {
 +                    /* To reach the host, we must go through a gateway. */
 +                    next_hop->s_addr = gateway;
 +                }
 +                *netdev_name = xstrdup(iface);
 +                fclose(stream);
 +                return 0;
 +            }
 +        }
 +    }
 +
 +    fclose(stream);
 +    return ENXIO;
 +}
 +
 +/* Looks up the ARP table entry for 'ip' on 'netdev'.  If one exists and can be
 + * successfully retrieved, it stores the corresponding MAC address in 'mac' and
 + * returns 0.  Otherwise, it returns a positive errno value; in particular,
 + * ENXIO indicates that there is not ARP table entry for 'ip' on 'netdev'. */
 +static int
 +netdev_linux_arp_lookup(const struct netdev *netdev,
 +                        uint32_t ip, uint8_t mac[ETH_ADDR_LEN])
 +{
 +    struct arpreq r;
 +    struct sockaddr_in *pa;
 +    int retval;
 +
 +    memset(&r, 0, sizeof r);
 +    pa = (struct sockaddr_in *) &r.arp_pa;
 +    pa->sin_family = AF_INET;
 +    pa->sin_addr.s_addr = ip;
 +    pa->sin_port = 0;
 +    r.arp_ha.sa_family = ARPHRD_ETHER;
 +    r.arp_flags = 0;
 +    strncpy(r.arp_dev, netdev->name, sizeof r.arp_dev);
 +    COVERAGE_INC(netdev_arp_lookup);
 +    retval = ioctl(af_inet_sock, SIOCGARP, &r) < 0 ? errno : 0;
 +    if (!retval) {
 +        memcpy(mac, r.arp_ha.sa_data, ETH_ADDR_LEN);
 +    } else if (retval != ENXIO) {
 +        VLOG_WARN_RL(&rl, "%s: could not look up ARP entry for "IP_FMT": %s",
 +                     netdev->name, IP_ARGS(&ip), strerror(retval));
 +    }
 +    return retval;
 +}
 +
 +static int
 +nd_to_iff_flags(enum netdev_flags nd)
 +{
 +    int iff = 0;
 +    if (nd & NETDEV_UP) {
 +        iff |= IFF_UP;
 +    }
 +    if (nd & NETDEV_PROMISC) {
 +        iff |= IFF_PROMISC;
 +    }
 +    return iff;
 +}
 +
 +static int
 +iff_to_nd_flags(int iff)
 +{
 +    enum netdev_flags nd = 0;
 +    if (iff & IFF_UP) {
 +        nd |= NETDEV_UP;
 +    }
 +    if (iff & IFF_PROMISC) {
 +        nd |= NETDEV_PROMISC;
 +    }
 +    return nd;
 +}
 +
 +static int
 +netdev_linux_update_flags(struct netdev *netdev, enum netdev_flags off,
 +                          enum netdev_flags on, enum netdev_flags *old_flagsp)
 +{
 +    int old_flags, new_flags;
 +    int error;
 +
 +    error = get_flags(netdev, &old_flags);
 +    if (!error) {
 +        *old_flagsp = iff_to_nd_flags(old_flags);
 +        new_flags = (old_flags & ~nd_to_iff_flags(off)) | nd_to_iff_flags(on);
 +        if (new_flags != old_flags) {
 +            error = set_flags(netdev, new_flags);
 +        }
 +    }
 +    return error;
 +}
 +
 +static void
 +poll_notify(struct list *list)
 +{
 +    struct netdev_linux_notifier *notifier;
 +    LIST_FOR_EACH (notifier, struct netdev_linux_notifier, node, list) {
 +        struct netdev_notifier *n = &notifier->notifier;
 +        n->cb(n);
 +    }
 +}
 +
 +static void
 +netdev_linux_poll_cb(const struct rtnetlink_change *change,
 +                     void *aux UNUSED)
 +{
 +    if (change) {
 +        struct list *list = shash_find_data(&netdev_linux_notifiers,
 +                                            change->ifname);
 +        if (list) {
 +            poll_notify(list);
 +        }
 +    } else {
 +        struct shash_node *node;
 +        SHASH_FOR_EACH (node, &netdev_linux_notifiers) {
 +            poll_notify(node->data);
 +        }
 +    }
 +}
 +
 +static int
 +netdev_linux_poll_add(struct netdev *netdev,
 +                      void (*cb)(struct netdev_notifier *), void *aux,
 +                      struct netdev_notifier **notifierp)
 +{
 +    const char *netdev_name = netdev_get_name(netdev);
 +    struct netdev_linux_notifier *notifier;
 +    struct list *list;
 +
 +    if (shash_is_empty(&netdev_linux_notifiers)) {
 +        int error = rtnetlink_notifier_register(&netdev_linux_poll_notifier,
 +                                                   netdev_linux_poll_cb, NULL);
 +        if (error) {
 +            return error;
 +        }
 +    }
 +
 +    list = shash_find_data(&netdev_linux_notifiers, netdev_name);
 +    if (!list) {
 +        list = xmalloc(sizeof *list);
 +        list_init(list);
 +        shash_add(&netdev_linux_notifiers, netdev_name, list);
 +    }
 +
 +    notifier = xmalloc(sizeof *notifier);
 +    netdev_notifier_init(&notifier->notifier, netdev, cb, aux);
 +    list_push_back(list, &notifier->node);
 +    *notifierp = &notifier->notifier;
 +    return 0;
 +}
 +
 +static void
 +netdev_linux_poll_remove(struct netdev_notifier *notifier_)
 +{
 +    struct netdev_linux_notifier *notifier =
 +        CONTAINER_OF(notifier_, struct netdev_linux_notifier, notifier);
 +    struct list *list;
 +
 +    /* Remove 'notifier' from its list. */
 +    list = list_remove(&notifier->node);
 +    if (list_is_empty(list)) {
 +        /* The list is now empty.  Remove it from the hash and free it. */
 +        const char *netdev_name = netdev_get_name(notifier->notifier.netdev);
 +        shash_delete(&netdev_linux_notifiers,
 +                     shash_find(&netdev_linux_notifiers, netdev_name));
 +        free(list);
 +    }
 +    free(notifier);
 +
 +    /* If that was the last notifier, unregister. */
 +    if (shash_is_empty(&netdev_linux_notifiers)) {
 +        rtnetlink_notifier_unregister(&netdev_linux_poll_notifier);
 +    }
 +}
 +
 +const struct netdev_class netdev_linux_class = {
 +    "",                         /* prefix */
 +    "linux",                    /* name */
 +
 +    netdev_linux_init,
 +    netdev_linux_run,
 +    netdev_linux_wait,
 +
 +    netdev_linux_open,
 +    netdev_linux_close,
 +
 +    netdev_linux_enumerate,
 +
 +    netdev_linux_recv,
 +    netdev_linux_recv_wait,
 +    netdev_linux_drain,
 +
 +    netdev_linux_send,
 +    netdev_linux_send_wait,
 +
 +    netdev_linux_set_etheraddr,
 +    netdev_linux_get_etheraddr,
 +    netdev_linux_get_mtu,
 +    netdev_linux_get_carrier,
 +    netdev_linux_get_stats,
 +
 +    netdev_linux_get_features,
 +    netdev_linux_set_advertisements,
 +    netdev_linux_get_vlan_vid,
 +    netdev_linux_set_policing,
 +
 +    netdev_linux_get_in4,
 +    netdev_linux_set_in4,
 +    netdev_linux_get_in6,
 +    netdev_linux_add_router,
 +    netdev_linux_get_next_hop,
 +    netdev_linux_arp_lookup,
 +
 +    netdev_linux_update_flags,
 +
 +    netdev_linux_poll_add,
 +    netdev_linux_poll_remove,
 +};
 +
 +const struct netdev_class netdev_tap_class = {
 +    "tap",                      /* prefix */
 +    "tap",                      /* name */
 +
 +    netdev_linux_init,
 +    NULL,                       /* run */
 +    NULL,                       /* wait */
 +
 +    netdev_linux_open,
 +    netdev_linux_close,
 +
 +    netdev_linux_enumerate,
 +
 +    netdev_linux_recv,
 +    netdev_linux_recv_wait,
 +    netdev_linux_drain,
 +
 +    netdev_linux_send,
 +    netdev_linux_send_wait,
 +
 +    netdev_linux_set_etheraddr,
 +    netdev_linux_get_etheraddr,
 +    netdev_linux_get_mtu,
 +    netdev_linux_get_carrier,
 +    netdev_linux_get_stats,
 +
 +    netdev_linux_get_features,
 +    netdev_linux_set_advertisements,
 +    netdev_linux_get_vlan_vid,
 +    netdev_linux_set_policing,
 +
 +    netdev_linux_get_in4,
 +    netdev_linux_set_in4,
 +    netdev_linux_get_in6,
 +    netdev_linux_add_router,
 +    netdev_linux_get_next_hop,
 +    netdev_linux_arp_lookup,
 +
 +    netdev_linux_update_flags,
 +
 +    netdev_linux_poll_add,
 +    netdev_linux_poll_remove,
 +};
 +\f
 +static int
 +get_stats_via_netlink(int ifindex, struct netdev_stats *stats)
 +{
 +    /* Policy for RTNLGRP_LINK messages.
 +     *
 +     * There are *many* more fields in these messages, but currently we only
 +     * care about these fields. */
 +    static const struct nl_policy rtnlgrp_link_policy[] = {
 +        [IFLA_IFNAME] = { .type = NL_A_STRING, .optional = false },
 +        [IFLA_STATS] = { .type = NL_A_UNSPEC, .optional = true,
 +                         .min_len = sizeof(struct rtnl_link_stats) },
 +    };
 +
 +
 +    static struct nl_sock *rtnl_sock;
 +    struct ofpbuf request;
 +    struct ofpbuf *reply;
 +    struct ifinfomsg *ifi;
 +    const struct rtnl_link_stats *rtnl_stats;
 +    struct nlattr *attrs[ARRAY_SIZE(rtnlgrp_link_policy)];
 +    int error;
 +
 +    if (!rtnl_sock) {
 +        error = nl_sock_create(NETLINK_ROUTE, 0, 0, 0, &rtnl_sock);
 +        if (error) {
 +            VLOG_ERR_RL(&rl, "failed to create rtnetlink socket: %s",
 +                        strerror(error));
 +            return error;
 +        }
 +    }
 +
 +    ofpbuf_init(&request, 0);
 +    nl_msg_put_nlmsghdr(&request, rtnl_sock, sizeof *ifi,
 +                        RTM_GETLINK, NLM_F_REQUEST);
 +    ifi = ofpbuf_put_zeros(&request, sizeof *ifi);
 +    ifi->ifi_family = PF_UNSPEC;
 +    ifi->ifi_index = ifindex;
 +    error = nl_sock_transact(rtnl_sock, &request, &reply);
 +    ofpbuf_uninit(&request);
 +    if (error) {
 +        return error;
 +    }
 +
 +    if (!nl_policy_parse(reply, NLMSG_HDRLEN + sizeof(struct ifinfomsg),
 +                         rtnlgrp_link_policy,
 +                         attrs, ARRAY_SIZE(rtnlgrp_link_policy))) {
 +        ofpbuf_delete(reply);
 +        return EPROTO;
 +    }
 +
 +    if (!attrs[IFLA_STATS]) {
 +        VLOG_WARN_RL(&rl, "RTM_GETLINK reply lacks stats");
++        ofpbuf_delete(reply);
 +        return EPROTO;
 +    }
 +
 +    rtnl_stats = nl_attr_get(attrs[IFLA_STATS]);
 +    stats->rx_packets = rtnl_stats->rx_packets;
 +    stats->tx_packets = rtnl_stats->tx_packets;
 +    stats->rx_bytes = rtnl_stats->rx_bytes;
 +    stats->tx_bytes = rtnl_stats->tx_bytes;
 +    stats->rx_errors = rtnl_stats->rx_errors;
 +    stats->tx_errors = rtnl_stats->tx_errors;
 +    stats->rx_dropped = rtnl_stats->rx_dropped;
 +    stats->tx_dropped = rtnl_stats->tx_dropped;
 +    stats->multicast = rtnl_stats->multicast;
 +    stats->collisions = rtnl_stats->collisions;
 +    stats->rx_length_errors = rtnl_stats->rx_length_errors;
 +    stats->rx_over_errors = rtnl_stats->rx_over_errors;
 +    stats->rx_crc_errors = rtnl_stats->rx_crc_errors;
 +    stats->rx_frame_errors = rtnl_stats->rx_frame_errors;
 +    stats->rx_fifo_errors = rtnl_stats->rx_fifo_errors;
 +    stats->rx_missed_errors = rtnl_stats->rx_missed_errors;
 +    stats->tx_aborted_errors = rtnl_stats->tx_aborted_errors;
 +    stats->tx_carrier_errors = rtnl_stats->tx_carrier_errors;
 +    stats->tx_fifo_errors = rtnl_stats->tx_fifo_errors;
 +    stats->tx_heartbeat_errors = rtnl_stats->tx_heartbeat_errors;
 +    stats->tx_window_errors = rtnl_stats->tx_window_errors;
 +
++    ofpbuf_delete(reply);
++
 +    return 0;
 +}
 +
 +static int
 +get_stats_via_proc(const char *netdev_name, struct netdev_stats *stats)
 +{
 +    static const char fn[] = "/proc/net/dev";
 +    char line[1024];
 +    FILE *stream;
 +    int ln;
 +
 +    stream = fopen(fn, "r");
 +    if (!stream) {
 +        VLOG_WARN_RL(&rl, "%s: open failed: %s", fn, strerror(errno));
 +        return errno;
 +    }
 +
 +    ln = 0;
 +    while (fgets(line, sizeof line, stream)) {
 +        if (++ln >= 3) {
 +            char devname[16];
 +#define X64 "%"SCNu64
 +            if (sscanf(line,
 +                       " %15[^:]:"
 +                       X64 X64 X64 X64 X64 X64 X64 "%*u"
 +                       X64 X64 X64 X64 X64 X64 X64 "%*u",
 +                       devname,
 +                       &stats->rx_bytes,
 +                       &stats->rx_packets,
 +                       &stats->rx_errors,
 +                       &stats->rx_dropped,
 +                       &stats->rx_fifo_errors,
 +                       &stats->rx_frame_errors,
 +                       &stats->multicast,
 +                       &stats->tx_bytes,
 +                       &stats->tx_packets,
 +                       &stats->tx_errors,
 +                       &stats->tx_dropped,
 +                       &stats->tx_fifo_errors,
 +                       &stats->collisions,
 +                       &stats->tx_carrier_errors) != 15) {
 +                VLOG_WARN_RL(&rl, "%s:%d: parse error", fn, ln);
 +            } else if (!strcmp(devname, netdev_name)) {
 +                stats->rx_length_errors = UINT64_MAX;
 +                stats->rx_over_errors = UINT64_MAX;
 +                stats->rx_crc_errors = UINT64_MAX;
 +                stats->rx_missed_errors = UINT64_MAX;
 +                stats->tx_aborted_errors = UINT64_MAX;
 +                stats->tx_heartbeat_errors = UINT64_MAX;
 +                stats->tx_window_errors = UINT64_MAX;
 +                fclose(stream);
 +                return 0;
 +            }
 +        }
 +    }
 +    VLOG_WARN_RL(&rl, "%s: no stats for %s", fn, netdev_name);
 +    fclose(stream);
 +    return ENODEV;
 +}
 +\f
 +static int
 +get_flags(const struct netdev *netdev, int *flags)
 +{
 +    struct ifreq ifr;
 +    int error;
 +
 +    error = netdev_linux_do_ioctl(netdev, &ifr, SIOCGIFFLAGS, "SIOCGIFFLAGS");
 +    *flags = ifr.ifr_flags;
 +    return error;
 +}
 +
 +static int
 +set_flags(struct netdev *netdev, int flags)
 +{
 +    struct ifreq ifr;
 +
 +    ifr.ifr_flags = flags;
 +    return netdev_linux_do_ioctl(netdev, &ifr, SIOCSIFFLAGS, "SIOCSIFFLAGS");
 +}
 +
 +static int
 +do_get_ifindex(const char *netdev_name)
 +{
 +    struct ifreq ifr;
 +
 +    strncpy(ifr.ifr_name, netdev_name, sizeof ifr.ifr_name);
 +    COVERAGE_INC(netdev_get_ifindex);
 +    if (ioctl(af_inet_sock, SIOCGIFINDEX, &ifr) < 0) {
 +        VLOG_WARN_RL(&rl, "ioctl(SIOCGIFINDEX) on %s device failed: %s",
 +                     netdev_name, strerror(errno));
 +        return -errno;
 +    }
 +    return ifr.ifr_ifindex;
 +}
 +
 +static int
 +get_ifindex(const struct netdev *netdev_, int *ifindexp)
 +{
 +    struct netdev_linux *netdev = netdev_linux_cast(netdev_);
 +    *ifindexp = 0;
 +    if (!(netdev->cache->valid & VALID_IFINDEX)) {
 +        int ifindex = do_get_ifindex(netdev_get_name(netdev_));
 +        if (ifindex < 0) {
 +            return -ifindex;
 +        }
 +        netdev->cache->valid |= VALID_IFINDEX;
 +        netdev->cache->ifindex = ifindex;
 +    }
 +    *ifindexp = netdev->cache->ifindex;
 +    return 0;
 +}
 +
 +static int
 +get_etheraddr(const char *netdev_name, uint8_t ea[ETH_ADDR_LEN])
 +{
 +    struct ifreq ifr;
 +    int hwaddr_family;
 +
 +    memset(&ifr, 0, sizeof ifr);
 +    strncpy(ifr.ifr_name, netdev_name, sizeof ifr.ifr_name);
 +    COVERAGE_INC(netdev_get_hwaddr);
 +    if (ioctl(af_inet_sock, SIOCGIFHWADDR, &ifr) < 0) {
 +        VLOG_ERR("ioctl(SIOCGIFHWADDR) on %s device failed: %s",
 +                 netdev_name, strerror(errno));
 +        return errno;
 +    }
 +    hwaddr_family = ifr.ifr_hwaddr.sa_family;
 +    if (hwaddr_family != AF_UNSPEC && hwaddr_family != ARPHRD_ETHER) {
 +        VLOG_WARN("%s device has unknown hardware address family %d",
 +                  netdev_name, hwaddr_family);
 +    }
 +    memcpy(ea, ifr.ifr_hwaddr.sa_data, ETH_ADDR_LEN);
 +    return 0;
 +}
 +
 +static int
 +set_etheraddr(const char *netdev_name, int hwaddr_family,
 +              const uint8_t mac[ETH_ADDR_LEN])
 +{
 +    struct ifreq ifr;
 +
 +    memset(&ifr, 0, sizeof ifr);
 +    strncpy(ifr.ifr_name, netdev_name, sizeof ifr.ifr_name);
 +    ifr.ifr_hwaddr.sa_family = hwaddr_family;
 +    memcpy(ifr.ifr_hwaddr.sa_data, mac, ETH_ADDR_LEN);
 +    COVERAGE_INC(netdev_set_hwaddr);
 +    if (ioctl(af_inet_sock, SIOCSIFHWADDR, &ifr) < 0) {
 +        VLOG_ERR("ioctl(SIOCSIFHWADDR) on %s device failed: %s",
 +                 netdev_name, strerror(errno));
 +        return errno;
 +    }
 +    return 0;
 +}
 +
 +static int
 +netdev_linux_do_ethtool(struct netdev *netdev, struct ethtool_cmd *ecmd,
 +                        int cmd, const char *cmd_name)
 +{
 +    struct ifreq ifr;
 +
 +    memset(&ifr, 0, sizeof ifr);
 +    strncpy(ifr.ifr_name, netdev->name, sizeof ifr.ifr_name);
 +    ifr.ifr_data = (caddr_t) ecmd;
 +
 +    ecmd->cmd = cmd;
 +    COVERAGE_INC(netdev_ethtool);
 +    if (ioctl(af_inet_sock, SIOCETHTOOL, &ifr) == 0) {
 +        return 0;
 +    } else {
 +        if (errno != EOPNOTSUPP) {
 +            VLOG_WARN_RL(&rl, "ethtool command %s on network device %s "
 +                         "failed: %s", cmd_name, netdev->name,
 +                         strerror(errno));
 +        } else {
 +            /* The device doesn't support this operation.  That's pretty
 +             * common, so there's no point in logging anything. */
 +        }
 +        return errno;
 +    }
 +}
 +
 +static int
 +netdev_linux_do_ioctl(const struct netdev *netdev, struct ifreq *ifr,
 +                      int cmd, const char *cmd_name)
 +{
 +    strncpy(ifr->ifr_name, netdev_get_name(netdev), sizeof ifr->ifr_name);
 +    if (ioctl(af_inet_sock, cmd, ifr) == -1) {
 +        VLOG_DBG_RL(&rl, "%s: ioctl(%s) failed: %s",
 +                    netdev_get_name(netdev), cmd_name, strerror(errno));
 +        return errno;
 +    }
 +    return 0;
 +}
 +
 +static int
 +netdev_linux_get_ipv4(const struct netdev *netdev, struct in_addr *ip,
 +                      int cmd, const char *cmd_name)
 +{
 +    struct ifreq ifr;
 +    int error;
 +
 +    ifr.ifr_addr.sa_family = AF_INET;
 +    error = netdev_linux_do_ioctl(netdev, &ifr, cmd, cmd_name);
 +    if (!error) {
 +        const struct sockaddr_in *sin = (struct sockaddr_in *) &ifr.ifr_addr;
 +        *ip = sin->sin_addr;
 +    }
 +    return error;
 +}
diff --combined lib/rconn.c
@@@ -89,7 -89,7 +89,7 @@@ struct rconn 
      time_t last_admitted;
  
      /* These values are simply for statistics reporting, not used directly by
 -     * anything internal to the rconn (or the secchan for that matter). */
 +     * anything internal to the rconn (or ofproto for that matter). */
      unsigned int packets_received;
      unsigned int n_attempted_connections, n_successful_connections;
      time_t creation_time;
@@@ -499,7 -499,7 +499,7 @@@ rconn_recv(struct rconn *rc
          int error = vconn_recv(rc->vconn, &buffer);
          if (!error) {
              copy_to_monitor(rc, buffer);
-             if (is_admitted_msg(buffer)
+             if (rc->probably_admitted || is_admitted_msg(buffer)
                  || time_now() - rc->last_connected >= 30) {
                  rc->probably_admitted = true;
                  rc->last_admitted = time_now();
@@@ -637,15 -637,22 +637,22 @@@ rconn_is_connected(const struct rconn *
      return is_connected_state(rconn->state);
  }
  
- /* Returns 0 if 'rconn' is connected.  Otherwise, if 'rconn' is in a "failure
-  * mode" (that is, it is not connected), returns the number of seconds that it
-  * has been in failure mode, ignoring any times that it connected but the
-  * controller's admission control policy caused it to be quickly
-  * disconnected. */
+ /* Returns true if 'rconn' is connected and thought to have been accepted by
+  * the peer's admission-control policy. */
+ bool
+ rconn_is_admitted(const struct rconn *rconn)
+ {
+     return (rconn_is_connected(rconn)
+             && rconn->last_admitted >= rconn->last_connected);
+ }
+ /* Returns 0 if 'rconn' is currently connected and considered to have been
+  * accepted by the peer's admission-control policy, otherwise the number of
+  * seconds since 'rconn' was last in such a state. */
  int
  rconn_failure_duration(const struct rconn *rconn)
  {
-     return rconn_is_connected(rconn) ? 0 : time_now() - rconn->last_admitted;
+     return rconn_is_admitted(rconn) ? 0 : time_now() - rconn->last_admitted;
  }
  
  /* Returns the IP address of the peer, or 0 if the peer's IP address is not
diff --combined lib/vconn.c
@@@ -139,12 -139,12 +139,12 @@@ vconn_usage(bool active, bool passive, 
  
      if (passive) {
          printf("Passive OpenFlow connection methods:\n");
 -        printf("  ptcp:[PORT]             "
 -               "listen to TCP PORT (default: %d)\n",
 +        printf("  ptcp:[PORT][:IP]        "
 +               "listen to TCP PORT (default: %d) on IP\n",
                 OFP_TCP_PORT);
  #ifdef HAVE_OPENSSL
 -        printf("  pssl:[PORT]             "
 -               "listen for SSL on PORT (default: %d)\n",
 +        printf("  pssl:[PORT][:IP]        "
 +               "listen for SSL on PORT (default: %d) on IP\n",
                 OFP_SSL_PORT);
  #endif
          printf("  punix:FILE              "
@@@ -364,7 -364,7 +364,7 @@@ vcs_recv_hello(struct vconn *vconn
  
      if (retval != EAGAIN) {
          vconn->state = VCS_DISCONNECTED;
 -        vconn->error = retval;
 +        vconn->error = retval == EOF ? ECONNRESET : retval;
      }
  }
  
@@@ -458,7 -458,10 +458,7 @@@ vconn_recv(struct vconn *vconn, struct 
  static int
  do_recv(struct vconn *vconn, struct ofpbuf **msgp)
  {
 -    int retval;
 -
 -again:
 -    retval = (vconn->class->recv)(vconn, msgp);
 +    int retval = (vconn->class->recv)(vconn, msgp);
      if (!retval) {
          struct ofp_header *oh;
  
              && oh->type != OFPT_VENDOR)
          {
              if (vconn->version < 0) {
 -                if (oh->type == OFPT_PACKET_IN
 -                    || oh->type == OFPT_FLOW_EXPIRED
 -                    || oh->type == OFPT_PORT_STATUS) {
 -                    /* The kernel datapath is stateless and doesn't really
 -                     * support version negotiation, so it can end up sending
 -                     * these asynchronous message before version negotiation
 -                     * is complete.  Just ignore them.
 -                     *
 -                     * (After we move OFPT_PORT_STATUS messages from the kernel
 -                     * into secchan, we won't get those here, since secchan
 -                     * does proper version negotiation.) */
 -                    ofpbuf_delete(*msgp);
 -                    goto again;
 -                }
                  VLOG_ERR_RL(&bad_ofmsg_rl,
                              "%s: received OpenFlow message type %"PRIu8" "
                              "before version negotiation complete",
@@@ -911,6 -928,28 +911,28 @@@ make_add_simple_flow(const flow_t *flow
      return buffer;
  }
  
+ struct ofpbuf *
+ make_packet_in(uint32_t buffer_id, uint16_t in_port, uint8_t reason,
+                const struct ofpbuf *payload, int max_send_len)
+ {
+     struct ofp_packet_in *opi;
+     struct ofpbuf *buf;
+     int send_len;
+     send_len = MIN(max_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(in_port);
+     opi->reason = reason;
+     ofpbuf_put(buf, payload->data, send_len);
+     update_openflow_length(buf);
+     return buf;
+ }
  struct ofpbuf *
  make_packet_out(const struct ofpbuf *packet, uint32_t buffer_id,
                  uint16_t in_port,
@@@ -1388,26 -1427,9 +1410,26 @@@ normalize_match(struct ofp_match *m
      m->wildcards = htonl(wc);
  }
  
 +/* Initializes 'vconn' as a new vconn named 'name', implemented via 'class'.
 + * The initial connection status, supplied as 'connect_status', is interpreted
 + * as follows:
 + *
 + *      - 0: 'vconn' is connected.  Its 'send' and 'recv' functions may be
 + *        called in the normal fashion.
 + *
 + *      - EAGAIN: 'vconn' is trying to complete a connection.  Its 'connect'
 + *        function should be called to complete the connection.
 + *
 + *      - Other positive errno values indicate that the connection failed with
 + *        the specified error.
 + *
 + * After calling this function, vconn_close() must be used to destroy 'vconn',
 + * otherwise resources will be leaked.
 + *
 + * The caller retains ownership of 'name'. */
  void
  vconn_init(struct vconn *vconn, struct vconn_class *class, int connect_status,
 -           const char *name, bool reconnectable)
 +           const char *name)
  {
      vconn->class = class;
      vconn->state = (connect_status == EAGAIN ? VCS_CONNECTING
      vconn->local_ip = 0;
      vconn->local_port = 0;
      vconn->name = xstrdup(name);
 -    vconn->reconnectable = reconnectable;
  }
  
  void
diff --combined ofproto/fail-open.c
  #include "flow.h"
  #include "mac-learning.h"
  #include "odp-util.h"
+ #include "ofpbuf.h"
  #include "ofproto.h"
+ #include "pktbuf.h"
+ #include "poll-loop.h"
  #include "rconn.h"
  #include "status.h"
  #include "timeval.h"
+ #include "vconn.h"
  
  #define THIS_MODULE VLM_fail_open
  #include "vlog.h"
  
+ /*
+  * Fail-open mode.
+  *
+  * In fail-open mode, the switch detects when the controller cannot be
+  * contacted or when the controller is dropping switch connections because the
+  * switch does not pass its admission control policy.  In those situations the
+  * switch sets up flows itself using the "normal" action.
+  *
+  * There is a little subtlety to implementation, to properly handle the case
+  * where the controller allows switch connections but drops them a few seconds
+  * later for admission control reasons.  Because of this case, we don't want to
+  * just stop setting up flows when we connect to the controller: if we did,
+  * then new flow setup and existing flows would stop during the duration of
+  * connection to the controller, and thus the whole network would go down for
+  * that period of time.
+  *
+  * So, instead, we add some special caseswhen we are connected to a controller,
+  * but not yet sure that it has admitted us:
+  *
+  *     - We set up flows immediately ourselves, but simultaneously send out an
+  *       OFPT_PACKET_IN to the controller.  We put a special bogus buffer-id in
+  *       these OFPT_PACKET_IN messages so that duplicate packets don't get sent
+  *       out to the network when the controller replies.
+  *
+  *     - We also send out OFPT_PACKET_IN messages for totally bogus packets
+  *       every so often, in case no real new flows are arriving in the network.
+  *
+  *     - We don't flush the flow table at the time we connect, because this
+  *       could cause network stuttering in a switch with lots of flows or very
+  *       high-bandwidth flows by suddenly throwing lots of packets down to
+  *       userspace.
+  */
  struct fail_open {
      struct ofproto *ofproto;
      struct rconn *controller;
      int trigger_duration;
      int last_disconn_secs;
      struct status_category *ss_cat;
+     long long int next_bogus_packet_in;
+     struct rconn_packet_counter *bogus_packet_counter;
  };
  
- /* Causes the switch to enter or leave fail-open mode, if appropriate. */
- void
fail_open_run(struct fail_open *fo)
+ /* Returns true if 'fo' should be in fail-open mode, otherwise false. */
+ static inline bool
should_fail_open(const 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;
+     return rconn_failure_duration(fo->controller) >= fo->trigger_duration;
+ }
+ /* Returns true if 'fo' is currently in fail-open mode, otherwise false. */
+ bool
+ fail_open_is_active(const struct fail_open *fo)
+ {
+     return fo->last_disconn_secs != 0;
+ }
  
-             VLOG_WARN("No longer in fail-open mode");
-             fo->last_disconn_secs = 0;
+ static void
+ send_bogus_packet_in(struct fail_open *fo)
+ {
+     uint8_t mac[ETH_ADDR_LEN];
+     struct ofpbuf *opi;
+     struct ofpbuf b;
  
-             memset(&flow, 0, sizeof flow);
-             ofproto_delete_flow(fo->ofproto, &flow, OFPFW_ALL, 70000);
-         } else {
+     /* Compose ofp_packet_in. */
+     ofpbuf_init(&b, 128);
+     eth_addr_random(mac);
+     compose_benign_packet(&b, "Open vSwitch Controller Probe", 0xa033, mac);
+     opi = make_packet_in(pktbuf_get_null(), OFPP_LOCAL, OFPR_NO_MATCH, &b, 64);
+     ofpbuf_uninit(&b);
+     /* Send. */
+     rconn_send_with_limit(fo->controller, opi, fo->bogus_packet_counter, 1);
+ }
+ /* Enter fail-open mode if we should be in it.  Handle reconnecting to a
+  * controller from fail-open mode. */
+ void
+ fail_open_run(struct fail_open *fo)
+ {
+     /* Enter fail-open mode if 'fo' is not in it but should be.  */
+     if (should_fail_open(fo)) {
+         int disconn_secs = rconn_failure_duration(fo->controller);
+         if (!fail_open_is_active(fo)) {
              VLOG_WARN("Could not connect to controller (or switch failed "
                        "controller's post-connection admission control "
                        "policy) for %d seconds, failing open", disconn_secs);
               * fail-open rule from fail_open_flushed() when
               * ofproto_flush_flows() calls back to us. */
              ofproto_flush_flows(fo->ofproto);
+         } else if (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;
          }
-     } 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;
      }
+     /* Schedule a bogus packet-in if we're connected and in fail-open. */
+     if (fail_open_is_active(fo)) {
+         if (rconn_is_connected(fo->controller)) {
+             bool expired = time_msec() >= fo->next_bogus_packet_in;
+             if (expired) {
+                 send_bogus_packet_in(fo);
+             }
+             if (expired || fo->next_bogus_packet_in == LLONG_MAX) {
+                 fo->next_bogus_packet_in = time_msec() + 2000;
+             }
+         } else {
+             fo->next_bogus_packet_in = LLONG_MAX;
+         }
+     }
  }
  
+ /* If 'fo' is currently in fail-open mode and its rconn has connected to the
+  * controller, exits fail open mode. */
  void
- fail_open_wait(struct fail_open *fo UNUSED)
+ fail_open_maybe_recover(struct fail_open *fo)
  {
-     /* Nothing to do. */
+     if (fail_open_is_active(fo) && rconn_is_admitted(fo->controller)) {
+         flow_t flow;
+         VLOG_WARN("No longer in fail-open mode");
+         fo->last_disconn_secs = 0;
+         fo->next_bogus_packet_in = LLONG_MAX;
+         memset(&flow, 0, sizeof flow);
+         ofproto_delete_flow(fo->ofproto, &flow, OFPFW_ALL, FAIL_OPEN_PRIORITY);
+     }
+ }
+ void
+ fail_open_wait(struct fail_open *fo)
+ {
+     if (fo->next_bogus_packet_in != LLONG_MAX) {
+         poll_timer_wait(fo->next_bogus_packet_in - time_msec());
+     }
  }
  
  void
@@@ -92,7 -191,7 +191,7 @@@ fail_open_flushed(struct fail_open *fo
          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,
+         ofproto_add_flow(fo->ofproto, &flow, OFPFW_ALL, FAIL_OPEN_PRIORITY,
                           &action, 1, 0);
      }
  }
@@@ -121,6 -220,8 +220,8 @@@ fail_open_create(struct ofproto *ofprot
      fo->last_disconn_secs = 0;
      fo->ss_cat = switch_status_register(switch_status, "fail-open",
                                          fail_open_status_cb, fo);
+     fo->next_bogus_packet_in = LLONG_MAX;
+     fo->bogus_packet_counter = rconn_packet_counter_create();
      return fo;
  }
  
@@@ -136,6 -237,7 +237,7 @@@ fail_open_destroy(struct fail_open *fo
      if (fo) {
          /* We don't own fo->controller. */
          switch_status_unregister(fo->ss_cat);
+         rconn_packet_counter_destroy(fo->bogus_packet_counter);
          free(fo);
      }
  }
diff --combined ofproto/fail-open.h
@@@ -26,13 -26,21 +26,21 @@@ struct ofproto
  struct rconn;
  struct switch_status;
  
+ /* Priority of the rule added by the fail-open subsystem when a switch enters
+  * fail-open mode.  This priority value uniquely identifies a fail-open flow
+  * (OpenFlow priorities max out at 65535 and nothing else in Open vSwitch
+  * creates flows with this priority). */
+ #define FAIL_OPEN_PRIORITY 70000
  struct fail_open *fail_open_create(struct ofproto *, int trigger_duration,
                                     struct switch_status *,
                                     struct rconn *controller);
  void fail_open_set_trigger_duration(struct fail_open *, int trigger_duration);
  void fail_open_destroy(struct fail_open *);
  void fail_open_wait(struct fail_open *);
+ bool fail_open_is_active(const struct fail_open *);
  void fail_open_run(struct fail_open *);
+ void fail_open_maybe_recover(struct fail_open *);
  void fail_open_flushed(struct fail_open *);
  
  #endif /* fail-open.h */
diff --combined ofproto/in-band.c
  #define THIS_MODULE VLM_in_band
  #include "vlog.h"
  
+ /* In-band control allows a single network to be used for OpenFlow
+  * traffic and other data traffic.  Refer to ovs-vswitchd.conf(5) and 
+  * secchan(8) for a description of configuring in-band control.
+  *
+  * This comment is an attempt to describe how in-band control works at a
+  * wire- and implementation-level.  Correctly implementing in-band
+  * control has proven difficult due to its many subtleties, and has thus
+  * gone through many iterations.  Please read through and understand the
+  * reasoning behind the chosen rules before making modifications.
+  *
+  * In Open vSwitch, in-band control is implemented as "hidden" flows (in
+  * that they are not visible through OpenFlow) and at a higher priority
+  * than wildcarded flows can be setup by the controller.  This is done 
+  * so that the controller cannot interfere with them and possibly break 
+  * connectivity with its switches.  It is possible to see all flows, 
+  * including in-band ones, with the ovs-appctl "bridge/dump-flows" 
+  * command.
+  *
+  * The following rules are always enabled with the "normal" action by a 
+  * switch with in-band control:
+  *
+  *    a. DHCP requests sent from the local port.
+  *    b. ARP replies to the local port's MAC address.
+  *    c. ARP requests from the local port's MAC address.
+  *    d. ARP replies to the remote side's MAC address.  Note that the 
+  *       remote side is either the controller or the gateway to reach 
+  *       the controller.
+  *    e. ARP requests from the remote side's MAC address.  Note that
+  *       like (d), the MAC is either for the controller or gateway.
+  *    f. ARP replies containing the controller's IP address as a target.
+  *    g. ARP requests containing the controller's IP address as a source.
+  *    h. OpenFlow (6633/tcp) traffic to the controller's IP.
+  *    i. OpenFlow (6633/tcp) traffic from the controller's IP.
+  *
+  * The goal of these rules is to be as narrow as possible to allow a
+  * switch to join a network and be able to communicate with a
+  * controller.  As mentioned earlier, these rules have higher priority
+  * than the controller's rules, so if they are too broad, they may 
+  * prevent the controller from implementing its policy.  As such,
+  * in-band actively monitors some aspects of flow and packet processing
+  * so that the rules can be made more precise.
+  *
+  * In-band control monitors attempts to add flows into the datapath that
+  * could interfere with its duties.  The datapath only allows exact
+  * match entries, so in-band control is able to be very precise about
+  * the flows it prevents.  Flows that miss in the datapath are sent to
+  * userspace to be processed, so preventing these flows from being
+  * cached in the "fast path" does not affect correctness.  The only type 
+  * of flow that is currently prevented is one that would prevent DHCP 
+  * replies from being seen by the local port.  For example, a rule that 
+  * forwarded all DHCP traffic to the controller would not be allowed, 
+  * but one that forwarded to all ports (including the local port) would.
+  *
+  * As mentioned earlier, packets that miss in the datapath are sent to
+  * the userspace for processing.  The userspace has its own flow table,
+  * the "classifier", so in-band checks whether any special processing 
+  * is needed before the classifier is consulted.  If a packet is a DHCP 
+  * response to a request from the local port, the packet is forwarded to 
+  * the local port, regardless of the flow table.  Note that this requires 
+  * L7 processing of DHCP replies to determine whether the 'chaddr' field 
+  * matches the MAC address of the local port.
+  *
+  * It is interesting to note that for an L3-based in-band control
+  * mechanism, the majority of rules are devoted to ARP traffic.  At first 
+  * glance, some of these rules appear redundant.  However, each serves an 
+  * important role.  First, in order to determine the MAC address of the 
+  * remote side (controller or gateway) for other ARP rules, we must allow 
+  * ARP traffic for our local port with rules (b) and (c).  If we are 
+  * between a switch and its connection to the controller, we have to 
+  * allow the other switch's ARP traffic to through.  This is done with 
+  * rules (d) and (e), since we do not know the addresses of the other
+  * switches a priori, but do know the controller's or gateway's.  Finally, 
+  * if the controller is running in a local guest VM that is not reached 
+  * through the local port, the switch that is connected to the VM must 
+  * allow ARP traffic based on the controller's IP address, since it will 
+  * not know the MAC address of the local port that is sending the traffic 
+  * or the MAC address of the controller in the guest VM.
+  *
+  * With a few notable exceptions below, in-band should work in most
+  * network setups.  The following are considered "supported' in the
+  * current implementation: 
+  *
+  *    - Locally Connected.  The switch and controller are on the same
+  *      subnet.  This uses rules (a), (b), (c), (h), and (i).
+  *
+  *    - Reached through Gateway.  The switch and controller are on
+  *      different subnets and must go through a gateway.  This uses
+  *      rules (a), (b), (c), (h), and (i).
+  *
+  *    - Between Switch and Controller.  This switch is between another
+  *      switch and the controller, and we want to allow the other
+  *      switch's traffic through.  This uses rules (d), (e), (h), and
+  *      (i).  It uses (b) and (c) indirectly in order to know the MAC
+  *      address for rules (d) and (e).  Note that DHCP for the other
+  *      switch will not work unless the controller explicitly lets this 
+  *      switch pass the traffic.
+  *
+  *    - Between Switch and Gateway.  This switch is between another
+  *      switch and the gateway, and we want to allow the other switch's
+  *      traffic through.  This uses the same rules and logic as the
+  *      "Between Switch and Controller" configuration described earlier.
+  *
+  *    - Controller on Local VM.  The controller is a guest VM on the
+  *      system running in-band control.  This uses rules (a), (b), (c), 
+  *      (h), and (i).
+  *
+  *    - Controller on Local VM with Different Networks.  The controller
+  *      is a guest VM on the system running in-band control, but the
+  *      local port is not used to connect to the controller.  For
+  *      example, an IP address is configured on eth0 of the switch.  The
+  *      controller's VM is connected through eth1 of the switch, but an
+  *      IP address has not been configured for that port on the switch.
+  *      As such, the switch will use eth0 to connect to the controller,
+  *      and eth1's rules about the local port will not work.  In the
+  *      example, the switch attached to eth0 would use rules (a), (b), 
+  *      (c), (h), and (i) on eth0.  The switch attached to eth1 would use 
+  *      rules (f), (g), (h), and (i).
+  *
+  * The following are explicitly *not* supported by in-band control:
+  *
+  *    - Specify Controller by Name.  Currently, the controller must be 
+  *      identified by IP address.  A naive approach would be to permit
+  *      all DNS traffic.  Unfortunately, this would prevent the
+  *      controller from defining any policy over DNS.  Since switches
+  *      that are located behind us need to connect to the controller, 
+  *      in-band cannot simply add a rule that allows DNS traffic from
+  *      the local port.  The "correct" way to support this is to parse
+  *      DNS requests to allow all traffic related to a request for the
+  *      controller's name through.  Due to the potential security
+  *      problems and amount of processing, we decided to hold off for
+  *      the time-being.
+  *
+  *    - Multiple Controllers.  There is nothing intrinsic in the high-
+  *      level design that prevents using multiple (known) controllers, 
+  *      however, the current implementation's data structures assume
+  *      only one.
+  *
+  *    - Differing Controllers for Switches.  All switches must know
+  *      the L3 addresses for all the controllers that other switches 
+  *      may use, since rules need to be setup to allow traffic related 
+  *      to those controllers through.  See rules (f), (g), (h), and (i).
+  *
+  *    - Differing Routes for Switches.  In order for the switch to 
+  *      allow other switches to connect to a controller through a 
+  *      gateway, it allows the gateway's traffic through with rules (d)
+  *      and (e).  If the routes to the controller differ for the two
+  *      switches, we will not know the MAC address of the alternate 
+  *      gateway.
+  */
  #define IB_BASE_PRIORITY 18181800
  
  enum {
-     IBR_FROM_LOCAL_DHCP,          /* From local port, DHCP. */
-     IBR_TO_LOCAL_ARP,             /* To local port, ARP. */
-     IBR_FROM_LOCAL_ARP,           /* From local port, ARP. */
-     IBR_TO_REMOTE_ARP,            /* To remote MAC, ARP. */
-     IBR_FROM_REMOTE_ARP,          /* From remote MAC, ARP. */
-     IBR_TO_CTL_ARP,               /* To controller IP, ARP. */
-     IBR_FROM_CTL_ARP,             /* From controller IP, ARP. */
-     IBR_TO_CTL_OFP,               /* To controller, OpenFlow port. */
-     IBR_FROM_CTL_OFP,             /* From controller, OpenFlow port. */
+     IBR_FROM_LOCAL_DHCP,          /* (a) From local port, DHCP. */
+     IBR_TO_LOCAL_ARP,             /* (b) To local port, ARP. */
+     IBR_FROM_LOCAL_ARP,           /* (c) From local port, ARP. */
+     IBR_TO_REMOTE_ARP,            /* (d) To remote MAC, ARP. */
+     IBR_FROM_REMOTE_ARP,          /* (e) From remote MAC, ARP. */
+     IBR_TO_CTL_ARP,               /* (f) To controller IP, ARP. */
+     IBR_FROM_CTL_ARP,             /* (g) From controller IP, ARP. */
+     IBR_TO_CTL_OFP,               /* (h) To controller, OpenFlow port. */
+     IBR_FROM_CTL_OFP,             /* (i) From controller, OpenFlow port. */
  #if OFP_TCP_PORT != OFP_SSL_PORT
  #error Need to support separate TCP and SSL flows.
  #endif
@@@ -75,13 -225,12 +225,13 @@@ struct in_band 
  
      /* Keep track of local port's information. */
      uint8_t local_mac[ETH_ADDR_LEN];       /* Current MAC. */
 -    char local_name[IF_NAMESIZE];          /* Local device name. */
 +    struct netdev *local_netdev;           /* Local port's network device. */
      time_t next_local_refresh;
  
      /* Keep track of controller and next hop's information. */
      uint32_t controller_ip;                /* Controller IP, 0 if unknown. */
      uint8_t remote_mac[ETH_ADDR_LEN];      /* Remote MAC. */
 +    struct netdev *remote_netdev;
      uint8_t last_remote_mac[ETH_ADDR_LEN]; /* Previous remote MAC. */
      time_t next_remote_refresh;
  
@@@ -96,17 -245,14 +246,17 @@@ get_remote_mac(struct in_band *ib
  {
      int retval;
      bool have_mac;
 -    struct in_addr c_in4, r_in4;
 -    char *dev_name;
 +    struct in_addr c_in4;   /* Controller's IP address. */
 +    struct in_addr r_in4;   /* Next hop IP address. */
 +    char *next_hop_dev;
      time_t now = time_now();
  
      if (now >= ib->next_remote_refresh) {
 +        /* Find the next-hop IP address. */
          c_in4.s_addr = ib->controller_ip;
          memset(ib->remote_mac, 0, sizeof ib->remote_mac);
 -        retval = netdev_get_next_hop(&c_in4, &r_in4, &dev_name);
 +        retval = netdev_get_next_hop(ib->local_netdev,
 +                                     &c_in4, &r_in4, &next_hop_dev);
          if (retval) {
              VLOG_WARN("cannot find route for controller ("IP_FMT"): %s",
                      IP_ARGS(&ib->controller_ip), strerror(retval));
              r_in4.s_addr = c_in4.s_addr;
          }
  
 -        retval = netdev_nodev_arp_lookup(dev_name, r_in4.s_addr, 
 -                                         ib->remote_mac);
 +        /* Get the next-hop IP and network device. */
 +        if (!ib->remote_netdev
 +            || strcmp(netdev_get_name(ib->remote_netdev), next_hop_dev))
 +        {
 +            netdev_close(ib->remote_netdev);
 +            retval = netdev_open(next_hop_dev, NETDEV_ETH_TYPE_NONE,
 +                                 &ib->remote_netdev);
 +            if (retval) {
 +                VLOG_WARN_RL(&rl, "cannot open netdev %s (next hop "
 +                             "to controller "IP_FMT"): %s",
 +                             next_hop_dev, IP_ARGS(&ib->controller_ip),
 +                             strerror(retval));
 +                ib->next_remote_refresh = now + 1;
 +                return NULL;
 +            }
 +        }
 +
 +        /* Look up the MAC address of the next-hop IP address. */
 +        retval = netdev_arp_lookup(ib->remote_netdev, r_in4.s_addr,
 +                                   ib->remote_mac);
          if (retval) {
              VLOG_DBG_RL(&rl, "cannot look up remote MAC address ("IP_FMT"): %s",
                          IP_ARGS(&r_in4.s_addr), strerror(retval));
          }
          have_mac = !eth_addr_is_zero(ib->remote_mac);
 -        free(dev_name);
 -
 -        if (have_mac 
 -                && !eth_addr_equals(ib->last_remote_mac, ib->remote_mac)) {
 +        free(next_hop_dev);
 +        if (have_mac
 +            && !eth_addr_equals(ib->last_remote_mac, ib->remote_mac)) {
              VLOG_DBG("remote MAC address changed from "ETH_ADDR_FMT" to "
                       ETH_ADDR_FMT,
                       ETH_ADDR_ARGS(ib->last_remote_mac),
@@@ -170,7 -299,7 +320,7 @@@ get_local_mac(struct in_band *ib
      time_t now = time_now();
      if (now >= ib->next_local_refresh) {
          uint8_t ea[ETH_ADDR_LEN];
 -        if (!netdev_nodev_get_etheraddr(ib->local_name, ea)) {
 +        if (ib->local_netdev && !netdev_get_etheraddr(ib->local_netdev, ea)) {
              memcpy(ib->local_mac, ea, ETH_ADDR_LEN);
          }
          ib->next_local_refresh = now + 1;
@@@ -449,44 -578,30 +599,44 @@@ in_band_flushed(struct in_band *in_band
      }
  }
  
 -void
 +int
  in_band_create(struct ofproto *ofproto, struct dpif *dpif,
                 struct switch_status *ss, struct rconn *controller, 
                 struct in_band **in_bandp)
  {
      struct in_band *in_band;
 +    char local_name[IF_NAMESIZE];
 +    struct netdev *local_netdev;
      int error;
  
 -    in_band = xcalloc(1, sizeof *in_band);
 -    error = dpif_port_get_name(dpif, ODPP_LOCAL, in_band->local_name, 
 -                               sizeof in_band->local_name);
 +    error = dpif_port_get_name(dpif, ODPP_LOCAL,
 +                               local_name, sizeof local_name);
      if (error) {
 -        free(in_band);
 -        return;
 +        VLOG_ERR("failed to initialize in-band control: cannot get name "
 +                 "of datapath local port (%s)", strerror(error));
 +        return error;
      }
  
 +    error = netdev_open(local_name, NETDEV_ETH_TYPE_NONE, &local_netdev);
 +    if (error) {
 +        VLOG_ERR("failed to initialize in-band control: cannot open "
 +                 "datapath local port %s (%s)", local_name, strerror(error));
 +        return error;
 +    }
 +
 +    in_band = xcalloc(1, sizeof *in_band);
      in_band->ofproto = ofproto;
      in_band->controller = controller;
      in_band->ss_cat = switch_status_register(ss, "in-band",
                                               in_band_status_cb, in_band);
 -    in_band->next_remote_refresh = TIME_MIN;
 +    in_band->local_netdev = local_netdev;
      in_band->next_local_refresh = TIME_MIN;
 +    in_band->remote_netdev = NULL;
 +    in_band->next_remote_refresh = TIME_MIN;
  
      *in_bandp = in_band;
 +
 +    return 0;
  }
  
  void
@@@ -494,8 -609,6 +644,8 @@@ in_band_destroy(struct in_band *in_band
  {
      if (in_band) {
          switch_status_unregister(in_band->ss_cat);
 +        netdev_close(in_band->local_netdev);
 +        netdev_close(in_band->remote_netdev);
          /* We don't own the rconn. */
      }
  }
diff --combined ofproto/ofproto.c
@@@ -135,7 -135,7 +135,7 @@@ rule_is_hidden(const struct rule *rule
          return true;
      }
  
 -    /* Rules with priority higher than UINT16_MAX are set up by secchan itself
 +    /* Rules with priority higher than UINT16_MAX are set up by ofproto itself
       * (e.g. by in-band control) and are intentionally hidden from the
       * controller. */
      if (rule->cr.priority > UINT16_MAX) {
@@@ -194,8 -194,8 +194,8 @@@ struct ofproto 
      char *serial;               /* Serial number. */
  
      /* Datapath. */
 -    struct dpif dpif;
 -    struct dpifmon *dpifmon;
 +    struct dpif *dpif;
 +    struct netdev_monitor *netdev_monitor;
      struct port_array ports;    /* Index is ODP port nr; ofport->opp.port_no is
                                   * OFP port nr. */
      struct shash port_by_name;
@@@ -237,7 -237,7 +237,7 @@@ static struct vlog_rate_limit rl = VLOG
  
  static const struct ofhooks default_ofhooks;
  
 -static uint64_t pick_datapath_id(struct dpif *, uint64_t fallback_dpid);
 +static uint64_t pick_datapath_id(const struct ofproto *);
  static uint64_t pick_fallback_dpid(void);
  static void send_packet_in_miss(struct ofpbuf *, void *ofproto);
  static void send_packet_in_action(struct ofpbuf *, void *ofproto);
@@@ -261,9 -261,10 +261,9 @@@ in
  ofproto_create(const char *datapath, const struct ofhooks *ofhooks, void *aux,
                 struct ofproto **ofprotop)
  {
 -    struct dpifmon *dpifmon;
      struct odp_stats stats;
      struct ofproto *p;
 -    struct dpif dpif;
 +    struct dpif *dpif;
      int error;
  
      *ofprotop = NULL;
          VLOG_ERR("failed to open datapath %s: %s", datapath, strerror(error));
          return error;
      }
 -    error = dpif_get_dp_stats(&dpif, &stats);
 +    error = dpif_get_dp_stats(dpif, &stats);
      if (error) {
          VLOG_ERR("failed to obtain stats for datapath %s: %s",
                   datapath, strerror(error));
 -        dpif_close(&dpif);
 +        dpif_close(dpif);
          return error;
      }
 -    error = dpif_set_listen_mask(&dpif, ODPL_MISS | ODPL_ACTION);
 +    error = dpif_recv_set_mask(dpif, ODPL_MISS | ODPL_ACTION);
      if (error) {
          VLOG_ERR("failed to listen on datapath %s: %s",
                   datapath, strerror(error));
 -        dpif_close(&dpif);
 -        return error;
 -    }
 -    dpif_flow_flush(&dpif);
 -    dpif_purge(&dpif);
 -
 -    /* Start monitoring datapath ports for status changes. */
 -    error = dpifmon_create(datapath, &dpifmon);
 -    if (error) {
 -        VLOG_ERR("failed to starting monitoring datapath %s: %s",
 -                 datapath, strerror(error));
 -        dpif_close(&dpif);
 +        dpif_close(dpif);
          return error;
      }
 +    dpif_flow_flush(dpif);
 +    dpif_recv_purge(dpif);
  
      /* Initialize settings. */
      p = xcalloc(1, sizeof *p);
      p->fallback_dpid = pick_fallback_dpid();
 -    p->datapath_id = pick_datapath_id(&dpif, p->fallback_dpid);
 -    VLOG_INFO("using datapath ID %012"PRIx64, p->datapath_id);
 +    p->datapath_id = p->fallback_dpid;
      p->manufacturer = xstrdup("Nicira Networks, Inc.");
      p->hardware = xstrdup("Reference Implementation");
      p->software = xstrdup(VERSION BUILDNR);
  
      /* Initialize datapath. */
      p->dpif = dpif;
 -    p->dpifmon = dpifmon;
 +    p->netdev_monitor = netdev_monitor_create();
      port_array_init(&p->ports);
      shash_init(&p->port_by_name);
      p->max_ports = stats.max_ports;
          return error;
      }
  
 +    /* Pick final datapath ID. */
 +    p->datapath_id = pick_datapath_id(p);
 +    VLOG_INFO("using datapath ID %012"PRIx64, p->datapath_id);
 +
      *ofprotop = p;
      return 0;
  }
@@@ -366,7 -373,9 +366,7 @@@ voi
  ofproto_set_datapath_id(struct ofproto *p, uint64_t datapath_id)
  {
      uint64_t old_dpid = p->datapath_id;
 -    p->datapath_id = (datapath_id
 -                      ? datapath_id
 -                      : pick_datapath_id(&p->dpif, p->fallback_dpid));
 +    p->datapath_id = datapath_id ? datapath_id : pick_datapath_id(p);
      if (p->datapath_id != old_dpid) {
          VLOG_INFO("datapath ID changed to %012"PRIx64, p->datapath_id);
          rconn_reconnect(p->controller->rconn);
@@@ -424,8 -433,9 +424,8 @@@ ofproto_set_in_band(struct ofproto *p, 
  {
      if (in_band != (p->in_band != NULL)) {
          if (in_band) {
 -            in_band_create(p, &p->dpif, p->switch_status, 
 -                           p->controller->rconn, &p->in_band);
 -            return 0;
 +            return in_band_create(p, p->dpif, p->switch_status,
 +                                  p->controller->rconn, &p->in_band);
          } else {
              ofproto_set_discovery(p, false, NULL, true);
              in_band_destroy(p->in_band);
@@@ -447,7 -457,7 +447,7 @@@ ofproto_set_discovery(struct ofproto *p
                  return error;
              }
              error = discovery_create(re, update_resolv_conf,
 -                                     &p->dpif, p->switch_status,
 +                                     p->dpif, p->switch_status,
                                       &p->discovery);
              if (error) {
                  return error;
@@@ -704,8 -714,8 +704,8 @@@ ofproto_destroy(struct ofproto *p
          ofconn_destroy(ofconn, p);
      }
  
 -    dpif_close(&p->dpif);
 -    dpifmon_destroy(p->dpifmon);
 +    dpif_close(p->dpif);
 +    netdev_monitor_destroy(p->netdev_monitor);
      PORT_ARRAY_FOR_EACH (ofport, &p->ports, port_no) {
          ofport_free(ofport);
      }
@@@ -747,17 -757,6 +747,17 @@@ ofproto_run(struct ofproto *p
      return error;
  }
  
 +static void
 +process_port_change(struct ofproto *ofproto, int error, char *devname)
 +{
 +    if (error == ENOBUFS) {
 +        reinit_ports(ofproto);
 +    } else if (!error) {
 +        update_port(ofproto, devname);
 +        free(devname);
 +    }
 +}
 +
  int
  ofproto_run1(struct ofproto *p)
  {
          struct ofpbuf *buf;
          int error;
  
 -        error = dpif_recv(&p->dpif, &buf);
 +        error = dpif_recv(p->dpif, &buf);
          if (error) {
              if (error == ENODEV) {
                  /* Someone destroyed the datapath behind our back.  The caller
                   * better destroy us and give up, because we're just going to
                   * spin from here on out. */
                  static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
 -                VLOG_ERR_RL(&rl, "dp%u: datapath was destroyed externally",
 -                            dpif_id(&p->dpif));
 +                VLOG_ERR_RL(&rl, "%s: datapath was destroyed externally",
 +                            dpif_name(p->dpif));
                  return ENODEV;
              }
              break;
          handle_odp_msg(p, buf);
      }
  
 -    while ((error = dpifmon_poll(p->dpifmon, &devname)) != EAGAIN) {
 -        if (error == ENOBUFS) {
 -            reinit_ports(p);
 -        } else if (!error) {
 -            update_port(p, devname);
 -            free(devname);
 -        }
 +    while ((error = dpif_port_poll(p->dpif, &devname)) != EAGAIN) {
 +        process_port_change(p, error, devname);
 +    }
 +    while ((error = netdev_monitor_poll(p->netdev_monitor,
 +                                        &devname)) != EAGAIN) {
 +        process_port_change(p, error, devname);
      }
  
      if (p->in_band) {
              }
          }
      }
-     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) {
          ofconn_run(ofconn, p);
      }
  
+     /* Fail-open maintenance.  Do this after processing the ofconns since
+      * fail-open checks the status of the controller rconn. */
+     if (p->fail_open) {
+         fail_open_run(p->fail_open);
+     }
      for (i = 0; i < p->n_listeners; i++) {
          struct vconn *vconn;
          int retval;
@@@ -904,9 -907,8 +907,9 @@@ ofproto_wait(struct ofproto *p
      struct ofconn *ofconn;
      size_t i;
  
 -    dpif_recv_wait(&p->dpif);
 -    dpifmon_wait(p->dpifmon);
 +    dpif_recv_wait(p->dpif);
 +    dpif_port_poll_wait(p->dpif);
 +    netdev_monitor_poll_wait(p->netdev_monitor);
      LIST_FOR_EACH (ofconn, struct ofconn, node, &p->all_conns) {
          ofconn_wait(ofconn);
      }
@@@ -976,7 -978,7 +979,7 @@@ ofproto_send_packet(struct ofproto *p, 
  
      /* XXX Should we translate the dpif_execute() errno value into an OpenFlow
       * error code? */
 -    dpif_execute(&p->dpif, flow->in_port, odp_actions.actions,
 +    dpif_execute(p->dpif, flow->in_port, odp_actions.actions,
                   odp_actions.n_actions, packet);
      return 0;
  }
@@@ -1028,7 -1030,7 +1031,7 @@@ ofproto_flush_flows(struct ofproto *ofp
  {
      COVERAGE_INC(ofproto_flush);
      classifier_for_each(&ofproto->cls, CLS_INC_ALL, destroy_rule, ofproto);
 -    dpif_flow_flush(&ofproto->dpif);
 +    dpif_flow_flush(ofproto->dpif);
      if (ofproto->in_band) {
          in_band_flushed(ofproto->in_band);
      }
@@@ -1051,7 -1053,7 +1054,7 @@@ reinit_ports(struct ofproto *p
      PORT_ARRAY_FOR_EACH (ofport, &p->ports, port_no) {
          svec_add (&devnames, (char *) ofport->opp.name);
      }
 -    dpif_port_list(&p->dpif, &odp_ports, &n_odp_ports);
 +    dpif_port_list(p->dpif, &odp_ports, &n_odp_ports);
      for (i = 0; i < n_odp_ports; i++) {
          svec_add (&devnames, odp_ports[i].devname);
      }
@@@ -1081,7 -1083,7 +1084,7 @@@ refresh_port_group(struct ofproto *p, u
              ports[n_ports++] = port_no;
          }
      }
 -    dpif_port_group_set(&p->dpif, group, ports, n_ports);
 +    dpif_port_group_set(p->dpif, group, ports, n_ports);
      free(ports);
  }
  
@@@ -1113,7 -1115,7 +1116,7 @@@ make_ofport(const struct odp_port *odp_
      ofport = xmalloc(sizeof *ofport);
      ofport->netdev = netdev;
      ofport->opp.port_no = odp_port_to_ofp_port(odp_port->port);
 -    memcpy(ofport->opp.hw_addr, netdev_get_etheraddr(netdev), ETH_ALEN);
 +    netdev_get_etheraddr(netdev, ofport->opp.hw_addr);
      memcpy(ofport->opp.name, odp_port->devname,
             MIN(sizeof ofport->opp.name, sizeof odp_port->devname));
      ofport->opp.name[sizeof ofport->opp.name - 1] = '\0';
@@@ -1188,7 -1190,6 +1191,7 @@@ send_port_status(struct ofproto *p, con
  static void
  ofport_install(struct ofproto *p, struct ofport *ofport)
  {
 +    netdev_monitor_add(p->netdev_monitor, ofport->netdev);
      port_array_set(&p->ports, ofp_port_to_odp_port(ofport->opp.port_no),
                     ofport);
      shash_add(&p->port_by_name, (char *) ofport->opp.name, ofport);
  static void
  ofport_remove(struct ofproto *p, struct ofport *ofport)
  {
 +    netdev_monitor_remove(p->netdev_monitor, ofport->netdev);
      port_array_set(&p->ports, ofp_port_to_odp_port(ofport->opp.port_no), NULL);
      shash_delete(&p->port_by_name,
                   shash_find(&p->port_by_name, (char *) ofport->opp.name));
@@@ -1223,7 -1223,7 +1226,7 @@@ update_port(struct ofproto *p, const ch
      COVERAGE_INC(ofproto_update_port);
  
      /* Query the datapath for port information. */
 -    error = dpif_port_query_by_name(&p->dpif, devname, &odp_port);
 +    error = dpif_port_query_by_name(p->dpif, devname, &odp_port);
  
      /* Find the old ofport. */
      old_ofport = shash_find_data(&p->port_by_name, devname);
@@@ -1292,7 -1292,7 +1295,7 @@@ init_ports(struct ofproto *p
      size_t i;
      int error;
  
 -    error = dpif_port_list(&p->dpif, &ports, &n_ports);
 +    error = dpif_port_list(p->dpif, &ports, &n_ports);
      if (error) {
          return error;
      }
@@@ -1355,6 -1355,9 +1358,9 @@@ ofconn_run(struct ofconn *ofconn, struc
              if (!of_msg) {
                  break;
              }
+             if (p->fail_open) {
+                 fail_open_maybe_recover(p->fail_open);
+             }
              handle_openflow(ofconn, p, of_msg);
              ofpbuf_delete(of_msg);
          }
@@@ -1494,7 -1497,7 +1500,7 @@@ rule_execute(struct ofproto *ofproto, s
      }
  
      /* Execute the ODP actions. */
 -    if (!dpif_execute(&ofproto->dpif, flow->in_port,
 +    if (!dpif_execute(ofproto->dpif, flow->in_port,
                        actions, n_actions, packet)) {
          struct odp_flow_stats stats;
          flow_extract_stats(flow, packet, &stats);
@@@ -1603,7 -1606,7 +1609,7 @@@ do_put_flow(struct ofproto *ofproto, st
      put->flow.actions = rule->odp_actions;
      put->flow.n_actions = rule->n_odp_actions;
      put->flags = flags;
 -    return dpif_flow_put(&ofproto->dpif, put);
 +    return dpif_flow_put(ofproto->dpif, put);
  }
  
  static void
@@@ -1684,7 -1687,7 +1690,7 @@@ rule_uninstall(struct ofproto *p, struc
          odp_flow.key = rule->cr.flow;
          odp_flow.actions = NULL;
          odp_flow.n_actions = 0;
 -        if (!dpif_flow_del(&p->dpif, &odp_flow)) {
 +        if (!dpif_flow_del(p->dpif, &odp_flow)) {
              update_stats(rule, &odp_flow.stats);
          }
          rule->installed = false;
@@@ -1832,7 -1835,7 +1838,7 @@@ handle_get_config_request(struct ofprot
      bool drop_frags;
  
      /* Figure out flags. */
 -    dpif_get_drop_frags(&p->dpif, &drop_frags);
 +    dpif_get_drop_frags(p->dpif, &drop_frags);
      flags = drop_frags ? OFPC_FRAG_DROP : OFPC_FRAG_NORMAL;
      if (ofconn->send_flow_exp) {
          flags |= OFPC_SEND_FLOW_EXP;
@@@ -1865,10 -1868,10 +1871,10 @@@ handle_set_config(struct ofproto *p, st
      if (ofconn == p->controller) {
          switch (flags & OFPC_FRAG_MASK) {
          case OFPC_FRAG_NORMAL:
 -            dpif_set_drop_frags(&p->dpif, false);
 +            dpif_set_drop_frags(p->dpif, false);
              break;
          case OFPC_FRAG_DROP:
 -            dpif_set_drop_frags(&p->dpif, true);
 +            dpif_set_drop_frags(p->dpif, true);
              break;
          default:
              VLOG_WARN_RL(&rl, "requested bad fragment mode (flags=%"PRIx16")",
@@@ -2165,7 -2168,7 +2171,7 @@@ handle_packet_out(struct ofproto *p, st
      if (opo->buffer_id != htonl(UINT32_MAX)) {
          error = pktbuf_retrieve(ofconn->pktbuf, ntohl(opo->buffer_id),
                                  &buffer, &in_port);
-         if (error) {
+         if (error || !buffer) {
              return error;
          }
          payload = *buffer;
          return error;
      }
  
 -    dpif_execute(&p->dpif, flow.in_port, actions.actions, actions.n_actions,
 +    dpif_execute(p->dpif, flow.in_port, actions.actions, actions.n_actions,
                   &payload);
      ofpbuf_delete(buffer);
  
@@@ -2323,7 -2326,7 +2329,7 @@@ handle_table_stats_request(struct ofpro
      n_wild = classifier_count(&p->cls) - classifier_count_exact(&p->cls);
  
      /* Hash table. */
 -    dpif_get_dp_stats(&p->dpif, &dpstats);
 +    dpif_get_dp_stats(p->dpif, &dpstats);
      ots = append_stats_reply(sizeof *ots, ofconn, &msg);
      memset(ots, 0, sizeof *ots);
      ots->table_id = TABLEID_HASH;
@@@ -2418,7 -2421,7 +2424,7 @@@ query_stats(struct ofproto *p, struct r
  
      packet_count = rule->packet_count;
      byte_count = rule->byte_count;
 -    if (!dpif_flow_get_multiple(&p->dpif, odp_flows, n_odp_flows)) {
 +    if (!dpif_flow_get_multiple(p->dpif, odp_flows, n_odp_flows)) {
          size_t i;
          for (i = 0; i < n_odp_flows; i++) {
              struct odp_flow *odp_flow = &odp_flows[i];
@@@ -3042,7 -3045,7 +3048,7 @@@ handle_odp_msg(struct ofproto *p, struc
          memset(&action, 0, sizeof(action));
          action.output.type = ODPAT_OUTPUT;
          action.output.port = ODPP_LOCAL;
 -        dpif_execute(&p->dpif, flow.in_port, &action, 1, &payload);
 +        dpif_execute(p->dpif, flow.in_port, &action, 1, &payload);
      }
  
      rule = lookup_valid_rule(p, &flow);
  
      rule_execute(p, rule, &payload, &flow);
      rule_reinstall(p, rule);
-     ofpbuf_delete(packet);
+     if (rule->super && rule->super->cr.priority == FAIL_OPEN_PRIORITY
+         && rconn_is_connected(p->controller->rconn)) {
+         /*
+          * Extra-special case for fail-open mode.
+          *
+          * We are in fail-open mode and the packet matched the fail-open rule,
+          * but we are connected to a controller too.  We should send the packet
+          * up to the controller in the hope that it will try to set up a flow
+          * and thereby allow us to exit fail-open.
+          *
+          * See the top-level comment in fail-open.c for more information.
+          */
+         pinsched_send(p->miss_sched, in_port, packet, send_packet_in_miss, p);
+     } else {
+         ofpbuf_delete(packet);
+     }
  }
  \f
  static void
@@@ -3148,7 -3167,7 +3170,7 @@@ send_flow_exp(struct ofproto *p, struc
  {
      struct ofconn *ofconn;
      struct ofconn *prev;
 -    struct ofpbuf *buf;
 +    struct ofpbuf *buf = NULL;
  
      /* We limit the maximum number of queued flow expirations it by accounting
       * them under the counter for replies.  That works because preventing
      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);
+                 queue_tx(ofpbuf_clone(buf), prev, prev->reply_counter);
              } else {
                  buf = compose_flow_exp(rule, now, reason);
              }
          }
      }
      if (prev) {
-         queue_tx(buf, prev, ofconn->reply_counter);
+         queue_tx(buf, prev, prev->reply_counter);
      }
  }
  
@@@ -3240,7 -3259,7 +3262,7 @@@ update_used(struct ofproto *p
      size_t i;
      int error;
  
 -    error = dpif_flow_list_all(&p->dpif, &flows, &n_flows);
 +    error = dpif_flow_list_all(p->dpif, &flows, &n_flows);
      if (error) {
          return;
      }
              classifier_find_rule_exactly(&p->cls, &f->key, 0, UINT16_MAX));
          if (!rule || !rule->installed) {
              COVERAGE_INC(ofproto_unexpected_rule);
 -            dpif_flow_del(&p->dpif, f);
 +            dpif_flow_del(p->dpif, f);
              continue;
          }
  
@@@ -3267,25 -3286,22 +3289,22 @@@ static voi
  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;
+     struct odp_msg *msg = packet->data;
+     struct ofpbuf payload;
+     struct ofpbuf *opi;
+     uint8_t reason;
  
-     msg = packet->data;
+     /* Extract packet payload from 'msg'. */
      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);
+     /* Construct ofp_packet_in message. */
+     reason = msg->type == _ODPL_ACTION_NR ? OFPR_ACTION : OFPR_NO_MATCH;
+     opi = make_packet_in(buffer_id, odp_port_to_ofp_port(msg->port), reason,
+                          &payload, send_len);
+     /* Send. */
+     rconn_send_with_limit(ofconn->rconn, opi, ofconn->packet_in_counter, 100);
  }
  
  static void
@@@ -3308,6 -3324,7 +3327,7 @@@ static voi
  send_packet_in_miss(struct ofpbuf *packet, void *p_)
  {
      struct ofproto *p = p_;
+     bool in_fail_open = p->fail_open && fail_open_is_active(p->fail_open);
      struct ofconn *ofconn;
      struct ofpbuf payload;
      struct odp_msg *msg;
      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);
+             struct pktbuf *pb = ofconn->pktbuf;
+             uint32_t buffer_id = (in_fail_open
+                                   ? pktbuf_get_null()
+                                   : pktbuf_save(pb, &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);
  }
  
  static uint64_t
 -pick_datapath_id(struct dpif *dpif, uint64_t fallback_dpid)
 +pick_datapath_id(const struct ofproto *ofproto)
  {
 -    char local_name[IF_NAMESIZE];
 -    uint8_t ea[ETH_ADDR_LEN];
 -    int error;
 +    const struct ofport *port;
  
 -    error = dpif_get_name(dpif, local_name, sizeof local_name);
 -    if (!error) {
 -        error = netdev_nodev_get_etheraddr(local_name, ea);
 +    port = port_array_get(&ofproto->ports, ODPP_LOCAL);
 +    if (port) {
 +        uint8_t ea[ETH_ADDR_LEN];
 +        int error;
 +
 +        error = netdev_get_etheraddr(port->netdev, ea);
          if (!error) {
              return eth_addr_to_uint64(ea);
          }
          VLOG_WARN("could not get MAC address for %s (%s)",
 -                  local_name, strerror(error));
 +                  netdev_get_name(port->netdev), strerror(error));
      }
 -
 -    return fallback_dpid;
 +    return ofproto->fallback_dpid;
  }
  
  static uint64_t
diff --combined ofproto/pktbuf.c
@@@ -51,6 -51,7 +51,7 @@@ struct packet 
  struct pktbuf {
      struct packet packets[PKTBUF_CNT];
      unsigned int buffer_idx;
+     unsigned int null_idx;
  };
  
  int
@@@ -78,6 -79,22 +79,22 @@@ pktbuf_destroy(struct pktbuf *pb
      }
  }
  
+ static unsigned int
+ make_id(unsigned int buffer_idx, unsigned int cookie)
+ {
+     return buffer_idx | (cookie << PKTBUF_BITS);
+ }
+ /* Attempts to allocate an OpenFlow packet buffer id within 'pb'.  The packet
+  * buffer will store a copy of 'buffer' and the port number 'in_port', which
+  * should be the datapath port number on which 'buffer' was received.
+  *
+  * If successful, returns the packet buffer id (a number other than
+  * UINT32_MAX).  pktbuf_retrieve() can later be used to retrieve the buffer and
+  * its input port number (buffers do expire after a time, so this is not
+  * guaranteed to be true forever).  On failure, returns UINT32_MAX.
+  *
+  * The caller retains ownership of 'buffer'. */
  uint32_t
  pktbuf_save(struct pktbuf *pb, struct ofpbuf *buffer, uint16_t in_port)
  {
      p->buffer = ofpbuf_clone(buffer);
      p->timeout = time_msec() + OVERWRITE_MSECS;
      p->in_port = in_port;
-     return (p - pb->packets) | (p->cookie << PKTBUF_BITS);
+     return make_id(p - pb->packets, p->cookie);
+ }
+ /*
+  * Allocates and returns a "null" packet buffer id.  The returned packet buffer
+  * id is considered valid by pktbuf_retrieve(), but it is not associated with
+  * actual buffered data.
+  *
+  * This function is always successful.
+  *
+  * This is useful in one special case: with the current OpenFlow design, the
+  * "fail-open" code cannot always know whether a connection to a controller is
+  * actually valid until it receives a OFPT_PACKET_OUT or OFPT_FLOW_MOD request,
+  * but at that point the packet in question has already been forwarded (since
+  * we are still in "fail-open" mode).  If the packet was buffered in the usual
+  * way, then the OFPT_PACKET_OUT or OFPT_FLOW_MOD would cause a duplicate
+  * packet in the network.  Null packet buffer ids identify such a packet that
+  * has already been forwarded, so that Open vSwitch can quietly ignore the
+  * request to re-send it.  (After that happens, the switch exits fail-open
+  * mode.)
+  *
+  * See the top-level comment in fail-open.c for an overview.
+  */
+ uint32_t
+ pktbuf_get_null(void)
+ {
+     return make_id(0, COOKIE_MAX);
  }
  
+ /* Attempts to retrieve a saved packet with the given 'id' from 'pb'.  Returns
+  * 0 if successful, otherwise an OpenFlow error code constructed with
+  * ofp_mkerr().
+  *
+  * On success, ordinarily stores the buffered packet in '*bufferp' and the
+  * datapath port number on which the packet was received in '*in_port'.  The
+  * caller becomes responsible for freeing the buffer.  However, if 'id'
+  * identifies a "null" packet buffer (created with pktbuf_get_null()), stores
+  * NULL in '*bufferp' and -1 in '*in_port'.
+  *
+  * On failure, stores NULL in in '*bufferp' and -1 in '*in_port'. */
  int
  pktbuf_retrieve(struct pktbuf *pb, uint32_t id, struct ofpbuf **bufferp,
                  uint16_t *in_port)
              VLOG_WARN_RL(&rl, "attempt to reuse buffer %08"PRIx32, id);
              error = ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BUFFER_EMPTY);
          }
-     } else {
+     } else if (id >> PKTBUF_BITS != COOKIE_MAX) {
          COVERAGE_INC(pktbuf_bad_cookie);
          VLOG_WARN_RL(&rl, "cookie mismatch: %08"PRIx32" != %08"PRIx32,
                       id, (id & PKTBUF_MASK) | (p->cookie << PKTBUF_BITS));
          error = ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_COOKIE);
+     } else {
+         COVERAGE_INC(pktbuf_null_cookie);
+         VLOG_INFO_RL(&rl, "Received null cookie %08"PRIx32" (this is normal "
+                      "if the switch was recently in fail-open mode)", id);
+         error = 0;
      }
      *bufferp = NULL;
      *in_port = -1;
diff --combined ofproto/pktbuf.h
@@@ -27,6 -27,7 +27,7 @@@ int pktbuf_capacity(void)
  struct pktbuf *pktbuf_create(void);
  void pktbuf_destroy(struct pktbuf *);
  uint32_t pktbuf_save(struct pktbuf *, struct ofpbuf *buffer, uint16_t in_port);
+ uint32_t pktbuf_get_null(void);
  int pktbuf_retrieve(struct pktbuf *, uint32_t id, struct ofpbuf **bufferp,
                      uint16_t *in_port);
  void pktbuf_discard(struct pktbuf *, uint32_t id);
@@@ -15,7 -15,7 +15,7 @@@ ovs\-appctl \- utility for configuring 
  .sp 1
  The available \fItarget\fR options are:
  .br
- [\fB-t\fR \fIpid\fR | \fB--target=\fIpid\fR]
+ [\fB-t\fR \fIsocket\fR | \fB--target=\fIsocket\fR]
  .sp 1
  The available \fIaction\fR options are:
  .br
@@@ -79,7 -79,7 +79,7 @@@ expanded as follows
  
  .RS
  .IP \fB%A\fR
 -The name of the application logging the message, e.g. \fBsecchan\fR.
 +The name of the application logging the message, e.g. \fBovs-vswitchd\fR.
  
  .IP \fB%c\fR
  The name of the module (as shown by \fBovs\-appctl --list\fR) logging
@@@ -163,4 -163,4 +163,4 @@@ error occurs.  Use \fB-e help\fR to pri
  
  .BR ovs\-controller (8),
  .BR ovs\-dpctl (8),
 -.BR secchan (8)
 +.BR ovs\-openflowd (8)
@@@ -16,22 -16,16 +16,22 @@@ protocol, causing them to function as L
  one or more of the following OpenFlow connection methods:
  
  .TP
 -\fBpssl:\fR[\fIport\fR]
 +\fBpssl:\fR[\fIport\fR][\fB:\fIip\fR]
  Listens for SSL connections from remote OpenFlow switches on
  \fIport\fR (default: 6633).  The \fB--private-key\fR,
  \fB--certificate\fR, and \fB--ca-cert\fR options are mandatory when
  this form is used.
 +By default, \fB\*(PN\fR listens for connections to any local IP
 +address, but \fIip\fR may be specified to listen only for connections
 +to the given \fIip\fR.
  
  .TP
 -\fBptcp:\fR[\fIport\fR]
 +\fBptcp:\fR[\fIport\fR][\fB:\fIip\fR]
  Listens for TCP connections from remote OpenFlow switches 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
@@@ -81,7 -75,7 +81,7 @@@ already have the controller CA certific
  confidence in the controller's identity.  However, this option allows
  a newly installed switch to obtain the controller CA certificate on
  first boot using, e.g., the \fB--bootstrap-ca-cert\fR option to
 -\fBsecchan\fR(8).
 +\fBovs\-openflowd\fR(8).
  
  .IP "\fB-n\fR, \fB--noflow\fR"
  By default, \fBovs\-controller\fR sets up a flow in each OpenFlow switch
@@@ -102,7 -96,7 +102,7 @@@ recommended, flows will never expire.  
  This option affects only flows set up by the OpenFlow controller.  In
  some configurations, the switch can set up some flows
  on its own.  To set the idle time for those flows, pass
 -\fB--max-idle\fR to \fBsecchan\fR (on the switch).
 +\fB--max-idle\fR to \fBovs\-openflowd\fR (on the switch).
  
  This option has no effect when \fB-n\fR (or \fB--noflow\fR) is in use
  (because the controller does not set up flows in that case).
@@@ -119,6 -113,13 +119,13 @@@ through the controller and every packe
  This option is most useful for debugging.  It reduces switching
  performance, so it should not be used in production.
  
+ .IP "\fB--mute\fR"
+ Prevents ovs\-controller from replying to any OpenFlow messages sent
+ to it by switches.
+ .IP
+ This option is only for debugging the Open vSwitch implementation of
+ ``fail open'' mode.  It must not be used in production.
  .so lib/daemon.man
  .so lib/vlog.man
  .so lib/common.man
@@@ -132,6 -133,6 +139,6 @@@ To bind locally to port 6633 (the defau
  
  .SH "SEE ALSO"
  
 -.BR secchan (8),
 +.BR ovs\-openflowd (8),
  .BR ovs\-appctl (8),
  .BR ovs\-dpctl (8)
diff --combined vswitchd/bridge.c
  #include "odp-util.h"
  #include "ofp-print.h"
  #include "ofpbuf.h"
 +#include "ofproto/ofproto.h"
  #include "packets.h"
  #include "poll-loop.h"
  #include "port-array.h"
  #include "proc-net-compat.h"
  #include "process.h"
 -#include "secchan/ofproto.h"
  #include "socket-util.h"
  #include "stp.h"
  #include "svec.h"
@@@ -71,18 -71,17 +71,18 @@@ struct dst 
  extern uint64_t mgmt_id;
  
  struct iface {
 +    /* These members are always valid. */
      struct port *port;          /* Containing port. */
      size_t port_ifidx;          /* Index within containing port. */
 -
      char *name;                 /* Host network device name. */
 -    int dp_ifidx;               /* Index within kernel datapath. */
 -
 -    uint8_t mac[ETH_ADDR_LEN];  /* Ethernet address (all zeros if unknowns). */
 -
      tag_type tag;               /* Tag associated with this interface. */
 -    bool enabled;               /* May be chosen for flows? */
      long long delay_expires;    /* Time after which 'enabled' may change. */
 +
 +    /* These members are valid only after bridge_reconfigure() causes them to
 +     * be initialized.*/
 +    int dp_ifidx;               /* Index within kernel datapath. */
 +    struct netdev *netdev;      /* Network device. */
 +    bool enabled;               /* May be chosen for flows? */
  };
  
  #define BOND_MASK 0xff
@@@ -160,7 -159,7 +160,7 @@@ struct bridge 
      struct ofproto *ofproto;    /* OpenFlow switch. */
  
      /* Kernel datapath information. */
 -    struct dpif dpif;           /* Kernel datapath. */
 +    struct dpif *dpif;          /* Datapath. */
      struct port_array ifaces;   /* Indexed by kernel datapath port number. */
  
      /* Bridge ports. */
@@@ -203,11 -202,10 +203,11 @@@ static void bridge_fetch_dp_ifaces(stru
  static void bridge_flush(struct bridge *);
  static void bridge_pick_local_hw_addr(struct bridge *,
                                        uint8_t ea[ETH_ADDR_LEN],
 -                                      const char **devname);
 +                                      struct iface **hw_addr_iface);
  static uint64_t bridge_pick_datapath_id(struct bridge *,
                                          const uint8_t bridge_ea[ETH_ADDR_LEN],
 -                                        const char *devname);
 +                                        struct iface *hw_addr_iface);
 +static struct iface *bridge_get_local_iface(struct bridge *);
  static uint64_t dpid_from_hash(const void *, size_t nbytes);
  
  static void bridge_unixctl_fdb_show(struct unixctl_conn *, const char *args);
@@@ -227,7 -225,6 +227,7 @@@ static struct port *port_from_dp_ifidx(
                                         uint16_t dp_ifidx);
  static void port_update_bond_compat(struct port *);
  static void port_update_vlan_compat(struct port *);
 +static void port_update_bonding(struct port *);
  
  static void mirror_create(struct bridge *, const char *name);
  static void mirror_destroy(struct mirror *);
@@@ -266,8 -263,8 +266,8 @@@ bridge_get_ifaces(struct svec *svec
              for (j = 0; j < port->n_ifaces; j++) {
                  struct iface *iface = port->ifaces[j];
                  if (iface->dp_ifidx < 0) {
 -                    VLOG_ERR("%s interface not in dp%u, ignoring",
 -                             iface->name, dpif_id(&br->dpif));
 +                    VLOG_ERR("%s interface not in datapath %s, ignoring",
 +                             iface->name, dpif_name(br->dpif));
                  } else {
                      if (iface->dp_ifidx != ODPP_LOCAL) {
                          svec_add(svec, iface->name);
  void
  bridge_init(void)
  {
 -    int retval;
 -    int i;
 -
 -    bond_init();
 +    struct svec dpif_names;
 +    size_t i;
  
      unixctl_command_register("fdb/show", bridge_unixctl_fdb_show);
  
 -    for (i = 0; i < DP_MAX; i++) {
 -        struct dpif dpif;
 -        char devname[16];
 +    svec_init(&dpif_names);
 +    dp_enumerate(&dpif_names);
 +    for (i = 0; i < dpif_names.n; i++) {
 +        const char *dpif_name = dpif_names.names[i];
 +        struct dpif *dpif;
 +        int retval;
  
 -        sprintf(devname, "dp%d", i);
 -        retval = dpif_open(devname, &dpif);
 +        retval = dpif_open(dpif_name, &dpif);
          if (!retval) {
 -            char dpif_name[IF_NAMESIZE];
 -            if (dpif_get_name(&dpif, dpif_name, sizeof dpif_name)
 -                || !cfg_has("bridge.%s.port", dpif_name)) {
 -                dpif_delete(&dpif);
 +            struct svec all_names;
 +            size_t j;
 +
 +            svec_init(&all_names);
 +            dpif_get_all_names(dpif, &all_names);
 +            for (j = 0; j < all_names.n; j++) {
 +                if (cfg_has("bridge.%s.port", all_names.names[j])) {
 +                    goto found;
 +                }
              }
 -            dpif_close(&dpif);
 -        } else if (retval != ENODEV) {
 -            VLOG_ERR("failed to delete datapath dp%d: %s",
 -                     i, strerror(retval));
 +            dpif_delete(dpif);
 +        found:
 +            svec_destroy(&all_names);
 +            dpif_close(dpif);
          }
      }
 +    svec_destroy(&dpif_names);
  
      unixctl_command_register("bridge/dump-flows", bridge_unixctl_dump_flows);
  
 +    bond_init();
      bridge_reconfigure();
  }
  
@@@ -358,105 -348,43 +358,105 @@@ bridge_configure_ssl(void
       * the old certificate will still be trusted until vSwitch is
       * restarted.  We may want to address this in vconn's SSL library. */
      if (config_string_change("ssl.ca-cert", &cacert_file)
 -            || (stat(cacert_file, &s) && errno == ENOENT)) {
 +        || (cacert_file && stat(cacert_file, &s) && errno == ENOENT)) {
          vconn_ssl_set_ca_cert_file(cacert_file,
                                     cfg_get_bool(0, "ssl.bootstrap-ca-cert"));
      }
  }
  #endif
  
 +/* iterate_and_prune_ifaces() callback function that opens the network device
 + * for 'iface', if it is not already open, and retrieves the interface's MAC
 + * address and carrier status. */
 +static bool
 +init_iface_netdev(struct bridge *br UNUSED, struct iface *iface,
 +                  void *aux UNUSED)
 +{
 +    if (iface->netdev) {
 +        return true;
 +    } else if (!netdev_open(iface->name, NETDEV_ETH_TYPE_NONE,
 +                            &iface->netdev)) {
 +        netdev_get_carrier(iface->netdev, &iface->enabled);
 +        return true;
 +    } else {
 +        /* If the network device can't be opened, then we're not going to try
 +         * to do anything with this interface. */
 +        return false;
 +    }
 +}
 +
 +static bool
 +check_iface_dp_ifidx(struct bridge *br, struct iface *iface, void *aux UNUSED)
 +{
 +    if (iface->dp_ifidx >= 0) {
 +        VLOG_DBG("%s has interface %s on port %d",
 +                 dpif_name(br->dpif),
 +                 iface->name, iface->dp_ifidx);
 +        return true;
 +    } else {
 +        VLOG_ERR("%s interface not in %s, dropping",
 +                 iface->name, dpif_name(br->dpif));
 +        return false;
 +    }
 +}
 +
 +static bool
 +set_iface_policing(struct bridge *br UNUSED, struct iface *iface,
 +                   void *aux UNUSED)
 +{
 +    int rate = cfg_get_int(0, "port.%s.ingress.policing-rate", iface->name);
 +    int burst = cfg_get_int(0, "port.%s.ingress.policing-burst", iface->name);
 +    netdev_set_policing(iface->netdev, rate, burst);
 +    return true;
 +}
 +
 +/* Calls 'cb' for each interfaces in 'br', passing along the 'aux' argument.
 + * Deletes from 'br' all the interfaces for which 'cb' returns false, and then
 + * deletes from 'br' any ports that no longer have any interfaces. */
 +static void
 +iterate_and_prune_ifaces(struct bridge *br,
 +                         bool (*cb)(struct bridge *, struct iface *,
 +                                    void *aux),
 +                         void *aux)
 +{
 +    size_t i, j;
 +
 +    for (i = 0; i < br->n_ports; ) {
 +        struct port *port = br->ports[i];
 +        for (j = 0; j < port->n_ifaces; ) {
 +            struct iface *iface = port->ifaces[j];
 +            if (cb(br, iface, aux)) {
 +                j++;
 +            } else {
 +                iface_destroy(iface);
 +            }
 +        }
 +
 +        if (port->n_ifaces) {
 +            i++;
 +        } else  {
 +            VLOG_ERR("%s port has no interfaces, dropping", port->name);
 +            port_destroy(port);
 +        }
 +    }
 +}
 +
  void
  bridge_reconfigure(void)
  {
 -    struct svec old_br, new_br, raw_new_br;
 +    struct svec old_br, new_br;
      struct bridge *br, *next;
 -    size_t i, j;
 +    size_t i;
  
      COVERAGE_INC(bridge_reconfigure);
  
 -    /* Collect old bridges. */
 +    /* Collect old and new bridges. */
      svec_init(&old_br);
 +    svec_init(&new_br);
      LIST_FOR_EACH (br, struct bridge, node, &all_bridges) {
          svec_add(&old_br, br->name);
      }
 -
 -    /* Collect new bridges. */
 -    svec_init(&raw_new_br);
 -    cfg_get_subsections(&raw_new_br, "bridge");
 -    svec_init(&new_br);
 -    for (i = 0; i < raw_new_br.n; i++) {
 -        const char *name = raw_new_br.names[i];
 -        if ((!strncmp(name, "dp", 2) && isdigit(name[2])) ||
 -            (!strncmp(name, "nl:", 3) && isdigit(name[3]))) {
 -            VLOG_ERR("%s is not a valid bridge name (bridges may not be "
 -                     "named \"dp\" or \"nl:\" followed by a digit)", name);
 -        } else {
 -            svec_add(&new_br, name);
 -        }
 -    }
 -    svec_destroy(&raw_new_br);
 +    cfg_get_subsections(&new_br, "bridge");
  
      /* Get rid of deleted bridges and add new bridges. */
      svec_sort(&old_br);
          size_t n_dpif_ports;
          struct svec want_ifaces;
  
 -        dpif_port_list(&br->dpif, &dpif_ports, &n_dpif_ports);
 +        dpif_port_list(br->dpif, &dpif_ports, &n_dpif_ports);
          bridge_get_all_ifaces(br, &want_ifaces);
          for (i = 0; i < n_dpif_ports; i++) {
              const struct odp_port *p = &dpif_ports[i];
              if (!svec_contains(&want_ifaces, p->devname)
                  && strcmp(p->devname, br->name)) {
 -                int retval = dpif_port_del(&br->dpif, p->port);
 +                int retval = dpif_port_del(br->dpif, p->port);
                  if (retval) {
 -                    VLOG_ERR("failed to remove %s interface from dp%u: %s",
 -                             p->devname, dpif_id(&br->dpif), strerror(retval));
 +                    VLOG_ERR("failed to remove %s interface from %s: %s",
 +                             p->devname, dpif_name(br->dpif),
 +                             strerror(retval));
                  }
              }
          }
          struct odp_port *dpif_ports;
          size_t n_dpif_ports;
          struct svec cur_ifaces, want_ifaces, add_ifaces;
 -        int next_port_no;
  
 -        dpif_port_list(&br->dpif, &dpif_ports, &n_dpif_ports);
 +        dpif_port_list(br->dpif, &dpif_ports, &n_dpif_ports);
          svec_init(&cur_ifaces);
          for (i = 0; i < n_dpif_ports; i++) {
              svec_add(&cur_ifaces, dpif_ports[i].devname);
          bridge_get_all_ifaces(br, &want_ifaces);
          svec_diff(&want_ifaces, &cur_ifaces, &add_ifaces, NULL, NULL);
  
 -        next_port_no = 1;
          for (i = 0; i < add_ifaces.n; i++) {
              const char *if_name = add_ifaces.names[i];
 -            for (;;) {
 -                bool internal;
 -                int error;
 -
 -                /* It's an internal interface if it's marked that way, or if
 -                 * it's a bonded interface for which we're faking up a network
 -                 * device. */
 -                internal = cfg_get_bool(0, "iface.%s.internal", if_name);
 -                if (cfg_get_bool(0, "bonding.%s.fake-iface", if_name)) {
 -                    struct port *port = port_lookup(br, if_name);
 -                    if (port && port->n_ifaces > 1) {
 -                        internal = true;
 -                    }
 -                }
 +            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 == EFBIG) {
 +                VLOG_ERR("ran out of valid port numbers on %s",
 +                         dpif_name(br->dpif));
 +                break;
 +            } else if (error) {
 +                VLOG_ERR("failed to add %s interface to %s: %s",
 +                         if_name, dpif_name(br->dpif), strerror(error));
 +            }
          }
 -    out:
          svec_destroy(&cur_ifaces);
          svec_destroy(&want_ifaces);
          svec_destroy(&add_ifaces);
      LIST_FOR_EACH (br, struct bridge, node, &all_bridges) {
          uint8_t ea[8];
          uint64_t dpid;
 -        struct iface *local_iface = NULL;
 -        const char *devname;
 -        uint8_t engine_type = br->dpif.minor;
 -        uint8_t engine_id = br->dpif.minor;
 +        struct iface *local_iface;
 +        struct iface *hw_addr_iface;
 +        uint8_t engine_type, engine_id;
          bool add_id_to_iface = false;
          struct svec nf_hosts;
  
          bridge_fetch_dp_ifaces(br);
 -        for (i = 0; i < br->n_ports; ) {
 -            struct port *port = br->ports[i];
 +        iterate_and_prune_ifaces(br, init_iface_netdev, NULL);
  
 -            for (j = 0; j < port->n_ifaces; ) {
 -                struct iface *iface = port->ifaces[j];
 -                if (iface->dp_ifidx < 0) {
 -                    VLOG_ERR("%s interface not in dp%u, dropping",
 -                             iface->name, dpif_id(&br->dpif));
 -                    iface_destroy(iface);
 -                } else {
 -                    if (iface->dp_ifidx == ODPP_LOCAL) {
 -                        local_iface = iface;
 -                    }
 -                    VLOG_DBG("dp%u has interface %s on port %d",
 -                             dpif_id(&br->dpif), iface->name, iface->dp_ifidx);
 -                    j++;
 -                }
 -            }
 -            if (!port->n_ifaces) {
 -                VLOG_ERR("%s port has no interfaces, dropping", port->name);
 -                port_destroy(port);
 -                continue;
 -            }
 -            i++;
 -        }
 +        iterate_and_prune_ifaces(br, check_iface_dp_ifidx, NULL);
  
          /* Pick local port hardware address, datapath ID. */
 -        bridge_pick_local_hw_addr(br, ea, &devname);
 +        bridge_pick_local_hw_addr(br, ea, &hw_addr_iface);
 +        local_iface = bridge_get_local_iface(br);
          if (local_iface) {
 -            int error = netdev_nodev_set_etheraddr(local_iface->name, ea);
 +            int error = netdev_set_etheraddr(local_iface->netdev, ea);
              if (error) {
                  static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
                  VLOG_ERR_RL(&rl, "bridge %s: failed to set bridge "
              }
          }
  
 -        dpid = bridge_pick_datapath_id(br, ea, devname);
 +        dpid = bridge_pick_datapath_id(br, ea, hw_addr_iface);
          ofproto_set_datapath_id(br->ofproto, dpid);
  
          /* Set NetFlow configuration on this bridge. */
 +        dpif_get_netflow_ids(br->dpif, &engine_type, &engine_id);
          if (cfg_has("netflow.%s.engine-type", br->name)) {
              engine_type = cfg_get_int(0, "netflow.%s.engine-type", 
                      br->name);
              VLOG_ERR("bridge %s: problem setting netflow collectors", 
                      br->name);
          }
+         svec_destroy(&nf_hosts);
  
          /* Update the controller and related settings.  It would be more
           * straightforward to call this from bridge_reconfigure_one(), but we
          for (i = 0; i < br->n_ports; i++) {
              struct port *port = br->ports[i];
              port_update_vlan_compat(port);
 +            port_update_bonding(port);
          }
      }
      LIST_FOR_EACH (br, struct bridge, node, &all_bridges) {
          brstp_reconfigure(br);
 +        iterate_and_prune_ifaces(br, set_iface_policing, NULL);
      }
  }
  
  static void
  bridge_pick_local_hw_addr(struct bridge *br, uint8_t ea[ETH_ADDR_LEN],
 -                          const char **devname)
 +                          struct iface **hw_addr_iface)
  {
      uint64_t requested_ea;
      size_t i, j;
      int error;
  
 -    *devname = NULL;
 +    *hw_addr_iface = NULL;
  
      /* Did the user request a particular MAC? */
      requested_ea = cfg_get_mac(0, "bridge.%s.mac", br->name);
              for (j = 0; j < port->n_ifaces; j++) {
                  struct iface *candidate = port->ifaces[j];
                  uint8_t candidate_ea[ETH_ADDR_LEN];
 -                if (!netdev_nodev_get_etheraddr(candidate->name, candidate_ea)
 +                if (!netdev_get_etheraddr(candidate->netdev, candidate_ea)
                      && eth_addr_equals(iface_ea, candidate_ea)) {
                      iface = candidate;
                  }
              }
  
              /* Grab MAC. */
 -            error = netdev_nodev_get_etheraddr(iface->name, iface_ea);
 +            error = netdev_get_etheraddr(iface->netdev, iface_ea);
              if (error) {
                  static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
                  VLOG_ERR_RL(&rl, "failed to obtain Ethernet address of %s: %s",
              memcmp(iface_ea, ea, ETH_ADDR_LEN) < 0)
          {
              memcpy(ea, iface_ea, ETH_ADDR_LEN);
 -            *devname = iface ? iface->name : NULL;
 +            *hw_addr_iface = iface;
          }
      }
      if (eth_addr_is_multicast(ea) || eth_addr_is_vif(ea)) {
          memcpy(ea, br->default_ea, ETH_ADDR_LEN);
 -        *devname = NULL;
 +        *hw_addr_iface = NULL;
          VLOG_WARN("bridge %s: using default bridge Ethernet "
                    "address "ETH_ADDR_FMT, br->name, ETH_ADDR_ARGS(ea));
      } else {
  
  /* Choose and returns the datapath ID for bridge 'br' given that the bridge
   * Ethernet address is 'bridge_ea'.  If 'bridge_ea' is the Ethernet address of
 - * a network device, then that network device's name must be passed in as
 - * 'devname'; if 'bridge_ea' was derived some other way, then 'devname' must be
 - * passed in as a null pointer. */
 + * an interface on 'br', then that interface must be passed in as
 + * 'hw_addr_iface'; if 'bridge_ea' was derived some other way, then
 + * 'hw_addr_iface' must be passed in as a null pointer. */
  static uint64_t
  bridge_pick_datapath_id(struct bridge *br,
                          const uint8_t bridge_ea[ETH_ADDR_LEN],
 -                        const char *devname)
 +                        struct iface *hw_addr_iface)
  {
      /*
       * The procedure for choosing a bridge MAC address will, in the most
          return dpid;
      }
  
 -    if (devname) {
 +    if (hw_addr_iface) {
          int vlan;
 -        if (!netdev_get_vlan_vid(devname, &vlan)) {
 +        if (!netdev_get_vlan_vid(hw_addr_iface->netdev, &vlan)) {
              /*
               * A bridge whose MAC address is taken from a VLAN network device
               * (that is, a network device created with vconfig(8) or similar
@@@ -898,26 -854,6 +899,26 @@@ bridge_flush(struct bridge *br
          mac_learning_flush(br->ml);
      }
  }
 +
 +/* Returns the 'br' interface for the ODPP_LOCAL port, or null if 'br' has no
 + * such interface. */
 +static struct iface *
 +bridge_get_local_iface(struct bridge *br)
 +{
 +    size_t i, j;
 +
 +    for (i = 0; i < br->n_ports; i++) {
 +        struct port *port = br->ports[i];
 +        for (j = 0; j < port->n_ifaces; j++) {
 +            struct iface *iface = port->ifaces[j];
 +            if (iface->dp_ifidx == ODPP_LOCAL) {
 +                return iface;
 +            }
 +        }
 +    }
 +
 +    return NULL;
 +}
  \f
  /* Bridge unixctl user interface functions. */
  static void
@@@ -960,7 -896,7 +961,7 @@@ bridge_create(const char *name
      br = xcalloc(1, sizeof *br);
  
      error = dpif_create(name, &br->dpif);
 -    if (error == EEXIST) {
 +    if (error == EEXIST || error == EBUSY) {
          error = dpif_open(name, &br->dpif);
          if (error) {
              VLOG_ERR("datapath %s already exists but cannot be opened: %s",
              free(br);
              return NULL;
          }
 -        dpif_flow_flush(&br->dpif);
 +        dpif_flow_flush(br->dpif);
      } else if (error) {
          VLOG_ERR("failed to create datapath %s: %s", name, strerror(error));
          free(br);
      error = ofproto_create(name, &bridge_ofhooks, br, &br->ofproto);
      if (error) {
          VLOG_ERR("failed to create switch %s: %s", name, strerror(error));
 -        dpif_delete(&br->dpif);
 -        dpif_close(&br->dpif);
 +        dpif_delete(br->dpif);
 +        dpif_close(br->dpif);
          free(br);
          return NULL;
      }
  
      list_push_back(&all_bridges, &br->node);
  
 -    VLOG_INFO("created bridge %s on dp%u", br->name, dpif_id(&br->dpif));
 +    VLOG_INFO("created bridge %s on %s", br->name, dpif_name(br->dpif));
  
      return br;
  }
@@@ -1011,12 -947,12 +1012,12 @@@ bridge_destroy(struct bridge *br
              port_destroy(br->ports[br->n_ports - 1]);
          }
          list_remove(&br->node);
 -        error = dpif_delete(&br->dpif);
 +        error = dpif_delete(br->dpif);
          if (error && error != ENOENT) {
 -            VLOG_ERR("failed to delete dp%u: %s",
 -                     dpif_id(&br->dpif), strerror(error));
 +            VLOG_ERR("failed to delete %s: %s",
 +                     dpif_name(br->dpif), strerror(error));
          }
 -        dpif_close(&br->dpif);
 +        dpif_close(br->dpif);
          ofproto_destroy(br->ofproto);
          free(br->controller);
          mac_learning_destroy(br->ml);
@@@ -1108,29 -1044,13 +1109,29 @@@ bridge_get_controller(const struct brid
      return controller && controller[0] ? controller : NULL;
  }
  
 +static bool
 +check_duplicate_ifaces(struct bridge *br, struct iface *iface, void *ifaces_)
 +{
 +    struct svec *ifaces = ifaces_;
 +    if (!svec_contains(ifaces, iface->name)) {
 +        svec_add(ifaces, iface->name);
 +        svec_sort(ifaces);
 +        return true;
 +    } else {
 +        VLOG_ERR("bridge %s: %s interface is on multiple ports, "
 +                 "removing from %s",
 +                 br->name, iface->name, iface->port->name);
 +        return false;
 +    }
 +}
 +
  static void
  bridge_reconfigure_one(struct bridge *br)
  {
      struct svec old_ports, new_ports, ifaces;
      struct svec listeners, old_listeners;
      struct svec snoops, old_snoops;
 -    size_t i, j;
 +    size_t i;
  
      /* Collect old ports. */
      svec_init(&old_ports);
      svec_init(&new_ports);
      cfg_get_all_keys(&new_ports, "bridge.%s.port", br->name);
      svec_sort(&new_ports);
 -    if (bridge_get_controller(br) && !svec_contains(&new_ports, br->name)) {
 -        svec_add(&new_ports, br->name);
 -        svec_sort(&new_ports);
 +    if (bridge_get_controller(br)) {
 +        char local_name[IF_NAMESIZE];
 +        int error;
 +
 +        error = dpif_port_get_name(br->dpif, ODPP_LOCAL,
 +                                   local_name, sizeof local_name);
 +        if (!error && !svec_contains(&new_ports, local_name)) {
 +            svec_add(&new_ports, local_name);
 +            svec_sort(&new_ports);
 +        }
      }
      if (!svec_is_unique(&new_ports)) {
          VLOG_WARN("bridge %s: %s specified twice as bridge port",
  
      /* Check and delete duplicate interfaces. */
      svec_init(&ifaces);
 -    for (i = 0; i < br->n_ports; ) {
 -        struct port *port = br->ports[i];
 -        for (j = 0; j < port->n_ifaces; ) {
 -            struct iface *iface = port->ifaces[j];
 -            if (svec_contains(&ifaces, iface->name)) {
 -                VLOG_ERR("bridge %s: %s interface is on multiple ports, "
 -                         "removing from %s",
 -                         br->name, iface->name, port->name);
 -                iface_destroy(iface);
 -            } else {
 -                svec_add(&ifaces, iface->name);
 -                svec_sort(&ifaces);
 -                j++;
 -            }
 -        }
 -        if (!port->n_ifaces) {
 -            VLOG_ERR("%s port has no interfaces, dropping", port->name);
 -            port_destroy(port);
 -        } else {
 -            i++;
 -        }
 -    }
 +    iterate_and_prune_ifaces(br, check_duplicate_ifaces, &ifaces);
      svec_destroy(&ifaces);
  
      /* Delete all flows if we're switching from connected to standalone or vice
@@@ -1269,8 -1203,9 +1270,8 @@@ bridge_reconfigure_controller(struct br
                                    cfg_get_string(0, "%s.accept-regex", pfx),
                                    update_resolv_conf);
          } else {
 -            struct netdev *netdev;
 +            struct iface *local_iface;
              bool in_band;
 -            int error;
  
              in_band = (!cfg_is_valid(CFG_BOOL | CFG_REQUIRED,
                                       "%s.in-band", pfx)
              ofproto_set_discovery(br->ofproto, false, NULL, NULL);
              ofproto_set_in_band(br->ofproto, in_band);
  
 -            error = netdev_open(br->name, NETDEV_ETH_TYPE_NONE, &netdev);
 -            if (!error) {
 -                if (cfg_is_valid(CFG_IP | CFG_REQUIRED, "%s.ip", pfx)) {
 -                    struct in_addr ip, mask, gateway;
 -                    ip.s_addr = cfg_get_ip(0, "%s.ip", pfx);
 -                    mask.s_addr = cfg_get_ip(0, "%s.netmask", pfx);
 -                    gateway.s_addr = cfg_get_ip(0, "%s.gateway", pfx);
 -
 -                    netdev_turn_flags_on(netdev, NETDEV_UP, true);
 -                    if (!mask.s_addr) {
 -                        mask.s_addr = guess_netmask(ip.s_addr);
 -                    }
 -                    if (!netdev_set_in4(netdev, ip, mask)) {
 -                        VLOG_INFO("bridge %s: configured IP address "IP_FMT", "
 -                                  "netmask "IP_FMT,
 -                                  br->name, IP_ARGS(&ip.s_addr),
 -                                  IP_ARGS(&mask.s_addr));
 -                    }
 +            local_iface = bridge_get_local_iface(br);
 +            if (local_iface
 +                && cfg_is_valid(CFG_IP | CFG_REQUIRED, "%s.ip", pfx)) {
 +                struct netdev *netdev = local_iface->netdev;
 +                struct in_addr ip, mask, gateway;
 +                ip.s_addr = cfg_get_ip(0, "%s.ip", pfx);
 +                mask.s_addr = cfg_get_ip(0, "%s.netmask", pfx);
 +                gateway.s_addr = cfg_get_ip(0, "%s.gateway", pfx);
 +
 +                netdev_turn_flags_on(netdev, NETDEV_UP, true);
 +                if (!mask.s_addr) {
 +                    mask.s_addr = guess_netmask(ip.s_addr);
 +                }
 +                if (!netdev_set_in4(netdev, ip, mask)) {
 +                    VLOG_INFO("bridge %s: configured IP address "IP_FMT", "
 +                              "netmask "IP_FMT,
 +                              br->name, IP_ARGS(&ip.s_addr),
 +                              IP_ARGS(&mask.s_addr));
 +                }
  
 -                    if (gateway.s_addr) {
 -                        if (!netdev_add_router(gateway)) {
 -                            VLOG_INFO("bridge %s: configured gateway "IP_FMT,
 -                                      br->name, IP_ARGS(&gateway.s_addr));
 -                        }
 +                if (gateway.s_addr) {
 +                    if (!netdev_add_router(netdev, gateway)) {
 +                        VLOG_INFO("bridge %s: configured gateway "IP_FMT,
 +                                  br->name, IP_ARGS(&gateway.s_addr));
                      }
                  }
 -                netdev_close(netdev);
              }
          }
  
@@@ -1433,17 -1369,17 +1434,17 @@@ bridge_fetch_dp_ifaces(struct bridge *b
      }
      port_array_clear(&br->ifaces);
  
 -    dpif_port_list(&br->dpif, &dpif_ports, &n_dpif_ports);
 +    dpif_port_list(br->dpif, &dpif_ports, &n_dpif_ports);
      for (i = 0; i < n_dpif_ports; i++) {
          struct odp_port *p = &dpif_ports[i];
          struct iface *iface = iface_lookup(br, p->devname);
          if (iface) {
              if (iface->dp_ifidx >= 0) {
 -                VLOG_WARN("dp%u reported interface %s twice",
 -                          dpif_id(&br->dpif), p->devname);
 +                VLOG_WARN("%s reported interface %s twice",
 +                          dpif_name(br->dpif), p->devname);
              } else if (iface_from_dp_ifidx(br, p->port)) {
 -                VLOG_WARN("dp%u reported interface %"PRIu16" twice",
 -                          dpif_id(&br->dpif), p->port);
 +                VLOG_WARN("%s reported interface %"PRIu16" twice",
 +                          dpif_name(br->dpif), p->port);
              } else {
                  port_array_set(&br->ifaces, p->port, iface);
                  iface->dp_ifidx = p->port;
@@@ -1590,6 -1526,7 +1591,7 @@@ bond_enable_slave(struct iface *iface, 
          }
          iface->tag = tag_create_random();
      }
+     port_update_bond_compat(port);
  }
  
  static void
@@@ -2055,6 -1992,7 +2057,6 @@@ bridge_port_changed_ofhook_cb(enum ofp_
  
          bridge_flush(br);
      } else {
 -        memcpy(iface->mac, opp->hw_addr, ETH_ADDR_LEN);
          if (port->n_ifaces > 1) {
              bool up = !(opp->state & OFPPS_LINK_DOWN);
              bond_link_status_update(iface, up);
@@@ -2414,10 -2352,7 +2416,7 @@@ bond_send_learning_packets(struct port 
      ofpbuf_init(&packet, 128);
      error = n_packets = n_errors = 0;
      LIST_FOR_EACH (e, struct mac_entry, lru_node, &br->ml->lrus) {
-         static const char s[] = "Open vSwitch Bond Failover";
          union ofp_action actions[2], *a;
-         struct eth_header *eth;
-         struct llc_snap_header *llc_snap;
          uint16_t dp_ifidx;
          tag_type tags = 0;
          flow_t flow;
              continue;
          }
  
-         /* Compose packet to send. */
-         ofpbuf_clear(&packet);
-         eth = ofpbuf_put_zeros(&packet, ETH_HEADER_LEN);
-         llc_snap = ofpbuf_put_zeros(&packet, LLC_SNAP_HEADER_LEN);
-         ofpbuf_put(&packet, s, sizeof s); /* Includes null byte. */
-         ofpbuf_put(&packet, e->mac, ETH_ADDR_LEN);
-         memcpy(eth->eth_dst, eth_addr_broadcast, ETH_ADDR_LEN);
-         memcpy(eth->eth_src, e->mac, ETH_ADDR_LEN);
-         eth->eth_type = htons(packet.size - ETH_HEADER_LEN);
-         llc_snap->llc.llc_dsap = LLC_DSAP_SNAP;
-         llc_snap->llc.llc_ssap = LLC_SSAP_SNAP;
-         llc_snap->llc.llc_cntl = LLC_CNTL_SNAP;
-         memcpy(llc_snap->snap.snap_org, "\x00\x23\x20", 3);
-         llc_snap->snap.snap_type = htons(0xf177); /* Random number. */
          /* Compose actions. */
          memset(actions, 0, sizeof actions);
          a = actions;
  
          /* Send packet. */
          n_packets++;
+         compose_benign_packet(&packet, "Open vSwitch Bond Failover", 0xf177,
+                               e->mac);
          flow_extract(&packet, ODPP_NONE, &flow);
          retval = ofproto_send_packet(br->ofproto, &flow, actions, a - actions,
                                       &packet);
@@@ -2751,25 -2671,6 +2735,25 @@@ bond_unixctl_disable_slave(struct unixc
      enable_slave(conn, args, false);
  }
  
 +static void
 +bond_unixctl_hash(struct unixctl_conn *conn, const char *args)
 +{
 +      uint8_t mac[ETH_ADDR_LEN];
 +      uint8_t hash;
 +      char *hash_cstr;
 +
 +      if (sscanf(args, ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(mac))
 +          == ETH_ADDR_SCAN_COUNT) {
 +              hash = bond_hash(mac);
 +
 +              hash_cstr = xasprintf("%u", hash);
 +              unixctl_command_reply(conn, 200, hash_cstr);
 +              free(hash_cstr);
 +      } else {
 +              unixctl_command_reply(conn, 501, "invalid mac");
 +      }
 +}
 +
  static void
  bond_init(void)
  {
                               bond_unixctl_set_active_slave);
      unixctl_command_register("bond/enable-slave", bond_unixctl_enable_slave);
      unixctl_command_register("bond/disable-slave", bond_unixctl_disable_slave);
 +    unixctl_command_register("bond/hash", bond_unixctl_hash);
  }
  \f
  /* Port functions. */
@@@ -3061,12 -2961,25 +3045,25 @@@ port_update_bond_compat(struct port *po
          struct iface *iface = port->ifaces[i];
          struct compat_bond_slave *slave = &bond.slaves[i];
          slave->name = iface->name;
-         slave->up = ((iface->enabled && iface->delay_expires == LLONG_MAX) ||
-                      (!iface->enabled && iface->delay_expires != LLONG_MAX));
+         /* We need to make the same determination as the Linux bonding
+          * code to determine whether a slave should be consider "up".
+          * The Linux function bond_miimon_inspect() supports four 
+          * BOND_LINK_* states:
+          *      
+          *    - BOND_LINK_UP: carrier detected, updelay has passed.
+          *    - BOND_LINK_FAIL: carrier lost, downdelay in progress.
+          *    - BOND_LINK_DOWN: carrier lost, downdelay has passed.
+          *    - BOND_LINK_BACK: carrier detected, updelay in progress.
+          *
+          * The function bond_info_show_slave() only considers BOND_LINK_UP 
+          * to be "up" and anything else to be "down".
+          */
+         slave->up = iface->enabled && iface->delay_expires == LLONG_MAX;
          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);
@@@ -3097,8 -3010,7 +3094,8 @@@ port_update_vlan_compat(struct port *po
                  && p->n_ifaces
                  && (!vlandev_name || strcmp(p->name, vlandev_name) <= 0))
              {
 -                const uint8_t *ea = p->ifaces[0]->mac;
 +                uint8_t ea[ETH_ADDR_LEN];
 +                netdev_get_etheraddr(p->ifaces[0]->netdev, ea);
                  if (!eth_addr_is_multicast(ea) &&
                      !eth_addr_is_reserved(ea) &&
                      !eth_addr_is_zero(ea)) {
@@@ -3124,7 -3036,18 +3121,7 @@@ iface_create(struct port *port, const c
      iface->dp_ifidx = -1;
      iface->tag = tag_create_random();
      iface->delay_expires = LLONG_MAX;
 -
 -    if (!cfg_get_bool(0, "iface.%s.internal", iface->name)) {
 -        netdev_nodev_get_etheraddr(name, iface->mac);
 -        netdev_nodev_get_carrier(name, &iface->enabled);
 -    } else {
 -        /* Internal interfaces are created later by the call to dpif_port_add()
 -         * in bridge_reconfigure().  Until then, we can't obtain any
 -         * information about them.  (There's no real value in doing so, anyway,
 -         * because the 'mac' and 'enabled' values are only used for interfaces
 -         * that are bond slaves, and it doesn't normally make sense to bond an
 -         * internal interface.) */
 -    }
 +    iface->netdev = NULL;
  
      if (port->n_ifaces >= port->allocated_ifaces) {
          port->ifaces = x2nrealloc(port->ifaces, &port->allocated_ifaces,
  
      VLOG_DBG("attached network device %s to port %s", iface->name, port->name);
  
 -    port_update_bonding(port);
      bridge_flush(port->bridge);
  }
  
@@@ -3156,7 -3080,6 +3153,7 @@@ iface_destroy(struct iface *iface
          del = port->ifaces[iface->port_ifidx] = port->ifaces[--port->n_ifaces];
          del->port_ifidx = iface->port_ifidx;
  
 +        netdev_close(iface->netdev);
          free(iface->name);
          free(iface);
  
              bond_send_learning_packets(port);
          }
  
 -        port_update_bonding(port);
          bridge_flush(port->bridge);
      }
  }
@@@ -3513,25 -3437,23 +3510,25 @@@ brstp_send_bpdu(struct ofpbuf *pkt, in
      if (!iface) {
          VLOG_WARN_RL(&rl, "%s: cannot send BPDU on unknown port %d",
                       br->name, port_no);
 -    } else if (eth_addr_is_zero(iface->mac)) {
 -        VLOG_WARN_RL(&rl, "%s: cannot send BPDU on port %d with unknown MAC",
 -                     br->name, port_no);
      } else {
 -        union ofp_action action;
          struct eth_header *eth = pkt->l2;
 -        flow_t flow;
  
 -        memcpy(eth->eth_src, iface->mac, ETH_ADDR_LEN);
 +        netdev_get_etheraddr(iface->netdev, eth->eth_src);
 +        if (eth_addr_is_zero(eth->eth_src)) {
 +            VLOG_WARN_RL(&rl, "%s: cannot send BPDU on port %d "
 +                         "with unknown MAC", br->name, port_no);
 +        } else {
 +            union ofp_action action;
 +            flow_t flow;
  
 -        memset(&action, 0, sizeof action);
 -        action.type = htons(OFPAT_OUTPUT);
 -        action.output.len = htons(sizeof action);
 -        action.output.port = htons(port_no);
 +            memset(&action, 0, sizeof action);
 +            action.type = htons(OFPAT_OUTPUT);
 +            action.output.len = htons(sizeof action);
 +            action.output.port = htons(port_no);
  
 -        flow_extract(pkt, ODPP_NONE, &flow);
 -        ofproto_send_packet(br->ofproto, &flow, &action, 1, pkt);
 +            flow_extract(pkt, ODPP_NONE, &flow);
 +            ofproto_send_packet(br->ofproto, &flow, &action, 1, pkt);
 +        }
      }
      ofpbuf_delete(pkt);
  }
diff --combined vswitchd/ovs-brcompatd.c
@@@ -38,6 -38,7 +38,6 @@@
  #include "coverage.h"
  #include "daemon.h"
  #include "dirs.h"
 -#include "dpif.h"
  #include "dynamic-string.h"
  #include "fatal-signal.h"
  #include "fault.h"
@@@ -303,6 -304,7 +303,6 @@@ static voi
  prune_ports(void)
  {
      int i, j;
 -    int error;
      struct svec bridges, delete;
  
      if (cfg_lock(NULL, 0)) {
          get_bridge_ifaces(br_name, &ifaces, -1);
          for (j = 0; j < ifaces.n; j++) {
              const char *iface_name = ifaces.names[j];
 -            enum netdev_flags flags;
  
              /* The local port and internal ports are created and destroyed by
               * ovs-vswitchd itself, so don't bother checking for them at all.
                  continue;
              }
  
 -            error = netdev_nodev_get_flags(iface_name, &flags);
 -            if (error == ENODEV) {
 +            if (!netdev_exists(iface_name)) {
                  VLOG_INFO_RL(&rl, "removing dead interface %s from %s",
                               iface_name, br_name);
                  svec_add(&delete, iface_name);
 -            } else if (error) {
 -                VLOG_INFO_RL(&rl, "unknown error %d on interface %s from %s",
 -                             error, iface_name, br_name);
              }
          }
          svec_destroy(&ifaces);
      svec_destroy(&delete);
  }
  
 -/* Checks whether a network device named 'name' exists and returns true if so,
 - * false otherwise.
 - *
 - * XXX it is possible that this doesn't entirely accomplish what we want in
 - * context, since ovs-vswitchd.conf may cause vswitchd to create or destroy
 - * network devices based on iface.*.internal settings.
 - *
 - * XXX may want to move this to lib/netdev.
 - *
 - * XXX why not just use netdev_nodev_get_flags() or similar function? */
 -static bool
 -netdev_exists(const char *name)
 -{
 -    struct stat s;
 -    char *filename;
 -    int error;
 -
 -    filename = xasprintf("/sys/class/net/%s", name);
 -    error = stat(filename, &s);
 -    free(filename);
 -    return !error;
 -}
 -
  static int
  add_bridge(const char *br_name)
  {
@@@ -495,7 -525,7 +495,7 @@@ del_port(const char *br_name, const cha
  {
      cfg_del_entry("bridge.%s.port=%s", br_name, port_name);
      cfg_del_match("bonding.*.slave=%s", port_name);
-     cfg_del_match("vlan.%s.*", port_name);
+     cfg_del_match("vlan.%s.[!0-9]*", port_name);
  }
  
  static int
@@@ -655,14 -685,8 +655,14 @@@ handle_fdb_query_cmd(struct ofpbuf *buf
      for (i = 0; i < ifaces.n; i++) {
          const char *iface_name = ifaces.names[i];
          struct mac *mac = &local_macs[n_local_macs];
 -        if (!netdev_nodev_get_etheraddr(iface_name, mac->addr)) {
 -            n_local_macs++;
 +        struct netdev *netdev;
 +
 +        error = netdev_open(iface_name, NETDEV_ETH_TYPE_NONE, &netdev);
 +        if (netdev) {
 +            if (!netdev_get_etheraddr(netdev, mac->addr)) {
 +                n_local_macs++;
 +            }
 +            netdev_close(netdev);
          }
      }
      svec_destroy(&ifaces);
@@@ -970,6 -994,7 +970,6 @@@ rtnl_recv_update(void
              const char *port_name = nl_attr_get_string(attrs[IFLA_IFNAME]);
              char br_name[IFNAMSIZ];
              uint32_t br_idx = nl_attr_get_u32(attrs[IFLA_MASTER]);
 -            enum netdev_flags flags;
  
              if (!if_indextoname(br_idx, br_name)) {
                  ofpbuf_delete(buf);
                  return;
              }
  
 -            if (netdev_nodev_get_flags(port_name, &flags) == ENODEV) {
 +            if (!netdev_exists(port_name)) {
                  /* Network device is really gone. */
                  struct svec ports;
  
@@@ -1091,7 -1116,6 +1091,7 @@@ main(int argc, char *argv[]
      for (;;) {
          unixctl_server_run(unixctl);
          brc_recv_update();
 +        netdev_run();
  
          /* If 'prune_timeout' is non-zero, we actively prune from the
           * config file any 'bridge.<br_name>.port' entries that are no 
  
          nl_sock_wait(brc_sock, POLLIN);
          unixctl_server_wait(unixctl);
 +        netdev_wait();
          poll_block();
      }
  
@@@ -195,8 -195,8 +195,8 @@@ MD5SUM = '/usr/bin/md5sum
  MULTIPATHD = '/sbin/multipathd'
  NETSTAT = '/bin/netstat'
  OMREPORT = '/opt/dell/srvadmin/oma/bin/omreport'
 -OVS_DPCTL = '/root/vswitch/bin/ovs-dpctl'
 -OVS_OFCTL = '/root/vswitch/bin/ovs-ofctl'
 +OVS_DPCTL = '/usr/bin/ovs-dpctl'
 +OVS_OFCTL = '/usr/bin/ovs-ofctl'
  PS = '/bin/ps'
  PVS = '/usr/sbin/pvs'
  ROUTE = '/sbin/route'
@@@ -278,6 -278,7 +278,7 @@@ CAP_SYSTEM_SERVICES      = 'system-serv
  CAP_TAPDISK_LOGS         = 'tapdisk-logs'
  CAP_VNCTERM              = 'vncterm'
  CAP_VSWITCH_CONFIG       = 'vswitch-config'
+ CAP_VSWITCH_LOGS         = 'vswitch-logs'
  CAP_VSWITCH_STATUS       = 'vswitch-status'
  CAP_WLB                  = 'wlb'
  CAP_X11_LOGS             = 'X11'
@@@ -345,6 -346,7 +346,7 @@@ cap(CAP_TAPDISK_LOGS,        PII_NO
  cap(CAP_VNCTERM,             PII_MAYBE, checked = False)
  cap(CAP_VSWITCH_CONFIG,      PII_YES,
                                          min_size=0,     max_size=20*MB)
+ cap(CAP_VSWITCH_LOGS,        PII_YES,                   max_size=20*MB)
  cap(CAP_VSWITCH_STATUS,      PII_YES,                   max_size=19*KB,
      max_time=30)
  cap(CAP_WLB,                 PII_NO,                    max_size=3*MB,
@@@ -494,7 -496,7 +496,7 @@@ def main(argv = None)
                     CAP_NETWORK_CONFIG, CAP_NETWORK_STATUS, CAP_PROCESS_LIST, CAP_HIGH_AVAILABILITY,
                     CAP_PAM, CAP_PERSISTENT_STATS, CAP_MULTIPATH,
                     CAP_SYSTEM_LOGS, CAP_SYSTEM_SERVICES, CAP_TAPDISK_LOGS,
-                    CAP_VNCTERM, CAP_VSWITCH_CONFIG, CAP_VSWITCH_STATUS, CAP_WLB, 
+                    CAP_VNCTERM, CAP_VSWITCH_CONFIG, CAP_VSWITCH_LOGS, CAP_VSWITCH_STATUS, CAP_WLB, 
                     CAP_X11_LOGS, CAP_X11_AUTH, CAP_XAPI_DEBUG, CAP_XAPI_SUBPROCESS, 
                     CAP_XENSERVER_CONFIG, CAP_XENSERVER_DOMAINS, CAP_XENSERVER_DATABASES, 
                     CAP_XENSERVER_INSTALL, CAP_XENSERVER_LOGS, CAP_XEN_INFO, CAP_XHA_LIVESET, CAP_YUM]
@@@ -709,6 -711,13 +711,13 @@@ exclude those logs from the archive
  
      file_output(CAP_VSWITCH_CONFIG, [OVS_VSWITCH_CONF])
  
+     file_output(CAP_VSWITCH_LOGS, 
+          [ VAR_LOG_DIR + x for x in
+            [ 'ovs-brcompatd.log', 'ovs-vswitchd.log', 'vswitch-cfg-update.log', 'vswitch-xsplugin.log' ] +
+            [ f % n for n in range(1, 20) \
+                  for f in ['ovs-brcompatd.log.%d', 'ovs-brcompatd.log.%d.gz', 
+                            'ovs-vswitchd.log.%d', 'ovs-vswitchd.log.%d.gz']]])
      cmd_output(CAP_VSWITCH_STATUS, [OVS_DPCTL, 'show'])
      tree_output(CAP_VSWITCH_STATUS, VSWITCH_CORE_DIR)
      for d in dp_list():