Merge citrix branch into master.
authorBen Pfaff <blp@nicira.com>
Thu, 16 Jul 2009 18:54:37 +0000 (11:54 -0700)
committerBen Pfaff <blp@nicira.com>
Thu, 16 Jul 2009 18:54:37 +0000 (11:54 -0700)
108 files changed:
INSTALL.Linux
INSTALL.SSL
INSTALL.XenServer
INSTALL.bridge [new file with mode: 0644]
Makefile.am
README
REPORTING-BUGS [new file with mode: 0644]
SubmittingPatches [new file with mode: 0644]
configure.ac
datapath/actions.c
datapath/brc_sysfs.h
datapath/brc_sysfs_dp.c
datapath/brc_sysfs_if.c
datapath/brcompat.c
datapath/datapath.c
datapath/datapath.h
datapath/dp_dev.c
datapath/linux-2.6/Modules.mk
datapath/linux-2.6/compat-2.6/include/linux/kobject.h [new file with mode: 0644]
datapath/linux-2.6/compat-2.6/include/linux/netdevice.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/ovs-switchui.c
include/openflow/openflow.h
include/openvswitch/datapath-protocol.h
lib/automake.mk
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-linux.h [new file with mode: 0644]
lib/netdev.c
lib/netdev.h
lib/rconn.c
lib/shash.c
lib/shash.h
lib/socket-util.c
lib/socket-util.h
lib/vconn-ssl.c
lib/vconn-stream.c
lib/vconn-tcp.c
lib/vconn-unix.c
lib/vconn.c
lib/vlog-modules.def
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 100% 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/automake.mk
tests/test-dhcp-client.c
tests/test-vconn.c [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-vswitchd.8.in
vswitchd/ovs-vswitchd.c
vswitchd/ovs-vswitchd.conf.5.in
xenserver/vswitch-xen.spec

index 2683940..817c9ab 100644 (file)
@@ -168,4 +168,4 @@ complete.
 Bug Reporting
 -------------
 
-Please report problems to ovs-bugs@openvswitch.org.
+Please report problems to bugs@openvswitch.org.
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..7738cd2
--- /dev/null
@@ -0,0 +1,75 @@
+              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 do not recommend using the
+approach described here outside such an environment, since the other
+tools included with Open vSwitch are easier to use and more
+general-purpose than the Linux bridging tools.
+
+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 ovs-vswitchd.conf file.  This file may be empty
+   when ovs-vswitchd, or you may add any valid configuration
+   directives to it (as described in ovs-vswitchd.conf(5)), but it
+   must exist.
+
+   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..18dbb53 100644 (file)
@@ -57,7 +57,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
diff --git a/README b/README
index 7311292..1fa27ba 100644 (file)
--- a/README
+++ b/README
@@ -3,7 +3,7 @@
 What is Open vSwitch?
 ---------------------
 
-Open vSwitch is an Ethernet switch for virtual servers with the
+Open vSwitch is a versatile software-based Ethernet switch with the 
 following features:
 
        * NIC bonding with automatic fail-over and source MAC-based TX
@@ -15,8 +15,7 @@ following features:
 
        * NetFlow v5 flow logging.
 
-       * Connectivity to an external OpenFlow controller, such as
-          NOX.
+       * Connectivity to an external OpenFlow controller, such as NOX.
 
 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 +26,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 +81,5 @@ the manpages.
 Contact 
 -------
 
-ovs-bugs@openvswitch.org
+bugs@openvswitch.org
 http://openvswitch.org/
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 30d2dc5..c8e9d1b 100644 (file)
@@ -13,7 +13,7 @@
 # limitations under the License.
 
 AC_PREREQ(2.60)
-AC_INIT(openvswitch, 0.90.0, ovs-bugs@openvswitch.org)
+AC_INIT(openvswitch, 0.90.2, bugs@openvswitch.org)
 NX_BUILDNR
 AC_CONFIG_SRCDIR([datapath/datapath.c])
 AC_CONFIG_MACRO_DIR([m4])
index a037e43..9b82f9e 100644 (file)
@@ -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 526f5fa..78540d5 100644 (file)
@@ -20,14 +20,5 @@ int brc_sysfs_del_dp(struct datapath *dp);
 int brc_sysfs_add_if(struct net_bridge_port *p);
 int brc_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
-
 #endif /* brc_sysfs.h */
 
index bb779b6..c1e82eb 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 BRC_DEVICE_ATTR CLASS_DEVICE_ATTR
+#define DEVICE_PARAMS struct class_device *d
+#define DEVICE_ARGS d
+#define DEV_ATTR(NAME) class_device_attr_##NAME
 #else
-#define to_kobj(d) &(d)->dev.kobj
 #define BRC_DEVICE_ATTR DEVICE_ATTR
+#define 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 BRC_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 BRC_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 BRC_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 BRC_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 BRC_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 BRC_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 BRC_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 BRC_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 BRC_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 BRC_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 BRC_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 BRC_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 BRC_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 BRC_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 BRC_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 BRC_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 BRC_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 brc_sysfs_add_dp(struct datapath *dp)
 {
-       struct kobject *kobj = to_kobj(dp->ports[ODPP_LOCAL]->dev);
+       struct kobject *kobj = &dp->ports[ODPP_LOCAL]->dev->NETDEV_DEV_MEMBER.kobj;
        int err;
 
        err = sysfs_create_group(kobj, &bridge_group);
@@ -495,12 +483,12 @@ int brc_sysfs_add_dp(struct datapath *dp)
        err = kobject_register(&dp->ifobj);
        if (err) {
                pr_info("%s: can't add kobject (directory) %s/%s\n",
-                               __FUNCTION__, dp_name(dp), dp->ifobj.name);
+                       __FUNCTION__, dp_name(dp), kobject_name(&dp->ifobj));
                goto out2;
        }
 #else
-       br->ifobj = kobject_create_and_add(SYSFS_BRIDGE_PORT_SUBDIR, kobj);
-       if (!br->ifobj) {
+       dp->ifobj = kobject_create_and_add(SYSFS_BRIDGE_PORT_SUBDIR, kobj);
+       if (!dp->ifobj) {
                pr_info("%s: can't add kobject (directory) %s/%s\n",
                        __func__, dp_name(dp), SYSFS_BRIDGE_PORT_SUBDIR);
                goto out2;
@@ -516,7 +504,7 @@ int brc_sysfs_add_dp(struct datapath *dp)
 
 int brc_sysfs_del_dp(struct datapath *dp)
 {
-       struct kobject *kobj = to_kobj(dp->ports[ODPP_LOCAL]->dev);
+       struct kobject *kobj = &dp->ports[ODPP_LOCAL]->dev->NETDEV_DEV_MEMBER.kobj;
 
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
        kobject_unregister(&dp->ifobj);
@@ -527,7 +515,7 @@ int brc_sysfs_del_dp(struct datapath *dp)
 
        return 0;
 }
-#else /* !SUPPORT_SYSFS */
+#else /* !CONFIG_SYSFS */
 int brc_sysfs_add_dp(struct datapath *dp) { return 0; }
 int brc_sysfs_del_dp(struct datapath *dp) { return 0; }
 int brc_sysfs_add_if(struct net_bridge_port *p) { return 0; }
@@ -537,4 +525,4 @@ int brc_sysfs_del_if(struct net_bridge_port *p)
        kfree(p);
        return 0;
 }
-#endif /* !SUPPORT_SYSFS */
+#endif /* !CONFIG_SYSFS */
index ceda129..7a9b8b8 100644 (file)
@@ -21,7 +21,7 @@
 #include "brc_sysfs.h"
 #include "datapath.h"
 
-#ifdef SUPPORT_SYSFS
+#ifdef CONFIG_SYSFS
 
 struct brport_attribute {
        struct attribute        attr;
@@ -289,18 +289,14 @@ int brc_sysfs_add_if(struct net_bridge_port *p)
        struct brport_attribute **a;
        int err;
 
-       kobject_init(&p->kobj);
-       kobject_set_name(&p->kobj, SYSFS_BRIDGE_PORT_ATTR);
-       p->kobj.ktype = &brport_ktype;
-       p->kobj.kset = NULL;
-       p->kobj.parent = &(p->dev->class_dev.kobj);
-
-       err = kobject_add(&p->kobj);
+       err = kobject_init_and_add(&p->kobj, &brport_ktype,
+                                  &(p->dev->NETDEV_DEV_MEMBER.kobj),
+                                  SYSFS_BRIDGE_PORT_ATTR);
        if (err)
-               goto err_put;
+               goto err;
 
        err = sysfs_create_link(&p->kobj,
-                               &dp->ports[ODPP_LOCAL]->dev->class_dev.kobj,
+                               &dp->ports[ODPP_LOCAL]->dev->NETDEV_DEV_MEMBER.kobj,
                                SYSFS_BRIDGE_PORT_LINK);
        if (err)
                goto err_del;
@@ -311,7 +307,11 @@ int brc_sysfs_add_if(struct net_bridge_port *p)
                        goto err_del;
        }
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
        err = sysfs_create_link(&dp->ifobj, &p->kobj, p->dev->name);
+#else
+       err = sysfs_create_link(dp->ifobj, &p->kobj, p->dev->name);
+#endif
        if (err)
                goto err_del;
 
@@ -321,8 +321,8 @@ int brc_sysfs_add_if(struct net_bridge_port *p)
 
 err_del:
        kobject_del(&p->kobj);
-err_put:
        kobject_put(&p->kobj);
+err:
        return err;
 }
 
@@ -339,4 +339,4 @@ int brc_sysfs_del_if(struct net_bridge_port *p)
 
        return 0;
 }
