Merge commit 'origin/citrix'
authorJustin Pettit <jpettit@nicira.com>
Tue, 25 Aug 2009 20:23:11 +0000 (13:23 -0700)
committerJustin Pettit <jpettit@nicira.com>
Tue, 25 Aug 2009 20:23:11 +0000 (13:23 -0700)
142 files changed:
.gitignore
CodingStyle
INSTALL.Linux
INSTALL.OpenFlow [new file with mode: 0644]
INSTALL.SSL
INSTALL.XenServer
INSTALL.bridge [new file with mode: 0644]
Makefile.am
README
README-gcov [new file with mode: 0644]
REPORTING-BUGS [new file with mode: 0644]
SubmittingPatches [new file with mode: 0644]
acinclude.m4
configure.ac
datapath/actions.c
datapath/datapath.c
datapath/datapath.h
datapath/dp_dev.c
datapath/dp_sysfs.h
datapath/dp_sysfs_dp.c
datapath/dp_sysfs_if.c
datapath/linux-2.6/compat-2.6/include/linux/kobject.h
datapath/linux-2.6/compat-2.6/include/linux/rtnetlink.h
debian/changelog
debian/control
debian/control.modules.in
debian/openvswitch-controller.README.Debian
debian/openvswitch-monitor.default
debian/openvswitch-switch-config.templates
debian/openvswitch-switch.init
debian/openvswitch-switch.install
debian/openvswitch-switch.logrotate
debian/openvswitch-switch.manpages
debian/openvswitch-switch.template
debian/openvswitch-switchui.default
debian/ovs-switch-setup.8
debian/po/templates.pot
extras/ezio/automake.mk
extras/ezio/ovs-switchui.c
include/openflow/openflow.h
include/openvswitch/datapath-protocol.h
lib/automake.mk
lib/dhcp-client.c
lib/dpif-linux.c [new file with mode: 0644]
lib/dpif-netdev.c [new file with mode: 0644]
lib/dpif-provider.h [new file with mode: 0644]
lib/dpif.c
lib/dpif.h
lib/dpif.man
lib/hmap.h
lib/netdev-linux.c [new file with mode: 0644]
lib/netdev-provider.h [new file with mode: 0644]
lib/netdev.c
lib/netdev.h
lib/rconn.c
lib/rtnetlink.c [new file with mode: 0644]
lib/rtnetlink.h [new file with mode: 0644]
lib/shash.c
lib/shash.h
lib/socket-util.c
lib/socket-util.h
lib/unixctl.c
lib/vconn-ssl.c
lib/vconn-stream.c
lib/vconn-tcp.c
lib/vconn-unix.c
lib/vconn.c
lib/vlog-modules.def
m4/openvswitch.m4
ofproto/.gitignore [moved from secchan/.gitignore with 53% similarity]
ofproto/automake.mk [new file with mode: 0644]
ofproto/commands/automake.mk [moved from secchan/commands/automake.mk with 71% similarity]
ofproto/commands/reboot [moved from secchan/commands/reboot with 100% similarity]
ofproto/discovery.c [moved from secchan/discovery.c with 98% similarity]
ofproto/discovery.h [moved from secchan/discovery.h with 100% similarity]
ofproto/executer.c [moved from secchan/executer.c with 99% similarity]
ofproto/executer.h [moved from secchan/executer.h with 100% similarity]
ofproto/fail-open.c [moved from secchan/fail-open.c with 100% similarity]
ofproto/fail-open.h [moved from secchan/fail-open.h with 100% similarity]
ofproto/in-band.c [moved from secchan/in-band.c with 93% similarity]
ofproto/in-band.h [moved from secchan/in-band.h with 98% similarity]
ofproto/netflow.c [moved from secchan/netflow.c with 100% similarity]
ofproto/netflow.h [moved from secchan/netflow.h with 100% similarity]
ofproto/ofproto.c [moved from secchan/ofproto.c with 97% similarity]
ofproto/ofproto.h [moved from secchan/ofproto.h with 100% similarity]
ofproto/pinsched.c [moved from secchan/pinsched.c with 100% similarity]
ofproto/pinsched.h [moved from secchan/pinsched.h with 100% similarity]
ofproto/pktbuf.c [moved from secchan/pktbuf.c with 100% similarity]
ofproto/pktbuf.h [moved from secchan/pktbuf.h with 100% similarity]
ofproto/status.c [moved from secchan/status.c with 100% similarity]
ofproto/status.h [moved from secchan/status.h with 98% similarity]
secchan/automake.mk [deleted file]
tests/.gitignore
tests/atlocal.in [new file with mode: 0644]
tests/automake.mk
tests/lcov-post.at [new file with mode: 0644]
tests/lcov-pre.at [new file with mode: 0644]
tests/library.at [new file with mode: 0644]
tests/stp.at [new file with mode: 0644]
tests/test-dhcp-client.c
tests/test-flows.sh [deleted file]
tests/test-stp-ieee802.1d-1998 [deleted file]
tests/test-stp-ieee802.1d-2004-fig17.4 [deleted file]
tests/test-stp-ieee802.1d-2004-fig17.6 [deleted file]
tests/test-stp-ieee802.1d-2004-fig17.7 [deleted file]
tests/test-stp-iol-io-1.1 [deleted file]
tests/test-stp-iol-io-1.2 [deleted file]
tests/test-stp-iol-io-1.4 [deleted file]
tests/test-stp-iol-io-1.5 [deleted file]
tests/test-stp-iol-op-1.1 [deleted file]
tests/test-stp-iol-op-1.4 [deleted file]
tests/test-stp-iol-op-3.1 [deleted file]
tests/test-stp-iol-op-3.3 [deleted file]
tests/test-stp-iol-op-3.4 [deleted file]
tests/test-stp.sh [deleted file]
tests/test-vconn.c [new file with mode: 0644]
tests/testsuite.at [new file with mode: 0644]
utilities/.gitignore
utilities/automake.mk
utilities/ovs-appctl.8.in
utilities/ovs-controller.8.in
utilities/ovs-discover.8.in
utilities/ovs-discover.c
utilities/ovs-dpctl.8.in
utilities/ovs-dpctl.c
utilities/ovs-monitor
utilities/ovs-ofctl.8.in
utilities/ovs-ofctl.c
utilities/ovs-openflowd.8.in [moved from secchan/secchan.8.in with 80% similarity]
utilities/ovs-openflowd.c [moved from secchan/main.c with 98% similarity]
utilities/ovs-pki.8.in
vswitchd/automake.mk
vswitchd/bridge.c
vswitchd/ovs-brcompatd.8.in
vswitchd/ovs-brcompatd.c
vswitchd/ovs-vswitchd.8.in
vswitchd/ovs-vswitchd.c
vswitchd/ovs-vswitchd.conf.5.in
vswitchd/port.c [deleted file]
vswitchd/port.h [deleted file]
xenserver/opt_xensource_libexec_interface-reconfigure
xenserver/vswitch-xen.spec

index 480615a..3230266 100644 (file)
@@ -36,6 +36,7 @@
 /depcomp
 /install-sh
 /missing
+/package.m4
 /stamp-h1
 Module.symvers
 TAGS
index 126b45a..69df907 100644 (file)
@@ -168,7 +168,7 @@ prototype:
   Omit parameter names from function prototypes when the names do not
 give useful information, e.g.:
 
-    int netdev_get_mtu(const struct netdev *);
+    int netdev_get_mtu(const struct netdev *, int *mtup);
 
 
 STATEMENTS
index 2683940..9767f99 100644 (file)
@@ -2,8 +2,8 @@
                  ====================================
 
 This document describes how to build and install Open vSwitch on a
-generic Linux host host.  If you want to install Open vSwitch on a
-Citrix XenServer 5.5.0, see INSTALL.XenServer instead.
+generic Linux host.  If you want to install Open vSwitch on a Citrix
+XenServer version 5.5.0, see INSTALL.XenServer instead.
 
 This version of Open vSwitch should be built manually with "configure"
 and "make".  Debian packaging for Open vSwitch is also included, but
@@ -168,4 +168,4 @@ complete.
 Bug Reporting
 -------------
 
-Please report problems to ovs-bugs@openvswitch.org.
+Please report problems to bugs@openvswitch.org.
diff --git a/INSTALL.OpenFlow b/INSTALL.OpenFlow
new file mode 100644 (file)
index 0000000..a1b7799
--- /dev/null
@@ -0,0 +1,157 @@
+            Using Open vSwitch as a Simple OpenFlow Switch
+            ==============================================
+
+Open vSwitch uses OpenFlow as its preferred method of remote flow table
+configuration.  This is the simplest method of using it with an OpenFlow
+controller.  All that is required is to follow the instructions in
+INSTALL.Linux and add the bridge.<name>.controller set of parameters to the 
+ovs-vswitchd(8) configuration file as described in ovs-vswitchd.conf(5).  
+We recommend using OpenFlow in this manner.  However, it is also possible to 
+use Open vSwitch as a simple OpenFlow switch like that provided by the 
+OpenFlow reference implementation [1].  The remainder of this file describes 
+how to user it in that manner.
+
+What is OpenFlow?
+-----------------
+
+OpenFlow is a flow-based switch specification designed to enable
+researchers to run experiments in live networks.  OpenFlow is based on a
+simple Ethernet flow switch that exposes a standardized interface for
+adding and removing flow entries.
+
+An OpenFlow switch consists of three parts: (1) A "flow table" in
+which each flow entry is associated with an action telling the switch
+how to process the flow, (2) a "secure channel" that connects the switch
+to a remote process (a controller), allowing commands and packets to
+be sent between the controller and the switch, and (3) an OpenFlow
+protocol implementation, providing an open and standard way for a
+controller to talk to the switch.
+
+An OpenFlow switch can thus serve as a simple datapath element that
+forwards packets between ports according to flow actions defined by
+the controller using OpenFlow commands.  Example actions are:
+
+    - Forward this flow's packets to the given port(s)
+    - Drop this flow's packets
+    - Encapsulate and forward this flow's packets to the controller.
+
+The OpenFlow switch is defined in detail in the OpenFlow switch
+Specification [2].
+
+Installation Procedure
+----------------------
+
+The procedure below explains how to use the Open vSwitch as a simple
+OpenFlow switch. 
+
+1. Build and install the Open vSwitch kernel modules and userspace
+   programs as described in INSTALL.Linux.
+
+   It is important to run "make install", because some Open vSwitch
+   programs expect to find files in locations selected at installation
+   time.
+
+2. Load the openvswitch kernel module (which was built in step 1), e.g.:
+
+      % insmod datapath/linux-2.6/openvswitch_mod.ko
+
+   This kernel module cannot be loaded if the Linux bridge module is
+   already loaded.  Thus, you may need to remove any existing bridges
+   and unload the bridge module with "rmmod bridge" before you can do
+   this.
+
+3. Create a datapath instance.  The command below creates a datapath
+   identified as dp0 (see ovs-dpctl(8) for more detailed usage
+   information).
+
+      # ovs-dpctl add-dp dp0
+   
+   Creating datapath dp0 creates a new network device, also named dp0.
+   This network device, called the datapath's "local port", will be
+   bridged to the physical switch ports by ovs-openflowd(8).  It is
+   optionally used for in-band control as described in step 5.
+
+4. Use ovs-dpctl to attach the datapath to physical interfaces on the
+   machine.  Say, for example, you want to create a trivial 2-port
+   switch using interfaces eth1 and eth2, you would issue the following
+   commands:
+
+      # ovs-dpctl add-if dp0 eth1
+      # ovs-dpctl add-if dp0 eth2
+
+   You can verify that the interfaces were successfully added by asking
+   ovs-dpctl to print the current status of datapath dp0:
+
+      # ovs-dpctl show dp0
+
+5. Arrange so that the switch can reach the controller over the network.  
+   This can be done in two ways.  The switch may be configured for 
+   out-of-band control, which means it uses a network separate from the 
+   data traffic that it controls.  Alternatively, the switch may be 
+   configured to contact the controller over one of the network devices 
+   under its control.  In-band control is often more convenient than 
+   out-of-band, because it is not necessary to maintain two independent 
+   networks.
+
+      - If you are using out-of-band control, at this point make sure
+        that the switch machine can reach the controller over the
+        network.
+
+      - If you are using in-band control, then at this point you must
+        configure the dp0 network device created in step 3.  This
+        device is not yet bridged to any physical network (because
+        ovs-openflowd does that, and it is not yet running), so the next
+        step depends on whether connectivity is required to configure
+        the device's IP address:
+
+           * If the switch has a static IP address, you may configure
+             its IP address now, e.g.:
+
+                # ifconfig dp0 192.168.1.1
+
+           * If the switch does not have a static IP address, e.g. its
+             IP address is obtained dynamically via DHCP, then proceed
+             to the next step.  The DHCP client will not be able to 
+             contact the DHCP server until the secure channel has 
+             started.  The address will be obtained in step 7.
+
+      - If you are using in-band control with controller discovery, no
+        configuration is required at this point.  You may proceed to
+        the next step.
+
+6. Run ovs-openflowd to start the secure channel connecting the datapath to
+   a remote controller.  If the controller is running on host
+   192.168.1.2 port 6633 (the default port), the ovs-openflowd invocation
+   would look like this:
+
+      # ovs-openflowd dp0 tcp:192.168.1.2
+
+   - If you are using in-band control with controller discovery, omit
+     the second argument to the ovs-openflowd command.
+
+   - If you are using out-of-band control, add --out-of-band to the
+     command line.
+
+   Using the "tcp:<controller_ip>" argument causes the switch to connect
+   in an insecure manner.  Please see INSTALL.SSL for a description of
+   how to connect securely using SSL.
+
+7. If you are using in-band control with manual configuration, and the
+   switch obtains its IP address dynamically, then you may now obtain
+   the switch's IP address, e.g. by invoking a DHCP client.  The
+   secure channel will only be able to connect to the controller after
+   an IP address has been obtained.
+
+8. The secure channel should connect to the controller within a few
+   seconds.  It may take a little longer if controller discovery is in
+   use, because the switch must then also obtain its own IP address
+   and the controller's location via DHCP.
+
+References
+----------
+
+    [1] OpenFlow Reference Implementation.
+        <http://www.openflowswitch.org/wp/downloads/>
+
+    [2] OpenFlow Switch Specification.
+        <http://openflowswitch.org/documents/openflow-spec-latest.pdf>
index d672d62..8df47bc 100644 (file)
@@ -314,4 +314,4 @@ configured, so for security you should use only "ssl:" connections.
 Reporting Bugs
 --------------
 
-Please report problems to ovs-bugs@openvswitch.org.
+Please report problems to bugs@openvswitch.org.
index bdfc26a..83c7605 100644 (file)
@@ -67,4 +67,4 @@ rebooted as soon as possible.
 Reporting Bugs
 --------------
 
-Please report problems to ovs-bugs@openvswitch.org.
+Please report problems to bugs@openvswitch.org.
diff --git a/INSTALL.bridge b/INSTALL.bridge
new file mode 100644 (file)
index 0000000..76d180f
--- /dev/null
@@ -0,0 +1,78 @@
+              Replacing a Linux Bridge with Open vSwitch
+              ==========================================
+
+This file documents how Open vSwitch may be used as a drop-in
+replacement for a Linux kernel bridge in an environment that includes
+elements that are tightly tied to the Linux bridge tools
+(e.g. "brctl") and architecture.  We recommend directly using the
+management tools provided with Open vSwitch rather than these
+compatibility hooks for environments that are not tightly tied to the
+Linux bridging tools; they are more efficient and better reflect the
+actual operation and status.
+
+
+Installation Procedure
+----------------------
+
+The procedure below explains how to use the Open vSwitch bridge
+compatibility support.  This procedure is written from the perspective
+of a system administrator manually loading and starting Open vSwitch
+in bridge compatibility mode, but of course in practice one would want
+to update system scripts to follow these steps.
+
+1. Build and install the Open vSwitch kernel modules and userspace
+   programs as described in INSTALL.Linux.
+
+   It is important to run "make install", because some Open vSwitch
+   programs expect to find files in locations selected at installation
+   time.
+
+2. Load both the openvswitch and brcompat kernel modules (which were
+   built in step 1), e.g.:
+
+      % insmod datapath/linux-2.6/openvswitch_mod.ko
+      % insmod datapath/linux-2.6/brcompat_mod.ko
+
+   These kernel modules cannot be loaded if the Linux bridge module is
+   already loaded.  Thus, you may need to remove any existing bridges
+   and unload the bridge module with "rmmod bridge" before you can do
+   this.  In addition, if you edit your system configuration files to
+   load these modules at boot time, it should happen before any bridge
+   configuration (e.g. before any calls to "brctl" or "ifup" of any
+   bridge interfaces), to ensure that the Open vSwitch kernel modules
+   are loaded before the Linux kernel bridge module.
+
+3. Create an initial version of the configuration file, for example
+   /etc/ovs-vswitchd.conf.  This file may be empty initially or may
+   contain add any valid configuration directives described in
+   ovs-vswitchd.conf(5).  However, it must exist when you start
+   ovs-vswitchd.
+
+   To create an empty configuration file:
+
+      % touch /etc/ovs-vswitchd.conf
+
+4. Start ovs-vswitchd and ovs-brcompatd, e.g.:
+
+      % ovs-vswitchd -P -D -vANY:console:EMER /etc/ovs-vswitchd.conf
+      % ovs-brcompatd -P -D -vANY:console:EMER /etc/ovs-vswitchd.conf
+
+5. Now you should be able to manage the Open vSwitch using brctl and
+   related tools.  For example, you can create an Open vSwitch bridge,
+   add interfaces to it, then print information about bridges with the
+   commands:
+
+      % brctl addbr br0
+      % brctl addif br0 eth0
+      % brctl addif br0 eth1
+      % brctl show
+
+   Each of these commands actually uses or modifies the Open vSwitch
+   configuration file, then notifies the ovs-vswitchd daemon of the
+   change.  For example, after executing the commands above starting
+   from an empty configuration file, "cat /etc/ovs-vswitchd.conf"
+   should show that the configuration file now contains the following:
+
+      bridge.br0.port=br0
+      bridge.br0.port=eth0
+      bridge.br0.port=eth1
index 59ffc19..1b49456 100644 (file)
@@ -9,7 +9,6 @@ AUTOMAKE_OPTIONS = foreign subdir-objects
 ACLOCAL_AMFLAGS = -I m4
 SUBDIRS = datapath
 
-if ENABLE_USERSPACE
 AM_CPPFLAGS = $(SSL_CFLAGS)
 AM_CPPFLAGS += $(NCURSES_CFLAGS)
 AM_CPPFLAGS += $(PCRE_CFLAGS)
@@ -27,9 +26,11 @@ endif
 
 CLEANFILES =
 DISTCLEANFILES =
-EXTRA_DIST = INSTALL.Linux INSTALL.XenServer INSTALL.SSL
-TESTS =
-TESTS_ENVIRONMENT =
+EXTRA_DIST = INSTALL.bridge \
+       INSTALL.Linux \
+       INSTALL.OpenFlow \
+       INSTALL.SSL \
+       INSTALL.XenServer 
 bin_PROGRAMS =
 sbin_PROGRAMS =
 bin_SCRIPTS =
@@ -57,7 +58,7 @@ SUFFIXES = .in
                 -e 's,[@]PERL[@],$(PERL),g' > $@
 
 include lib/automake.mk
-include secchan/automake.mk
+include ofproto/automake.mk
 include utilities/automake.mk
 include tests/automake.mk
 include include/automake.mk
@@ -65,9 +66,4 @@ include third-party/automake.mk
 include debian/automake.mk
 include vswitchd/automake.mk
 include xenserver/automake.mk
-if HAVE_CURSES
-if HAVE_PCRE
 include extras/ezio/automake.mk
-endif
-endif
-endif # ENABLE_USERSPACE
diff --git a/README b/README
index 7311292..146795b 100644 (file)
--- a/README
+++ b/README
@@ -3,20 +3,30 @@
 What is Open vSwitch?
 ---------------------
 
-Open vSwitch is an Ethernet switch for virtual servers with the
-following features:
-
-       * NIC bonding with automatic fail-over and source MAC-based TX
-         load balancing ("SLB").
-
-       * 802.1Q VLAN support.
-
-       * Port mirroring, with optional VLAN tagging.
-
-       * NetFlow v5 flow logging.
-
-       * Connectivity to an external OpenFlow controller, such as
-          NOX.
+Open vSwitch is a multilayer software switch licensed under the open
+source Apache 2 license.  Our goal is to implement a production
+quality switch platform that supports standard management interfaces
+(e.g. NetFlow, RSPAN, ERSPAN, IOS-like CLI), and opens the forwarding
+functions to programmatic extension and control.
+
+Open vSwitch is well suited to function as a virtual switch in VM
+environments.  In addition to exposing standard control and visibility
+interfaces to the virtual networking layer, it was designed to support
+distribution across multiple physical servers.  Open vSwitch supports
+multiple Linux-based virtualization technologies including
+Xen/XenServer, KVM, and VirtualBox.
+
+The bulk of the code is written in platform-independent C and is
+easily ported to other environments.  The current release of Open
+vSwitch supports the following features:
+
+    * Visibility into inter-VM communication via NetFlow, SPAN, and RSPAN
+    * Standard 802.1Q VLAN model with trunking
+    * Per VM policing
+    * NIC bonding with source-MAC load balancing
+    * Kernel-based forwarding
+    * Support for OpenFlow
+    * Compatibility layer for the Linux bridging code
 
 Open vSwitch supports Linux 2.6.15 and up, with testing focused on
 2.6.18 with Centos and Xen patches and version 2.6.26 from kernel.org.
@@ -27,49 +37,48 @@ What's here?
 
 The main components of this distribution are:
 
-        - ovs-vswitchd, a daemon that implements the virtual switch,
-          along with a companion Linux kernel module for flow-based
-          switching.
+    * ovs-vswitchd, a daemon that implements the switch, along with 
+      a companion Linux kernel module for flow-based switching.
 
-        - ovs-brcompatd, a daemon that allows ovs-vswitchd to act as a
-          drop-in replacement for the Linux bridge in many
-          environments, along with a companion Linux kernel module to
-          intercept bridge ioctls.
+    * ovs-brcompatd, a daemon that allows ovs-vswitchd to act as a
+      drop-in replacement for the Linux bridge in many environments, 
+      along with a companion Linux kernel module to intercept bridge 
+      ioctls.
 
-       - ovs-dpctl, a tool for configuring the virtual switch kernel
-          module.
+    * ovs-dpctl, a tool for configuring the switch kernel module.
 
-        - Scripts and specs for building RPMs that allow Open vSwitch
-          to be installed on a Citrix XenServer host as a drop-in
-          replacement for its virtual switch, with additional
-          functionality.
+    * Scripts and specs for building RPMs that allow Open vSwitch
+      to be installed on a Citrix XenServer host as a drop-in
+      replacement for its switch, with additional functionality.
 
-       - vlog-appctl, a utility that can control Open vSwitch daemons,
-          adjusting their logging levels among other uses.
+    * ovs-appctl, a utility that can control Open vSwitch daemons,
+      adjusting their logging levels among other uses.
 
 Open vSwitch also provides an OpenFlow implementation and tools for
 those interested in OpenFlow but not additional Open vSwitch features:
 
-       - secchan, a program that implements a simple OpenFlow switch
-          (without the special features provided by ovs-vswitchd) using
-          the same kernel module as ovs-vswitchd.
+    * ovs-openflowd, a program that implements a simple OpenFlow
+      switch (without the special features provided by ovs-vswitchd)
+      using the same kernel module as ovs-vswitchd.
 
-       - ovs-controller, a simple OpenFlow controller.
+    * ovs-controller, a simple OpenFlow controller.
 
-       - ovs-ofctl, a utility for querying and controlling OpenFlow
-          switches and controllers.
+    * ovs-ofctl, a utility for querying and controlling OpenFlow
+      switches and controllers.
 
-       - ovs-pki, a utility for creating and managing the public-key
-          infrastructure for OpenFlow switches.
+    * ovs-pki, a utility for creating and managing the public-key
+      infrastructure for OpenFlow switches.
 
-       - A patch to tcpdump that enables it to parse OpenFlow
-          messages.
+    * A patch to tcpdump that enables it to parse OpenFlow messages.
 
 What other documentation is available?
 --------------------------------------
 
 To install Open vSwitch on a regular Linux machine, read INSTALL.Linux.
 
+To use Open vSwitch as a drop-in replacement for the Linux bridge,
+read INSTALL.bridge.
+
 To build RPMs for installing Open vSwitch on a Citrix XenServer host
 or resource pool, read INSTALL.XenServer.
 
@@ -83,5 +92,5 @@ the manpages.
 Contact 
 -------
 
-ovs-bugs@openvswitch.org
+bugs@openvswitch.org
 http://openvswitch.org/
diff --git a/README-gcov b/README-gcov
new file mode 100644 (file)
index 0000000..665a1ec
--- /dev/null
@@ -0,0 +1,31 @@
+Building with gcov support
+==========================
+
+The Open vSwitch "configure" script supports the following
+code-coverage related options:
+
+  --disable-coverage
+  --enable-coverage=no
+
+    Do not build with gcov code coverage support.
+
+    This is the default if no coverage option is passed to
+    "configure".
+
+  --enable-coverage
+  --enable-coverage=yes
+
+    Build with gcov code coverage support, but do not assume that any
+    coverage-related tools are installed and do not add special
+    coverage support to the test suite.
+
+  --enable-coverage=lcov
+
+    Build with gcov code coverage support, as above, but also add
+    support for coverage analysis to the test suite.  Running "make
+    check" will produce a directory "tests/coverage.html" in the build
+    directory with an analysis of the test suite's coverage.
+
+    This setting requires the lcov suite of utilities to be installed.
+    The "lcov" and "genhtml" programs from lcov must be in PATH.  lcov
+    is available at: http://ltp.sourceforge.net/coverage/lcov.php
diff --git a/REPORTING-BUGS b/REPORTING-BUGS
new file mode 100644 (file)
index 0000000..8412ec6
--- /dev/null
@@ -0,0 +1,53 @@
+Reporting Bugs in Open vSwitch
+==============================
+
+We are eager to hear from users about problems that they have
+encountered with Open vSwitch.  This file documents how best to report
+bugs so as to ensure that they can be fixed as quickly as possible.
+
+Please report bugs by sending email to bugs@openvswitch.org.  Include
+as much of the following information as you can in your report:
+
+        * The Open vSwitch version number (as output by "ovs-vswitchd
+          --version").
+
+        * The Git commit number (as output by "git rev-parse HEAD"),
+          if you built from a Git snapshot.
+
+        * Any local patches or changes you have applied (if any).
+
+        * The kernel version on which Open vSwitch is running (from
+          /proc/version) and the distribution and version number of
+          your OS (e.g. "Centos 5.0").
+
+        * The contents of the vswitchd configuration file (usually
+          /etc/ovs-vswitchd.conf).
+
+        * The output of "ovs-dpctl show".
+
+        * If you have Open vSwitch configured to connect to an
+          OpenFlow controller, the output of "ovs-ofctl show <bridge>"
+          for each <bridge> configured in the vswitchd configuration
+          file.
+
+        * A description of the problem, which should include:
+
+                - What you did that make the problem appear.
+
+                - What you expected to happen.
+
+                - What actually happened.
+
+        * A fix or workaround, if you have one.
+
+        * Any other information that you think might be relevant.
+
+bugs@openvswitch.org is a public mailing list, to which anyone can
+subscribe, so please do not include confidential information in your
+bug report.
+
+Contact 
+-------
+
+bugs@openvswitch.org
+http://openvswitch.org/
diff --git a/SubmittingPatches b/SubmittingPatches
new file mode 100644 (file)
index 0000000..50398f8
--- /dev/null
@@ -0,0 +1,220 @@
+How to Submit Patches for Open vSwitch
+======================================
+
+Send changes to Open vSwitch as patches to discuss@openvswitch.org.
+One patch per email, please.  More details are included below.
+
+If you are using Git, then "git format-patch" takes care of most of
+the mechanics described below for you.
+
+Before You Start
+----------------
+
+Before you send patches at all, make sure that each patch makes sense.
+In particular:
+
+        - A given patch should not break anything, even if later
+          patches fix the problems that it causes.  The source tree
+          should still build and work after each patch is applied.
+          (This enables "git bisect" to work best.)
+
+        - A patch should make one logical change.  Don't make
+          multiple, logically unconnected changes to disparate
+          subsystems in a single patch.
+
+        - A patch that adds or removes user-visible features should
+          also update the appropriate user documentation or manpages.
+
+Testing is also important:
+
+        - A patch that adds or deletes files should be tested with
+          "make distcheck" before submission.
+
+        - A patch that modifies Linux kernel code should be at least
+          build-tested on various Linux kernel versions before
+          submission.  I suggest versions 2.6.18, 2.6.27, and whatever
+          the current latest release version is at the time.
+
+        - A patch that modifies the ofproto or vswitchd code should be
+          tested in at least simple cases before submission.
+
+        - A patch that modifies xenserver code should be tested on
+          XenServer before submission.
+
+Email Subject
+-------------
+
+The subject line of your email should be in the following format:
+[PATCH <n>/<m>] <area>: <summary>
+
+        - [PATCH <n>/<m>] indicates that this is the nth of a series
+          of m patches.  It helps reviewers to read patches in the
+          correct order.  You may omit this prefix if you are sending
+          only one patch.
+
+        - <area>: indicates the area of the Open vSwitch to which the
+          change applies (often the name of a source file or a
+          directory).  You may omit it if the change crosses multiple
+          distinct pieces of code.
+
+        - <summary> briefly describes the change.
+
+The subject, minus the [PATCH <n>/<m>] prefix, becomes the first line
+of the commit's change log message.
+
+Description
+-----------
+
+The body of the email should start with a more thorough description of
+the change.  This becomes the body of the commit message, following
+the subject.  There is no need to duplicate the summary given in the
+subject.
+
+Please limit lines in the description to 79 characters in width.
+
+The description should include:
+
+        - The rationale for the change.
+
+        - Design description and rationale (but this might be better
+          added as code comments).
+
+        - Testing that you performed (or testing that should be done
+          but you could not for whatever reason).
+
+There is no need to describe what the patch actually changed, if the
+reader can see it for himself.
+
+If the patch refers to a commit already in the Open vSwitch
+repository, please include both the commit number and the subject of
+the patch, e.g. 'commit 632d136c "vswitch: Remove restriction on
+datapath names."'.
+
+If you, the person sending the patch, did not write the patch
+yourself, then the very first line of the body should take the form
+"From: <author name> <author email>", followed by a blank line.  This
+will automatically cause the named author to be credited with
+authorship in the repository.  If others contributed to the patch, but
+are not the main authors, then please credit them as part of the
+description (e.g. "Thanks to Bob J. User for reporting this bug.").
+
+Comments
+--------
+
+If you want to include any comments in your email that should not be
+part of the commit's change log message, put them after the
+description, separated by a line that contains just "---".  It may be
+helpful to include a diffstat here for changes that touch multiple
+files.
+
+Patch
+-----
+
+The patch should be in the body of the email following the descrition,
+separated by a blank line.
+
+Patches should be in "diff -up" format.  We recommend that you use Git
+to produce your patches, in which case you should use the -M -C
+options to "git diff" (or other Git tools) if your patch renames or
+copies files.  Quilt (http://savannah.nongnu.org/projects/quilt) might
+be useful if you do not want to use Git.
+
+Patches should be inline in the email message.  Some email clients
+corrupt white space or wrap lines in patches.  There are hints on how
+to configure many email clients to avoid this problem at:
+        http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=blob_plain;f=Documentation/email-clients.txt
+If you cannot convince your email client not to mangle patches, then
+sending the patch as an attachment is a second choice.
+
+Please follow the style used in the code that you are modifying.  The
+CodingStyle file describes the coding style used in most of Open
+vSwitch.  Use Linux kernel coding style for Linux kernel code.
+
+Example
+-------
+
+From 632d136c7b108cd3d39a2e64fe6230e23977caf8 Mon Sep 17 00:00:00 2001
+From: Ben Pfaff <blp@nicira.com>
+Date: Mon, 6 Jul 2009 10:17:54 -0700
+Subject: [PATCH] vswitch: Remove restriction on datapath names.
+
+Commit f4b96c92c "vswitch: Disallow bridges named "dpN" or "nl:N"" disabled
+naming bridges "dpN" because the vswitchd code made the bad assumption that
+the bridge's local port has the same name as the bridge, which was not
+true (at the time) for bridges named dpN.  Now that assumption has been
+eliminated, so this commit eliminates the restriction too.
+
+This change is also a cleanup in that it eliminates one form of the
+vswitch's dependence on specifics of the dpif implementation.
+---
+ vswitchd/bridge.c               |   23 +++++------------------
+ vswitchd/ovs-vswitchd.conf.5.in |    3 +--
+ 2 files changed, 6 insertions(+), 20 deletions(-)
+
+diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c
+index 32647ea..00cffbc 100644
+--- a/vswitchd/bridge.c
++++ b/vswitchd/bridge.c
+@@ -351,32 +351,19 @@ bridge_configure_ssl(void)
+ 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;
+     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])) {
+-            VLOG_ERR("%s is not a valid bridge name (bridges may not be "
+-                     "named \"dp\" 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);
+@@ -793,7 +780,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",
+diff --git a/vswitchd/ovs-vswitchd.conf.5.in b/vswitchd/ovs-vswitchd.conf.5.in
+index 5483ad5..d82a08a 100644
+--- a/vswitchd/ovs-vswitchd.conf.5.in
++++ b/vswitchd/ovs-vswitchd.conf.5.in
+@@ -50,8 +50,7 @@ configure \fBovs\-vswitchd\fR.
+ .SS "Bridge Configuration"
+ A bridge (switch) with a given \fIname\fR is configured by specifying
+ the names of its network devices as values for key
+-\fBbridge.\fIname\fB.port\fR.  (The specified \fIname\fR may not begin
+-with \fBdp\fR followed by a digit.)
++\fBbridge.\fIname\fB.port\fR.
+ .PP
+ The names given on \fBbridge.\fIname\fB.port\fR must be the names of
+ existing network devices, except for ``internal ports.''  An internal
+-- 
+1.6.3.3
+
index 4294920..2f38997 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-dnl Checks for --disable-userspace.
-AC_DEFUN([OVS_CHECK_USERSPACE],
-  [AC_ARG_ENABLE(
-     [userspace],
-     [AC_HELP_STRING([--disable-userspace], 
-                     [Disable building userspace components.])],
-     [case "${enableval}" in
-        (yes) build_userspace=true ;;
-        (no)  build_userspace=false ;;
-        (*) AC_MSG_ERROR([bad value ${enableval} for --enable-userspace]) ;;
-      esac],
-     [build_userspace=true])
-   AM_CONDITIONAL([ENABLE_USERSPACE], [$build_userspace])])
-
 dnl OVS_CHECK_LINUX(OPTION, VERSION, VARIABLE, CONDITIONAL)
 dnl
 dnl Configure linux kernel source tree 
index 7f150f4..d674ab4 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-AC_PREREQ(2.60)
-AC_INIT(openvswitch, 0.90.4, ovs-bugs@openvswitch.org)
+AC_PREREQ(2.63)
+AC_INIT(openvswitch, 0.90.3, 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
@@ -37,7 +38,7 @@ AC_USE_SYSTEM_EXTENSIONS
 AC_C_BIGENDIAN
 AC_SYS_LARGEFILE
 
-OVS_CHECK_USERSPACE
+OVS_CHECK_COVERAGE
 OVS_CHECK_NDEBUG
 OVS_CHECK_NETLINK
 OVS_CHECK_OPENSSL
@@ -48,33 +49,31 @@ OVS_CHECK_PCRE
 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,6 +83,7 @@ AC_CONFIG_FILES([Makefile
 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
index a037e43..8a3e8ab 100644 (file)
@@ -96,7 +96,7 @@ modify_vlan_tci(struct datapath *dp, struct sk_buff *skb,
                 * when we send the packet out on the wire, and it will fail at
                 * that point because skb_checksum_setup() will not look inside
                 * an 802.1Q header. */
-               skb_checksum_setup(skb);
+               vswitch_skb_checksum_setup(skb);
 
                /* GSO is not implemented for packets with an 802.1Q header, so
                 * we have to do segmentation before we add that header.
@@ -361,7 +361,7 @@ int execute_actions(struct datapath *dp, struct sk_buff *skb,
         * then freeing the original skbuff is wasteful.  So the following code
         * is slightly obscure just to avoid that. */
        int prev_port = -1;
-       int err = 0;
+       int err;
        for (; n_actions > 0; a++, n_actions--) {
                WARN_ON_ONCE(skb_shared(skb));
                if (prev_port != -1) {
@@ -420,5 +420,5 @@ int execute_actions(struct datapath *dp, struct sk_buff *skb,
                do_output(dp, skb, prev_port);
        else
                kfree_skb(skb);
-       return err;
+       return 0;
 }
index 5092ced..e0b03d9 100644 (file)
@@ -165,7 +165,8 @@ static void dp_ifinfo_notify(int event, struct net_bridge_port *port)
                kfree_skb(skb);
                goto errout;
        }
-       err = rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, GFP_KERNEL);
+       rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, GFP_KERNEL);
+       return;
 errout:
        if (err < 0)
                rtnl_set_sk_err(net, RTNLGRP_LINK, err);
@@ -224,9 +225,7 @@ static int create_dp(int dp_idx, const char __user *devnamep)
 
        /* Initialize kobject for bridge.  This will be added as
         * /sys/class/net/<devname>/brif later, if sysfs is enabled. */
-       kobject_set_name(&dp->ifobj, SYSFS_BRIDGE_PORT_SUBDIR); /* "brif" */
        dp->ifobj.kset = NULL;
-       dp->ifobj.parent = NULL;
        kobject_init(&dp->ifobj, &dp_ktype);
 
        /* Allocate table. */
@@ -256,9 +255,7 @@ static int create_dp(int dp_idx, const char __user *devnamep)
        mutex_unlock(&dp_mutex);
        rtnl_unlock();
 
-#ifdef SUPPORT_SYSFS
        dp_sysfs_add_dp(dp);
-#endif
 
        return 0;
 
@@ -286,9 +283,7 @@ static void do_destroy_dp(struct datapath *dp)
                if (p->port_no != ODPP_LOCAL)
                        dp_del_port(p);
 
-#ifdef SUPPORT_SYSFS
        dp_sysfs_del_dp(dp);
-#endif
 
        rcu_assign_pointer(dps[dp->dp_idx], NULL);
 
@@ -333,7 +328,7 @@ static void release_nbp(struct kobject *kobj)
 }
 
 struct kobj_type brport_ktype = {
-#ifdef SUPPORT_SYSFS
+#ifdef CONFIG_SYSFS
        .sysfs_ops = &brport_sysfs_ops,
 #endif
        .release = release_nbp
@@ -370,9 +365,7 @@ static int new_nbp(struct datapath *dp, struct net_device *dev, int port_no)
 
        /* Initialize kobject for bridge.  This will be added as
         * /sys/class/net/<devname>/brport later, if sysfs is enabled. */
-       kobject_set_name(&p->kobj, SYSFS_BRIDGE_PORT_ATTR); /* "brport" */
        p->kobj.kset = NULL;
-       p->kobj.parent = &p->dev->NETDEV_DEV_MEMBER.kobj;
        kobject_init(&p->kobj, &brport_ktype);
 
        dp_ifinfo_notify(RTM_NEWLINK, p);
@@ -392,11 +385,6 @@ static int add_port(int dp_idx, struct odp_port __user *portp)
        if (copy_from_user(&port, portp, sizeof port))
                goto out;
        port.devname[IFNAMSIZ - 1] = '\0';
-       port_no = port.port;
-
-       err = -EINVAL;
-       if (port_no < 0 || port_no >= DP_MAX_PORTS)
-               goto out;
 
        rtnl_lock();
        dp = get_dp_locked(dp_idx);
@@ -404,10 +392,13 @@ static int add_port(int dp_idx, struct odp_port __user *portp)
        if (!dp)
                goto out_unlock_rtnl;
 
-       err = -EEXIST;
-       if (dp->ports[port_no])
-               goto out_unlock_dp;
+       for (port_no = 1; port_no < DP_MAX_PORTS; port_no++)
+               if (!dp->ports[port_no])
+                       goto got_port_no;
+       err = -EXFULL;
+       goto out_unlock_dp;
 
+got_port_no:
        if (!(port.flags & ODP_PORT_INTERNAL)) {
                err = -ENODEV;
                dev = dev_get_by_name(&init_net, port.devname);
@@ -430,9 +421,9 @@ static int add_port(int dp_idx, struct odp_port __user *portp)
        if (err)
                goto out_put;
 
-#ifdef SUPPORT_SYSFS
        dp_sysfs_add_if(dp->ports[port_no]);
-#endif
+
+       err = __put_user(port_no, &port.port);
 
 out_put:
        dev_put(dev);
@@ -448,10 +439,8 @@ int dp_del_port(struct net_bridge_port *p)
 {
        ASSERT_RTNL();
 
-#ifdef SUPPORT_SYSFS
        if (p->port_no != ODPP_LOCAL)
                dp_sysfs_del_if(p);
-#endif
        dp_ifinfo_notify(RTM_DELLINK, p);
 
        p->dp->n_ports--;
@@ -587,7 +576,7 @@ static int dp_frame_hook(struct net_bridge_port *p, struct sk_buff **pskb)
 #error
 #endif
 
-#ifdef CONFIG_XEN
+#if defined(CONFIG_XEN) && LINUX_VERSION_CODE == KERNEL_VERSION(2,6,18)
 /* This code is copied verbatim from net/dev/core.c in Xen's
  * linux-2.6.18-92.1.10.el5.xs5.0.0.394.644.  We can't call those functions
  * directly because they aren't exported. */
@@ -603,7 +592,7 @@ static int skb_pull_up_to(struct sk_buff *skb, void *ptr)
        }
 }
 
-int skb_checksum_setup(struct sk_buff *skb)
+int vswitch_skb_checksum_setup(struct sk_buff *skb)
 {
        if (skb->proto_csum_blank) {
                if (skb->protocol != htons(ETH_P_IP))
@@ -634,7 +623,9 @@ int skb_checksum_setup(struct sk_buff *skb)
 out:
        return -EPROTO;
 }
-#endif
+#else
+int vswitch_skb_checksum_setup(struct sk_buff *skb) { return 0; }
+#endif /* CONFIG_XEN && linux == 2.6.18 */
 
 int
 dp_output_control(struct datapath *dp, struct sk_buff *skb, int queue_no,
@@ -660,7 +651,7 @@ dp_output_control(struct datapath *dp, struct sk_buff *skb, int queue_no,
         * the non-Xen case, but it is difficult to trigger or test this case
         * there, hence the WARN_ON_ONCE().
         */
-       err = skb_checksum_setup(skb);
+       err = vswitch_skb_checksum_setup(skb);
        if (err)
                goto err_kfree_skb;
 #ifndef CHECKSUM_HW
@@ -836,6 +827,7 @@ static void get_stats(struct sw_flow *flow, struct odp_flow_stats *stats)
        stats->n_bytes = flow->byte_count;
        stats->ip_tos = flow->ip_tos;
        stats->tcp_flags = flow->tcp_flags;
+       stats->error = 0;
 }
 
 static void clear_stats(struct sw_flow *flow)
@@ -964,8 +956,6 @@ static int put_actions(const struct sw_flow *flow, struct odp_flow __user *ufp)
 
        if (!n_actions)
                return 0;
-       if (ufp->n_actions > INT_MAX / sizeof(union odp_action))
-               return -EINVAL;
 
        sf_acts = rcu_dereference(flow->sf_acts);
        if (__put_user(sf_acts->n_actions, &ufp->n_actions) ||
@@ -991,9 +981,7 @@ static int answer_query(struct sw_flow *flow, struct odp_flow __user *ufp)
        return put_actions(flow, ufp);
 }
 
-static int del_or_query_flow(struct datapath *dp,
-                            struct odp_flow __user *ufp,
-                            unsigned int cmd)
+static int del_flow(struct datapath *dp, struct odp_flow __user *ufp)
 {
        struct dp_table *table = rcu_dereference(dp->table);
        struct odp_flow uf;
@@ -1010,29 +998,24 @@ static int del_or_query_flow(struct datapath *dp,
        if (!flow)
                goto error;
 
-       if (cmd == ODP_FLOW_DEL) {
-               /* XXX redundant lookup */
-               error = dp_table_delete(table, flow);
-               if (error)
-                       goto error;
+       /* XXX redundant lookup */
+       error = dp_table_delete(table, flow);
+       if (error)
+               goto error;
 
-               /* XXX These statistics might lose a few packets, since other
-                * CPUs can be using this flow.  We used to synchronize_rcu()
-                * to make sure that we get completely accurate stats, but that
-                * blows our performance, badly. */
-               dp->n_flows--;
-               error = answer_query(flow, ufp);
-               flow_deferred_free(flow);
-       } else {
-               error = answer_query(flow, ufp);
-       }
+       /* XXX These statistics might lose a few packets, since other CPUs can
+        * be using this flow.  We used to synchronize_rcu() to make sure that
+        * we get completely accurate stats, but that blows our performance,
+        * badly. */
+       dp->n_flows--;
+       error = answer_query(flow, ufp);
+       flow_deferred_free(flow);
 
 error:
        return error;
 }
 
-static int query_multiple_flows(struct datapath *dp,
-                               const struct odp_flowvec *flowvec)
+static int query_flows(struct datapath *dp, const struct odp_flowvec *flowvec)
 {
        struct dp_table *table = rcu_dereference(dp->table);
        int i;
@@ -1048,7 +1031,7 @@ static int query_multiple_flows(struct datapath *dp,
 
                flow = dp_table_lookup(table, &uf.key);
                if (!flow)
-                       error = __clear_user(&ufp->stats, sizeof ufp->stats);
+                       error = __put_user(ENOENT, &ufp->stats.error);
                else
                        error = answer_query(flow, ufp);
                if (error)
@@ -1181,8 +1164,7 @@ error:
        return err;
 }
 
-static int
-get_dp_stats(struct datapath *dp, struct odp_stats __user *statsp)
+static int get_dp_stats(struct datapath *dp, struct odp_stats __user *statsp)
 {
        struct odp_stats stats;
        int i;
@@ -1297,7 +1279,7 @@ list_ports(struct datapath *dp, struct odp_portvec __user *pvp)
                                break;
                }
        }
-       return put_user(idx, &pvp->n_ports);
+       return put_user(dp->n_ports, &pvp->n_ports);
 }
 
 /* RCU callback for freeing a dp_port_group */
@@ -1381,24 +1363,28 @@ static long openvswitch_ioctl(struct file *f, unsigned int cmd,
        /* Handle commands with special locking requirements up front. */
        switch (cmd) {
        case ODP_DP_CREATE:
-               return create_dp(dp_idx, (char __user *)argp);
+               err = create_dp(dp_idx, (char __user *)argp);
+               goto exit;
 
        case ODP_DP_DESTROY:
-               return destroy_dp(dp_idx);
+               err = destroy_dp(dp_idx);
+               goto exit;
 
        case ODP_PORT_ADD:
-               return add_port(dp_idx, (struct odp_port __user *)argp);
+               err = add_port(dp_idx, (struct odp_port __user *)argp);
+               goto exit;
 
        case ODP_PORT_DEL:
                err = get_user(port_no, (int __user *)argp);
-               if (err)
-                       break;
-               return del_port(dp_idx, port_no);
+               if (!err)
+                       err = del_port(dp_idx, port_no);
+               goto exit;
        }
 
        dp = get_dp_locked(dp_idx);
+       err = -ENODEV;
        if (!dp)
-               return -ENODEV;
+               goto exit;
 
        switch (cmd) {
        case ODP_DP_STATS:
@@ -1460,13 +1446,11 @@ static long openvswitch_ioctl(struct file *f, unsigned int cmd,
                break;
 
        case ODP_FLOW_DEL:
-       case ODP_FLOW_GET:
-               err = del_or_query_flow(dp, (struct odp_flow __user *)argp,
-                                       cmd);
+               err = del_flow(dp, (struct odp_flow __user *)argp);
                break;
 
-       case ODP_FLOW_GET_MULTIPLE:
-               err = do_flowvec_ioctl(dp, argp, query_multiple_flows);
+       case ODP_FLOW_GET:
+               err = do_flowvec_ioctl(dp, argp, query_flows);
                break;
 
        case ODP_FLOW_LIST:
@@ -1482,6 +1466,7 @@ static long openvswitch_ioctl(struct file *f, unsigned int cmd,
                break;
        }
        mutex_unlock(&dp->mutex);
+exit:
        return err;
 }
 
index 62c79d4..b520084 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/netdevice.h>
 #include <linux/workqueue.h>
 #include <linux/skbuff.h>
+#include <linux/version.h>
 #include "flow.h"
 #include "dp_sysfs.h"
 
@@ -133,4 +134,6 @@ static inline int skb_checksum_setup(struct sk_buff *skb)
 }
 #endif
 
+int vswitch_skb_checksum_setup(struct sk_buff *skb);
+
 #endif /* datapath.h */
index b0ba301..008f3f6 100644 (file)
@@ -118,7 +118,7 @@ static void dp_getinfo(struct net_device *netdev, struct ethtool_drvinfo *info)
 {
        struct dp_dev *dp_dev = dp_dev_priv(netdev);
        strcpy(info->driver, "openvswitch");
-       sprintf(info->bus_info, "%d", dp_dev->dp->dp_idx);
+       sprintf(info->bus_info, "%d.%d", dp_dev->dp->dp_idx, dp_dev->port_no);
 }
 
 static struct ethtool_ops dp_ethtool_ops = {
index c0ac01b..be044ea 100644 (file)
@@ -20,16 +20,7 @@ int dp_sysfs_del_dp(struct datapath *dp);
 int dp_sysfs_add_if(struct net_bridge_port *p);
 int dp_sysfs_del_if(struct net_bridge_port *p);
 
-#include <linux/version.h>
-#if LINUX_VERSION_CODE == KERNEL_VERSION(2,6,18)
-#define SUPPORT_SYSFS 1
-#else
-/* We only support sysfs on Linux 2.6.18 because that's the only place we
- * really need it (on Xen, for brcompat) and it's a big pain to try to support
- * multiple versions. */
-#endif
-
-#ifdef SUPPORT_SYSFS
+#ifdef CONFIG_SYSFS
 extern struct sysfs_ops brport_sysfs_ops;
 #endif
 
index 503f22e..3cd6d1a 100644 (file)
 #include "datapath.h"
 #include "dp_dev.h"
 
-#ifdef SUPPORT_SYSFS
+#ifdef CONFIG_SYSFS
 #define to_dev(obj)    container_of(obj, struct device, kobj)
 
 /* Hack to attempt to build on more platforms. */
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)
-#define to_kobj(d) &(d)->class_dev.kobj
 #define DP_DEVICE_ATTR CLASS_DEVICE_ATTR
+#define DEVICE_PARAMS struct class_device *d
+#define DEVICE_ARGS d
+#define DEV_ATTR(NAME) class_device_attr_##NAME
 #else
-#define to_kobj(d) &(d)->dev.kobj
 #define DP_DEVICE_ATTR DEVICE_ATTR
+#define DEVICE_PARAMS struct device *d, struct device_attribute *attr
+#define DEVICE_ARGS d, attr
+#define DEV_ATTR(NAME) dev_attr_##NAME
 #endif
 
 /*
  * Common code for storing bridge parameters.
  */
-static ssize_t store_bridge_parm(struct class_device *d,
+static ssize_t store_bridge_parm(DEVICE_PARAMS,
                                 const char *buf, size_t len,
                                 void (*set)(struct datapath *, unsigned long))
 {
@@ -76,8 +80,7 @@ static ssize_t store_bridge_parm(struct class_device *d,
 }
 
 
-static ssize_t show_forward_delay(struct class_device *d,
-                                 char *buf)
+static ssize_t show_forward_delay(DEVICE_PARAMS, char *buf)
 {
 #if 0
        struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
@@ -99,15 +102,15 @@ static void set_forward_delay(struct datapath *dp, unsigned long val)
 #endif
 }
 
-static ssize_t store_forward_delay(struct class_device *d,
+static ssize_t store_forward_delay(DEVICE_PARAMS,
                                   const char *buf, size_t len)
 {
-       return store_bridge_parm(d, buf, len, set_forward_delay);
+       return store_bridge_parm(DEVICE_ARGS, buf, len, set_forward_delay);
 }
 static DP_DEVICE_ATTR(forward_delay, S_IRUGO | S_IWUSR,
                   show_forward_delay, store_forward_delay);
 
-static ssize_t show_hello_time(struct class_device *d, char *buf)
+static ssize_t show_hello_time(DEVICE_PARAMS, char *buf)
 {
 #if 0
        return sprintf(buf, "%lu\n",
@@ -129,17 +132,16 @@ static void set_hello_time(struct datapath *dp, unsigned long val)
 #endif
 }
 
-static ssize_t store_hello_time(struct class_device *d,
+static ssize_t store_hello_time(DEVICE_PARAMS,
                                const char *buf,
                                size_t len)
 {
-       return store_bridge_parm(d, buf, len, set_hello_time);
+       return store_bridge_parm(DEVICE_ARGS, buf, len, set_hello_time);
 }
 static DP_DEVICE_ATTR(hello_time, S_IRUGO | S_IWUSR, show_hello_time,
                   store_hello_time);
 
-static ssize_t show_max_age(struct class_device *d, 
-                           char *buf)
+static ssize_t show_max_age(DEVICE_PARAMS, char *buf)
 {
 #if 0
        return sprintf(buf, "%lu\n",
@@ -161,15 +163,14 @@ static void set_max_age(struct datapath *dp, unsigned long val)
 #endif
 }
 
-static ssize_t store_max_age(struct class_device *d, 
+static ssize_t store_max_age(DEVICE_PARAMS,
                             const char *buf, size_t len)
 {
-       return store_bridge_parm(d, buf, len, set_max_age);
+       return store_bridge_parm(DEVICE_ARGS, buf, len, set_max_age);
 }
 static DP_DEVICE_ATTR(max_age, S_IRUGO | S_IWUSR, show_max_age, store_max_age);
 
-static ssize_t show_ageing_time(struct class_device *d,
-                               char *buf)
+static ssize_t show_ageing_time(DEVICE_PARAMS, char *buf)
 {
 #if 0
        struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
@@ -188,16 +189,15 @@ static void set_ageing_time(struct datapath *dp, unsigned long val)
 #endif
 }
 
-static ssize_t store_ageing_time(struct class_device *d,
+static ssize_t store_ageing_time(DEVICE_PARAMS,
                                 const char *buf, size_t len)
 {
-       return store_bridge_parm(d, buf, len, set_ageing_time);
+       return store_bridge_parm(DEVICE_ARGS, buf, len, set_ageing_time);
 }
 static DP_DEVICE_ATTR(ageing_time, S_IRUGO | S_IWUSR, show_ageing_time,
                   store_ageing_time);
 
-static ssize_t show_stp_state(struct class_device *d,
-                             char *buf)
+static ssize_t show_stp_state(DEVICE_PARAMS, char *buf)
 {
 #if 0
        struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
@@ -208,7 +208,7 @@ static ssize_t show_stp_state(struct class_device *d,
 }
 
 
-static ssize_t store_stp_state(struct class_device *d,
+static ssize_t store_stp_state(DEVICE_PARAMS,
                               const char *buf,
                               size_t len)
 {
@@ -236,8 +236,7 @@ static ssize_t store_stp_state(struct class_device *d,
 static DP_DEVICE_ATTR(stp_state, S_IRUGO | S_IWUSR, show_stp_state,
                   store_stp_state);
 
-static ssize_t show_priority(struct class_device *d, 
-                            char *buf)
+static ssize_t show_priority(DEVICE_PARAMS, char *buf)
 {
 #if 0
        struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
@@ -257,15 +256,14 @@ static void set_priority(struct datapath *dp, unsigned long val)
 #endif
 }
 
-static ssize_t store_priority(struct class_device *d, 
+static ssize_t store_priority(DEVICE_PARAMS,
                               const char *buf, size_t len)
 {
-       return store_bridge_parm(d, buf, len, set_priority);
+       return store_bridge_parm(DEVICE_ARGS, buf, len, set_priority);
 }
 static DP_DEVICE_ATTR(priority, S_IRUGO | S_IWUSR, show_priority, store_priority);
 
-static ssize_t show_root_id(struct class_device *d, 
-                           char *buf)
+static ssize_t show_root_id(DEVICE_PARAMS, char *buf)
 {
 #if 0
        return br_show_bridge_id(buf, &to_bridge(d)->designated_root);
@@ -275,8 +273,7 @@ static ssize_t show_root_id(struct class_device *d,
 }
 static DP_DEVICE_ATTR(root_id, S_IRUGO, show_root_id, NULL);
 
-static ssize_t show_bridge_id(struct class_device *d, 
-                             char *buf)
+static ssize_t show_bridge_id(DEVICE_PARAMS, char *buf)
 {
        struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
        const unsigned char *addr = dp->ports[ODPP_LOCAL]->dev->dev_addr;
@@ -287,8 +284,7 @@ static ssize_t show_bridge_id(struct class_device *d,
 }
 static DP_DEVICE_ATTR(bridge_id, S_IRUGO, show_bridge_id, NULL);
 
-static ssize_t show_root_port(struct class_device *d, 
-                             char *buf)
+static ssize_t show_root_port(DEVICE_PARAMS, char *buf)
 {
 #if 0
        return sprintf(buf, "%d\n", to_bridge(d)->root_port);
@@ -298,8 +294,7 @@ static ssize_t show_root_port(struct class_device *d,
 }
 static DP_DEVICE_ATTR(root_port, S_IRUGO, show_root_port, NULL);
 
-static ssize_t show_root_path_cost(struct class_device *d,
-                                  char *buf)
+static ssize_t show_root_path_cost(DEVICE_PARAMS, char *buf)
 {
 #if 0
        return sprintf(buf, "%d\n", to_bridge(d)->root_path_cost);
@@ -309,8 +304,7 @@ static ssize_t show_root_path_cost(struct class_device *d,
 }
 static DP_DEVICE_ATTR(root_path_cost, S_IRUGO, show_root_path_cost, NULL);
 
-static ssize_t show_topology_change(struct class_device *d,
-                                   char *buf)
+static ssize_t show_topology_change(DEVICE_PARAMS, char *buf)
 {
 #if 0
        return sprintf(buf, "%d\n", to_bridge(d)->topology_change);
@@ -320,8 +314,7 @@ static ssize_t show_topology_change(struct class_device *d,
 }
 static DP_DEVICE_ATTR(topology_change, S_IRUGO, show_topology_change, NULL);
 
-static ssize_t show_topology_change_detected(struct class_device *d,
-                                            char *buf)
+static ssize_t show_topology_change_detected(DEVICE_PARAMS, char *buf)
 {
 #if 0
        struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
@@ -333,8 +326,7 @@ static ssize_t show_topology_change_detected(struct class_device *d,
 static DP_DEVICE_ATTR(topology_change_detected, S_IRUGO,
                   show_topology_change_detected, NULL);
 
-static ssize_t show_hello_timer(struct class_device *d,
-                               char *buf)
+static ssize_t show_hello_timer(DEVICE_PARAMS, char *buf)
 {
 #if 0
        struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
@@ -345,8 +337,7 @@ static ssize_t show_hello_timer(struct class_device *d,
 }
 static DP_DEVICE_ATTR(hello_timer, S_IRUGO, show_hello_timer, NULL);
 
-static ssize_t show_tcn_timer(struct class_device *d, 
-                             char *buf)
+static ssize_t show_tcn_timer(DEVICE_PARAMS, char *buf)
 {
 #if 0
        struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
@@ -357,8 +348,7 @@ static ssize_t show_tcn_timer(struct class_device *d,
 }
 static DP_DEVICE_ATTR(tcn_timer, S_IRUGO, show_tcn_timer, NULL);
 
-static ssize_t show_topology_change_timer(struct class_device *d,
-                                         char *buf)
+static ssize_t show_topology_change_timer(DEVICE_PARAMS, char *buf)
 {
 #if 0
        struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
@@ -370,8 +360,7 @@ static ssize_t show_topology_change_timer(struct class_device *d,
 static DP_DEVICE_ATTR(topology_change_timer, S_IRUGO, show_topology_change_timer,
                   NULL);
 
-static ssize_t show_gc_timer(struct class_device *d, 
-                            char *buf)
+static ssize_t show_gc_timer(DEVICE_PARAMS, char *buf)
 {
 #if 0
        struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
@@ -382,8 +371,7 @@ static ssize_t show_gc_timer(struct class_device *d,
 }
 static DP_DEVICE_ATTR(gc_timer, S_IRUGO, show_gc_timer, NULL);
 
-static ssize_t show_group_addr(struct class_device *d,
-                              char *buf)
+static ssize_t show_group_addr(DEVICE_PARAMS, char *buf)
 {
 #if 0
        struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
@@ -396,7 +384,7 @@ static ssize_t show_group_addr(struct class_device *d,
 #endif
 }
 
-static ssize_t store_group_addr(struct class_device *d,
+static ssize_t store_group_addr(DEVICE_PARAMS,
                                const char *buf, size_t len)
 {
        struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
@@ -439,23 +427,23 @@ static DP_DEVICE_ATTR(group_addr, S_IRUGO | S_IWUSR,
                   show_group_addr, store_group_addr);
 
 static struct attribute *bridge_attrs[] = {
-       &class_device_attr_forward_delay.attr,
-       &class_device_attr_hello_time.attr,
-       &class_device_attr_max_age.attr,
-       &class_device_attr_ageing_time.attr,
-       &class_device_attr_stp_state.attr,
-       &class_device_attr_priority.attr,
-       &class_device_attr_bridge_id.attr,
-       &class_device_attr_root_id.attr,
-       &class_device_attr_root_path_cost.attr,
-       &class_device_attr_root_port.attr,
-       &class_device_attr_topology_change.attr,
-       &class_device_attr_topology_change_detected.attr,
-       &class_device_attr_hello_timer.attr,
-       &class_device_attr_tcn_timer.attr,
-       &class_device_attr_topology_change_timer.attr,
-       &class_device_attr_gc_timer.attr,
-       &class_device_attr_group_addr.attr,
+       &DEV_ATTR(forward_delay).attr,
+       &DEV_ATTR(hello_time).attr,
+       &DEV_ATTR(max_age).attr,
+       &DEV_ATTR(ageing_time).attr,
+       &DEV_ATTR(stp_state).attr,
+       &DEV_ATTR(priority).attr,
+       &DEV_ATTR(bridge_id).attr,
+       &DEV_ATTR(root_id).attr,
+       &DEV_ATTR(root_path_cost).attr,
+       &DEV_ATTR(root_port).attr,
+       &DEV_ATTR(topology_change).attr,
+       &DEV_ATTR(topology_change_detected).attr,
+       &DEV_ATTR(hello_timer).attr,
+       &DEV_ATTR(tcn_timer).attr,
+       &DEV_ATTR(topology_change_timer).attr,
+       &DEV_ATTR(gc_timer).attr,
+       &DEV_ATTR(group_addr).attr,
        NULL
 };
 
@@ -476,7 +464,7 @@ static struct attribute_group bridge_group = {
  */
 int dp_sysfs_add_dp(struct datapath *dp)
 {
-       struct kobject *kobj = to_kobj(dp->ports[ODPP_LOCAL]->dev);
+       struct kobject *kobj = &dp->ports[ODPP_LOCAL]->dev->NETDEV_DEV_MEMBER.kobj;
        int err;
 
        /* Create /sys/class/net/<devname>/bridge directory. */
@@ -488,11 +476,10 @@ int dp_sysfs_add_dp(struct datapath *dp)
        }
 
        /* Create /sys/class/net/<devname>/brif directory. */
-       dp->ifobj.parent = kobj;
-       err = kobject_add(&dp->ifobj);
+       err = kobject_add(&dp->ifobj, kobj, SYSFS_BRIDGE_PORT_SUBDIR);
        if (err) {
                pr_info("%s: can't add kobject (directory) %s/%s\n",
-                               __FUNCTION__, dp_name(dp), dp->ifobj.name);
+                       __FUNCTION__, dp_name(dp), kobject_name(&dp->ifobj));
                goto out2;
        }
        kobject_uevent(&dp->ifobj, KOBJ_ADD);
@@ -506,21 +493,16 @@ int dp_sysfs_add_dp(struct datapath *dp)
 
 int dp_sysfs_del_dp(struct datapath *dp)
 {
-       struct kobject *kobj = to_kobj(dp->ports[ODPP_LOCAL]->dev);
+       struct kobject *kobj = &dp->ports[ODPP_LOCAL]->dev->NETDEV_DEV_MEMBER.kobj;
 
        kobject_del(&dp->ifobj);
        sysfs_remove_group(kobj, &bridge_group);
 
        return 0;
 }
-#else /* !SUPPORT_SYSFS */
+#else /* !CONFIG_SYSFS */
 int dp_sysfs_add_dp(struct datapath *dp) { return 0; }
 int dp_sysfs_del_dp(struct datapath *dp) { return 0; }
 int dp_sysfs_add_if(struct net_bridge_port *p) { return 0; }
-int dp_sysfs_del_if(struct net_bridge_port *p)
-{
-       dev_put(p->dev);
-       kfree(p);
-       return 0;
-}
-#endif /* !SUPPORT_SYSFS */
+int dp_sysfs_del_if(struct net_bridge_port *p) { return 0; }
+#endif /* !CONFIG_SYSFS */
index ab928f6..95c26dc 100644 (file)
@@ -21,7 +21,7 @@
 #include "dp_sysfs.h"
 #include "datapath.h"
 
-#ifdef SUPPORT_SYSFS
+#ifdef CONFIG_SYSFS
 
 struct brport_attribute {
        struct attribute        attr;
@@ -278,9 +278,10 @@ int dp_sysfs_add_if(struct net_bridge_port *p)
        int err;
 
        /* Create /sys/class/net/<devname>/brport directory. */
-       err = kobject_add(&p->kobj);
+       err = kobject_add(&p->kobj, &p->dev->NETDEV_DEV_MEMBER.kobj,
+                         SYSFS_BRIDGE_PORT_ATTR);
        if (err)
-               goto err_put;
+               goto err;
 
        /* Create symlink from /sys/class/net/<devname>/brport/bridge to
         * /sys/class/net/<bridgename>. */
@@ -306,15 +307,12 @@ int dp_sysfs_add_if(struct net_bridge_port *p)
 
        kobject_uevent(&p->kobj, KOBJ_ADD);
 
-       return err;
+       return 0;
 
 err_del:
        kobject_del(&p->kobj);
-err_put:
-       kobject_put(&p->kobj);
-
-       /* Ensure that dp_sysfs_del_if becomes a no-op. */
-       p->kobj.dentry = NULL;
+err:
+       p->linkname[0] = 0;
        return err;
 }
 
@@ -322,12 +320,10 @@ int dp_sysfs_del_if(struct net_bridge_port *p)
 {
        if (p->linkname[0]) {
                sysfs_remove_link(&p->dp->ifobj, p->linkname);
-               p->linkname[0] = '\0';
-       }
-       if (p->kobj.dentry) {
                kobject_uevent(&p->kobj, KOBJ_REMOVE);
                kobject_del(&p->kobj);
+               p->linkname[0] = '\0';
        }
        return 0;
 }
-#endif /* SUPPORT_SYSFS */
+#endif /* CONFIG_SYSFS */
index c0de3d2..4cf797e 100644 (file)
@@ -4,6 +4,7 @@
 #include_next <linux/kobject.h>
 
 #include <linux/version.h>
+
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
 #define kobject_init(kobj, ktype) rpl_kobject_init(kobj, ktype)
 static inline void rpl_kobject_init(struct kobject *kobj, struct kobj_type *ktype)
@@ -11,6 +12,19 @@ static inline void rpl_kobject_init(struct kobject *kobj, struct kobj_type *ktyp
        kobj->ktype = ktype;
        (kobject_init)(kobj);
 }
+
+#define kobject_add(kobj, parent, name) rpl_kobject_add(kobj, parent, name)
+static inline int rpl_kobject_add(struct kobject *kobj,
+                                 struct kobject *parent,
+                                 const char *name)
+{
+       int err = kobject_set_name(kobj, "%s", name);
+       if (err)
+               return err;
+       kobj->parent = parent;
+       return (kobject_add)(kobj);
+}
 #endif
 
+
 #endif /* linux/kobject.h wrapper */
index 8bc5156..4d7bd78 100644 (file)
@@ -4,7 +4,7 @@
 #include_next <linux/rtnetlink.h>
 
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
-static inline int rtnl_notify(struct sk_buff *skb, struct net *net, u32 pid,
+static inline void rtnl_notify(struct sk_buff *skb, struct net *net, u32 pid,
                              u32 group, struct nlmsghdr *nlh, gfp_t flags)
 {
        BUG_ON(nlh);            /* not implemented */
@@ -12,7 +12,6 @@ static inline int rtnl_notify(struct sk_buff *skb, struct net *net, u32 pid,
                /* errors reported via destination sk->sk_err */
                nlmsg_multicast(rtnl, skb, 0, group);
        }
-       return 0;
 }
 
 static inline void rtnl_set_sk_err(struct net *net, u32 group, int error)
@@ -20,10 +19,15 @@ static inline void rtnl_set_sk_err(struct net *net, u32 group, int error)
        netlink_set_err(rtnl, 0, group, error);
 }
 #elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
+/* No 'net' parameter in these versions. */
 #define rtnl_notify(skb, net, pid, group, nlh, flags) \
-       ((void) (net), rtnl_notify(skb, pid, group, nlh, flags))
+       ((void) (net), (void) rtnl_notify(skb, pid, group, nlh, flags))
 #define rtnl_set_sk_err(net, group, error) \
        ((void) (net), rtnl_set_sk_err(group, error))
-#endif /* linux kernel < 2.6.25 */
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30)
+/* Make the return type effectively 'void' to match Linux 2.6.30+. */
+#define rtnl_notify(skb, net, pid, group, nlh, flags) \
+       ((void) rtnl_notify(skb, net, pid, group, nlh, flags))
+#endif
 
 #endif /* linux/rtnetlink.h wrapper */
index 4aa1f90..76ed626 100644 (file)
@@ -2,4 +2,4 @@ openvswitch (0.90.0) unstable; urgency=low
 
   * Development version.
 
- -- Open vSwitch developers <ovs-dev@openvswitch.org>  Mon, 19 Nov 2007 14:57:52 -0800
+ -- Open vSwitch developers <dev@openvswitch.org>  Mon, 19 Nov 2007 14:57:52 -0800
index 09eda11..13da9d7 100644 (file)
@@ -1,7 +1,7 @@
 Source: openvswitch
 Section: net
 Priority: extra
-Maintainer: Open vSwitch developers <ovs-dev@openvswitch.org>
+Maintainer: Open vSwitch developers <dev@openvswitch.org>
 Build-Depends: debhelper (>= 5), autoconf (>= 2.60), automake1.10, libssl-dev, pkg-config (>= 0.21), po-debconf, bzip2, openssl, libncurses5-dev, libpcre3-dev
 Standards-Version: 3.7.3
 
@@ -15,8 +15,7 @@ Description: Source code for Open vSwitch datapath Linux module
  from it using module-assistant or make-kpkg.  README.Debian in this
  package provides further instructions.
  .
- Open vSwitch is a software-based Ethernet switch targeted at virtual
- servers.
+ Open vSwitch is a full-featured software-based Ethernet switch.
 
 Package: openvswitch-common
 Architecture: any
@@ -25,8 +24,7 @@ Description: Open vSwitch common components
  openvswitch-common provides components required by both openvswitch-switch
  and openvswitch-controller.
  .
- Open vSwitch is a software-based Ethernet switch targeted at virtual
- servers.
+ Open vSwitch is a full-featured software-based Ethernet switch.
 
 Package: openvswitch-switch
 Architecture: any
@@ -36,8 +34,7 @@ Description: Open vSwitch switch implementations
  openvswitch-switch provides the userspace components and utilities for
  the Open vSwitch kernel-based switch.  
  .
- Open vSwitch is a software-based Ethernet switch targeted at virtual
- servers.
+ Open vSwitch is a full-featured software-based Ethernet switch.
 
 Package: openvswitch-switch-config
 Architecture: any
@@ -46,8 +43,7 @@ Description: Open vSwitch switch implementations
  openvswitch-switch-config provides a utility for interactively configuring
  the Open vSwitch switch provided in the openvswitch-switch package.
  .
- Open vSwitch is a software-based Ethernet switch targeted at virtual
- servers.
+ Open vSwitch is a full-featured software-based Ethernet switch.
 
 Package: openvswitch-switchui
 Architecture: any
@@ -71,8 +67,7 @@ Description: Open vSwitch public key infrastructure
  Open vSwitch switches and controllers, reducing the risk of
  man-in-the-middle attacks on the Open vSwitch network infrastructure.
  .
- Open vSwitch is a software-based Ethernet switch targeted at virtual
- servers.
+ Open vSwitch is a full-featured software-based Ethernet switch.
 
 Package: openvswitch-pki-server
 Architecture: all
@@ -84,8 +79,7 @@ Description: Open vSwitch public key infrastructure (HTTP server support)
  convenient OpenFlow switch setup using the ovs-switch-setup program
  in the openvswitch-switch package.
  .
- Open vSwitch is a software-based Ethernet switch targeted at virtual
- servers.
+ Open vSwitch is a full-featured software-based Ethernet switch.
 
 Package: openvswitch-controller
 Architecture: any
@@ -94,8 +88,7 @@ Description: Open vSwitch controller implementation
  The Open vSwitch controller enables OpenFlow switches that connect to it
  to act as MAC-learning Ethernet switches.
  .
- Open vSwitch is a software-based Ethernet switch targeted at virtual
- servers.
+ Open vSwitch is a full-featured software-based Ethernet switch.
 
 Package: corekeeper
 Architecture: all
@@ -117,9 +110,9 @@ Architecture: any
 Recommends: openvswitch-switch
 Depends: ${shlibs:Depends}, ${misc:Depends}
 Description: Monitor utility for Open vSwitch switches
- The ovs-monitor utility included in this package monitors the secure
- channel and datapath.  If either become unresponsive, the switch is
rebooted.
+ The ovs-monitor utility included in this package monitors the
+ ovs-openflowd process and the kernel datapath.  If either become
unresponsive, it reboots the machine.
 
 Package: openvswitch-wdt
 Architecture: any
index 4da85b4..af44a58 100644 (file)
@@ -1,7 +1,7 @@
 Source: openvswitch
 Section: net
 Priority: extra
-Maintainer: Open vSwitch developers <ovs-dev@openvswitch.org>
+Maintainer: Open vSwitch developers <dev@openvswitch.org>
 Build-Depends: debhelper (>= 5.0.37)
 Standards-Version: 3.7.3
 
index 18819a7..94b95c4 100644 (file)
@@ -6,7 +6,8 @@ README.Debian for openvswitch-controller
 
 * To enable OpenFlow switches to automatically discover the location
   of the controller, you must install and configure a DHCP server.
-  The secchan(8) manpage (found in the openvswitch-switch package) gives
-  a working example configuration file for the ISC DHCP server.
+  The ovs-openflowd(8) manpage (found in the openvswitch-switch
+  package) gives a working example configuration file for the ISC DHCP
+  server.
 
- -- Ben Pfaff <blp@nicira.com>, Mon, 11 May 2009 13:26:38 -0700
+ -- Ben Pfaff <blp@nicira.com>, Wed,  8 Jul 2009 09:39:53 -0700
index f0c356e..3b6ccdf 100644 (file)
 # it reboots the system.  A value of zero disables the monitor.
 THRESHOLD=3
 
-# INTERVAL: The number of seconds to wait between probing secchan and
-# the datapath.
+# INTERVAL: The number of seconds to wait between probing
+# ovs-openflowd and the datapath.
 INTERVAL=1
 
 # LOG_FILE: File to log messages related to monitoring.
 LOG_FILE="/var/log/openvswitch/monitor"
 
-# SWITCH_VCONN: The vconn used to connect to the switch (secchan).
-# The secchan must be configured to listen to this vconn.  The default
-# here set is also listened to by default by the openvswitch-switch
-# package, so ordinarily there is no need to modify this.
-SWITCH_VCONN="/var/run/secchan.mgmt"
+# SWITCH_VCONN: The vconn used to connect to the switch
+# (ovs-openflowd).  The ovs-openflowd must be configured to listen to
+# this vconn.  The default here set is also listened to by default by
+# the openvswitch-switch package, so ordinarily there is no need to
+# modify this.
+SWITCH_VCONN="/var/run/ovs-openflowd.mgmt"
index 5945ad7..8ea2d5b 100644 (file)
@@ -64,7 +64,7 @@ _Description: Preparing to discover controller.
  The setup program will now attempt to discover the OpenFlow controller.
  Controller discovery may take up to 30 seconds.  Please be patient.
  .
- See secchan(8) for instructions on how to configure a DHCP server for
+ See ovs-openflowd(8) for instructions on how to configure a DHCP server for
  controller discovery.
 
 Template: openvswitch-switch/discovery-failure
@@ -73,7 +73,7 @@ _Description: Controller discovery failed.
  The controller's location could not be determined automatically.
  .
  Ensure that the OpenFlow DHCP server is properly configured.  See
secchan(8) for instructions on how to configure a DHCP server for
ovs-openflowd(8) for instructions on how to configure a DHCP server for
  controller discovery.
 
 Template: openvswitch-switch/discovery-success
index c579a08..da4ec69 100755 (executable)
@@ -19,9 +19,9 @@
 ### END INIT INFO
 
 PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
-DAEMON=/usr/sbin/secchan
-NAME=secchan
-DESC=secchan
+DAEMON=/usr/sbin/ovs-openflowd
+NAME=ovs-openflowd
+DESC=ovs-openflowd
 
 test -x $DAEMON || exit 0
 
@@ -35,7 +35,7 @@ DODTIME=1                   # Time to wait for the server to die, in seconds
                             # let some servers to die gracefully and
                             # 'restart' will not work
 
-# Include secchan defaults if available
+# Include ovs-openflowd defaults if available
 unset NETDEVS
 unset MODE
 unset SWITCH_IP
@@ -319,7 +319,7 @@ case "$1" in
             check_op "Setting core limit to $CORE_LIMIT" ulimit -c "$CORE_LIMIT"
         fi
 
-        # Compose secchan options.
+        # Compose ovs-openflowd options.
         set --
         set -- "$@" --verbose=ANY:console:emer --verbose=ANY:syslog:err
         set -- "$@" --log-file
index 9fddacf..a0cf9bf 100644 (file)
@@ -1,4 +1,4 @@
-_debian/secchan/secchan usr/sbin
+_debian/utilities/ovs-openflowd usr/sbin
 _debian/utilities/ovs-dpctl usr/sbin
 _debian/utilities/ovs-discover usr/sbin
 _debian/utilities/ovs-kill usr/sbin
index 41394e8..a45cc2a 100644 (file)
@@ -1,4 +1,4 @@
-/var/log/openvswitch/secchan.log {
+/var/log/openvswitch/ovs-openflowd.log {
         daily
         compress
         create 640 root adm
@@ -6,6 +6,6 @@
         missingok
         rotate 30
         postrotate
-                ovs-appctl --target /var/run/secchan.pid --reopen
+                ovs-appctl --target /var/run/ovs-openflowd.pid --reopen
         endscript
 }
index f789eba..821503a 100644 (file)
@@ -1,4 +1,4 @@
-_debian/secchan/secchan.8
+_debian/utilities/ovs-openflowd.8
 _debian/utilities/ovs-discover.8
 _debian/utilities/ovs-dpctl.8
 _debian/utilities/ovs-kill.8
index 69f1de6..44f9210 100644 (file)
@@ -1,7 +1,7 @@
 # This is a POSIX shell fragment                -*- sh -*-
 
-# To configure the secure channel, fill in the following properly and
-# uncomment them.  Afterward, the secure channel will come up
+# To configure the OpenFlow switch, fill in the following properly and
+# uncomment them.  Afterward, the switch will come up
 # automatically at boot time.  It can be started immediately with
 #       /etc/init.d/openvswitch-switch start
 # Alternatively, use the ovs-switch-setup program (from the
@@ -101,12 +101,12 @@ SWITCH_IP=dhcp
 # Set CACERT_MODE to 'secure' or 'bootstrap' for these respective cases.
 #CACERT_MODE=secure
 
-# MGMT_VCONNS: List of vconns (space-separated) on which secchan
+# MGMT_VCONNS: List of vconns (space-separated) on which ovs-openflowd
 # should listen for management connections from ovs-ofctl, etc.
 # openvswitch-switchui by default connects to
-# unix:/var/run/secchan.mgmt, so do not disable this if you want to
+# unix:/var/run/ovs-openflowd.mgmt, so do not disable this if you want to
 # use openvswitch-switchui.
-MGMT_VCONNS="punix:/var/run/secchan.mgmt"
+MGMT_VCONNS="punix:/var/run/ovs-openflowd.mgmt"
 
 # COMMANDS: Access control list for the commands that can be executed
 # remotely over the OpenFlow protocol, as a comma-separated list of
@@ -122,7 +122,7 @@ MGMT_VCONNS="punix:/var/run/secchan.mgmt"
 #DISCONNECTED_MODE=switch
 
 # STP: Enable or disabled 802.1D-1998 Spanning Tree Protocol.  Set to
-# 'yes' to enable STP, 'no' to disable it.  If unset, secchan's
+# 'yes' to enable STP, 'no' to disable it.  If unset, ovs-openflowd's
 # current default is 'no' (but this may change in the future).
 #STP=no
 
@@ -133,17 +133,17 @@ MGMT_VCONNS="punix:/var/run/secchan.mgmt"
 #RATE_LIMIT=1000
 
 # INACTIVITY_PROBE: The maximum number of seconds of inactivity on the
-# controller connection before secchan sends an inactivity probe
+# controller connection before ovs-openflowd sends an inactivity probe
 # message to the controller.  The valid range is 5 and up.  If unset,
-# secchan defaults to 5 seconds.
+# ovs-openflowd defaults to 5 seconds.
 #INACTIVITY_PROBE=5
 
-# MAX_BACKOFF: The maximum time that secchan will wait between
+# MAX_BACKOFF: The maximum time that ovs-openflowd will wait between
 # attempts to connect to the controller.  The valid range is 1 and up.
-# If unset, secchan defaults to 8 seconds.
+# If unset, ovs-openflowd defaults to 8 seconds.
 #MAX_BACKOFF=8
 
-# DAEMON_OPTS: Additional options to pass to secchan, e.g. "--fail=open"
+# DAEMON_OPTS: Additional options to pass to ovs-openflowd, e.g. "--fail=open"
 DAEMON_OPTS=""
 
 # CORE_LIMIT: Maximum size for core dumps.
index 6cdbf7a..a759af8 100644 (file)
@@ -1,7 +1,7 @@
 # This is a POSIX shell fragment                -*- sh -*-
 
-# To configure the switch monitor, modify the following.  Afterward,
-# the secure channel will come up automatically at boot time.  It can
+# To configure the switch UI, modify the following.  Afterward,
+# the switch UI will come up automatically at boot time.  It can
 # be restarted immediately with
 #       /etc/init.d/openvswitch-switchui start
 
@@ -9,11 +9,11 @@
 # sourced by /etc/init.d/openvswitch-switchui
 # installed at /etc/default/openvswitch-switchui by the maintainer scripts
 
-# SWITCH_VCONN: The vconn used to connect to the switch (secchan).
-# The secchan must be configured to listen to this vconn.  The default
+# SWITCH_VCONN: The vconn used to connect to the switch (ovs-openflowd).
+# The ovs-openflowd must be configured to listen to this vconn.  The default
 # here set is also listened to by default by the openvswitch-switch
 # package, so ordinarily there is no need to modify this.
-SWITCH_VCONN="unix:/var/run/secchan.mgmt"
+SWITCH_VCONN="unix:/var/run/ovs-openflowd.mgmt"
 
 # EZIO3_DEVICE: To display the switch monitor on an EZIO3 (aka
 # MTB-134) 16x2 LCD displays found on server appliances made by
index 696ad36..9c0da4c 100644 (file)
@@ -38,4 +38,4 @@ obtained from the OpenFlow PKI server.
 
 .BR ovs\-dpctl (8),
 .BR ovs-pki (8),
-.BR secchan (8)
+.BR ovs-openflowd (8)
index 39dee7a..abccbc4 100644 (file)
@@ -7,7 +7,7 @@
 msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
-"Report-Msgid-Bugs-To: ovs-dev@openvswitch.org\n"
+"Report-Msgid-Bugs-To: dev@openvswitch.org\n"
 "POT-Creation-Date: 2009-05-11 13:38-0700\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
@@ -168,7 +168,7 @@ msgstr ""
 #. Description
 #: ../openvswitch-switch-config.templates:5001
 msgid ""
-"See secchan(8) for instructions on how to configure a DHCP server for "
+"See ovs-openflowd(8) for instructions on how to configure a DHCP server for "
 "controller discovery."
 msgstr ""
 
@@ -188,7 +188,7 @@ msgstr ""
 #. Description
 #: ../openvswitch-switch-config.templates:6001
 msgid ""
-"Ensure that the OpenFlow DHCP server is properly configured.  See secchan(8) "
+"Ensure that the OpenFlow DHCP server is properly configured.  See ovs-openflowd(8) "
 "for instructions on how to configure a DHCP server for controller discovery."
 msgstr ""
 
index 2aeaa64..7c7db24 100644 (file)
@@ -6,6 +6,9 @@
 # without warranty of any kind.
 
 EXTRA_DIST += extras/ezio/ezio3.ti
+
+if HAVE_CURSES
+if HAVE_PCRE
 install-data-hook:
        @echo tic -x $(srcdir)/extras/ezio/ezio3.ti
        @if ! tic -x $(srcdir)/extras/ezio/ezio3.ti; then                             \
@@ -47,3 +50,5 @@ extras_ezio_ovs_switchui_LDADD = \
        $(PCRE_LIBS) \
        $(SSL_LIBS) \
        -lm
+endif # HAVE_PCRE
+endif # HAVE_CURSES
index 040bc70..ad006fa 100644 (file)
@@ -114,7 +114,7 @@ static void compose_messages(const struct dict *, struct rconn *rconn);
 
 static void show_flows(struct rconn *);
 static void show_dpid_ip(struct rconn *, const struct dict *);
-static void show_secchan_state(const struct dict *);
+static void show_ofproto_state(const struct dict *);
 static void show_fail_open_state(const struct dict *);
 static void show_discovery_state(const struct dict *);
 static void show_remote_state(const struct dict *);
@@ -257,7 +257,7 @@ compose_messages(const struct dict *dict, struct rconn *rconn)
     if (!show_reboot_state()) {
         show_flows(rconn);
         show_dpid_ip(rconn, dict);
-        show_secchan_state(dict);
+        show_ofproto_state(dict);
         show_fail_open_state(dict);
         show_discovery_state(dict);
         show_remote_state(dict);
@@ -648,7 +648,7 @@ addf(const char *format, ...)
 }
 
 static void
-show_secchan_state(const struct dict *dict)
+show_ofproto_state(const struct dict *dict)
 {
     static struct message *msg;
     const char *is_connected;
@@ -2480,7 +2480,7 @@ choose_netdevs(struct svec *choices)
 
         retval = netdev_open(name, NETDEV_ETH_TYPE_NONE, &netdev);
         if (!retval) {
-            bool exclude = netdev_get_in4(netdev, NULL);
+            bool exclude = netdev_get_in4(netdev, NULL) == 0;
             netdev_close(netdev);
             if (exclude) {
                 continue;
index f780f07..1d70aab 100644 (file)
@@ -320,8 +320,8 @@ enum ofp_action_type {
 
 /* Action structure for OFPAT_OUTPUT, which sends packets out 'port'.  
  * When the 'port' is the OFPP_CONTROLLER, 'max_len' indicates the max 
- * number of bytes to send.  A 'max_len' of zero means the entire packet 
- * should be sent. */
+ * number of bytes to send.  A 'max_len' of zero means no bytes of the
+ * packet should be sent. */
 struct ofp_action_output {
     uint16_t type;                  /* OFPAT_OUTPUT. */
     uint16_t len;                   /* Length is 8. */
index 951664a..868c854 100644 (file)
@@ -37,7 +37,7 @@
  * ----------------------------------------------------------------------
  */
 
-/* Protocol between secchan and datapath. */
+/* Protocol between userspace and kernel datapath. */
 
 #ifndef OPENVSWITCH_DATAPATH_PROTOCOL_H
 #define OPENVSWITCH_DATAPATH_PROTOCOL_H 1
 #define ODP_PORT_GROUP_GET      _IOWR('O', 12, struct odp_port_group)
 
 #define ODP_FLOW_GET            _IOWR('O', 13, struct odp_flow)
-#define ODP_FLOW_GET_MULTIPLE   _IOWR('O', 14, struct odp_flowvec)
+#define ODP_FLOW_PUT            _IOWR('O', 14, struct odp_flow)
 #define ODP_FLOW_LIST           _IOWR('O', 15, struct odp_flowvec)
-
 #define ODP_FLOW_FLUSH          _IO('O', 16)
-#define ODP_FLOW_PUT            _IOWR('O', 17, struct odp_flow)
-#define ODP_FLOW_DEL            _IOWR('O', 18, struct odp_flow)
+#define ODP_FLOW_DEL            _IOWR('O', 17, struct odp_flow)
 
-#define ODP_EXECUTE             _IOR('O', 19, struct odp_execute)
+#define ODP_EXECUTE             _IOR('O', 18, struct odp_execute)
 
 struct odp_stats {
     /* Flows. */
@@ -149,7 +147,7 @@ struct odp_flow_stats {
     __u32 used_nsec;
     __u8 tcp_flags;
     __u8 ip_tos;
-    __u16 reserved;
+    __u16 error;                /* Used by ODP_FLOW_GET. */
 };
 
 struct odp_flow_key {
index f6798a1..b0d10fd 100644 (file)
@@ -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 \
@@ -52,6 +57,8 @@ lib_libopenvswitch_a_SOURCES = \
        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 \
@@ -75,6 +82,8 @@ lib_libopenvswitch_a_SOURCES = \
        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 \
@@ -117,8 +126,6 @@ CLEANFILES += $(nodist_lib_libopenvswitch_a_SOURCES)
 
 if HAVE_NETLINK
 lib_libopenvswitch_a_SOURCES += \
-       lib/dpif.c \
-       lib/dpif.h \
        lib/netlink-protocol.h \
        lib/netlink.c \
        lib/netlink.h
@@ -172,17 +179,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
index 9057121..4f90781 100644 (file)
@@ -411,7 +411,7 @@ dhclient_configure_netdev(struct dhclient *cli)
     }
 
     if (!error && router.s_addr) {
-        error = netdev_add_router(router);
+        error = netdev_add_router(cli->netdev, router);
         if (error) {
             VLOG_ERR("failed to add default route to "IP_FMT" on %s: %s",
                      IP_ARGS(&router), netdev_get_name(cli->netdev),
@@ -882,7 +882,7 @@ dhclient_msg_init(struct dhclient *cli, enum dhcp_msg_type type,
     msg->xid = cli->xid;
     msg->secs = cli->secs;
     msg->type = type;
-    memcpy(msg->chaddr, netdev_get_etheraddr(cli->netdev), ETH_ADDR_LEN);
+    netdev_get_etheraddr(cli->netdev, msg->chaddr);
 }
 
 /* If time goes backward this returns a large number, which makes it look like
@@ -905,9 +905,13 @@ timeout(struct dhclient *cli, unsigned int secs)
 static bool
 do_receive_msg(struct dhclient *cli, struct dhcp_msg *msg)
 {
+    uint8_t cli_mac[ETH_ADDR_LEN];
     struct ofpbuf b;
+    int mtu;
 
-    ofpbuf_init(&b, netdev_get_mtu(cli->netdev) + VLAN_ETH_HEADER_LEN);
+    netdev_get_mtu(cli->netdev, &mtu);
+    ofpbuf_init(&b, mtu + VLAN_ETH_HEADER_LEN);
+    netdev_get_etheraddr(cli->netdev, cli_mac);
     for (; cli->received < 50; cli->received++) {
         const struct ip_header *ip;
         const struct dhcp_header *dhcp;
@@ -925,8 +929,7 @@ do_receive_msg(struct dhclient *cli, struct dhcp_msg *msg)
             || flow.nw_proto != IP_TYPE_UDP
             || flow.tp_dst != htons(DHCP_CLIENT_PORT)
             || !(eth_addr_is_broadcast(flow.dl_dst)
-                 || eth_addr_equals(flow.dl_dst,
-                                    netdev_get_etheraddr(cli->netdev)))) {
+                 || eth_addr_equals(flow.dl_dst, cli_mac))) {
             continue;
         }
 
@@ -977,7 +980,7 @@ do_send_msg(struct dhclient *cli, const struct dhcp_msg *msg)
 
     dhcp_assemble(msg, &b);
 
-    memcpy(eh.eth_src, netdev_get_etheraddr(cli->netdev), ETH_ADDR_LEN);
+    netdev_get_etheraddr(cli->netdev, eh.eth_src);
     memcpy(eh.eth_dst, eth_addr_broadcast, ETH_ADDR_LEN);
     eh.eth_type = htons(ETH_TYPE_IP);
 
diff --git a/lib/dpif-linux.c b/lib/dpif-linux.c
new file mode 100644 (file)
index 0000000..4d8c804
--- /dev/null
@@ -0,0 +1,749 @@
+/*
+ * Copyright (c) 2008, 2009 Nicira Networks.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+#include "dpif.h"
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <net/if.h>
+#include <linux/ethtool.h>
+#include <linux/rtnetlink.h>
+#include <linux/sockios.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+#include "dpif-provider.h"
+#include "ofpbuf.h"
+#include "poll-loop.h"
+#include "rtnetlink.h"
+#include "svec.h"
+#include "util.h"
+
+#include "vlog.h"
+#define THIS_MODULE VLM_dpif_linux
+
+/* Datapath interface for the openvswitch Linux kernel module. */
+struct dpif_linux {
+    struct dpif dpif;
+    int fd;
+
+    /* Used by dpif_linux_get_all_names(). */
+    char *local_ifname;
+    int minor;
+
+    /* Change notification. */
+    int local_ifindex;          /* Ifindex of local port. */
+    struct svec changed_ports;  /* Ports that have changed. */
+    struct rtnetlink_notifier port_notifier;
+    bool change_error;
+};
+
+static struct vlog_rate_limit error_rl = VLOG_RATE_LIMIT_INIT(9999, 5);
+
+static int do_ioctl(const struct dpif *, int cmd, const void *arg);
+static int lookup_minor(const char *name, int *minor);
+static int finish_open(struct dpif *, const char *local_ifname);
+static int create_minor(const char *name, int minor, struct dpif **dpifp);
+static int open_minor(int minor, struct dpif **dpifp);
+static int make_openvswitch_device(int minor, char **fnp);
+static void dpif_linux_port_changed(const struct rtnetlink_change *,
+                                    void *dpif);
+
+static struct dpif_linux *
+dpif_linux_cast(const struct dpif *dpif)
+{
+    dpif_assert_class(dpif, &dpif_linux_class);
+    return CONTAINER_OF(dpif, struct dpif_linux, dpif);
+}
+
+static int
+dpif_linux_enumerate(struct svec *all_dps)
+{
+    int error;
+    int i;
+
+    error = 0;
+    for (i = 0; i < ODP_MAX; i++) {
+        struct dpif *dpif;
+        char devname[16];
+        int retval;
+
+        sprintf(devname, "dp%d", i);
+        retval = dpif_open(devname, &dpif);
+        if (!retval) {
+            svec_add(all_dps, devname);
+            dpif_close(dpif);
+        } else if (retval != ENODEV && !error) {
+            error = retval;
+        }
+    }
+    return error;
+}
+
+static int
+dpif_linux_open(const char *name UNUSED, char *suffix, bool create,
+                struct dpif **dpifp)
+{
+    int minor;
+
+    minor = !strncmp(name, "dp", 2) && isdigit(name[2]) ? atoi(name + 2) : -1;
+    if (create) {
+        if (minor >= 0) {
+            return create_minor(suffix, minor, dpifp);
+        } else {
+            /* Scan for unused minor number. */
+            for (minor = 0; minor < ODP_MAX; minor++) {
+                int error = create_minor(suffix, minor, dpifp);
+                if (error != EBUSY) {
+                    return error;
+                }
+            }
+
+            /* All datapath numbers in use. */
+            return ENOBUFS;
+        }
+    } else {
+        struct dpif_linux *dpif;
+        struct odp_port port;
+        int error;
+
+        if (minor < 0) {
+            error = lookup_minor(suffix, &minor);
+            if (error) {
+                return error;
+            }
+        }
+
+        error = open_minor(minor, dpifp);
+        if (error) {
+            return error;
+        }
+        dpif = dpif_linux_cast(*dpifp);
+
+        /* We need the local port's ifindex for the poll function.  Start by
+         * getting the local port's name. */
+        memset(&port, 0, sizeof port);
+        port.port = ODPP_LOCAL;
+        if (ioctl(dpif->fd, ODP_PORT_QUERY, &port)) {
+            error = errno;
+            if (error != ENODEV) {
+                VLOG_WARN("%s: probe returned unexpected error: %s",
+                          dpif_name(*dpifp), strerror(error));
+            }
+            dpif_close(*dpifp);
+            return error;
+        }
+
+        /* Then use that to finish up opening. */
+        return finish_open(&dpif->dpif, port.devname);
+    }
+}
+
+static void
+dpif_linux_close(struct dpif *dpif_)
+{
+    struct dpif_linux *dpif = dpif_linux_cast(dpif_);
+    rtnetlink_notifier_unregister(&dpif->port_notifier);
+    svec_destroy(&dpif->changed_ports);
+    free(dpif->local_ifname);
+    close(dpif->fd);
+    free(dpif);
+}
+
+static int
+dpif_linux_get_all_names(const struct dpif *dpif_, struct svec *all_names)
+{
+    struct dpif_linux *dpif = dpif_linux_cast(dpif_);
+
+    svec_add_nocopy(all_names, xasprintf("dp%d", dpif->minor));
+    svec_add(all_names, dpif->local_ifname);
+    return 0;
+}
+
+static int
+dpif_linux_delete(struct dpif *dpif_)
+{
+    return do_ioctl(dpif_, ODP_DP_DESTROY, NULL);
+}
+
+static int
+dpif_linux_get_stats(const struct dpif *dpif_, struct odp_stats *stats)
+{
+    return do_ioctl(dpif_, ODP_DP_STATS, stats);
+}
+
+static int
+dpif_linux_get_drop_frags(const struct dpif *dpif_, bool *drop_fragsp)
+{
+    int drop_frags;
+    int error;
+
+    error = do_ioctl(dpif_, ODP_GET_DROP_FRAGS, &drop_frags);
+    if (!error) {
+        *drop_fragsp = drop_frags & 1;
+    }
+    return error;
+}
+
+static int
+dpif_linux_set_drop_frags(struct dpif *dpif_, bool drop_frags)
+{
+    int drop_frags_int = drop_frags;
+    return do_ioctl(dpif_, ODP_SET_DROP_FRAGS, &drop_frags_int);
+}
+
+static int
+dpif_linux_port_add(struct dpif *dpif_, const char *devname, uint16_t flags,
+                    uint16_t *port_no)
+{
+    struct odp_port port;
+    int error;
+
+    memset(&port, 0, sizeof port);
+    strncpy(port.devname, devname, sizeof port.devname);
+    port.flags = flags;
+    error = do_ioctl(dpif_, ODP_PORT_ADD, &port);
+    if (!error) {
+        *port_no = port.port;
+    }
+    return error;
+}
+
+static int
+dpif_linux_port_del(struct dpif *dpif_, uint16_t port_no)
+{
+    int tmp = port_no;
+    return do_ioctl(dpif_, ODP_PORT_DEL, &tmp);
+}
+
+static int
+dpif_linux_port_query_by_number(const struct dpif *dpif_, uint16_t port_no,
+                          struct odp_port *port)
+{
+    memset(port, 0, sizeof *port);
+    port->port = port_no;
+    return do_ioctl(dpif_, ODP_PORT_QUERY, port);
+}
+
+static int
+dpif_linux_port_query_by_name(const struct dpif *dpif_, const char *devname,
+                              struct odp_port *port)
+{
+    memset(port, 0, sizeof *port);
+    strncpy(port->devname, devname, sizeof port->devname);
+    return do_ioctl(dpif_, ODP_PORT_QUERY, port);
+}
+
+static int
+dpif_linux_flow_flush(struct dpif *dpif_)
+{
+    return do_ioctl(dpif_, ODP_FLOW_FLUSH, NULL);
+}
+
+static int
+dpif_linux_port_list(const struct dpif *dpif_, struct odp_port *ports, int n)
+{
+    struct odp_portvec pv;
+    int error;
+
+    pv.ports = ports;
+    pv.n_ports = n;
+    error = do_ioctl(dpif_, ODP_PORT_LIST, &pv);
+    return error ? -error : pv.n_ports;
+}
+
+static int
+dpif_linux_port_poll(const struct dpif *dpif_, char **devnamep)
+{
+    struct dpif_linux *dpif = dpif_linux_cast(dpif_);
+
+    if (dpif->change_error) {
+        dpif->change_error = false;
+        svec_clear(&dpif->changed_ports);
+        return ENOBUFS;
+    } else if (dpif->changed_ports.n) {
+        *devnamep = dpif->changed_ports.names[--dpif->changed_ports.n];
+        return 0;
+    } else {
+        return EAGAIN;
+    }
+}
+
+static void
+dpif_linux_port_poll_wait(const struct dpif *dpif_)
+{
+    struct dpif_linux *dpif = dpif_linux_cast(dpif_);
+    if (dpif->changed_ports.n || dpif->change_error) {
+        poll_immediate_wake();
+    } else {
+        rtnetlink_notifier_wait();
+    }
+}
+
+static int
+dpif_linux_port_group_get(const struct dpif *dpif_, int group,
+                          uint16_t ports[], int n)
+{
+    struct odp_port_group pg;
+    int error;
+
+    assert(n <= UINT16_MAX);
+    pg.group = group;
+    pg.ports = ports;
+    pg.n_ports = n;
+    error = do_ioctl(dpif_, ODP_PORT_GROUP_GET, &pg);
+    return error ? -error : pg.n_ports;
+}
+
+static int
+dpif_linux_port_group_set(struct dpif *dpif_, int group,
+                          const uint16_t ports[], int n)
+{
+    struct odp_port_group pg;
+
+    assert(n <= UINT16_MAX);
+    pg.group = group;
+    pg.ports = (uint16_t *) ports;
+    pg.n_ports = n;
+    return do_ioctl(dpif_, ODP_PORT_GROUP_SET, &pg);
+}
+
+static int
+dpif_linux_flow_get(const struct dpif *dpif_, struct odp_flow flows[], int n)
+{
+    struct odp_flowvec fv;
+    fv.flows = flows;
+    fv.n_flows = n;
+    return do_ioctl(dpif_, ODP_FLOW_GET, &fv);
+}
+
+static int
+dpif_linux_flow_put(struct dpif *dpif_, struct odp_flow_put *put)
+{
+    return do_ioctl(dpif_, ODP_FLOW_PUT, put);
+}
+
+static int
+dpif_linux_flow_del(struct dpif *dpif_, struct odp_flow *flow)
+{
+    return do_ioctl(dpif_, ODP_FLOW_DEL, flow);
+}
+
+static int
+dpif_linux_flow_list(const struct dpif *dpif_, struct odp_flow flows[], int n)
+{
+    struct odp_flowvec fv;
+    int error;
+
+    fv.flows = flows;
+    fv.n_flows = n;
+    error = do_ioctl(dpif_, ODP_FLOW_LIST, &fv);
+    return error ? -error : fv.n_flows;
+}
+
+static int
+dpif_linux_execute(struct dpif *dpif_, uint16_t in_port,
+                   const union odp_action actions[], int n_actions,
+                   const struct ofpbuf *buf)
+{
+    struct odp_execute execute;
+    memset(&execute, 0, sizeof execute);
+    execute.in_port = in_port;
+    execute.actions = (union odp_action *) actions;
+    execute.n_actions = n_actions;
+    execute.data = buf->data;
+    execute.length = buf->size;
+    return do_ioctl(dpif_, ODP_EXECUTE, &execute);
+}
+
+static int
+dpif_linux_recv_get_mask(const struct dpif *dpif_, int *listen_mask)
+{
+    return do_ioctl(dpif_, ODP_GET_LISTEN_MASK, listen_mask);
+}
+
+static int
+dpif_linux_recv_set_mask(struct dpif *dpif_, int listen_mask)
+{
+    return do_ioctl(dpif_, ODP_SET_LISTEN_MASK, &listen_mask);
+}
+
+static int
+dpif_linux_recv(struct dpif *dpif_, struct ofpbuf **bufp)
+{
+    struct dpif_linux *dpif = dpif_linux_cast(dpif_);
+    struct ofpbuf *buf;
+    int retval;
+    int error;
+
+    buf = ofpbuf_new(65536);
+    retval = read(dpif->fd, ofpbuf_tail(buf), ofpbuf_tailroom(buf));
+    if (retval < 0) {
+        error = errno;
+        if (error != EAGAIN) {
+            VLOG_WARN_RL(&error_rl, "%s: read failed: %s",
+                         dpif_name(dpif_), strerror(error));
+        }
+    } else if (retval >= sizeof(struct odp_msg)) {
+        struct odp_msg *msg = buf->data;
+        if (msg->length <= retval) {
+            buf->size += retval;
+            *bufp = buf;
+            return 0;
+        } else {
+            VLOG_WARN_RL(&error_rl, "%s: discarding message truncated "
+                         "from %zu bytes to %d",
+                         dpif_name(dpif_), msg->length, retval);
+            error = ERANGE;
+        }
+    } else if (!retval) {
+        VLOG_WARN_RL(&error_rl, "%s: unexpected end of file", dpif_name(dpif_));
+        error = EPROTO;
+    } else {
+        VLOG_WARN_RL(&error_rl,
+                     "%s: discarding too-short message (%d bytes)",
+                     dpif_name(dpif_), retval);
+        error = ERANGE;
+    }
+
+    *bufp = NULL;
+    ofpbuf_delete(buf);
+    return error;
+}
+
+static void
+dpif_linux_recv_wait(struct dpif *dpif_)
+{
+    struct dpif_linux *dpif = dpif_linux_cast(dpif_);
+    poll_fd_wait(dpif->fd, POLLIN);
+}
+
+const struct dpif_class dpif_linux_class = {
+    "",                         /* This is the default class. */
+    "linux",
+    NULL,
+    NULL,
+    dpif_linux_enumerate,
+    dpif_linux_open,
+    dpif_linux_close,
+    dpif_linux_get_all_names,
+    dpif_linux_delete,
+    dpif_linux_get_stats,
+    dpif_linux_get_drop_frags,
+    dpif_linux_set_drop_frags,
+    dpif_linux_port_add,
+    dpif_linux_port_del,
+    dpif_linux_port_query_by_number,
+    dpif_linux_port_query_by_name,
+    dpif_linux_port_list,
+    dpif_linux_port_poll,
+    dpif_linux_port_poll_wait,
+    dpif_linux_port_group_get,
+    dpif_linux_port_group_set,
+    dpif_linux_flow_get,
+    dpif_linux_flow_put,
+    dpif_linux_flow_del,
+    dpif_linux_flow_flush,
+    dpif_linux_flow_list,
+    dpif_linux_execute,
+    dpif_linux_recv_get_mask,
+    dpif_linux_recv_set_mask,
+    dpif_linux_recv,
+    dpif_linux_recv_wait,
+};
+\f
+static int get_openvswitch_major(void);
+static int get_major(const char *target, int default_major);
+
+static int
+do_ioctl(const struct dpif *dpif_, int cmd, const void *arg)
+{
+    struct dpif_linux *dpif = dpif_linux_cast(dpif_);
+    return ioctl(dpif->fd, cmd, arg) ? errno : 0;
+}
+
+static int
+lookup_minor(const char *name, int *minorp)
+{
+    struct ethtool_drvinfo drvinfo;
+    int minor, port_no;
+    struct ifreq ifr;
+    int error;
+    int sock;
+
+    sock = socket(AF_INET, SOCK_DGRAM, 0);
+    if (sock < 0) {
+        VLOG_WARN("socket(AF_INET) failed: %s", strerror(errno));
+        error = errno;
+        goto error;
+    }
+
+    memset(&ifr, 0, sizeof ifr);
+    strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name);
+    ifr.ifr_data = (caddr_t) &drvinfo;
+
+    memset(&drvinfo, 0, sizeof drvinfo);
+    drvinfo.cmd = ETHTOOL_GDRVINFO;
+    if (ioctl(sock, SIOCETHTOOL, &ifr)) {
+        VLOG_WARN("ioctl(SIOCETHTOOL) failed: %s", strerror(errno));
+        error = errno;
+        goto error_close_sock;
+    }
+
+    if (strcmp(drvinfo.driver, "openvswitch")) {
+        VLOG_WARN("%s is not an openvswitch device", name);
+        error = EOPNOTSUPP;
+        goto error_close_sock;
+    }
+
+    if (sscanf(drvinfo.bus_info, "%d.%d", &minor, &port_no) != 2) {
+        VLOG_WARN("%s ethtool bus_info has unexpected format", name);
+        error = EPROTOTYPE;
+        goto error_close_sock;
+    } else if (port_no != ODPP_LOCAL) {
+        /* This is an Open vSwitch device but not the local port.  We
+         * intentionally support only using the name of the local port as the
+         * name of a datapath; otherwise, it would be too difficult to
+         * enumerate all the names of a datapath. */
+        error = EOPNOTSUPP;
+        goto error_close_sock;
+    }
+
+    *minorp = minor;
+    close(sock);
+    return 0;
+
+error_close_sock:
+    close(sock);
+error:
+    return error;
+}
+
+static int
+make_openvswitch_device(int minor, char **fnp)
+{
+    dev_t dev = makedev(get_openvswitch_major(), minor);
+    const char dirname[] = "/dev/net";
+    struct stat s;
+    char fn[128];
+
+    *fnp = NULL;
+    sprintf(fn, "%s/dp%d", dirname, minor);
+    if (!stat(fn, &s)) {
+        if (!S_ISCHR(s.st_mode)) {
+            VLOG_WARN_RL(&error_rl, "%s is not a character device, fixing",
+                         fn);
+        } else if (s.st_rdev != dev) {
+            VLOG_WARN_RL(&error_rl,
+                         "%s is device %u:%u instead of %u:%u, fixing",
+                         fn, major(s.st_rdev), minor(s.st_rdev),
+                         major(dev), minor(dev));
+        } else {
+            goto success;
+        }
+        if (unlink(fn)) {
+            VLOG_WARN_RL(&error_rl, "%s: unlink failed (%s)",
+                         fn, strerror(errno));
+            return errno;
+        }
+    } else if (errno == ENOENT) {
+        if (stat(dirname, &s)) {
+            if (errno == ENOENT) {
+                if (mkdir(dirname, 0755)) {
+                    VLOG_WARN_RL(&error_rl, "%s: mkdir failed (%s)",
+                                 dirname, strerror(errno));
+                    return errno;
+                }
+            } else {
+                VLOG_WARN_RL(&error_rl, "%s: stat failed (%s)",
+                             dirname, strerror(errno));
+                return errno;
+            }
+        }
+    } else {
+        VLOG_WARN_RL(&error_rl, "%s: stat failed (%s)", fn, strerror(errno));
+        return errno;
+    }
+
+    /* The device needs to be created. */
+    if (mknod(fn, S_IFCHR | 0700, dev)) {
+        VLOG_WARN_RL(&error_rl,
+                     "%s: creating character device %u:%u failed (%s)",
+                     fn, major(dev), minor(dev), strerror(errno));
+        return errno;
+    }
+
+success:
+    *fnp = xstrdup(fn);
+    return 0;
+}
+
+
+static int
+get_openvswitch_major(void)
+{
+    static unsigned int openvswitch_major;
+    if (!openvswitch_major) {
+        enum { DEFAULT_MAJOR = 248 };
+        openvswitch_major = get_major("openvswitch", DEFAULT_MAJOR);
+    }
+    return openvswitch_major;
+}
+
+static int
+get_major(const char *target, int default_major)
+{
+    const char fn[] = "/proc/devices";
+    char line[128];
+    FILE *file;
+    int ln;
+
+    file = fopen(fn, "r");
+    if (!file) {
+        VLOG_ERR("opening %s failed (%s)", fn, strerror(errno));
+        goto error;
+    }
+
+    for (ln = 1; fgets(line, sizeof line, file); ln++) {
+        char name[64];
+        int major;
+
+        if (!strncmp(line, "Character", 9) || line[0] == '\0') {
+            /* Nothing to do. */
+        } else if (!strncmp(line, "Block", 5)) {
+            /* We only want character devices, so skip the rest of the file. */
+            break;
+        } else if (sscanf(line, "%d %63s", &major, name)) {
+            if (!strcmp(name, target)) {
+                fclose(file);
+                return major;
+            }
+        } else {
+            static bool warned;
+            if (!warned) {
+                VLOG_WARN("%s:%d: syntax error", fn, ln);
+            }
+            warned = true;
+        }
+    }
+
+    VLOG_ERR("%s: %s major not found (is the module loaded?), using "
+             "default major %d", fn, target, default_major);
+error:
+    VLOG_INFO("using default major %d for %s", default_major, target);
+    return default_major;
+}
+
+static int
+finish_open(struct dpif *dpif_, const char *local_ifname)
+{
+    struct dpif_linux *dpif = dpif_linux_cast(dpif_);
+    dpif->local_ifname = strdup(local_ifname);
+    dpif->local_ifindex = if_nametoindex(local_ifname);
+    if (!dpif->local_ifindex) {
+        int error = errno;
+        dpif_close(dpif_);
+        VLOG_WARN("could not get ifindex of %s device: %s",
+                  local_ifname, strerror(errno));
+        return error;
+    }
+    return 0;
+}
+
+static int
+create_minor(const char *name, int minor, struct dpif **dpifp)
+{
+    int error = open_minor(minor, dpifp);
+    if (!error) {
+        error = do_ioctl(*dpifp, ODP_DP_CREATE, name);
+        if (!error) {
+            error = finish_open(*dpifp, name);
+        } else {
+            dpif_close(*dpifp);
+        }
+    }
+    return error;
+}
+
+static int
+open_minor(int minor, struct dpif **dpifp)
+{
+    int error;
+    char *fn;
+    int fd;
+
+    error = make_openvswitch_device(minor, &fn);
+    if (error) {
+        return error;
+    }
+
+    fd = open(fn, O_RDONLY | O_NONBLOCK);
+    if (fd >= 0) {
+        struct dpif_linux *dpif = xmalloc(sizeof *dpif);
+        error = rtnetlink_notifier_register(&dpif->port_notifier,
+                                           dpif_linux_port_changed, dpif);
+        if (!error) {
+            char *name;
+
+            name = xasprintf("dp%d", minor);
+            dpif_init(&dpif->dpif, &dpif_linux_class, name, minor, minor);
+            free(name);
+
+            dpif->fd = fd;
+            dpif->local_ifname = NULL;
+            dpif->minor = minor;
+            dpif->local_ifindex = 0;
+            svec_init(&dpif->changed_ports);
+            dpif->change_error = false;
+            *dpifp = &dpif->dpif;
+        } else {
+            free(dpif);
+        }
+    } else {
+        error = errno;
+        VLOG_WARN("%s: open failed (%s)", fn, strerror(error));
+    }
+    free(fn);
+
+    return error;
+}
+
+static void
+dpif_linux_port_changed(const struct rtnetlink_change *change, void *dpif_)
+{
+    struct dpif_linux *dpif = dpif_;
+
+    if (change) {
+        if (change->master_ifindex == dpif->local_ifindex
+            && (change->nlmsg_type == RTM_NEWLINK
+                || change->nlmsg_type == RTM_DELLINK))
+        {
+            /* Our datapath changed, either adding a new port or deleting an
+             * existing one. */
+            if (!svec_contains(&dpif->changed_ports, change->ifname)) {
+                svec_add(&dpif->changed_ports, change->ifname);
+                svec_sort(&dpif->changed_ports);
+            }
+        }
+    } else {
+        dpif->change_error = true;
+    }
+}
diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
new file mode 100644 (file)
index 0000000..4ff1a42
--- /dev/null
@@ -0,0 +1,1326 @@
+/*
+ * 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 "dpif.h"
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <net/if.h>
+#include <linux/rtnetlink.h>
+#include <linux/ethtool.h>
+#include <linux/sockios.h>
+#include <netinet/in.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include <unistd.h>
+
+#include "csum.h"
+#include "dpif-provider.h"
+#include "flow.h"
+#include "hmap.h"
+#include "list.h"
+#include "netdev.h"
+#include "odp-util.h"
+#include "ofp-print.h"
+#include "ofpbuf.h"
+#include "packets.h"
+#include "poll-loop.h"
+#include "queue.h"
+#include "timeval.h"
+#include "util.h"
+
+#include "vlog.h"
+#define THIS_MODULE VLM_dpif_netdev
+
+/* Configuration parameters. */
+enum { N_QUEUES = 2 };          /* Number of queues for dpif_recv(). */
+enum { MAX_QUEUE_LEN = 100 };   /* Maximum number of packets per queue. */
+enum { N_GROUPS = 16 };         /* Number of port groups. */
+enum { MAX_PORTS = 256 };       /* Maximum number of ports. */
+enum { MAX_FLOWS = 65536 };     /* Maximum number of flows in flow table. */
+
+/* Enough headroom to add a vlan tag, plus an extra 2 bytes to allow IP
+ * headers to be aligned on a 4-byte boundary.  */
+enum { DP_NETDEV_HEADROOM = 2 + VLAN_HEADER_LEN };
+
+/* Datapath based on the network device interface from netdev.h. */
+struct dp_netdev {
+    struct list node;
+    int dp_idx;
+    int open_cnt;
+    bool deleted;
+
+    bool drop_frags;            /* Drop all IP fragments, if true. */
+    struct ovs_queue queues[N_QUEUES]; /* Messages queued for dpif_recv(). */
+    struct hmap flow_table;     /* Flow table. */
+    struct odp_port_group groups[N_GROUPS];
+
+    /* Statistics. */
+    long long int n_frags;      /* Number of dropped IP fragments. */
+    long long int n_hit;        /* Number of flow table matches. */
+    long long int n_missed;     /* Number of flow table misses. */
+    long long int n_lost;       /* Number of misses not passed to client. */
+
+    /* Ports. */
+    int n_ports;
+    struct dp_netdev_port *ports[MAX_PORTS];
+    struct list port_list;
+    unsigned int serial;
+};
+
+/* A port in a netdev-based datapath. */
+struct dp_netdev_port {
+    int port_no;                /* Index into dp_netdev's 'ports'. */
+    struct list node;           /* Element in dp_netdev's 'port_list'. */
+    struct netdev *netdev;
+    bool internal;              /* Internal port (as ODP_PORT_INTERNAL)? */
+};
+
+/* A flow in dp_netdev's 'flow_table'. */
+struct dp_netdev_flow {
+    struct hmap_node node;      /* Element in dp_netdev's 'flow_table'. */
+    flow_t key;
+
+    /* Statistics. */
+       struct timeval used;        /* Last used time, in milliseconds. */
+       long long int packet_count; /* Number of packets matched. */
+       long long int byte_count;   /* Number of bytes matched. */
+       uint8_t ip_tos;             /* IP TOS value. */
+       uint16_t tcp_ctl;           /* Bitwise-OR of seen tcp_ctl values. */
+
+    /* Actions. */
+    union odp_action *actions;
+    unsigned int n_actions;
+};
+
+/* Interface to netdev-based datapath. */
+struct dpif_netdev {
+    struct dpif dpif;
+    struct dp_netdev *dp;
+    int listen_mask;
+    unsigned int dp_serial;
+};
+
+/* All netdev-based datapaths. */
+static struct dp_netdev *dp_netdevs[256];
+struct list dp_netdev_list = LIST_INITIALIZER(&dp_netdev_list);
+enum { N_DP_NETDEVS = ARRAY_SIZE(dp_netdevs) };
+
+/* Maximum port MTU seen so far. */
+static int max_mtu = ETH_PAYLOAD_MAX;
+
+static int get_port_by_number(struct dp_netdev *, uint16_t port_no,
+                              struct dp_netdev_port **portp);
+static int get_port_by_name(struct dp_netdev *, const char *devname,
+                            struct dp_netdev_port **portp);
+static void dp_netdev_free(struct dp_netdev *);
+static void dp_netdev_flow_flush(struct dp_netdev *);
+static int do_add_port(struct dp_netdev *, const char *devname, uint16_t flags,
+                       uint16_t port_no);
+static int do_del_port(struct dp_netdev *, uint16_t port_no);
+static int dp_netdev_output_control(struct dp_netdev *, const struct ofpbuf *,
+                                    int queue_no, int port_no, uint32_t arg);
+static int dp_netdev_execute_actions(struct dp_netdev *,
+                                     struct ofpbuf *, flow_t *,
+                                     const union odp_action *, int n);
+
+static struct dpif_netdev *
+dpif_netdev_cast(const struct dpif *dpif)
+{
+    dpif_assert_class(dpif, &dpif_netdev_class);
+    return CONTAINER_OF(dpif, struct dpif_netdev, dpif);
+}
+
+static struct dp_netdev *
+get_dp_netdev(const struct dpif *dpif)
+{
+    return dpif_netdev_cast(dpif)->dp;
+}
+
+static int
+name_to_dp_idx(const char *name)
+{
+    if (!strncmp(name, "dp", 2) && isdigit(name[2])) {
+        int dp_idx = atoi(name + 2);
+        if (dp_idx >= 0 && dp_idx < N_DP_NETDEVS) {
+            return dp_idx;
+        }
+    }
+    return -1;
+}
+
+static struct dp_netdev *
+find_dp_netdev(const char *name)
+{
+    int dp_idx;
+    size_t i;
+
+    dp_idx = name_to_dp_idx(name);
+    if (dp_idx >= 0) {
+        return dp_netdevs[dp_idx];
+    }
+
+    for (i = 0; i < N_DP_NETDEVS; i++) {
+        struct dp_netdev *dp = dp_netdevs[i];
+        if (dp) {
+            struct dp_netdev_port *port;
+            if (!get_port_by_name(dp, name, &port)) {
+                return dp;
+            }
+        }
+    }
+    return NULL;
+}
+
+static struct dpif *
+create_dpif_netdev(struct dp_netdev *dp)
+{
+    struct dpif_netdev *dpif;
+    char *dpname;
+
+    dp->open_cnt++;
+
+    dpname = xasprintf("netdev:dp%d", dp->dp_idx);
+    dpif = xmalloc(sizeof *dpif);
+    dpif_init(&dpif->dpif, &dpif_netdev_class, dpname, dp->dp_idx, dp->dp_idx);
+    dpif->dp = dp;
+    dpif->listen_mask = 0;
+    dpif->dp_serial = dp->serial;
+    free(dpname);
+
+    return &dpif->dpif;
+}
+
+static int
+create_dp_netdev(const char *name, int dp_idx, struct dpif **dpifp)
+{
+    struct dp_netdev *dp;
+    int error;
+    int i;
+
+    if (dp_netdevs[dp_idx]) {
+        return EBUSY;
+    }
+
+    /* Create datapath. */
+    dp_netdevs[dp_idx] = dp = xcalloc(1, sizeof *dp);
+    list_push_back(&dp_netdev_list, &dp->node);
+    dp->dp_idx = dp_idx;
+    dp->open_cnt = 0;
+    dp->drop_frags = false;
+    for (i = 0; i < N_QUEUES; i++) {
+        queue_init(&dp->queues[i]);
+    }
+    hmap_init(&dp->flow_table);
+    for (i = 0; i < N_GROUPS; i++) {
+        dp->groups[i].ports = NULL;
+        dp->groups[i].n_ports = 0;
+        dp->groups[i].group = i;
+    }
+    list_init(&dp->port_list);
+    error = do_add_port(dp, name, ODP_PORT_INTERNAL, ODPP_LOCAL);
+    if (error) {
+        dp_netdev_free(dp);
+        return error;
+    }
+
+    *dpifp = create_dpif_netdev(dp);
+    return 0;
+}
+
+static int
+dpif_netdev_open(const char *name UNUSED, char *suffix, bool create,
+                 struct dpif **dpifp)
+{
+    if (create) {
+        if (find_dp_netdev(suffix)) {
+            return EEXIST;
+        } else {
+            int dp_idx = name_to_dp_idx(suffix);
+            if (dp_idx >= 0) {
+                return create_dp_netdev(suffix, dp_idx, dpifp);
+            } else {
+                /* Scan for unused dp_idx number. */
+                for (dp_idx = 0; dp_idx < N_DP_NETDEVS; dp_idx++) {
+                    int error = create_dp_netdev(suffix, dp_idx, dpifp);
+                    if (error != EBUSY) {
+                        return error;
+                    }
+                }
+
+                /* All datapath numbers in use. */
+                return ENOBUFS;
+            }
+        }
+    } else {
+        struct dp_netdev *dp = find_dp_netdev(suffix);
+        if (dp) {
+            *dpifp = create_dpif_netdev(dp);
+            return 0;
+        } else {
+            return ENODEV;
+        }
+    }
+}
+
+static void
+dp_netdev_free(struct dp_netdev *dp)
+{
+    int i;
+
+    dp_netdev_flow_flush(dp);
+    while (dp->n_ports > 0) {
+        struct dp_netdev_port *port = CONTAINER_OF(
+            dp->port_list.next, struct dp_netdev_port, node);
+        do_del_port(dp, port->port_no);
+    }
+    for (i = 0; i < N_QUEUES; i++) {
+        queue_destroy(&dp->queues[i]);
+    }
+    hmap_destroy(&dp->flow_table);
+    for (i = 0; i < N_GROUPS; i++) {
+        free(dp->groups[i].ports);
+    }
+    dp_netdevs[dp->dp_idx] = NULL;
+    list_remove(&dp->node);
+    free(dp);
+}
+
+static void
+dpif_netdev_close(struct dpif *dpif)
+{
+    struct dp_netdev *dp = get_dp_netdev(dpif);
+    assert(dp->open_cnt > 0);
+    if (--dp->open_cnt == 0 && dp->deleted) {
+        dp_netdev_free(dp);
+    }
+    free(dpif);
+}
+
+static int
+dpif_netdev_delete(struct dpif *dpif)
+{
+    struct dp_netdev *dp = get_dp_netdev(dpif);
+    dp->deleted = true;
+    return 0;
+}
+
+static int
+dpif_netdev_get_stats(const struct dpif *dpif, struct odp_stats *stats)
+{
+    struct dp_netdev *dp = get_dp_netdev(dpif);
+    memset(stats, 0, sizeof *stats);
+    stats->n_flows = hmap_count(&dp->flow_table);
+    stats->cur_capacity = hmap_capacity(&dp->flow_table);
+    stats->max_capacity = MAX_FLOWS;
+    stats->n_ports = dp->n_ports;
+    stats->max_ports = MAX_PORTS;
+    stats->max_groups = N_GROUPS;
+    stats->n_frags = dp->n_frags;
+    stats->n_hit = dp->n_hit;
+    stats->n_missed = dp->n_missed;
+    stats->n_lost = dp->n_lost;
+    stats->max_miss_queue = MAX_QUEUE_LEN;
+    stats->max_action_queue = MAX_QUEUE_LEN;
+    return 0;
+}
+
+static int
+dpif_netdev_get_drop_frags(const struct dpif *dpif, bool *drop_fragsp)
+{
+    struct dp_netdev *dp = get_dp_netdev(dpif);
+    *drop_fragsp = dp->drop_frags;
+    return 0;
+}
+
+static int
+dpif_netdev_set_drop_frags(struct dpif *dpif, bool drop_frags)
+{
+    struct dp_netdev *dp = get_dp_netdev(dpif);
+    dp->drop_frags = drop_frags;
+    return 0;
+}
+
+static int
+do_add_port(struct dp_netdev *dp, const char *devname, uint16_t flags,
+            uint16_t port_no)
+{
+    bool internal = (flags & ODP_PORT_INTERNAL) != 0;
+    struct dp_netdev_port *port;
+    struct netdev *netdev;
+    int mtu;
+    int error;
+
+    /* XXX reject devices already in some dp_netdev. */
+
+    /* Open and validate network device. */
+    if (!internal) {
+        error = netdev_open(devname, NETDEV_ETH_TYPE_ANY, &netdev);
+    } else {
+        char *tapname = xasprintf("tap:%s", devname);
+        error = netdev_open(tapname, NETDEV_ETH_TYPE_ANY, &netdev);
+        free(tapname);
+    }
+    if (error) {
+        return error;
+    }
+    /* XXX reject loopback devices */
+    /* XXX reject non-Ethernet devices */
+
+    error = netdev_turn_flags_on(netdev, NETDEV_PROMISC, false);
+    if (error) {
+        netdev_close(netdev);
+        return error;
+    }
+
+    port = xmalloc(sizeof *port);
+    port->port_no = port_no;
+    port->netdev = netdev;
+    port->internal = internal;
+
+    netdev_get_mtu(netdev, &mtu);
+    if (mtu > max_mtu) {
+        max_mtu = mtu;
+    }
+
+    list_push_back(&dp->port_list, &port->node);
+    dp->ports[port_no] = port;
+    dp->n_ports++;
+    dp->serial++;
+
+    return 0;
+}
+
+static int
+dpif_netdev_port_add(struct dpif *dpif, const char *devname, uint16_t flags,
+                     uint16_t *port_nop)
+{
+    struct dp_netdev *dp = get_dp_netdev(dpif);
+    int port_no;
+
+    for (port_no = 0; port_no < MAX_PORTS; port_no++) {
+        if (!dp->ports[port_no]) {
+            *port_nop = port_no;
+            return do_add_port(dp, devname, flags, port_no);
+        }
+    }
+    return EXFULL;
+}
+
+static int
+dpif_netdev_port_del(struct dpif *dpif, uint16_t port_no)
+{
+    struct dp_netdev *dp = get_dp_netdev(dpif);
+    return port_no == ODPP_LOCAL ? EINVAL : do_del_port(dp, port_no);
+}
+
+static bool
+is_valid_port_number(uint16_t port_no)
+{
+    return port_no < MAX_PORTS;
+}
+
+static int
+get_port_by_number(struct dp_netdev *dp,
+                   uint16_t port_no, struct dp_netdev_port **portp)
+{
+    if (!is_valid_port_number(port_no)) {
+        *portp = NULL;
+        return EINVAL;
+    } else {
+        *portp = dp->ports[port_no];
+        return *portp ? 0 : ENOENT;
+    }
+}
+
+static int
+get_port_by_name(struct dp_netdev *dp,
+                 const char *devname, struct dp_netdev_port **portp)
+{
+    struct dp_netdev_port *port;
+
+    LIST_FOR_EACH (port, struct dp_netdev_port, node, &dp->port_list) {
+        if (!strcmp(netdev_get_name(port->netdev), devname)) {
+            *portp = port;
+            return 0;
+        }
+    }
+    return ENOENT;
+}
+
+static int
+do_del_port(struct dp_netdev *dp, uint16_t port_no)
+{
+    struct dp_netdev_port *port;
+    int error;
+
+    error = get_port_by_number(dp, port_no, &port);
+    if (error) {
+        return error;
+    }
+
+    list_remove(&port->node);
+    dp->ports[port->port_no] = NULL;
+    dp->n_ports--;
+    dp->serial++;
+
+    netdev_close(port->netdev);
+    free(port);
+
+    return 0;
+}
+
+static void
+answer_port_query(const struct dp_netdev_port *port, struct odp_port *odp_port)
+{
+    memset(odp_port, 0, sizeof *odp_port);
+    ovs_strlcpy(odp_port->devname, netdev_get_name(port->netdev),
+                sizeof odp_port->devname);
+    odp_port->port = port->port_no;
+    odp_port->flags = port->internal ? ODP_PORT_INTERNAL : 0;
+}
+
+static int
+dpif_netdev_port_query_by_number(const struct dpif *dpif, uint16_t port_no,
+                                 struct odp_port *odp_port)
+{
+    struct dp_netdev *dp = get_dp_netdev(dpif);
+    struct dp_netdev_port *port;
+    int error;
+
+    error = get_port_by_number(dp, port_no, &port);
+    if (!error) {
+        answer_port_query(port, odp_port);
+    }
+    return error;
+}
+
+static int
+dpif_netdev_port_query_by_name(const struct dpif *dpif, const char *devname,
+                               struct odp_port *odp_port)
+{
+    struct dp_netdev *dp = get_dp_netdev(dpif);
+    struct dp_netdev_port *port;
+    int error;
+
+    error = get_port_by_name(dp, devname, &port);
+    if (!error) {
+        answer_port_query(port, odp_port);
+    }
+    return error;
+}
+
+static void
+dp_netdev_free_flow(struct dp_netdev *dp, struct dp_netdev_flow *flow)
+{
+    hmap_remove(&dp->flow_table, &flow->node);
+    free(flow->actions);
+    free(flow);
+}
+
+static void
+dp_netdev_flow_flush(struct dp_netdev *dp)
+{
+    struct dp_netdev_flow *flow, *next;
+
+    HMAP_FOR_EACH_SAFE (flow, next, struct dp_netdev_flow, node,
+                        &dp->flow_table) {
+        dp_netdev_free_flow(dp, flow);
+    }
+}
+
+static int
+dpif_netdev_flow_flush(struct dpif *dpif)
+{
+    struct dp_netdev *dp = get_dp_netdev(dpif);
+    dp_netdev_flow_flush(dp);
+    return 0;
+}
+
+static int
+dpif_netdev_port_list(const struct dpif *dpif, struct odp_port *ports, int n)
+{
+    struct dp_netdev *dp = get_dp_netdev(dpif);
+    struct dp_netdev_port *port;
+    int i;
+
+    i = 0;
+    LIST_FOR_EACH (port, struct dp_netdev_port, node, &dp->port_list) {
+        struct odp_port *odp_port = &ports[i];
+        if (i >= n) {
+            break;
+        }
+        answer_port_query(port, odp_port);
+        i++;
+    }
+    return dp->n_ports;
+}
+
+static int
+dpif_netdev_port_poll(const struct dpif *dpif_, char **devnamep UNUSED)
+{
+    struct dpif_netdev *dpif = dpif_netdev_cast(dpif_);
+    if (dpif->dp_serial != dpif->dp->serial) {
+        dpif->dp_serial = dpif->dp->serial;
+        return ENOBUFS;
+    } else {
+        return EAGAIN;
+    }
+}
+
+static void
+dpif_netdev_port_poll_wait(const struct dpif *dpif_)
+{
+    struct dpif_netdev *dpif = dpif_netdev_cast(dpif_);
+    if (dpif->dp_serial != dpif->dp->serial) {
+        poll_immediate_wake();
+    }
+}
+
+static int
+get_port_group(const struct dpif *dpif, int group_no,
+               struct odp_port_group **groupp)
+{
+    struct dp_netdev *dp = get_dp_netdev(dpif);
+
+    if (group_no >= 0 && group_no < N_GROUPS) {
+        *groupp = &dp->groups[group_no];
+        return 0;
+    } else {
+        *groupp = NULL;
+        return EINVAL;
+    }
+}
+
+static int
+dpif_netdev_port_group_get(const struct dpif *dpif, int group_no,
+                           uint16_t ports[], int n)
+{
+    struct odp_port_group *group;
+    int error;
+
+    if (n < 0) {
+        return -EINVAL;
+    }
+
+    error = get_port_group(dpif, group_no, &group);
+    if (!error) {
+        memcpy(ports, group->ports, MIN(n, group->n_ports) * sizeof *ports);
+        return group->n_ports;
+    } else {
+        return -error;
+    }
+}
+
+static int
+dpif_netdev_port_group_set(struct dpif *dpif, int group_no,
+                           const uint16_t ports[], int n)
+{
+    struct odp_port_group *group;
+    int error;
+
+    if (n < 0 || n > MAX_PORTS) {
+        return EINVAL;
+    }
+
+    error = get_port_group(dpif, group_no, &group);
+    if (!error) {
+        free(group->ports);
+        group->ports = xmemdup(ports, n * sizeof *group->ports);
+        group->n_ports = n;
+        group->group = group_no;
+    }
+    return error;
+}
+
+static struct dp_netdev_flow *
+dp_netdev_lookup_flow(const struct dp_netdev *dp, const flow_t *key)
+{
+    struct dp_netdev_flow *flow;
+
+    assert(key->reserved == 0);
+    HMAP_FOR_EACH_WITH_HASH (flow, struct dp_netdev_flow, node,
+                             flow_hash(key, 0), &dp->flow_table) {
+        if (flow_equal(&flow->key, key)) {
+            return flow;
+        }
+    }
+    return NULL;
+}
+
+static void
+answer_flow_query(const struct dp_netdev_flow *flow,
+                  struct odp_flow *odp_flow)
+{
+    if (flow) {
+        odp_flow->key = flow->key;
+        odp_flow->stats.n_packets = flow->packet_count;
+        odp_flow->stats.n_bytes = flow->byte_count;
+        odp_flow->stats.used_sec = flow->used.tv_sec;
+        odp_flow->stats.used_nsec = flow->used.tv_usec * 1000;
+        odp_flow->stats.tcp_flags = TCP_FLAGS(flow->tcp_ctl);
+        odp_flow->stats.ip_tos = flow->ip_tos;
+        odp_flow->stats.error = 0;
+        if (odp_flow->n_actions > 0) {
+            unsigned int n = MIN(odp_flow->n_actions, flow->n_actions);
+            memcpy(odp_flow->actions, flow->actions,
+                   n * sizeof *odp_flow->actions);
+            odp_flow->n_actions = flow->n_actions;
+        }
+    } else {
+        odp_flow->stats.error = ENOENT;
+    }
+}
+
+static int
+dpif_netdev_flow_get(const struct dpif *dpif, struct odp_flow flows[], int n)
+{
+    struct dp_netdev *dp = get_dp_netdev(dpif);
+    int i;
+
+    for (i = 0; i < n; i++) {
+        struct odp_flow *odp_flow = &flows[i];
+        answer_flow_query(dp_netdev_lookup_flow(dp, &odp_flow->key), odp_flow);
+    }
+    return 0;
+}
+
+static int
+dpif_netdev_validate_actions(const union odp_action *actions, int n_actions,
+                             bool *mutates)
+{
+       unsigned int i;
+
+    *mutates = false;
+       for (i = 0; i < n_actions; i++) {
+               const union odp_action *a = &actions[i];
+               switch (a->type) {
+               case ODPAT_OUTPUT:
+                       if (a->output.port >= MAX_PORTS) {
+                               return EINVAL;
+            }
+                       break;
+
+               case ODPAT_OUTPUT_GROUP:
+            *mutates = true;
+                       if (a->output_group.group >= N_GROUPS) {
+                               return EINVAL;
+            }
+                       break;
+
+        case ODPAT_CONTROLLER:
+            break;
+
+               case ODPAT_SET_VLAN_VID:
+            *mutates = true;
+                       if (a->vlan_vid.vlan_vid & htons(~VLAN_VID_MASK)) {
+                               return EINVAL;
+            }
+                       break;
+
+               case ODPAT_SET_VLAN_PCP:
+            *mutates = true;
+                       if (a->vlan_pcp.vlan_pcp & ~VLAN_PCP_MASK) {
+                               return EINVAL;
+            }
+                       break;
+
+        case ODPAT_STRIP_VLAN:
+        case ODPAT_SET_DL_SRC:
+        case ODPAT_SET_DL_DST:
+        case ODPAT_SET_NW_SRC:
+        case ODPAT_SET_NW_DST:
+        case ODPAT_SET_TP_SRC:
+        case ODPAT_SET_TP_DST:
+            *mutates = true;
+            break;
+
+               default:
+            return EOPNOTSUPP;
+               }
+       }
+       return 0;
+}
+
+static int
+set_flow_actions(struct dp_netdev_flow *flow, struct odp_flow *odp_flow)
+{
+    size_t n_bytes;
+    bool mutates;
+    int error;
+
+    if (odp_flow->n_actions >= 4096 / sizeof *odp_flow->actions) {
+        return EINVAL;
+    }
+    error = dpif_netdev_validate_actions(odp_flow->actions,
+                                         odp_flow->n_actions, &mutates);
+    if (error) {
+        return error;
+    }
+
+    n_bytes = odp_flow->n_actions * sizeof *flow->actions;
+    flow->actions = xrealloc(flow->actions, n_bytes);
+    flow->n_actions = odp_flow->n_actions;
+    memcpy(flow->actions, odp_flow->actions, n_bytes);
+    return 0;
+}
+
+static int
+add_flow(struct dpif *dpif, struct odp_flow *odp_flow)
+{
+    struct dp_netdev *dp = get_dp_netdev(dpif);
+    struct dp_netdev_flow *flow;
+    int error;
+
+    flow = xcalloc(1, sizeof *flow);
+    flow->key = odp_flow->key;
+    flow->key.reserved = 0;
+
+    error = set_flow_actions(flow, odp_flow);
+    if (error) {
+        free(flow);
+        return error;
+    }
+
+    hmap_insert(&dp->flow_table, &flow->node, flow_hash(&flow->key, 0));
+    return 0;
+}
+
+static void
+clear_stats(struct dp_netdev_flow *flow)
+{
+    flow->used.tv_sec = 0;
+    flow->used.tv_usec = 0;
+    flow->packet_count = 0;
+    flow->byte_count = 0;
+    flow->ip_tos = 0;
+    flow->tcp_ctl = 0;
+}
+
+static int
+dpif_netdev_flow_put(struct dpif *dpif, struct odp_flow_put *put)
+{
+    struct dp_netdev *dp = get_dp_netdev(dpif);
+    struct dp_netdev_flow *flow;
+
+    flow = dp_netdev_lookup_flow(dp, &put->flow.key);
+    if (!flow) {
+        if (put->flags & ODPPF_CREATE) {
+            if (hmap_count(&dp->flow_table) < MAX_FLOWS) {
+                return add_flow(dpif, &put->flow);
+            } else {
+                return EXFULL;
+            }
+        } else {
+            return ENOENT;
+        }
+    } else {
+        if (put->flags & ODPPF_MODIFY) {
+            int error = set_flow_actions(flow, &put->flow);
+            if (!error && put->flags & ODPPF_ZERO_STATS) {
+                clear_stats(flow);
+            }
+            return error;
+        } else {
+            return EEXIST;
+        }
+    }
+}
+
+
+static int
+dpif_netdev_flow_del(struct dpif *dpif, struct odp_flow *odp_flow)
+{
+    struct dp_netdev *dp = get_dp_netdev(dpif);
+    struct dp_netdev_flow *flow;
+
+    flow = dp_netdev_lookup_flow(dp, &odp_flow->key);
+    if (flow) {
+        answer_flow_query(flow, odp_flow);
+        dp_netdev_free_flow(dp, flow);
+        return 0;
+    } else {
+        return ENOENT;
+    }
+}
+
+static int
+dpif_netdev_flow_list(const struct dpif *dpif, struct odp_flow flows[], int n)
+{
+    struct dp_netdev *dp = get_dp_netdev(dpif);
+    struct dp_netdev_flow *flow;
+    int i;
+
+    i = 0;
+    HMAP_FOR_EACH (flow, struct dp_netdev_flow, node, &dp->flow_table) {
+        if (i >= n) {
+            break;
+        }
+        answer_flow_query(flow, &flows[i++]);
+    }
+    return hmap_count(&dp->flow_table);
+}
+
+static int
+dpif_netdev_execute(struct dpif *dpif, uint16_t in_port,
+                    const union odp_action actions[], int n_actions,
+                    const struct ofpbuf *packet)
+{
+    struct dp_netdev *dp = get_dp_netdev(dpif);
+    struct ofpbuf copy;
+    bool mutates;
+    flow_t flow;
+    int error;
+
+    if (packet->size < ETH_HLEN || packet->size > UINT16_MAX) {
+        return EINVAL;
+    }
+
+    error = dpif_netdev_validate_actions(actions, n_actions, &mutates);
+    if (error) {
+        return error;
+    }
+
+    if (mutates) {
+        /* We need a deep copy of 'packet' since we're going to modify its
+         * data. */
+        ofpbuf_init(&copy, DP_NETDEV_HEADROOM + packet->size);
+        copy.data = (char*)copy.base + DP_NETDEV_HEADROOM;
+        ofpbuf_put(&copy, packet->data, packet->size);
+    } else {
+        /* We still need a shallow copy of 'packet', even though we won't
+         * modify its data, because flow_extract() modifies packet->l2, etc.
+         * We could probably get away with modifying those but it's more polite
+         * if we don't. */
+        copy = *packet;
+    }
+    flow_extract(&copy, in_port, &flow);
+    error = dp_netdev_execute_actions(dp, &copy, &flow, actions, n_actions);
+    if (mutates) {
+        ofpbuf_uninit(&copy);
+    }
+    return error;
+}
+
+static int
+dpif_netdev_recv_get_mask(const struct dpif *dpif, int *listen_mask)
+{
+    struct dpif_netdev *dpif_netdev = dpif_netdev_cast(dpif);
+    *listen_mask = dpif_netdev->listen_mask;
+    return 0;
+}
+
+static int
+dpif_netdev_recv_set_mask(struct dpif *dpif, int listen_mask)
+{
+    struct dpif_netdev *dpif_netdev = dpif_netdev_cast(dpif);
+    if (!(listen_mask & ~ODPL_ALL)) {
+        dpif_netdev->listen_mask = listen_mask;
+        return 0;
+    } else {
+        return EINVAL;
+    }
+}
+
+static struct ovs_queue *
+find_nonempty_queue(struct dpif *dpif)
+{
+    struct dpif_netdev *dpif_netdev = dpif_netdev_cast(dpif);
+    struct dp_netdev *dp = get_dp_netdev(dpif);
+    int mask = dpif_netdev->listen_mask;
+    int i;
+
+    for (i = 0; i < N_QUEUES; i++) {
+        struct ovs_queue *q = &dp->queues[i];
+        if (q->n && mask & (1u << i)) {
+            return q;
+        }
+    }
+    return NULL;
+}
+
+static int
+dpif_netdev_recv(struct dpif *dpif, struct ofpbuf **bufp)
+{
+    struct ovs_queue *q = find_nonempty_queue(dpif);
+    if (q) {
+        *bufp = queue_pop_head(q);
+        return 0;
+    } else {
+        return EAGAIN;
+    }
+}
+
+static void
+dpif_netdev_recv_wait(struct dpif *dpif)
+{
+    struct ovs_queue *q = find_nonempty_queue(dpif);
+    if (q) {
+        poll_immediate_wake();
+    } else {
+        /* No messages ready to be received, and dp_wait() will ensure that we
+         * wake up to queue new messages, so there is nothing to do. */
+    }
+}
+\f
+static void
+dp_netdev_flow_used(struct dp_netdev_flow *flow, const flow_t *key,
+                    const struct ofpbuf *packet)
+{
+    time_timeval(&flow->used);
+    flow->packet_count++;
+    flow->byte_count += packet->size;
+    if (key->dl_type == htons(ETH_P_IP)) {
+        struct ip_header *nh = packet->l3;
+        flow->ip_tos = nh->ip_tos;
+
+        if (key->nw_proto == IPPROTO_TCP) {
+            struct tcp_header *th = packet->l4;
+            flow->tcp_ctl |= th->tcp_ctl;
+        }
+    }
+}
+
+static void
+dp_netdev_port_input(struct dp_netdev *dp, struct dp_netdev_port *port,
+                     struct ofpbuf *packet)
+{
+    struct dp_netdev_flow *flow;
+    flow_t key;
+
+    if (flow_extract(packet, port->port_no, &key) && dp->drop_frags) {
+        dp->n_frags++;
+        return;
+    }
+
+    flow = dp_netdev_lookup_flow(dp, &key);
+    if (flow) {
+        dp_netdev_flow_used(flow, &key, packet);
+        dp_netdev_execute_actions(dp, packet, &key,
+                                  flow->actions, flow->n_actions);
+        dp->n_hit++;
+    } else {
+        dp->n_missed++;
+        dp_netdev_output_control(dp, packet, _ODPL_MISS_NR, port->port_no, 0);
+    }
+}
+
+static void
+dp_netdev_run(void)
+{
+    struct ofpbuf packet;
+    struct dp_netdev *dp;
+
+    ofpbuf_init(&packet, DP_NETDEV_HEADROOM + max_mtu);
+    LIST_FOR_EACH (dp, struct dp_netdev, node, &dp_netdev_list) {
+        struct dp_netdev_port *port;
+
+        LIST_FOR_EACH (port, struct dp_netdev_port, node, &dp->port_list) {
+            int error;
+
+            /* Reset packet contents. */
+            packet.data = (char*)packet.base + DP_NETDEV_HEADROOM;
+            packet.size = 0;
+
+            error = netdev_recv(port->netdev, &packet);
+            if (!error) {
+                dp_netdev_port_input(dp, port, &packet);
+            } else if (error != EAGAIN) {
+                struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+                VLOG_ERR_RL(&rl, "error receiving data from %s: %s",
+                            netdev_get_name(port->netdev), strerror(error));
+            }
+        }
+    }
+    ofpbuf_uninit(&packet);
+}
+
+static void
+dp_netdev_wait(void)
+{
+    struct dp_netdev *dp;
+
+    LIST_FOR_EACH (dp, struct dp_netdev, node, &dp_netdev_list) {
+        struct dp_netdev_port *port;
+        LIST_FOR_EACH (port, struct dp_netdev_port, node, &dp->port_list) {
+            netdev_recv_wait(port->netdev);
+        }
+    }
+}
+
+static void
+dp_netdev_modify_vlan_tci(struct ofpbuf *packet, flow_t *key,
+                          uint16_t tci, uint16_t mask)
+{
+    struct vlan_eth_header *veh;
+
+    if (key->dl_vlan != htons(ODP_VLAN_NONE)) {
+        /* Modify 'mask' bits, but maintain other TCI bits. */
+        veh = packet->l2;
+        veh->veth_tci &= ~htons(mask);
+        veh->veth_tci |= htons(tci);
+    } else {
+        /* Insert new 802.1Q header. */
+        struct eth_header *eh = packet->l2;
+        struct vlan_eth_header tmp;
+        memcpy(tmp.veth_dst, eh->eth_dst, ETH_ADDR_LEN);
+        memcpy(tmp.veth_src, eh->eth_src, ETH_ADDR_LEN);
+        tmp.veth_type = htons(ETH_TYPE_VLAN);
+        tmp.veth_tci = htons(tci);
+        tmp.veth_next_type = eh->eth_type;
+
+        veh = ofpbuf_push_uninit(packet, VLAN_HEADER_LEN);
+        memcpy(veh, &tmp, sizeof tmp);
+        packet->l2 = (char*)packet->l2 - VLAN_HEADER_LEN;
+    }
+
+    key->dl_vlan = veh->veth_tci & htons(VLAN_VID_MASK);
+}
+
+static void
+dp_netdev_strip_vlan(struct ofpbuf *packet, flow_t *key)
+{
+    struct vlan_eth_header *veh = packet->l2;
+    if (veh->veth_type == htons(ETH_TYPE_VLAN)) {
+        struct eth_header tmp;
+
+        memcpy(tmp.eth_dst, veh->veth_dst, ETH_ADDR_LEN);
+        memcpy(tmp.eth_src, veh->veth_src, ETH_ADDR_LEN);
+        tmp.eth_type = veh->veth_next_type;
+
+        packet->size -= VLAN_HEADER_LEN;
+        packet->data = (char*)packet->data + VLAN_HEADER_LEN;
+        packet->l2 = (char*)packet->l2 + VLAN_HEADER_LEN;
+        memcpy(packet->data, &tmp, sizeof tmp);
+
+        key->dl_vlan = htons(ODP_VLAN_NONE);
+    }
+}
+
+static void
+dp_netdev_set_dl_src(struct ofpbuf *packet,
+                     const uint8_t dl_addr[ETH_ADDR_LEN])
+{
+    struct eth_header *eh = packet->l2;
+    memcpy(eh->eth_src, dl_addr, sizeof eh->eth_src);
+}
+
+static void
+dp_netdev_set_dl_dst(struct ofpbuf *packet,
+                     const uint8_t dl_addr[ETH_ADDR_LEN])
+{
+    struct eth_header *eh = packet->l2;
+    memcpy(eh->eth_dst, dl_addr, sizeof eh->eth_dst);
+}
+
+static void
+dp_netdev_set_nw_addr(struct ofpbuf *packet, flow_t *key,
+                      const struct odp_action_nw_addr *a)
+{
+    if (key->dl_type == htons(ETH_TYPE_IP)) {
+        struct ip_header *nh = packet->l3;
+        uint32_t *field;
+
+        field = a->type == ODPAT_SET_NW_SRC ? &nh->ip_src : &nh->ip_dst;
+        if (key->nw_proto == IP_TYPE_TCP) {
+            struct tcp_header *th = packet->l4;
+            th->tcp_csum = recalc_csum32(th->tcp_csum, *field, a->nw_addr);
+        } else if (key->nw_proto == IP_TYPE_UDP) {
+            struct udp_header *uh = packet->l4;
+            if (uh->udp_csum) {
+                uh->udp_csum = recalc_csum32(uh->udp_csum, *field, a->nw_addr);
+                if (!uh->udp_csum) {
+                    uh->udp_csum = 0xffff;
+                }
+            }
+        }
+        nh->ip_csum = recalc_csum32(nh->ip_csum, *field, a->nw_addr);
+        *field = a->nw_addr;
+    }
+}
+
+static void
+dp_netdev_set_tp_port(struct ofpbuf *packet, flow_t *key,
+                      const struct odp_action_tp_port *a)
+{
+       if (key->dl_type == htons(ETH_P_IP)) {
+        uint16_t *field;
+        if (key->nw_proto == IPPROTO_TCP) {
+            struct tcp_header *th = packet->l4;
+            field = a->type == ODPAT_SET_TP_SRC ? &th->tcp_src : &th->tcp_dst;
+            th->tcp_csum = recalc_csum16(th->tcp_csum, *field, a->tp_port);
+            *field = a->tp_port;
+        } else if (key->nw_proto == IPPROTO_UDP) {
+            struct udp_header *uh = packet->l4;
+            field = a->type == ODPAT_SET_TP_SRC ? &uh->udp_src : &uh->udp_dst;
+            uh->udp_csum = recalc_csum16(uh->udp_csum, *field, a->tp_port);
+            *field = a->tp_port;
+        }
+    }
+}
+
+static void
+dp_netdev_output_port(struct dp_netdev *dp, struct ofpbuf *packet,
+                      uint16_t out_port)
+{
+       struct dp_netdev_port *p = dp->ports[out_port];
+    if (p) {
+        netdev_send(p->netdev, packet);
+    }
+}
+
+static void
+dp_netdev_output_group(struct dp_netdev *dp, uint16_t group, uint16_t in_port,
+                       struct ofpbuf *packet)
+{
+       struct odp_port_group *g = &dp->groups[group];
+       int i;
+
+       for (i = 0; i < g->n_ports; i++) {
+        uint16_t out_port = g->ports[i];
+        if (out_port != in_port) {
+            dp_netdev_output_port(dp, packet, out_port);
+        }
+       }
+}
+
+static int
+dp_netdev_output_control(struct dp_netdev *dp, const struct ofpbuf *packet,
+                         int queue_no, int port_no, uint32_t arg)
+{
+    struct ovs_queue *q = &dp->queues[queue_no];
+    struct odp_msg *header;
+    struct ofpbuf *msg;
+    size_t msg_size;
+
+    if (q->n >= MAX_QUEUE_LEN) {
+        dp->n_lost++;
+        return ENOBUFS;
+    }
+
+    msg_size = sizeof *header + packet->size;
+    msg = ofpbuf_new(msg_size);
+    header = ofpbuf_put_uninit(msg, sizeof *header);
+    header->type = queue_no;
+    header->length = msg_size;
+    header->port = port_no;
+    header->arg = arg;
+    ofpbuf_put(msg, packet->data, packet->size);
+    queue_push_tail(q, msg);
+
+    return 0;
+}
+
+static int
+dp_netdev_execute_actions(struct dp_netdev *dp,
+                          struct ofpbuf *packet, flow_t *key,
+                          const union odp_action *actions, int n_actions)
+{
+    int i;
+    for (i = 0; i < n_actions; i++) {
+        const union odp_action *a = &actions[i];
+
+               switch (a->type) {
+               case ODPAT_OUTPUT:
+            dp_netdev_output_port(dp, packet, a->output.port);
+                       break;
+
+               case ODPAT_OUTPUT_GROUP:
+                       dp_netdev_output_group(dp, a->output_group.group, key->in_port,
+                                   packet);
+                       break;
+
+               case ODPAT_CONTROLLER:
+            dp_netdev_output_control(dp, packet, _ODPL_ACTION_NR,
+                                     key->in_port, a->controller.arg);
+                       break;
+
+               case ODPAT_SET_VLAN_VID:
+                       dp_netdev_modify_vlan_tci(packet, key, ntohs(a->vlan_vid.vlan_vid),
+                                      VLAN_VID_MASK);
+            break;
+
+               case ODPAT_SET_VLAN_PCP:
+                       dp_netdev_modify_vlan_tci(packet, key, a->vlan_pcp.vlan_pcp << 13,
+                                      VLAN_PCP_MASK);
+            break;
+
+               case ODPAT_STRIP_VLAN:
+                       dp_netdev_strip_vlan(packet, key);
+                       break;
+
+               case ODPAT_SET_DL_SRC:
+            dp_netdev_set_dl_src(packet, a->dl_addr.dl_addr);
+                       break;
+
+               case ODPAT_SET_DL_DST:
+            dp_netdev_set_dl_dst(packet, a->dl_addr.dl_addr);
+                       break;
+
+               case ODPAT_SET_NW_SRC:
+               case ODPAT_SET_NW_DST:
+                       dp_netdev_set_nw_addr(packet, key, &a->nw_addr);
+                       break;
+
+               case ODPAT_SET_TP_SRC:
+               case ODPAT_SET_TP_DST:
+                       dp_netdev_set_tp_port(packet, key, &a->tp_port);
+                       break;
+               }
+       }
+    return 0;
+}
+
+const struct dpif_class dpif_netdev_class = {
+    "netdev",
+    "netdev",
+    dp_netdev_run,
+    dp_netdev_wait,
+    NULL,                       /* enumerate */
+    dpif_netdev_open,
+    dpif_netdev_close,
+    NULL,                       /* get_all_names */
+    dpif_netdev_delete,
+    dpif_netdev_get_stats,
+    dpif_netdev_get_drop_frags,
+    dpif_netdev_set_drop_frags,
+    dpif_netdev_port_add,
+    dpif_netdev_port_del,
+    dpif_netdev_port_query_by_number,
+    dpif_netdev_port_query_by_name,
+    dpif_netdev_port_list,
+    dpif_netdev_port_poll,
+    dpif_netdev_port_poll_wait,
+    dpif_netdev_port_group_get,
+    dpif_netdev_port_group_set,
+    dpif_netdev_flow_get,
+    dpif_netdev_flow_put,
+    dpif_netdev_flow_del,
+    dpif_netdev_flow_flush,
+    dpif_netdev_flow_list,
+    dpif_netdev_execute,
+    dpif_netdev_recv_get_mask,
+    dpif_netdev_recv_set_mask,
+    dpif_netdev_recv,
+    dpif_netdev_recv_wait,
+};
diff --git a/lib/dpif-provider.h b/lib/dpif-provider.h
new file mode 100644 (file)
index 0000000..bd159b2
--- /dev/null
@@ -0,0 +1,298 @@
+/*
+ * Copyright (c) 2009 Nicira Networks.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef DPIF_PROVIDER_H
+#define DPIF_PROVIDER_H 1
+
+/* Provider interface to dpifs, which provide an interface to an Open vSwitch
+ * datapath. */
+
+#include <assert.h>
+#include "dpif.h"
+
+/* Open vSwitch datapath interface.
+ *
+ * This structure should be treated as opaque by dpif implementations. */
+struct dpif {
+    const struct dpif_class *class;
+    char *name;
+    uint8_t netflow_engine_type;
+    uint8_t netflow_engine_id;
+};
+
+void dpif_init(struct dpif *, const struct dpif_class *, const char *name,
+               uint8_t netflow_engine_type, uint8_t netflow_engine_id);
+static inline void dpif_assert_class(const struct dpif *dpif,
+                                     const struct dpif_class *class)
+{
+    assert(dpif->class == class);
+}
+
+/* Datapath interface class structure, to be defined by each implementation of
+ * a datapath interface
+ *
+ * These functions return 0 if successful or a positive errno value on failure,
+ * except where otherwise noted.
+ *
+ * These functions are expected to execute synchronously, that is, to block as
+ * necessary to obtain a result.  Thus, they may not return EAGAIN or
+ * EWOULDBLOCK or EINPROGRESS.  We may relax this requirement in the future if
+ * and when we encounter performance problems. */
+struct dpif_class {
+    /* Prefix for names of dpifs in this class, e.g. "udatapath:".
+     *
+     * One dpif class may have the empty string "" as its prefix, in which case
+     * that dpif class is associated with dpif names that don't match any other
+     * class name. */
+    const char *prefix;
+
+    /* Class name, for use in error messages. */
+    const char *name;
+
+    /* Performs periodic work needed by dpifs of this class, if any is
+     * necessary. */
+    void (*run)(void);
+
+    /* Arranges for poll_block() to wake up if the "run" member function needs
+     * to be called. */
+    void (*wait)(void);
+
+    /* Enumerates the names of all known created datapaths, if possible, into
+     * 'all_dps'.  The caller has already initialized 'all_dps' and other dpif
+     * classes might already have added names to it.
+     *
+     * This is used by the vswitch at startup, so that it can delete any
+     * datapaths that are not configured.
+     *
+     * Some kinds of datapaths might not be practically enumerable, in which
+     * case this function may be a null pointer. */
+    int (*enumerate)(struct svec *all_dps);
+
+    /* Attempts to open an existing dpif, if 'create' is false, or to open an
+     * existing dpif or create a new one, if 'create' is true.  'name' is the
+     * full dpif name provided by the user, e.g. "udatapath:/var/run/mypath".
+     * This name is useful for error messages but must not be modified.
+     *
+     * 'suffix' is a copy of 'name' following the dpif's 'prefix'.
+     *
+     * If successful, stores a pointer to the new dpif in '*dpifp'.  On failure
+     * there are no requirements on what is stored in '*dpifp'. */
+    int (*open)(const char *name, char *suffix, bool create,
+                struct dpif **dpifp);
+
+    /* Closes 'dpif' and frees associated memory. */
+    void (*close)(struct dpif *dpif);
+
+    /* Enumerates all names that may be used to open 'dpif' into 'all_names'.
+     * The Linux datapath, for example, supports opening a datapath both by
+     * number, e.g. "dp0", and by the name of the datapath's local port.  For
+     * some datapaths, this might be an infinite set (e.g. in a file name,
+     * slashes may be duplicated any number of times), in which case only the
+     * names most likely to be used should be enumerated.
+     *
+     * The caller has already initialized 'all_names' and might already have
+     * added some names to it.  This function should not disturb any existing
+     * names in 'all_names'.
+     *
+     * If a datapath class does not support multiple names for a datapath, this
+     * function may be a null pointer.
+     *
+     * This is used by the vswitch at startup, */
+    int (*get_all_names)(const struct dpif *dpif, struct svec *all_names);
+
+    /* Attempts to destroy the dpif underlying 'dpif'.
+     *
+     * If successful, 'dpif' will not be used again except as an argument for
+     * the 'close' member function. */
+    int (*delete)(struct dpif *dpif);
+
+    /* Retrieves statistics for 'dpif' into 'stats'. */
+    int (*get_stats)(const struct dpif *dpif, struct odp_stats *stats);
+
+    /* Retrieves 'dpif''s current treatment of IP fragments into '*drop_frags':
+     * true indicates that fragments are dropped, false indicates that
+     * fragments are treated in the same way as other IP packets (except that
+     * the L4 header cannot be read). */
+    int (*get_drop_frags)(const struct dpif *dpif, bool *drop_frags);
+
+    /* Changes 'dpif''s treatment of IP fragments to 'drop_frags', whose
+     * meaning is the same as for the get_drop_frags member function. */
+    int (*set_drop_frags)(struct dpif *dpif, bool drop_frags);
+
+    /* Creates a new port in 'dpif' connected to network device 'devname'.
+     * 'flags' is a set of ODP_PORT_* flags.  If successful, sets '*port_no'
+     * to the new port's port number. */
+    int (*port_add)(struct dpif *dpif, const char *devname, uint16_t flags,
+                    uint16_t *port_no);
+
+    /* Removes port numbered 'port_no' from 'dpif'. */
+    int (*port_del)(struct dpif *dpif, uint16_t port_no);
+
+    /* Queries 'dpif' for a port with the given 'port_no' or 'devname'.  Stores
+     * information about the port into '*port' if successful. */
+    int (*port_query_by_number)(const struct dpif *dpif, uint16_t port_no,
+                                struct odp_port *port);
+    int (*port_query_by_name)(const struct dpif *dpif, const char *devname,
+                              struct odp_port *port);
+
+    /* Stores in 'ports' information about up to 'n' ports attached to 'dpif',
+     * in no particular order.  Returns the number of ports attached to 'dpif'
+     * (not the number stored), if successful, otherwise a negative errno
+     * value. */
+    int (*port_list)(const struct dpif *dpif, struct odp_port *ports, int n);
+
+    /* Polls for changes in the set of ports in 'dpif'.  If the set of ports in
+     * 'dpif' has changed, then this function should do one of the
+     * following:
+     *
+     * - Preferably: store the name of the device that was added to or deleted
+     *   from 'dpif' in '*devnamep' and return 0.  The caller is responsible
+     *   for freeing '*devnamep' (with free()) when it no longer needs it.
+     *
+     * - Alternatively: return ENOBUFS, without indicating the device that was
+     *   added or deleted.
+     *
+     * Occasional 'false positives', in which the function returns 0 while
+     * indicating a device that was not actually added or deleted or returns
+     * ENOBUFS without any change, are acceptable.
+     *
+     * If the set of ports in 'dpif' has not changed, returns EAGAIN.  May also
+     * return other positive errno values to indicate that something has gone
+     * wrong. */
+    int (*port_poll)(const struct dpif *dpif, char **devnamep);
+
+    /* Arranges for the poll loop to wake up when 'port_poll' will return a
+     * value other than EAGAIN. */
+    void (*port_poll_wait)(const struct dpif *dpif);
+
+    /* Stores in 'ports' the port numbers of up to 'n' ports that belong to
+     * 'group' in 'dpif'.  Returns the number of ports in 'group' (not the
+     * number stored), if successful, otherwise a negative errno value. */
+    int (*port_group_get)(const struct dpif *dpif, int group,
+                          uint16_t ports[], int n);
+
+    /* Changes port group 'group' in 'dpif' to consist of the 'n' ports whose
+     * numbers are given in 'ports'.
+     *
+     * Use the get_stats member function to obtain the number of supported port
+     * groups. */
+    int (*port_group_set)(struct dpif *dpif, int group,
+                          const uint16_t ports[], int n);
+
+    /* For each flow 'flow' in the 'n' flows in 'flows':
+     *
+     * - If a flow matching 'flow->key' exists in 'dpif':
+     *
+     *     Stores 0 into 'flow->stats.error' and stores statistics for the flow
+     *     into 'flow->stats'.
+     *
+     *     If 'flow->n_actions' is zero, then 'flow->actions' is ignored.  If
+     *     'flow->n_actions' is nonzero, then 'flow->actions' should point to
+     *     an array of the specified number of actions.  At most that many of
+     *     the flow's actions will be copied into that array.
+     *     'flow->n_actions' will be updated to the number of actions actually
+     *     present in the flow, which may be greater than the number stored if
+     *     the flow has more actions than space available in the array.
+     *
+     * - Flow-specific errors are indicated by a positive errno value in
+     *   'flow->stats.error'.  In particular, ENOENT indicates that no flow
+     *   matching 'flow->key' exists in 'dpif'.  When an error value is stored,
+     *   the contents of 'flow->key' are preserved but other members of 'flow'
+     *   should be treated as indeterminate.
+     *
+     * Returns 0 if all 'n' flows in 'flows' were updated (whether they were
+     * individually successful or not is indicated by 'flow->stats.error',
+     * however).  Returns a positive errno value if an error that prevented
+     * this update occurred, in which the caller must not depend on any
+     * elements in 'flows' being updated or not updated.
+     */
+    int (*flow_get)(const struct dpif *dpif, struct odp_flow flows[], int n);
+
+    /* Adds or modifies a flow in 'dpif' as specified in 'put':
+     *
+     * - If the flow specified in 'put->flow' does not exist in 'dpif', then
+     *   behavior depends on whether ODPPF_CREATE is specified in 'put->flags':
+     *   if it is, the flow will be added, otherwise the operation will fail
+     *   with ENOENT.
+     *
+     * - Otherwise, the flow specified in 'put->flow' does exist in 'dpif'.
+     *   Behavior in this case depends on whether ODPPF_MODIFY is specified in
+     *   'put->flags': if it is, the flow's actions will be updated, otherwise
+     *   the operation will fail with EEXIST.  If the flow's actions are
+     *   updated, then its statistics will be zeroed if ODPPF_ZERO_STATS is set
+     *   in 'put->flags', left as-is otherwise.
+     */
+    int (*flow_put)(struct dpif *dpif, struct odp_flow_put *put);
+
+    /* Deletes a flow matching 'flow->key' from 'dpif' or returns ENOENT if
+     * 'dpif' does not contain such a flow.
+     *
+     * If successful, updates 'flow->stats', 'flow->n_actions', and
+     * 'flow->actions' as described in more detail under the flow_get member
+     * function below. */
+    int (*flow_del)(struct dpif *dpif, struct odp_flow *flow);
+
+    /* Deletes all flows from 'dpif' and clears all of its queues of received
+     * packets. */
+    int (*flow_flush)(struct dpif *dpif);
+
+    /* Stores up to 'n' flows in 'dpif' into 'flows', updating their statistics
+     * and actions as described under the flow_get member function.  If
+     * successful, returns the number of flows actually present in 'dpif',
+     * which might be greater than the number stored (if 'dpif' has more than
+     * 'n' flows).  On failure, returns a negative errno value. */
+    int (*flow_list)(const struct dpif *dpif, struct odp_flow flows[], int n);
+
+    /* Performs the 'n_actions' actions in 'actions' on the Ethernet frame
+     * specified in 'packet'.
+     *
+     * Pretends that the frame was originally received on the port numbered
+     * 'in_port'.  This affects only ODPAT_OUTPUT_GROUP actions, which will not
+     * send a packet out their input port.  Specify the number of an unused
+     * port (e.g. UINT16_MAX is currently always unused) to avoid this
+     * behavior. */
+    int (*execute)(struct dpif *dpif, uint16_t in_port,
+                   const union odp_action actions[], int n_actions,
+                   const struct ofpbuf *packet);
+
+    /* Retrieves 'dpif''s "listen mask" into '*listen_mask'.  Each ODPL_* bit
+     * set in '*listen_mask' indicates the 'dpif' will receive messages of the
+     * corresponding type when it calls the recv member function. */
+    int (*recv_get_mask)(const struct dpif *dpif, int *listen_mask);
+
+    /* Sets 'dpif''s "listen mask" to 'listen_mask'.  Each ODPL_* bit set in
+     * 'listen_mask' indicates the 'dpif' will receive messages of the
+     * corresponding type when it calls the recv member function. */
+    int (*recv_set_mask)(struct dpif *dpif, int listen_mask);
+
+    /* Attempts to receive a message from 'dpif'.  If successful, stores the
+     * message into '*packetp'.  The message, if one is received, must begin
+     * with 'struct odp_msg' as a header.  Only messages of the types selected
+     * with the set_listen_mask member function should be received.
+     *
+     * This function must not block.  If no message is ready to be received
+     * when it is called, it should return EAGAIN without blocking. */
+    int (*recv)(struct dpif *dpif, struct ofpbuf **packetp);
+
+    /* Arranges for the poll loop to wake up when 'dpif' has a message queued
+     * to be received with the recv member function. */
+    void (*recv_wait)(struct dpif *dpif);
+};
+
+extern const struct dpif_class dpif_linux_class;
+extern const struct dpif_class dpif_netdev_class;
+
+#endif /* dpif-provider.h */
index 80f6ed6..e823fa3 100644 (file)
  */
 
 #include <config.h>
-#include "dpif.h"
+#include "dpif-provider.h"
 
 #include <assert.h>
 #include <ctype.h>
 #include <errno.h>
-#include <fcntl.h>
 #include <inttypes.h>
-#include <net/if.h>
-#include <linux/rtnetlink.h>
-#include <linux/ethtool.h>
-#include <linux/sockios.h>
-#include <netinet/in.h>
 #include <stdlib.h>
 #include <string.h>
-#include <sys/ioctl.h>
-#include <sys/stat.h>
-#include <sys/sysmacros.h>
-#include <unistd.h>
 
 #include "coverage.h"
 #include "dynamic-string.h"
 #include "vlog.h"
 #define THIS_MODULE VLM_dpif
 
+static const struct dpif_class *dpif_classes[] = {
+    &dpif_linux_class,
+    &dpif_netdev_class,
+};
+enum { N_DPIF_CLASSES = ARRAY_SIZE(dpif_classes) };
+
 /* Rate limit for individual messages going to or from the datapath, output at
  * DBG level.  This is very high because, if these are enabled, it is because
  * we really need to see them. */
@@ -58,17 +54,53 @@ static struct vlog_rate_limit dpmsg_rl = VLOG_RATE_LIMIT_INIT(600, 600);
 /* Not really much point in logging many dpif errors. */
 static struct vlog_rate_limit error_rl = VLOG_RATE_LIMIT_INIT(9999, 5);
 
-static int get_minor_from_name(const char *name, unsigned int *minor);
-static int name_to_minor(const char *name, unsigned int *minor);
-static int lookup_minor(const char *name, unsigned int *minor);
-static int open_by_minor(unsigned int minor, struct dpif *);
-static int make_openvswitch_device(unsigned int minor, char **fnp);
+static void log_operation(const struct dpif *, const char *operation,
+                          int error);
+static void log_flow_operation(const struct dpif *, const char *operation,
+                               int error, struct odp_flow *flow);
+static void log_flow_put(struct dpif *, int error,
+                         const struct odp_flow_put *);
+static bool should_log_flow_message(int error);
 static void check_rw_odp_flow(struct odp_flow *);
 
+/* Performs periodic work needed by all the various kinds of dpifs.
+ *
+ * If your program opens any dpifs, it must call both this function and
+ * netdev_run() within its main poll loop. */
+void
+dp_run(void)
+{
+    int i;
+    for (i = 0; i < N_DPIF_CLASSES; i++) {
+        const struct dpif_class *class = dpif_classes[i];
+        if (class->run) {
+            class->run();
+        }
+    }
+}
 
-/* Clears 'all_dps' and enumerates the names of all known created
- * datapaths into it.  Returns 0 if successful, otherwise a positive 
- * errno value. */
+/* Arranges for poll_block() to wake up when dp_run() needs to be called.
+ *
+ * If your program opens any dpifs, it must call both this function and
+ * netdev_wait() within its main poll loop. */
+void
+dp_wait(void)
+{
+    int i;
+    for (i = 0; i < N_DPIF_CLASSES; i++) {
+        const struct dpif_class *class = dpif_classes[i];
+        if (class->wait) {
+            class->wait();
+        }
+    }
+}
+
+/* Clears 'all_dps' and enumerates the names of all known created datapaths, 
+ * where possible, into it.  The caller must first initialize 'all_dps'.
+ * Returns 0 if successful, otherwise a positive errno value.
+ *
+ * Some kinds of datapaths might not be practically enumerable.  This is not
+ * considered an error. */
 int
 dp_enumerate(struct svec *all_dps)
 {
@@ -77,484 +109,588 @@ dp_enumerate(struct svec *all_dps)
 
     svec_clear(all_dps);
     error = 0;
-    for (i = 0; i < ODP_MAX; i++) {
-        struct dpif dpif;
-        char devname[16];
-        int retval;
-
-        sprintf(devname, "dp%d", i);
-        retval = dpif_open(devname, &dpif);
-        if (!retval) {
-            svec_add(all_dps, devname);
-            dpif_close(&dpif);
-        } else if (retval != ENODEV && !error) {
-            error = retval;
+    for (i = 0; i < N_DPIF_CLASSES; i++) {
+        const struct dpif_class *class = dpif_classes[i];
+        int retval = class->enumerate ? class->enumerate(all_dps) : 0;
+        if (retval) {
+            VLOG_WARN("failed to enumerate %s datapaths: %s",
+                      class->name, strerror(retval));
+            if (!error) {
+                error = retval;
+            }
         }
     }
     return error;
 }
 
-int
-dpif_open(const char *name, struct dpif *dpif)
+static int
+do_open(const char *name_, bool create, struct dpif **dpifp)
 {
-    int listen_mask;
+    char *name = xstrdup(name_);
+    char *prefix, *suffix, *colon;
+    struct dpif *dpif = NULL;
     int error;
+    int i;
 
-    dpif->fd = -1;
-
-    error = name_to_minor(name, &dpif->minor);
-    if (error) {
-        return error;
-    }
-
-    error = open_by_minor(dpif->minor, dpif);
-    if (error) {
-        return error;
+    colon = strchr(name, ':');
+    if (colon) {
+        *colon = '\0';
+        prefix = name;
+        suffix = colon + 1;
+    } else {
+        prefix = "";
+        suffix = name;
     }
 
-    /* We can open the device, but that doesn't mean that it's been created.
-     * If it hasn't been, then any command other than ODP_DP_CREATE will
-     * return ENODEV.  Try something innocuous. */
-    listen_mask = 0;            /* Make Valgrind happy. */
-    if (ioctl(dpif->fd, ODP_GET_LISTEN_MASK, &listen_mask)) {
-        error = errno;
-        if (error != ENODEV) {
-            VLOG_WARN("dp%u: probe returned unexpected error: %s",
-                      dpif->minor, strerror(error));
+    for (i = 0; i < N_DPIF_CLASSES; i++) {
+        const struct dpif_class *class = dpif_classes[i];
+        if (!strcmp(prefix, class->prefix)) {
+            error = class->open(name_, suffix, create, &dpif);
+            goto exit;
         }
-        dpif_close(dpif);
-        return error;
     }
-    return 0;
+    error = EAFNOSUPPORT;
+
+exit:
+    *dpifp = error ? NULL : dpif;
+    return error;
 }
 
+/* Tries to open an existing datapath named 'name'.  Will fail if no datapath
+ * named 'name' exists.  Returns 0 if successful, otherwise a positive errno
+ * value.  On success stores a pointer to the datapath in '*dpifp', otherwise a
+ * null pointer. */
+int
+dpif_open(const char *name, struct dpif **dpifp)
+{
+    return do_open(name, false, dpifp);
+}
+
+/* Tries to create and open a new datapath with the given 'name'.  Will fail if
+ * a datapath named 'name' already exists.  Returns 0 if successful, otherwise
+ * a positive errno value.  On success stores a pointer to the datapath in
+ * '*dpifp', otherwise a null pointer.*/
+int
+dpif_create(const char *name, struct dpif **dpifp)
+{
+    return do_open(name, true, dpifp);
+}
+
+/* Closes and frees the connection to 'dpif'.  Does not destroy the datapath
+ * itself; call dpif_delete() first, instead, if that is desirable. */
 void
 dpif_close(struct dpif *dpif)
 {
     if (dpif) {
-        close(dpif->fd);
-        dpif->fd = -1;
+        char *name = dpif->name;
+        dpif->class->close(dpif);
+        free(name);
     }
 }
 
-static int
-do_ioctl(const struct dpif *dpif, int cmd, const char *cmd_name,
-         const void *arg)
+/* Returns the name of datapath 'dpif' (for use in log messages). */
+const char *
+dpif_name(const struct dpif *dpif)
 {
-    int error = ioctl(dpif->fd, cmd, arg) ? errno : 0;
-    if (cmd_name) {
-        if (error) {
-            VLOG_WARN_RL(&error_rl, "dp%u: ioctl(%s) failed (%s)",
-                         dpif->minor, cmd_name, strerror(error));
-        } else {
-            VLOG_DBG_RL(&dpmsg_rl, "dp%u: ioctl(%s): success",
-                        dpif->minor, cmd_name);
-        }
-    }
-    return error;
+    return dpif->name;
 }
 
+/* Enumerates all names that may be used to open 'dpif' into 'all_names'.  The
+ * Linux datapath, for example, supports opening a datapath both by number,
+ * e.g. "dp0", and by the name of the datapath's local port.  For some
+ * datapaths, this might be an infinite set (e.g. in a file name, slashes may
+ * be duplicated any number of times), in which case only the names most likely
+ * to be used will be enumerated.
+ *
+ * The caller must already have initialized 'all_names'.  Any existing names in
+ * 'all_names' will not be disturbed. */
 int
-dpif_create(const char *name, struct dpif *dpif)
+dpif_get_all_names(const struct dpif *dpif, struct svec *all_names)
 {
-    unsigned int minor;
-    int error;
-
-    if (!get_minor_from_name(name, &minor)) {
-        /* Minor was specified in 'name', go ahead and create it. */
-        error = open_by_minor(minor, dpif);
-        if (error) {
-            return error;
-        }
-
-        if (!strncmp(name, "nl:", 3)) {
-            char devname[128];
-            sprintf(devname, "of%u", minor);
-            error = ioctl(dpif->fd, ODP_DP_CREATE, devname) < 0 ? errno : 0;
-        } else {
-            error = ioctl(dpif->fd, ODP_DP_CREATE, name) < 0 ? errno : 0;
-        }
+    if (dpif->class->get_all_names) {
+        int error = dpif->class->get_all_names(dpif, all_names);
         if (error) {
-            dpif_close(dpif);
+            VLOG_WARN_RL(&error_rl,
+                         "failed to retrieve names for datpath %s: %s",
+                         dpif_name(dpif), strerror(error));
         }
         return error;
     } else {
-        for (minor = 0; minor < ODP_MAX; minor++) {
-            error = open_by_minor(minor, dpif);
-            if (error) {
-                return error;
-            }
-
-            error = ioctl(dpif->fd, ODP_DP_CREATE, name) < 0 ? errno : 0;
-            if (!error) {
-                return 0;
-            }
-            dpif_close(dpif);
-            if (error != EBUSY) {
-                return error;
-            }
-        }
-        return ENOBUFS;
+        svec_add(all_names, dpif_name(dpif));
+        return 0;
     }
 }
 
+/* Destroys the datapath that 'dpif' is connected to, first removing all of its
+ * ports.  After calling this function, it does not make sense to pass 'dpif'
+ * to any functions other than dpif_name() or dpif_close(). */
 int
-dpif_get_name(struct dpif *dpif, char *name, size_t name_size)
+dpif_delete(struct dpif *dpif)
 {
-    struct odp_port port;
     int error;
 
-    assert(name_size > 0);
-    *name = '\0';
+    COVERAGE_INC(dpif_destroy);
 
-    error = dpif_port_query_by_number(dpif, ODPP_LOCAL, &port);
-    if (!error) {
-        ovs_strlcpy(name, port.devname, name_size);
-    }
+    error = dpif->class->delete(dpif);
+    log_operation(dpif, "delete", error);
     return error;
 }
 
-int
-dpif_delete(struct dpif *dpif)
-{
-    COVERAGE_INC(dpif_destroy);
-    return do_ioctl(dpif, ODP_DP_DESTROY, "ODP_DP_DESTROY", NULL);
-}
-
+/* Retrieves statistics for 'dpif' into 'stats'.  Returns 0 if successful,
+ * otherwise a positive errno value. */
 int
 dpif_get_dp_stats(const struct dpif *dpif, struct odp_stats *stats)
 {
-    memset(stats, 0, sizeof *stats);
-    return do_ioctl(dpif, ODP_DP_STATS, "ODP_DP_STATS", stats);
-}
-
-int
-dpif_get_drop_frags(const struct dpif *dpif, bool *drop_frags)
-{
-    int tmp;
-    int error = do_ioctl(dpif, ODP_GET_DROP_FRAGS, "ODP_GET_DROP_FRAGS", &tmp);
-    *drop_frags = error ? tmp & 1 : false;
+    int error = dpif->class->get_stats(dpif, stats);
+    if (error) {
+        memset(stats, 0, sizeof *stats);
+    }
+    log_operation(dpif, "get_stats", error);
     return error;
 }
 
+/* Retrieves the current IP fragment handling policy for 'dpif' into
+ * '*drop_frags': true indicates that fragments are dropped, false indicates
+ * that fragments are treated in the same way as other IP packets (except that
+ * the L4 header cannot be read).  Returns 0 if successful, otherwise a
+ * positive errno value. */
 int
-dpif_set_drop_frags(struct dpif *dpif, bool drop_frags)
-{
-    int tmp = drop_frags;
-    return do_ioctl(dpif, ODP_SET_DROP_FRAGS, "ODP_SET_DROP_FRAGS", &tmp);
-}
-
-int
-dpif_get_listen_mask(const struct dpif *dpif, int *listen_mask)
+dpif_get_drop_frags(const struct dpif *dpif, bool *drop_frags)
 {
-    int error = do_ioctl(dpif, ODP_GET_LISTEN_MASK, "ODP_GET_LISTEN_MASK",
-                         listen_mask);
+    int error = dpif->class->get_drop_frags(dpif, drop_frags);
     if (error) {
-        *listen_mask = 0;
+        *drop_frags = false;
     }
+    log_operation(dpif, "get_drop_frags", error);
     return error;
 }
 
+/* Changes 'dpif''s treatment of IP fragments to 'drop_frags', whose meaning is
+ * the same as for the get_drop_frags member function.  Returns 0 if
+ * successful, otherwise a positive errno value. */
 int
-dpif_set_listen_mask(struct dpif *dpif, int listen_mask)
+dpif_set_drop_frags(struct dpif *dpif, bool drop_frags)
 {
-    return do_ioctl(dpif, ODP_SET_LISTEN_MASK, "ODP_SET_LISTEN_MASK",
-                    &listen_mask);
+    int error = dpif->class->set_drop_frags(dpif, drop_frags);
+    log_operation(dpif, "set_drop_frags", error);
+    return error;
 }
 
+/* Attempts to add 'devname' as a port on 'dpif', given the combination of
+ * ODP_PORT_* flags in 'flags'.  If successful, returns 0 and sets '*port_nop'
+ * to the new port's port number (if 'port_nop' is non-null).  On failure,
+ * returns a positive errno value and sets '*port_nop' to UINT16_MAX (if
+ * 'port_nop' is non-null). */
 int
-dpif_purge(struct dpif *dpif)
+dpif_port_add(struct dpif *dpif, const char *devname, uint16_t flags,
+              uint16_t *port_nop)
 {
-    struct odp_stats stats;
-    unsigned int i;
+    uint16_t port_no;
     int error;
 
-    COVERAGE_INC(dpif_purge);
-
-    error = dpif_get_dp_stats(dpif, &stats);
-    if (error) {
-        return error;
-    }
-
-    for (i = 0; i < stats.max_miss_queue + stats.max_action_queue; i++) {
-        struct ofpbuf *buf;
-        error = dpif_recv(dpif, &buf);
-        if (error) {
-            return error == EAGAIN ? 0 : error;
-        }
-        ofpbuf_delete(buf);
-    }
-    return 0;
-}
-
-int
-dpif_port_add(struct dpif *dpif, const char *devname, uint16_t port_no,
-              uint16_t flags)
-{
-    struct odp_port port;
-
     COVERAGE_INC(dpif_port_add);
-    memset(&port, 0, sizeof port);
-    strncpy(port.devname, devname, sizeof port.devname);
-    port.port = port_no;
-    port.flags = flags;
-    if (!ioctl(dpif->fd, ODP_PORT_ADD, &port)) {
-        VLOG_DBG_RL(&dpmsg_rl, "dp%u: added %s as port %"PRIu16,
-                    dpif->minor, devname, port_no);
-        return 0;
+
+    error = dpif->class->port_add(dpif, devname, flags, &port_no);
+    if (!error) {
+        VLOG_DBG_RL(&dpmsg_rl, "%s: added %s as port %"PRIu16,
+                    dpif_name(dpif), devname, port_no);
     } else {
-        VLOG_WARN_RL(&error_rl, "dp%u: failed to add %s as port "
-                     "%"PRIu16": %s", dpif->minor, devname, port_no,
-                     strerror(errno));
-        return errno;
+        VLOG_WARN_RL(&error_rl, "%s: failed to add %s as port: %s",
+                     dpif_name(dpif), devname, strerror(error));
+        port_no = UINT16_MAX;
+    }
+    if (port_nop) {
+        *port_nop = port_no;
     }
+    return error;
 }
 
+/* Attempts to remove 'dpif''s port number 'port_no'.  Returns 0 if successful,
+ * otherwise a positive errno value. */
 int
 dpif_port_del(struct dpif *dpif, uint16_t port_no)
 {
-    int tmp = port_no;
+    int error;
+
     COVERAGE_INC(dpif_port_del);
-    return do_ioctl(dpif, ODP_PORT_DEL, "ODP_PORT_DEL", &tmp);
+
+    error = dpif->class->port_del(dpif, port_no);
+    log_operation(dpif, "port_del", error);
+    return error;
 }
 
+/* Looks up port number 'port_no' in 'dpif'.  On success, returns 0 and
+ * initializes '*port' appropriately; on failure, returns a positive errno
+ * value. */
 int
 dpif_port_query_by_number(const struct dpif *dpif, uint16_t port_no,
                           struct odp_port *port)
 {
-    memset(port, 0, sizeof *port);
-    port->port = port_no;
-    if (!ioctl(dpif->fd, ODP_PORT_QUERY, port)) {
-        VLOG_DBG_RL(&dpmsg_rl, "dp%u: port %"PRIu16" is device %s",
-                    dpif->minor, port_no, port->devname);
-        return 0;
+    int error = dpif->class->port_query_by_number(dpif, port_no, port);
+    if (!error) {
+        VLOG_DBG_RL(&dpmsg_rl, "%s: port %"PRIu16" is device %s",
+                    dpif_name(dpif), port_no, port->devname);
     } else {
-        VLOG_WARN_RL(&error_rl, "dp%u: failed to query port %"PRIu16": %s",
-                     dpif->minor, port_no, strerror(errno));
-        return errno;
+        memset(port, 0, sizeof *port);
+        VLOG_WARN_RL(&error_rl, "%s: failed to query port %"PRIu16": %s",
+                     dpif_name(dpif), port_no, strerror(error));
     }
+    return error;
 }
 
+/* Looks up port named 'devname' in 'dpif'.  On success, returns 0 and
+ * initializes '*port' appropriately; on failure, returns a positive errno
+ * value. */
 int
 dpif_port_query_by_name(const struct dpif *dpif, const char *devname,
                         struct odp_port *port)
 {
-    memset(port, 0, sizeof *port);
-    strncpy(port->devname, devname, sizeof port->devname);
-    if (!ioctl(dpif->fd, ODP_PORT_QUERY, port)) {
-        VLOG_DBG_RL(&dpmsg_rl, "dp%u: device %s is on port %"PRIu16,
-                    dpif->minor, devname, port->port);
-        return 0;
+    int error = dpif->class->port_query_by_name(dpif, devname, port);
+    if (!error) {
+        VLOG_DBG_RL(&dpmsg_rl, "%s: device %s is on port %"PRIu16,
+                    dpif_name(dpif), devname, port->port);
+    } else {
+        memset(port, 0, sizeof *port);
+
+        /* Log level is DBG here because all the current callers are interested
+         * in whether 'dpif' actually has a port 'devname', so that it's not an
+         * issue worth logging if it doesn't. */
+        VLOG_DBG_RL(&error_rl, "%s: failed to query port %s: %s",
+                    dpif_name(dpif), devname, strerror(error));
+    }
+    return error;
+}
+
+/* Looks up port number 'port_no' in 'dpif'.  On success, returns 0 and copies
+ * the port's name into the 'name_size' bytes in 'name', ensuring that the
+ * result is null-terminated.  On failure, returns a positive errno value and
+ * makes 'name' the empty string. */
+int
+dpif_port_get_name(struct dpif *dpif, uint16_t port_no,
+                   char *name, size_t name_size)
+{
+    struct odp_port port;
+    int error;
+
+    assert(name_size > 0);
+
+    error = dpif_port_query_by_number(dpif, port_no, &port);
+    if (!error) {
+        ovs_strlcpy(name, port.devname, name_size);
     } else {
-        VLOG_WARN_RL(&error_rl, "dp%u: failed to query port %s: %s",
-                     dpif->minor, devname, strerror(errno));
-        return errno;
+        *name = '\0';
     }
+    return error;
 }
 
+/* Obtains a list of all the ports in 'dpif'.
+ *
+ * If successful, returns 0 and sets '*portsp' to point to an array of
+ * appropriately initialized port structures and '*n_portsp' to the number of
+ * ports in the array.  The caller is responsible for freeing '*portp' by
+ * calling free().
+ *
+ * On failure, returns a positive errno value and sets '*portsp' to NULL and
+ * '*n_portsp' to 0. */
 int
 dpif_port_list(const struct dpif *dpif,
-               struct odp_port **ports, size_t *n_ports)
+               struct odp_port **portsp, size_t *n_portsp)
 {
-    struct odp_portvec pv;
-    struct odp_stats stats;
+    struct odp_port *ports;
+    size_t n_ports;
     int error;
 
-    do {
+    for (;;) {
+        struct odp_stats stats;
+        int retval;
+
         error = dpif_get_dp_stats(dpif, &stats);
         if (error) {
-            goto error;
+            goto exit;
         }
 
-        *ports = xcalloc(1, stats.n_ports * sizeof **ports);
-        pv.ports = *ports;
-        pv.n_ports = stats.n_ports;
-        error = do_ioctl(dpif, ODP_PORT_LIST, "ODP_PORT_LIST", &pv);
-        if (error) {
-            free(*ports);
-            goto error;
+        ports = xcalloc(stats.n_ports, sizeof *ports);
+        retval = dpif->class->port_list(dpif, ports, stats.n_ports);
+        if (retval < 0) {
+            /* Hard error. */
+            error = -retval;
+            free(ports);
+            goto exit;
+        } else if (retval <= stats.n_ports) {
+            /* Success. */
+            error = 0;
+            n_ports = retval;
+            goto exit;
+        } else {
+            /* Soft error: port count increased behind our back.  Try again. */
+            free(ports);
         }
-    } while (pv.n_ports != stats.n_ports);
-    *n_ports = pv.n_ports;
-    return 0;
+    }
 
-error:
-    *ports = NULL;
-    *n_ports = 0;
+exit:
+    if (error) {
+        *portsp = NULL;
+        *n_portsp = 0;
+    } else {
+        *portsp = ports;
+        *n_portsp = n_ports;
+    }
+    log_operation(dpif, "port_list", error);
     return error;
 }
 
+/* Polls for changes in the set of ports in 'dpif'.  If the set of ports in
+ * 'dpif' has changed, this function does one of the following:
+ *
+ * - Stores the name of the device that was added to or deleted from 'dpif' in
+ *   '*devnamep' and returns 0.  The caller is responsible for freeing
+ *   '*devnamep' (with free()) when it no longer needs it.
+ *
+ * - Returns ENOBUFS and sets '*devnamep' to NULL.
+ *
+ * This function may also return 'false positives', where it returns 0 and
+ * '*devnamep' names a device that was not actually added or deleted or it
+ * returns ENOBUFS without any change.
+ *
+ * Returns EAGAIN if the set of ports in 'dpif' has not changed.  May also
+ * return other positive errno values to indicate that something has gone
+ * wrong. */
 int
-dpif_port_group_set(struct dpif *dpif, uint16_t group,
-                    const uint16_t ports[], size_t n_ports)
+dpif_port_poll(const struct dpif *dpif, char **devnamep)
 {
-    struct odp_port_group pg;
+    int error = dpif->class->port_poll(dpif, devnamep);
+    if (error) {
+        *devnamep = NULL;
+    }
+    return error;
+}
 
-    COVERAGE_INC(dpif_port_group_set);
-    assert(n_ports <= UINT16_MAX);
-    pg.group = group;
-    pg.ports = (uint16_t *) ports;
-    pg.n_ports = n_ports;
-    return do_ioctl(dpif, ODP_PORT_GROUP_SET, "ODP_PORT_GROUP_SET", &pg);
+/* Arranges for the poll loop to wake up when port_poll(dpif) will return a
+ * value other than EAGAIN. */
+void
+dpif_port_poll_wait(const struct dpif *dpif)
+{
+    dpif->class->port_poll_wait(dpif);
 }
 
-/* Careful: '*n_out' can be greater than 'n_ports' on return, if 'n_ports' is
- * less than the number of ports in 'group'. */
+/* Retrieves a list of the port numbers in port group 'group' in 'dpif'.
+ *
+ * On success, returns 0 and points '*ports' to a newly allocated array of
+ * integers, each of which is a 'dpif' port number for a port in
+ * 'group'.  Stores the number of elements in the array in '*n_ports'.  The
+ * caller is responsible for freeing '*ports' by calling free().
+ *
+ * On failure, returns a positive errno value and sets '*ports' to NULL and
+ * '*n_ports' to 0. */
 int
 dpif_port_group_get(const struct dpif *dpif, uint16_t group,
-                    uint16_t ports[], size_t n_ports, size_t *n_out)
+                    uint16_t **ports, size_t *n_ports)
 {
-    struct odp_port_group pg;
     int error;
 
-    assert(n_ports <= UINT16_MAX);
-    pg.group = group;
-    pg.ports = ports;
-    pg.n_ports = n_ports;
-    error = do_ioctl(dpif, ODP_PORT_GROUP_GET, "ODP_PORT_GROUP_GET", &pg);
-    *n_out = error ? 0 : pg.n_ports;
+    *ports = NULL;
+    *n_ports = 0;
+    for (;;) {
+        int retval = dpif->class->port_group_get(dpif, group,
+                                                 *ports, *n_ports);
+        if (retval < 0) {
+            /* Hard error. */
+            error = -retval;
+            free(*ports);
+            *ports = NULL;
+            *n_ports = 0;
+            break;
+        } else if (retval <= *n_ports) {
+            /* Success. */
+            error = 0;
+            *n_ports = retval;
+            break;
+        } else {
+            /* Soft error: there were more ports than we expected in the
+             * group.  Try again. */
+            free(*ports);
+            *ports = xcalloc(retval, sizeof **ports);
+            *n_ports = retval;
+        }
+    }
+    log_operation(dpif, "port_group_get", error);
     return error;
 }
 
+/* Updates port group 'group' in 'dpif', making it contain the 'n_ports' ports
+ * whose 'dpif' port numbers are given in 'n_ports'.  Returns 0 if
+ * successful, otherwise a positive errno value.
+ *
+ * Behavior is undefined if the values in ports[] are not unique. */
 int
-dpif_flow_flush(struct dpif *dpif)
+dpif_port_group_set(struct dpif *dpif, uint16_t group,
+                    const uint16_t ports[], size_t n_ports)
 {
-    COVERAGE_INC(dpif_flow_flush);
-    return do_ioctl(dpif, ODP_FLOW_FLUSH, "ODP_FLOW_FLUSH", NULL);
-}
+    int error;
 
-static enum vlog_level
-flow_message_log_level(int error)
-{
-    return error ? VLL_WARN : VLL_DBG;
+    COVERAGE_INC(dpif_port_group_set);
+
+    error = dpif->class->port_group_set(dpif, group, ports, n_ports);
+    log_operation(dpif, "port_group_set", error);
+    return error;
 }
 
-static bool
-should_log_flow_message(int error)
+/* Deletes all flows from 'dpif'.  Returns 0 if successful, otherwise a
+ * positive errno value.  */
+int
+dpif_flow_flush(struct dpif *dpif)
 {
-    return !vlog_should_drop(THIS_MODULE, flow_message_log_level(error),
-                             error ? &error_rl : &dpmsg_rl);
+    int error;
+
+    COVERAGE_INC(dpif_flow_flush);
+
+    error = dpif->class->flow_flush(dpif);
+    log_operation(dpif, "flow_flush", error);
+    return error;
 }
 
-static void
-log_flow_message(const struct dpif *dpif, int error,
-                 const char *operation,
-                 const flow_t *flow, const struct odp_flow_stats *stats,
-                 const union odp_action *actions, size_t n_actions)
+/* Queries 'dpif' for a flow entry matching 'flow->key'.
+ *
+ * If a flow matching 'flow->key' exists in 'dpif', stores statistics for the
+ * flow into 'flow->stats'.  If 'flow->n_actions' is zero, then 'flow->actions'
+ * is ignored.  If 'flow->n_actions' is nonzero, then 'flow->actions' should
+ * point to an array of the specified number of actions.  At most that many of
+ * the flow's actions will be copied into that array.  'flow->n_actions' will
+ * be updated to the number of actions actually present in the flow, which may
+ * be greater than the number stored if the flow has more actions than space
+ * available in the array.
+ *
+ * If no flow matching 'flow->key' exists in 'dpif', returns ENOENT.  On other
+ * failure, returns a positive errno value. */
+int
+dpif_flow_get(const struct dpif *dpif, struct odp_flow *flow)
 {
-    struct ds ds = DS_EMPTY_INITIALIZER;
-    ds_put_format(&ds, "dp%u: ", dpif->minor);
-    if (error) {
-        ds_put_cstr(&ds, "failed to ");
-    }
-    ds_put_format(&ds, "%s ", operation);
-    if (error) {
-        ds_put_format(&ds, "(%s) ", strerror(error));
-    }
-    flow_format(&ds, flow);
-    if (stats) {
-        ds_put_cstr(&ds, ", ");
-        format_odp_flow_stats(&ds, stats);
+    int error;
+
+    COVERAGE_INC(dpif_flow_get);
+
+    check_rw_odp_flow(flow);
+    error = dpif->class->flow_get(dpif, flow, 1);
+    if (!error) {
+        error = flow->stats.error;
     }
-    if (actions || n_actions) {
-        ds_put_cstr(&ds, ", actions:");
-        format_odp_actions(&ds, actions, n_actions);
+    if (should_log_flow_message(error)) {
+        log_flow_operation(dpif, "flow_get", error, flow);
     }
-    vlog(THIS_MODULE, flow_message_log_level(error), "%s", ds_cstr(&ds));
-    ds_destroy(&ds);
+    return error;
 }
 
-static int
-do_flow_ioctl(const struct dpif *dpif, int cmd, struct odp_flow *flow,
-              const char *operation, bool show_stats)
+/* For each flow 'flow' in the 'n' flows in 'flows':
+ *
+ * - If a flow matching 'flow->key' exists in 'dpif':
+ *
+ *     Stores 0 into 'flow->stats.error' and stores statistics for the flow
+ *     into 'flow->stats'.
+ *
+ *     If 'flow->n_actions' is zero, then 'flow->actions' is ignored.  If
+ *     'flow->n_actions' is nonzero, then 'flow->actions' should point to an
+ *     array of the specified number of actions.  At most that many of the
+ *     flow's actions will be copied into that array.  'flow->n_actions' will
+ *     be updated to the number of actions actually present in the flow, which
+ *     may be greater than the number stored if the flow has more actions than
+ *     space available in the array.
+ *
+ * - Flow-specific errors are indicated by a positive errno value in
+ *   'flow->stats.error'.  In particular, ENOENT indicates that no flow
+ *   matching 'flow->key' exists in 'dpif'.  When an error value is stored, the
+ *   contents of 'flow->key' are preserved but other members of 'flow' should
+ *   be treated as indeterminate.
+ *
+ * Returns 0 if all 'n' flows in 'flows' were updated (whether they were
+ * individually successful or not is indicated by 'flow->stats.error',
+ * however).  Returns a positive errno value if an error that prevented this
+ * update occurred, in which the caller must not depend on any elements in
+ * 'flows' being updated or not updated.
+ */
+int
+dpif_flow_get_multiple(const struct dpif *dpif,
+                       struct odp_flow flows[], size_t n)
 {
-    int error = do_ioctl(dpif, cmd, NULL, flow);
-    if (error && show_stats) {
-        flow->n_actions = 0;
-    }
-    if (should_log_flow_message(error)) {
-        log_flow_message(dpif, error, operation, &flow->key,
-                         show_stats && !error ? &flow->stats : NULL,
-                         flow->actions, flow->n_actions);
+    int error;
+    size_t i;
+
+    COVERAGE_ADD(dpif_flow_get, n);
+
+    for (i = 0; i < n; i++) {
+        check_rw_odp_flow(&flows[i]);
     }
+
+    error = dpif->class->flow_get(dpif, flows, n);
+    log_operation(dpif, "flow_get_multiple", error);
     return error;
 }
 
+/* Adds or modifies a flow in 'dpif' as specified in 'put':
+ *
+ * - If the flow specified in 'put->flow' does not exist in 'dpif', then
+ *   behavior depends on whether ODPPF_CREATE is specified in 'put->flags': if
+ *   it is, the flow will be added, otherwise the operation will fail with
+ *   ENOENT.
+ *
+ * - Otherwise, the flow specified in 'put->flow' does exist in 'dpif'.
+ *   Behavior in this case depends on whether ODPPF_MODIFY is specified in
+ *   'put->flags': if it is, the flow's actions will be updated, otherwise the
+ *   operation will fail with EEXIST.  If the flow's actions are updated, then
+ *   its statistics will be zeroed if ODPPF_ZERO_STATS is set in 'put->flags',
+ *   left as-is otherwise.
+ *
+ * Returns 0 if successful, otherwise a positive errno value.
+ */
 int
 dpif_flow_put(struct dpif *dpif, struct odp_flow_put *put)
 {
-    int error = do_ioctl(dpif, ODP_FLOW_PUT, NULL, put);
+    int error;
+
     COVERAGE_INC(dpif_flow_put);
+
+    error = dpif->class->flow_put(dpif, put);
     if (should_log_flow_message(error)) {
-        struct ds operation = DS_EMPTY_INITIALIZER;
-        ds_put_cstr(&operation, "put");
-        if (put->flags & ODPPF_CREATE) {
-            ds_put_cstr(&operation, "[create]");
-        }
-        if (put->flags & ODPPF_MODIFY) {
-            ds_put_cstr(&operation, "[modify]");
-        }
-        if (put->flags & ODPPF_ZERO_STATS) {
-            ds_put_cstr(&operation, "[zero]");
-        }
-#define ODPPF_ALL (ODPPF_CREATE | ODPPF_MODIFY | ODPPF_ZERO_STATS)
-        if (put->flags & ~ODPPF_ALL) {
-            ds_put_format(&operation, "[%x]", put->flags & ~ODPPF_ALL);
-        }
-        log_flow_message(dpif, error, ds_cstr(&operation), &put->flow.key,
-                         !error ? &put->flow.stats : NULL,
-                         put->flow.actions, put->flow.n_actions);
-        ds_destroy(&operation);
+        log_flow_put(dpif, error, put);
     }
     return error;
 }
 
+/* Deletes a flow matching 'flow->key' from 'dpif' or returns ENOENT if 'dpif'
+ * does not contain such a flow.
+ *
+ * If successful, updates 'flow->stats', 'flow->n_actions', and 'flow->actions'
+ * as described for dpif_flow_get(). */
 int
 dpif_flow_del(struct dpif *dpif, struct odp_flow *flow)
 {
+    int error;
+
     COVERAGE_INC(dpif_flow_del);
-    check_rw_odp_flow(flow);
-    memset(&flow->stats, 0, sizeof flow->stats);
-    return do_flow_ioctl(dpif, ODP_FLOW_DEL, flow, "delete flow", true);
-}
 
-int
-dpif_flow_get(const struct dpif *dpif, struct odp_flow *flow)
-{
-    COVERAGE_INC(dpif_flow_query);
     check_rw_odp_flow(flow);
     memset(&flow->stats, 0, sizeof flow->stats);
-    return do_flow_ioctl(dpif, ODP_FLOW_GET, flow, "get flow", true);
-}
-
-int
-dpif_flow_get_multiple(const struct dpif *dpif,
-                       struct odp_flow flows[], size_t n)
-{
-    struct odp_flowvec fv;
-    size_t i;
 
-    COVERAGE_ADD(dpif_flow_query_multiple, n);
-    fv.flows = flows;
-    fv.n_flows = n;
-    for (i = 0; i < n; i++) {
-        check_rw_odp_flow(&flows[i]);
+    error = dpif->class->flow_del(dpif, flow);
+    if (should_log_flow_message(error)) {
+        log_flow_operation(dpif, "delete flow", error, flow);
     }
-    return do_ioctl(dpif, ODP_FLOW_GET_MULTIPLE, "ODP_FLOW_GET_MULTIPLE",
-                    &fv);
+    return error;
 }
 
+/* Stores up to 'n' flows in 'dpif' into 'flows', including their statistics
+ * but not including any information about their actions.  If successful,
+ * returns 0 and sets '*n_out' to the number of flows actually present in
+ * 'dpif', which might be greater than the number stored (if 'dpif' has more
+ * than 'n' flows).  On failure, returns a negative errno value and sets
+ * '*n_out' to 0. */
 int
 dpif_flow_list(const struct dpif *dpif, struct odp_flow flows[], size_t n,
                size_t *n_out)
 {
-    struct odp_flowvec fv;
     uint32_t i;
-    int error;
+    int retval;
 
     COVERAGE_INC(dpif_flow_query_list);
-    fv.flows = flows;
-    fv.n_flows = n;
     if (RUNNING_ON_VALGRIND) {
         memset(flows, 0, n * sizeof *flows);
     } else {
@@ -563,19 +699,31 @@ dpif_flow_list(const struct dpif *dpif, struct odp_flow flows[], size_t n,
             flows[i].n_actions = 0;
         }
     }
-    error = do_ioctl(dpif, ODP_FLOW_LIST, NULL, &fv);
-    if (error) {
+    retval = dpif->class->flow_list(dpif, flows, n);
+    if (retval < 0) {
         *n_out = 0;
-        VLOG_WARN_RL(&error_rl, "dp%u: flow list failed (%s)",
-                     dpif->minor, strerror(error));
+        VLOG_WARN_RL(&error_rl, "%s: flow list failed (%s)",
+                     dpif_name(dpif), strerror(-retval));
+        return -retval;
     } else {
-        COVERAGE_ADD(dpif_flow_query_list_n, fv.n_flows);
-        *n_out = fv.n_flows;
-        VLOG_DBG_RL(&dpmsg_rl, "dp%u: listed %zu flows", dpif->minor, *n_out);
+        COVERAGE_ADD(dpif_flow_query_list_n, retval);
+        *n_out = MIN(n, retval);
+        VLOG_DBG_RL(&dpmsg_rl, "%s: listed %zu flows (of %d)",
+                    dpif_name(dpif), *n_out, retval);
+        return 0;
     }
-    return error;
 }
 
+/* Retrieves all of the flows in 'dpif'.
+ *
+ * If successful, returns 0 and stores in '*flowsp' a pointer to a newly
+ * allocated array of flows, including their statistics but not including any
+ * information about their actions, and sets '*np' to the number of flows in
+ * '*flowsp'.  The caller is responsible for freeing '*flowsp' by calling
+ * free().
+ *
+ * On failure, returns a positive errno value and sets '*flowsp' to NULL and
+ * '*np' to 0. */
 int
 dpif_flow_list_all(const struct dpif *dpif,
                    struct odp_flow **flowsp, size_t *np)
@@ -601,15 +749,24 @@ dpif_flow_list_all(const struct dpif *dpif,
     }
 
     if (stats.n_flows != n_flows) {
-        VLOG_WARN_RL(&error_rl, "dp%u: datapath stats reported %"PRIu32" "
+        VLOG_WARN_RL(&error_rl, "%s: datapath stats reported %"PRIu32" "
                      "flows but flow listing reported %zu",
-                     dpif->minor, stats.n_flows, n_flows);
+                     dpif_name(dpif), stats.n_flows, n_flows);
     }
     *flowsp = flows;
     *np = n_flows;
     return 0;
 }
 
+/* Causes 'dpif' to perform the 'n_actions' actions in 'actions' on the
+ * Ethernet frame specified in 'packet'.
+ *
+ * Pretends that the frame was originally received on the port numbered
+ * 'in_port'.  This affects only ODPAT_OUTPUT_GROUP actions, which will not
+ * send a packet out their input port.  Specify the number of an unused port
+ * (e.g. UINT16_MAX is currently always unused) to avoid this behavior.
+ *
+ * Returns 0 if successful, otherwise a positive errno value. */
 int
 dpif_execute(struct dpif *dpif, uint16_t in_port,
              const union odp_action actions[], size_t n_actions,
@@ -619,14 +776,7 @@ dpif_execute(struct dpif *dpif, uint16_t in_port,
 
     COVERAGE_INC(dpif_execute);
     if (n_actions > 0) {
-        struct odp_execute execute;
-        memset(&execute, 0, sizeof execute);
-        execute.in_port = in_port;
-        execute.actions = (union odp_action *) actions;
-        execute.n_actions = n_actions;
-        execute.data = buf->data;
-        execute.length = buf->size;
-        error = do_ioctl(dpif, ODP_EXECUTE, NULL, &execute);
+        error = dpif->class->execute(dpif, in_port, actions, n_actions, buf);
     } else {
         error = 0;
     }
@@ -634,7 +784,7 @@ dpif_execute(struct dpif *dpif, uint16_t in_port,
     if (!(error ? VLOG_DROP_WARN(&error_rl) : VLOG_DROP_DBG(&dpmsg_rl))) {
         struct ds ds = DS_EMPTY_INITIALIZER;
         char *packet = ofp_packet_to_string(buf->data, buf->size, buf->size);
-        ds_put_format(&ds, "dp%u: execute ", dpif->minor);
+        ds_put_format(&ds, "%s: execute ", dpif_name(dpif));
         format_odp_actions(&ds, actions, n_actions);
         if (error) {
             ds_put_format(&ds, " failed (%s)", strerror(error));
@@ -647,424 +797,208 @@ dpif_execute(struct dpif *dpif, uint16_t in_port,
     return error;
 }
 
+/* Retrieves 'dpif''s "listen mask" into '*listen_mask'.  Each ODPL_* bit set
+ * in '*listen_mask' indicates that dpif_recv() will receive messages of that
+ * type.  Returns 0 if successful, otherwise a positive errno value. */
 int
-dpif_recv(struct dpif *dpif, struct ofpbuf **bufp)
+dpif_recv_get_mask(const struct dpif *dpif, int *listen_mask)
 {
-    struct ofpbuf *buf;
-    int retval;
-    int error;
-
-    buf = ofpbuf_new(65536);
-    retval = read(dpif->fd, ofpbuf_tail(buf), ofpbuf_tailroom(buf));
-    if (retval < 0) {
-        error = errno;
-        if (error != EAGAIN) {
-            VLOG_WARN_RL(&error_rl, "dp%u: read failed: %s",
-                         dpif->minor, strerror(error));
-        }
-    } else if (retval >= sizeof(struct odp_msg)) {
-        struct odp_msg *msg = buf->data;
-        if (msg->length <= retval) {
-            buf->size += retval;
-            if (VLOG_IS_DBG_ENABLED()) {
-                void *payload = msg + 1;
-                size_t length = buf->size - sizeof *msg;
-                char *s = ofp_packet_to_string(payload, length, length);
-                VLOG_DBG_RL(&dpmsg_rl, "dp%u: received %s message of length "
-                            "%zu on port %"PRIu16": %s", dpif->minor,
-                            (msg->type == _ODPL_MISS_NR ? "miss"
-                             : msg->type == _ODPL_ACTION_NR ? "action"
-                             : "<unknown>"),
-                            msg->length - sizeof(struct odp_msg),
-                            msg->port, s);
-                free(s);
-            }
-            *bufp = buf;
-            COVERAGE_INC(dpif_recv);
-            return 0;
-        } else {
-            VLOG_WARN_RL(&error_rl, "dp%u: discarding message truncated "
-                         "from %zu bytes to %d",
-                         dpif->minor, msg->length, retval);
-            error = ERANGE;
-        }
-    } else if (!retval) {
-        VLOG_WARN_RL(&error_rl, "dp%u: unexpected end of file", dpif->minor);
-        error = EPROTO;
-    } else {
-        VLOG_WARN_RL(&error_rl,
-                     "dp%u: discarding too-short message (%d bytes)",
-                     dpif->minor, retval);
-        error = ERANGE;
+    int error = dpif->class->recv_get_mask(dpif, listen_mask);
+    if (error) {
+        *listen_mask = 0;
     }
-
-    *bufp = NULL;
-    ofpbuf_delete(buf);
+    log_operation(dpif, "recv_get_mask", error);
     return error;
 }
 
-void
-dpif_recv_wait(struct dpif *dpif)
-{
-    poll_fd_wait(dpif->fd, POLLIN);
-}
-\f
-struct dpifmon {
-    struct dpif dpif;
-    struct nl_sock *sock;
-    int local_ifindex;
-};
-
+/* Sets 'dpif''s "listen mask" to 'listen_mask'.  Each ODPL_* bit set in
+ * '*listen_mask' requests that dpif_recv() receive messages of that type.
+ * Returns 0 if successful, otherwise a positive errno value. */
 int
-dpifmon_create(const char *datapath_name, struct dpifmon **monp)
+dpif_recv_set_mask(struct dpif *dpif, int listen_mask)
 {
-    struct dpifmon *mon;
-    char local_name[IFNAMSIZ];
-    int error;
-
-    mon = *monp = xmalloc(sizeof *mon);
-
-    error = dpif_open(datapath_name, &mon->dpif);
-    if (error) {
-        goto error;
-    }
-    error = dpif_get_name(&mon->dpif, local_name, sizeof local_name);
-    if (error) {
-        goto error_close_dpif;
-    }
-
-    mon->local_ifindex = if_nametoindex(local_name);
-    if (!mon->local_ifindex) {
-        error = errno;
-        VLOG_WARN("could not get ifindex of %s device: %s",
-                  local_name, strerror(errno));
-        goto error_close_dpif;
-    }
-
-    error = nl_sock_create(NETLINK_ROUTE, RTNLGRP_LINK, 0, 0, &mon->sock);
-    if (error) {
-        VLOG_WARN("could not create rtnetlink socket: %s", strerror(error));
-        goto error_close_dpif;
-    }
-
-    return 0;
-
-error_close_dpif:
-    dpif_close(&mon->dpif);
-error:
-    free(mon);
-    *monp = NULL;
+    int error = dpif->class->recv_set_mask(dpif, listen_mask);
+    log_operation(dpif, "recv_set_mask", error);
     return error;
 }
 
-void
-dpifmon_destroy(struct dpifmon *mon)
+/* Attempts to receive a message from 'dpif'.  If successful, stores the
+ * message into '*packetp'.  The message, if one is received, will begin with
+ * 'struct odp_msg' as a header.  Only messages of the types selected with
+ * dpif_set_listen_mask() will ordinarily be received (but if a message type is
+ * enabled and then later disabled, some stragglers might pop up).
+ *
+ * Returns 0 if successful, otherwise a positive errno value.  Returns EAGAIN
+ * if no message is immediately available. */
+int
+dpif_recv(struct dpif *dpif, struct ofpbuf **packetp)
 {
-    if (mon) {
-        dpif_close(&mon->dpif);
-        nl_sock_destroy(mon->sock);
+    int error = dpif->class->recv(dpif, packetp);
+    if (!error) {
+        if (VLOG_IS_DBG_ENABLED()) {
+            struct ofpbuf *buf = *packetp;
+            struct odp_msg *msg = buf->data;
+            void *payload = msg + 1;
+            size_t payload_len = buf->size - sizeof *msg;
+            char *s = ofp_packet_to_string(payload, payload_len, payload_len);
+            VLOG_DBG_RL(&dpmsg_rl, "%s: received %s message of length "
+                        "%zu on port %"PRIu16": %s", dpif_name(dpif),
+                        (msg->type == _ODPL_MISS_NR ? "miss"
+                         : msg->type == _ODPL_ACTION_NR ? "action"
+                         : "<unknown>"),
+                        payload_len, msg->port, s);
+            free(s);
+        }
+    } else {
+        *packetp = NULL;
     }
+    return error;
 }
 
+/* Discards all messages that would otherwise be received by dpif_recv() on
+ * 'dpif'.  Returns 0 if successful, otherwise a positive errno value. */
 int
-dpifmon_poll(struct dpifmon *mon, char **devnamep)
+dpif_recv_purge(struct dpif *dpif)
 {
-    static struct vlog_rate_limit slow_rl = VLOG_RATE_LIMIT_INIT(1, 5);
-    static const struct nl_policy rtnlgrp_link_policy[] = {
-        [IFLA_IFNAME] = { .type = NL_A_STRING },
-        [IFLA_MASTER] = { .type = NL_A_U32, .optional = true },
-    };
-    struct nlattr *attrs[ARRAY_SIZE(rtnlgrp_link_policy)];
-    struct ofpbuf *buf;
+    struct odp_stats stats;
+    unsigned int i;
     int error;
 
-    *devnamep = NULL;
-again:
-    error = nl_sock_recv(mon->sock, &buf, false);
-    switch (error) {
-    case 0:
-        if (!nl_policy_parse(buf, NLMSG_HDRLEN + sizeof(struct ifinfomsg),
-                             rtnlgrp_link_policy,
-                             attrs, ARRAY_SIZE(rtnlgrp_link_policy))) {
-            VLOG_WARN_RL(&slow_rl, "received bad rtnl message");
-            error = ENOBUFS;
-        } else {
-            const char *devname = nl_attr_get_string(attrs[IFLA_IFNAME]);
-            bool for_us;
-
-            if (attrs[IFLA_MASTER]) {
-                uint32_t master_ifindex = nl_attr_get_u32(attrs[IFLA_MASTER]);
-                for_us = master_ifindex == mon->local_ifindex;
-            } else {
-                /* It's for us if that device is one of our ports.  This is
-                 * open-coded instead of using dpif_port_query_by_name() to
-                 * avoid logging a warning on failure. */
-                struct odp_port port;
-                memset(&port, 0, sizeof port);
-                strncpy(port.devname, devname, sizeof port.devname);
-                for_us = !ioctl(mon->dpif.fd, ODP_PORT_QUERY, &port);
-            }
+    COVERAGE_INC(dpif_purge);
 
-            if (!for_us) {
-                /* Not for us, try again. */
-                ofpbuf_delete(buf);
-                COVERAGE_INC(dpifmon_poll_false_wakeup);
-                goto again;
-            }
-            COVERAGE_INC(dpifmon_poll_changed);
-            *devnamep = xstrdup(devname);
+    error = dpif_get_dp_stats(dpif, &stats);
+    if (error) {
+        return error;
+    }
+
+    for (i = 0; i < stats.max_miss_queue + stats.max_action_queue; i++) {
+        struct ofpbuf *buf;
+        error = dpif_recv(dpif, &buf);
+        if (error) {
+            return error == EAGAIN ? 0 : error;
         }
         ofpbuf_delete(buf);
-        break;
-
-    case EAGAIN:
-        /* Nothing to do. */
-        break;
-
-    case ENOBUFS:
-        VLOG_WARN_RL(&slow_rl, "dpifmon socket overflowed");
-        break;
-
-    default:
-        VLOG_WARN_RL(&slow_rl, "error on dpifmon socket: %s", strerror(error));
-        break;
     }
-    return error;
+    return 0;
 }
 
+/* Arranges for the poll loop to wake up when 'dpif' has a message queued to be
+ * received with dpif_recv(). */
 void
-dpifmon_run(struct dpifmon *mon UNUSED)
+dpif_recv_wait(struct dpif *dpif)
 {
-    /* Nothing to do in this implementation. */
+    dpif->class->recv_wait(dpif);
 }
 
+/* Obtains the NetFlow engine type and engine ID for 'dpif' into '*engine_type'
+ * and '*engine_id', respectively. */
 void
-dpifmon_wait(struct dpifmon *mon)
+dpif_get_netflow_ids(const struct dpif *dpif,
+                     uint8_t *engine_type, uint8_t *engine_id)
 {
-    nl_sock_wait(mon->sock, POLLIN);
+    *engine_type = dpif->netflow_engine_type;
+    *engine_id = dpif->netflow_engine_id;
 }
 \f
-static int get_openvswitch_major(void);
-static int get_major(const char *target, int default_major);
-
-static int
-lookup_minor(const char *name, unsigned int *minor)
+void
+dpif_init(struct dpif *dpif, const struct dpif_class *class, const char *name,
+          uint8_t netflow_engine_type, uint8_t netflow_engine_id)
 {
-    struct ethtool_drvinfo drvinfo;
-    struct ifreq ifr;
-    int error;
-    int sock;
-
-    *minor = -1;
-    sock = socket(AF_INET, SOCK_DGRAM, 0);
-    if (sock < 0) {
-        VLOG_WARN("socket(AF_INET) failed: %s", strerror(errno));
-        error = errno;
-        goto error;
-    }
-
-    memset(&ifr, 0, sizeof ifr);
-    strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name);
-    ifr.ifr_data = (caddr_t) &drvinfo;
-
-    memset(&drvinfo, 0, sizeof drvinfo);
-    drvinfo.cmd = ETHTOOL_GDRVINFO;
-    if (ioctl(sock, SIOCETHTOOL, &ifr)) {
-        VLOG_WARN("ioctl(SIOCETHTOOL) failed: %s", strerror(errno));
-        error = errno;
-        goto error_close_sock;
-    }
-
-    if (strcmp(drvinfo.driver, "openvswitch")) {
-        VLOG_WARN("%s is not an openvswitch device", name);
-        error = EOPNOTSUPP;
-        goto error_close_sock;
-    }
-
-    if (!isdigit(drvinfo.bus_info[0])) {
-        VLOG_WARN("%s ethtool info does not contain an openvswitch minor",
-                  name);
-        error = EPROTOTYPE;
-        goto error_close_sock;
-    }
-
-    *minor = atoi(drvinfo.bus_info);
-    close(sock);
-    return 0;
-
-error_close_sock:
-    close(sock);
-error:
-    return error;
+    dpif->class = class;
+    dpif->name = xstrdup(name);
+    dpif->netflow_engine_type = netflow_engine_type;
+    dpif->netflow_engine_id = netflow_engine_id;
 }
-
-static int
-make_openvswitch_device(unsigned int minor, char **fnp)
+\f
+static void
+log_operation(const struct dpif *dpif, const char *operation, int error)
 {
-    dev_t dev = makedev(get_openvswitch_major(), minor);
-    const char dirname[] = "/dev/net";
-    struct stat s;
-    char fn[128];
-
-    *fnp = NULL;
-    sprintf(fn, "%s/dp%d", dirname, minor);
-    if (!stat(fn, &s)) {
-        if (!S_ISCHR(s.st_mode)) {
-            VLOG_WARN_RL(&error_rl, "%s is not a character device, fixing",
-                         fn);
-        } else if (s.st_rdev != dev) {
-            VLOG_WARN_RL(&error_rl,
-                         "%s is device %u:%u instead of %u:%u, fixing",
-                         fn, major(s.st_rdev), minor(s.st_rdev),
-                         major(dev), minor(dev));
-        } else {
-            goto success;
-        }
-        if (unlink(fn)) {
-            VLOG_WARN_RL(&error_rl, "%s: unlink failed (%s)",
-                         fn, strerror(errno));
-            return errno;
-        }
-    } else if (errno == ENOENT) {
-        if (stat(dirname, &s)) {
-            if (errno == ENOENT) {
-                if (mkdir(dirname, 0755)) {
-                    VLOG_WARN_RL(&error_rl, "%s: mkdir failed (%s)",
-                                 dirname, strerror(errno));
-                    return errno;
-                }
-            } else {
-                VLOG_WARN_RL(&error_rl, "%s: stat failed (%s)",
-                             dirname, strerror(errno));
-                return errno;
-            }
-        }
+    if (!error) {
+        VLOG_DBG_RL(&dpmsg_rl, "%s: %s success", dpif_name(dpif), operation);
     } else {
-        VLOG_WARN_RL(&error_rl, "%s: stat failed (%s)", fn, strerror(errno));
-        return errno;
+        VLOG_WARN_RL(&error_rl, "%s: %s failed (%s)",
+                     dpif_name(dpif), operation, strerror(error));
     }
-
-    /* The device needs to be created. */
-    if (mknod(fn, S_IFCHR | 0700, dev)) {
-        VLOG_WARN_RL(&error_rl,
-                     "%s: creating character device %u:%u failed (%s)",
-                     fn, major(dev), minor(dev), strerror(errno));
-        return errno;
-    }
-
-success:
-    *fnp = xstrdup(fn);
-    return 0;
 }
 
-
-static int
-get_openvswitch_major(void)
+static enum vlog_level
+flow_message_log_level(int error)
 {
-    static unsigned int openvswitch_major;
-    if (!openvswitch_major) {
-        enum { DEFAULT_MAJOR = 248 };
-        openvswitch_major = get_major("openvswitch", DEFAULT_MAJOR);
-    }
-    return openvswitch_major;
+    return error ? VLL_WARN : VLL_DBG;
 }
 
-static int
-get_major(const char *target, int default_major)
+static bool
+should_log_flow_message(int error)
 {
-    const char fn[] = "/proc/devices";
-    char line[128];
-    FILE *file;
-    int ln;
-
-    file = fopen(fn, "r");
-    if (!file) {
-        VLOG_ERR("opening %s failed (%s)", fn, strerror(errno));
-        goto error;
-    }
-
-    for (ln = 1; fgets(line, sizeof line, file); ln++) {
-        char name[64];
-        int major;
-
-        if (!strncmp(line, "Character", 9) || line[0] == '\0') {
-            /* Nothing to do. */
-        } else if (!strncmp(line, "Block", 5)) {
-            /* We only want character devices, so skip the rest of the file. */
-            break;
-        } else if (sscanf(line, "%d %63s", &major, name)) {
-            if (!strcmp(name, target)) {
-                fclose(file);
-                return major;
-            }
-        } else {
-            static bool warned;
-            if (!warned) {
-                VLOG_WARN("%s:%d: syntax error", fn, ln);
-            }
-            warned = true;
-        }
-    }
-
-    VLOG_ERR("%s: %s major not found (is the module loaded?), using "
-             "default major %d", fn, target, default_major);
-error:
-    VLOG_INFO("using default major %d for %s", default_major, target);
-    return default_major;
+    return !vlog_should_drop(THIS_MODULE, flow_message_log_level(error),
+                             error ? &error_rl : &dpmsg_rl);
 }
 
-static int
-name_to_minor(const char *name, unsigned int *minor)
+static void
+log_flow_message(const struct dpif *dpif, int error, const char *operation,
+                 const flow_t *flow, const struct odp_flow_stats *stats,
+                 const union odp_action *actions, size_t n_actions)
 {
-    if (!get_minor_from_name(name, minor)) {
-        return 0;
+    struct ds ds = DS_EMPTY_INITIALIZER;
+    ds_put_format(&ds, "%s: ", dpif_name(dpif));
+    if (error) {
+        ds_put_cstr(&ds, "failed to ");
+    }
+    ds_put_format(&ds, "%s ", operation);
+    if (error) {
+        ds_put_format(&ds, "(%s) ", strerror(error));
+    }
+    flow_format(&ds, flow);
+    if (stats) {
+        ds_put_cstr(&ds, ", ");
+        format_odp_flow_stats(&ds, stats);
+    }
+    if (actions || n_actions) {
+        ds_put_cstr(&ds, ", actions:");
+        format_odp_actions(&ds, actions, n_actions);
     }
-    return lookup_minor(name, minor);
+    vlog(THIS_MODULE, flow_message_log_level(error), "%s", ds_cstr(&ds));
+    ds_destroy(&ds);
 }
 
-static int
-get_minor_from_name(const char *name, unsigned int *minor)
+static void
+log_flow_operation(const struct dpif *dpif, const char *operation, int error,
+                   struct odp_flow *flow)
 {
-    if (!strncmp(name, "dp", 2) && isdigit(name[2])) {
-        *minor = atoi(name + 2);
-        return 0;
-    } else if (!strncmp(name, "nl:", 3) && isdigit(name[3])) {
-        /* This is for compatibility only and will be dropped. */
-        *minor = atoi(name + 3);
-        return 0;
-    } else {
-        return EINVAL;
+    if (error) {
+        flow->n_actions = 0;
     }
+    log_flow_message(dpif, error, operation, &flow->key,
+                     !error ? &flow->stats : NULL,
+                     flow->actions, flow->n_actions);
 }
 
-static int
-open_by_minor(unsigned int minor, struct dpif *dpif)
+static void
+log_flow_put(struct dpif *dpif, int error, const struct odp_flow_put *put)
 {
-    int error;
-    char *fn;
-    int fd;
+    enum { ODPPF_ALL = ODPPF_CREATE | ODPPF_MODIFY | ODPPF_ZERO_STATS };
+    struct ds s;
 
-    dpif->minor = -1;
-    dpif->fd = -1;
-    error = make_openvswitch_device(minor, &fn);
-    if (error) {
-        return error;
+    ds_init(&s);
+    ds_put_cstr(&s, "put");
+    if (put->flags & ODPPF_CREATE) {
+        ds_put_cstr(&s, "[create]");
     }
-
-    fd = open(fn, O_RDONLY | O_NONBLOCK);
-    if (fd < 0) {
-        error = errno;
-        VLOG_WARN("%s: open failed (%s)", fn, strerror(error));
-        free(fn);
-        return error;
+    if (put->flags & ODPPF_MODIFY) {
+        ds_put_cstr(&s, "[modify]");
     }
-
-    free(fn);
-    dpif->minor = minor;
-    dpif->fd = fd;
-    return 0;
+    if (put->flags & ODPPF_ZERO_STATS) {
+        ds_put_cstr(&s, "[zero]");
+    }
+    if (put->flags & ~ODPPF_ALL) {
+        ds_put_format(&s, "[%x]", put->flags & ~ODPPF_ALL);
+    }
+    log_flow_message(dpif, error, ds_cstr(&s), &put->flow.key,
+                     !error ? &put->flow.stats : NULL,
+                     put->flow.actions, put->flow.n_actions);
+    ds_destroy(&s);
 }
-\f
+
 /* There is a tendency to construct odp_flow objects on the stack and to
  * forget to properly initialize their "actions" and "n_actions" members.
  * When this happens, we get memory corruption because the kernel
index 1e5412a..216c099 100644 (file)
 #include <stddef.h>
 #include <stdint.h>
 
+struct dpif;
 struct ofpbuf;
 struct svec;
 
-/* A datapath interface.  Opaque. */
-struct dpif {
-    unsigned int minor;         /* For use in error messages. */
-    int fd;
-};
-
+void dp_run(void);
+void dp_wait(void);
 int dp_enumerate(struct svec *);
 
-int dpif_open(const char *name, struct dpif *);
-int dpif_create(const char *name, struct dpif *);
+int dpif_open(const char *name, struct dpif **);
+int dpif_create(const char *name, struct dpif **);
 void dpif_close(struct dpif *);
 
-static inline unsigned int dpif_id(const struct dpif *dpif);
-int dpif_get_name(struct dpif *, char *name, size_t name_size);
+const char *dpif_name(const struct dpif *);
+int dpif_get_all_names(const struct dpif *, struct svec *);
 
 int dpif_delete(struct dpif *);
 
@@ -51,23 +48,24 @@ int dpif_get_dp_stats(const struct dpif *, struct odp_stats *);
 int dpif_get_drop_frags(const struct dpif *, bool *drop_frags);
 int dpif_set_drop_frags(struct dpif *, bool drop_frags);
 
-int dpif_get_listen_mask(const struct dpif *, int *listen_mask);
-int dpif_set_listen_mask(struct dpif *, int listen_mask);
-int dpif_purge(struct dpif *);
-
-int dpif_port_add(struct dpif *, const char *devname, uint16_t port_no,
-                  uint16_t flags);
+int dpif_port_add(struct dpif *, const char *devname, uint16_t flags,
+                  uint16_t *port_no);
 int dpif_port_del(struct dpif *, uint16_t port_no);
 int dpif_port_query_by_number(const struct dpif *, uint16_t port_no,
                               struct odp_port *);
 int dpif_port_query_by_name(const struct dpif *, const char *devname,
                             struct odp_port *);
+int dpif_port_get_name(struct dpif *, uint16_t port_no,
+                       char *name, size_t name_size);
 int dpif_port_list(const struct dpif *, struct odp_port **, size_t *n_ports);
 
+int dpif_port_poll(const struct dpif *, char **devnamep);
+void dpif_port_poll_wait(const struct dpif *);
+
+int dpif_port_group_get(const struct dpif *, uint16_t group,
+                        uint16_t **ports, size_t *n_ports);
 int dpif_port_group_set(struct dpif *, uint16_t group,
                         const uint16_t ports[], size_t n_ports);
-int dpif_port_group_get(const struct dpif *, uint16_t group,
-                        uint16_t ports[], size_t n_ports, size_t *n_out);
 
 int dpif_flow_flush(struct dpif *);
 int dpif_flow_put(struct dpif *, struct odp_flow_put *);
@@ -83,23 +81,13 @@ int dpif_execute(struct dpif *, uint16_t in_port,
                  const union odp_action[], size_t n_actions,
                  const struct ofpbuf *);
 
+int dpif_recv_get_mask(const struct dpif *, int *listen_mask);
+int dpif_recv_set_mask(struct dpif *, int listen_mask);
 int dpif_recv(struct dpif *, struct ofpbuf **);
+int dpif_recv_purge(struct dpif *);
 void dpif_recv_wait(struct dpif *);
 
-static inline unsigned int
-dpif_id(const struct dpif *dpif)
-{
-    return dpif->minor;
-}
-\f
-struct dpifmon;
-
-int dpifmon_create(const char *datapath_name, struct dpifmon **);
-void dpifmon_destroy(struct dpifmon *);
-
-int dpifmon_poll(struct dpifmon *, char **devnamep);
-
-void dpifmon_run(struct dpifmon *);
-void dpifmon_wait(struct dpifmon *);
+void dpif_get_netflow_ids(const struct dpif *,
+                          uint8_t *engine_type, uint8_t *engine_id);
 
 #endif /* dpif.h */
index 72175b0..3a58caf 100644 (file)
@@ -9,8 +9,3 @@ inclusive.
 The name of the network device associated with the datapath's local
 port.  (\fB\*(PN\fR internally converts this into a datapath number,
 as above.)
-
-.TP
-\fBnl:\fIN\fR
-This is an obsolete synonym for \fBdp\fIN\fR.
-.RE
index 67c8e70..1809a91 100644 (file)
@@ -108,6 +108,14 @@ hmap_count(const struct hmap *hmap)
     return hmap->n;
 }
 
+/* Returns the maximum number of nodes that 'hmap' may hold before it should be
+ * rehashed. */
+static inline size_t
+hmap_capacity(const struct hmap *hmap)
+{
+    return hmap->mask * 2 + 1;
+}
+
 /* Returns true if 'hmap' currently contains no nodes,
  * false otherwise. */
 static inline bool
diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c
new file mode 100644 (file)
index 0000000..3e34044
--- /dev/null
@@ -0,0 +1,1593 @@
+/*
+ * 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
+};
+
+/* 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 in4;
+    struct in6_addr in6;
+    int mtu;
+    int carrier;
+};
+
+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 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)
+{
+    static int use_netlink_stats = -1;
+    int error;
+
+    COVERAGE_INC(netdev_get_stats);
+    if (use_netlink_stats < 0) {
+        use_netlink_stats = check_for_working_netlink_stats();
+    }
+    if (use_netlink_stats) {
+        int ifindex;
+
+        error = get_ifindex(netdev, &ifindex);
+        if (!error) {
+            error = get_stats_via_netlink(ifindex, stats);
+        }
+    } else {
+        error = get_stats_via_proc(netdev->name, stats);
+    }
+    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;
+}
+
+/* If 'netdev' has an assigned IPv4 address, sets '*in4' to that address (if
+ * 'in4' is non-null) and returns true.  Otherwise, returns false. */
+static int
+netdev_linux_get_in4(const struct netdev *netdev_, struct in_addr *in4)
+{
+    struct netdev_linux *netdev = netdev_linux_cast(netdev_);
+    if (!(netdev->cache->valid & VALID_IN4)) {
+        const struct sockaddr_in *sin;
+        struct ifreq ifr;
+        int error;
+
+        ifr.ifr_addr.sa_family = AF_INET;
+        error = netdev_linux_do_ioctl(netdev_, &ifr,
+                                      SIOCGIFADDR, "SIOCGIFADDR");
+        if (error) {
+            return error;
+        }
+
+        sin = (struct sockaddr_in *) &ifr.ifr_addr;
+        netdev->cache->in4 = sin->sin_addr;
+        netdev->cache->valid |= VALID_IN4;
+    }
+    *in4 = netdev->cache->in4;
+    return in4->s_addr == INADDR_ANY ? EADDRNOTAVAIL : 0;
+}
+
+/* Assigns 'addr' as 'netdev''s IPv4 address and 'mask' as its netmask.  If
+ * 'addr' is INADDR_ANY, 'netdev''s IPv4 address is cleared.  Returns a
+ * positive errno value. */
+static int
+netdev_linux_set_in4(struct netdev *netdev_, struct in_addr addr,
+                     struct in_addr mask)
+{
+    struct netdev_linux *netdev = netdev_linux_cast(netdev_);
+    int error;
+
+    error = do_set_addr(netdev_, SIOCSIFADDR, "SIOCSIFADDR", addr);
+    if (!error) {
+        netdev->cache->valid |= VALID_IN4;
+        netdev->cache->in4 = addr;
+        if (addr.s_addr != INADDR_ANY) {
+            error = do_set_addr(netdev_, SIOCSIFNETMASK,
+                                "SIOCSIFNETMASK", mask);
+        }
+    }
+    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;
+}
+
+/* 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_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_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");
+        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;
+
+    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;
+}
diff --git a/lib/netdev-provider.h b/lib/netdev-provider.h
new file mode 100644 (file)
index 0000000..9c880b1
--- /dev/null
@@ -0,0 +1,284 @@
+/*
+ * Copyright (c) 2009 Nicira Networks.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef NETDEV_PROVIDER_H
+#define NETDEV_PROVIDER_H 1
+
+/* Generic interface to network devices. */
+
+#include <assert.h>
+#include "netdev.h"
+#include "list.h"
+
+/* A network device (e.g. an Ethernet device).
+ *
+ * This structure should be treated as opaque by network device
+ * implementations. */
+struct netdev {
+    const struct netdev_class *class;
+    char *name;                      /* e.g. "eth0" */
+    enum netdev_flags save_flags;    /* Initial device flags. */
+    enum netdev_flags changed_flags; /* Flags that we changed. */
+    struct list node;                /* Element in global list. */
+};
+
+void netdev_init(struct netdev *, const char *name,
+                 const struct netdev_class *);
+static inline void netdev_assert_class(const struct netdev *netdev,
+                                       const struct netdev_class *class)
+{
+    assert(netdev->class == class);
+}
+
+/* A network device notifier.
+ *
+ * Network device implementations should use netdev_notifier_init() to
+ * initialize this structure, but they may freely read its members after
+ * initialization. */
+struct netdev_notifier {
+    struct netdev *netdev;
+    void (*cb)(struct netdev_notifier *);
+    void *aux;
+};
+void netdev_notifier_init(struct netdev_notifier *, struct netdev *,
+                          void (*cb)(struct netdev_notifier *), void *aux);
+
+/* Network device class structure, to be defined by each implementation of a
+ * network device.
+ *
+ * These functions return 0 if successful or a positive errno value on failure,
+ * except where otherwise noted. */
+struct netdev_class {
+    /* Prefix for names of netdevs in this class, e.g. "ndunix:".
+     *
+     * One netdev class may have the empty string "" as its prefix, in which
+     * case that netdev class is associated with netdev names that do not
+     * contain a colon. */
+    const char *prefix;
+
+    /* Class name, for use in error messages. */
+    const char *name;
+
+    /* Called only once, at program startup.  Returning an error from this
+     * function will prevent any network device in this class from being
+     * opened.
+     *
+     * This function may be set to null if a network device class needs no
+     * initialization at program startup. */
+    int (*init)(void);
+
+    /* Performs periodic work needed by netdevs of this class.  May be null if
+     * no periodic work is necessary. */
+    void (*run)(void);
+
+    /* Arranges for poll_block() to wake up if the "run" member function needs
+     * to be called.  May be null if nothing is needed here. */
+    void (*wait)(void);
+
+    /* Attempts to open a network device.  On success, sets '*netdevp' to the
+     * new network device.  'name' is the full network device name provided by
+     * the user.  This name is useful for error messages but must not be
+     * modified.
+     *
+     * 'suffix' is a copy of 'name' following the netdev's 'prefix'.
+     *
+     * 'ethertype' may be a 16-bit Ethernet protocol value in host byte order
+     * to capture frames of that type received on the device.  It may also be
+     * one of the 'enum netdev_pseudo_ethertype' values to receive frames in
+     * one of those categories. */
+    int (*open)(const char *name, char *suffix, int ethertype,
+                struct netdev **netdevp);
+
+    /* Closes 'netdev'. */
+    void (*close)(struct netdev *netdev);
+
+    /* Enumerates the names of all network devices of this class.
+     *
+     * The caller has already initialized 'all_names' and might already have
+     * added some names to it.  This function should not disturb any existing
+     * names in 'all_names'.
+     *
+     * If this netdev class does not support enumeration, this may be a null
+     * pointer. */
+    int (*enumerate)(struct svec *all_anmes);
+
+    /* Attempts to receive a packet from 'netdev' into the 'size' bytes in
+     * 'buffer'.  If successful, returns the number of bytes in the received
+     * packet, otherwise a negative errno value.  Returns -EAGAIN immediately
+     * if no packet is ready to be received. */
+    int (*recv)(struct netdev *netdev, void *buffer, size_t size);
+
+    /* 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'. */
+    void (*recv_wait)(struct netdev *netdev);
+
+    /* Discards all packets waiting to be received from 'netdev'. */
+    int (*drain)(struct netdev *netdev);
+
+    /* Sends the 'size'-byte packet in '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 network device is expected to maintain a packet transmission queue,
+     * so that the caller does not ordinarily have to do additional queuing of
+     * packets. */
+    int (*send)(struct netdev *netdev, const void *buffer, size_t size);
+
+    /* Registers with the poll loop to wake up from the next call to
+     * poll_block() when the packet transmission queue for 'netdev' has
+     * sufficient room to transmit a packet with netdev_send().
+     *
+     * The network device is expected to maintain a packet transmission queue,
+     * so that the caller does not ordinarily have to do additional queuing of
+     * packets.  Thus, this function is unlikely to ever be useful. */
+    void (*send_wait)(struct netdev *netdev);
+
+    /* Sets 'netdev''s Ethernet address to 'mac' */
+    int (*set_etheraddr)(struct netdev *netdev, const uint8_t mac[6]);
+
+    /* Retrieves 'netdev''s Ethernet address into 'mac'. */
+    int (*get_etheraddr)(const struct netdev *netdev, uint8_t mac[6]);
+
+    /* Retrieves 'netdev''s MTU into '*mtup'.
+     *
+     * The MTU is the maximum size of transmitted (and received) packets, in
+     * bytes, not including the hardware header; thus, this is typically 1500
+     * bytes for Ethernet devices.*/
+    int (*get_mtu)(const struct netdev *, int *mtup);
+
+    /* Sets 'carrier' to true if carrier is active (link light is on) on
+     * 'netdev'. */
+    int (*get_carrier)(const struct netdev *netdev, bool *carrier);
+
+    /* Retrieves current device stats for 'netdev' into 'stats'.
+     *
+     * A network device that supports some statistics but not others, it should
+     * set the values of the unsupported statistics to all-1-bits
+     * (UINT64_MAX). */
+    int (*get_stats)(const struct netdev *netdev, struct netdev_stats *stats);
+
+    /* Stores the features supported by 'netdev' into each of '*current',
+     * '*advertised', '*supported', and '*peer'.  Each value is a bitmap of
+     * "enum ofp_port_features" bits, in host byte order. */
+    int (*get_features)(struct netdev *netdev,
+                        uint32_t *current, uint32_t *advertised,
+                        uint32_t *supported, uint32_t *peer);
+
+    /* Set the features advertised by 'netdev' to 'advertise', which is a
+     * bitmap of "enum ofp_port_features" bits, in host byte order.
+     *
+     * This function may be set to null for a network device that does not
+     * support configuring advertisements. */
+    int (*set_advertisements)(struct netdev *, uint32_t advertise);
+
+    /* If 'netdev' is 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.
+     *
+     * Returns ENOENT if 'netdev_name' is the name of a network device that is
+     * not a VLAN device.
+     *
+     * This function should be set to null if it doesn't make any sense for
+     * your network device (it probably doesn't). */
+    int (*get_vlan_vid)(const struct netdev *netdev, int *vlan_vid);
+
+    /* Attempts to set input rate limiting (policing) policy, such that up to
+     * 'kbits_rate' kbps of traffic is accepted, with a maximum accumulative
+     * burst size of 'kbits' kb.
+     *
+     * This function may be set to null if policing is not supported. */
+    int (*set_policing)(struct netdev *netdev, unsigned int kbits_rate,
+                        unsigned int kbits_burst);
+
+    /* If 'netdev' has an assigned IPv4 address, sets '*in4' to that address.
+     *
+     * The following error values have well-defined meanings:
+     *
+     *   - EADDRNOTAVAIL: 'netdev' has no assigned IPv4 address.
+     *
+     *   - EOPNOTSUPP: No IPv4 network stack attached to 'netdev'.
+     *
+     * This function may be set to null if it would always return EOPNOTSUPP
+     * anyhow. */
+    int (*get_in4)(const struct netdev *netdev, struct in_addr *in4);
+
+    /* Assigns 'addr' as 'netdev''s IPv4 address and 'mask' as its netmask.  If
+     * 'addr' is INADDR_ANY, 'netdev''s IPv4 address is cleared.
+     *
+     * This function may be set to null if it would always return EOPNOTSUPP
+     * anyhow. */
+    int (*set_in4)(struct netdev *, struct in_addr addr, struct in_addr mask);
+
+    /* If 'netdev' has an assigned IPv6 address, sets '*in6' to that address.
+     *
+     * The following error values have well-defined meanings:
+     *
+     *   - EADDRNOTAVAIL: 'netdev' has no assigned IPv6 address.
+     *
+     *   - EOPNOTSUPP: No IPv6 network stack attached to 'netdev'.
+     *
+     * This function may be set to null if it would always return EOPNOTSUPP
+     * anyhow. */
+    int (*get_in6)(const struct netdev *netdev, struct in6_addr *in6);
+
+    /* Adds 'router' as a default IP gateway for the TCP/IP stack that
+     * corresponds to 'netdev'.
+     *
+     * This function may be set to null if it would always return EOPNOTSUPP
+     * anyhow. */
+    int (*add_router)(struct netdev *netdev, struct in_addr router);
+
+    /* Looks up the ARP table entry for 'ip' on 'netdev' and stores the
+     * corresponding MAC address in 'mac'.  A return value of ENXIO, in
+     * particular, indicates that there is no ARP table entry for 'ip' on
+     * 'netdev'.
+     *
+     * This function may be set to null if it would always return EOPNOTSUPP
+     * anyhow. */
+    int (*arp_lookup)(const struct netdev *, uint32_t ip, uint8_t mac[6]);
+
+    /* Retrieves the current set of flags on 'netdev' into '*old_flags'.  Then,
+     * turns off the flags that are set to 1 in 'off' and turns on the flags
+     * that are set to 1 in 'on'.  (No bit will be set to 1 in both 'off' and
+     * 'on'; that is, off & on == 0.)
+     *
+     * This function may be invoked from a signal handler.  Therefore, it
+     * should not do anything that is not signal-safe (such as logging). */
+    int (*update_flags)(struct netdev *netdev, enum netdev_flags off,
+                        enum netdev_flags on, enum netdev_flags *old_flags);
+
+    /* Arranges for 'cb' to be called whenever one of the attributes of
+     * 'netdev' changes and sets '*notifierp' to a newly created
+     * netdev_notifier that represents this arrangement.  The created notifier
+     * will have its 'netdev', 'cb', and 'aux' members set to the values of the
+     * corresponding parameters. */
+    int (*poll_add)(struct netdev *netdev,
+                    void (*cb)(struct netdev_notifier *), void *aux,
+                    struct netdev_notifier **notifierp);
+
+    /* Cancels poll notification for 'notifier'. */
+    void (*poll_remove)(struct netdev_notifier *notifier);
+};
+
+extern const struct netdev_class netdev_linux_class;
+extern const struct netdev_class netdev_tap_class;
+
+#endif /* netdev.h */
index 6644f7e..dcb63fa 100644 (file)
 
 #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 <stdlib.h>
 #include <string.h>
 #include "dynamic-string.h"
 #include "fatal-signal.h"
 #include "list.h"
-#include "netlink.h"
+#include "netdev-provider.h"
 #include "ofpbuf.h"
-#include "openflow/openflow.h"
 #include "packets.h"
 #include "poll-loop.h"
-#include "socket-util.h"
+#include "shash.h"
 #include "svec.h"
 
-/* linux/if.h defines IFF_LOWER_UP, net/if.h doesn't.
- * net/if.h defines if_nameindex(), linux/if.h doesn't.
- * We can't include both headers, so define IFF_LOWER_UP ourselves. */
-#ifndef IFF_LOWER_UP
-#define IFF_LOWER_UP 0x10000
-#endif
-
-/* 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
-
 #define THIS_MODULE VLM_netdev
 #include "vlog.h"
 
-struct netdev {
-    struct list node;
-    char *name;
-
-    /* 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. */
-
-    /* Cached network device information. */
-    int ifindex;                /* -1 if not known. */
-    uint8_t etheraddr[ETH_ADDR_LEN];
-    struct in6_addr in6;
-    int speed;
-    int mtu;
-    int txqlen;
-    int hwaddr_family;
-
-    int save_flags;             /* Initial device flags. */
-    int changed_flags;          /* Flags that we changed. */
-};
-
-/* Policy for RTNLGRP_LINK messages.
- *
- * There are *many* more fields in these messages, but currently we only care
- * about interface names. */
-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 const struct netdev_class *netdev_classes[] = {
+    &netdev_linux_class,
+    &netdev_tap_class,
 };
+static int n_netdev_classes = ARRAY_SIZE(netdev_classes);
 
 /* All open network devices. */
 static struct list netdev_list = LIST_INITIALIZER(&netdev_list);
 
-/* An AF_INET socket (used for ioctl operations). */
-static int af_inet_sock = -1;
-
-/* NETLINK_ROUTE socket. */
-static struct nl_sock *rtnl_sock;
-
-/* 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 use_netlink_stats;
-
 /* 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 void init_netdev(void);
-static int do_open_netdev(const char *name, int ethertype, int tap_fd,
-                          struct netdev **netdev_);
+static void restore_all_flags(void *aux);
 static int restore_flags(struct netdev *netdev);
-static int get_flags(const char *netdev_name, int *flagsp);
-static int set_flags(const char *netdev_name, int flags);
-static int do_get_ifindex(const char *netdev_name);
-static int get_ifindex(const struct netdev *, int *ifindexp);
-static int get_etheraddr(const char *netdev_name, uint8_t ea[ETH_ADDR_LEN],
-                         int *hwaddr_familyp);
-static int set_etheraddr(const char *netdev_name, int hwaddr_family,
-                         const uint8_t[ETH_ADDR_LEN]);
-
-/* Obtains the IPv6 address for 'name' into 'in6'. */
-static void
-get_ipv6_address(const char *name, struct in6_addr *in6)
+
+/* Attempts to initialize the netdev module.  Returns 0 if successful,
+ * otherwise a positive errno value.
+ *
+ * Calling this function is optional.  If not called explicitly, it will
+ * automatically be called upon the first attempt to open a network device. */
+int
+netdev_initialize(void)
 {
-    FILE *file;
-    char line[128];
-
-    file = fopen("/proc/net/if_inet6", "r");
-    if (file == NULL) {
-        /* This most likely indicates that the host doesn't have IPv6 support,
-         * so it's not really a failure condition.*/
-        *in6 = in6addr_any;
-        return;
-    }
+    static int status = -1;
+    if (status < 0) {
+        int i, j;
+
+        fatal_signal_add_hook(restore_all_flags, NULL, true);
 
-    while (fgets(line, sizeof line, file)) {
-        uint8_t *s6 = in6->s6_addr;
-        char ifname[16 + 1];
-
-#define X8 "%2"SCNx8
-        if (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
-            && !strcmp(name, ifname))
-        {
-            fclose(file);
-            return;
+        status = 0;
+        for (i = j = 0; i < n_netdev_classes; i++) {
+            const struct netdev_class *class = netdev_classes[i];
+            if (class->init) {
+                int retval = class->init();
+                if (!retval) {
+                    netdev_classes[j++] = class;
+                } else {
+                    VLOG_ERR("failed to initialize %s network device "
+                             "class: %s", class->name, strerror(retval));
+                    if (!status) {
+                        status = retval;
+                    }
+                }
+            }
         }
+        n_netdev_classes = j;
     }
-    *in6 = in6addr_any;
-
-    fclose(file);
+    return status;
 }
 
-static int
-do_ethtool(struct netdev *netdev, struct ethtool_cmd *ecmd,
-           int cmd, const char *cmd_name)
+/* Performs periodic work needed by all the various kinds of netdevs.
+ *
+ * If your program opens any netdevs, it must call this function within its
+ * main poll loop. */
+void
+netdev_run(void)
 {
-    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(netdev->netdev_fd, 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. */
+    int i;
+    for (i = 0; i < n_netdev_classes; i++) {
+        const struct netdev_class *class = netdev_classes[i];
+        if (class->run) {
+            class->run();
         }
-        return errno;
     }
 }
 
-static int
-do_get_features(struct netdev *netdev,
-                uint32_t *current, uint32_t *advertised,
-                uint32_t *supported, uint32_t *peer)
+/* Arranges for poll_block() to wake up when netdev_run() needs to be called.
+ *
+ * If your program opens any netdevs, it must call this function within its
+ * main poll loop. */
+void
+netdev_wait(void)
 {
-    struct ethtool_cmd ecmd;
-    int error;
-
-    *current = 0;
-    *supported = 0;
-    *advertised = 0;
-    *peer = 0;
-
-    memset(&ecmd, 0, sizeof ecmd);
-    error = do_ethtool(netdev, &ecmd, ETHTOOL_GSET, "ETHTOOL_GSET");
-    if (error) {
-        return error;
-    }
-
-    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;
-    }
-
-    /* Set the advertised features */
-    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;
-    }
-
-    /* Set the current features */
-    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;
-    }
-
-    if (ecmd.port == PORT_TP) {
-        *current |= OFPPF_COPPER;
-    }
-    else if (ecmd.port == PORT_FIBRE) {
-        *current |= OFPPF_FIBER;
-    }
-
-    if (ecmd.autoneg) {
-        *current |= OFPPF_AUTONEG;
+    int i;
+    for (i = 0; i < n_netdev_classes; i++) {
+        const struct netdev_class *class = netdev_classes[i];
+        if (class->wait) {
+            class->wait();
+        }
     }
-    return 0;
 }
 
 /* Opens the network device named 'name' (e.g. "eth0") and returns zero if
@@ -331,181 +131,36 @@ do_get_features(struct netdev *netdev,
  * the 'enum netdev_pseudo_ethertype' values to receive frames in one of those
  * categories. */
 int
-netdev_open(const char *name, int ethertype, struct netdev **netdevp) 
+netdev_open(const char *name_, int ethertype, struct netdev **netdevp)
 {
-    if (!strncmp(name, "tap:", 4)) {
-        return netdev_open_tap(name + 4, netdevp);
-    } else {
-        return do_open_netdev(name, ethertype, -1, netdevp); 
-    }
-}
-
-/* Opens a TAP virtual network device.  If 'name' is a nonnull, non-empty
- * string, attempts to assign that name to the TAP device (failing if the name
- * is already in use); otherwise, a name is automatically assigned.  Returns
- * zero if successful, otherwise a positive errno value.  On success, sets
- * '*netdevp' to the new network device, otherwise to null.  */
-int
-netdev_open_tap(const char *name, struct netdev **netdevp)
-{
-    static const char tap_dev[] = "/dev/net/tun";
-    struct ifreq ifr;
+    char *name = xstrdup(name_);
+    char *prefix, *suffix, *colon;
+    struct netdev *netdev = NULL;
     int error;
-    int tap_fd;
-
-    tap_fd = open(tap_dev, O_RDWR);
-    if (tap_fd < 0) {
-        ovs_error(errno, "opening \"%s\" failed", tap_dev);
-        return errno;
-    }
-
-    memset(&ifr, 0, sizeof ifr);
-    ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
-    if (name) {
-        strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name);
-    }
-    if (ioctl(tap_fd, TUNSETIFF, &ifr) < 0) {
-        int error = errno;
-        ovs_error(error, "ioctl(TUNSETIFF) on \"%s\" failed", tap_dev);
-        close(tap_fd);
-        return error;
-    }
-
-    error = set_nonblocking(tap_fd);
-    if (error) {
-        ovs_error(error, "set_nonblocking on \"%s\" failed", tap_dev);
-        close(tap_fd);
-        return error;
-    }
-
-    error = do_open_netdev(ifr.ifr_name, NETDEV_ETH_TYPE_NONE, tap_fd,
-                           netdevp);
-    if (error) {
-        close(tap_fd);
-    }
-    return error;
-}
-
-static int
-do_open_netdev(const char *name, int ethertype, int tap_fd,
-               struct netdev **netdev_)
-{
-    int netdev_fd;
-    struct sockaddr_ll sll;
-    struct ifreq ifr;
-    int ifindex = -1;
-    uint8_t etheraddr[ETH_ADDR_LEN];
-    struct in6_addr in6;
-    int mtu;
-    int txqlen;
-    int hwaddr_family;
-    int error;
-    struct netdev *netdev;
+    int i;
 
-    init_netdev();
-    *netdev_ = NULL;
-    COVERAGE_INC(netdev_open);
-
-    /* Create raw socket. */
-    netdev_fd = socket(PF_PACKET, SOCK_RAW,
-                       htons(ethertype == NETDEV_ETH_TYPE_NONE ? 0
-                             : ethertype == NETDEV_ETH_TYPE_ANY ? ETH_P_ALL
-                             : ethertype == NETDEV_ETH_TYPE_802_2 ? ETH_P_802_2
-                             : ethertype));
-    if (netdev_fd < 0) {
-        return errno;
+    netdev_initialize();
+    colon = strchr(name, ':');
+    if (colon) {
+        *colon = '\0';
+        prefix = name;
+        suffix = colon + 1;
+    } else {
+        prefix = "";
+        suffix = name;
     }
 
-    if (ethertype != NETDEV_ETH_TYPE_NONE) {
-        /* Set non-blocking mode. */
-        error = set_nonblocking(netdev_fd);
-        if (error) {
-            goto error_already_set;
-        }
-
-        /* Get ethernet device index. */
-        ifindex = do_get_ifindex(name);
-        if (ifindex < 0) {
-            return -ifindex;
-        }
-
-        /* Bind to specific ethernet device. */
-        memset(&sll, 0, sizeof sll);
-        sll.sll_family = AF_PACKET;
-        sll.sll_ifindex = ifindex;
-        if (bind(netdev_fd, (struct sockaddr *) &sll, sizeof sll) < 0) {
-            VLOG_ERR("bind to %s failed: %s", name, strerror(errno));
-            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_fd);
-        if (error) {
-            goto error_already_set;
+    for (i = 0; i < n_netdev_classes; i++) {
+        const struct netdev_class *class = netdev_classes[i];
+        if (!strcmp(prefix, class->prefix)) {
+            error = class->open(name_, suffix, ethertype, &netdev);
+            goto exit;
         }
     }
+    error = EAFNOSUPPORT;
 
-    /* Get MAC address. */
-    error = get_etheraddr(name, etheraddr, &hwaddr_family);
-    if (error) {
-        goto error_already_set;
-    }
-
-    /* Get MTU. */
-    strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name);
-    if (ioctl(netdev_fd, SIOCGIFMTU, &ifr) < 0) {
-        VLOG_ERR("ioctl(SIOCGIFMTU) on %s device failed: %s",
-                 name, strerror(errno));
-        goto error;
-    }
-    mtu = ifr.ifr_mtu;
-
-    /* Get TX queue length. */
-    if (ioctl(netdev_fd, SIOCGIFTXQLEN, &ifr) < 0) {
-        VLOG_ERR("ioctl(SIOCGIFTXQLEN) on %s device failed: %s",
-                 name, strerror(errno));
-        goto error;
-    }
-    txqlen = ifr.ifr_qlen;
-
-    get_ipv6_address(name, &in6);
-
-    /* Allocate network device. */
-    netdev = xmalloc(sizeof *netdev);
-    netdev->name = xstrdup(name);
-    netdev->ifindex = ifindex;
-    netdev->txqlen = txqlen;
-    netdev->hwaddr_family = hwaddr_family;
-    netdev->netdev_fd = netdev_fd;
-    netdev->tap_fd = tap_fd < 0 ? netdev_fd : tap_fd;
-    memcpy(netdev->etheraddr, etheraddr, sizeof etheraddr);
-    netdev->mtu = mtu;
-    netdev->in6 = in6;
-
-    /* Save flags to restore at close or exit. */
-    error = get_flags(netdev->name, &netdev->save_flags);
-    if (error) {
-        goto error_already_set;
-    }
-    netdev->changed_flags = 0;
-    fatal_signal_block();
-    list_push_back(&netdev_list, &netdev->node);
-    fatal_signal_unblock();
-
-    /* Success! */
-    *netdev_ = netdev;
-    return 0;
-
-error:
-    error = errno;
-error_already_set:
-    close(netdev_fd);
-    if (tap_fd >= 0) {
-        close(tap_fd);
-    }
+exit:
+    *netdevp = error ? NULL : netdev;
     return error;
 }
 
@@ -514,9 +169,10 @@ void
 netdev_close(struct netdev *netdev)
 {
     if (netdev) {
-        /* Bring down interface and drop promiscuous mode, if we brought up
-         * the interface or enabled promiscuous mode. */
+        char *name;
         int error;
+
+        /* Restore flags that we changed, if any. */
         fatal_signal_block();
         error = restore_flags(netdev);
         list_remove(&netdev->node);
@@ -527,23 +183,59 @@ netdev_close(struct netdev *netdev)
         }
 
         /* Free. */
-        free(netdev->name);
-        close(netdev->netdev_fd);
-        if (netdev->netdev_fd != netdev->tap_fd) {
-            close(netdev->tap_fd);
+        name = netdev->name;
+        netdev->class->close(netdev);
+        free(name);
+    }
+}
+
+/* Returns true if a network device named 'name' exists and may be opened,
+ * otherwise false. */
+bool
+netdev_exists(const char *name)
+{
+    struct netdev *netdev;
+    int error;
+
+    error = netdev_open(name, NETDEV_ETH_TYPE_NONE, &netdev);
+    if (!error) {
+        netdev_close(netdev);
+        return true;
+    } else {
+        if (error != ENODEV) {
+            VLOG_WARN("failed to open network device %s: %s",
+                      name, strerror(error));
         }
-        free(netdev);
+        return false;
     }
 }
 
-/* Pads 'buffer' out with zero-bytes to the minimum valid length of an
- * Ethernet packet, if necessary.  */
-static void
-pad_to_minimum_length(struct ofpbuf *buffer)
+/* Initializes 'svec' with a list of the names of all known network devices. */
+int
+netdev_enumerate(struct svec *svec)
 {
-    if (buffer->size < ETH_TOTAL_MIN) {
-        ofpbuf_put_zeros(buffer, ETH_TOTAL_MIN - buffer->size);
+    int error;
+    int i;
+
+    svec_init(svec);
+
+    netdev_initialize();
+
+    error = 0;
+    for (i = 0; i < n_netdev_classes; i++) {
+        const struct netdev_class *class = netdev_classes[i];
+        if (class->enumerate) {
+            int retval = class->enumerate(svec);
+            if (retval) {
+                VLOG_WARN("failed to enumerate %s network devices: %s",
+                          class->name, strerror(retval));
+                if (!error) {
+                    error = retval;
+                }
+            }
+        }
     }
+    return error;
 }
 
 /* Attempts to receive a packet from 'netdev' into 'buffer', which the caller
@@ -561,31 +253,22 @@ pad_to_minimum_length(struct ofpbuf *buffer)
 int
 netdev_recv(struct netdev *netdev, struct ofpbuf *buffer)
 {
-    ssize_t n_bytes;
+    int retval;
 
     assert(buffer->size == 0);
     assert(ofpbuf_tailroom(buffer) >= ETH_TOTAL_MIN);
-    do {
-        n_bytes = read(netdev->tap_fd,
-                       ofpbuf_tail(buffer), ofpbuf_tailroom(buffer));
-    } while (n_bytes < 0 && errno == EINTR);
-    if (n_bytes < 0) {
-        if (errno != EAGAIN) {
-            VLOG_WARN_RL(&rl, "error receiving Ethernet packet on %s: %s",
-                         strerror(errno), netdev->name);
-        }
-        return errno;
-    } else {
+
+    retval = netdev->class->recv(netdev,
+                                 buffer->data, ofpbuf_tailroom(buffer));
+    if (retval >= 0) {
         COVERAGE_INC(netdev_received);
-        buffer->size += n_bytes;
-
-        /* When the kernel internally sends out an Ethernet frame on an
-         * interface, it gives us a copy *before* padding the frame to the
-         * minimum length.  Thus, when it sends out something like an ARP
-         * request, we see a too-short frame.  So pad it out to the minimum
-         * length. */
-        pad_to_minimum_length(buffer);
+        buffer->size += retval;
+        if (buffer->size < ETH_TOTAL_MIN) {
+            ofpbuf_put_zeros(buffer, ETH_TOTAL_MIN - buffer->size);
+        }
         return 0;
+    } else {
+        return -retval;
     }
 }
 
@@ -594,19 +277,14 @@ netdev_recv(struct netdev *netdev, struct ofpbuf *buffer)
 void
 netdev_recv_wait(struct netdev *netdev)
 {
-    poll_fd_wait(netdev->tap_fd, POLLIN);
+    netdev->class->recv_wait(netdev);
 }
 
 /* Discards all packets waiting to be received from 'netdev'. */
 int
 netdev_drain(struct netdev *netdev)
 {
-    if (netdev->tap_fd != netdev->netdev_fd) {
-        drain_fd(netdev->tap_fd, netdev->txqlen);
-        return 0;
-    } else {
-        return drain_rcvbuf(netdev->netdev_fd);
-    }
+    return netdev->class->drain(netdev);
 }
 
 /* Sends 'buffer' on 'netdev'.  Returns 0 if successful, otherwise a positive
@@ -621,32 +299,11 @@ netdev_drain(struct netdev *netdev)
 int
 netdev_send(struct netdev *netdev, const struct ofpbuf *buffer)
 {
-    ssize_t n_bytes;
-
-    do {
-        n_bytes = write(netdev->tap_fd, buffer->data, buffer->size);
-    } while (n_bytes < 0 && errno == EINTR);
-
-    if (n_bytes < 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 != EAGAIN) {
-            VLOG_WARN_RL(&rl, "error sending Ethernet packet on %s: %s",
-                         netdev->name, strerror(errno));
-        }
-        return errno;
-    } else if (n_bytes != buffer->size) {
-        VLOG_WARN_RL(&rl,
-                     "send partial Ethernet packet (%d bytes of %zu) on %s",
-                     (int) n_bytes, buffer->size, netdev->name);
-        return EMSGSIZE;
-    } else {
+    int error = netdev->class->send(netdev, buffer->data, buffer->size);
+    if (!error) {
         COVERAGE_INC(netdev_sent);
-        return 0;
     }
+    return error;
 }
 
 /* Registers with the poll loop to wake up from the next call to poll_block()
@@ -659,12 +316,7 @@ netdev_send(struct netdev *netdev, const struct ofpbuf *buffer)
 void
 netdev_send_wait(struct netdev *netdev)
 {
-    if (netdev->tap_fd == netdev->netdev_fd) {
-        poll_fd_wait(netdev->tap_fd, POLLOUT);
-    } else {
-        /* TAP device always accepts packets.*/
-        poll_immediate_wake();
-    }
+    return netdev->class->send_wait(netdev);
 }
 
 /* Attempts to set 'netdev''s MAC address to 'mac'.  Returns 0 if successful,
@@ -672,26 +324,16 @@ netdev_send_wait(struct netdev *netdev)
 int
 netdev_set_etheraddr(struct netdev *netdev, const uint8_t mac[ETH_ADDR_LEN])
 {
-    int error = set_etheraddr(netdev->name, netdev->hwaddr_family, mac);
-    if (!error) {
-        memcpy(netdev->etheraddr, mac, ETH_ADDR_LEN);
-    }
-    return error;
+    return netdev->class->set_etheraddr(netdev, mac);
 }
 
+/* Retrieves 'netdev''s MAC address.  If successful, returns 0 and copies the
+ * the MAC address into 'mac'.  On failure, returns a positive errno value and
+ * clears 'mac' to all-zeros. */
 int
-netdev_nodev_set_etheraddr(const char *name, const uint8_t mac[ETH_ADDR_LEN])
+netdev_get_etheraddr(const struct netdev *netdev, uint8_t mac[ETH_ADDR_LEN])
 {
-    init_netdev();
-    return set_etheraddr(name, ARPHRD_ETHER, mac);
-}
-
-/* Returns a pointer to 'netdev''s MAC address.  The caller must not modify or
- * free the returned buffer. */
-const uint8_t *
-netdev_get_etheraddr(const struct netdev *netdev)
-{
-    return netdev->etheraddr;
+    return netdev->class->get_etheraddr(netdev, mac);
 }
 
 /* Returns the name of the network device that 'netdev' represents,
@@ -702,13 +344,23 @@ netdev_get_name(const struct netdev *netdev)
     return netdev->name;
 }
 
-/* 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. */
+/* Retrieves the MTU of 'netdev'.  The MTU is the maximum size of transmitted
+ * (and received) packets, in bytes, not including the hardware header; thus,
+ * this is typically 1500 bytes for Ethernet devices.
+ *
+ * If successful, returns 0 and stores the MTU size in '*mtup'.  On failure,
+ * returns a positive errno value and stores ETH_PAYLOAD_MAX (1500) in
+ * '*mtup'. */
 int
-netdev_get_mtu(const struct netdev *netdev
+netdev_get_mtu(const struct netdev *netdev, int *mtup)
 {
-    return netdev->mtu;
+    int error = netdev->class->get_mtu(netdev, mtup);
+    if (error) {
+        VLOG_WARN_RL(&rl, "failed to retrieve MTU for network device %s: %s",
+                     netdev_get_name(netdev), strerror(error));
+        *mtup = ETH_PAYLOAD_MAX;
+    }
+    return error;
 }
 
 /* Stores the features supported by 'netdev' into each of '*current',
@@ -722,123 +374,45 @@ netdev_get_features(struct netdev *netdev,
                     uint32_t *supported, uint32_t *peer)
 {
     uint32_t dummy[4];
-    return do_get_features(netdev,
-                           current ? current : &dummy[0],
-                           advertised ? advertised : &dummy[1],
-                           supported ? supported : &dummy[2],
-                           peer ? peer : &dummy[3]);
+    return netdev->class->get_features(netdev,
+                                       current ? current : &dummy[0],
+                                       advertised ? advertised : &dummy[1],
+                                       supported ? supported : &dummy[2],
+                                       peer ? peer : &dummy[3]);
 }
 
+/* Set the features advertised by 'netdev' to 'advertise'.  Returns 0 if
+ * successful, otherwise a positive errno value. */
 int
 netdev_set_advertisements(struct netdev *netdev, uint32_t advertise)
 {
-    struct ethtool_cmd ecmd;
-    int error;
-
-    memset(&ecmd, 0, sizeof ecmd);
-    error = 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 do_ethtool(netdev, &ecmd, ETHTOOL_SSET, "ETHTOOL_SSET");
+    return (netdev->class->set_advertisements
+            ? netdev->class->set_advertisements(netdev, advertise)
+            : EOPNOTSUPP);
 }
 
-/* If 'netdev' has an assigned IPv4 address, sets '*in4' to that address (if
- * 'in4' is non-null) and returns true.  Otherwise, returns false. */
-bool
-netdev_nodev_get_in4(const char *netdev_name, struct in_addr *in4)
-{
-    struct ifreq ifr;
-    struct in_addr ip = { INADDR_ANY };
-
-    init_netdev();
-
-    strncpy(ifr.ifr_name, netdev_name, sizeof ifr.ifr_name);
-    ifr.ifr_addr.sa_family = AF_INET;
-    COVERAGE_INC(netdev_get_in4);
-    if (ioctl(af_inet_sock, SIOCGIFADDR, &ifr) == 0) {
-        struct sockaddr_in *sin = (struct sockaddr_in *) &ifr.ifr_addr;
-        ip = sin->sin_addr;
-    } else {
-        VLOG_DBG_RL(&rl, "%s: ioctl(SIOCGIFADDR) failed: %s",
-                    netdev_name, strerror(errno));
-    }
-    if (in4) {
-        *in4 = ip;
-    }
-    return ip.s_addr != INADDR_ANY;
-}
-
-bool
+/* If 'netdev' has an assigned IPv4 address, sets '*in4' to that address and
+ * returns 0.  Otherwise, returns a positive errno value and sets '*in4' to 0
+ * (INADDR_ANY).
+ *
+ * The following error values have well-defined meanings:
+ *
+ *   - EADDRNOTAVAIL: 'netdev' has no assigned IPv4 address.
+ *
+ *   - EOPNOTSUPP: No IPv4 network stack attached to 'netdev'.
+ *
+ * 'in4' may be null, in which case the address itself is not reported. */
+int
 netdev_get_in4(const struct netdev *netdev, struct in_addr *in4)
 {
-    return netdev_nodev_get_in4(netdev->name, in4);
-}
-
-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 sock,
-            int ioctl_nr, const char *ioctl_name, struct in_addr addr)
-{
-    struct ifreq ifr;
+    struct in_addr dummy;
     int error;
 
-    strncpy(ifr.ifr_name, netdev->name, sizeof ifr.ifr_name);
-    make_in4_sockaddr(&ifr.ifr_addr, addr);
-    COVERAGE_INC(netdev_set_in4);
-    error = ioctl(sock, ioctl_nr, &ifr) < 0 ? errno : 0;
-    if (error) {
-        VLOG_WARN("ioctl(%s): %s", ioctl_name, strerror(error));
+    error = (netdev->class->get_in4
+             ? netdev->class->get_in4(netdev, in4 ? in4 : &dummy)
+             : EOPNOTSUPP);
+    if (error && in4) {
+        in4->s_addr = 0;
     }
     return error;
 }
@@ -849,69 +423,46 @@ do_set_addr(struct netdev *netdev, int sock,
 int
 netdev_set_in4(struct netdev *netdev, struct in_addr addr, struct in_addr mask)
 {
-    int error;
-
-    error = do_set_addr(netdev, af_inet_sock,
-                        SIOCSIFADDR, "SIOCSIFADDR", addr);
-    if (!error && addr.s_addr != INADDR_ANY) {
-        error = do_set_addr(netdev, af_inet_sock,
-                            SIOCSIFNETMASK, "SIOCSIFNETMASK", mask);
-    }
-    return error;
+    return (netdev->class->set_in4
+            ? netdev->class->set_in4(netdev, addr, mask)
+            : EOPNOTSUPP);
 }
 
-/* Adds 'router' as a default IP gateway. */
+/* Adds 'router' as a default IP gateway for the TCP/IP stack that corresponds
+ * to 'netdev'. */
 int
-netdev_add_router(struct in_addr router)
+netdev_add_router(struct netdev *netdev, 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;
-}
-
-/* If 'netdev' has an assigned IPv6 address, sets '*in6' to that address (if
- * 'in6' is non-null) and returns true.  Otherwise, returns false. */
-bool
-netdev_get_in6(const struct netdev *netdev, struct in6_addr *in6)
-{
-    if (in6) {
-        *in6 = netdev->in6;
-    }
-    return memcmp(&netdev->in6, &in6addr_any, sizeof netdev->in6) != 0;
+    return (netdev->class->add_router
+            ? netdev->class->add_router(netdev, router)
+            : EOPNOTSUPP);
 }
 
-/* Obtains the current flags for 'netdev' and stores them into '*flagsp'.
- * Returns 0 if successful, otherwise a positive errno value.  On failure,
- * stores 0 into '*flagsp'. */
+/* If 'netdev' has an assigned IPv6 address, sets '*in6' to that address and
+ * returns 0.  Otherwise, returns a positive errno value and sets '*in6' to
+ * all-zero-bits (in6addr_any).
+ *
+ * The following error values have well-defined meanings:
+ *
+ *   - EADDRNOTAVAIL: 'netdev' has no assigned IPv6 address.
+ *
+ *   - EOPNOTSUPP: No IPv6 network stack attached to 'netdev'.
+ *
+ * 'in6' may be null, in which case the address itself is not reported. */
 int
-netdev_get_flags(const struct netdev *netdev, enum netdev_flags *flagsp)
+netdev_get_in6(const struct netdev *netdev, struct in6_addr *in6)
 {
-    return netdev_nodev_get_flags(netdev->name, flagsp);
-}
+    struct in6_addr dummy;
+    int error;
 
-static int
-nd_to_iff_flags(enum netdev_flags nd)
-{
-    int iff = 0;
-    if (nd & NETDEV_UP) {
-        iff |= IFF_UP;
+    error = (netdev->class->get_in6
+             ? netdev->class->get_in6(netdev, in6 ? in6 : &dummy)
+             : EOPNOTSUPP);
+    if (error && in6) {
+        memset(in6, 0, sizeof *in6);
     }
-    if (nd & NETDEV_PROMISC) {
-        iff |= IFF_PROMISC;
-    }
-    return iff;
+    return error;
 }
 
 /* On 'netdev', turns off the flags in 'off' and then turns on the flags in
@@ -920,26 +471,44 @@ nd_to_iff_flags(enum netdev_flags nd)
  * successful, otherwise a positive errno value. */
 static int
 do_update_flags(struct netdev *netdev, enum netdev_flags off,
-                enum netdev_flags on, bool permanent)
+                enum netdev_flags on, enum netdev_flags *old_flagsp,
+                bool permanent)
 {
-    int old_flags, new_flags;
+    enum netdev_flags old_flags;
     int error;
 
-    error = get_flags(netdev->name, &old_flags);
+    error = netdev->class->update_flags(netdev, off & ~on, on, &old_flags);
     if (error) {
-        return error;
-    }
-
-    new_flags = (old_flags & ~nd_to_iff_flags(off)) | nd_to_iff_flags(on);
-    if (!permanent) {
-        netdev->changed_flags |= new_flags ^ old_flags; 
+        VLOG_WARN_RL(&rl, "failed to %s flags for network device %s: %s",
+                     off || on ? "set" : "get", netdev_get_name(netdev),
+                     strerror(error));
+        old_flags = 0;
+    } else if ((off || on) && !permanent) {
+        enum netdev_flags new_flags = (old_flags & ~off) | on;
+        enum netdev_flags changed_flags = old_flags ^ new_flags;
+        if (changed_flags) {
+            if (!netdev->changed_flags) {
+                netdev->save_flags = old_flags;
+            }
+            netdev->changed_flags |= changed_flags;
+        }
     }
-    if (new_flags != old_flags) {
-        error = set_flags(netdev->name, new_flags);
+    if (old_flagsp) {
+        *old_flagsp = old_flags;
     }
     return error;
 }
 
+/* Obtains the current flags for 'netdev' and stores them into '*flagsp'.
+ * Returns 0 if successful, otherwise a positive errno value.  On failure,
+ * stores 0 into '*flagsp'. */
+int
+netdev_get_flags(const struct netdev *netdev_, enum netdev_flags *flagsp)
+{
+    struct netdev *netdev = (struct netdev *) netdev_;
+    return do_update_flags(netdev, 0, 0, flagsp, false);
+}
+
 /* Sets the flags for 'netdev' to 'flags'.
  * If 'permanent' is true, the changes will persist; otherwise, they
  * will be reverted when 'netdev' is closed or the program exits.
@@ -948,7 +517,7 @@ int
 netdev_set_flags(struct netdev *netdev, enum netdev_flags flags,
                  bool permanent)
 {
-    return do_update_flags(netdev, -1, flags, permanent);
+    return do_update_flags(netdev, -1, flags, NULL, permanent);
 }
 
 /* Turns on the specified 'flags' on 'netdev'.
@@ -959,7 +528,7 @@ int
 netdev_turn_flags_on(struct netdev *netdev, enum netdev_flags flags,
                      bool permanent)
 {
-    return do_update_flags(netdev, 0, flags, permanent);
+    return do_update_flags(netdev, 0, flags, NULL, permanent);
 }
 
 /* Turns off the specified 'flags' on 'netdev'.
@@ -970,505 +539,268 @@ int
 netdev_turn_flags_off(struct netdev *netdev, enum netdev_flags flags,
                       bool permanent)
 {
-    return do_update_flags(netdev, flags, 0, permanent);
+    return do_update_flags(netdev, flags, 0, NULL, permanent);
 }
 
 /* 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'. */
-int
-netdev_nodev_arp_lookup(const char *netdev_name, uint32_t ip, 
-                        uint8_t mac[ETH_ADDR_LEN]) 
-{
-    struct arpreq r;
-    struct sockaddr_in *pa;
-    int retval;
-
-    init_netdev();
-
-    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;
-}
-
+ * ENXIO indicates that there is no ARP table entry for 'ip' on 'netdev'. */
 int
-netdev_arp_lookup(const struct netdev *netdev, uint32_t ip, 
-                  uint8_t mac[ETH_ADDR_LEN]) 
+netdev_arp_lookup(const struct netdev *netdev,
+                  uint32_t ip, uint8_t mac[ETH_ADDR_LEN])
 {
-    return netdev_nodev_arp_lookup(netdev->name, ip, mac);
-}
-
-static int
-get_stats_via_netlink(int ifindex, struct netdev_stats *stats)
-{
-    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;
-
-    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);
+    int error = (netdev->class->arp_lookup
+                 ? netdev->class->arp_lookup(netdev, ip, mac)
+                 : EOPNOTSUPP);
     if (error) {
-        return error;
+        memset(mac, 0, ETH_ADDR_LEN);
     }
-
-    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");
-        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;
-
-    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;
+    return error;
 }
 
+/* Sets 'carrier' to true if carrier is active (link light is on) on
+ * 'netdev'. */
 int
 netdev_get_carrier(const struct netdev *netdev, bool *carrier)
 {
-    return netdev_nodev_get_carrier(netdev->name, carrier);
-}
-
-int
-netdev_nodev_get_carrier(const char *netdev_name, bool *carrier)
-{
-    char line[8];
-    int retval;
-    int error;
-    char *fn;
-    int fd;
-
-    *carrier = false;
-
-    fn = xasprintf("/sys/class/net/%s/carrier", netdev_name);
-    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_close;
-    } else if (retval == 0) {
-        error = EPROTO;
-        VLOG_WARN_RL(&rl, "%s: unexpected end of file", fn);
-        goto exit_close;
-    }
-
-    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_close;
+    int error = (netdev->class->get_carrier
+                 ? netdev->class->get_carrier(netdev, carrier)
+                 : EOPNOTSUPP);
+    if (error) {
+        *carrier = false;
     }
-    *carrier = line[0] != '0';
-    error = 0;
-
-exit_close:
-    close(fd);
-exit:
-    free(fn);
     return error;
 }
 
+/* Retrieves current device stats for 'netdev'. */
 int
 netdev_get_stats(const struct netdev *netdev, struct netdev_stats *stats)
 {
     int error;
 
     COVERAGE_INC(netdev_get_stats);
-    if (use_netlink_stats) {
-        int ifindex;
-
-        error = get_ifindex(netdev, &ifindex);
-        if (!error) {
-            error = get_stats_via_netlink(ifindex, stats);
-        }
-    } else {
-        error = get_stats_via_proc(netdev->name, stats);
-    }
-
+    error = (netdev->class->get_stats
+             ? netdev->class->get_stats(netdev, stats)
+             : EOPNOTSUPP);
     if (error) {
         memset(stats, 0xff, sizeof *stats);
     }
     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. */
-int
-netdev_nodev_set_policing(const char *netdev_name, uint32_t kbits_rate,
-                          uint32_t kbits_burst)
-{
-    char command[1024];
-
-    init_netdev();
-
-    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;
-}
-
+/* Attempts to set input rate limiting (policing) policy, such that up to
+ * 'kbits_rate' kbps of traffic is accepted, with a maximum accumulative burst
+ * size of 'kbits' kb. */
 int
 netdev_set_policing(struct netdev *netdev, uint32_t kbits_rate,
                     uint32_t kbits_burst)
 {
-    return netdev_nodev_set_policing(netdev->name, kbits_rate, kbits_burst);
+    return (netdev->class->set_policing
+            ? netdev->class->set_policing(netdev, kbits_rate, kbits_burst)
+            : EOPNOTSUPP);
 }
 
-/* Initializes 'svec' with a list of the names of all known network devices. */
-void
-netdev_enumerate(struct svec *svec)
+/* If 'netdev' is 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. */
+int
+netdev_get_vlan_vid(const struct netdev *netdev, int *vlan_vid)
 {
-    struct if_nameindex *names;
-
-    svec_init(svec);
-    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);
-    } else {
-        VLOG_WARN("could not obtain list of network device names: %s",
-                  strerror(errno));
+    int error = (netdev->class->get_vlan_vid
+                 ? netdev->class->get_vlan_vid(netdev, vlan_vid)
+                 : ENOENT);
+    if (error) {
+        *vlan_vid = 0;
     }
+    return error;
 }
 
-/* Attempts to locate a device based on its IPv4 address.  The caller
- * may provide a hint as to the device by setting 'netdev_name' to a
- * likely device name.  This string must be malloc'd, since if it is 
- * not correct then it will be freed.  If there is no hint, then
- * 'netdev_name' must be the NULL pointer.
- *
- * If the device is found, the return value will be true and 'netdev_name' 
- * contains the device's name as a string, which the caller is responsible 
- * for freeing.  If the device is not found, the return value is false. */
-bool
-netdev_find_dev_by_in4(const struct in_addr *in4, char **netdev_name)
+/* Returns a network device that has 'in4' as its IP address, if one exists,
+ * otherwise a null pointer. */
+struct netdev *
+netdev_find_dev_by_in4(const struct in_addr *in4)
 {
-    int i;
-    struct in_addr dev_in4;
+    struct netdev *netdev;
     struct svec dev_list;
+    size_t i;
 
-    /* Check the hint first. */
-    if (*netdev_name && (netdev_nodev_get_in4(*netdev_name, &dev_in4)) 
-            && (dev_in4.s_addr == in4->s_addr)) {
-        return true;
-    }
-
-    free(*netdev_name);
-    *netdev_name = NULL;
     netdev_enumerate(&dev_list);
-
-    for (i=0; i<dev_list.n; i++) {
-        if ((netdev_nodev_get_in4(dev_list.names[i], &dev_in4)) 
-                && (dev_in4.s_addr == in4->s_addr)) {
-            *netdev_name = xstrdup(dev_list.names[i]);
-            svec_destroy(&dev_list);
-            return true;
+    for (i = 0; i < dev_list.n; i++) {
+        const char *name = dev_list.names[i];
+        struct in_addr dev_in4;
+
+        if (!netdev_open(name, NETDEV_ETH_TYPE_NONE, &netdev)
+            && !netdev_get_in4(netdev, &dev_in4)
+            && dev_in4.s_addr == in4->s_addr) {
+            goto exit;
         }
+        netdev_close(netdev);
     }
+    netdev = NULL;
 
+exit:
     svec_destroy(&dev_list);
-    return false;
+    return netdev;
 }
-
-/* Obtains the current flags for the network device named 'netdev_name' and
- * stores them into '*flagsp'.  Returns 0 if successful, otherwise a positive
- * errno value.  On error, stores 0 into '*flagsp'.
+\f
+/* Initializes 'netdev' as a netdev named 'name' of the specified 'class'.
  *
- * If only device flags are needed, this is more efficient than calling
- * netdev_open(), netdev_get_flags(), netdev_close(). */
-int
-netdev_nodev_get_flags(const char *netdev_name, enum netdev_flags *flagsp)
+ * This function adds 'netdev' to a netdev-owned linked list, so it is very
+ * important that 'netdev' only be freed after calling netdev_close(). */
+void
+netdev_init(struct netdev *netdev, const char *name,
+            const struct netdev_class *class)
 {
-    int error, flags;
-
-    init_netdev();
-
-    *flagsp = 0;
-    error = get_flags(netdev_name, &flags);
-    if (error) {
-        return error;
-    }
-
-    if (flags & IFF_UP) {
-        *flagsp |= NETDEV_UP;
-    }
-    if (flags & IFF_PROMISC) {
-        *flagsp |= NETDEV_PROMISC;
-    }
-    return 0;
+    netdev->class = class;
+    netdev->name = xstrdup(name);
+    netdev->save_flags = 0;
+    netdev->changed_flags = 0;
+    list_push_back(&netdev_list, &netdev->node);
 }
 
-int
-netdev_nodev_get_etheraddr(const char *netdev_name, uint8_t mac[6])
+/* Initializes 'notifier' as a netdev notifier for 'netdev', for which
+ * notification will consist of calling 'cb', with auxiliary data 'aux'. */
+void
+netdev_notifier_init(struct netdev_notifier *notifier, struct netdev *netdev,
+                     void (*cb)(struct netdev_notifier *), void *aux)
 {
-    init_netdev();
+    notifier->netdev = netdev;
+    notifier->cb = cb;
+    notifier->aux = aux;
+}
+\f
+/* Tracks changes in the status of a set of network devices. */
+struct netdev_monitor {
+    struct shash polled_netdevs;
+    struct shash changed_netdevs;
+};
 
-    return get_etheraddr(netdev_name, mac, NULL);
+/* Creates and returns a new structure for monitor changes in the status of
+ * network devices. */
+struct netdev_monitor *
+netdev_monitor_create(void)
+{
+    struct netdev_monitor *monitor = xmalloc(sizeof *monitor);
+    shash_init(&monitor->polled_netdevs);
+    shash_init(&monitor->changed_netdevs);
+    return monitor;
 }
 
-/* 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. */
-int
-netdev_get_vlan_vid(const char *netdev_name, int *vlan_vid)
+/* Destroys 'monitor'. */
+void
+netdev_monitor_destroy(struct netdev_monitor *monitor)
 {
-    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 (monitor) {
+        struct shash_node *node;
 
-    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);
+        SHASH_FOR_EACH (node, &monitor->polled_netdevs) {
+            struct netdev_notifier *notifier = node->data;
+            notifier->netdev->class->poll_remove(notifier);
         }
-        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);
+        shash_destroy(&monitor->polled_netdevs);
+        shash_destroy(&monitor->changed_netdevs);
+        free(monitor);
     }
-    ds_destroy(&line);
-    if (error) {
-        *vlan_vid = -1;
-    }
-    return error;
 }
-\f
-static void restore_all_flags(void *aux);
 
-/* Set up a signal hook to restore network device flags on program
- * termination.  */
 static void
-init_netdev(void)
+netdev_monitor_cb(struct netdev_notifier *notifier)
 {
-    static bool inited;
-    if (!inited) {
-        int ifindex;
-        int error;
-
-        inited = true;
-
-        fatal_signal_add_hook(restore_all_flags, NULL, true);
+    struct netdev_monitor *monitor = notifier->aux;
+    const char *name = netdev_get_name(notifier->netdev);
+    if (!shash_find(&monitor->changed_netdevs, name)) {
+        shash_add(&monitor->changed_netdevs, name, NULL);
+    }
+}
 
-        af_inet_sock = socket(AF_INET, SOCK_DGRAM, 0);
-        if (af_inet_sock < 0) {
-            ovs_fatal(errno, "socket(AF_INET)");
+/* Attempts to add 'netdev' as a netdev monitored by 'monitor'.  Returns 0 if
+ * successful, otherwise a positive errno value.
+ *
+ * Adding a given 'netdev' to a monitor multiple times is equivalent to adding
+ * it once. */
+int
+netdev_monitor_add(struct netdev_monitor *monitor, struct netdev *netdev)
+{
+    const char *netdev_name = netdev_get_name(netdev);
+    int error = 0;
+    if (!shash_find(&monitor->polled_netdevs, netdev_name)
+        && netdev->class->poll_add)
+    {
+        struct netdev_notifier *notifier;
+        error = netdev->class->poll_add(netdev, netdev_monitor_cb, monitor,
+                                        &notifier);
+        if (!error) {
+            assert(notifier->netdev == netdev);
+            shash_add(&monitor->polled_netdevs, netdev_name, notifier);
         }
+    }
+    return error;
+}
 
-        error = nl_sock_create(NETLINK_ROUTE, 0, 0, 0, &rtnl_sock);
-        if (error) {
-            ovs_fatal(error, "socket(AF_NETLINK, NETLINK_ROUTE)");
+/* Removes 'netdev' from the set of netdevs monitored by 'monitor'.  (This has
+ * no effect if 'netdev' is not in the set of devices monitored by
+ * 'monitor'.) */
+void
+netdev_monitor_remove(struct netdev_monitor *monitor, struct netdev *netdev)
+{
+    const char *netdev_name = netdev_get_name(netdev);
+    struct shash_node *node;
+
+    node = shash_find(&monitor->polled_netdevs, netdev_name);
+    if (node) {
+        /* Cancel future notifications. */
+        struct netdev_notifier *notifier = node->data;
+        netdev->class->poll_remove(notifier);
+        shash_delete(&monitor->polled_netdevs, node);
+
+        /* Drop any pending notification. */
+        node = shash_find(&monitor->changed_netdevs, netdev_name);
+        if (node) {
+            shash_delete(&monitor->changed_netdevs, node);
         }
+    }
+}
 
-        /* Decide on the netdev_get_stats() implementation to use.  Netlink is
-         * preferable, so if that works, we'll use it. */
-        ifindex = do_get_ifindex("lo");
-        if (ifindex < 0) {
-            VLOG_WARN("failed to get ifindex for lo, "
-                      "obtaining netdev stats from proc");
-            use_netlink_stats = false;
-        } else {
-            struct netdev_stats stats;
-            error = get_stats_via_netlink(ifindex, &stats);
-            if (!error) {
-                VLOG_DBG("obtaining netdev stats via rtnetlink");
-                use_netlink_stats = 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));
-                use_netlink_stats = false;
-            }
-        }
+/* Checks for changes to netdevs in the set monitored by 'monitor'.  If any of
+ * the attributes (Ethernet address, carrier status, speed or peer-advertised
+ * speed, flags, etc.) of a network device monitored by 'monitor' has changed,
+ * sets '*devnamep' to the name of a device that has changed and returns 0.
+ * The caller is responsible for freeing '*devnamep' (with free()).
+ *
+ * If no devices have changed, sets '*devnamep' to NULL and returns EAGAIN.
+ */
+int
+netdev_monitor_poll(struct netdev_monitor *monitor, char **devnamep)
+{
+    struct shash_node *node = shash_first(&monitor->changed_netdevs);
+    if (!node) {
+        *devnamep = NULL;
+        return EAGAIN;
+    } else {
+        *devnamep = xstrdup(node->name);
+        shash_delete(&monitor->changed_netdevs, node);
+        return 0;
     }
 }
 
+/* Registers with the poll loop to wake up from the next call to poll_block()
+ * when netdev_monitor_poll(monitor) would indicate that a device has
+ * changed. */
+void
+netdev_monitor_poll_wait(const struct netdev_monitor *monitor)
+{
+    if (!shash_is_empty(&monitor->changed_netdevs)) {
+        poll_immediate_wake();
+    } else {
+        /* XXX Nothing needed here for netdev_linux, but maybe other netdev
+         * classes need help. */
+    }
+}
+\f
 /* Restore the network device flags on 'netdev' to those that were active
  * before we changed them.  Returns 0 if successful, otherwise a positive
  * errno value.
@@ -1477,27 +809,13 @@ init_netdev(void)
 static int
 restore_flags(struct netdev *netdev)
 {
-    struct ifreq ifr;
-    int restore_flags;
-
-    /* Get current flags. */
-    strncpy(ifr.ifr_name, netdev->name, sizeof ifr.ifr_name);
-    COVERAGE_INC(netdev_get_flags);
-    if (ioctl(netdev->netdev_fd, SIOCGIFFLAGS, &ifr) < 0) {
-        return errno;
+    if (netdev->changed_flags) {
+        enum netdev_flags restore = netdev->save_flags & netdev->changed_flags;
+        enum netdev_flags old_flags;
+        return netdev->class->update_flags(netdev,
+                                           netdev->changed_flags & ~restore,
+                                           restore, &old_flags);
     }
-
-    /* Restore flags that we might have changed, if necessary. */
-    restore_flags = netdev->changed_flags & (IFF_PROMISC | IFF_UP);
-    if ((ifr.ifr_flags ^ netdev->save_flags) & restore_flags) {
-        ifr.ifr_flags &= ~restore_flags;
-        ifr.ifr_flags |= netdev->save_flags & restore_flags;
-        COVERAGE_INC(netdev_set_flags);
-        if (ioctl(netdev->netdev_fd, SIOCSIFFLAGS, &ifr) < 0) {
-            return errno;
-        }
-    }
-
     return 0;
 }
 
@@ -1511,108 +829,3 @@ restore_all_flags(void *aux UNUSED)
         restore_flags(netdev);
     }
 }
-
-static int
-get_flags(const char *netdev_name, int *flags)
-{
-    struct ifreq ifr;
-    strncpy(ifr.ifr_name, netdev_name, sizeof ifr.ifr_name);
-    COVERAGE_INC(netdev_get_flags);
-    if (ioctl(af_inet_sock, SIOCGIFFLAGS, &ifr) < 0) {
-        VLOG_ERR("ioctl(SIOCGIFFLAGS) on %s device failed: %s",
-                 netdev_name, strerror(errno));
-        return errno;
-    }
-    *flags = ifr.ifr_flags;
-    return 0;
-}
-
-static int
-set_flags(const char *netdev_name, int flags)
-{
-    struct ifreq ifr;
-    strncpy(ifr.ifr_name, netdev_name, sizeof ifr.ifr_name);
-    ifr.ifr_flags = flags;
-    COVERAGE_INC(netdev_set_flags);
-    if (ioctl(af_inet_sock, SIOCSIFFLAGS, &ifr) < 0) {
-        VLOG_ERR("ioctl(SIOCSIFFLAGS) on %s device failed: %s",
-                 netdev_name, strerror(errno));
-        return errno;
-    }
-    return 0;
-}
-
-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)
-{
-    *ifindexp = 0;
-    if (netdev->ifindex < 0) {
-        int ifindex = do_get_ifindex(netdev->name);
-        if (ifindex < 0) {
-            return -ifindex;
-        }
-        ((struct netdev *) netdev)->ifindex = ifindex;
-    }
-    *ifindexp = netdev->ifindex;
-    return 0;
-}
-
-static int
-get_etheraddr(const char *netdev_name, uint8_t ea[ETH_ADDR_LEN],
-              int *hwaddr_familyp)
-{
-    struct ifreq ifr;
-
-    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;
-    }
-    if (hwaddr_familyp) {
-        int hwaddr_family = ifr.ifr_hwaddr.sa_family;
-        *hwaddr_familyp = hwaddr_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;
-}
index d8f1e09..b66d7bc 100644 (file)
@@ -34,7 +34,8 @@ struct svec;
 
 enum netdev_flags {
     NETDEV_UP = 0x0001,         /* Device enabled? */
-    NETDEV_PROMISC = 0x0002     /* Promiscuous mode? */
+    NETDEV_PROMISC = 0x0002,    /* Promiscuous mode? */
+    NETDEV_LOOPBACK = 0x0004    /* This is a loopback device. */
 };
 
 enum netdev_pseudo_ethertype {
@@ -43,6 +44,9 @@ enum netdev_pseudo_ethertype {
     NETDEV_ETH_TYPE_802_2        /* Receive all IEEE 802.2 frames. */
 };
 
+/* Network device statistics.
+ *
+ * Values of unsupported statistics are set to all-1-bits (UINT64_MAX). */
 struct netdev_stats {
     uint64_t rx_packets;        /* Total packets received. */
     uint64_t tx_packets;        /* Total packets transmitted. */
@@ -73,49 +77,59 @@ struct netdev_stats {
 
 struct netdev;
 
+int netdev_initialize(void);
+void netdev_run(void);
+void netdev_wait(void);
+
 int netdev_open(const char *name, int ethertype, struct netdev **);
-int netdev_open_tap(const char *name, struct netdev **);
 void netdev_close(struct netdev *);
 
+bool netdev_exists(const char *name);
+
+int netdev_enumerate(struct svec *);
+
+const char *netdev_get_name(const struct netdev *);
+int netdev_get_mtu(const struct netdev *, int *mtup);
+
 int netdev_recv(struct netdev *, struct ofpbuf *);
 void netdev_recv_wait(struct netdev *);
 int netdev_drain(struct netdev *);
+
 int netdev_send(struct netdev *, const struct ofpbuf *);
 void netdev_send_wait(struct netdev *);
+
 int netdev_set_etheraddr(struct netdev *, const uint8_t mac[6]);
-const uint8_t *netdev_get_etheraddr(const struct netdev *);
-const char *netdev_get_name(const struct netdev *);
-int netdev_get_mtu(const struct netdev *);
+int netdev_get_etheraddr(const struct netdev *, uint8_t mac[6]);
+
+int netdev_get_carrier(const struct netdev *, bool *carrier);
 int netdev_get_features(struct netdev *,
                         uint32_t *current, uint32_t *advertised,
                         uint32_t *supported, uint32_t *peer);
 int netdev_set_advertisements(struct netdev *, uint32_t advertise);
-bool netdev_get_in4(const struct netdev *, struct in_addr *);
+
+int netdev_get_in4(const struct netdev *, struct in_addr *);
 int netdev_set_in4(struct netdev *, struct in_addr addr, struct in_addr mask);
-int netdev_add_router(struct in_addr router);
-bool netdev_get_in6(const struct netdev *, struct in6_addr *);
+int netdev_get_in6(const struct netdev *, struct in6_addr *);
+int netdev_add_router(struct netdev *, struct in_addr router);
+int netdev_arp_lookup(const struct netdev *, uint32_t ip, uint8_t mac[6]);
+
 int netdev_get_flags(const struct netdev *, enum netdev_flags *);
 int netdev_set_flags(struct netdev *, enum netdev_flags, bool permanent);
 int netdev_turn_flags_on(struct netdev *, enum netdev_flags, bool permanent);
 int netdev_turn_flags_off(struct netdev *, enum netdev_flags, bool permanent);
-int netdev_arp_lookup(const struct netdev *, uint32_t ip, uint8_t mac[6]);
-int netdev_get_carrier(const struct netdev *, bool *carrier);
+
 int netdev_get_stats(const struct netdev *, struct netdev_stats *);
 int netdev_set_policing(struct netdev *, uint32_t kbits_rate, 
                         uint32_t kbits_burst);
 
-void netdev_enumerate(struct svec *);
-bool netdev_find_dev_by_in4(const struct in_addr *in4, char **netdev_name);
-int netdev_nodev_get_flags(const char *netdev_name, enum netdev_flags *);
-bool netdev_nodev_get_in4(const char *netdev_name, struct in_addr *);
-int netdev_nodev_set_etheraddr(const char *name, const uint8_t mac[6]);
-int netdev_nodev_get_etheraddr(const char *netdev_name, uint8_t mac[6]);
-int netdev_nodev_set_policing(const char *netdev_name, uint32_t kbits_rate, 
-                              uint32_t kbits_burst);
-int netdev_nodev_arp_lookup(const char *netdev_name, uint32_t ip, 
-                            uint8_t mac[6]);
-int netdev_nodev_get_carrier(const char *netdev_name, bool *carrier);
-
-int netdev_get_vlan_vid(const char *netdev_name, int *vlan_vid);
+int netdev_get_vlan_vid(const struct netdev *, int *vlan_vid);
+struct netdev *netdev_find_dev_by_in4(const struct in_addr *);
+
+struct netdev_monitor *netdev_monitor_create(void);
+void netdev_monitor_destroy(struct netdev_monitor *);
+int netdev_monitor_add(struct netdev_monitor *, struct netdev *);
+void netdev_monitor_remove(struct netdev_monitor *, struct netdev *);
+int netdev_monitor_poll(struct netdev_monitor *, char **devnamep);
+void netdev_monitor_poll_wait(const struct netdev_monitor *);
 
 #endif /* netdev.h */
index 09c0e9d..4c6c9e9 100644 (file)
@@ -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;
diff --git a/lib/rtnetlink.c b/lib/rtnetlink.c
new file mode 100644 (file)
index 0000000..29e02f6
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+ * 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 "rtnetlink.h"
+
+#include <errno.h>
+#include <sys/socket.h>
+#include <linux/rtnetlink.h>
+#include <net/if.h>
+#include <poll.h>
+
+#include "coverage.h"
+#include "netlink.h"
+#include "ofpbuf.h"
+
+#define THIS_MODULE VLM_rtnetlink
+#include "vlog.h"
+
+/* rtnetlink socket. */
+static struct nl_sock *notify_sock;
+
+/* All registered notifiers. */
+static struct list all_notifiers = LIST_INITIALIZER(&all_notifiers);
+
+static void rtnetlink_report_change(const struct nlmsghdr *,
+                                    const struct ifinfomsg *,
+                                    struct nlattr *attrs[]);
+static void rtnetlink_report_notify_error(void);
+
+/* Registers 'cb' to be called with auxiliary data 'aux' with network device
+ * change notifications.  The notifier is stored in 'notifier', which the
+ * caller must not modify or free.
+ *
+ * This is probably not the function that you want.  You should probably be
+ * using dpif_port_poll() or netdev_monitor_create(), which unlike this
+ * function are not Linux-specific.
+ *
+ * Returns 0 if successful, otherwise a positive errno value. */
+int
+rtnetlink_notifier_register(struct rtnetlink_notifier *notifier,
+                            rtnetlink_notify_func *cb, void *aux)
+{
+    if (!notify_sock) {
+        int error = nl_sock_create(NETLINK_ROUTE, RTNLGRP_LINK, 0, 0,
+                                   &notify_sock);
+        if (error) {
+            VLOG_WARN("could not create rtnetlink socket: %s",
+                      strerror(error));
+            return error;
+        }
+    } else {
+        /* Catch up on notification work so that the new notifier won't
+         * receive any stale notifications. */
+        rtnetlink_notifier_run();
+    }
+
+    list_push_back(&all_notifiers, &notifier->node);
+    notifier->cb = cb;
+    notifier->aux = aux;
+    return 0;
+}
+
+/* Cancels notification on 'notifier', which must have previously been
+ * registered with lxnetdev_notifier_register(). */
+void
+rtnetlink_notifier_unregister(struct rtnetlink_notifier *notifier)
+{
+    list_remove(&notifier->node);
+    if (list_is_empty(&all_notifiers)) {
+        nl_sock_destroy(notify_sock);
+        notify_sock = NULL;
+    }
+}
+
+/* Calls all of the registered notifiers, passing along any as-yet-unreported
+ * netdev change events. */
+void
+rtnetlink_notifier_run(void)
+{
+    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+
+    if (!notify_sock) {
+        return;
+    }
+
+    for (;;) {
+        /* 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 rtnetlink_policy[] = {
+            [IFLA_IFNAME] = { .type = NL_A_STRING, .optional = false },
+            [IFLA_MASTER] = { .type = NL_A_U32, .optional = true },
+        };
+
+        struct nlattr *attrs[ARRAY_SIZE(rtnetlink_policy)];
+        struct ofpbuf *buf;
+        int error;
+
+        error = nl_sock_recv(notify_sock, &buf, false);
+        if (!error) {
+            if (nl_policy_parse(buf, NLMSG_HDRLEN + sizeof(struct ifinfomsg),
+                                rtnetlink_policy,
+                                attrs, ARRAY_SIZE(rtnetlink_policy))) {
+                struct ifinfomsg *ifinfo;
+
+                ifinfo = (void *) ((char *) buf->data + NLMSG_HDRLEN);
+                rtnetlink_report_change(buf->data, ifinfo, attrs);
+            } else {
+                VLOG_WARN_RL(&rl, "received bad rtnl message");
+                rtnetlink_report_notify_error();
+            }
+            ofpbuf_delete(buf);
+        } else if (error == EAGAIN) {
+            return;
+        } else {
+            if (error == ENOBUFS) {
+                VLOG_WARN_RL(&rl, "rtnetlink receive buffer overflowed");
+            } else {
+                VLOG_WARN_RL(&rl, "error reading rtnetlink socket: %s",
+                             strerror(error));
+            }
+            rtnetlink_report_notify_error();
+        }
+    }
+}
+
+/* Causes poll_block() to wake up when network device change notifications are
+ * ready. */
+void
+rtnetlink_notifier_wait(void)
+{
+    if (notify_sock) {
+        nl_sock_wait(notify_sock, POLLIN);
+    }
+}
+
+static void
+rtnetlink_report_change(const struct nlmsghdr *nlmsg,
+                           const struct ifinfomsg *ifinfo,
+                           struct nlattr *attrs[])
+{
+    struct rtnetlink_notifier *notifier;
+    struct rtnetlink_change change;
+
+    COVERAGE_INC(rtnetlink_changed);
+
+    change.nlmsg_type = nlmsg->nlmsg_type;
+    change.ifi_index = ifinfo->ifi_index;
+    change.ifname = nl_attr_get_string(attrs[IFLA_IFNAME]);
+    change.master_ifindex = (attrs[IFLA_MASTER]
+                             ? nl_attr_get_u32(attrs[IFLA_MASTER]) : 0);
+
+    LIST_FOR_EACH (notifier, struct rtnetlink_notifier, node,
+                   &all_notifiers) {
+        notifier->cb(&change, notifier->aux);
+    }
+}
+
+static void
+rtnetlink_report_notify_error(void)
+{
+    struct rtnetlink_notifier *notifier;
+
+    LIST_FOR_EACH (notifier, struct rtnetlink_notifier, node,
+                   &all_notifiers) {
+        notifier->cb(NULL, notifier->aux);
+    }
+}
diff --git a/lib/rtnetlink.h b/lib/rtnetlink.h
new file mode 100644 (file)
index 0000000..ca7df7b
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2009 Nicira Networks.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef RTNETLINK_H
+#define RTNETLINK_H 1
+
+/* These functions are Linux specific, so they should be used directly only by
+ * Linux-specific code. */
+
+#include "list.h"
+
+/* A digested version of an rtnetlink message sent down by the kernel to
+ * indicate that a network device has been created or destroyed or changed.  */
+struct rtnetlink_change {
+    /* Copied from struct nlmsghdr. */
+    int nlmsg_type;             /* e.g. RTM_NEWLINK, RTM_DELLINK. */
+
+    /* Copied from struct ifinfomsg. */
+    int ifi_index;              /* Index of network device. */
+
+    /* Extracted from Netlink attributes. */
+    const char *ifname;         /* Name of network device. */
+    int master_ifindex;         /* Ifindex of datapath master (0 if none). */
+};
+
+/* Function called to report that a netdev has changed.  'change' describes the
+ * specific change.  It may be null if the buffer of change information
+ * overflowed, in which case the function must assume that every device may
+ * have changed.  'aux' is as specified in the call to
+ * lxnetdev_notifier_register().  */
+typedef void rtnetlink_notify_func(const struct rtnetlink_change *, void *aux);
+
+struct rtnetlink_notifier {
+    struct list node;
+    rtnetlink_notify_func *cb;
+    void *aux;
+};
+
+int rtnetlink_notifier_register(struct rtnetlink_notifier *,
+                                rtnetlink_notify_func *, void *aux);
+void rtnetlink_notifier_unregister(struct rtnetlink_notifier *);
+void rtnetlink_notifier_run(void);
+void rtnetlink_notifier_wait(void);
+
+#endif /* rtnetlink.h */
index 9ddafe0..7bb8cd7 100644 (file)
@@ -44,22 +44,29 @@ shash_clear(struct shash *sh)
 {
     struct shash_node *node, *next;
 
-    HMAP_FOR_EACH_SAFE (node, next, struct shash_node, node, &sh->map) {
+    SHASH_FOR_EACH_SAFE (node, next, sh) {
         hmap_remove(&sh->map, &node->node);
         free(node->name);
         free(node);
     }
 }
 
-/* It is the caller's responsible to avoid duplicate names, if that is
+bool
+shash_is_empty(const struct shash *shash)
+{
+    return hmap_is_empty(&shash->map);
+}
+
+/* It is the caller's responsibility to avoid duplicate names, if that is
  * desirable. */
-void
+struct shash_node *
 shash_add(struct shash *sh, const char *name, void *data)
 {
     struct shash_node *node = xmalloc(sizeof *node);
     node->name = xstrdup(name);
     node->data = data;
     hmap_insert(&sh->map, &node->node, hash_name(name));
+    return node;
 }
 
 void
@@ -91,3 +98,11 @@ shash_find_data(const struct shash *sh, const char *name)
     struct shash_node *node = shash_find(sh, name);
     return node ? node->data : NULL;
 }
+
+struct shash_node *
+shash_first(const struct shash *shash)
+{
+    struct hmap_node *node = hmap_first(&shash->map);
+    return node ? CONTAINER_OF(node, struct shash_node, node) : NULL;
+}
+
index 9201bb5..5794a20 100644 (file)
@@ -31,12 +31,21 @@ struct shash {
 
 #define SHASH_INITIALIZER(SHASH) { HMAP_INITIALIZER(&(SHASH)->map) }
 
+#define SHASH_FOR_EACH(SHASH_NODE, SHASH)                               \
+    HMAP_FOR_EACH (SHASH_NODE, struct shash_node, node, &(SHASH)->map)
+
+#define SHASH_FOR_EACH_SAFE(SHASH_NODE, NEXT, SHASH)                \
+    HMAP_FOR_EACH_SAFE (SHASH_NODE, NEXT, struct shash_node, node,  \
+                        &(SHASH)->map)
+
 void shash_init(struct shash *);
 void shash_destroy(struct shash *);
 void shash_clear(struct shash *);
-void shash_add(struct shash *, const char *, void *);
+bool shash_is_empty(const struct shash *);
+struct shash_node *shash_add(struct shash *, const char *, void *);
 void shash_delete(struct shash *, struct shash_node *);
 struct shash_node *shash_find(const struct shash *, const char *);
 void *shash_find_data(const struct shash *, const char *);
+struct shash_node *shash_first(const struct shash *);
 
 #endif /* shash.h */
index d13255f..3fcd5a1 100644 (file)
@@ -23,6 +23,7 @@
 #include <poll.h>
 #include <stddef.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 #include <sys/resource.h>
 #include <sys/un.h>
@@ -290,6 +291,172 @@ guess_netmask(uint32_t ip)
             : htonl(0));                          /* ??? */
 }
 
+/* Opens a non-blocking TCP socket and connects to 'target', which should be a
+ * string in the format "<host>[:<port>]", where <host> is required and <port>
+ * is optional, with 'default_port' assumed if <port> is omitted.
+ *
+ * On success, returns 0 (indicating connection complete) or EAGAIN (indicating
+ * connection in progress), in which case the new file descriptor is stored
+ * into '*fdp'.  On failure, returns a positive errno value other than EAGAIN
+ * and stores -1 into '*fdp'.
+ *
+ * If 'sinp' is non-null, then on success the target address is stored into
+ * '*sinp'. */
+int
+tcp_open_active(const char *target_, uint16_t default_port,
+                struct sockaddr_in *sinp, int *fdp)
+{
+    char *target = xstrdup(target_);
+    char *save_ptr = NULL;
+    const char *host_name;
+    const char *port_string;
+    struct sockaddr_in sin;
+    int fd = -1;
+    int error;
+
+    /* Defaults. */
+    memset(&sin, 0, sizeof sin);
+    sin.sin_family = AF_INET;
+    sin.sin_port = htons(default_port);
+
+    /* Tokenize. */
+    host_name = strtok_r(target, ":", &save_ptr);
+    port_string = strtok_r(NULL, ":", &save_ptr);
+    if (!host_name) {
+        ovs_error(0, "%s: bad peer name format", target_);
+        error = EAFNOSUPPORT;
+        goto exit;
+    }
+
+    /* Look up IP, port. */
+    error = lookup_ip(host_name, &sin.sin_addr);
+    if (error) {
+        goto exit;
+    }
+    if (port_string && atoi(port_string)) {
+        sin.sin_port = htons(atoi(port_string));
+    }
+
+    /* Create non-blocking socket. */
+    fd = socket(AF_INET, SOCK_STREAM, 0);
+    if (fd < 0) {
+        VLOG_ERR("%s: socket: %s", target_, strerror(errno));
+        error = errno;
+        goto exit;
+    }
+    error = set_nonblocking(fd);
+    if (error) {
+        goto exit_close;
+    }
+
+    /* Connect. */
+    error = connect(fd, (struct sockaddr *) &sin, sizeof sin) == 0 ? 0 : errno;
+    if (error == EINPROGRESS) {
+        error = EAGAIN;
+    } else if (error && error != EAGAIN) {
+        goto exit_close;
+    }
+
+    /* Success: error is 0 or EAGAIN. */
+    goto exit;
+
+exit_close:
+    close(fd);
+exit:
+    if (!error || error == EAGAIN) {
+        if (sinp) {
+            *sinp = sin;
+        }
+        *fdp = fd;
+    } else {
+        *fdp = -1;
+    }
+    free(target);
+    return error;
+}
+
+/* Opens a non-blocking TCP socket, binds to 'target', and listens for incoming
+ * connections.  'target' should be a string in the format "[<port>][:<ip>]",
+ * where both <port> and <ip> are optional.  If <port> is omitted, it defaults
+ * to 'default_port'; if <ip> is omitted it defaults to the wildcard IP
+ * address.
+ *
+ * The socket will have SO_REUSEADDR turned on.
+ *
+ * On success, returns a non-negative file descriptor.  On failure, returns a
+ * negative errno value. */
+int
+tcp_open_passive(const char *target_, uint16_t default_port)
+{
+    char *target = xstrdup(target_);
+    char *string_ptr = target;
+    struct sockaddr_in sin;
+    const char *host_name;
+    const char *port_string;
+    int fd, error;
+    unsigned int yes  = 1;
+
+    /* Address defaults. */
+    memset(&sin, 0, sizeof sin);
+    sin.sin_family = AF_INET;
+    sin.sin_addr.s_addr = htonl(INADDR_ANY);
+    sin.sin_port = htons(default_port);
+
+    /* Parse optional port number. */
+    port_string = strsep(&string_ptr, ":");
+    if (port_string && atoi(port_string)) {
+        sin.sin_port = htons(atoi(port_string));
+    }
+
+    /* Parse optional bind IP. */
+    host_name = strsep(&string_ptr, ":");
+    if (host_name && host_name[0]) {
+        error = lookup_ip(host_name, &sin.sin_addr);
+        if (error) {
+            goto exit;
+        }
+    }
+
+    /* Create non-blocking socket, set SO_REUSEADDR. */
+    fd = socket(AF_INET, SOCK_STREAM, 0);
+    if (fd < 0) {
+        error = errno;
+        VLOG_ERR("%s: socket: %s", target_, strerror(error));
+        goto exit;
+    }
+    error = set_nonblocking(fd);
+    if (error) {
+        goto exit_close;
+    }
+    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes) < 0) {
+        error = errno;
+        VLOG_ERR("%s: setsockopt(SO_REUSEADDR): %s", target_, strerror(error));
+        goto exit_close;
+    }
+
+    /* Bind. */
+    if (bind(fd, (struct sockaddr *) &sin, sizeof sin) < 0) {
+        error = errno;
+        VLOG_ERR("%s: bind: %s", target_, strerror(error));
+        goto exit_close;
+    }
+
+    /* Listen. */
+    if (listen(fd, 10) < 0) {
+        error = errno;
+        VLOG_ERR("%s: listen: %s", target_, strerror(error));
+        goto exit_close;
+    }
+    error = 0;
+    goto exit;
+
+exit_close:
+    close(fd);
+exit:
+    free(target);
+    return error ? -error : fd;
+}
+
 /* Returns a readable and writable fd for /dev/null, if successful, otherwise
  * a negative errno value.  The caller must not close the returned fd (because
  * the same fd will be handed out to subsequent callers). */
index 3ba2c47..febe5e7 100644 (file)
@@ -34,6 +34,10 @@ int get_unix_name_len(socklen_t sun_len);
 uint32_t guess_netmask(uint32_t ip);
 int get_null_fd(void);
 
+int tcp_open_active(const char *target, uint16_t default_port,
+                    struct sockaddr_in *sinp, int *fdp);
+int tcp_open_passive(const char *target, uint16_t default_port);
+
 int read_fully(int fd, void *, size_t, size_t *bytes_read);
 int write_fully(int fd, const void *, size_t, size_t *bytes_written);
 
index 526c3fe..17bd6cb 100644 (file)
@@ -82,7 +82,7 @@ unixctl_help(struct unixctl_conn *conn, const char *args UNUSED)
     struct shash_node *node;
 
     ds_put_cstr(&ds, "The available commands are:\n");
-    HMAP_FOR_EACH (node, struct shash_node, node, &commands.map) {
+    SHASH_FOR_EACH (node, &commands) {
         ds_put_format(&ds, "\t%s\n", node->name);
     }
     unixctl_command_reply(conn, 214, ds_cstr(&ds));
index f681bdf..9bb2df8 100644 (file)
@@ -280,59 +280,21 @@ ssl_vconn_cast(struct vconn *vconn)
 static int
 ssl_open(const char *name, char *suffix, struct vconn **vconnp)
 {
-    char *save_ptr = NULL;
-    char *host_name, *port_string;
     struct sockaddr_in sin;
-    int retval;
-    int fd;
-
-    retval = ssl_init();
-    if (retval) {
-        return retval;
-    }
-
-    host_name = strtok_r(suffix, ":", &save_ptr);
-    port_string = strtok_r(NULL, ":", &save_ptr);
-    if (!host_name) {
-        ovs_error(0, "%s: bad peer name format", name);
-        return EAFNOSUPPORT;
-    }
-
-    memset(&sin, 0, sizeof sin);
-    sin.sin_family = AF_INET;
-    if (lookup_ip(host_name, &sin.sin_addr)) {
-        return ENOENT;
-    }
-    sin.sin_port = htons(port_string && *port_string ? atoi(port_string)
-                         : OFP_SSL_PORT);
+    int error, fd;
 
-    /* Create socket. */
-    fd = socket(AF_INET, SOCK_STREAM, 0);
-    if (fd < 0) {
-        VLOG_ERR("%s: socket: %s", name, strerror(errno));
-        return errno;
-    }
-    retval = set_nonblocking(fd);
-    if (retval) {
-        close(fd);
-        return retval;
+    error = ssl_init();
+    if (error) {
+        return error;
     }
 
-    /* Connect socket. */
-    retval = connect(fd, (struct sockaddr *) &sin, sizeof sin);
-    if (retval < 0) {
-        if (errno == EINPROGRESS) {
-            return new_ssl_vconn(name, fd, CLIENT, STATE_TCP_CONNECTING,
-                                 &sin, vconnp);
-        } else {
-            int error = errno;
-            VLOG_ERR("%s: connect: %s", name, strerror(error));
-            close(fd);
-            return error;
-        }
+    error = tcp_open_active(suffix, OFP_SSL_PORT, &sin, &fd);
+    if (fd >= 0) {
+        int state = error ? STATE_TCP_CONNECTING : STATE_SSL_CONNECTING;
+        return new_ssl_vconn(name, fd, CLIENT, state, &sin, vconnp);
     } else {
-        return new_ssl_vconn(name, fd, CLIENT, STATE_SSL_CONNECTING,
-                             &sin, vconnp);
+        VLOG_ERR("%s: connect: %s", name, strerror(error));
+        return error;
     }
 }
 
@@ -805,55 +767,18 @@ pssl_pvconn_cast(struct pvconn *pvconn)
 static int
 pssl_open(const char *name, char *suffix, struct pvconn **pvconnp)
 {
-    struct sockaddr_in sin;
     struct pssl_pvconn *pssl;
     int retval;
     int fd;
-    unsigned int yes = 1;
 
     retval = ssl_init();
     if (retval) {
         return retval;
     }
 
-    /* Create socket. */
-    fd = socket(AF_INET, SOCK_STREAM, 0);
+    fd = tcp_open_passive(suffix, OFP_SSL_PORT);
     if (fd < 0) {
-        int error = errno;
-        VLOG_ERR("%s: socket: %s", name, strerror(error));
-        return error;
-    }
-
-    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes) < 0) {
-        int error = errno;
-        VLOG_ERR("%s: setsockopt(SO_REUSEADDR): %s", name, strerror(errno));
-        return error;
-    }
-
-    memset(&sin, 0, sizeof sin);
-    sin.sin_family = AF_INET;
-    sin.sin_addr.s_addr = htonl(INADDR_ANY);
-    sin.sin_port = htons(atoi(suffix) ? atoi(suffix) : OFP_SSL_PORT);
-    retval = bind(fd, (struct sockaddr *) &sin, sizeof sin);
-    if (retval < 0) {
-        int error = errno;
-        VLOG_ERR("%s: bind: %s", name, strerror(error));
-        close(fd);
-        return error;
-    }
-
-    retval = listen(fd, 10);
-    if (retval < 0) {
-        int error = errno;
-        VLOG_ERR("%s: listen: %s", name, strerror(error));
-        close(fd);
-        return error;
-    }
-
-    retval = set_nonblocking(fd);
-    if (retval) {
-        close(fd);
-        return retval;
+        return -fd;
     }
 
     pssl = xmalloc(sizeof *pssl);
index 6d38255..b38c568 100644 (file)
@@ -270,23 +270,7 @@ new_pstream_pvconn(const char *name, int fd,
                                    size_t sa_len, struct vconn **),
                   struct pvconn **pvconnp)
 {
-    struct pstream_pvconn *ps;
-    int retval;
-
-    retval = set_nonblocking(fd);
-    if (retval) {
-        close(fd);
-        return retval;
-    }
-
-    if (listen(fd, 10) < 0) {
-        int error = errno;
-        VLOG_ERR("%s: listen: %s", name, strerror(error));
-        close(fd);
-        return error;
-    }
-
-    ps = xmalloc(sizeof *ps);
+    struct pstream_pvconn *ps = xmalloc(sizeof *ps);
     pvconn_init(&ps->pvconn, &pstream_pvconn_class, name);
     ps->fd = fd;
     ps->accept_cb = accept_cb;
index 1161713..eece228 100644 (file)
@@ -72,51 +72,15 @@ new_tcp_vconn(const char *name, int fd, int connect_status,
 static int
 tcp_open(const char *name, char *suffix, struct vconn **vconnp)
 {
-    char *save_ptr = NULL;
-    const char *host_name;
-    const char *port_string;
     struct sockaddr_in sin;
-    int retval;
-    int fd;
-
-    host_name = strtok_r(suffix, ":", &save_ptr);
-    port_string = strtok_r(NULL, ":", &save_ptr);
-    if (!host_name) {
-        ovs_error(0, "%s: bad peer name format", name);
-        return EAFNOSUPPORT;
-    }
-
-    memset(&sin, 0, sizeof sin);
-    sin.sin_family = AF_INET;
-    if (lookup_ip(host_name, &sin.sin_addr)) {
-        return ENOENT;
-    }
-    sin.sin_port = htons(port_string ? atoi(port_string) : OFP_TCP_PORT);
-
-    fd = socket(AF_INET, SOCK_STREAM, 0);
-    if (fd < 0) {
-        VLOG_ERR("%s: socket: %s", name, strerror(errno));
-        return errno;
-    }
-
-    retval = set_nonblocking(fd);
-    if (retval) {
-        close(fd);
-        return retval;
-    }
+    int fd, error;
 
-    retval = connect(fd, (struct sockaddr *) &sin, sizeof sin);
-    if (retval < 0) {
-        if (errno == EINPROGRESS) {
-            return new_tcp_vconn(name, fd, EAGAIN, &sin, vconnp);
-        } else {
-            int error = errno;
-            VLOG_ERR("%s: connect: %s", name, strerror(error));
-            close(fd);
-            return error;
-        }
+    error = tcp_open_active(suffix, OFP_TCP_PORT, NULL, &fd);
+    if (fd >= 0) {
+        return new_tcp_vconn(name, fd, error, &sin, vconnp);
     } else {
-        return new_tcp_vconn(name, fd, 0, &sin, vconnp);
+        VLOG_ERR("%s: connect: %s", name, strerror(error));
+        return error;
     }
 }
 
@@ -136,37 +100,16 @@ static int ptcp_accept(int fd, const struct sockaddr *sa, size_t sa_len,
                        struct vconn **vconnp);
 
 static int
-ptcp_open(const char *name, char *suffix, struct pvconn **pvconnp)
+ptcp_open(const char *name UNUSED, char *suffix, struct pvconn **pvconnp)
 {
-    struct sockaddr_in sin;
-    int retval;
     int fd;
-    unsigned int yes  = 1;
 
-    fd = socket(AF_INET, SOCK_STREAM, 0);
+    fd = tcp_open_passive(suffix, OFP_TCP_PORT);
     if (fd < 0) {
-        VLOG_ERR("%s: socket: %s", name, strerror(errno));
-        return errno;
-    }
-
-    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes) < 0) {
-        VLOG_ERR("%s: setsockopt(SO_REUSEADDR): %s", name, strerror(errno));
-        return errno;
-    }
-
-    memset(&sin, 0, sizeof sin);
-    sin.sin_family = AF_INET;
-    sin.sin_addr.s_addr = htonl(INADDR_ANY);
-    sin.sin_port = htons(atoi(suffix) ? atoi(suffix) : OFP_TCP_PORT);
-    retval = bind(fd, (struct sockaddr *) &sin, sizeof sin);
-    if (retval < 0) {
-        int error = errno;
-        VLOG_ERR("%s: bind: %s", name, strerror(error));
-        close(fd);
-        return error;
+        return -fd;
+    } else {
+        return new_pstream_pvconn("ptcp", fd, ptcp_accept, pvconnp);
     }
-
-    return new_pstream_pvconn("ptcp", fd, ptcp_accept, pvconnp);
 }
 
 static int
index e39eaea..93d14e8 100644 (file)
@@ -81,7 +81,7 @@ static int punix_accept(int fd, const struct sockaddr *sa, size_t sa_len,
 static int
 punix_open(const char *name UNUSED, char *suffix, struct pvconn **pvconnp)
 {
-    int fd;
+    int fd, error;
 
     fd = make_unix_socket(SOCK_STREAM, true, true, suffix, NULL);
     if (fd < 0) {
@@ -89,6 +89,19 @@ punix_open(const char *name UNUSED, char *suffix, struct pvconn **pvconnp)
         return errno;
     }
 
+    error = set_nonblocking(fd);
+    if (error) {
+        close(fd);
+        return error;
+    }
+
+    if (listen(fd, 10) < 0) {
+        error = errno;
+        VLOG_ERR("%s: listen: %s", name, strerror(error));
+        close(fd);
+        return error;
+    }
+
     return new_pstream_pvconn("punix", fd, punix_accept, pvconnp);
 }
 
index b5e1102..3210923 100644 (file)
@@ -139,12 +139,12 @@ vconn_usage(bool active, bool passive, bool bootstrap UNUSED)
 
     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 @@ vcs_recv_hello(struct vconn *vconn)
 
     if (retval != EAGAIN) {
         vconn->state = VCS_DISCONNECTED;
-        vconn->error = retval;
+        vconn->error = retval == EOF ? ECONNRESET : retval;
     }
 }
 
@@ -458,10 +458,7 @@ vconn_recv(struct vconn *vconn, struct ofpbuf **msgp)
 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;
 
@@ -481,20 +478,6 @@ again:
             && 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",
index 216111b..849c867 100644 (file)
@@ -30,6 +30,8 @@ VLOG_MODULE(dhcp)
 VLOG_MODULE(dhcp_client)
 VLOG_MODULE(discovery)
 VLOG_MODULE(dpif)
+VLOG_MODULE(dpif_linux)
+VLOG_MODULE(dpif_netdev)
 VLOG_MODULE(dpctl)
 VLOG_MODULE(executer)
 VLOG_MODULE(ezio_term)
@@ -42,21 +44,22 @@ VLOG_MODULE(learning_switch)
 VLOG_MODULE(mac_learning)
 VLOG_MODULE(mgmt)
 VLOG_MODULE(netdev)
+VLOG_MODULE(netdev_linux)
 VLOG_MODULE(netflow)
 VLOG_MODULE(netlink)
 VLOG_MODULE(ofctl)
 VLOG_MODULE(ovs_discover)
 VLOG_MODULE(ofproto)
+VLOG_MODULE(openflowd)
 VLOG_MODULE(pktbuf)
 VLOG_MODULE(pcap)
 VLOG_MODULE(poll_loop)
 VLOG_MODULE(port_watcher)
 VLOG_MODULE(proc_net_compat)
 VLOG_MODULE(process)
-VLOG_MODULE(secchan)
 VLOG_MODULE(rconn)
+VLOG_MODULE(rtnetlink)
 VLOG_MODULE(stp)
-VLOG_MODULE(stp_secchan)
 VLOG_MODULE(stats)
 VLOG_MODULE(status)
 VLOG_MODULE(svec)
index ce55311..d65baba 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+dnl Checks for --enable-coverage and updates CFLAGS and LDFLAGS appropriately.
+AC_DEFUN([OVS_CHECK_COVERAGE],
+  [AC_REQUIRE([AC_PROG_CC])
+   AC_ARG_ENABLE(
+     [coverage],
+     [AC_HELP_STRING([--enable-coverage], 
+                     [Enable gcov coverage tool.])],
+     [case "${enableval}" in
+        (lcov) coverage=true lcov=true ;;
+        (yes) coverage=true lcov=false ;;
+        (no)  coverage=false lcov=false ;;
+        (*) AC_MSG_ERROR([bad value ${enableval} for --enable-coverage]) ;;
+      esac],
+     [coverage=false lcov=false])
+   if $coverage; then
+     CFLAGS="$CFLAGS -O0 --coverage"
+     LDFLAGS="$LDFLAGS --coverage"
+   fi
+   if $lcov; then
+     if lcov --version >/dev/null 2>&1; then :; else
+       AC_MSG_ERROR([--enable-coverage=lcov was specified but lcov is not in \$PATH])
+     fi
+   fi
+   AC_SUBST([LCOV], [$lcov])])
+
 dnl Checks for --enable-ndebug and defines NDEBUG if it is specified.
 AC_DEFUN([OVS_CHECK_NDEBUG],
   [AC_ARG_ENABLE(
@@ -203,7 +228,7 @@ dnl Checks for libpcre.
 AC_DEFUN([OVS_CHECK_PCRE],
   [dnl Make sure that pkg-config is installed.
    m4_pattern_forbid([PKG_CHECK_MODULES])
-   PKG_CHECK_MODULES([PCRE], [libpcre], [HAVE_PCRE=yes], [HAVE_PCRE=no])
+   PKG_CHECK_MODULES([PCRE], [libpcre >= 7.2], [HAVE_PCRE=yes], [HAVE_PCRE=no])
    AM_CONDITIONAL([HAVE_PCRE], [test "$HAVE_PCRE" = yes])
    if test "$HAVE_PCRE" = yes; then
       AC_DEFINE([HAVE_PCRE], [1], [Define to 1 if libpcre is installed.])
similarity index 53%
rename from secchan/.gitignore
rename to ofproto/.gitignore
index ada6566..b336cc7 100644 (file)
@@ -1,4 +1,2 @@
 /Makefile
 /Makefile.in
-/secchan
-/secchan.8
diff --git a/ofproto/automake.mk b/ofproto/automake.mk
new file mode 100644 (file)
index 0000000..232d45f
--- /dev/null
@@ -0,0 +1,29 @@
+# Copyright (C) 2009 Nicira Networks, Inc.
+#
+# Copying and distribution of this file, with or without modification,
+# are permitted in any medium without royalty provided the copyright
+# notice and this notice are preserved.  This file is offered as-is,
+# without warranty of any kind.
+
+noinst_LIBRARIES += ofproto/libofproto.a
+ofproto_libofproto_a_SOURCES = \
+       ofproto/discovery.c \
+       ofproto/discovery.h \
+       ofproto/executer.c \
+       ofproto/executer.h \
+       ofproto/fail-open.c \
+       ofproto/fail-open.h \
+       ofproto/in-band.c \
+       ofproto/in-band.h \
+       ofproto/netflow.c \
+       ofproto/netflow.h \
+       ofproto/ofproto.c \
+       ofproto/ofproto.h \
+       ofproto/pktbuf.c \
+       ofproto/pktbuf.h \
+       ofproto/pinsched.c \
+       ofproto/pinsched.h \
+       ofproto/status.c \
+       ofproto/status.h
+
+include ofproto/commands/automake.mk
similarity index 71%
rename from secchan/commands/automake.mk
rename to ofproto/commands/automake.mk
index cbe44d8..96d165f 100644 (file)
@@ -1,3 +1,3 @@
 commandsdir = ${pkgdatadir}/commands
 dist_commands_SCRIPTS = \
-       secchan/commands/reboot
+       ofproto/commands/reboot
similarity index 98%
rename from secchan/discovery.c
rename to ofproto/discovery.c
index da52482..2868db5 100644 (file)
@@ -112,7 +112,8 @@ discovery_create(const char *re, bool update_resolv_conf,
     d->update_resolv_conf = update_resolv_conf;
 
     /* Initialize DHCP client. */
-    error = dpif_get_name(dpif, local_name, sizeof local_name);
+    error = dpif_port_get_name(dpif, ODPP_LOCAL,
+                               local_name, sizeof local_name);
     if (error) {
         VLOG_ERR("failed to query datapath local port: %s", strerror(error));
         goto error_regfree;
@@ -168,7 +169,7 @@ discovery_set_accept_controller_re(struct discovery *d, const char *re_)
     int error;
     char *re;
 
-    re = (!re_ ? xstrdup(vconn_ssl_is_configured() ? "^ssl:.*" : ".*")
+    re = (!re_ ? xstrdup(vconn_ssl_is_configured() ? "^ssl:.*" : "^tcp:.*")
           : re_[0] == '^' ? xstrdup(re_) : xasprintf("^%s", re_));
     regex = xmalloc(sizeof *regex);
     error = regcomp(regex, re, REG_NOSUB | REG_EXTENDED);
similarity index 100%
rename from secchan/discovery.h
rename to ofproto/discovery.h
similarity index 99%
rename from secchan/executer.c
rename to ofproto/executer.c
index 6b8c8e5..bc42ccf 100644 (file)
@@ -80,7 +80,7 @@ static void send_child_message(struct rconn *, uint32_t xid, uint32_t status,
 
 /* Returns true if 'cmd' is allowed by 'acl', which is a command-separated
  * access control list in the format described for --command-acl in
- * secchan(8). */
+ * ovs-openflowd(8). */
 static bool
 executer_is_permitted(const char *acl_, const char *cmd)
 {
similarity index 100%
rename from secchan/executer.h
rename to ofproto/executer.h
similarity index 100%
rename from secchan/fail-open.c
rename to ofproto/fail-open.c
similarity index 100%
rename from secchan/fail-open.h
rename to ofproto/fail-open.h
similarity index 93%
rename from secchan/in-band.c
rename to ofproto/in-band.c
index 9b699ca..a08af07 100644 (file)
@@ -74,7 +74,7 @@ struct in_band {
     uint32_t last_ip;           /* Last known IP, 0 if never known. */
     uint8_t mac[ETH_ADDR_LEN];  /* Current MAC, 0 if unknown. */
     uint8_t last_mac[ETH_ADDR_LEN]; /* Last known MAC, 0 if never known */
-    char *dev_name;
+    struct netdev *netdev;
     time_t next_refresh;        /* Next time to refresh MAC address. */
 
     /* Keeping track of the local port's MAC address. */
@@ -102,14 +102,21 @@ get_controller_mac(struct in_band *ib)
         /* Look up MAC address. */
         memset(ib->mac, 0, sizeof ib->mac);
         if (ib->ip) {
-            uint32_t local_ip = rconn_get_local_ip(ib->controller);
+            struct in_addr local_in4 = { rconn_get_local_ip(ib->controller) };
             struct in_addr in4;
             int retval;
 
-            in4.s_addr = local_ip;
-            if (netdev_find_dev_by_in4(&in4, &ib->dev_name)) {
-                retval = netdev_nodev_arp_lookup(ib->dev_name, ib->ip,
-                        ib->mac);
+            /* Refresh device with IP address 'in4'. */
+            if (!ib->netdev
+                || netdev_get_in4(ib->netdev, &in4)
+                || in4.s_addr != local_in4.s_addr)
+            {
+                netdev_close(ib->netdev);
+                ib->netdev = netdev_find_dev_by_in4(&local_in4);
+            }
+
+            if (ib->netdev) {
+                retval = netdev_arp_lookup(ib->netdev, ib->ip, ib->mac);
                 if (retval) {
                     VLOG_DBG_RL(&rl, "cannot look up controller MAC address "
                                 "("IP_FMT"): %s",
@@ -117,7 +124,7 @@ get_controller_mac(struct in_band *ib)
                 }
             } else {
                 VLOG_DBG_RL(&rl, "cannot find device with IP address "IP_FMT,
-                    IP_ARGS(&local_ip));
+                    IP_ARGS(&local_in4.s_addr));
             }
         }
         have_mac = !eth_addr_is_zero(ib->mac);
@@ -151,7 +158,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 (ib->dev_name && (!netdev_nodev_get_etheraddr(ib->dev_name, ea))) {
+        if (ib->netdev && !netdev_get_etheraddr(ib->netdev, ea)) {
             memcpy(ib->local_mac, ea, ETH_ADDR_LEN);
         }
         ib->next_local_refresh = now + 1;
@@ -333,7 +340,7 @@ in_band_create(struct ofproto *ofproto, struct switch_status *ss,
                                              in_band_status_cb, in_band);
     in_band->next_refresh = TIME_MIN;
     in_band->next_local_refresh = TIME_MIN;
-    in_band->dev_name = NULL;
+    in_band->netdev = NULL;
 
     *in_bandp = in_band;
 }
@@ -343,6 +350,7 @@ in_band_destroy(struct in_band *in_band)
 {
     if (in_band) {
         switch_status_unregister(in_band->ss_cat);
+        netdev_close(in_band->netdev);
         /* We don't own the rconn. */
     }
 }
similarity index 98%
rename from secchan/in-band.h
rename to ofproto/in-band.h
index 1e13a6a..624bee9 100644 (file)
@@ -23,7 +23,6 @@ struct dpif;
 struct in_band;
 struct ofproto;
 struct rconn;
-struct secchan;
 struct settings;
 struct switch_status;
 
similarity index 100%
rename from secchan/netflow.c
rename to ofproto/netflow.c
similarity index 100%
rename from secchan/netflow.h
rename to ofproto/netflow.h
similarity index 97%
rename from secchan/ofproto.c
rename to ofproto/ofproto.c
index efa5c9b..c26cdc2 100644 (file)
@@ -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 @@ 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 @@ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
 
 static const struct ofhooks default_ofhooks;
 
-static uint64_t pick_datapath_id(struct dpif *, uint64_t fallback_dpid);
+static uint64_t pick_datapath_id(const struct ofproto *);
 static uint64_t pick_fallback_dpid(void);
 static void send_packet_in_miss(struct ofpbuf *, void *ofproto);
 static void send_packet_in_action(struct ofpbuf *, void *ofproto);
@@ -261,10 +261,9 @@ int
 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;
@@ -275,37 +274,27 @@ ofproto_create(const char *datapath, const struct ofhooks *ofhooks, void *aux,
         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);
@@ -313,7 +302,7 @@ ofproto_create(const char *datapath, const struct ofhooks *ofhooks, void *aux,
 
     /* 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;
@@ -365,6 +354,10 @@ ofproto_create(const char *datapath, const struct ofhooks *ofhooks, void *aux,
         return error;
     }
 
+    /* Pick final datapath ID. */
+    p->datapath_id = pick_datapath_id(p);
+    VLOG_INFO("using datapath ID %012"PRIx64, p->datapath_id);
+
     *ofprotop = p;
     return 0;
 }
@@ -373,9 +366,7 @@ void
 ofproto_set_datapath_id(struct ofproto *p, uint64_t datapath_id)
 {
     uint64_t old_dpid = p->datapath_id;
-    p->datapath_id = (datapath_id
-                      ? datapath_id
-                      : pick_datapath_id(&p->dpif, p->fallback_dpid));
+    p->datapath_id = datapath_id ? datapath_id : pick_datapath_id(p);
     if (p->datapath_id != old_dpid) {
         VLOG_INFO("datapath ID changed to %012"PRIx64, p->datapath_id);
         rconn_reconnect(p->controller->rconn);
@@ -457,7 +448,7 @@ ofproto_set_discovery(struct ofproto *p, bool discovery,
                 return error;
             }
             error = discovery_create(re, update_resolv_conf,
-                                     &p->dpif, p->switch_status,
+                                     p->dpif, p->switch_status,
                                      &p->discovery);
             if (error) {
                 return error;
@@ -714,8 +705,8 @@ ofproto_destroy(struct ofproto *p)
         ofconn_destroy(ofconn, p);
     }
 
-    dpif_close(&p->dpif);
-    dpifmon_destroy(p->dpifmon);
+    dpif_close(p->dpif);
+    netdev_monitor_destroy(p->netdev_monitor);
     PORT_ARRAY_FOR_EACH (ofport, &p->ports, port_no) {
         ofport_free(ofport);
     }
@@ -757,6 +748,17 @@ ofproto_run(struct ofproto *p)
     return error;
 }
 
+static void
+process_port_change(struct ofproto *ofproto, int error, char *devname)
+{
+    if (error == ENOBUFS) {
+        reinit_ports(ofproto);
+    } else if (!error) {
+        update_port(ofproto, devname);
+        free(devname);
+    }
+}
+
 int
 ofproto_run1(struct ofproto *p)
 {
@@ -769,15 +771,15 @@ 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;
@@ -786,13 +788,12 @@ ofproto_run1(struct ofproto *p)
         handle_odp_msg(p, buf);
     }
 
-    while ((error = dpifmon_poll(p->dpifmon, &devname)) != EAGAIN) {
-        if (error == ENOBUFS) {
-            reinit_ports(p);
-        } else if (!error) {
-            update_port(p, devname);
-            free(devname);
-        }
+    while ((error = dpif_port_poll(p->dpif, &devname)) != EAGAIN) {
+        process_port_change(p, error, devname);
+    }
+    while ((error = netdev_monitor_poll(p->netdev_monitor,
+                                        &devname)) != EAGAIN) {
+        process_port_change(p, error, devname);
     }
 
     if (p->in_band) {
@@ -904,8 +905,9 @@ ofproto_wait(struct ofproto *p)
     struct ofconn *ofconn;
     size_t i;
 
-    dpif_recv_wait(&p->dpif);
-    dpifmon_wait(p->dpifmon);
+    dpif_recv_wait(p->dpif);
+    dpif_port_poll_wait(p->dpif);
+    netdev_monitor_poll_wait(p->netdev_monitor);
     LIST_FOR_EACH (ofconn, struct ofconn, node, &p->all_conns) {
         ofconn_wait(ofconn);
     }
@@ -975,7 +977,7 @@ ofproto_send_packet(struct ofproto *p, const flow_t *flow,
 
     /* XXX Should we translate the dpif_execute() errno value into an OpenFlow
      * error code? */
-    dpif_execute(&p->dpif, flow->in_port, odp_actions.actions,
+    dpif_execute(p->dpif, flow->in_port, odp_actions.actions,
                  odp_actions.n_actions, packet);
     return 0;
 }
@@ -1027,7 +1029,7 @@ ofproto_flush_flows(struct ofproto *ofproto)
 {
     COVERAGE_INC(ofproto_flush);
     classifier_for_each(&ofproto->cls, CLS_INC_ALL, destroy_rule, ofproto);
-    dpif_flow_flush(&ofproto->dpif);
+    dpif_flow_flush(ofproto->dpif);
     if (ofproto->in_band) {
         in_band_flushed(ofproto->in_band);
     }
@@ -1050,7 +1052,7 @@ reinit_ports(struct ofproto *p)
     PORT_ARRAY_FOR_EACH (ofport, &p->ports, port_no) {
         svec_add (&devnames, (char *) ofport->opp.name);
     }
-    dpif_port_list(&p->dpif, &odp_ports, &n_odp_ports);
+    dpif_port_list(p->dpif, &odp_ports, &n_odp_ports);
     for (i = 0; i < n_odp_ports; i++) {
         svec_add (&devnames, odp_ports[i].devname);
     }
@@ -1080,7 +1082,7 @@ refresh_port_group(struct ofproto *p, unsigned int group)
             ports[n_ports++] = port_no;
         }
     }
-    dpif_port_group_set(&p->dpif, group, ports, n_ports);
+    dpif_port_group_set(p->dpif, group, ports, n_ports);
     free(ports);
 }
 
@@ -1112,7 +1114,7 @@ make_ofport(const struct odp_port *odp_port)
     ofport = xmalloc(sizeof *ofport);
     ofport->netdev = netdev;
     ofport->opp.port_no = odp_port_to_ofp_port(odp_port->port);
-    memcpy(ofport->opp.hw_addr, netdev_get_etheraddr(netdev), ETH_ALEN);
+    netdev_get_etheraddr(netdev, ofport->opp.hw_addr);
     memcpy(ofport->opp.name, odp_port->devname,
            MIN(sizeof ofport->opp.name, sizeof odp_port->devname));
     ofport->opp.name[sizeof ofport->opp.name - 1] = '\0';
@@ -1187,6 +1189,7 @@ send_port_status(struct ofproto *p, const struct ofport *ofport,
 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);
@@ -1195,6 +1198,7 @@ ofport_install(struct ofproto *p, struct ofport *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));
@@ -1220,7 +1224,7 @@ update_port(struct ofproto *p, const char *devname)
     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);
@@ -1289,7 +1293,7 @@ init_ports(struct ofproto *p)
     size_t i;
     int error;
 
-    error = dpif_port_list(&p->dpif, &ports, &n_ports);
+    error = dpif_port_list(p->dpif, &ports, &n_ports);
     if (error) {
         return error;
     }
@@ -1491,7 +1495,7 @@ rule_execute(struct ofproto *ofproto, struct rule *rule,
     }
 
     /* Execute the ODP actions. */
-    if (!dpif_execute(&ofproto->dpif, flow->in_port,
+    if (!dpif_execute(ofproto->dpif, flow->in_port,
                       actions, n_actions, packet)) {
         struct odp_flow_stats stats;
         flow_extract_stats(flow, packet, &stats);
@@ -1600,7 +1604,7 @@ do_put_flow(struct ofproto *ofproto, struct rule *rule, int flags,
     put->flow.actions = rule->odp_actions;
     put->flow.n_actions = rule->n_odp_actions;
     put->flags = flags;
-    return dpif_flow_put(&ofproto->dpif, put);
+    return dpif_flow_put(ofproto->dpif, put);
 }
 
 static void
@@ -1681,7 +1685,7 @@ rule_uninstall(struct ofproto *p, struct rule *rule)
         odp_flow.key = rule->cr.flow;
         odp_flow.actions = NULL;
         odp_flow.n_actions = 0;
-        if (!dpif_flow_del(&p->dpif, &odp_flow)) {
+        if (!dpif_flow_del(p->dpif, &odp_flow)) {
             update_stats(rule, &odp_flow.stats);
         }
         rule->installed = false;
@@ -1829,7 +1833,7 @@ handle_get_config_request(struct ofproto *p, struct ofconn *ofconn,
     bool drop_frags;
 
     /* Figure out flags. */
-    dpif_get_drop_frags(&p->dpif, &drop_frags);
+    dpif_get_drop_frags(p->dpif, &drop_frags);
     flags = drop_frags ? OFPC_FRAG_DROP : OFPC_FRAG_NORMAL;
     if (ofconn->send_flow_exp) {
         flags |= OFPC_SEND_FLOW_EXP;
@@ -1862,10 +1866,10 @@ handle_set_config(struct ofproto *p, struct ofconn *ofconn,
     if (ofconn == p->controller) {
         switch (flags & OFPC_FRAG_MASK) {
         case OFPC_FRAG_NORMAL:
-            dpif_set_drop_frags(&p->dpif, false);
+            dpif_set_drop_frags(p->dpif, false);
             break;
         case OFPC_FRAG_DROP:
-            dpif_set_drop_frags(&p->dpif, true);
+            dpif_set_drop_frags(p->dpif, true);
             break;
         default:
             VLOG_WARN_RL(&rl, "requested bad fragment mode (flags=%"PRIx16")",
@@ -2170,7 +2174,7 @@ handle_packet_out(struct ofproto *p, struct ofconn *ofconn,
         return error;
     }
 
-    dpif_execute(&p->dpif, flow.in_port, actions.actions, actions.n_actions,
+    dpif_execute(p->dpif, flow.in_port, actions.actions, actions.n_actions,
                  &payload);
     ofpbuf_delete(buffer);
 
@@ -2313,7 +2317,7 @@ handle_table_stats_request(struct ofproto *p, struct ofconn *ofconn,
     n_wild = classifier_count(&p->cls) - classifier_count_exact(&p->cls);
 
     /* Hash table. */
-    dpif_get_dp_stats(&p->dpif, &dpstats);
+    dpif_get_dp_stats(p->dpif, &dpstats);
     ots = append_stats_reply(sizeof *ots, ofconn, &msg);
     memset(ots, 0, sizeof *ots);
     ots->table_id = TABLEID_HASH;
@@ -2408,7 +2412,7 @@ query_stats(struct ofproto *p, struct rule *rule,
 
     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];
@@ -3219,7 +3223,7 @@ update_used(struct ofproto *p)
     size_t i;
     int error;
 
-    error = dpif_flow_list_all(&p->dpif, &flows, &n_flows);
+    error = dpif_flow_list_all(p->dpif, &flows, &n_flows);
     if (error) {
         return;
     }
@@ -3232,7 +3236,7 @@ update_used(struct ofproto *p)
             classifier_find_rule_exactly(&p->cls, &f->key, 0, UINT16_MAX));
         if (!rule || !rule->installed) {
             COVERAGE_INC(ofproto_unexpected_rule);
-            dpif_flow_del(&p->dpif, f);
+            dpif_flow_del(p->dpif, f);
             continue;
         }
 
@@ -3307,23 +3311,23 @@ send_packet_in_miss(struct ofpbuf *packet, void *p_)
 }
 
 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
similarity index 100%
rename from secchan/ofproto.h
rename to ofproto/ofproto.h
similarity index 100%
rename from secchan/pinsched.c
rename to ofproto/pinsched.c
similarity index 100%
rename from secchan/pinsched.h
rename to ofproto/pinsched.h
similarity index 100%
rename from secchan/pktbuf.c
rename to ofproto/pktbuf.c
similarity index 100%
rename from secchan/pktbuf.h
rename to ofproto/pktbuf.h
similarity index 100%
rename from secchan/status.c
rename to ofproto/status.c
similarity index 98%
rename from secchan/status.h
rename to ofproto/status.h
index 7856674..1186fa5 100644 (file)
@@ -21,7 +21,6 @@
 
 struct nicira_header;
 struct rconn;
-struct secchan;
 struct ofproto;
 struct status_reply;
 
diff --git a/secchan/automake.mk b/secchan/automake.mk
deleted file mode 100644 (file)
index d6bf1b0..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-# Copyright (C) 2009 Nicira Networks, Inc.
-#
-# Copying and distribution of this file, with or without modification,
-# are permitted in any medium without royalty provided the copyright
-# notice and this notice are preserved.  This file is offered as-is,
-# without warranty of any kind.
-
-bin_PROGRAMS += secchan/secchan
-man_MANS += secchan/secchan.8
-
-secchan_secchan_SOURCES = secchan/main.c
-secchan_secchan_LDADD = \
-       secchan/libsecchan.a \
-       lib/libopenvswitch.a \
-       $(FAULT_LIBS) \
-       $(SSL_LIBS)
-
-noinst_LIBRARIES += secchan/libsecchan.a
-secchan_libsecchan_a_SOURCES = \
-       secchan/discovery.c \
-       secchan/discovery.h \
-       secchan/executer.c \
-       secchan/executer.h \
-       secchan/fail-open.c \
-       secchan/fail-open.h \
-       secchan/in-band.c \
-       secchan/in-band.h \
-       secchan/netflow.c \
-       secchan/netflow.h \
-       secchan/ofproto.c \
-       secchan/ofproto.h \
-       secchan/pktbuf.c \
-       secchan/pktbuf.h \
-       secchan/pinsched.c \
-       secchan/pinsched.h \
-       secchan/status.c \
-       secchan/status.h
-
-EXTRA_DIST += secchan/secchan.8.in
-DISTCLEANFILES += secchan/secchan.8
-
-include secchan/commands/automake.mk
index 1112503..706aa14 100644 (file)
@@ -8,3 +8,4 @@
 /test-list
 /test-stp
 /test-type-props
+/testsuite
diff --git a/tests/atlocal.in b/tests/atlocal.in
new file mode 100644 (file)
index 0000000..d0ca704
--- /dev/null
@@ -0,0 +1,3 @@
+# -*- shell-script -*-
+PERL='@PERL@'
+LCOV='@LCOV@'
index 3a417c7..07e56ff 100644 (file)
@@ -1,40 +1,70 @@
-TESTS += tests/test-classifier
+EXTRA_DIST += \
+       $(TESTSUITE_AT) \
+       $(TESTSUITE) \
+       tests/atlocal.in \
+       $(srcdir)/package.m4 \
+       $(srcdir)/tests/testsuite
+TESTSUITE_AT = \
+       tests/testsuite.at \
+       tests/lcov-pre.at \
+       tests/library.at \
+       tests/stp.at \
+       tests/lcov-post.at
+TESTSUITE = $(srcdir)/tests/testsuite
+DISTCLEANFILES += tests/atconfig tests/atlocal $(TESTSUITE)
+
+check-local: tests/atconfig tests/atlocal $(TESTSUITE)
+       $(SHELL) '$(TESTSUITE)' -C tests AUTOTEST_PATH='utilities:vswitchd:tests' $(TESTSUITEFLAGS)
+
+clean-local:
+       test ! -f '$(TESTSUITE)' || $(SHELL) '$(TESTSUITE)' -C tests --clean
+
+AUTOM4TE = autom4te
+AUTOTEST = $(AUTOM4TE) --language=autotest
+$(TESTSUITE): package.m4 $(TESTSUITE_AT)
+       $(AUTOTEST) -I '$(srcdir)' -o $@.tmp $@.at
+       mv $@.tmp $@
+
+# The `:;' works around a Bash 3.2 bug when the output is not writeable.
+$(srcdir)/package.m4: $(top_srcdir)/configure.ac
+       :;{ \
+         echo '# Signature of the current package.' && \
+         echo 'm4_define([AT_PACKAGE_NAME],      [@PACKAGE_NAME@])' && \
+         echo 'm4_define([AT_PACKAGE_TARNAME],   [@PACKAGE_TARNAME@])' && \
+         echo 'm4_define([AT_PACKAGE_VERSION],   [@PACKAGE_VERSION@])' && \
+         echo 'm4_define([AT_PACKAGE_STRING],    [@PACKAGE_STRING@])' && \
+         echo 'm4_define([AT_PACKAGE_BUGREPORT], [@PACKAGE_BUGREPORT@])'; \
+       } >'$(srcdir)/package.m4'
+
 noinst_PROGRAMS += tests/test-classifier
 tests_test_classifier_SOURCES = tests/test-classifier.c
 tests_test_classifier_LDADD = lib/libopenvswitch.a
 
-TESTS += tests/test-csum
 noinst_PROGRAMS += tests/test-csum
 tests_test_csum_SOURCES = tests/test-csum.c
 tests_test_csum_LDADD = lib/libopenvswitch.a
 
-TESTS += tests/test-flows.sh
 noinst_PROGRAMS += tests/test-flows
 tests_test_flows_SOURCES = tests/test-flows.c
 tests_test_flows_LDADD = lib/libopenvswitch.a
-dist_check_SCRIPTS = tests/test-flows.sh tests/flowgen.pl
+dist_check_SCRIPTS = tests/flowgen.pl
 
-TESTS += tests/test-hash
 noinst_PROGRAMS += tests/test-hash
 tests_test_hash_SOURCES = tests/test-hash.c
 tests_test_hash_LDADD = lib/libopenvswitch.a
 
-TESTS += tests/test-hmap
 noinst_PROGRAMS += tests/test-hmap
 tests_test_hmap_SOURCES = tests/test-hmap.c
 tests_test_hmap_LDADD = lib/libopenvswitch.a
 
-TESTS += tests/test-list
 noinst_PROGRAMS += tests/test-list
 tests_test_list_SOURCES = tests/test-list.c
 tests_test_list_LDADD = lib/libopenvswitch.a
 
-TESTS += tests/test-sha1
 noinst_PROGRAMS += tests/test-sha1
 tests_test_sha1_SOURCES = tests/test-sha1.c
 tests_test_sha1_LDADD = lib/libopenvswitch.a
 
-TESTS += tests/test-type-props
 noinst_PROGRAMS += tests/test-type-props
 tests_test_type_props_SOURCES = tests/test-type-props.c
 
@@ -42,25 +72,11 @@ noinst_PROGRAMS += tests/test-dhcp-client
 tests_test_dhcp_client_SOURCES = tests/test-dhcp-client.c
 tests_test_dhcp_client_LDADD = lib/libopenvswitch.a $(FAULT_LIBS)
 
-TESTS += tests/test-stp.sh
-EXTRA_DIST += tests/test-stp.sh
 noinst_PROGRAMS += tests/test-stp
-
 tests_test_stp_SOURCES = tests/test-stp.c
 tests_test_stp_LDADD = lib/libopenvswitch.a
-stp_files = \
-       tests/test-stp-ieee802.1d-1998 \
-       tests/test-stp-ieee802.1d-2004-fig17.4 \
-       tests/test-stp-ieee802.1d-2004-fig17.6 \
-       tests/test-stp-ieee802.1d-2004-fig17.7 \
-       tests/test-stp-iol-op-1.1 \
-       tests/test-stp-iol-op-1.4 \
-       tests/test-stp-iol-op-3.1 \
-       tests/test-stp-iol-op-3.3 \
-       tests/test-stp-iol-io-1.1 \
-       tests/test-stp-iol-io-1.2 \
-       tests/test-stp-iol-io-1.4 \
-       tests/test-stp-iol-io-1.5
-TESTS_ENVIRONMENT += stp_files='$(stp_files)'
-
-EXTRA_DIST += $(stp_files)
+
+noinst_PROGRAMS += tests/test-vconn
+tests_test_vconn_SOURCES = tests/test-vconn.c
+tests_test_vconn_LDADD = lib/libopenvswitch.a $(SSL_LIBS)
+
diff --git a/tests/lcov-post.at b/tests/lcov-post.at
new file mode 100644 (file)
index 0000000..957cdfa
--- /dev/null
@@ -0,0 +1,6 @@
+AT_BANNER([code coverage])
+
+AT_SETUP([generate coverage.html with lcov])
+AT_CHECK([$LCOV || exit 77])
+AT_CHECK([cd $abs_builddir && genhtml -o coverage.html coverage.info], [0], [ignore], [ignore])
+AT_CLEANUP
diff --git a/tests/lcov-pre.at b/tests/lcov-pre.at
new file mode 100644 (file)
index 0000000..1ecfeb1
--- /dev/null
@@ -0,0 +1,21 @@
+AT_BANNER([code coverage])
+
+m4_define([_OVS_RUN_LCOV], [test $LCOV = false || lcov -b $abs_top_builddir -d $abs_top_builddir $1])
+
+AT_SETUP([initialize lcov])
+AT_CHECK([rm -fr $abs_builddir/coverage.html])
+AT_CHECK([rm -f $abs_builddir/coverage.info])
+AT_CHECK([$LCOV || exit 77])
+AT_CHECK([_OVS_RUN_LCOV([-c -i -o - > $abs_builddir/coverage.info])], [0], [ignore], [ignore])
+AT_CLEANUP
+
+# OVS_CHECK_LCOV(COMMAND, [STATUS = `0'], [STDOUT = `'], [STDERR = `'], 
+#                [RUN-IF-FAIL], [RUN-IF-PASS])
+#
+# This macro is equivalent to AT_CHECK, except that COMMAND should be a single
+# shell command that invokes a program whose code coverage is to be measured
+# (if configure was invoked with --coverage).  
+m4_define([OVS_CHECK_LCOV],
+    [AT_CHECK([_OVS_RUN_LCOV([-z])], [0], [ignore], [ignore])
+     AT_CHECK($@)
+     AT_CHECK([_OVS_RUN_LCOV([-c -o - >> $abs_builddir/coverage.info])], [0], [ignore], [ignore])])
diff --git a/tests/library.at b/tests/library.at
new file mode 100644 (file)
index 0000000..c48828e
--- /dev/null
@@ -0,0 +1,39 @@
+AT_BANNER([library unit tests])
+
+AT_SETUP([test flow extractor])
+AT_CHECK([$PERL `which flowgen.pl` >/dev/null 3>flows 4>pcap])
+OVS_CHECK_LCOV([test-flows <flows 3<pcap], [0], [checked 247 packets, 0 errors
+])
+AT_CLEANUP
+
+AT_SETUP([test TCP/IP checksumming])
+OVS_CHECK_LCOV([test-csum], [0], [ignore])
+AT_CLEANUP
+
+AT_SETUP([test flow classifier])
+OVS_CHECK_LCOV([test-classifier], [0], [ignore])
+AT_CLEANUP
+
+AT_SETUP([test hash functions])
+OVS_CHECK_LCOV([test-hash], [0], [ignore])
+AT_CLEANUP
+
+AT_SETUP([test hash map])
+OVS_CHECK_LCOV([test-hmap], [0], [ignore])
+AT_CLEANUP
+
+AT_SETUP([test linked lists])
+OVS_CHECK_LCOV([test-list], [0], [ignore])
+AT_CLEANUP
+
+AT_SETUP([test SHA-1])
+OVS_CHECK_LCOV([test-sha1], [0], [ignore])
+AT_CLEANUP
+
+AT_SETUP([test type properties])
+OVS_CHECK_LCOV([test-type-props], [0], [ignore])
+AT_CLEANUP
+
+AT_SETUP([test vconn library])
+OVS_CHECK_LCOV([test-vconn], [0], [ignore])
+AT_CLEANUP
diff --git a/tests/stp.at b/tests/stp.at
new file mode 100644 (file)
index 0000000..6e2bcf9
--- /dev/null
@@ -0,0 +1,303 @@
+AT_BANNER([Spanning Tree Protocol unit tests])
+
+AT_SETUP([STP example from IEEE 802.1D-1998])
+AT_KEYWORDS([STP])
+AT_DATA([test-stp-ieee802.1d-1998],
+[bridge 0 0x42 = a b
+bridge 1 0x97 = c:5 a d:5
+bridge 2 0x45 = b e
+bridge 3 0x57 = b:5 e:5
+bridge 4 0x83 = a:5 e:5
+run 1000
+check 0 = root
+check 1 = F F:10 F
+check 2 = F:10 B
+check 3 = F:5 F
+check 4 = F:5 B
+])
+OVS_CHECK_LCOV([test-stp test-stp-ieee802.1d-1998])
+AT_CLEANUP
+
+AT_SETUP([STP example from IEEE 802.1D-2004 figures 17.4 and 17.5])
+AT_KEYWORDS([STP])
+AT_DATA([test-stp-ieee802.1d-2004-fig17.4],
+[bridge 0 0x111 = a b e c
+bridge 1 0x222 = a b d f
+bridge 2 0x333 = c d l j h g
+bridge 3 0x444 = e f n m k i
+bridge 4 0x555 = g i 0 0
+bridge 5 0x666 = h k 0 0
+bridge 6 0x777 = j m 0 0
+bridge 7 0x888 = l n 0 0
+run 1000
+check 0 = root
+check 1 = F:10 B F F
+check 2 = F:10 B F F F F
+check 3 = F:10 B F F F F
+check 4 = F:20 B F F
+check 5 = F:20 B F F
+check 6 = F:20 B F F
+check 7 = F:20 B F F
+
+# Now connect two ports of bridge 7 to the same LAN.
+bridge 7 = l n o o
+# Same results except for bridge 7:
+run 1000
+check 0 = root
+check 1 = F:10 B F F
+check 2 = F:10 B F F F F
+check 3 = F:10 B F F F F
+check 4 = F:20 B F F
+check 5 = F:20 B F F
+check 6 = F:20 B F F
+check 7 = F:20 B F B
+])
+OVS_CHECK_LCOV([test-stp test-stp-ieee802.1d-2004-fig17.4])
+AT_CLEANUP
+
+AT_SETUP([STP example from IEEE 802.1D-2004 figure 17.6])
+AT_KEYWORDS([STP])
+AT_DATA([test-stp-ieee802.1d-2004-fig17.6],
+[bridge 0 0x111 = a b l
+bridge 1 0x222 = b c d
+bridge 2 0x333 = d e f
+bridge 3 0x444 = f g h
+bridge 4 0x555 = j h i
+bridge 5 0x666 = l j k
+run 1000
+check 0 = root
+check 1 = F:10 F F
+check 2 = F:20 F F
+check 3 = F:30 F B
+check 4 = F:20 F F
+check 5 = F:10 F F
+])
+OVS_CHECK_LCOV([test-stp test-stp-ieee802.1d-2004-fig17.6])
+AT_CLEANUP
+
+AT_SETUP([STP example from IEEE 802.1D-2004 figure 17.7])
+AT_KEYWORDS([STP])
+AT_DATA([test-stp-ieee802.1d-2004-fig17.7],
+[bridge 0 0xaa = b
+bridge 1 0x111 = a b d f h g e c
+bridge 2 0x222 = g h j l n m k i
+run 1000
+check 0 = root
+check 1 = F F:10 F F F F F F
+check 2 = B F:20 F F F F F F
+
+# This is not the port priority change described in that figure,
+# but I don't understand what port priority change would cause
+# that change.
+bridge 2 = g X j l n m k i
+run 1000
+check 0 = root
+check 1 = F F:10 F F F F F F
+check 2 = F:20 D F F F F F F
+])
+OVS_CHECK_LCOV([test-stp test-stp-ieee802.1d-2004-fig17.7])
+AT_CLEANUP
+
+AT_SETUP([STP.io.1.1: Link Failure])
+AT_KEYWORDS([STP])
+AT_DATA([test-stp-iol-io-1.1],
+[# This test file approximates the following test from "Bridge
+# Functions Consortium Spanning Tree Interoperability Test Suite
+# Version 1.5":
+# 
+# STP.io.1.1: Link Failure
+bridge 0 0x111 = a b c
+bridge 1 0x222 = a b c
+run 1000
+check 0 = root
+check 1 = F:10 B B
+bridge 1 = 0 _ _
+run 1000
+check 0 = root
+check 1 = F F:10 B
+bridge 1 = X _ _
+run 1000
+check 0 = root
+check 1 = D F:10 B
+bridge 1 = _ 0 _
+run 1000
+check 0 = root
+check 1 = D F F:10
+bridge 1 = _ X _
+run 1000
+check 0 = root
+check 1 = D D F:10
+])
+OVS_CHECK_LCOV([test-stp test-stp-iol-io-1.1])
+AT_CLEANUP
+
+AT_SETUP([STP.io.1.2: Repeated Network])
+AT_KEYWORDS([STP])
+AT_DATA([test-stp-iol-io-1.2],
+[# This test file approximates the following test from "Bridge
+# Functions Consortium Spanning Tree Interoperability Test Suite
+# Version 1.5":
+# STP.io.1.2: Repeated Network
+bridge 0 0x111 = a a
+bridge 1 0x222 = a a
+run 1000
+check 0 = rootid:0x111 F B
+check 1 = rootid:0x111 F:10 B
+bridge 1 = a^0x90 _
+run 1000
+check 0 = rootid:0x111 F B
+check 1 = rootid:0x111 B F:10
+])
+OVS_CHECK_LCOV([test-stp test-stp-iol-io-1.2])
+AT_CLEANUP
+
+AT_SETUP([STP.io.1.4: Network Initialization])
+AT_KEYWORDS([STP])
+AT_DATA([test-stp-iol-io-1.4],
+[# This test file approximates the following test from "Bridge
+# Functions Consortium Spanning Tree Interoperability Test Suite
+# Version 1.5":
+# STP.io.1.4: Network Initialization
+bridge 0 0x111 = a b c
+bridge 1 0x222 = b d e
+bridge 2 0x333 = a d f
+bridge 3 0x444 = c e f
+run 1000
+check 0 = root
+check 1 = F:10 F F
+check 2 = F:10 B F
+check 3 = F:10 B B
+])
+OVS_CHECK_LCOV([test-stp test-stp-iol-io-1.4])
+AT_CLEANUP
+
+AT_SETUP([STP.io.1.5: Topology Change])
+AT_KEYWORDS([STP])
+AT_DATA([test-stp-iol-io-1.5],
+[# This test file approximates the following test from "Bridge
+# Functions Consortium Spanning Tree Interoperability Test Suite
+# Version 1.5":
+# STP.io.1.5: Topology Change
+bridge 0 0x111 = a b d c
+bridge 1 0x222 = a b f e
+bridge 2 0x333 = c d g h
+bridge 3 0x444 = e f g h
+run 1000
+check 0 = root
+check 1 = F:10 B F F
+check 2 = B F:10 F F
+check 3 = B F:20 B B
+bridge 1^0x7000
+run 1000
+check 0 = F:10 B F F
+check 1 = root
+check 2 = B F:20 B B
+check 3 = B F:10 F F
+bridge 2^0x6000
+run 1000
+check 0 = F F B F:10
+check 1 = F:20 B B B
+check 2 = root
+check 3 = F F F:10 B
+bridge 3^0x5000
+run 1000
+check 0 = B B B F:20
+check 1 = F F B F:10
+check 2 = F F F:10 B
+check 3 = root
+bridge 0^0x4000
+bridge 1^0x4001
+bridge 2^0x4002
+bridge 3^0x4003
+run 1000
+check 0 = root
+check 1 = F:10 B F F
+check 2 = B F:10 F F
+check 3 = B F:20 B B
+])
+OVS_CHECK_LCOV([test-stp test-stp-iol-io-1.5])
+AT_CLEANUP
+
+AT_SETUP([STP.op.1.1 and STP.op.1.2])
+AT_KEYWORDS([STP])
+AT_DATA([test-stp-iol-op-1.1],
+[# This test file approximates the following tests from "Bridge
+# Functions Consortium Spanning Tree Protocol Operations Test Suite
+# Version 2.3":
+# Test STP.op.1.1: Root ID Initialized to Bridge ID
+# Test STP.op.1.2: Root Path Cost Initialized to Zero
+bridge 0 0x123 =
+check 0 = root
+])
+OVS_CHECK_LCOV([test-stp test-stp-iol-op-1.1])
+AT_CLEANUP
+
+AT_SETUP([STP.op.1.4: All Ports Initialized to Designated Ports])
+AT_KEYWORDS([STP])
+AT_DATA([test-stp-iol-op-1.4],
+[# This test file approximates the following test from "Bridge
+# Functions Consortium Spanning Tree Protocol Operations Test Suite
+# Version 2.3":
+# Test STP.op.1.4: All Ports Initialized to Designated Ports
+bridge 0 0x123 = a b c d e f
+check 0 = Li Li Li Li Li Li
+run 1000
+check 0 = F F F F F F
+])
+OVS_CHECK_LCOV([test-stp test-stp-iol-op-1.4])
+AT_CLEANUP
+
+AT_SETUP([STP.op.3.1: Root Bridge Selection: Root ID Values])
+AT_KEYWORDS([STP])
+AT_DATA([test-stp-iol-op-3.1],
+[# This test file approximates the following test from "Bridge
+# Functions Consortium Spanning Tree Protocol Operations Test Suite
+# Version 2.3":
+# Test STP.op.3.1: Root Bridge Selection: Root ID Values
+bridge 0 0x111 = a
+bridge 1 0x222 = a
+check 0 = rootid:0x111 Li
+check 1 = rootid:0x222 Li
+run 1000
+check 0 = rootid:0x111 root
+check 1 = rootid:0x111 F:10
+])
+OVS_CHECK_LCOV([test-stp test-stp-iol-op-3.1])
+AT_CLEANUP
+
+AT_SETUP([STP.op.3.3: Root Bridge Selection: Bridge ID Values])
+AT_KEYWORDS([STP])
+AT_DATA([test-stp-iol-op-3.3],
+[# This test file approximates the following test from "Bridge
+# Functions Consortium Spanning Tree Protocol Operations Test Suite
+# Version 2.3":
+# Test STP.op.3.3: Root Bridge Selection: Bridge ID Values
+bridge 0 0x333^0x6000 = a
+bridge 1 0x222^0x7000 = b
+bridge 2 0x111 = a b
+run 1000
+check 0 = rootid:0x333^0x6000 root
+check 1 = rootid:0x333^0x6000 F:20
+check 2 = rootid:0x333^0x6000 F:10 F
+])
+OVS_CHECK_LCOV([test-stp test-stp-iol-op-3.3])
+AT_CLEANUP
+
+AT_SETUP([STP.op.3.3: Root Bridge Selection: Bridge ID Values])
+AT_KEYWORDS([STP])
+AT_DATA([test-stp-iol-op-3.4],
+[# This test file approximates the following test from "Bridge
+# Functions Consortium Spanning Tree Protocol Operations Test Suite
+# Version 2.3":
+# Test STP.op.3.3: Root Bridge Selection: Bridge ID Values
+bridge 0 0x333^0x6000 = a
+bridge 1 0x222^0x7000 = b
+bridge 2 0x111 = a b
+run 1000
+check 0 = rootid:0x333^0x6000 root
+check 1 = rootid:0x333^0x6000 F:20
+check 2 = rootid:0x333^0x6000 F:10 F
+])
+OVS_CHECK_LCOV([test-stp test-stp-iol-op-3.4])
+AT_CLEANUP
+
index 7ea476c..e4471c7 100644 (file)
@@ -176,8 +176,8 @@ usage(void)
            "\nDHCP options:\n"
            "  --request-ip=IP         request specified IP address (default:\n"
            "                          do not request a specific IP)\n"
-           "  --vendor-class=STRING   use STRING as vendor class (default:\n"
-           "                          none); use OpenFlow to imitate secchan\n"
+           "  --vendor-class=STRING   use STRING as vendor class; use\n"
+           "                          OpenFlow to imitate ovs-openflowd\n"
            "  --no-resolv-conf        do not update /etc/resolv.conf\n",
            program_name, program_name);
     vlog_usage();
diff --git a/tests/test-flows.sh b/tests/test-flows.sh
deleted file mode 100755 (executable)
index 833f28e..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-#! /bin/sh -e
-
-# 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.
-
-srcdir=`cd $srcdir && pwd`
-trap 'rm -f flows$$ pcap$$ out$$' 0 1 2 13 15
-cd tests
-"$srcdir"/tests/flowgen.pl >/dev/null 3>flows$$ 4>pcap$$
-./test-flows <flows$$ 3<pcap$$ >out$$ || true
-diff -u - out$$ <<EOF
-checked 247 packets, 0 errors
-EOF
diff --git a/tests/test-stp-ieee802.1d-1998 b/tests/test-stp-ieee802.1d-1998
deleted file mode 100644 (file)
index f1982a0..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-# This is the STP example from IEEE 802.1D-1998.
-bridge 0 0x42 = a b
-bridge 1 0x97 = c:5 a d:5
-bridge 2 0x45 = b e
-bridge 3 0x57 = b:5 e:5
-bridge 4 0x83 = a:5 e:5
-run 1000
-check 0 = root
-check 1 = F F:10 F
-check 2 = F:10 B
-check 3 = F:5 F
-check 4 = F:5 B
diff --git a/tests/test-stp-ieee802.1d-2004-fig17.4 b/tests/test-stp-ieee802.1d-2004-fig17.4
deleted file mode 100644 (file)
index 1f70863..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-# This is the STP example from IEEE 802.1D-2004 figures 17.4 and 17.5.
-bridge 0 0x111 = a b e c
-bridge 1 0x222 = a b d f
-bridge 2 0x333 = c d l j h g
-bridge 3 0x444 = e f n m k i
-bridge 4 0x555 = g i 0 0
-bridge 5 0x666 = h k 0 0
-bridge 6 0x777 = j m 0 0
-bridge 7 0x888 = l n 0 0
-run 1000
-check 0 = root
-check 1 = F:10 B F F
-check 2 = F:10 B F F F F
-check 3 = F:10 B F F F F
-check 4 = F:20 B F F
-check 5 = F:20 B F F
-check 6 = F:20 B F F
-check 7 = F:20 B F F
-
-# Now connect two ports of bridge 7 to the same LAN.
-bridge 7 = l n o o
-# Same results except for bridge 7:
-run 1000
-check 0 = root
-check 1 = F:10 B F F
-check 2 = F:10 B F F F F
-check 3 = F:10 B F F F F
-check 4 = F:20 B F F
-check 5 = F:20 B F F
-check 6 = F:20 B F F
-check 7 = F:20 B F B
diff --git a/tests/test-stp-ieee802.1d-2004-fig17.6 b/tests/test-stp-ieee802.1d-2004-fig17.6
deleted file mode 100644 (file)
index 6ed5917..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-# This is the STP example from IEEE 802.1D-2004 figure 17.6.
-bridge 0 0x111 = a b l
-bridge 1 0x222 = b c d
-bridge 2 0x333 = d e f
-bridge 3 0x444 = f g h
-bridge 4 0x555 = j h i
-bridge 5 0x666 = l j k
-run 1000
-check 0 = root
-check 1 = F:10 F F
-check 2 = F:20 F F
-check 3 = F:30 F B
-check 4 = F:20 F F
-check 5 = F:10 F F
diff --git a/tests/test-stp-ieee802.1d-2004-fig17.7 b/tests/test-stp-ieee802.1d-2004-fig17.7
deleted file mode 100644 (file)
index daa0cdf..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-# This is the STP example from IEEE 802.1D-2004 figure 17.7.
-bridge 0 0xaa = b
-bridge 1 0x111 = a b d f h g e c
-bridge 2 0x222 = g h j l n m k i
-run 1000
-check 0 = root
-check 1 = F F:10 F F F F F F
-check 2 = B F:20 F F F F F F
-
-# This is not the port priority change described in that figure,
-# but I don't understand what port priority change would cause
-# that change.
-bridge 2 = g X j l n m k i
-run 1000
-check 0 = root
-check 1 = F F:10 F F F F F F
-check 2 = F:20 D F F F F F F
diff --git a/tests/test-stp-iol-io-1.1 b/tests/test-stp-iol-io-1.1
deleted file mode 100644 (file)
index 186d6c4..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-# This test file approximates the following test from "Bridge
-# Functions Consortium Spanning Tree Interoperability Test Suite
-# Version 1.5":
-# STP.io.1.1: Link Failure
-bridge 0 0x111 = a b c
-bridge 1 0x222 = a b c
-run 1000
-check 0 = root
-check 1 = F:10 B B
-bridge 1 = 0 _ _
-run 1000
-check 0 = root
-check 1 = F F:10 B
-bridge 1 = X _ _
-run 1000
-check 0 = root
-check 1 = D F:10 B
-bridge 1 = _ 0 _
-run 1000
-check 0 = root
-check 1 = D F F:10
-bridge 1 = _ X _
-run 1000
-check 0 = root
-check 1 = D D F:10
diff --git a/tests/test-stp-iol-io-1.2 b/tests/test-stp-iol-io-1.2
deleted file mode 100644 (file)
index 285bbd8..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-# This test file approximates the following test from "Bridge
-# Functions Consortium Spanning Tree Interoperability Test Suite
-# Version 1.5":
-# STP.io.1.2: Repeated Network
-bridge 0 0x111 = a a
-bridge 1 0x222 = a a
-run 1000
-check 0 = rootid:0x111 F B
-check 1 = rootid:0x111 F:10 B
-bridge 1 = a^0x90 _
-run 1000
-check 0 = rootid:0x111 F B
-check 1 = rootid:0x111 B F:10
-
diff --git a/tests/test-stp-iol-io-1.4 b/tests/test-stp-iol-io-1.4
deleted file mode 100644 (file)
index 0065aaf..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-# This test file approximates the following test from "Bridge
-# Functions Consortium Spanning Tree Interoperability Test Suite
-# Version 1.5":
-# STP.io.1.4: Network Initialization
-bridge 0 0x111 = a b c
-bridge 1 0x222 = b d e
-bridge 2 0x333 = a d f
-bridge 3 0x444 = c e f
-run 1000
-check 0 = root
-check 1 = F:10 F F
-check 2 = F:10 B F
-check 3 = F:10 B B
diff --git a/tests/test-stp-iol-io-1.5 b/tests/test-stp-iol-io-1.5
deleted file mode 100644 (file)
index 285d29d..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-# This test file approximates the following test from "Bridge
-# Functions Consortium Spanning Tree Interoperability Test Suite
-# Version 1.5":
-# STP.io.1.5: Topology Change
-bridge 0 0x111 = a b d c
-bridge 1 0x222 = a b f e
-bridge 2 0x333 = c d g h
-bridge 3 0x444 = e f g h
-run 1000
-check 0 = root
-check 1 = F:10 B F F
-check 2 = B F:10 F F
-check 3 = B F:20 B B
-bridge 1^0x7000
-run 1000
-check 0 = F:10 B F F
-check 1 = root
-check 2 = B F:20 B B
-check 3 = B F:10 F F
-bridge 2^0x6000
-run 1000
-check 0 = F F B F:10
-check 1 = F:20 B B B
-check 2 = root
-check 3 = F F F:10 B
-bridge 3^0x5000
-run 1000
-check 0 = B B B F:20
-check 1 = F F B F:10
-check 2 = F F F:10 B
-check 3 = root
-bridge 0^0x4000
-bridge 1^0x4001
-bridge 2^0x4002
-bridge 3^0x4003
-run 1000
-check 0 = root
-check 1 = F:10 B F F
-check 2 = B F:10 F F
-check 3 = B F:20 B B
diff --git a/tests/test-stp-iol-op-1.1 b/tests/test-stp-iol-op-1.1
deleted file mode 100644 (file)
index 8432bf3..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-# This test file approximates the following tests from "Bridge
-# Functions Consortium Spanning Tree Protocol Operations Test Suite
-# Version 2.3":
-# Test STP.op.1.1 Â­ Root ID Initialized to Bridge ID
-# Test STP.op.1.2 Â­ Root Path Cost Initialized to Zero
-bridge 0 0x123 =
-check 0 = root
diff --git a/tests/test-stp-iol-op-1.4 b/tests/test-stp-iol-op-1.4
deleted file mode 100644 (file)
index 6a12116..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-# This test file approximates the following test from "Bridge
-# Functions Consortium Spanning Tree Protocol Operations Test Suite
-# Version 2.3":
-# Test STP.op.1.4 Â­ All Ports Initialized to Designated Ports
-bridge 0 0x123 = a b c d e f
-check 0 = Li Li Li Li Li Li
-run 1000
-check 0 = F F F F F F
diff --git a/tests/test-stp-iol-op-3.1 b/tests/test-stp-iol-op-3.1
deleted file mode 100644 (file)
index 3e1099c..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-# This test file approximates the following test from "Bridge
-# Functions Consortium Spanning Tree Protocol Operations Test Suite
-# Version 2.3":
-# Test STP.op.3.1 Â­ Root Bridge Selection: Root ID Values
-bridge 0 0x111 = a
-bridge 1 0x222 = a
-check 0 = rootid:0x111 Li
-check 1 = rootid:0x222 Li
-run 1000
-check 0 = rootid:0x111 root
-check 1 = rootid:0x111 F:10
diff --git a/tests/test-stp-iol-op-3.3 b/tests/test-stp-iol-op-3.3
deleted file mode 100644 (file)
index 2bcd45e..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-# This test file approximates the following test from "Bridge
-# Functions Consortium Spanning Tree Protocol Operations Test Suite
-# Version 2.3":
-# Test STP.op.3.3 Â­ Root Bridge Selection: Bridge ID Values
-bridge 0 0x333^0x6000 = a
-bridge 1 0x222^0x7000 = b
-bridge 2 0x111 = a b
-run 1000
-check 0 = rootid:0x333^0x6000 root
-check 1 = rootid:0x333^0x6000 F:20
-check 2 = rootid:0x333^0x6000 F:10 F
diff --git a/tests/test-stp-iol-op-3.4 b/tests/test-stp-iol-op-3.4
deleted file mode 100644 (file)
index 2bcd45e..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-# This test file approximates the following test from "Bridge
-# Functions Consortium Spanning Tree Protocol Operations Test Suite
-# Version 2.3":
-# Test STP.op.3.3 Â­ Root Bridge Selection: Bridge ID Values
-bridge 0 0x333^0x6000 = a
-bridge 1 0x222^0x7000 = b
-bridge 2 0x111 = a b
-run 1000
-check 0 = rootid:0x333^0x6000 root
-check 1 = rootid:0x333^0x6000 F:20
-check 2 = rootid:0x333^0x6000 F:10 F
diff --git a/tests/test-stp.sh b/tests/test-stp.sh
deleted file mode 100755 (executable)
index 4f194ac..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-#! /bin/sh
-
-# Copyright (c) 2008 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.
-
-set -e
-progress=
-for d in ${stp_files}; do
-    echo "Testing $d..."
-    $SUPERVISOR ./tests/test-stp ${srcdir}/$d
-done
diff --git a/tests/test-vconn.c b/tests/test-vconn.c
new file mode 100644 (file)
index 0000000..7f85137
--- /dev/null
@@ -0,0 +1,384 @@
+/*
+ * 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 "vconn.h"
+#include <errno.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "poll-loop.h"
+#include "socket-util.h"
+#include "timeval.h"
+#include "util.h"
+#include "vlog.h"
+
+#undef NDEBUG
+#include <assert.h>
+
+struct fake_pvconn {
+    const char *type;
+    char *pvconn_name;
+    char *vconn_name;
+    int fd;
+};
+
+static void
+fpv_create(const char *type, struct fake_pvconn *fpv)
+{
+    fpv->type = type;
+    if (!strcmp(type, "unix")) {
+        static int unix_count = 0;
+        char *bind_path;
+        int fd;
+
+        bind_path = xasprintf("fake-pvconn.%d", unix_count++);
+        fd = make_unix_socket(SOCK_STREAM, false, false, bind_path, NULL);
+        if (fd < 0) {
+            ovs_fatal(-fd, "%s: could not bind to Unix domain socket",
+                      bind_path);
+        }
+
+        fpv->pvconn_name = xasprintf("punix:%s", bind_path);
+        fpv->vconn_name = xasprintf("unix:%s", bind_path);
+        fpv->fd = fd;
+        free(bind_path);
+    } else if (!strcmp(type, "tcp")) {
+        struct sockaddr_in sin;
+        socklen_t sin_len;
+        int fd;
+
+        /* Create TCP socket. */
+        fd = socket(PF_INET, SOCK_STREAM, 0);
+        if (fd < 0) {
+            ovs_fatal(errno, "failed to create TCP socket");
+        }
+
+        /* Bind TCP socket to localhost on any available port. */
+        sin.sin_family = AF_INET;
+        sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+        sin.sin_port = htons(0);
+        if (bind(fd, (struct sockaddr *) &sin, sizeof sin) < 0) {
+            ovs_fatal(errno, "failed to bind TCP socket");
+        }
+
+        /* Retrieve socket's port number. */
+        sin_len = sizeof sin;
+        if (getsockname(fd, &sin, &sin_len) < 0) {
+            ovs_fatal(errno, "failed to read TCP socket name");
+        }
+        if (sin_len != sizeof sin || sin.sin_family != AF_INET) {
+            ovs_fatal(errno, "bad TCP socket name");
+        }
+
+        /* Save info. */
+        fpv->pvconn_name = xasprintf("ptcp:%"PRIu16":127.0.0.1",
+                                    ntohs(sin.sin_port));
+        fpv->vconn_name = xasprintf("tcp:127.0.0.1:%"PRIu16,
+                                    ntohs(sin.sin_port));
+        fpv->fd = fd;
+    } else {
+        abort();
+    }
+
+    /* Listen. */
+    if (listen(fpv->fd, 0) < 0) {
+        ovs_fatal(errno, "%s: listen failed", fpv->vconn_name);
+    }
+}
+
+static int
+fpv_accept(struct fake_pvconn *fpv)
+{
+    int fd;
+
+    fd = accept(fpv->fd, NULL, NULL);
+    if (fd < 0) {
+        ovs_fatal(errno, "%s: accept failed", fpv->pvconn_name);
+    }
+    return fd;
+}
+
+static void
+fpv_close(struct fake_pvconn *fpv)
+{
+    if (fpv->fd >= 0) {
+        if (close(fpv->fd) < 0) {
+            ovs_fatal(errno, "failed to close %s fake pvconn", fpv->type);
+        }
+        fpv->fd = -1;
+    }
+}
+
+static void
+fpv_destroy(struct fake_pvconn *fpv)
+{
+    fpv_close(fpv);
+    free(fpv->pvconn_name);
+    free(fpv->vconn_name);
+}
+
+/* Connects to a fake_pvconn with vconn_open(), then closes the listener and
+ * verifies that vconn_connect() reports 'expected_error'. */
+static void
+test_refuse_connection(const char *type, int expected_error)
+{
+    struct fake_pvconn fpv;
+    struct vconn *vconn;
+
+    fpv_create(type, &fpv);
+    assert(!vconn_open(fpv.vconn_name, OFP_VERSION, &vconn));
+    fpv_close(&fpv);
+    assert(vconn_connect(vconn) == expected_error);
+    vconn_close(vconn);
+    fpv_destroy(&fpv);
+}
+
+/* Connects to a fake_pvconn with vconn_open(), accepts that connection and
+ * closes it immediately, and verifies that vconn_connect() reports
+ * 'expected_error'. */
+static void
+test_accept_then_close(const char *type, int expected_error)
+{
+    struct fake_pvconn fpv;
+    struct vconn *vconn;
+
+    fpv_create(type, &fpv);
+    assert(!vconn_open(fpv.vconn_name, OFP_VERSION, &vconn));
+    close(fpv_accept(&fpv));
+    fpv_close(&fpv);
+    assert(vconn_connect(vconn) == expected_error);
+    vconn_close(vconn);
+    fpv_destroy(&fpv);
+}
+
+/* Connects to a fake_pvconn with vconn_open(), accepts that connection and
+ * reads the hello message from it, then closes the connection and verifies
+ * that vconn_connect() reports 'expected_error'. */
+static void
+test_read_hello(const char *type, int expected_error)
+{
+    struct fake_pvconn fpv;
+    struct vconn *vconn;
+    int fd;
+
+    fpv_create(type, &fpv);
+    assert(!vconn_open(fpv.vconn_name, OFP_VERSION, &vconn));
+    fd = fpv_accept(&fpv);
+    fpv_destroy(&fpv);
+    assert(!set_nonblocking(fd));
+    for (;;) {
+       struct ofp_header hello;
+       int retval;
+
+       retval = read(fd, &hello, sizeof hello);
+       if (retval == sizeof hello) {
+           assert(hello.version == OFP_VERSION);
+           assert(hello.type == OFPT_HELLO);
+           assert(hello.length == htons(sizeof hello));
+           break;
+       } else {
+           assert(errno == EAGAIN);
+       }
+
+       assert(vconn_connect(vconn) == EAGAIN);
+       vconn_connect_wait(vconn);
+       poll_fd_wait(fd, POLLIN);
+       poll_block();
+    }
+    close(fd);
+    assert(vconn_connect(vconn) == expected_error);
+    vconn_close(vconn);
+}
+
+/* Connects to a fake_pvconn with vconn_open(), accepts that connection and
+ * sends the 'out' bytes in 'out_size' to it (presumably an OFPT_HELLO
+ * message), then verifies that vconn_connect() reports
+ * 'expect_connect_error'. */
+static void
+test_send_hello(const char *type, const void *out, size_t out_size,
+                int expect_connect_error)
+{
+    struct fake_pvconn fpv;
+    struct vconn *vconn;
+    bool read_hello, connected;
+    struct ofpbuf *msg;
+    int fd;
+
+    fpv_create(type, &fpv);
+    assert(!vconn_open(fpv.vconn_name, OFP_VERSION, &vconn));
+    fd = fpv_accept(&fpv);
+    fpv_destroy(&fpv);
+
+    write(fd, out, out_size);
+
+    assert(!set_nonblocking(fd));
+
+    read_hello = connected = false;
+    for (;;) {
+       if (!read_hello) {
+           struct ofp_header hello;
+           int retval = read(fd, &hello, sizeof hello);
+           if (retval == sizeof hello) {
+               assert(hello.version == OFP_VERSION);
+               assert(hello.type == OFPT_HELLO);
+               assert(hello.length == htons(sizeof hello));
+               read_hello = true;
+           } else {
+               assert(errno == EAGAIN);
+           }
+       }
+
+       if (!connected) {
+           int error = vconn_connect(vconn);
+           if (error == expect_connect_error) {
+               if (!error) {
+                   connected = true;
+               } else {
+                   close(fd);
+                   vconn_close(vconn);
+                   return;
+               }
+           } else {
+               assert(error == EAGAIN);
+           }
+       }
+
+       if (read_hello && connected) {
+           break;
+       }
+
+       if (!connected) {
+           vconn_connect_wait(vconn);
+       }
+       if (!read_hello) {
+           poll_fd_wait(fd, POLLIN);
+       }
+       poll_block();
+    }
+    close(fd);
+    assert(vconn_recv(vconn, &msg) == EOF);
+    vconn_close(vconn);
+}
+
+/* Try connecting and sending a normal hello, which should succeed. */
+static void
+test_send_plain_hello(const char *type)
+{
+    struct ofp_header hello;
+
+    hello.version = OFP_VERSION;
+    hello.type = OFPT_HELLO;
+    hello.length = htons(sizeof hello);
+    hello.xid = htonl(0x12345678);
+    test_send_hello(type, &hello, sizeof hello, 0);
+}
+
+/* Try connecting and sending an extra-long hello, which should succeed (since
+ * the specification says that implementations must accept and ignore extra
+ * data). */
+static void
+test_send_long_hello(const char *type)
+{
+    struct ofp_header hello;
+    char buffer[sizeof hello * 2];
+
+    hello.version = OFP_VERSION;
+    hello.type = OFPT_HELLO;
+    hello.length = htons(sizeof buffer);
+    hello.xid = htonl(0x12345678);
+    memset(buffer, 0, sizeof buffer);
+    memcpy(buffer, &hello, sizeof hello);
+    test_send_hello(type, buffer, sizeof buffer, 0);
+}
+
+/* Try connecting and sending an echo request instead of a hello, which should
+ * fail with EPROTO. */
+static void
+test_send_echo_hello(const char *type)
+{
+    struct ofp_header echo;
+
+    echo.version = OFP_VERSION;
+    echo.type = OFPT_ECHO_REQUEST;
+    echo.length = htons(sizeof echo);
+    echo.xid = htonl(0x89abcdef);
+    test_send_hello(type, &echo, sizeof echo, EPROTO);
+}
+
+/* Try connecting and sending a hello packet that has its length field as 0,
+ * which should fail with EPROTO. */
+static void
+test_send_short_hello(const char *type)
+{
+    struct ofp_header hello;
+
+    memset(&hello, 0, sizeof hello);
+    test_send_hello(type, &hello, sizeof hello, EPROTO);
+}
+
+/* Try connecting and sending a hello packet that has a bad version, which
+ * should fail with EPROTO. */
+static void
+test_send_invalid_version_hello(const char *type)
+{
+    struct ofp_header hello;
+
+    hello.version = OFP_VERSION - 1;
+    hello.type = OFPT_HELLO;
+    hello.length = htons(sizeof hello);
+    hello.xid = htonl(0x12345678);
+    test_send_hello(type, &hello, sizeof hello, EPROTO);
+}
+
+int
+main(int argc UNUSED, char *argv[])
+{
+    set_program_name(argv[0]);
+    time_init();
+    vlog_init();
+    signal(SIGPIPE, SIG_IGN);
+    vlog_set_levels(VLM_ANY_MODULE, VLF_ANY_FACILITY, VLL_EMER);
+
+    time_alarm(10);
+
+    test_refuse_connection("unix", EPIPE);
+    test_refuse_connection("tcp", ECONNRESET);
+
+    test_accept_then_close("unix", EPIPE);
+    test_accept_then_close("tcp", ECONNRESET);
+
+    test_read_hello("unix", ECONNRESET);
+    test_read_hello("tcp", ECONNRESET);
+
+    test_send_plain_hello("unix");
+    test_send_plain_hello("tcp");
+
+    test_send_long_hello("unix");
+    test_send_long_hello("tcp");
+
+    test_send_echo_hello("unix");
+    test_send_echo_hello("tcp");
+
+    test_send_short_hello("unix");
+    test_send_short_hello("tcp");
+
+    test_send_invalid_version_hello("unix");
+    test_send_invalid_version_hello("tcp");
+
+    return 0;
+}
diff --git a/tests/testsuite.at b/tests/testsuite.at
new file mode 100644 (file)
index 0000000..f894c2a
--- /dev/null
@@ -0,0 +1,22 @@
+AT_INIT
+
+AT_COPYRIGHT([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.])
+
+AT_TESTED([ovs-vswitchd])
+
+m4_include([tests/lcov-pre.at])
+m4_include([tests/library.at])
+m4_include([tests/stp.at])
+m4_include([tests/lcov-post.at])
index 32a7f2e..ebbd691 100644 (file)
@@ -15,6 +15,8 @@
 /ovs-kill.8
 /ovs-ofctl
 /ovs-ofctl.8
+/ovs-openflowd
+/ovs-openflowd.8
 /ovs-parse-leaks
 /ovs-pki
 /ovs-pki-cgi
index 97b827a..5bf3cbb 100644 (file)
@@ -6,6 +6,7 @@ bin_PROGRAMS += \
        utilities/ovs-dpctl \
        utilities/ovs-kill \
        utilities/ovs-ofctl \
+       utilities/ovs-openflowd \
        utilities/ovs-wdt
 noinst_PROGRAMS += utilities/nlmon
 bin_SCRIPTS += utilities/ovs-pki
@@ -20,6 +21,7 @@ EXTRA_DIST += \
        utilities/ovs-dpctl.8.in \
        utilities/ovs-kill.8.in \
        utilities/ovs-ofctl.8.in \
+       utilities/ovs-openflowd.8.in \
        utilities/ovs-parse-leaks.in \
        utilities/ovs-pki-cgi.in \
        utilities/ovs-pki.8.in \
@@ -32,6 +34,7 @@ DISTCLEANFILES += \
        utilities/ovs-dpctl.8 \
        utilities/ovs-kill.8 \
        utilities/ovs-ofctl.8 \
+       utilities/ovs-openflowd.8 \
        utilities/ovs-parse-leaks \
        utilities/ovs-pki \
        utilities/ovs-pki.8 \
@@ -45,6 +48,7 @@ man_MANS += \
        utilities/ovs-dpctl.8 \
        utilities/ovs-kill.8 \
        utilities/ovs-ofctl.8 \
+       utilities/ovs-openflowd.8 \
        utilities/ovs-pki.8
 
 utilities_ovs_appctl_SOURCES = utilities/ovs-appctl.c
@@ -68,6 +72,13 @@ utilities_ovs_kill_LDADD = lib/libopenvswitch.a
 utilities_ovs_ofctl_SOURCES = utilities/ovs-ofctl.c
 utilities_ovs_ofctl_LDADD = lib/libopenvswitch.a $(FAULT_LIBS) $(SSL_LIBS)
 
+utilities_ovs_openflowd_SOURCES = utilities/ovs-openflowd.c
+utilities_ovs_openflowd_LDADD = \
+       ofproto/libofproto.a \
+       lib/libopenvswitch.a \
+       $(FAULT_LIBS) \
+       $(SSL_LIBS)
+
 utilities_ovs_wdt_SOURCES = utilities/ovs-wdt.c
 
 utilities_nlmon_SOURCES = utilities/nlmon.c
index 9bf97fd..d5e6b82 100644 (file)
@@ -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 @@ error occurs.  Use \fB-e help\fR to print a list of available commands.
 
 .BR ovs\-controller (8),
 .BR ovs\-dpctl (8),
-.BR secchan (8)
+.BR ovs\-openflowd (8)
index 750fcea..658cf13 100644 (file)
@@ -16,16 +16,22 @@ protocol, causing them to function as L2 MAC-learning switches or hub.
 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
@@ -75,7 +81,7 @@ already have the controller CA certificate for it to have any
 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
@@ -96,7 +102,7 @@ recommended, flows will never expire.  The default is 60 seconds.
 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).
@@ -126,6 +132,6 @@ To bind locally to port 6633 (the default) and wait for incoming connections fro
 
 .SH "SEE ALSO"
 
-.BR secchan (8),
+.BR ovs\-openflowd (8),
 .BR ovs\-appctl (8),
 .BR ovs\-dpctl (8)
index e9685d9..61e4b12 100644 (file)
@@ -17,7 +17,7 @@ receives an acceptable DHCP response.  It will accept any valid DHCP
 reply that has the same vendor class identifier and includes a
 vendor-specific option with code 1 whose contents are a string
 specifying the location of the controller in the same format used on
-the \fBsecchan\fR command line (e.g. \fBssl:192.168.0.1\fR).
+the \fBovs\-openflowd\fR command line (e.g. \fBssl:192.168.0.1\fR).
 
 When \fBovs\-discover\fR receives an acceptable response, it prints
 the details of the response on \fBstdout\fR.  Then, by default, it
@@ -28,15 +28,16 @@ itself to the background.
 .SH OPTIONS
 .TP
 \fB--accept-vconn=\fIregex\fR
-By default, \fBovs\-discover\fR accepts any controller location
-advertised over DHCP.  With this option, only controllers whose names
-match POSIX extended regular expression \fIregex\fR will be accepted.
-Specifying \fBssl:.*\fR for \fIregex\fR, for example, would cause only
-SSL controller connections to be accepted.
+With this option, only controllers whose names match POSIX extended
+regular expression \fIregex\fR will be accepted.  Specifying
+\fBssl:.*\fR for \fIregex\fR, for example, would cause only SSL
+controller connections to be accepted.
 
 The \fIregex\fR is implicitly anchored at the beginning of the
 controller location string, as if it begins with \fB^\fR.
 
+When this option is not given, the default \fIregex\fR is
+\fBtcp:.*\fR.
 .TP
 \fB--exit-without-bind\fR
 By default, \fBovs\-discover\fR binds the network device that receives
@@ -113,5 +114,5 @@ arriving IP packets, will not.
 
 .SH "SEE ALSO"
 
-.BR secchan (8),
-.BR ovs-pki (8)
+.BR ovs\-openflowd (8),
+.BR ovs\-pki (8)
index 308c405..dc91bce 100644 (file)
@@ -48,7 +48,7 @@ static int n_ifaces;
 
 /* --accept-vconn: Regular expression specifying the class of controller vconns
  * that we will accept during autodiscovery. */
-static const char *accept_controller_re = ".*";
+static const char *accept_controller_re = "tcp:.*";
 static regex_t accept_controller_regex;
 
 /* --exit-without-bind: Exit after discovering the controller, without binding
index a1719f8..dc4d456 100644 (file)
@@ -41,14 +41,13 @@ The following commands manage datapaths.
 
 Creates datapath \fIdp\fR.  The name of the new datapath's local port
 depends on how \fIdp\fR is specified: if it takes the form
-\fBdp\fIN\fR, the local port will be named \fBdp\fIN\fR; if \fIdp\fR
-is \fBnl:\fI, the local port will be named \fBof\fIN\fR; otherwise,
+\fBdp\fIN\fR, the local port will be named \fBdp\fIN\fR; otherwise,
 the local port's name will be \fIdp\fR.
 
 This will fail if the host already has 256 datapaths, if a network
 device with the same name as the new datapath's local port already
-exists, or if \fIdp\fR is given in the form \fBdp\fIN\fR or
-\fBnl:\fIN\fR and a datapath numbered \fIN\fR already exists.
+exists, or if \fIdp\fR is given in the form \fBdp\fIN\fR
+and a datapath numbered \fIN\fR already exists.
 
 If \fInetdev\fRs are specified, \fBovs\-dpctl\fR adds them to the datapath.
 
@@ -71,11 +70,6 @@ A \fInetdev\fR may be followed by a comma-separated list of options.
 The following options are currently supported:
 
 .RS
-.IP "\fBport=\fIportno\fR"
-Specifies \fIportno\fR (a number between 1 and 255) as the port number
-at which \fInetdev\fR will be attached.  By default, \fBadd\-if\fR
-automatically selects the lowest available port number.
-
 .IP "\fBinternal\fR"
 Instead of attaching an existing \fInetdev\fR, creates an internal
 port (analogous to the local port) with that name.
@@ -120,9 +114,10 @@ up may be confused about their disappearance.
 .IP "\fBdump-groups \fIdp\fR"
 Prints to the console the sets of port groups maintained by datapath
 \fIdp\fR.  Ordinarily there are at least 2 port groups in a datapath
-that \fBsecchan\fR or \fBvswitch\fR is controlling: group 0 contains
+that \fBovs\-openflowd\fR or \fBovs\-vswitch\fR is controlling: group
+0 contains
 all ports except those disabled by STP, and group 1 contains all
-ports.  Additional groups might be used in the future.
+ports.  Additional or different groups might be used in the future.
 
 This command is primarily useful for debugging Open vSwitch.  OpenFlow
 does not have a concept of port groups.
@@ -151,7 +146,7 @@ Creates datapath number 0.
 Adds two network devices to the new datapath.
 
 .PP
-At this point one would ordinarily start \fBsecchan\fR(8) on
+At this point one would ordinarily start \fBovs\-openflowd\fR(8) on
 \fBdp0\fR, transforming \fBdp0\fR into an OpenFlow switch.  Then, when
 the switch and the datapath is no longer needed:
 
@@ -165,6 +160,6 @@ Deletes the datapath.
 
 .SH "SEE ALSO"
 
-.BR secchan (8),
 .BR ovs\-appctl (8),
+.BR ovs\-openflowd (8),
 .BR ovs\-vswitchd (8)
index 886cdb3..5a0da82 100644 (file)
@@ -213,9 +213,9 @@ static int if_up(const char *netdev_name)
 static void
 do_add_dp(int argc UNUSED, char *argv[])
 {
-    struct dpif dpif;
+    struct dpif *dpif;
     run(dpif_create(argv[1], &dpif), "add_dp");
-    dpif_close(&dpif);
+    dpif_close(dpif);
     if (argc > 2) {
         do_add_if(argc, argv);
     }
@@ -224,10 +224,10 @@ do_add_dp(int argc UNUSED, char *argv[])
 static void
 do_del_dp(int argc UNUSED, char *argv[])
 {
-    struct dpif dpif;
+    struct dpif *dpif;
     run(dpif_open(argv[1], &dpif), "opening datapath");
-    run(dpif_delete(&dpif), "del_dp");
-    dpif_close(&dpif);
+    run(dpif_delete(dpif), "del_dp");
+    dpif_close(dpif);
 }
 
 static int
@@ -245,41 +245,17 @@ query_ports(struct dpif *dpif, struct odp_port **ports, size_t *n_ports)
     qsort(*ports, *n_ports, sizeof **ports, compare_ports);
 }
 
-static uint16_t
-get_free_port(struct dpif *dpif)
-{
-    struct odp_port *ports;
-    size_t n_ports;
-    int port_no;
-
-    query_ports(dpif, &ports, &n_ports);
-    for (port_no = 0; port_no <= UINT16_MAX; port_no++) {
-        size_t i;
-        for (i = 0; i < n_ports; i++) {
-            if (ports[i].port == port_no) {
-                goto next_portno;
-            }
-        }
-        free(ports);
-        return port_no;
-
-    next_portno: ;
-    }
-    ovs_fatal(0, "no free datapath ports");
-}
-
 static void
 do_add_if(int argc UNUSED, char *argv[])
 {
     bool failure = false;
-    struct dpif dpif;
+    struct dpif *dpif;
     int i;
 
     run(dpif_open(argv[1], &dpif), "opening datapath");
     for (i = 2; i < argc; i++) {
         char *save_ptr = NULL;
         char *devname, *suboptions;
-        int port = -1;
         int flags = 0;
         int error;
 
@@ -292,11 +268,9 @@ do_add_if(int argc UNUSED, char *argv[])
         suboptions = strtok_r(NULL, "", &save_ptr);
         if (suboptions) {
             enum {
-                AP_PORT,
                 AP_INTERNAL
             };
             static char *options[] = {
-                "port",
                 "internal"
             };
 
@@ -304,13 +278,6 @@ do_add_if(int argc UNUSED, char *argv[])
                 char *value;
 
                 switch (getsubopt(&suboptions, options, &value)) {
-                case AP_PORT:
-                    if (!value) {
-                        ovs_error(0, "'port' suboption requires a value");
-                    }
-                    port = atoi(value);
-                    break;
-
                 case AP_INTERNAL:
                     flags |= ODP_PORT_INTERNAL;
                     break;
@@ -321,20 +288,16 @@ do_add_if(int argc UNUSED, char *argv[])
                 }
             }
         }
-        if (port < 0) {
-            port = get_free_port(&dpif);
-        }
 
-        error = dpif_port_add(&dpif, devname, port, flags);
+        error = dpif_port_add(dpif, devname, flags, NULL);
         if (error) {
-            ovs_error(error, "adding %s as port %"PRIu16" of %s failed",
-                      devname, port, argv[1]);
+            ovs_error(error, "adding %s to %s failed", devname, argv[1]);
             failure = true;
         } else if (if_up(devname)) {
             failure = true;
         }
     }
-    dpif_close(&dpif);
+    dpif_close(dpif);
     if (failure) {
         exit(EXIT_FAILURE);
     }
@@ -364,7 +327,7 @@ static void
 do_del_if(int argc UNUSED, char *argv[])
 {
     bool failure = false;
-    struct dpif dpif;
+    struct dpif *dpif;
     int i;
 
     run(dpif_open(argv[1], &dpif), "opening datapath");
@@ -375,18 +338,18 @@ do_del_if(int argc UNUSED, char *argv[])
 
         if (!name[strspn(name, "0123456789")]) {
             port = atoi(name);
-        } else if (!get_port_number(&dpif, name, &port)) {
+        } else if (!get_port_number(dpif, name, &port)) {
             failure = true;
             continue;
         }
 
-        error = dpif_port_del(&dpif, port);
+        error = dpif_port_del(dpif, port);
         if (error) {
             ovs_error(error, "deleting port %s from %s failed", name, argv[1]);
             failure = true;
         }
     }
-    dpif_close(&dpif);
+    dpif_close(dpif);
     if (failure) {
         exit(EXIT_FAILURE);
     }
@@ -400,7 +363,7 @@ show_dpif(struct dpif *dpif)
     size_t n_ports;
     size_t i;
 
-    printf("dp%u:\n", dpif_id(dpif));
+    printf("%s:\n", dpif_name(dpif));
     if (!dpif_get_dp_stats(dpif, &stats)) {
         printf("\tflows: cur:%"PRIu32", soft-max:%"PRIu32", "
                "hard-max:%"PRIu32"\n",
@@ -434,12 +397,12 @@ do_show(int argc, char *argv[])
         int i;
         for (i = 1; i < argc; i++) {
             const char *name = argv[i];
-            struct dpif dpif;
+            struct dpif *dpif;
             int error;
 
             error = dpif_open(name, &dpif);
             if (!error) {
-                show_dpif(&dpif);
+                show_dpif(dpif);
             } else {
                 ovs_error(error, "opening datapath %s failed", name);
                 failure = true;
@@ -449,13 +412,13 @@ do_show(int argc, char *argv[])
         unsigned int i;
         for (i = 0; i < ODP_MAX; i++) {
             char name[128];
-            struct dpif dpif;
+            struct dpif *dpif;
             int error;
 
             sprintf(name, "dp%u", i);
             error = dpif_open(name, &dpif);
             if (!error) {
-                show_dpif(&dpif);
+                show_dpif(dpif);
             } else if (error != ENODEV) {
                 ovs_error(error, "opening datapath %s failed", name);
                 failure = true;
@@ -478,16 +441,11 @@ do_dump_dps(int argc UNUSED, char *argv[] UNUSED)
     error = dp_enumerate(&all_dps);
 
     for (i = 0; i < all_dps.n; i++) {
-        struct dpif dpif;
-        char dpif_name[IF_NAMESIZE];
-
-        if (dpif_open(all_dps.names[i], &dpif)) {
-            continue;
-        }
-        if (!dpif_get_name(&dpif, dpif_name, sizeof dpif_name)) {
-            printf("%s\n", dpif_name);
+        struct dpif *dpif;
+        if (!dpif_open(all_dps.names[i], &dpif)) {
+            printf("%s\n", dpif_name(dpif));
+            dpif_close(dpif);
         }
-        dpif_close(&dpif);
     }
 
     svec_destroy(&all_dps);
@@ -500,13 +458,13 @@ static void
 do_dump_flows(int argc UNUSED, char *argv[])
 {
     struct odp_flow *flows;
-    struct dpif dpif;
+    struct dpif *dpif;
     size_t n_flows;
     struct ds ds;
     size_t i;
 
     run(dpif_open(argv[1], &dpif), "opening datapath");
-    run(dpif_flow_list_all(&dpif, &flows, &n_flows), "listing all flows");
+    run(dpif_flow_list_all(dpif, &flows, &n_flows), "listing all flows");
 
     ds_init(&ds);
     for (i = 0; i < n_flows; i++) {
@@ -516,41 +474,40 @@ do_dump_flows(int argc UNUSED, char *argv[])
 
         f->actions = actions;
         f->n_actions = MAX_ACTIONS;
-        dpif_flow_get(&dpif, f);
+        dpif_flow_get(dpif, f);
 
         ds_clear(&ds);
         format_odp_flow(&ds, f);
         printf("%s\n", ds_cstr(&ds));
     }
     ds_destroy(&ds);
-    dpif_close(&dpif);
+    dpif_close(dpif);
 }
 
 static void
 do_del_flows(int argc UNUSED, char *argv[])
 {
-    struct dpif dpif;
+    struct dpif *dpif;
 
     run(dpif_open(argv[1], &dpif), "opening datapath");
-    run(dpif_flow_flush(&dpif), "deleting all flows");
-    dpif_close(&dpif);
+    run(dpif_flow_flush(dpif), "deleting all flows");
+    dpif_close(dpif);
 }
 
 static void
 do_dump_groups(int argc UNUSED, char *argv[])
 {
     struct odp_stats stats;
-    struct dpif dpif;
+    struct dpif *dpif;
     unsigned int i;
 
     run(dpif_open(argv[1], &dpif), "opening datapath");
-    run(dpif_get_dp_stats(&dpif, &stats), "get datapath stats");
+    run(dpif_get_dp_stats(dpif, &stats), "get datapath stats");
     for (i = 0; i < stats.max_groups; i++) {
-        uint16_t ports[UINT16_MAX];
+        uint16_t *ports;
         size_t n_ports;
 
-        if (!dpif_port_group_get(&dpif, i, ports,
-                                 ARRAY_SIZE(ports), &n_ports) && n_ports) {
+        if (!dpif_port_group_get(dpif, i, &ports, &n_ports) && n_ports) {
             size_t j;
 
             printf("group %u:", i);
@@ -559,8 +516,9 @@ do_dump_groups(int argc UNUSED, char *argv[])
             }
             printf("\n");
         }
+        free(ports);
     }
-    dpif_close(&dpif);
+    dpif_close(dpif);
 }
 
 static void
index 215032a..40ad64b 100755 (executable)
@@ -16,8 +16,8 @@
 
 PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
 
-SECCHAN_PID=/var/run/secchan.pid
-SECCHAN_SOCK=/var/run/secchan.mgmt
+OPENFLOWD_PID=/var/run/ovs-openflowd.pid
+OPENFLOWD_SOCK=/var/run/ovs-openflowd.mgmt
 LOG_FILE=/var/log/openflow/monitor
 INTERVAL=1
 FAIL_THRESH=3
@@ -27,8 +27,8 @@ usage() {
     echo
     echo "OPTIONS:"
     echo "  -h   Show this message"
-    echo "  -p   PID file for secchan (default: $SECCHAN_PID)"
-    echo "  -s   Unix socket for secchan (default: $SECCHAN_SOCK)"
+    echo "  -p   PID file for ovs-openflowd (default: $OPENFLOWD_PID)"
+    echo "  -s   Unix socket for ovs-openflowd (default: $OPENFLOWD_SOCK)"
     echo "  -l   File to log messages (default: $LOG_FILE)"
     echo "  -i   Interval to send probes in seconds (default: $INTERVAL)"
     echo "  -c   Number of failed probes before reboot (default: $FAIL_THRESH)"
@@ -48,11 +48,11 @@ while getopts "hp:s:l:i:c:" OPTION; do
             ;;
 
         p) 
-            SECCHAN_PID=$OPTARG
+            OPENFLOWD_PID=$OPTARG
             ;;
 
         s) 
-            SECCHAN_SOCK=$OPTARG
+            OPENFLOWD_SOCK=$OPTARG
             ;;
 
         l) 
@@ -73,14 +73,14 @@ while getopts "hp:s:l:i:c:" OPTION; do
 done
 
 
-if [ ! -f $SECCHAN_PID ]; then
-    log "No secchan pid file: ${SECCHAN_PID}" 
-    echo "No secchan pid file: ${SECCHAN_PID}" 
+if [ ! -f $OPENFLOWD_PID ]; then
+    log "No ovs-openflowd pid file: ${OPENFLOWD_PID}"
+    echo "No ovs-openflowd pid file: ${OPENFLOWD_PID}"
 fi
 
-if [ ! -S $SECCHAN_SOCK ]; then
-    log "No secchan sock file: ${SECCHAN_SOCK}" 
-    echo "No secchan sock file: ${SECCHAN_SOCK}" 
+if [ ! -S $OPENFLOWD_SOCK ]; then
+    log "No ovs-openflowd sock file: ${OPENFLOWD_SOCK}"
+    echo "No ovs-openflowd sock file: ${OPENFLOWD_SOCK}"
 fi
 
 if [ ! -d `dirname $LOG_FILE` ]; then
@@ -88,17 +88,17 @@ if [ ! -d `dirname $LOG_FILE` ]; then
 fi
 
 let DP_DOWN=0
-let SECCHAN_DOWN=0
+let OPENFLOWD_DOWN=0
 log "===== Starting Monitor ===="
 while `/bin/true`; do
-    # Only check for liveness if the secchan's PID file exists.  The PID
-    # file is removed when secchan is brought down gracefully.
-    if [ -f $SECCHAN_PID ]; then
-        pid=`cat $SECCHAN_PID`
+    # Only check for liveness if ovs-openflowd's PID file exists.  The PID
+    # file is removed when ovs-openflowd is brought down gracefully.
+    if [ -f $OPENFLOWD_PID ]; then
+        pid=`cat $OPENFLOWD_PID`
         if [ -d /proc/$pid ]; then
-            # Check if the secchan and datapath still can communicate
-            if [ -S $SECCHAN_SOCK ]; then
-                ovs-ofctl probe -t 2 unix:$SECCHAN_SOCK 
+            # Check if the ovs-openflowd and datapath still can communicate
+            if [ -S $OPENFLOWD_SOCK ]; then
+                ovs-ofctl probe -t 2 unix:$OPENFLOWD_SOCK
                 if [ $? -ne 0 ]; then
                     log "datapath probe failed"
                     let DP_DOWN++
@@ -106,15 +106,15 @@ while `/bin/true`; do
                     let DP_DOWN=0
                 fi
             fi
-            let SECCHAN_DOWN=0
+            let OPENFLOWD_DOWN=0
         else
-            log "secchan probe failed"
-            let SECCHAN_DOWN++
+            log "ovs-openflowd probe failed"
+            let OPENFLOWD_DOWN++
         fi
     fi
 
-    if [ $SECCHAN_DOWN -ge $FAIL_THRESH ]; then
-        log "Failed to probe secchan after ${SECCHAN_DOWN} tries...rebooting!"
+    if [ $OPENFLOWD_DOWN -ge $FAIL_THRESH ]; then
+        log "Failed to probe ovs-openflowd after ${OPENFLOWD_DOWN} tries...rebooting!"
         reboot
     fi
 
index 6b39765..2e2b38b 100644 (file)
@@ -1,4 +1,4 @@
-.TH ovs\-ofctl 8 "March 2009" "Open vSwitch" "Open vSwitch Manual"
+.TH ovs\-ofctl 8 "June 2009" "Open vSwitch" "Open vSwitch Manual"
 .ds PN ovs\-ofctl
 
 .SH NAME
@@ -146,7 +146,7 @@ syntax of \fIflows\fR.
 \fBmonitor \fIswitch\fR [\fImiss-len\fR [\fIsend-exp]]
 Connects to \fIswitch\fR and prints to the console all OpenFlow
 messages received.  Usually, \fIswitch\fR should specify a connection
-named on \fBsecchan\fR(8)'s \fB-l\fR or \fB--listen\fR command line
+named on \fBovs\-openflowd\fR(8)'s \fB-l\fR or \fB--listen\fR command line
 option.
 
 If \fImiss-len\fR is provided, \fBovs\-ofctl\fR sends an OpenFlow ``set
@@ -223,8 +223,8 @@ Matches physical port \fIport_no\fR.  Switch ports are numbered as
 displayed by \fBovs\-ofctl show\fR.
 
 .IP \fBdl_vlan=\fIvlan\fR
-Matches IEEE 802.1q virtual LAN tag \fIvlan\fR.  Specify \fB0xffff\fR
-as \fIvlan\fR to match packets that are not tagged with a virtual LAN;
+Matches IEEE 802.1q Virtual LAN tag \fIvlan\fR.  Specify \fB0xffff\fR
+as \fIvlan\fR to match packets that are not tagged with a Virtual LAN;
 otherwise, specify a number between 0 and 4095, inclusive, as the
 12-bit VLAN ID to match.
 
@@ -326,7 +326,7 @@ omitted, then the entire packet is sent.
 .IP \fBlocal\fR
 Outputs the packet on the ``local port,'' which corresponds to the
 \fBof\fIn\fR network device (see \fBCONTACTING THE CONTROLLER\fR in
-\fBsecchan\fR(8) for information on the \fBof\fIn\fR network device).
+\fBovs\-openflowd\fR(8) for information on the \fBof\fIn\fR network device).
 
 .IP \fBdrop\fR
 Discards the packet, so no further processing or forwarding takes place.
@@ -470,7 +470,7 @@ The following examples assume that an OpenFlow switch on the local
 host has been configured to listen for management connections on a
 Unix domain socket named \fB@RUNDIR@/openflow.sock\fR, e.g. by
 specifying \fB--listen=punix:@RUNDIR@/openflow.sock\fR on the
-\fBsecchan\fR(8) command line.
+\fBovs\-openflowd\fR(8) command line.
 
 .TP
 \fBovs\-ofctl dump-tables unix:@RUNDIR@/openflow.sock\fR
index 04a869a..e873ed7 100644 (file)
@@ -251,7 +251,7 @@ static void run(int retval, const char *message, ...)
 static void
 open_vconn(const char *name, struct vconn **vconnp)
 {
-    struct dpif dpif;
+    struct dpif *dpif;
     struct stat s;
 
     if (strstr(name, ":")) {
@@ -268,9 +268,9 @@ open_vconn(const char *name, struct vconn **vconnp)
         char *socket_name;
         char *vconn_name;
 
-        run(dpif_get_name(&dpif, dpif_name, sizeof dpif_name),
+        run(dpif_port_get_name(dpif, ODPP_LOCAL, dpif_name, sizeof dpif_name),
             "obtaining name of %s", dpif_name);
-        dpif_close(&dpif);
+        dpif_close(dpif);
         if (strcmp(dpif_name, name)) {
             VLOG_INFO("datapath %s is named %s", name, dpif_name);
         }
@@ -617,6 +617,8 @@ str_to_action(char *str, struct ofpbuf *b)
              * packet to the controller. */
             if (arg && (strspn(act, "0123456789") == strlen(act))) {
                oao->max_len = htons(str_to_u32(arg));
+            } else {
+                oao->max_len = htons(UINT16_MAX);
             }
         } else if (parse_port_name(act, &port)) {
             put_output_action(b, port);
similarity index 80%
rename from secchan/secchan.8.in
rename to utilities/ovs-openflowd.8.in
index 2ad50ae..3d25574 100644 (file)
@@ -1,16 +1,16 @@
-.TH secchan 8 "March 2009" "Open vSwitch" "Open vSwitch Manual"
-.ds PN secchan
+.TH ovs\-openflowd 8 "March 2009" "Open vSwitch" "Open vSwitch Manual"
+.ds PN ovs\-openflowd
 
 .SH NAME
-secchan \- OpenFlow switch implementation
+ovs\-openflowd \- OpenFlow switch implementation
 
 .SH SYNOPSIS
-.B secchan
+.B ovs\-openflowd
 [\fIoptions\fR] \fIdatapath\fR [\fIcontroller\fR]
 
 .SH DESCRIPTION
-The \fBsecchan\fR program implements an OpenFlow switch using a
-flow-based datapath.  \fBsecchan\fR connects to an OpenFlow controller
+The \fBovs\-openflowd\fR program implements an OpenFlow switch using a
+flow-based datapath.  \fBovs\-openflowd\fR connects to an OpenFlow controller
 over TCP or SSL.
 
 The mandatory \fIdatapath\fR argument argument specifies the local datapath
@@ -39,7 +39,7 @@ The Unix domain server socket named \fIfile\fR.
 .RE
 
 .PP
-If \fIcontroller\fR is omitted, \fBsecchan\fR attempts to discover the
+If \fIcontroller\fR is omitted, \fBovs\-openflowd\fR attempts to discover the
 location of the controller automatically (see below).
 
 .SS "Contacting the Controller"
@@ -52,9 +52,9 @@ the data traffic that it controls, that is, the switch does not use
 any of the network devices added to the datapath with \fBovs\-dpctl
 add\-if\fR in its communication with the controller.
 
-To use \fBsecchan\fR in a network with out-of-band control, specify
-\fB--out-of-band\fR on the \fBsecchan\fR command line.  The control
-network must be configured separately, before or after \fBsecchan\fR
+To use \fBovs\-openflowd\fR in a network with out-of-band control, specify
+\fB--out-of-band\fR on the \fBovs\-openflowd\fR command line.  The control
+network must be configured separately, before or after \fBovs\-openflowd\fR
 is started.
 
 .IP in-band
@@ -65,7 +65,7 @@ add\-if\fR.  This configuration is often more convenient than
 out-of-band control, because it is not necessary to maintain two
 independent networks.
 
-In-band control is the default for \fBsecchan\fR, so no special
+In-band control is the default for \fBovs\-openflowd\fR, so no special
 command-line option is required.
 
 With in-band control, the location of the controller can be configured
@@ -73,23 +73,23 @@ manually or discovered automatically:
 
 .RS
 .IP "controller discovery"
-To make \fBsecchan\fR discover the location of the controller
+To make \fBovs\-openflowd\fR discover the location of the controller
 automatically, do not specify the location of the controller on the
-\fBsecchan\fR command line.
+\fBovs\-openflowd\fR command line.
 
-In this mode, \fBsecchan\fR will broadcast a DHCP request with vendor
+In this mode, \fBovs\-openflowd\fR will broadcast a DHCP request with vendor
 class identifier \fBOpenFlow\fR across the network devices added to
 the datapath with \fBovs\-dpctl add\-if\fR.  It will accept any valid DHCP
 reply that has the same vendor class identifier and includes a
 vendor-specific option with code 1 whose contents are a string
 specifying the location of the controller in the same format used on
-the \fBsecchan\fR command line (e.g. \fBssl:192.168.0.1\fR).
+the \fBovs\-openflowd\fR command line (e.g. \fBssl:192.168.0.1\fR).
 
 The DHCP reply may also, optionally, include a vendor-specific option
 with code 2 whose contents are a string specifying the URI to the base
 of the OpenFlow PKI (e.g. \fBhttp://192.168.0.1/openflow/pki\fR).
 This URI is used only for bootstrapping the OpenFlow PKI at initial
-switch setup; \fBsecchan\fR does not use it at all.
+switch setup; \fBovs\-openflowd\fR does not use it at all.
 
 The following ISC DHCP server configuration file assigns the IP
 address range 192.168.0.20 through 192.168.0.30 to OpenFlow switches
@@ -143,28 +143,28 @@ subnet 192.168.0.0 netmask 255.255.255.0 {
 
 .IP "manual configuration"
 To configure in-band control manually, specify the location of the
-controller on the \fBsecchan\fR command line as the \fIcontroller\fR
+controller on the \fBovs\-openflowd\fR command line as the \fIcontroller\fR
 argument.  You must also configure the network device for the OpenFlow
-``local port'' to allow \fBsecchan\fR to connect to that controller.
-The OpenFlow local port is a virtual network port that \fBsecchan\fR
+``local port'' to allow \fBovs\-openflowd\fR to connect to that controller.
+The OpenFlow local port is a virtual network port that \fBovs\-openflowd\fR
 bridges to the physical switch ports.  The name of the local port for
 a given \fIdatapath\fR may be seen by running \fBovs\-dpctl show
 \fIdatapath\fR; the local port is listed as port 0 in \fBshow\fR's
 output.
 
 .IP
-Before \fBsecchan\fR starts, the local port network device is not
+Before \fBovs\-openflowd\fR starts, the local port network device is not
 bridged to any physical network, so the next step depends on whether
 connectivity is required to configure the device's IP address.  If the
 switch has a static IP address, you may configure its IP address now
 with a command such as 
 .B ifconfig of0 192.168.1.1
-and then invoke \fBsecchan\fR.
+and then invoke \fBovs\-openflowd\fR.
 
 On the other hand, if the switch does not have a static IP address,
 e.g. it obtains its IP address dynamically via DHCP, the DHCP client
-will not be able to contact the DHCP server until the secure channel
-has started up.  Thus, start \fBsecchan\fR without configuring
+will not be able to contact the DHCP server until the OpenFlow switch
+has started up.  Thus, start \fBovs\-openflowd\fR without configuring
 the local port network device, and start the DHCP client afterward.
 .RE
 
@@ -172,7 +172,7 @@ the local port network device, and start the DHCP client afterward.
 .SS "Controller Discovery Options"
 .TP
 \fB--accept-vconn=\fIregex\fR
-When \fBsecchan\fR performs controller discovery (see \fBContacting
+When \fBovs\-openflowd\fR performs controller discovery (see \fBContacting
 the Controller\fR, above, for more information about controller
 discovery), it validates the controller location obtained via DHCP
 with a POSIX extended regular expression.  Only controllers whose
@@ -181,8 +181,8 @@ names match the regular expression will be accepted.
 The default regular expression is \fBssl:.*\fR (meaning that only SSL
 controller connections will be accepted) when any of the SSL
 configuration options \fB--private-key\fR, \fB--certificate\fR, or
-\fB--ca-cert\fR is specified.  The default is \fB.*\fR otherwise
-(meaning that any controller will be accepted).
+\fB--ca-cert\fR is specified.  The default is \fB^tcp:.*\fR otherwise
+(meaning that only TCP controller connections will be accepted).
 
 The \fIregex\fR is implicitly anchored at the beginning of the
 controller location string, as if it begins with \fB^\fR.
@@ -191,7 +191,7 @@ When controller discovery is not performed, this option has no effect.
 
 .TP
 \fB--no-resolv-conf\fR
-When \fBsecchan\fR performs controller discovery (see \fBContacting
+When \fBovs\-openflowd\fR performs controller discovery (see \fBContacting
 the Controller\fR, above, for more information about controller
 discovery), by default it overwrites the system's
 \fB/etc/resolv.conf\fR with domain information and DNS servers
@@ -199,10 +199,10 @@ obtained via DHCP.  If the location of the controller is specified
 using a hostname, rather than an IP address, and the network's DNS
 servers ever change, this behavior is essential.  But because it also
 interferes with any administrator or process that manages
-\fB/etc/resolv.conf\fR, when this option is specified, \fBsecchan\fR
+\fB/etc/resolv.conf\fR, when this option is specified, \fBovs\-openflowd\fR
 will not modify \fB/etc/resolv.conf\fR.
 
-\fBsecchan\fR will only modify \fBresolv.conf\fR if the DHCP response
+\fBovs\-openflowd\fR will only modify \fBresolv.conf\fR if the DHCP response
 that it receives specifies one or more DNS servers.
 
 When controller discovery is not performed, this option has no effect.
@@ -234,25 +234,25 @@ no new network connections can be set up.  If the connection to the
 controller stays down long enough, no packets can pass through the
 switch at all.
 
-If this option is set to \fBopen\fR (the default), \fBsecchan\fR will
+If this option is set to \fBopen\fR (the default), \fBovs\-openflowd\fR will
 take over responsibility for setting up flows in the local datapath
 when no message has been received from the controller for three times
 the inactivity probe interval (see below), or 45 seconds by default.
-In this ``fail open'' mode, \fBsecchan\fR causes the datapath to act
-like an ordinary MAC-learning switch.  \fBsecchan\fR will continue to
+In this ``fail open'' mode, \fBovs\-openflowd\fR causes the datapath to act
+like an ordinary MAC-learning switch.  \fBovs\-openflowd\fR will continue to
 retry connection to the controller in the background and, when the
 connection succeeds, it discontinues its fail-open behavior.
 
-If this option is set to \fBclosed\fR, then \fBsecchan\fR will not
+If this option is set to \fBclosed\fR, then \fBovs\-openflowd\fR will not
 set up flows on its own when the controller connection fails.
 
 .TP
 \fB--inactivity-probe=\fIsecs\fR
-When the secure channel is connected to the controller, the secure
-channel waits for a message to be received from the controller for
+When the OpenFlow switch is connected to the controller, the
+switch waits for a message to be received from the controller for
 \fIsecs\fR seconds before it sends a inactivity probe to the
 controller.  After sending the inactivity probe, if no response is
-received for an additional \fIsecs\fR seconds, the secure channel
+received for an additional \fIsecs\fR seconds, the switch
 assumes that the connection has been broken and attempts to reconnect.
 The default and the minimum value are both 5 seconds.
 
@@ -263,19 +263,19 @@ above).
 .TP
 \fB--max-idle=\fIsecs\fR|\fBpermanent\fR
 Sets \fIsecs\fR as the number of seconds that a flow set up by the
-secure channel will remain in the switch's flow table without any
+OpenFlow switch will remain in the switch's flow table without any
 matching packets being seen.  If \fBpermanent\fR is specified, which
-is not recommended, flows set up by the secure channel will never
+is not recommended, flows set up by the switch will never
 expire.  The default is 15 seconds.
 
-Most flows are set up by the OpenFlow controller, not by the secure
-channel.  This option affects only the following flows, which the
-secure channel sets up itself:
+Most flows are set up by the OpenFlow controller, not by the
+switch.  This option affects only the following flows, which the
+OpenFlow switch sets up itself:
 
 .RS
 .IP \(bu
-When \fB--fail=open\fR is specified, flows set up when the secure
-channel has not been able to contact the controller for the configured
+When \fB--fail=open\fR is specified, flows set up when the
+switch has not been able to contact the controller for the configured
 fail-open delay.
 
 .IP \(bu
@@ -306,14 +306,20 @@ multiple connection methods.
 
 .RS
 .TP
-\fBpssl:\fR[\fIport\fR]
+\fBpssl:\fR[\fIport\fR][\fB:\fIip\fR]
 Listens for SSL connections on \fIport\fR (default: 6633).  The
 \fB--private-key\fR, \fB--certificate\fR, and \fB--ca-cert\fR options
 are mandatory when this form is used.
+By default, \fB\*(PN\fR listens for connections to any local IP
+address, but \fIip\fR may be specified to listen only for connections
+to the given \fIip\fR.
 
 .TP
-\fBptcp:\fR[\fIport\fR]
+\fBptcp:\fR[\fIport\fR][\fB:\fIip\fR]
 Listens for TCP connections on \fIport\fR (default: 6633).
+By default, \fB\*(PN\fR listens for connections to any local IP
+address, but \fIip\fR may be specified to listen only for connections
+to the given \fIip\fR.
 
 .TP
 \fBpunix:\fIfile\fR
@@ -336,7 +342,7 @@ problems.
 
 .TP
 \fB--in-band\fR, \fB--out-of-band\fR
-Configures \fBsecchan\fR to operate in in-band or out-of-band control
+Configures \fBovs\-openflowd\fR to operate in in-band or out-of-band control
 mode (see \fBContacting the Controller\fR above).  When neither option
 is given, the default is in-band control.
 
@@ -395,7 +401,7 @@ Command names that include characters other than upper- and lower-case
 English letters, digits, and the underscore and hyphen characters are
 unconditionally disallowed.
 
-When the whitelist and blacklist permit a command name, \fBsecchan\fR
+When the whitelist and blacklist permit a command name, \fBovs\-openflowd\fR
 looks for a program with the same name as the command in the commands
 directory (see below).  Other directories are not searched.
 
@@ -429,7 +435,7 @@ the switch is connected to a trustworthy controller.
 .TP
 \fB--bootstrap-ca-cert=\fIcacert.pem\fR
 When \fIcacert.pem\fR exists, this option has the same effect as
-\fB-C\fR or \fB--ca-cert\fR.  If it does not exist, then \fBsecchan\fR
+\fB-C\fR or \fB--ca-cert\fR.  If it does not exist, then \fBovs\-openflowd\fR
 will attempt to obtain the CA certificate from the controller on its
 first SSL connection and save it to the named PEM file.  If it is
 successful, it will immediately drop the connection and reconnect, and
similarity index 98%
rename from secchan/main.c
rename to utilities/ovs-openflowd.c
index ee29f27..603e258 100644 (file)
 #include "compiler.h"
 #include "daemon.h"
 #include "dirs.h"
-#include "discovery.h"
 #include "dpif.h"
-#include "fail-open.h"
 #include "fault.h"
-#include "in-band.h"
 #include "leak-checker.h"
 #include "list.h"
 #include "netdev.h"
 #include "ofpbuf.h"
-#include "ofproto.h"
+#include "ofproto/ofproto.h"
 #include "openflow/openflow.h"
 #include "packets.h"
 #include "poll-loop.h"
 #include "rconn.h"
-#include "status.h"
 #include "svec.h"
 #include "timeval.h"
 #include "unixctl.h"
@@ -51,7 +47,7 @@
 #include "vconn.h"
 
 #include "vlog.h"
-#define THIS_MODULE VLM_secchan
+#define THIS_MODULE VLM_openflowd
 
 /* Behavior when the connection to the controller fails. */
 enum fail_mode {
@@ -198,9 +194,13 @@ main(int argc, char *argv[])
             ovs_fatal(error, "unrecoverable datapath error");
         }
         unixctl_server_run(unixctl);
+        dp_run();
+        netdev_run();
 
         ofproto_wait(ofproto);
         unixctl_server_wait(unixctl);
+        dp_wait();
+        netdev_wait();
         poll_block();
     }
 
@@ -496,7 +496,8 @@ parse_options(int argc, char *argv[], struct ofsettings *s)
 
     /* Set accept_controller_regex. */
     if (!s->accept_controller_re) {
-        s->accept_controller_re = vconn_ssl_is_configured() ? "^ssl:.*" : ".*";
+        s->accept_controller_re
+            = vconn_ssl_is_configured() ? "^ssl:.*" : "^tcp:.*";
     }
 
     /* Mode of operation. */
@@ -518,7 +519,7 @@ usage(void)
            "usage: %s [OPTIONS] DATAPATH [CONTROLLER]\n"
            "DATAPATH is a local datapath (e.g. \"dp0\").\n"
            "CONTROLLER is an active OpenFlow connection method; if it is\n"
-           "omitted, then secchan performs controller discovery.\n",
+           "omitted, then ovs-openflowd performs controller discovery.\n",
            program_name, program_name);
     vconn_usage(true, true, true);
     printf("\nOpenFlow options:\n"
@@ -538,7 +539,7 @@ usage(void)
            "                            closed: drop all packets\n"
            "                            open (default): act as learning switch\n"
            "  --inactivity-probe=SECS time between inactivity probes\n"
-           "  --max-idle=SECS         max idle for flows set up by secchan\n"
+           "  --max-idle=SECS         max idle for flows set up by switch\n"
            "  --max-backoff=SECS      max time between controller connection\n"
            "                          attempts (default: 8 seconds)\n"
            "  -l, --listen=METHOD     allow management connections on METHOD\n"
index 27dfccf..0f1c454 100644 (file)
@@ -325,6 +325,6 @@ Prints a help usage message and exits.
 
 .SH "SEE ALSO"
 
-.BR controller (8),
-.BR ovs\-pki\-cgi (8),
-.BR secchan (8)
+.BR ovs\-controller (8),
+.BR ovs\-openflowd (8),
+.BR ovs\-pki\-cgi (8)
index 6883731..8e27fc2 100644 (file)
@@ -13,8 +13,6 @@ vswitchd_ovs_vswitchd_SOURCES = \
        vswitchd/bridge.h \
        vswitchd/mgmt.c \
        vswitchd/mgmt.h \
-       vswitchd/port.c \
-       vswitchd/port.h \
        vswitchd/proc-net-compat.c \
        vswitchd/proc-net-compat.h \
        vswitchd/ovs-vswitchd.c \
@@ -22,7 +20,7 @@ vswitchd_ovs_vswitchd_SOURCES = \
        vswitchd/xenserver.c \
        vswitchd/xenserver.h
 vswitchd_ovs_vswitchd_LDADD = \
-       secchan/libsecchan.a \
+       ofproto/libofproto.a \
        lib/libopenvswitch.a \
        $(FAULT_LIBS) \
        $(SSL_LIBS)
index 7ec774a..ea0641e 100644 (file)
 #include "odp-util.h"
 #include "ofp-print.h"
 #include "ofpbuf.h"
+#include "ofproto/ofproto.h"
 #include "packets.h"
 #include "poll-loop.h"
 #include "port-array.h"
 #include "proc-net-compat.h"
 #include "process.h"
-#include "secchan/ofproto.h"
 #include "socket-util.h"
 #include "stp.h"
 #include "svec.h"
@@ -71,17 +71,18 @@ 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
@@ -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. */
@@ -202,10 +203,11 @@ static void bridge_fetch_dp_ifaces(struct bridge *);
 static void bridge_flush(struct bridge *);
 static void bridge_pick_local_hw_addr(struct bridge *,
                                       uint8_t ea[ETH_ADDR_LEN],
-                                      const char **devname);
+                                      struct iface **hw_addr_iface);
 static uint64_t bridge_pick_datapath_id(struct bridge *,
                                         const uint8_t bridge_ea[ETH_ADDR_LEN],
-                                        const char *devname);
+                                        struct iface *hw_addr_iface);
+static struct iface *bridge_get_local_iface(struct bridge *);
 static uint64_t dpid_from_hash(const void *, size_t nbytes);
 
 static void bridge_unixctl_fdb_show(struct unixctl_conn *, const char *args);
@@ -225,6 +227,7 @@ static struct port *port_from_dp_ifidx(const struct bridge *,
                                        uint16_t dp_ifidx);
 static void port_update_bond_compat(struct port *);
 static void port_update_vlan_compat(struct port *);
+static void port_update_bonding(struct port *);
 
 static void mirror_create(struct bridge *, const char *name);
 static void mirror_destroy(struct mirror *);
@@ -263,8 +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);
@@ -279,34 +282,40 @@ bridge_get_ifaces(struct svec *svec)
 void
 bridge_init(void)
 {
-    int retval;
-    int i;
-
-    bond_init();
+    struct svec dpif_names;
+    size_t i;
 
     unixctl_command_register("fdb/show", bridge_unixctl_fdb_show);
 
-    for (i = 0; i < DP_MAX; i++) {
-        struct dpif dpif;
-        char devname[16];
+    svec_init(&dpif_names);
+    dp_enumerate(&dpif_names);
+    for (i = 0; i < dpif_names.n; i++) {
+        const char *dpif_name = dpif_names.names[i];
+        struct dpif *dpif;
+        int retval;
 
-        sprintf(devname, "dp%d", i);
-        retval = dpif_open(devname, &dpif);
+        retval = dpif_open(dpif_name, &dpif);
         if (!retval) {
-            char dpif_name[IF_NAMESIZE];
-            if (dpif_get_name(&dpif, dpif_name, sizeof dpif_name)
-                || !cfg_has("bridge.%s.port", dpif_name)) {
-                dpif_delete(&dpif);
+            struct svec all_names;
+            size_t j;
+
+            svec_init(&all_names);
+            dpif_get_all_names(dpif, &all_names);
+            for (j = 0; j < all_names.n; j++) {
+                if (cfg_has("bridge.%s.port", all_names.names[j])) {
+                    goto found;
+                }
             }
-            dpif_close(&dpif);
-        } else if (retval != ENODEV) {
-            VLOG_ERR("failed to delete datapath dp%d: %s",
-                     i, strerror(retval));
+            dpif_delete(dpif);
+        found:
+            svec_destroy(&all_names);
+            dpif_close(dpif);
         }
     }
 
     unixctl_command_register("bridge/dump-flows", bridge_unixctl_dump_flows);
 
+    bond_init();
     bridge_reconfigure();
 }
 
@@ -348,43 +357,105 @@ bridge_configure_ssl(void)
      * the old certificate will still be trusted until vSwitch is
      * restarted.  We may want to address this in vconn's SSL library. */
     if (config_string_change("ssl.ca-cert", &cacert_file)
-            || (stat(cacert_file, &s) && errno == ENOENT)) {
+        || (cacert_file && stat(cacert_file, &s) && errno == ENOENT)) {
         vconn_ssl_set_ca_cert_file(cacert_file,
                                    cfg_get_bool(0, "ssl.bootstrap-ca-cert"));
     }
 }
 #endif
 
+/* iterate_and_prune_ifaces() callback function that opens the network device
+ * for 'iface', if it is not already open, and retrieves the interface's MAC
+ * address and carrier status. */
+static bool
+init_iface_netdev(struct bridge *br UNUSED, struct iface *iface,
+                  void *aux UNUSED)
+{
+    if (iface->netdev) {
+        return true;
+    } else if (!netdev_open(iface->name, NETDEV_ETH_TYPE_NONE,
+                            &iface->netdev)) {
+        netdev_get_carrier(iface->netdev, &iface->enabled);
+        return true;
+    } else {
+        /* If the network device can't be opened, then we're not going to try
+         * to do anything with this interface. */
+        return false;
+    }
+}
+
+static bool
+check_iface_dp_ifidx(struct bridge *br, struct iface *iface, void *aux UNUSED)
+{
+    if (iface->dp_ifidx >= 0) {
+        VLOG_DBG("%s has interface %s on port %d",
+                 dpif_name(br->dpif),
+                 iface->name, iface->dp_ifidx);
+        return true;
+    } else {
+        VLOG_ERR("%s interface not in %s, dropping",
+                 iface->name, dpif_name(br->dpif));
+        return false;
+    }
+}
+
+static bool
+set_iface_policing(struct bridge *br UNUSED, struct iface *iface,
+                   void *aux UNUSED)
+{
+    int rate = cfg_get_int(0, "port.%s.ingress.policing-rate", iface->name);
+    int burst = cfg_get_int(0, "port.%s.ingress.policing-burst", iface->name);
+    netdev_set_policing(iface->netdev, rate, burst);
+    return true;
+}
+
+/* Calls 'cb' for each interfaces in 'br', passing along the 'aux' argument.
+ * Deletes from 'br' all the interfaces for which 'cb' returns false, and then
+ * deletes from 'br' any ports that no longer have any interfaces. */
+static void
+iterate_and_prune_ifaces(struct bridge *br,
+                         bool (*cb)(struct bridge *, struct iface *,
+                                    void *aux),
+                         void *aux)
+{
+    size_t i, j;
+
+    for (i = 0; i < br->n_ports; ) {
+        struct port *port = br->ports[i];
+        for (j = 0; j < port->n_ifaces; ) {
+            struct iface *iface = port->ifaces[j];
+            if (cb(br, iface, aux)) {
+                j++;
+            } else {
+                iface_destroy(iface);
+            }
+        }
+
+        if (port->n_ifaces) {
+            i++;
+        } else  {
+            VLOG_ERR("%s port has no interfaces, dropping", port->name);
+            port_destroy(port);
+        }
+    }
+}
+
 void
 bridge_reconfigure(void)
 {
-    struct svec old_br, new_br, raw_new_br;
+    struct svec old_br, new_br;
     struct bridge *br, *next;
-    size_t i, j;
+    size_t i;
 
     COVERAGE_INC(bridge_reconfigure);
 
-    /* Collect old bridges. */
+    /* Collect old and new bridges. */
     svec_init(&old_br);
+    svec_init(&new_br);
     LIST_FOR_EACH (br, struct bridge, node, &all_bridges) {
         svec_add(&old_br, br->name);
     }
-
-    /* Collect new bridges. */
-    svec_init(&raw_new_br);
-    cfg_get_subsections(&raw_new_br, "bridge");
-    svec_init(&new_br);
-    for (i = 0; i < raw_new_br.n; i++) {
-        const char *name = raw_new_br.names[i];
-        if ((!strncmp(name, "dp", 2) && isdigit(name[2])) ||
-            (!strncmp(name, "nl:", 3) && isdigit(name[3]))) {
-            VLOG_ERR("%s is not a valid bridge name (bridges may not be "
-                     "named \"dp\" or \"nl:\" followed by a digit)", name);
-        } else {
-            svec_add(&new_br, name);
-        }
-    }
-    svec_destroy(&raw_new_br);
+    cfg_get_subsections(&new_br, "bridge");
 
     /* Get rid of deleted bridges and add new bridges. */
     svec_sort(&old_br);
@@ -425,16 +496,17 @@ bridge_reconfigure(void)
         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));
                 }
             }
         }
@@ -445,9 +517,8 @@ bridge_reconfigure(void)
         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);
@@ -457,42 +528,34 @@ bridge_reconfigure(void)
         bridge_get_all_ifaces(br, &want_ifaces);
         svec_diff(&want_ifaces, &cur_ifaces, &add_ifaces, NULL, NULL);
 
-        next_port_no = 1;
         for (i = 0; i < add_ifaces.n; i++) {
             const char *if_name = add_ifaces.names[i];
-            for (;;) {
-                bool internal;
-                int error;
-
-                /* It's an internal interface if it's marked that way, or if
-                 * it's a bonded interface for which we're faking up a network
-                 * device. */
-                internal = cfg_get_bool(0, "iface.%s.internal", if_name);
-                if (cfg_get_bool(0, "bonding.%s.fake-iface", if_name)) {
-                    struct port *port = port_lookup(br, if_name);
-                    if (port && port->n_ifaces > 1) {
-                        internal = true;
-                    }
-                }
+            bool internal;
+            int error;
 
-                /* Add to datapath. */
-                error = dpif_port_add(&br->dpif, if_name, next_port_no++,
-                                      internal ? ODP_PORT_INTERNAL : 0);
-                if (error != EEXIST) {
-                    if (next_port_no >= 256) {
-                        VLOG_ERR("ran out of valid port numbers on dp%u",
-                                 dpif_id(&br->dpif));
-                        goto out;
-                    }
-                    if (error) {
-                        VLOG_ERR("failed to add %s interface to dp%u: %s",
-                                 if_name, dpif_id(&br->dpif), strerror(error));
-                    }
-                    break;
+            /* It's an internal interface if it's marked that way, or if
+             * it's a bonded interface for which we're faking up a network
+             * device. */
+            internal = cfg_get_bool(0, "iface.%s.internal", if_name);
+            if (cfg_get_bool(0, "bonding.%s.fake-iface", if_name)) {
+                struct port *port = port_lookup(br, if_name);
+                if (port && port->n_ifaces > 1) {
+                    internal = true;
                 }
             }
+
+            /* Add to datapath. */
+            error = dpif_port_add(br->dpif, if_name,
+                                  internal ? ODP_PORT_INTERNAL : 0, NULL);
+            if (error == EXFULL) {
+                VLOG_ERR("ran out of valid port numbers on %s",
+                         dpif_name(br->dpif));
+                break;
+            } else if (error) {
+                VLOG_ERR("failed to add %s interface to %s: %s",
+                         if_name, dpif_name(br->dpif), strerror(error));
+            }
         }
-    out:
         svec_destroy(&cur_ifaces);
         svec_destroy(&want_ifaces);
         svec_destroy(&add_ifaces);
@@ -500,44 +563,22 @@ bridge_reconfigure(void)
     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 "
@@ -546,10 +587,11 @@ bridge_reconfigure(void)
             }
         }
 
-        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);
@@ -594,22 +636,24 @@ bridge_reconfigure(void)
         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);
@@ -652,7 +696,7 @@ bridge_pick_local_hw_addr(struct bridge *br, uint8_t ea[ETH_ADDR_LEN],
             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;
                 }
@@ -682,7 +726,7 @@ bridge_pick_local_hw_addr(struct bridge *br, uint8_t ea[ETH_ADDR_LEN],
             }
 
             /* 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",
@@ -697,13 +741,12 @@ bridge_pick_local_hw_addr(struct bridge *br, uint8_t ea[ETH_ADDR_LEN],
             !eth_addr_is_zero(iface_ea) &&
             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 {
@@ -714,13 +757,13 @@ bridge_pick_local_hw_addr(struct bridge *br, uint8_t ea[ETH_ADDR_LEN],
 
 /* 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
@@ -741,9 +784,9 @@ bridge_pick_datapath_id(struct bridge *br,
         return dpid;
     }
 
-    if (devname) {
+    if (hw_addr_iface) {
         int vlan;
-        if (!netdev_get_vlan_vid(devname, &vlan)) {
+        if (!netdev_get_vlan_vid(hw_addr_iface->netdev, &vlan)) {
             /*
              * A bridge whose MAC address is taken from a VLAN network device
              * (that is, a network device created with vconfig(8) or similar
@@ -853,6 +896,26 @@ bridge_flush(struct bridge *br)
         mac_learning_flush(br->ml);
     }
 }
+
+/* Returns the 'br' interface for the ODPP_LOCAL port, or null if 'br' has no
+ * such interface. */
+static struct iface *
+bridge_get_local_iface(struct bridge *br)
+{
+    size_t i, j;
+
+    for (i = 0; i < br->n_ports; i++) {
+        struct port *port = br->ports[i];
+        for (j = 0; j < port->n_ifaces; j++) {
+            struct iface *iface = port->ifaces[j];
+            if (iface->dp_ifidx == ODPP_LOCAL) {
+                return iface;
+            }
+        }
+    }
+
+    return NULL;
+}
 \f
 /* Bridge unixctl user interface functions. */
 static void
@@ -895,7 +958,7 @@ bridge_create(const char *name)
     br = xcalloc(1, sizeof *br);
 
     error = dpif_create(name, &br->dpif);
-    if (error == EEXIST) {
+    if (error == EEXIST || error == EBUSY) {
         error = dpif_open(name, &br->dpif);
         if (error) {
             VLOG_ERR("datapath %s already exists but cannot be opened: %s",
@@ -903,7 +966,7 @@ bridge_create(const char *name)
             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);
@@ -913,8 +976,8 @@ bridge_create(const char *name)
     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;
     }
@@ -931,7 +994,7 @@ bridge_create(const char *name)
 
     list_push_back(&all_bridges, &br->node);
 
-    VLOG_INFO("created bridge %s on dp%u", br->name, dpif_id(&br->dpif));
+    VLOG_INFO("created bridge %s on %s", br->name, dpif_name(br->dpif));
 
     return br;
 }
@@ -946,12 +1009,12 @@ bridge_destroy(struct bridge *br)
             port_destroy(br->ports[br->n_ports - 1]);
         }
         list_remove(&br->node);
-        error = dpif_delete(&br->dpif);
+        error = dpif_delete(br->dpif);
         if (error && error != ENOENT) {
-            VLOG_ERR("failed to delete dp%u: %s",
-                     dpif_id(&br->dpif), strerror(error));
+            VLOG_ERR("failed to delete %s: %s",
+                     dpif_name(br->dpif), strerror(error));
         }
-        dpif_close(&br->dpif);
+        dpif_close(br->dpif);
         ofproto_destroy(br->ofproto);
         free(br->controller);
         mac_learning_destroy(br->ml);
@@ -1043,13 +1106,29 @@ bridge_get_controller(const struct bridge *br)
     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);
@@ -1063,9 +1142,16 @@ bridge_reconfigure_one(struct bridge *br)
     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",
@@ -1100,28 +1186,7 @@ bridge_reconfigure_one(struct bridge *br)
 
     /* Check and delete duplicate interfaces. */
     svec_init(&ifaces);
-    for (i = 0; i < br->n_ports; ) {
-        struct port *port = br->ports[i];
-        for (j = 0; j < port->n_ifaces; ) {
-            struct iface *iface = port->ifaces[j];
-            if (svec_contains(&ifaces, iface->name)) {
-                VLOG_ERR("bridge %s: %s interface is on multiple ports, "
-                         "removing from %s",
-                         br->name, iface->name, port->name);
-                iface_destroy(iface);
-            } else {
-                svec_add(&ifaces, iface->name);
-                svec_sort(&ifaces);
-                j++;
-            }
-        }
-        if (!port->n_ifaces) {
-            VLOG_ERR("%s port has no interfaces, dropping", port->name);
-            port_destroy(port);
-        } else {
-            i++;
-        }
-    }
+    iterate_and_prune_ifaces(br, check_duplicate_ifaces, &ifaces);
     svec_destroy(&ifaces);
 
     /* Delete all flows if we're switching from connected to standalone or vice
@@ -1202,9 +1267,8 @@ bridge_reconfigure_controller(struct bridge *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)
@@ -1212,33 +1276,32 @@ bridge_reconfigure_controller(struct bridge *br)
             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);
             }
         }
 
@@ -1368,17 +1431,17 @@ bridge_fetch_dp_ifaces(struct bridge *br)
     }
     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;
@@ -1988,7 +2051,6 @@ bridge_port_changed_ofhook_cb(enum ofp_port_reason reason,
 
         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);
@@ -2980,7 +3042,7 @@ port_update_bond_compat(struct port *port)
         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);
@@ -3011,7 +3073,8 @@ port_update_vlan_compat(struct port *port)
                 && p->n_ifaces
                 && (!vlandev_name || strcmp(p->name, vlandev_name) <= 0))
             {
-                const uint8_t *ea = p->ifaces[0]->mac;
+                uint8_t ea[ETH_ADDR_LEN];
+                netdev_get_etheraddr(p->ifaces[0]->netdev, ea);
                 if (!eth_addr_is_multicast(ea) &&
                     !eth_addr_is_reserved(ea) &&
                     !eth_addr_is_zero(ea)) {
@@ -3037,18 +3100,7 @@ iface_create(struct port *port, const char *name)
     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,
@@ -3061,7 +3113,6 @@ iface_create(struct port *port, const char *name)
 
     VLOG_DBG("attached network device %s to port %s", iface->name, port->name);
 
-    port_update_bonding(port);
     bridge_flush(port->bridge);
 }
 
@@ -3081,6 +3132,7 @@ iface_destroy(struct iface *iface)
         del = port->ifaces[iface->port_ifidx] = port->ifaces[--port->n_ifaces];
         del->port_ifidx = iface->port_ifidx;
 
+        netdev_close(iface->netdev);
         free(iface->name);
         free(iface);
 
@@ -3090,7 +3142,6 @@ iface_destroy(struct iface *iface)
             bond_send_learning_packets(port);
         }
 
-        port_update_bonding(port);
         bridge_flush(port->bridge);
     }
 }
@@ -3429,23 +3480,25 @@ brstp_send_bpdu(struct ofpbuf *pkt, int port_no, void *br_)
     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);
 }
index 68965ca..cd911ca 100644 (file)
@@ -62,4 +62,4 @@ loaded.
 .BR ovs\-appctl (8),
 .BR ovs\-vswitchd (8),
 .BR ovs\-vswitchd.conf (5),
-\fBINSTALL\fR in the Open vSwitch distribution.
+\fBINSTALL.bridge\fR in the Open vSwitch distribution.
index 50ed632..d351c05 100644 (file)
@@ -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"
@@ -304,7 +303,6 @@ static void
 prune_ports(void)
 {
     int i, j;
-    int error;
     struct svec bridges, delete;
 
     if (cfg_lock(NULL, 0)) {
@@ -324,7 +322,6 @@ prune_ports(void)
         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.
@@ -335,14 +332,10 @@ prune_ports(void)
                 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);
@@ -364,29 +357,6 @@ prune_ports(void)
     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)
 {
@@ -685,8 +655,14 @@ handle_fdb_query_cmd(struct ofpbuf *buffer)
     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);
@@ -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);
@@ -1008,7 +983,7 @@ rtnl_recv_update(void)
                 return;
             }
 
-            if (netdev_nodev_get_flags(port_name, &flags) == ENODEV) {
+            if (!netdev_exists(port_name)) {
                 /* Network device is really gone. */
                 struct svec ports;
 
@@ -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 
@@ -1137,6 +1113,7 @@ main(int argc, char *argv[])
 
         nl_sock_wait(brc_sock, POLLIN);
         unixctl_server_wait(unixctl);
+        netdev_wait();
         poll_block();
     }
 
index 28e55ba..6941bdf 100644 (file)
@@ -1,16 +1,16 @@
-.TH ovs\-vswitchd 8 "March 2009" "Open vSwitch" "OpenVSwitch Manual"
+.TH ovs\-vswitchd 8 "June 2009" "Open vSwitch" "Open vSwitch Manual"
 .ds PN ovs\-vswitchd
 .
 .SH NAME
-ovs\-vswitchd \- virtual switch daemon
+ovs\-vswitchd \- Open vSwitch daemon
 .
 .SH SYNOPSIS
 .B ovs\-vswitchd
 \fIconfig\fR
 .
 .SH DESCRIPTION
-A daemon that manages and controls any number of virtual switches on
-the local machine.
+A daemon that manages and controls any number of Open vSwitch switches 
+on the local machine.
 .PP
 The mandatory \fIconfig\fR argument specifies a configuration file.
 For a description of \fBovs\-vswitchd\fR configuration syntax, see
@@ -22,8 +22,8 @@ operates switching across each bridge described in its configuration
 files.  If a logfile was specified on the command line it will also 
 be opened or reopened.
 .PP
-\fBovs\-vswitchd\fR virtual switches may be configured with any of the
-following features:
+\fBovs\-vswitchd\fR switches may be configured with any of the following 
+features:
 .
 .IP \(bu
 L2 switching with MAC learning.
@@ -46,17 +46,17 @@ Connectivity to an external OpenFlow controller, such as NOX.
 .
 .PP
 Only a single instance of \fBovs\-vswitchd\fR is intended to run at a time.
-A single \fBovs\-vswitchd\fR can manage any number of virtual switches, up
+A single \fBovs\-vswitchd\fR can manage any number of switch instances, up
 to the maximum number of supported Open vSwitch datapaths.
 .PP
-\fBovs\-vswitchd\fR does all the necessary management of OpenVSwitch datapaths
+\fBovs\-vswitchd\fR does all the necessary management of Open vSwitch datapaths
 itself.  Thus, external tools, such \fBovs\-dpctl\fR(8), are not needed for
 managing datapaths in conjunction with \fBovs\-vswitchd\fR, and their use
 to modify datapaths when \fBovs\-vswitchd\fR is running can interfere with
 its operation.  (\fBovs\-dpctl\fR may still be useful for diagnostics.)
 .PP
 An Open vSwitch datapath kernel module must be loaded for \fBovs\-vswitchd\fR
-to be useful.  Please refer to the \fBINSTALL\fR file included in the
+to be useful.  Please refer to the \fBINSTALL.Linux\fR file included in the
 Open vSwitch distribution for instructions on how to build and load
 the Open vSwitch kernel module.
 .PP
@@ -84,4 +84,4 @@ Only Linux 2.6.\fIx\fR is currently supported.
 .BR ovs\-appctl (8),
 .BR ovs\-vswitchd.conf (5),
 .BR ovs\-brcompatd (8),
-\fBINSTALL\fR in the Open vSwitch distribution.
+\fBINSTALL.Linux\fR in the Open vSwitch distribution.
index 01645ad..28491fc 100644 (file)
 #include "command-line.h"
 #include "compiler.h"
 #include "daemon.h"
+#include "dpif.h"
 #include "fault.h"
 #include "leak-checker.h"
 #include "mgmt.h"
+#include "netdev.h"
 #include "ovs-vswitchd.h"
 #include "poll-loop.h"
-#include "port.h"
 #include "proc-net-compat.h"
 #include "process.h"
 #include "signals.h"
@@ -86,7 +87,6 @@ main(int argc, char *argv[])
     }
     mgmt_init();
     bridge_init();
-    port_init();
     mgmt_reconfigure();
 
     need_reconfigure = false;
@@ -103,6 +103,8 @@ main(int argc, char *argv[])
             need_reconfigure = true;
         }
         unixctl_server_run(unixctl);
+        dp_run();
+        netdev_run();
 
         if (need_reconfigure) {
             poll_immediate_wake();
@@ -111,6 +113,8 @@ main(int argc, char *argv[])
         mgmt_wait();
         bridge_wait();
         unixctl_server_wait(unixctl);
+        dp_wait();
+        netdev_wait();
         poll_block();
     }
 
@@ -133,7 +137,6 @@ reconfigure(void)
     cfg_read();
     bridge_reconfigure();
     mgmt_reconfigure();
-    port_reconfigure();
 
     for (i = 0; i < n_conns; i++) {
         unixctl_command_reply(conns[i], 202, NULL);
@@ -234,7 +237,7 @@ parse_options(int argc, char *argv[])
 static void
 usage(void)
 {
-    printf("%s: virtual switch daemon\n"
+    printf("%s: Open vSwitch daemon\n"
            "usage: %s [OPTIONS] CONFIG\n"
            "CONFIG is a configuration file in ovs-vswitchd.conf(5) format.\n",
            program_name, program_name);
index d613419..0dbf94d 100644 (file)
 .  RE
 .  PP
 ..
-.TH ovs\-vswitchd.conf 5 "April 2009" "Open vSwitch" "OpenVSwitch Manual"
+.TH ovs\-vswitchd.conf 5 "June 2009" "Open vSwitch" "Open vSwitch Manual"
 .
 .SH NAME
 ovs\-vswitchd.conf \- configuration file for \fBovs\-vswitchd\fR
 .
 .SH DESCRIPTION
 This manual page describes the syntax for the configuration file used 
-by \fBovs\-vswitchd\fR(8), the virtual switch daemon.
+by \fBovs\-vswitchd\fR(8), the Open vSwitch daemon.
 .PP
 The configuration file is based on key-value pairs, which are given
 one per line in the form \fIkey\fB=\fIvalue\fR.  Each \fIkey\fR
@@ -50,14 +50,13 @@ configure \fBovs\-vswitchd\fR.
 .SS "Bridge Configuration"
 A bridge (switch) with a given \fIname\fR is configured by specifying
 the names of its network devices as values for key
-\fBbridge.\fIname\fB.port\fR.  (The specified \fIname\fR may not begin
-with \fBdp\fR or \fBnl:\fR followed by a digit.)
+\fBbridge.\fIname\fB.port\fR.
 .PP
 The names given on \fBbridge.\fIname\fB.port\fR must be the names of
 existing network devices, except for ``internal ports.''  An internal
 port is a simulated network device that receives traffic only
-through the virtual switch and switches any traffic sent it through
-virtual switch.  An internal port may configured with an IP address,
+through the switch and switches any traffic sent it through the
+switch.  An internal port may configured with an IP address,
 etc. using the usual system tools (e.g. \fBifconfig\fR, \fBip\fR).  To
 designate network device \fInetdev\fR as an internal port, add
 \fBiface.\fInetdev\fB.internal=true\fR to the configuration file.
@@ -339,7 +338,7 @@ This can be overridden with the \fBnetflow.\fIbridge\fB.engine-type\fR and
 \fBnetflow.\fIbridge\fB.engine-id\fR, respectively.  Each takes a value
 between 0 and 255, inclusive. 
 
-Many NetFlow collectors do not expect multiple virtual switches to be
+Many NetFlow collectors do not expect multiple switches to be
 sending messages from the same host, and they do not store the engine
 information which could be used to disambiguate the traffic.  To prevent
 flows from multiple switches appearing as if they came on the interface,
@@ -413,7 +412,7 @@ switch will perform all configured bridging and switching locally.
 .TP
 \fBdiscover\fR
 Use controller discovery to find the local OpenFlow controller.
-Refer to \fBsecchan\fR(8) for information on how to configure a DHCP
+Refer to \fB\ovs\-openflowd\fR(8) for information on how to configure a DHCP
 server to support controller discovery.  The following additional
 options control the discovery process:
 .
@@ -426,8 +425,8 @@ the regular expression will be accepted.
 .IP
 The default regular expression is \fBssl:.*\fR, meaning that only SSL
 controller connections will be accepted, when SSL is configured (see
-\fBSSL Configuration\fR), and \fB.*\fR otherwise, meaning that any
-controller will be accepted.
+\fBSSL Configuration\fR), and \fBtcp:.*\fR otherwise, meaning that only
+TCP controller connections will be accepted.
 .IP
 The regular expression is implicitly anchored at the beginning of the
 controller location string, as if it begins with \fB^\fR.
@@ -472,7 +471,7 @@ not in use, the following additional settings are honored:
 By default, or if this is set to \fBtrue\fR, \fBovs\-vswitchd\fR connects
 to the controller in-band.  If this is set to \fBfalse\fR,
 \fBovs\-vswitchd\fR connects to the controller out-of-band.  Refer to
-\fBsecchan\fR(8) for a description of in-band and out-of-band control.
+\fBovs\-openflowd\fR(8) for a description of in-band and out-of-band control.
 .IP "\fBbridge.\fIname\fB.controller.ip=\fIip\fR"
 If specified, the IP address to configure on the bridge's local port.
 .IP "\fBbridge.\fIname\fB.controller.netmask=\fInetmask\fR"
@@ -490,11 +489,11 @@ This optional setting may be set to \fIsecs\fR, a number of seconds.
 The minimum value of \fIsecs\fR is 5 seconds.  The default is taken
 from \fBmgmt.inactivity-probe\fR (see above).
 .IP
-When the virtual switch is connected to the controller, it waits for a
+When the switch is connected to the controller, it waits for a
 message to be received from the controller for \fIsecs\fR seconds
 before it sends a inactivity probe to the controller.  After sending
 the inactivity probe, if no response is received for an additional
-\fIsecs\fR seconds, the secure channel assumes that the connection has
+\fIsecs\fR seconds, \fBovs-vswitchd\fR assumes that the connection has
 been broken and attempts to reconnect.
 .IP
 Changing the inactivity probe interval also changes the interval
@@ -502,7 +501,7 @@ before entering standalone mode (see below).
 .IP "\fBbridge.\fIname\fB.controller.fail-mode=\fBstandalone\fR|\fBsecure\fR"
 .IQ "\fBmgmt.fail-mode=standalone\fR|\fBsecure\fR"
 When a controller is configured, it is, ordinarily, responsible for
-setting up all flows on the virtual switch.  Thus, if the connection to
+setting up all flows on the switch.  Thus, if the connection to
 the controller fails, no new network connections can be set up.  If
 the connection to the controller stays down long enough, no packets
 can pass through the switch at all.
@@ -527,7 +526,7 @@ connection attempts starts at 1 second and doubles on each failing
 attempt until it reaches the maximum.  The default maximum backoff
 time is taken from \fBmgmt.max-backoff\fR.
 .ST "Controller Rate-Limiting"
-These settings configure how the virtual switch applies a ``token
+These settings configure how the switch applies a ``token
 bucket'' to limit the rate at which packets in unknown flows are
 forwarded to the OpenFlow controller for flow-setup processing.  This
 feature prevents a single bridge from overwhelming a controller.
@@ -580,24 +579,23 @@ When \fBovs\-vswitchd\fR is configured to connect over SSL for management or
 for controller connectivity, the following settings are required:
 .TP
 \fBssl.private-key=\fIprivkey.pem\fR
-Specifies a PEM file containing the private key used as the virtual
+Specifies a PEM file containing the private key used as the 
 switch's identity for SSL connections to the controller.
 .TP
 \fBssl.certificate=\fIcert.pem\fR
 Specifies a PEM file containing a certificate, signed by the
 certificate authority (CA) used by the controller and manager, that
-certifies the virtual switch's private key, identifying a trustworthy
+certifies the switch's private key, identifying a trustworthy
 switch.
 .TP
 \fBssl.ca-cert=\fIcacert.pem\fR
 Specifies a PEM file containing the CA certificate used to verify that
-the virtual switch is connected to a trustworthy controller.
+the switch is connected to a trustworthy controller.
 .PP
 These files are read only once, at \fBovs\-vswitchd\fR startup time.  If
 their contents change, \fBovs\-vswitchd\fR must be killed and restarted.
 .PP
-These SSL settings apply to all SSL connections made by the virtual
-switch.
+These SSL settings apply to all SSL connections made by the switch.
 .ST "CA Certificate Bootstrap"
 Ordinarily, all of the files named in the SSL configuration must exist
 when \fBovs\-vswitchd\fR starts.  However, if \fBssl.bootstrap-ca-cert\fR
@@ -635,8 +633,11 @@ Listens for connections on the Unix domain server socket named \fIfile\fR.
 Listens for SSL connections on \fIport\fR (default: 6633).  SSL must
 be configured when this form is used (see \fBSSL Configuration\fR,
 above).
-.IP "\fBptcp:\fR[\fIport\fR]"
+.IP "\fBptcp:\fR[\fIport\fR][\fB:\fIip\fR]"
 Listens for TCP connections on \fIport\fR (default: 6633).
+By default, \fB\ovs\-vswitchd\fR listens for connections to any local
+IP address, but \fIip\fR may be specified to limit connections to the
+specified local \fIip\fR.
 .RE
 To entirely disable listening for management connections, set
 \fBbridge.\fIname\fB.openflow.listeners\fR to the single value
diff --git a/vswitchd/port.c b/vswitchd/port.c
deleted file mode 100644 (file)
index 147b9d4..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-/* 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 "bridge.h"
-#include "cfg.h"
-#include "netdev.h"
-#include "ovs-vswitchd.h"
-#include "port.h"
-#include "svec.h"
-
-#define THIS_MODULE VLM_port
-#include "vlog.h"
-
-static int
-set_ingress_policing(const char *port_name) 
-{
-    int kbits_rate = cfg_get_int(0, "port.%s.ingress.policing-rate", 
-            port_name);
-    int kbits_burst = cfg_get_int(0, "port.%s.ingress.policing-burst", 
-            port_name);
-
-    return netdev_nodev_set_policing(port_name, kbits_rate, kbits_burst);
-}
-
-void
-port_init(void)
-{
-    port_reconfigure();
-}
-
-void
-port_reconfigure(void)
-{
-    struct svec ports;
-    int i;
-
-    svec_init(&ports);
-    bridge_get_ifaces(&ports);
-    for (i=0; i<ports.n; i++) {
-        set_ingress_policing(ports.names[i]);
-    }
-}
diff --git a/vswitchd/port.h b/vswitchd/port.h
deleted file mode 100644 (file)
index 2055912..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-/* Copyright (c) 2009 Nicira Networks
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef VSWITCHD_PORT_H
-#define VSWITCHD_PORT_H 1
-
-void port_init(void);
-void port_reconfigure(void);
-
-#endif /* port.h */
index 301251f..61027c5 100755 (executable)
@@ -62,8 +62,9 @@ import syslog
 import traceback
 import time
 import re
-import pickle
 import random
+from xml.dom.minidom import getDOMImplementation
+from xml.dom.minidom import parse as parseXML
 
 output_directory = None
 
@@ -272,11 +273,215 @@ def get_netdev_by_mac(mac):
                 return device
     return None
 
+#
+# Helper functions for encoding/decoding database attributes to/from XML.
+#
+def str_to_xml(xml, parent, tag, val):
+    e = xml.createElement(tag)
+    parent.appendChild(e)
+    v = xml.createTextNode(val)
+    e.appendChild(v)
+def str_from_xml(n):
+    def getText(nodelist):
+        rc = ""
+        for node in nodelist:
+            if node.nodeType == node.TEXT_NODE:
+                rc = rc + node.data
+        return rc
+    return getText(n.childNodes).strip()
+
+
+def bool_to_xml(xml, parent, tag, val):
+    if val:
+        str_to_xml(xml, parent, tag, "True")
+    else:
+        str_to_xml(xml, parent, tag, "False")
+def bool_from_xml(n):
+    s = str_from_xml(n)
+    if s == "True":
+        return True
+    elif s == "False":
+        return False
+    else:
+        raise Error("Unknown boolean value %s" % s);
+
+def strlist_to_xml(xml, parent, ltag, itag, val):
+    e = xml.createElement(ltag)
+    parent.appendChild(e)
+    for v in val:
+        c = xml.createElement(itag)
+        e.appendChild(c)
+        cv = xml.createTextNode(v)
+        c.appendChild(cv)
+def strlist_from_xml(n, ltag, itag):
+    ret = []
+    for n in n.childNodes:
+        if n.nodeName == itag:
+            ret.append(str_from_xml(n))
+    return ret
+
+def otherconfig_to_xml(xml, parent, val, attrs):
+    otherconfig = xml.createElement("other_config")
+    parent.appendChild(otherconfig)
+    for n,v in val.items():
+        if not n in attrs:
+            raise Error("Unknown other-config attribute: %s" % n)
+        str_to_xml(xml, otherconfig, n, v)
+def otherconfig_from_xml(n, attrs):
+    ret = {}
+    for n in n.childNodes:
+        if n.nodeName in attrs:
+            ret[n.nodeName] = str_from_xml(n)
+    return ret
+
+#
+# Definitions of the database objects (and their attributes) used by interface-reconfigure.
+#
+# Each object is defined by a dictionary mapping an attribute name in
+# the xapi database to a tuple containing two items:
+#  - a function which takes this attribute and encodes it as XML.
+#  - a function which takes XML and decocdes it into a value.
+#
+# other-config attributes are specified as a simple array of strings
+
+PIF_XML_TAG = "pif"
+VLAN_XML_TAG = "vlan"
+BOND_XML_TAG = "bond"
+NETWORK_XML_TAG = "network"
+
+ETHTOOL_OTHERCONFIG_ATTRS = ['ethtool-%s' % x for x in 'autoneg', 'speed', 'duplex', 'rx', 'tx', 'sg', 'tso', 'ufo', 'gso' ]
+
+PIF_ATTRS = { 'uuid': (str_to_xml,str_from_xml),
+              'management': (bool_to_xml,bool_from_xml),
+              'network': (str_to_xml,str_from_xml),
+              'device': (str_to_xml,str_from_xml),
+              'bond_master_of': (lambda x, p, t, v: strlist_to_xml(x, p, 'bond_master_of', 'slave', v),
+                                 lambda n: strlist_from_xml(n, 'bond_master_of', 'slave')),
+              'bond_slave_of': (str_to_xml,str_from_xml),
+              'VLAN': (str_to_xml,str_from_xml),
+              'VLAN_master_of': (str_to_xml,str_from_xml),
+              'VLAN_slave_of': (lambda x, p, t, v: strlist_to_xml(x, p, 'VLAN_slave_of', 'master', v),
+                                lambda n: strlist_from_xml(n, 'VLAN_slave_Of', 'master')),
+              'ip_configuration_mode': (str_to_xml,str_from_xml),
+              'IP': (str_to_xml,str_from_xml),
+              'netmask': (str_to_xml,str_from_xml),
+              'gateway': (str_to_xml,str_from_xml),
+              'DNS': (str_to_xml,str_from_xml),
+              'MAC': (str_to_xml,str_from_xml),
+              'other_config': (lambda x, p, t, v: otherconfig_to_xml(x, p, v, PIF_OTHERCONFIG_ATTRS),
+                               lambda n: otherconfig_from_xml(n, PIF_OTHERCONFIG_ATTRS)),
+            }
+
+PIF_OTHERCONFIG_ATTRS = [ 'domain', 'peerdns', 'defaultroute', 'mtu', 'static-routes' ] + \
+                        [ 'bond-%s' % x for x in 'mode', 'miimon', 'downdelay', 'updelay', 'use_carrier' ] + \
+                        ETHTOOL_OTHERCONFIG_ATTRS
+
+VLAN_ATTRS = { 'uuid': (str_to_xml,str_from_xml),
+               'tagged_PIF': (str_to_xml,str_from_xml),
+               'untagged_PIF': (str_to_xml,str_from_xml),
+             }
+    
+BOND_ATTRS = { 'uuid': (str_to_xml,str_from_xml),
+               'master': (str_to_xml,str_from_xml),
+               'slaves': (lambda x, p, t, v: strlist_to_xml(x, p, 'slaves', 'slave', v),
+                          lambda n: strlist_from_xml(n, 'slaves', 'slave')),
+             }
+
+NETWORK_ATTRS = { 'uuid': (str_to_xml,str_from_xml),
+                  'bridge': (str_to_xml,str_from_xml),
+                  'PIFs': (lambda x, p, t, v: strlist_to_xml(x, p, 'PIFs', 'PIF', v),
+                           lambda n: strlist_from_xml(n, 'PIFs', 'PIF')),
+                  'other_config': (lambda x, p, t, v: otherconfig_to_xml(x, p, v, NETWORK_OTHERCONFIG_ATTRS),
+                                   lambda n: otherconfig_from_xml(n, NETWORK_OTHERCONFIG_ATTRS)),
+                }
+
+NETWORK_OTHERCONFIG_ATTRS = [ 'mtu', 'static-routes' ] + ETHTOOL_OTHERCONFIG_ATTRS
+
 class DatabaseCache(object):
+    def __read_xensource_inventory(self):
+        filename = "/etc/xensource-inventory"
+        f = open(filename, "r")
+        lines = [x.strip("\n") for x in f.readlines()]
+        f.close()
+
+        defs = [ (l[:l.find("=")], l[(l.find("=") + 1):]) for l in lines ]
+        defs = [ (a, b.strip("'")) for (a,b) in defs ]
+
+        return dict(defs)
+    def __pif_on_host(self,pif):
+        return self.__pifs.has_key(pif)
+
+    def __get_pif_records_from_xapi(self, session, host):
+        self.__pifs = {}
+        for (p,rec) in session.xenapi.PIF.get_all_records().items():
+            if rec['host'] != host:
+                continue
+            self.__pifs[p] = {}
+            for f in PIF_ATTRS:
+                self.__pifs[p][f] = rec[f]
+            self.__pifs[p]['other_config'] = {}
+            for f in PIF_OTHERCONFIG_ATTRS:
+                if not rec['other_config'].has_key(f): continue
+                self.__pifs[p]['other_config'][f] = rec['other_config'][f]
+
+    def __get_vlan_records_from_xapi(self, session):
+        self.__vlans = {}
+        for v in session.xenapi.VLAN.get_all():
+            rec = session.xenapi.VLAN.get_record(v)
+            if not self.__pif_on_host(rec['untagged_PIF']):
+                continue
+            self.__vlans[v] = {}
+            for f in VLAN_ATTRS:
+                self.__vlans[v][f] = rec[f]
+
+    def __get_bond_records_from_xapi(self, session):
+        self.__bonds = {}
+        for b in session.xenapi.Bond.get_all():
+            rec = session.xenapi.Bond.get_record(b)
+            if not self.__pif_on_host(rec['master']):
+                continue
+            self.__bonds[b] = {}
+            for f in BOND_ATTRS:
+                self.__bonds[b][f] = rec[f]
+
+    def __get_network_records_from_xapi(self, session):
+        self.__networks = {}
+        for n in session.xenapi.network.get_all():
+            rec = session.xenapi.network.get_record(n)
+            self.__networks[n] = {}
+            for f in NETWORK_ATTRS:
+                self.__networks[n][f] = rec[f]
+            self.__networks[n]['other_config'] = {}
+            for f in NETWORK_OTHERCONFIG_ATTRS:
+                if not rec['other_config'].has_key(f): continue
+                self.__networks[n]['other_config'][f] = rec['other_config'][f]
+
+    def __to_xml(self, xml, parent, key, ref, rec, attrs):
+        """Encode a database object as XML"""
+        e = xml.createElement(key)
+        parent.appendChild(e)
+        if ref:
+            e.setAttribute('ref', ref)
+
+        for n,v in rec.items():
+            if attrs.has_key(n):
+                h,_ = attrs[n]
+                h(xml, e, n, v)
+            else:
+                raise Error("Unknown attribute %s" % n)
+    def __from_xml(self, e, attrs):
+        """Decode a database object from XML"""
+        ref = e.attributes['ref'].value
+        rec = {}
+        for n in e.childNodes:
+            if n.nodeName in attrs:
+                _,h = attrs[n.nodeName]
+                rec[n.nodeName] = h(n)
+        return (ref,rec)
+    
     def __init__(self, session_ref=None, cache_file=None):
         if session_ref and cache_file:
             raise Error("can't specify session reference and cache file")
-
         if cache_file == None:
             session = XenAPI.xapi_local()
 
@@ -287,32 +492,70 @@ class DatabaseCache(object):
                 session._session = session_ref
 
             try:
-                self.__vlans = session.xenapi.VLAN.get_all_records()
-                self.__bonds = session.xenapi.Bond.get_all_records()
-                self.__pifs = session.xenapi.PIF.get_all_records()
-                self.__networks = session.xenapi.network.get_all_records()
+                
+                inventory = self.__read_xensource_inventory()
+                assert(inventory.has_key('INSTALLATION_UUID'))
+                log("host uuid is %s" % inventory['INSTALLATION_UUID'])
+                
+                host = session.xenapi.host.get_by_uuid(inventory['INSTALLATION_UUID'])
+                
+                self.__get_pif_records_from_xapi(session, host)
+
+                self.__get_vlan_records_from_xapi(session)
+                self.__get_bond_records_from_xapi(session)
+                self.__get_network_records_from_xapi(session)
             finally:
                 if not session_ref:
                     session.xenapi.session.logout()
         else:
             log("Loading xapi database cache from %s" % cache_file)
-            f = open(cache_file, 'r')
-            members = pickle.load(f)
-            self.extras = pickle.load(f)
-            f.close()
 
-            self.__vlans = members['vlans']
-            self.__bonds = members['bonds']
-            self.__pifs = members['pifs']
-            self.__networks = members['networks']
+            xml = parseXML(cache_file)
+
+            self.__pifs = {}
+            self.__bonds = {}
+            self.__vlans = {}
+            self.__networks = {}
 
-    def save(self, cache_file, extras):
+            assert(len(xml.childNodes) == 1)
+            toplevel = xml.childNodes[0]
+            
+            assert(toplevel.nodeName == "xenserver-network-configuration")
+            
+            for n in toplevel.childNodes:
+                if n.nodeName == "#text":
+                    pass
+                elif n.nodeName == PIF_XML_TAG:
+                    (ref,rec) = self.__from_xml(n, PIF_ATTRS)
+                    self.__pifs[ref] = rec
+                elif n.nodeName == BOND_XML_TAG:
+                    (ref,rec) = self.__from_xml(n, BOND_ATTRS)
+                    self.__bonds[ref] = rec
+                elif n.nodeName == VLAN_XML_TAG:
+                    (ref,rec) = self.__from_xml(n, VLAN_ATTRS)
+                    self.__vlans[ref] = rec
+                elif n.nodeName == NETWORK_XML_TAG:
+                    (ref,rec) = self.__from_xml(n, NETWORK_ATTRS)
+                    self.__networks[ref] = rec
+                else:
+                    raise Error("Unknown XML element %s" % n.nodeName)
+
+    def save(self, cache_file):
+
+        xml = getDOMImplementation().createDocument(
+            None, "xenserver-network-configuration", None)
+        for (ref,rec) in self.__pifs.items():
+            self.__to_xml(xml, xml.documentElement, PIF_XML_TAG, ref, rec, PIF_ATTRS)
+        for (ref,rec) in self.__bonds.items():
+            self.__to_xml(xml, xml.documentElement, BOND_XML_TAG, ref, rec, BOND_ATTRS)
+        for (ref,rec) in self.__vlans.items():
+            self.__to_xml(xml, xml.documentElement, VLAN_XML_TAG, ref, rec, VLAN_ATTRS)
+        for (ref,rec) in self.__networks.items():
+            self.__to_xml(xml, xml.documentElement, NETWORK_XML_TAG, ref, rec,
+                          NETWORK_ATTRS)
+            
         f = open(cache_file, 'w')
-        pickle.dump({'vlans': self.__vlans,
-                     'bonds': self.__bonds,
-                     'pifs': self.__pifs,
-                     'networks': self.__networks}, f)
-        pickle.dump(extras, f)
+        f.write(xml.toprettyxml())
         f.close()
 
     def get_pif_by_uuid(self, uuid):
@@ -326,33 +569,12 @@ class DatabaseCache(object):
 
         return pifs[0]
 
-    def get_pifs_by_record(self, record):
-        """record is partial pif record.
-        Get the pif(s) whose record matches.
-        """
-        def match(pifrec):
-            for key in record:
-                if record[key] != pifrec[key]:
-                    return False
-            return True
-            
+    def get_pifs_by_device(self, device):
         return map(lambda (ref,rec): ref,
-                   filter(lambda (ref,rec): match(rec),
+                   filter(lambda (ref,rec): rec['device'] == device,
                           self.__pifs.items()))
 
-    def get_pif_by_record(self, record):
-        """record is partial pif record.
-        Get the pif whose record matches.
-        """
-        pifs = self.get_pifs_by_record(record)
-        if len(pifs) == 0:
-            raise Error("No matching PIF \"%s\"" % str(record))
-        elif len(pifs) > 1:
-            raise Error("Multiple matching PIFs \"%s\"" % str(record))
-
-        return pifs[0]
-
-    def get_pif_by_bridge(self, host, bridge):
+    def get_pif_by_bridge(self, bridge):
         networks = map(lambda (ref,rec): ref,
                        filter(lambda (ref,rec): rec['bridge'] == bridge,
                               self.__networks.items()))
@@ -364,32 +586,29 @@ class DatabaseCache(object):
             nwrec = self.get_network_record(network)
             for pif in nwrec['PIFs']:
                 pifrec = self.get_pif_record(pif)
-                if pifrec['host'] != host:
-                    continue
                 if answer:
-                    raise Error("Multiple PIFs on %s for network %s" % (host, bridge))
+                    raise Error("Multiple PIFs on host for network %s" % (bridge))
                 answer = pif
         if not answer:
-            raise Error("No PIF on %s for network %s" % (host, bridge))
+            raise Error("No PIF on host for network %s" % (bridge))
         return answer
 
     def get_pif_record(self, pif):
         if self.__pifs.has_key(pif):
             return self.__pifs[pif]
-        raise Error("Unknown PIF \"%s\"" % pif)
+        raise Error("Unknown PIF \"%s\" (get_pif_record)" % pif)
     def get_all_pifs(self):
         return self.__pifs
     def pif_exists(self, pif):
         return self.__pifs.has_key(pif)
     
-    def get_management_pif(self, host):
+    def get_management_pif(self):
         """ Returns the management pif on host
         """
         all = self.get_all_pifs()
         for pif in all: 
             pifrec = self.get_pif_record(pif)
-            if pifrec['management'] and pifrec['host'] == host :
-                return pif
+            if pifrec['management']: return pif
         return None
 
     def get_network_record(self, network):
@@ -441,6 +660,7 @@ For a VLAN PIF, the datapath name is the bridge name for the PIF's VLAN slave.
 use it.)
 """
 
+
     pifrec = db.get_pif_record(pif)
 
     if pifrec['VLAN'] == '-1':
@@ -463,7 +683,6 @@ For a VLAN PIF, this is the VLAN slave's physical device PIF.
 For a bond master PIF, these are the bond slave PIFs.
 For a non-VLAN, non-bond master PIF, the PIF is its own physical device PIF.
 """
-
     pifrec = db.get_pif_record(pif)
 
     if pifrec['VLAN'] != '-1':
@@ -484,13 +703,16 @@ For a non-VLAN, non-bond master PIF, the physical device is the PIF itself.
 
 def log_pif_action(action, pif):
     pifrec = db.get_pif_record(pif)
-    pifrec['action'] = action
-    pifrec['interface-name'] = interface_name(pif)
+    rec = {}
+    rec['uuid'] = pifrec['uuid']
+    rec['ip_configuration_mode'] = pifrec['ip_configuration_mode']
+    rec['action'] = action
+    rec['interface-name'] = interface_name(pif)
     if action == "rewrite":
-        pifrec['message'] = "Rewrite PIF %(uuid)s configuration" % pifrec
+        rec['message'] = "Rewrite PIF %(uuid)s configuration" % rec
     else:
-        pifrec['message'] = "Bring %(action)s PIF %(uuid)s" % pifrec
-    log("%(message)s: %(interface-name)s configured as %(ip_configuration_mode)s" % pifrec)
+        rec['message'] = "Bring %(action)s PIF %(uuid)s" % rec
+    log("%(message)s: %(interface-name)s configured as %(ip_configuration_mode)s" % rec)
 
 def get_bond_masters_of_pif(pif):
     """Returns a list of PIFs which are bond masters of this PIF"""
@@ -516,7 +738,6 @@ def get_bond_slaves_of_pif(pif):
     """Returns a list of PIFs which make up the given bonded pif."""
     
     pifrec = db.get_pif_record(pif)
-    host = pifrec['host']
 
     bmo = pifrec['bond_master_of']
     if len(bmo) > 1:
@@ -657,10 +878,8 @@ This is because when we are called to bring up an interface with a bond master,
 we should bring down that master."""
 
     pifrec = db.get_pif_record(pif)
-    host = pifrec['host']
 
-    pifs_on_host = [ __pif for __pif in db.get_all_pifs() if
-                     db.get_pif_record(__pif)['host'] == host and 
+    pifs = [ __pif for __pif in db.get_all_pifs() if
                      (not  __pif in get_bond_masters_of_pif(pif)) ]
 
     peerdns_pif = None
@@ -669,7 +888,7 @@ we should bring down that master."""
     # loop through all the pifs on this host looking for one with
     #   other-config:peerdns = true, and one with
     #   other-config:default-route=true
-    for __pif in pifs_on_host:
+    for __pif in pifs:
         __pifrec = db.get_pif_record(__pif)
         __oc = __pifrec['other_config']
         if __oc.has_key('peerdns') and __oc['peerdns'] == 'true':
@@ -747,7 +966,6 @@ def configure_local_port(pif):
     datapath = datapath_name(pif)
     ipdev = ipdev_name(pif)
 
-    host = pifrec['host']
     nw = pifrec['network']
     nwrec = db.get_network_record(nw)
 
@@ -980,8 +1198,7 @@ def action_up(pif):
     # - The networks corresponding to any VLANs attached to the
     #   datapath's PIF.
     network_uuids = []
-    for nwpif in db.get_pifs_by_record({'device': pifrec['device'],
-                                        'host': pifrec['host']}):
+    for nwpif in db.get_pifs_by_device({'device': pifrec['device']}):
         net = db.get_pif_record(nwpif)['network']
         network_uuids += [db.get_network_record(net)['uuid']]
 
@@ -1240,9 +1457,8 @@ def main(argv=None):
                 action_force_rewrite(force_interface, force_rewrite_config)
             else:
                 db = DatabaseCache(cache_file=dbcache_file)
-                host = db.extras['host']
-                pif = db.get_pif_by_bridge(host, force_interface)
-                management_pif = db.get_management_pif(host)
+                pif = db.get_pif_by_bridge(force_interface)
+                management_pif = db.get_management_pif()
 
                 if action == "up":
                     action_up(pif)
@@ -1266,8 +1482,7 @@ def main(argv=None):
                 # pif is not going to be the management pif.
                 # Search DB cache for pif on same host with management=true
                 pifrec = db.get_pif_record(pif)
-                host = pifrec['host']
-                management_pif = db.get_management_pif(host)
+                management_pif = db.get_management_pif()
 
             log_pif_action(action, pif)
 
@@ -1285,7 +1500,7 @@ def main(argv=None):
 
             # Save cache.
             pifrec = db.get_pif_record(pif)
-            db.save(dbcache_file, {'host': pifrec['host']})
+            db.save(dbcache_file)
         
     except Usage, err:
         print >>sys.stderr, err.msg
@@ -1451,7 +1666,6 @@ def configure_network(pif, f):
     """
     
     pifrec = db.get_pif_record(pif)
-    host = pifrec['host']
     nw = pifrec['network']
     nwrec = db.get_network_record(nw)
     oc = None
@@ -1506,8 +1720,7 @@ def configure_network(pif, f):
     # This is because when we are called to bring up an interface with a bond master, it is implicit that
     # we should bring down that master.
     pifs_on_host = [ __pif for __pif in db.get_all_pifs() if
-                     db.get_pif_record(__pif)['host'] == host and 
-                     (not  __pif in get_bond_masters_of_pif(pif)) ]
+                     not __pif in get_bond_masters_of_pif(pif) ]
     other_pifs_on_host = [ __pif for __pif in pifs_on_host if __pif != pif ]
 
     peerdns_pif = None
index fda211a..576c6e6 100644 (file)
@@ -86,16 +86,17 @@ rm -rf \
     $RPM_BUILD_ROOT/root/vswitch/bin/ovs-controller \
     $RPM_BUILD_ROOT/root/vswitch/bin/ovs-discover \
     $RPM_BUILD_ROOT/root/vswitch/bin/ovs-kill \
+    $RPM_BUILD_ROOT/root/vswitch/bin/ovs-openflowd \
     $RPM_BUILD_ROOT/root/vswitch/bin/ovs-pki \
     $RPM_BUILD_ROOT/root/vswitch/bin/ovs-switchui \
     $RPM_BUILD_ROOT/root/vswitch/bin/ovs-wdt \
-    $RPM_BUILD_ROOT/root/vswitch/bin/secchan \
+    $RPM_BUILD_ROOT/root/vswitch/kernel_modules/veth_mod.ko \
     $RPM_BUILD_ROOT/root/vswitch/sbin/ovs-monitor \
     $RPM_BUILD_ROOT/root/vswitch/share/man/man8/ovs-controller.8 \
     $RPM_BUILD_ROOT/root/vswitch/share/man/man8/ovs-discover.8 \
     $RPM_BUILD_ROOT/root/vswitch/share/man/man8/ovs-kill.8 \
+    $RPM_BUILD_ROOT/root/vswitch/share/man/man8/ovs-openflowd.8 \
     $RPM_BUILD_ROOT/root/vswitch/share/man/man8/ovs-pki.8 \
-    $RPM_BUILD_ROOT/root/vswitch/share/man/man8/secchan.8 \
     $RPM_BUILD_ROOT/root/vswitch/share/openvswitch
 
 %clean
@@ -312,7 +313,6 @@ fi
 /etc/profile.d/vswitch.sh
 /root/vswitch/kernel_modules/brcompat_mod.ko
 /root/vswitch/kernel_modules/openvswitch_mod.ko
-/root/vswitch/kernel_modules/veth_mod.ko
 /root/vswitch/scripts/dump-vif-details
 /root/vswitch/scripts/interface-reconfigure
 /root/vswitch/scripts/vif