-#endif /* SUPPORT_SYSFS */
+#endif /* CONFIG_SYSFS */
index db0a70f..d9255e6 100644 (file)
@@ -527,18 +527,14 @@ int brc_add_dp(struct datapath *dp)
 {
        if (!try_module_get(THIS_MODULE))
                return -ENODEV;
-#ifdef SUPPORT_SYSFS
        brc_sysfs_add_dp(dp);
-#endif
 
        return 0;
 }
 
 int brc_del_dp(struct datapath *dp) 
 {
-#ifdef SUPPORT_SYSFS
        brc_sysfs_del_dp(dp);
-#endif
        module_put(THIS_MODULE);
 
        return 0;
@@ -573,10 +569,8 @@ __init brc_init(void)
        dp_del_dp_hook = brc_del_dp;
 
        /* Register hooks for interface adds and deletes */
-#ifdef SUPPORT_SYSFS
        dp_add_if_hook = brc_sysfs_add_if;
        dp_del_if_hook = brc_sysfs_del_if;
-#endif
 
        /* Randomize the initial sequence number.  This is not a security
         * feature; it only helps avoid crossed wires between userspace and
index 5e1a352..3edba7c 100644 (file)
@@ -177,7 +177,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);
@@ -365,11 +366,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);
@@ -377,10 +373,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);
@@ -406,6 +405,8 @@ static int add_port(int dp_idx, struct odp_port __user *portp)
        if (dp_add_if_hook)
                dp_add_if_hook(dp->ports[port_no]);
 
+       err = __put_user(port_no, &port.port);
+
 out_put:
        dev_put(dev);
 out_unlock_dp:
@@ -420,10 +421,13 @@ int dp_del_port(struct net_bridge_port *p)
 {
        ASSERT_RTNL();
 
-#ifdef SUPPORT_SYSFS
-       if (p->port_no != ODPP_LOCAL && dp_del_if_hook)
+       if (p->port_no != ODPP_LOCAL && dp_del_if_hook) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
                sysfs_remove_link(&p->dp->ifobj, p->dev->name);
+#else
+               sysfs_remove_link(p->dp->ifobj, p->dev->name);
 #endif
+       }
        dp_ifinfo_notify(RTM_DELLINK, p);
 
        p->dp->n_ports--;
@@ -565,6 +569,7 @@ static int dp_frame_hook(struct net_bridge_port *p, struct sk_buff **pskb)
 #endif
 
 #ifdef CONFIG_XEN
+#if 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. */
@@ -611,7 +616,8 @@ int skb_checksum_setup(struct sk_buff *skb)
 out:
        return -EPROTO;
 }
-#endif
+#endif /* linux == 2.6.18 */
+#endif /* CONFIG_XEN */
 
 int
 dp_output_control(struct datapath *dp, struct sk_buff *skb, int queue_no,
@@ -813,6 +819,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)
@@ -941,8 +948,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) ||
@@ -968,9 +973,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;
@@ -987,29 +990,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;
@@ -1025,7 +1023,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)
@@ -1158,8 +1156,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;
@@ -1251,7 +1248,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 */
@@ -1335,24 +1332,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:
@@ -1414,13 +1415,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:
@@ -1436,6 +1435,7 @@ static long openvswitch_ioctl(struct file *f, unsigned int cmd,
                break;
        }
        mutex_unlock(&dp->mutex);
+exit:
        return err;
 }
 
index 455580f..989dcd4 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 "brc_sysfs.h"
 
@@ -64,8 +65,12 @@ struct datapath {
        struct mutex mutex;
        int dp_idx;
 
-#ifdef SUPPORT_SYSFS
+#ifdef CONFIG_SYSFS
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
        struct kobject ifobj;
+#else
+       struct kobject *ifobj;
+#endif
 #endif
 
        int drop_frags;
@@ -94,7 +99,7 @@ struct net_bridge_port {
        u16 port_no;
        struct datapath *dp;
        struct net_device *dev;
-#ifdef SUPPORT_SYSFS
+#ifdef CONFIG_SYSFS
        struct kobject kobj;
 #endif
        struct list_head node;   /* Element in datapath.ports. */
index 3902a8c..9caf65a 100644 (file)
@@ -119,7 +119,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 bbc4c72..e7359a4 100644 (file)
@@ -12,6 +12,7 @@ openvswitch_headers += \
        linux-2.6/compat-2.6/include/linux/ipv6.h \
        linux-2.6/compat-2.6/include/linux/jiffies.h \
        linux-2.6/compat-2.6/include/linux/kernel.h \
+       linux-2.6/compat-2.6/include/linux/kobject.h \
        linux-2.6/compat-2.6/include/linux/log2.h \
        linux-2.6/compat-2.6/include/linux/lockdep.h \
        linux-2.6/compat-2.6/include/linux/mutex.h \
diff --git a/datapath/linux-2.6/compat-2.6/include/linux/kobject.h b/datapath/linux-2.6/compat-2.6/include/linux/kobject.h
new file mode 100644 (file)
index 0000000..7dbb010
--- /dev/null
@@ -0,0 +1,23 @@
+#ifndef __LINUX_KOBJECT_WRAPPER_H
+#define __LINUX_KOBJECT_WRAPPER_H 1
+
+#include_next <linux/kobject.h>
+
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
+static inline int kobject_init_and_add(struct kobject *kobj,
+                                       struct kobj_type *ktype,
+                                       struct kobject *parent,
+                                       const char *name)
+{
+       kobject_init(kobj);
+       kobject_set_name(kobj, "%s", name);
+       kobj->ktype = ktype;
+       kobj->kset = NULL;
+       kobj->parent = parent;
+
+       return kobject_add(kobj);
+}
+#endif
+
+#endif /* linux/kobject.h wrapper */
index 32e1735..d291513 100644 (file)
@@ -5,8 +5,19 @@
 
 struct net;
 
+/* Before 2.6.21, struct net_device has a "struct class_device" member named
+ * class_dev.  Beginning with 2.6.21, struct net_device instead has a "struct
+ * device" member named dev.  Otherwise the usage of these members is pretty
+ * much the same. */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)
+#define NETDEV_DEV_MEMBER class_dev
+#else
+#define NETDEV_DEV_MEMBER dev
+#endif
+
 #ifndef to_net_dev
-#define to_net_dev(class) container_of(class, struct net_device, class_dev)
+#define to_net_dev(class) \
+       container_of(class, struct net_device, NETDEV_DEV_MEMBER)
 #endif
 
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26)
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 24bf035..1664682 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 b238f72..ece07a8 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 7fe0e15..0a72198 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 15 seconds.
+# ovs-openflowd defaults to 15 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 15 seconds.
+# If unset, ovs-openflowd defaults to 15 seconds.
 #MAX_BACKOFF=15
 
-# 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 119e558..d425b8a 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 040bc70..8201968 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;
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 beaba32..ae36382 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..d129491 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-linux.h \
        lib/netdev.c \
        lib/netdev.h \
        lib/odp-util.c \
@@ -117,8 +124,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,6 +177,7 @@ 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 \
@@ -181,8 +187,8 @@ COVERAGE_FILES = \
        lib/unixctl.c \
        lib/util.c \
        lib/vconn.c \
-       secchan/ofproto.c \
-       secchan/pktbuf.c \
+       ofproto/ofproto.c \
+       ofproto/pktbuf.c \
        vswitchd/bridge.c \
        vswitchd/mgmt.c \
        vswitchd/ovs-brcompatd.c
diff --git a/lib/dpif-linux.c b/lib/dpif-linux.c
new file mode 100644 (file)
index 0000000..0973b5d
--- /dev/null
@@ -0,0 +1,757 @@
+/*
+ * 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 "netdev-linux.h"
+#include "ofpbuf.h"
+#include "poll-loop.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 linux_netdev_notifier port_notifier;
+};
+
+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 linux_netdev_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 void
+dpif_linux_run(void)
+{
+    linux_netdev_notifier_run();
+}
+
+static void
+dpif_linux_wait(void)
+{
+    linux_netdev_notifier_wait();
+}
+
+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_);
+    linux_netdev_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_);
+    int error;
+
+    error = linux_netdev_notifier_get_error(&dpif->port_notifier);
+    if (!error) {
+        if (!dpif->changed_ports.n) {
+            return EAGAIN;
+        }
+        *devnamep = dpif->changed_ports.names[--dpif->changed_ports.n];
+    } else {
+        svec_clear(&dpif->changed_ports);
+    }
+    return error;
+}
+
+static void
+dpif_linux_port_poll_wait(const struct dpif *dpif_)
+{
+    struct dpif_linux *dpif = dpif_linux_cast(dpif_);
+    if (dpif->changed_ports.n
+        || linux_netdev_notifier_peek_error(&dpif->port_notifier)) {
+        poll_immediate_wake();
+    } else {
+        linux_netdev_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",
+    dpif_linux_run,
+    dpif_linux_wait,
+    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 = linux_netdev_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);
+            *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 linux_netdev_change *change, void *dpif_)
+{
+    struct dpif_linux *dpif = dpif_;
+
+    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);
+        }
+    }
+}
diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
new file mode 100644 (file)
index 0000000..cae6d23
--- /dev/null
@@ -0,0 +1,1324 @@
+/*
+ * 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 {
+        error = netdev_open_tap(devname, &netdev);
+    }
+    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;
+
+    mtu = netdev_get_mtu(netdev);
+    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 21f1173..73fa4b9 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 "ofpbuf.h"
 #include "packets.h"
 #include "poll-loop.h"
+#include "svec.h"
 #include "util.h"
 #include "valgrind.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. */
@@ -57,474 +54,643 @@ 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 *);
 
-int
-dpif_open(const char *name, struct dpif *dpif)
+/* Performs periodic work needed by all the various kinds of dpifs.
+ *
+ * If your program opens any dpifs, it must call this function within its main
+ * poll loop. */
+void
+dp_run(void)
 {
-    int listen_mask;
-    int error;
-
-    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;
-    }
-
-    /* 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));
+    int i;
+    for (i = 0; i < N_DPIF_CLASSES; i++) {
+        const struct dpif_class *class = dpif_classes[i];
+        if (class->run) {
+            class->run();
         }
-        dpif_close(dpif);
-        return error;
     }
-    return 0;
 }
 
+/* Arranges for poll_block() to wake up when dp_run() needs to be called.
+ *
+ * If your program opens any dpifs, it must call this function within its main
+ * poll loop. */
 void
-dpif_close(struct dpif *dpif)
+dp_wait(void)
 {
-    if (dpif) {
-        close(dpif->fd);
-        dpif->fd = -1;
+    int i;
+    for (i = 0; i < N_DPIF_CLASSES; i++) {
+        const struct dpif_class *class = dpif_classes[i];
+        if (class->wait) {
+            class->wait();
+        }
     }
 }
 
-static int
-do_ioctl(const struct dpif *dpif, int cmd, const char *cmd_name,
-         const void *arg)
+/* Initializes 'all_dps' and enumerates the names of all known created
+ * datapaths, where possible, into it.  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)
 {
-    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);
+    int error;
+    int i;
+
+    svec_init(all_dps);
+    error = 0;
+    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_create(const char *name, struct dpif *dpif)
+static int
+do_open(const char *name_, bool create, struct dpif **dpifp)
 {
-    unsigned int minor;
+    char *name = xstrdup(name_);
+    char *prefix, *suffix, *colon;
+    struct dpif *dpif = NULL;
     int error;
+    int i;
 
-    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 (error) {
-            dpif_close(dpif);
-        }
-        return error;
+    colon = strchr(name, ':');
+    if (colon) {
+        *colon = '\0';
+        prefix = name;
+        suffix = colon + 1;
     } else {
-        for (minor = 0; minor < ODP_MAX; minor++) {
-            error = open_by_minor(minor, dpif);
-            if (error) {
-                return error;
-            }
+        prefix = "";
+        suffix = name;
+    }
 
-            error = ioctl(dpif->fd, ODP_DP_CREATE, name) < 0 ? errno : 0;
-            if (!error) {
-                return 0;
-            }
-            dpif_close(dpif);
-            if (error != EBUSY) {
-                return 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;
         }
-        return ENOBUFS;
     }
+    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_get_name(struct dpif *dpif, char *name, size_t name_size)
+dpif_open(const char *name, struct dpif **dpifp)
 {
-    struct odp_port port;
-    int error;
+    return do_open(name, false, dpifp);
+}
 
-    assert(name_size > 0);
-    *name = '\0';
+/* 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);
+}
 
-    error = dpif_port_query_by_number(dpif, ODPP_LOCAL, &port);
-    if (!error) {
-        ovs_strlcpy(name, port.devname, name_size);
+/* 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) {
+        char *name = dpif->name;
+        dpif->class->close(dpif);
+        free(name);
     }
-    return error;
 }
 
-int
-dpif_delete(struct dpif *dpif)
+/* Returns the name of datapath 'dpif' (for use in log messages). */
+const char *
+dpif_name(const struct dpif *dpif)
 {
-    COVERAGE_INC(dpif_destroy);
-    return do_ioctl(dpif, ODP_DP_DESTROY, "ODP_DP_DESTROY", NULL);
+    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_get_dp_stats(const struct dpif *dpif, struct odp_stats *stats)
+dpif_get_all_names(const struct dpif *dpif, struct svec *all_names)
 {
-    memset(stats, 0, sizeof *stats);
-    return do_ioctl(dpif, ODP_DP_STATS, "ODP_DP_STATS", stats);
+    if (dpif->class->get_all_names) {
+        int error = dpif->class->get_all_names(dpif, all_names);
+        if (error) {
+            VLOG_WARN_RL(&error_rl,
+                         "failed to retrieve names for datpath %s: %s",
+                         dpif_name(dpif), strerror(error));
+        }
+        return error;
+    } else {
+        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_drop_frags(const struct dpif *dpif, bool *drop_frags)
+dpif_delete(struct dpif *dpif)
 {
-    int tmp;
-    int error = do_ioctl(dpif, ODP_GET_DROP_FRAGS, "ODP_GET_DROP_FRAGS", &tmp);
-    *drop_frags = error ? tmp & 1 : false;
+    int error;
+
+    COVERAGE_INC(dpif_destroy);
+
+    error = dpif->class->delete(dpif);
+    log_operation(dpif, "delete", error);
     return error;
 }
 
+/* Retrieves statistics for 'dpif' into 'stats'.  Returns 0 if successful,
+ * otherwise a positive errno value. */
 int
-dpif_set_drop_frags(struct dpif *dpif, bool drop_frags)
+dpif_get_dp_stats(const struct dpif *dpif, struct odp_stats *stats)
 {
-    int tmp = drop_frags;
-    return do_ioctl(dpif, ODP_SET_DROP_FRAGS, "ODP_SET_DROP_FRAGS", &tmp);
+    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_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 {
-        VLOG_WARN_RL(&error_rl, "dp%u: failed to query port %s: %s",
-                     dpif->minor, devname, strerror(errno));
-        return errno;
+        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 {
+        *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 {
@@ -533,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)
@@ -571,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,
@@ -589,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;
     }
@@ -604,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));
@@ -617,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;
-    }
-
-    /* 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;
+        VLOG_WARN_RL(&error_rl, "%s: %s failed (%s)",
+                     dpif_name(dpif), operation, strerror(error));
     }
-
-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);
     }
-    return lookup_minor(name, minor);
+    if (actions || n_actions) {
+        ds_put_cstr(&ds, ", actions:");
+        format_odp_actions(&ds, actions, n_actions);
+    }
+    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 d81cf63..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 *);
 
@@ -48,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 *);
@@ -80,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..c753e28
--- /dev/null
@@ -0,0 +1,183 @@
+/*
+ * 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 "netdev-linux.h"
+
+#include <errno.h>
+#include <sys/socket.h>
+#include <linux/rtnetlink.h>
+#include <poll.h>
+
+#include "coverage.h"
+#include "netlink.h"
+#include "ofpbuf.h"
+
+#define THIS_MODULE VLM_netdev_linux
+#include "vlog.h"
+
+/* rtnetlink socket. */
+static struct nl_sock *rtnl_sock;
+
+/* All registered notifiers. */
+static struct list all_notifiers = LIST_INITIALIZER(&all_notifiers);
+
+static void linux_netdev_report_change(const struct nlmsghdr *,
+                                       const struct ifinfomsg *,
+                                       struct nlattr *attrs[]);
+static void linux_netdev_report_notify_error(int error);
+
+int
+linux_netdev_notifier_register(struct linux_netdev_notifier *notifier,
+                               linux_netdev_notify_func *cb, void *aux)
+{
+    if (!rtnl_sock) {
+        int error = nl_sock_create(NETLINK_ROUTE, RTNLGRP_LINK, 0, 0,
+                                   &rtnl_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. */
+        linux_netdev_notifier_run();
+    }
+
+    list_push_back(&all_notifiers, &notifier->node);
+    notifier->error = 0;
+    notifier->cb = cb;
+    notifier->aux = aux;
+    return 0;
+}
+
+void
+linux_netdev_notifier_unregister(struct linux_netdev_notifier *notifier)
+{
+    list_remove(&notifier->node);
+    if (list_is_empty(&all_notifiers)) {
+        nl_sock_destroy(rtnl_sock);
+        rtnl_sock = NULL;
+    }
+}
+
+int
+linux_netdev_notifier_get_error(struct linux_netdev_notifier *notifier)
+{
+    int error = notifier->error;
+    notifier->error = 0;
+    return error;
+}
+
+int
+linux_netdev_notifier_peek_error(const struct linux_netdev_notifier *notifier)
+{
+    return notifier->error;
+}
+
+static const struct nl_policy rtnlgrp_link_policy[] = {
+    [IFLA_IFNAME] = { .type = NL_A_STRING },
+    [IFLA_MASTER] = { .type = NL_A_U32, .optional = true },
+};
+
+void
+linux_netdev_notifier_run(void)
+{
+    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+
+    if (!rtnl_sock) {
+        return;
+    }
+
+    for (;;) {
+        struct nlattr *attrs[ARRAY_SIZE(rtnlgrp_link_policy)];
+        struct ofpbuf *buf;
+        int error;
+
+        error = nl_sock_recv(rtnl_sock, &buf, false);
+        if (!error) {
+            if (nl_policy_parse(buf, NLMSG_HDRLEN + sizeof(struct ifinfomsg),
+                                rtnlgrp_link_policy,
+                                attrs, ARRAY_SIZE(rtnlgrp_link_policy))) {
+                struct ifinfomsg *ifinfo;
+
+                ifinfo = (void *) ((char *) buf->data + NLMSG_HDRLEN);
+                linux_netdev_report_change(buf->data, ifinfo, attrs);
+            } else {
+                VLOG_WARN_RL(&rl, "received bad rtnl message");
+                linux_netdev_report_notify_error(ENOBUFS);
+            }
+            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));
+            }
+            linux_netdev_report_notify_error(error);
+        }
+    }
+}
+
+void
+linux_netdev_notifier_wait(void)
+{
+    if (rtnl_sock) {
+        nl_sock_wait(rtnl_sock, POLLIN);
+    }
+}
+
+static void
+linux_netdev_report_change(const struct nlmsghdr *nlmsg,
+                           const struct ifinfomsg *ifinfo,
+                           struct nlattr *attrs[])
+{
+    struct linux_netdev_notifier *notifier;
+    struct linux_netdev_change change;
+
+    COVERAGE_INC(linux_netdev_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 linux_netdev_notifier, node,
+                   &all_notifiers) {
+        if (!notifier->error) {
+            notifier->cb(&change, notifier->aux);
+        }
+    }
+}
+
+static void
+linux_netdev_report_notify_error(int error)
+{
+    struct linux_netdev_notifier *notifier;
+
+    LIST_FOR_EACH (notifier, struct linux_netdev_notifier, node,
+                   &all_notifiers) {
+        if (error != ENOBUFS || !notifier->error) {
+            notifier->error = error;
+        }
+    }
+}
diff --git a/lib/netdev-linux.h b/lib/netdev-linux.h
new file mode 100644 (file)
index 0000000..93ddfcb
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * 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_LINUX_H
+#define NETDEV_LINUX_H 1
+
+/* These functions are specific to the Linux implementation of dpif and netdev.
+ * They should only be used directly by Linux-specific code. */
+
+#include "list.h"
+
+struct linux_netdev_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). */
+};
+
+typedef void linux_netdev_notify_func(const struct linux_netdev_change *,
+                                      void *aux);
+
+struct linux_netdev_notifier {
+    struct list node;
+    int error;
+    linux_netdev_notify_func *cb;
+    void *aux;
+};
+
+int linux_netdev_notifier_register(struct linux_netdev_notifier *,
+                                   linux_netdev_notify_func *, void *aux);
+void linux_netdev_notifier_unregister(struct linux_netdev_notifier *);
+int linux_netdev_notifier_get_error(struct linux_netdev_notifier *);
+int linux_netdev_notifier_peek_error(const struct linux_netdev_notifier *);
+void linux_netdev_notifier_run(void);
+void linux_netdev_notifier_wait(void);
+
+#endif /* netdev-linux.h */
index 6644f7e..7982235 100644 (file)
 #include "dynamic-string.h"
 #include "fatal-signal.h"
 #include "list.h"
+#include "netdev-linux.h"
 #include "netlink.h"
 #include "ofpbuf.h"
 #include "openflow/openflow.h"
 #include "packets.h"
 #include "poll-loop.h"
+#include "shash.h"
 #include "socket-util.h"
 #include "svec.h"
 
@@ -729,6 +731,7 @@ netdev_get_features(struct netdev *netdev,
                            peer ? peer : &dummy[3]);
 }
 
+/* Set the features advertised by 'netdev' to 'advertise'. */
 int
 netdev_set_advertisements(struct netdev *netdev, uint32_t advertise)
 {
@@ -1130,6 +1133,8 @@ get_stats_via_proc(const char *netdev_name, struct netdev_stats *stats)
     return ENODEV;
 }
 
+/* Sets 'carrier' to true if carrier is active (link light is on) on 
+ * 'netdev'. */
 int
 netdev_get_carrier(const struct netdev *netdev, bool *carrier)
 {
@@ -1186,6 +1191,7 @@ exit:
     return error;
 }
 
+/* Retrieves current device stats for 'netdev'. */
 int
 netdev_get_stats(const struct netdev *netdev, struct netdev_stats *stats)
 {
@@ -1420,6 +1426,106 @@ done:
     return error;
 }
 \f
+struct netdev_monitor {
+    struct linux_netdev_notifier notifier;
+    struct shash polled_netdevs;
+    struct shash changed_netdevs;
+};
+
+static void netdev_monitor_change(const struct linux_netdev_change *change,
+                                  void *monitor);
+
+int
+netdev_monitor_create(struct netdev_monitor **monitorp)
+{
+    struct netdev_monitor *monitor;
+    int error;
+
+    monitor = xmalloc(sizeof *monitor);
+    error = linux_netdev_notifier_register(&monitor->notifier,
+                                           netdev_monitor_change, monitor);
+    if (error) {
+        free(monitor);
+        return error;
+    }
+    shash_init(&monitor->polled_netdevs);
+    shash_init(&monitor->changed_netdevs);
+    *monitorp = monitor;
+    return 0;
+}
+
+void
+netdev_monitor_destroy(struct netdev_monitor *monitor)
+{
+    if (monitor) {
+        linux_netdev_notifier_unregister(&monitor->notifier);
+        shash_destroy(&monitor->polled_netdevs);
+        free(monitor);
+    }
+}
+
+void
+netdev_monitor_add(struct netdev_monitor *monitor, struct netdev *netdev)
+{
+    if (!shash_find(&monitor->polled_netdevs, netdev_get_name(netdev))) {
+        shash_add(&monitor->polled_netdevs, netdev_get_name(netdev), NULL);
+    }
+}
+
+void
+netdev_monitor_remove(struct netdev_monitor *monitor, struct netdev *netdev)
+{
+    struct shash_node *node;
+
+    node = shash_find(&monitor->polled_netdevs, netdev_get_name(netdev));
+    if (node) {
+        shash_delete(&monitor->polled_netdevs, node);
+        node = shash_find(&monitor->changed_netdevs, netdev_get_name(netdev));
+        if (node) {
+            shash_delete(&monitor->changed_netdevs, node);
+        }
+    }
+}
+
+int
+netdev_monitor_poll(struct netdev_monitor *monitor, char **devnamep)
+{
+    int error = linux_netdev_notifier_get_error(&monitor->notifier);
+    *devnamep = NULL;
+    if (!error) {
+        struct shash_node *node = shash_first(&monitor->changed_netdevs);
+        if (!node) {
+            return EAGAIN;
+        }
+        *devnamep = xstrdup(node->name);
+        shash_delete(&monitor->changed_netdevs, node);
+    } else {
+        shash_clear(&monitor->changed_netdevs);
+    }
+    return error;
+}
+
+void
+netdev_monitor_poll_wait(const struct netdev_monitor *monitor)
+{
+    if (!shash_is_empty(&monitor->changed_netdevs)
+        || linux_netdev_notifier_peek_error(&monitor->notifier)) {
+        poll_immediate_wake();
+    } else {
+        linux_netdev_notifier_wait();
+    }
+}
+
+static void
+netdev_monitor_change(const struct linux_netdev_change *change, void *monitor_)
+{
+    struct netdev_monitor *monitor = monitor_;
+    if (shash_find(&monitor->polled_netdevs, change->ifname)
+        && !shash_find(&monitor->changed_netdevs, change->ifname)) {
+        shash_add(&monitor->changed_netdevs, change->ifname, NULL);
+    }
+}
+\f
 static void restore_all_flags(void *aux);
 
 /* Set up a signal hook to restore network device flags on program
index d8f1e09..2106f6e 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 {
@@ -118,4 +119,12 @@ int netdev_nodev_get_carrier(const char *netdev_name, bool *carrier);
 
 int netdev_get_vlan_vid(const char *netdev_name, int *vlan_vid);
 
+struct netdev_monitor;
+int netdev_monitor_create(struct netdev_monitor **);
+void netdev_monitor_destroy(struct netdev_monitor *);
+void 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 1301f25..181cae5 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;
index 9ddafe0..9316372 100644 (file)
@@ -51,6 +51,12 @@ shash_clear(struct shash *sh)
     }
 }
 
+bool
+shash_is_empty(const struct shash *shash)
+{
+    return hmap_is_empty(&shash->map);
+}
+
 /* It is the caller's responsible to avoid duplicate names, if that is
  * desirable. */
 void
@@ -91,3 +97,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..bcac41b 100644 (file)
@@ -34,9 +34,11 @@ struct shash {
 void shash_init(struct shash *);
 void shash_destroy(struct shash *);
 void shash_clear(struct shash *);
+bool shash_is_empty(const struct shash *);
 void 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 086a329..d537434 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>
@@ -299,6 +300,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 ef1b564..9bb2df8 100644 (file)
@@ -280,58 +280,21 @@ ssl_vconn_cast(struct vconn *vconn)
 static int
 ssl_open(const char *name, char *suffix, struct vconn **vconnp)
 {
-    char *save_ptr, *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;
     }
 }
 
@@ -804,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 a91b7d3..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;
-    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 f493f83..ee2fb0d 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..63b25cc 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,21 @@ 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(stp)
-VLOG_MODULE(stp_secchan)
 VLOG_MODULE(stats)
 VLOG_MODULE(status)
 VLOG_MODULE(svec)
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 100%
rename from secchan/in-band.c
rename to ofproto/in-band.c
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 599a978..9cf46cc 100644 (file)
@@ -133,7 +133,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) {
@@ -192,8 +192,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;
@@ -259,10 +259,10 @@ int
 ofproto_create(const char *datapath, const struct ofhooks *ofhooks, void *aux,
                struct ofproto **ofprotop)
 {
-    struct dpifmon *dpifmon;
+    struct netdev_monitor *netdev_monitor;
     struct odp_stats stats;
     struct ofproto *p;
-    struct dpif dpif;
+    struct dpif *dpif;
     int error;
 
     *ofprotop = NULL;
@@ -273,36 +273,36 @@ 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);
+        dpif_close(dpif);
         return error;
     }
-    dpif_flow_flush(&dpif);
-    dpif_purge(&dpif);
+    dpif_flow_flush(dpif);
+    dpif_recv_purge(dpif);
 
-    /* Start monitoring datapath ports for status changes. */
-    error = dpifmon_create(datapath, &dpifmon);
+    /* Arrange to monitor datapath ports for status changes. */
+    error = netdev_monitor_create(&netdev_monitor);
     if (error) {
         VLOG_ERR("failed to starting monitoring datapath %s: %s",
                  datapath, strerror(error));
-        dpif_close(&dpif);
+        dpif_close(dpif);
         return error;
     }
 
     /* Initialize settings. */
     p = xcalloc(1, sizeof *p);
     p->fallback_dpid = pick_fallback_dpid();
-    p->datapath_id = pick_datapath_id(&dpif, p->fallback_dpid);
+    p->datapath_id = pick_datapath_id(dpif, p->fallback_dpid);
     VLOG_INFO("using datapath ID %012"PRIx64, p->datapath_id);
     p->manufacturer = xstrdup("Nicira Networks, Inc.");
     p->hardware = xstrdup("Reference Implementation");
@@ -311,7 +311,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;
     port_array_init(&p->ports);
     shash_init(&p->port_by_name);
     p->max_ports = stats.max_ports;
@@ -373,7 +373,7 @@ 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));
+                      : pick_datapath_id(p->dpif, p->fallback_dpid));
     if (p->datapath_id != old_dpid) {
         VLOG_INFO("datapath ID changed to %012"PRIx64, p->datapath_id);
         rconn_reconnect(p->controller->rconn);
@@ -455,7 +455,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;
@@ -712,8 +712,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);
     }
@@ -755,6 +755,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)
 {
@@ -767,15 +778,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;
@@ -784,13 +795,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) {
@@ -902,8 +912,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);
     }
@@ -973,7 +984,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;
 }
@@ -1025,7 +1036,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);
     }
@@ -1048,7 +1059,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);
     }
@@ -1078,7 +1089,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);
 }
 
@@ -1185,6 +1196,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);
@@ -1193,6 +1205,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));
@@ -1216,7 +1229,7 @@ update_port(struct ofproto *p, const char *devname)
 
     COVERAGE_INC(ofproto_update_port);
     ofport = shash_find_data(&p->port_by_name, devname);
-    error = dpif_port_query_by_name(&p->dpif, devname, &odp_port);
+    error = dpif_port_query_by_name(p->dpif, devname, &odp_port);
     if (!error) {
         if (!ofport) {
             /* New port. */
@@ -1269,7 +1282,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;
     }
@@ -1471,7 +1484,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);
@@ -1580,7 +1593,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
@@ -1661,7 +1674,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;
@@ -1809,7 +1822,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;
@@ -1842,10 +1855,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")",
@@ -2150,7 +2163,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);
 
@@ -2293,7 +2306,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;
@@ -2388,7 +2401,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];
@@ -3146,7 +3159,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;
     }
@@ -3159,7 +3172,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;
         }
 
@@ -3240,7 +3253,8 @@ pick_datapath_id(struct dpif *dpif, uint64_t fallback_dpid)
     uint8_t ea[ETH_ADDR_LEN];
     int error;
 
-    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) {
         error = netdev_nodev_get_etheraddr(local_name, ea);
         if (!error) {
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 3a417c7..e251317 100644 (file)
@@ -64,3 +64,9 @@ stp_files = \
 TESTS_ENVIRONMENT += stp_files='$(stp_files)'
 
 EXTRA_DIST += $(stp_files)
+
+TESTS += tests/test-vconn
+noinst_PROGRAMS += tests/test-vconn
+tests_test_vconn_SOURCES = tests/test-vconn.c
+tests_test_vconn_LDADD = lib/libopenvswitch.a $(SSL_LIBS)
+
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-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;
+}
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 31c7a86..b6b05d0 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
@@ -76,7 +82,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
@@ -97,7 +103,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).
@@ -127,6 +133,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 d38ce9e..fcb579e 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
@@ -114,5 +115,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 0474105..8c83cac 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 652ebb1..3d8854b 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.
@@ -116,9 +110,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.
@@ -147,7 +142,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:
 
@@ -161,6 +156,6 @@ Deletes the datapath.
 
 .SH "SEE ALSO"
 
-.BR secchan (8),
 .BR ovs\-appctl (8),
+.BR ovs\-openflowd (8),
 .BR ovs\-vswitchd (8)
index c44291b..2702818 100644 (file)
@@ -211,9 +211,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);
     }
@@ -222,10 +222,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
@@ -243,41 +243,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;
 
@@ -290,11 +266,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"
             };
 
@@ -302,13 +276,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;
@@ -319,20 +286,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);
     }
@@ -362,7 +325,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");
@@ -373,18 +336,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);
     }
@@ -398,7 +361,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",
@@ -432,12 +395,12 @@ do_show(int argc UNUSED, 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;
@@ -447,13 +410,13 @@ do_show(int argc UNUSED, 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;
@@ -469,13 +432,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++) {
@@ -485,41 +448,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);
@@ -528,8 +490,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 4b87cd3..0b4856e 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
@@ -147,7 +147,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
@@ -224,8 +224,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.
 
@@ -327,7 +327,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.
@@ -471,7 +471,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 b40842a..3684fab 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
@@ -40,7 +40,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"
@@ -53,9 +53,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
@@ -66,7 +66,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
@@ -74,23 +74,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
@@ -144,28 +144,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
 
@@ -173,7 +173,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
@@ -182,8 +182,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.
@@ -192,7 +192,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
@@ -200,10 +200,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.
@@ -235,25 +235,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 is 15 seconds, and the minimum value is 5 seconds.
 
@@ -264,19 +264,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
@@ -307,14 +307,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
@@ -337,7 +343,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 b3192ab..f60dea5 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,11 @@ main(int argc, char *argv[])
             ovs_fatal(error, "unrecoverable datapath error");
         }
         unixctl_server_run(unixctl);
+        dp_run();
 
         ofproto_wait(ofproto);
         unixctl_server_wait(unixctl);
+        dp_wait();
         poll_block();
     }
 
@@ -497,7 +495,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. */
@@ -519,7 +518,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"
@@ -539,7 +538,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: 15 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..e3e6ea3 100644 (file)
@@ -22,7 +22,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 b0b2a8f..f977c2b 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"
@@ -158,7 +158,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. */
@@ -261,8 +261,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);
@@ -277,32 +277,37 @@ 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];
+    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);
         }
     }
 
+    bond_init();
     bridge_reconfigure();
 }
 
@@ -344,7 +349,7 @@ 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"));
     }
@@ -354,33 +359,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])) ||
-            (!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);
@@ -421,16 +412,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));
                 }
             }
         }
@@ -441,9 +433,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);
@@ -453,28 +444,20 @@ 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 (;;) {
-                int internal = cfg_get_bool(0, "iface.%s.internal", if_name);
-                int 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;
-                }
+            int internal = cfg_get_bool(0, "iface.%s.internal", if_name);
+            int flags = internal ? ODP_PORT_INTERNAL : 0;
+            int error = dpif_port_add(br->dpif, if_name, flags, NULL);
+            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);
@@ -484,8 +467,7 @@ bridge_reconfigure(void)
         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;
+        uint8_t engine_type, engine_id;
         bool add_id_to_iface = false;
         struct svec nf_hosts;
 
@@ -496,15 +478,16 @@ bridge_reconfigure(void)
             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));
+                    VLOG_ERR("%s interface not in %s, dropping",
+                             iface->name, dpif_name(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);
+                    VLOG_DBG("%s has interface %s on port %d",
+                             dpif_name(br->dpif),
+                             iface->name, iface->dp_ifidx);
                     j++;
                 }
             }
@@ -532,6 +515,7 @@ bridge_reconfigure(void)
         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);
@@ -830,7 +814,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",
@@ -838,7 +822,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);
@@ -848,8 +832,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;
     }
@@ -866,7 +850,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;
 }
@@ -881,12 +865,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);
@@ -977,9 +961,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",
@@ -1116,6 +1107,7 @@ bridge_reconfigure_controller(struct bridge *br)
                                   cfg_get_string(0, "%s.accept-regex", pfx),
                                   update_resolv_conf);
         } else {
+            char local_name[IF_NAMESIZE];
             struct netdev *netdev;
             bool in_band;
             int error;
@@ -1126,7 +1118,11 @@ 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);
+            error = dpif_port_get_name(br->dpif, ODPP_LOCAL,
+                                       local_name, sizeof local_name);
+            if (!error) {
+                error = netdev_open(local_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;
@@ -1279,17 +1275,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;
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 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 8c87fea..4352f5f 100644 (file)
@@ -28,6 +28,7 @@
 #include "command-line.h"
 #include "compiler.h"
 #include "daemon.h"
+#include "dpif.h"
 #include "fault.h"
 #include "leak-checker.h"
 #include "mgmt.h"
@@ -100,6 +101,7 @@ main(int argc, char *argv[])
             need_reconfigure = true;
         }
         unixctl_server_run(unixctl);
+        dp_run();
 
         if (need_reconfigure) {
             poll_immediate_wake();
@@ -108,6 +110,7 @@ main(int argc, char *argv[])
         mgmt_wait();
         bridge_wait();
         unixctl_server_wait(unixctl);
+        dp_wait();
         poll_block();
     }
 
@@ -230,7 +233,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 d0e2474..7599355 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.
@@ -326,7 +325,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,
@@ -401,7 +400,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:
 .
@@ -414,8 +413,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.
@@ -459,7 +458,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"
@@ -477,11 +476,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
@@ -489,7 +488,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.
@@ -514,7 +513,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.
@@ -567,24 +566,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
@@ -622,8 +620,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
index d95d616..373bfb2 100644 (file)
@@ -80,16 +80,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
@@ -287,7 +288,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