Merge branch 'mainstream'
authorGiuseppe Lettieri <g.lettieri@iet.unipi.it>
Wed, 16 Jan 2013 15:24:43 +0000 (16:24 +0100)
committerGiuseppe Lettieri <g.lettieri@iet.unipi.it>
Wed, 16 Jan 2013 15:24:43 +0000 (16:24 +0100)
Conflicts:
lib/dpif-netdev.c

241 files changed:
AUTHORS
CodingStyle
FAQ
INSTALL
INSTALL.Debian [new file with mode: 0644]
INSTALL.Libvirt
INSTALL.RHEL
INSTALL.bridge [deleted file]
Makefile.am
NEWS
OPENFLOW-1.1+ [new file with mode: 0644]
README
acinclude.m4
build-aux/check-structs
build-aux/extract-ofp-errors
build-aux/extract-ofp-msgs
configure.ac
datapath/Modules.mk
datapath/actions.c
datapath/brcompat_main.c [deleted file]
datapath/checksum.h
datapath/compat.h
datapath/datapath.c
datapath/datapath.h
datapath/dp_notify.c
datapath/dp_sysfs.h [deleted file]
datapath/dp_sysfs_dp.c [deleted file]
datapath/dp_sysfs_if.c [deleted file]
datapath/flow.c
datapath/flow.h
datapath/linux/.gitignore
datapath/linux/Makefile.main.in
datapath/linux/Modules.mk
datapath/linux/compat/exthdrs_core.c
datapath/linux/compat/genetlink-brcompat.c [deleted file]
datapath/linux/compat/genetlink-openvswitch.c
datapath/linux/compat/genetlink.inc [deleted file]
datapath/linux/compat/include/asm/percpu.h [new file with mode: 0644]
datapath/linux/compat/include/linux/bug.h [new file with mode: 0644]
datapath/linux/compat/include/linux/etherdevice.h
datapath/linux/compat/include/linux/if.h
datapath/linux/compat/include/linux/kernel.h
datapath/linux/compat/include/linux/rtnetlink.h
datapath/linux/compat/include/linux/udp.h
datapath/linux/compat/include/net/genetlink.h
datapath/linux/compat/include/net/inet_frag.h [new file with mode: 0644]
datapath/linux/compat/include/net/ipv6.h
datapath/linux/compat/include/net/netlink.h
datapath/tunnel.c
datapath/tunnel.h
datapath/vport-capwap.c
datapath/vport-generic.c [deleted file]
datapath/vport-generic.h [deleted file]
datapath/vport-gre.c
datapath/vport-internal_dev.c
datapath/vport-netdev.c
datapath/vport-netdev.h
datapath/vport-patch.c
datapath/vport-vxlan.c [new file with mode: 0644]
datapath/vport.c
datapath/vport.h
debian/.gitignore
debian/automake.mk
debian/changelog
debian/control
debian/copyright.in
debian/dkms.conf.in
debian/openvswitch-brcompat.install [deleted file]
debian/openvswitch-brcompat.manpages [deleted file]
debian/openvswitch-brcompat.postinst [deleted file]
debian/openvswitch-brcompat.postrm [deleted file]
debian/openvswitch-common.manpages
debian/openvswitch-controller.default
debian/openvswitch-switch.init
debian/openvswitch-switch.template
include/linux/openvswitch.h
include/openflow/automake.mk
include/openflow/nicira-ext.h
include/openflow/openflow-1.0.h
include/openflow/openflow-1.1.h
include/openflow/openflow-1.2.h
include/openflow/openflow-1.3.h [new file with mode: 0644]
include/openflow/openflow-common.h
include/openflow/openflow.h
include/openvswitch/automake.mk
include/openvswitch/brcompat-netlink.h [deleted file]
include/openvswitch/tunnel.h
include/sparse/netinet/ip6.h
lib/automake.mk
lib/bond.c
lib/classifier.c
lib/csum.c
lib/csum.h
lib/daemon.c
lib/dpif-linux.c
lib/dpif-linux.h
lib/dpif-netdev.c
lib/dpif-provider.h
lib/dpif.c
lib/dpif.h
lib/flow.c
lib/flow.h
lib/jsonrpc.c
lib/lacp.c
lib/learning-switch.c
lib/mac-learning.c
lib/mac-learning.h
lib/match.c
lib/match.h
lib/meta-flow.c
lib/meta-flow.h
lib/netdev-bsd.c
lib/netdev-dummy.c
lib/netdev-linux.c
lib/netdev-provider.h
lib/netdev-vport.c
lib/netdev-vport.h
lib/netdev.c
lib/netdev.h
lib/nx-match.c
lib/odp-util.c
lib/odp-util.h
lib/ofp-actions.c
lib/ofp-actions.h
lib/ofp-errors.c
lib/ofp-errors.h
lib/ofp-msgs.c
lib/ofp-msgs.h
lib/ofp-parse.c
lib/ofp-print.c
lib/ofp-util.c
lib/ofp-util.def
lib/ofp-util.h
lib/ofp-version-opt.c [new file with mode: 0644]
lib/ofp-version-opt.h [new file with mode: 0644]
lib/ofp-version.man [new file with mode: 0644]
lib/packets.c
lib/packets.h
lib/rconn.c
lib/rconn.h
lib/socket-util.c
lib/socket-util.h
lib/sset.c
lib/sset.h
lib/stream-ssl.c
lib/stream-tcp.c
lib/stream-unix.c
lib/util.h
lib/vconn-provider.h
lib/vconn-stream.c
lib/vconn.c
lib/vconn.h
lib/vlog-unixctl.man
lib/vlog.c
lib/vlog.h
lib/worker.c
m4/openvswitch.m4
manpages.mk
ofproto/automake.mk
ofproto/connmgr.c
ofproto/connmgr.h
ofproto/in-band.c
ofproto/in-band.h
ofproto/ofproto-dpif-governor.c
ofproto/ofproto-dpif-sflow.c
ofproto/ofproto-dpif-sflow.h
ofproto/ofproto-dpif-unixctl.man [new file with mode: 0644]
ofproto/ofproto-dpif.c
ofproto/ofproto-provider.h
ofproto/ofproto-unixctl.man
ofproto/ofproto.c
ofproto/ofproto.h
ofproto/pktbuf.c
ovsdb/jsonrpc-server.c
ovsdb/ovsdb-client.1.in
ovsdb/ovsdb-doc.in
ovsdb/ovsdb-server.1.in
ovsdb/ovsdb-server.c
ovsdb/ovsdb-tool.1.in
python/ovs/daemon.py
python/ovs/poller.py
python/ovs/socket_util.py
python/ovs/stream.py
rhel/etc_init.d_openvswitch
rhel/etc_sysconfig_network-scripts_ifup-ovs
rhel/openvswitch-fedora.spec.in
rhel/openvswitch-kmod-fedora.spec.in
rhel/openvswitch.spec.in
rhel/usr_share_openvswitch_scripts_sysconfig.template
tests/automake.mk
tests/learn.at
tests/odp.at
tests/ofp-actions.at
tests/ofp-errors.at
tests/ofp-print.at
tests/ofp-util.at [new file with mode: 0644]
tests/ofproto-dpif.at
tests/ofproto-macros.at
tests/ofproto.at
tests/ovs-ofctl.at
tests/ovs-vsctl.at
tests/test-classifier.c
tests/test-flows.c
tests/test-netflow.c
tests/test-odp.c
tests/test-vconn.c
tests/testsuite.at
utilities/bugtool/.gitignore
utilities/bugtool/automake.mk
utilities/bugtool/ovs-bugtool.8.in [moved from utilities/bugtool/ovs-bugtool.8 with 95% similarity]
utilities/bugtool/ovs-bugtool.in
utilities/ovs-appctl.8.in
utilities/ovs-benchmark.1.in
utilities/ovs-controller.8.in
utilities/ovs-controller.c
utilities/ovs-ctl.8
utilities/ovs-ctl.in
utilities/ovs-dpctl.8.in
utilities/ovs-dpctl.c
utilities/ovs-l3ping.8.in
utilities/ovs-ofctl.8.in
utilities/ovs-ofctl.c
utilities/ovs-pcap.1.in
utilities/ovs-pki.8.in
utilities/ovs-save
utilities/ovs-tcpundump.1.in
utilities/ovs-test.8.in
utilities/ovs-vlan-bug-workaround.8.in
utilities/ovs-vlan-test.8.in
utilities/ovs-vsctl.8.in
utilities/ovs-vsctl.c
vswitchd/.gitignore
vswitchd/automake.mk
vswitchd/bridge.c
vswitchd/ovs-brcompatd.8.in [deleted file]
vswitchd/ovs-brcompatd.c [deleted file]
vswitchd/ovs-vswitchd.8.in
vswitchd/vswitch.ovsschema
vswitchd/vswitch.xml
vswitchd/xenserver.c
xenserver/openvswitch-xen.spec.in

diff --git a/AUTHORS b/AUTHORS
index 4687865..dc2b05c 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -26,6 +26,7 @@ Dominic Curran          dominic.curran@citrix.com
 Ed Maste                emaste at freebsd.org
 Edward Tomasz Napierała trasz@freebsd.org
 Ethan Jackson           ethan@nicira.com
+FUJITA Tomonori         fujita.tomonori@lab.ntt.co.jp
 Gaetano Catalli         gaetano.catalli@gmail.com
 Giuseppe Lettieri       g.lettieri@iet.unipi.it
 Glen Gibb               grg@stanford.edu
@@ -34,6 +35,7 @@ Henry Mai               hmai@nicira.com
 Hao Zheng               hzheng@nicira.com
 Ian Campbell            Ian.Campbell@citrix.com
 Isaku Yamahata          yamahata@valinux.co.jp
+Jarno Rajahalme         jarno.rajahalme@nsn.com
 Jean Tourrilhes         jt@hpl.hp.com
 Jeremy Stribling        strib@nicira.com
 Jesse Gross             jesse@nicira.com
@@ -42,6 +44,7 @@ Joe Stringer            joe@wand.net.nz
 Jun Nakajima            jun.nakajima@intel.com
 Justin Pettit           jpettit@nicira.com
 Keith Amidon            keith@nicira.com
+Krishna Kondaka         kkondaka@vmware.com
 Kyle Mestery            kmestery@cisco.com
 Leo Alterman            lalterman@nicira.com
 Luca Giraudo            lgiraudo@nicira.com
@@ -62,6 +65,7 @@ Sanjay Sane             ssane@nicira.com
 Shan Wei                davidshan@tencent.com
 Shih-Hao Li             shli@nicira.com
 Simon Horman            horms@verge.net.au
+SUGYO Kazushi           sugyo.org@gmail.com
 Tadaaki Nagao           nagao@stratosphere.co.jp
 Tetsuo NAKAGAWA         nakagawa@mxc.nes.nec.co.jp
 Thomas Goirand          zigo@debian.org
@@ -75,12 +79,14 @@ Vivien Bernet-Rollande  vbr@soprive.net
 Wei Yongjun             yjwei@cn.fujitsu.com
 Yasuhito Takamiya       yasuhito@gmail.com
 Yu Zhiguo               yuzg@cn.fujitsu.com
+Zoltan Kiss             zoltan.kiss@citrix.com
 Zhi Yong Wu             zwu.kernel@gmail.com
 
 The following additional people are mentioned in commit logs as having
 provided helpful bug reports or suggestions.
 
 Aaron M. Ucko           ucko@debian.org
+Adam Heath              doogie@brainfood.com
 Ahmed Bilal             numan252@gmail.com
 Alan Shieh              ashieh@nicira.com
 Alban Browaeys          prahal@yahoo.com
@@ -96,6 +102,7 @@ Bob Ball                bob.ball@citrix.com
 Brad Hall               brad@nicira.com
 Brandon Heller          brandonh@stanford.edu
 Brendan Kelley          bkelley@nicira.com
+Brent Salisbury         brent.salisbury@gmail.com
 Bryan Fulton            bryan@nicira.com
 Bryan Osoro             bosoro@nicira.com
 Cedric Hobbs            cedric@nicira.com
@@ -128,14 +135,17 @@ Janis Hamme             janis.hamme@student.kit.edu
 Jari Sundell            sundell.software@gmail.com
 Jed Daniels             openvswitch@jeddaniels.com
 Jeongkeun Lee           jklee@hp.com
+Jing Ai                 ai_jing2000@hotmail.com
 Joan Cirer              joan@ev0.net
 John Galgay             john@galgay.net
 Kevin Mancuso           kevin.mancuso@rackspace.com
+Kiran Shanbhog          kiran@vmware.com
 Kirill Kabardin
 Koichi Yagishita        yagishita.koichi@jrc.co.jp
 Konstantin Khorenko     khorenko@openvz.org
 Kris zhang              zhang.kris@gmail.com
 Krishna Miriyala        krishna@nicira.com
+Logan Rosen             logatronico@gmail.com
 Luca Falavigna          dktrkranz@debian.org
 Luiz Henrique Ozaki     luiz.ozaki@gmail.com
 Michael A. Collins      mike.a.collins@ark-net.org
@@ -161,6 +171,7 @@ Ramana Reddy            gtvrreddy@gmail.com
 Rob Sherwood            rob.sherwood@bigswitch.com
 Roger Leigh             rleigh@codelibre.net
 Rogério Vinhal Nunes
+Saul St. John           sstjohn@cs.wisc.edu
 Scott Hendricks         shendricks@nicira.com
 Sean Brady              sbrady@gtfservices.com
 Sebastian Andrzej Siewior  sebastian@breakpoint.cc
index b0aeb4e..ee7a0e6 100644 (file)
@@ -432,8 +432,8 @@ precedence makes it necessary, or unless the operands are themselves
 expressions that use && and ||.  Thus:
 
     if (!isdigit((unsigned char)s[0])
-            || !isdigit((unsigned char)s[1])
-            || !isdigit((unsigned char)s[2])) {
+        || !isdigit((unsigned char)s[1])
+        || !isdigit((unsigned char)s[2])) {
         printf("string %s does not start with 3-digit code\n", s);
     }
 
diff --git a/FAQ b/FAQ
index b14bfa4..6d14fc8 100644 (file)
--- a/FAQ
+++ b/FAQ
@@ -128,21 +128,38 @@ A: All official releases have been through a comprehensive testing
    supplanted by the next major release.  The current LTS release is
    1.4.x.
 
+Q: What Linux kernel versions does each Open vSwitch release work with?
+
+A: The following table lists the Linux kernel versions against which the
+   given versions of the Open vSwitch kernel module will successfully
+   build.  The Linux kernel versions are upstream kernel versions, so
+   Linux kernels modified from the upstream sources may not build in
+   some cases even if they are based on a supported version.  This is
+   most notably true of Red Hat Enterprise Linux (RHEL) kernels, which
+   are extensively modified from upstream.
+
+   Open vSwitch   Linux kernel
+   ------------   -------------
+       1.4.x      2.6.18 to 3.2
+       1.5.x      2.6.18 to 3.2
+       1.6.x      2.6.18 to 3.2
+       1.7.x      2.6.18 to 3.3
+       1.8.x      2.6.18 to 3.4
+       1.9.x      2.6.18 to 3.7
+
+   Open vSwitch userspace should also work with the Linux kernel module
+   built into Linux 3.3 and later.
+
+   Open vSwitch userspace is not sensitive to the Linux kernel version.
+   It should build against almost any kernel, certainly against 2.6.18
+   and later.
+
 Q: What features are not available in the Open vSwitch kernel datapath
    that ships as part of the upstream Linux kernel?
 
 A: The kernel module in upstream Linux 3.3 and later does not include
    the following features:
 
-       - Bridge compatibility, that is, support for the ovs-brcompatd
-         daemon that (if you enable it) lets "brctl" and other Linux
-         bridge tools transparently work with Open vSwitch instead.
-
-         We do not expect bridge compatibility to ever be available in
-         upstream Linux.  If you need bridge compatibility, use the
-         kernel module from the Open vSwitch distribution instead of the
-         upstream Linux kernel module.
-
        - Tunnel virtual ports, that is, interfaces with type "gre",
          "ipsec_gre", "capwap".  It is possible to create tunnels in
          Linux and attach them to Open vSwitch as system devices.
@@ -168,6 +185,21 @@ A: Tunnel and patch virtual ports are not supported, as described in the
    may not be transmitted.
 
 
+Terminology
+-----------
+
+Q: I thought Open vSwitch was a virtual Ethernet switch, but the
+   documentation keeps talking about bridges.  What's a bridge?
+
+A: In networking, the terms "bridge" and "switch" are synonyms.  Open
+   vSwitch implements an Ethernet switch, which means that it is also
+   an Ethernet bridge.
+
+Q: What's a VLAN?
+
+A: See the "VLAN" section below.
+
+
 Basic Configuration
 -------------------
 
@@ -414,6 +446,14 @@ Q: Is there any documentation on the database tables and fields?
 
 A: Yes.  ovs-vswitchd.conf.db(5) is a comprehensive reference.
 
+Q: When I run ovs-dpctl I no longer see the bridges I created.  Instead,
+   I only see a datapath called "ovs-system".  How can I see datapath
+   information about a particular bridge?
+
+A: In version 1.9.0, OVS switched to using a single datapath that is
+   shared by all bridges of that type.  The "ovs-appctl dpif/*"
+   commands provide similar functionality that is scoped by the bridge.
+
 
 VLANs
 -----
@@ -546,6 +586,25 @@ A: It's possible that you have the VLAN configured on your physical
          equally well.  Refer to the documentation for the Port table
          in ovs-vswitchd.conf.db(5) for more information.
 
+Q: I added a pair of VMs on different VLANs, like this:
+
+       ovs-vsctl add-br br0
+       ovs-vsctl add-port br0 eth0
+       ovs-vsctl add-port br0 tap0 tag=9
+       ovs-vsctl add-port br0 tap1 tag=10
+
+    but the VMs can't access each other, the external network, or the
+    Internet.
+
+A: It is to be expected that the VMs can't access each other.  VLANs
+   are a means to partition a network.  When you configured tap0 and
+   tap1 as access ports for different VLANs, you indicated that they
+   should be isolated from each other.
+
+   As for the external network and the Internet, it seems likely that
+   the machines you are trying to access are not on VLAN 9 (or 10) and
+   that the Internet is not available on VLAN 9 (or 10).
+
 Q: Can I configure an IP address on a VLAN?
 
 A: Yes.  Use an "internal port" configured as an access port.  For
@@ -602,12 +661,24 @@ Controllers
 
 Q: What versions of OpenFlow does Open vSwitch support?
 
-A: Open vSwitch supports OpenFlow 1.0.  It also includes a number of
-   extensions that bring many of the features from later versions of
-   OpenFlow.  Work is underway to provide support for later versions and
-   can be tracked here:
+A: Open vSwitch 1.9 and earlier support only OpenFlow 1.0 (plus
+   extensions that bring in many of the features from later versions
+   of OpenFlow).
+
+   Open vSwitch versions 1.10 and later will have experimental support
+   for OpenFlow 1.2 and 1.3.  On these versions of Open vSwitch, the
+   following command enables OpenFlow 1.0, 1.2, and 1.3 on bridge br0:
+
+       ovs-vsctl set bridge br0 protocols=openflow10,openflow12,openflow13
+
+   Support for OpenFlow 1.1 is incomplete enough that it cannot yet be
+   enabled, even experimentally.
 
-       http://openvswitch.org/development/openflow-1-x-plan/
+   Support for OpenFlow 1.2 and 1.3 is still incomplete.  Work to be
+   done is tracked in OPENFLOW-1.1+ in the Open vSwitch source tree
+   (also via http://openvswitch.org/development/openflow-1-x-plan/).
+   When support for a given OpenFlow version is solidly implemented,
+   Open vSwitch will enable that version by default.
 
 Q: I'm getting "error type 45250 code 0".  What's that?
 
diff --git a/INSTALL b/INSTALL
index 4d94c52..275e86e 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -2,12 +2,13 @@
            ================================================
 
 This document describes how to build and install Open vSwitch on a
-generic Linux or FreeBSD host.  If you want to install Open vSwitch on
-a Citrix XenServer, see INSTALL.XenServer instead.
+generic Linux or FreeBSD host. For specifics around installation on a
+specific platform, please see one of these files:
 
-This version of Open vSwitch may be built manually with "configure"
-and "make", as described below.  You may also build Debian packages by
-running "dpkg-buildpackage".
+    - INSTALL.Debian
+    - INSTALL.Fedora
+    - INSTALL.RHEL
+    - INSTALL.XenServer
 
 Build Requirements
 ------------------
diff --git a/INSTALL.Debian b/INSTALL.Debian
new file mode 100644 (file)
index 0000000..62f4c19
--- /dev/null
@@ -0,0 +1,125 @@
+           How to Build Debian Packages for Open vSwitch
+            =============================================
+
+This document describes how to build Debian packages for Open vSwitch.
+To install Open vSwitch on Debian without building Debian packages,
+see INSTALL instead.
+
+These instructions should also work on Ubuntu and other Debian
+derivative distributions.
+
+
+Before You Begin
+----------------
+
+Before you begin, consider whether you really need to build packages
+yourself.  Debian "wheezy" and "sid", as well as recent versions of
+Ubuntu, contain pre-built Debian packages for Open vSwitch.  It is
+easier to install these than to build your own.  To use packages from
+your distribution, skip ahead to "Installing .deb Packages", below.
+
+
+Building Open vSwitch Debian packages
+-------------------------------------
+
+You may build from an Open vSwitch distribution tarball or from an
+Open vSwitch Git tree with these instructions.
+
+You do not need to be the superuser to build the Debian packages.
+
+1. Install the "build-essential" and "fakeroot" packages, e.g. with
+   "apt-get install build-essential fakeroot".
+
+2. Obtain and unpack an Open vSwitch source distribution and "cd" into
+   its top level directory.
+
+3. Install the build dependencies listed under "Build-Depends:" near
+   the top of debian/rules.  You can install these any way you like,
+   e.g. with "apt-get install".
+
+   Check your work by running "dpkg-checkbuilddeps".  If you've
+   installed all the dependencies properly, dpkg-checkbuilddeps will
+   exit without printing anything.  If you forgot to install some
+   dependencies, it will tell you which ones.
+
+4. Run:
+
+       fakeroot debian/rules binary
+
+   This will do a serial build that runs the unit tests.  If you
+   prefer, you can run a faster parallel build, e.g.:
+
+       DEB_BUILD_OPTIONS='parallel=8' fakeroot debian/rules binary
+
+   If you are in a big hurry, you can even skip the unit tests:
+
+       DEB_BUILD_OPTIONS='parallel=8 nocheck' fakeroot debian/rules binary
+
+5. The generated .deb files will be in the parent directory of the
+   Open vSwitch source distribution.
+
+
+Installing .deb Packages
+------------------------
+
+These instructions apply to installing from Debian packages that you
+built yourself, as described in the previous section, or from packages
+provided by Debian or a Debian derivative distribution such as Ubuntu.
+In the former case, use a command such as "dpkg -i" to install the
+.deb files that you build, and in the latter case use a program such
+as "apt-get" or "aptitude" to download and install the provided
+packages.
+
+You must be superuser to install Debian packages.
+
+1. Start by installing the "openvswitch-switch" and
+   "openvswitch-common" packages.  These packages include the core
+   userspace components of the switch.
+
+2. Install an Open vSwitch kernel module.  There are multiple ways to
+   do this.  In order of increasing manual effort, these are:
+
+       * Use a Linux kernel 3.3 or later, which has an integrated Open
+        vSwitch kernel module.
+
+        The upstream Linux kernel module lacks a few features that
+        are in the third-party module.  For details, please see the
+        FAQ, "What features are not available in the Open vSwitch
+        kernel datapath that ships as part of the upstream Linux
+        kernel?".
+
+       * Install the "openvswitch-datapath-dkms" Debian package that
+        you built earlier.  This should automatically build and
+        install the Open vSwitch kernel module for your running
+        kernel.
+
+        This option requires that you have a compiler and toolchain
+        installed on the machine where you run Open vSwitch, which
+        may be unacceptable in some production server environments.
+
+       * Install the "openvswitch-datapath-source" Debian package, use
+        "module-assistant" to build a Debian package of the Open
+        vSwitch kernel module for your kernel, and then install that
+        Debian package.
+
+        You can install the kernel module Debian packages that you
+        build this way on the same machine where you built it or on
+        another machine or machines, which means that you don't
+        necessarily have to have any build infrastructure on the
+        machines where you use the kernel module.
+
+        /usr/share/doc/openvswitch-datapath-source/README.Debian has
+        details on the build process.
+
+       * Build and install the kernel module by hand.
+
+Open vSwitch .deb packages not mentioned above are rarely useful.
+Please refer to their individual package descriptions to find out
+whether any of them are useful to you.
+
+
+Bug Reporting
+-------------
+
+Please report problems to bugs@openvswitch.org.
+
index 1bc45d5..fc5575c 100644 (file)
@@ -2,9 +2,7 @@
                  ====================================
 
 This document describes how to use Open vSwitch with Libvirt 0.9.11 or
-later. The Open vSwitch support in Libvirt 0.9.11 eliminates the need to
-use OVS Linux Bridge compatibility layer (brcompatd) and interface up/down
-scripts. This document assumes that you followed INSTALL or installed
+later. This document assumes that you followed INSTALL or installed
 Open vSwitch from distribution packaging such as a .deb or .rpm. The Open
 vSwitch support is included by default in Libvirt 0.9.11. Consult
 www.libvirt.org for instructions on how to build the latest Libvirt, if your
index ff79c89..874d337 100644 (file)
@@ -17,22 +17,58 @@ Before you begin, note the RPM source directory on your version of
 RHEL.  On RHEL 5, the default RPM source directory is
 /usr/src/redhat/SOURCES.  On RHEL 6, it is $HOME/rpmbuild/SOURCES.
 
-1. If you are building from an Open vSwitch Git tree, then you will
+1. Install build prerequisites:
+
+   yum install gcc make python-devel openssl-devel kernel-devel \
+       kernel-debug-devel
+
+2. Some versions of the RHEL 6 kernel-devel package contain a broken
+   "build" symlink.  If you are using such a version, you must fix
+   the problem before continuing.
+
+   To find out whether you are affected, run:
+
+       cd /lib/modules/<version>
+       ls -l build/
+
+   where <version> is the version number of the RHEL 6 kernel.  (The
+   trailing slash in the final command is important.  Be sure to include
+   it.)  If the "ls" command produces a directory listing, your
+   kernel-devel package is OK.  If it produces a "No such file or
+   directory" error, your kernel-devel package is buggy.
+
+   If your kernel-devel package is buggy, then you can fix it with:
+
+       cd /lib/modules/<version>
+       rm build
+       ln -s /usr/src/kernels/<target> build
+
+   where <target> is the name of an existing directory under
+   /usr/src/kernels, whose name should be similar to <version> but may
+   contain some extra parts.  Once you have done this, verify the fix with
+   the same procedure you used above to check for the problem.
+
+3. If you are building from an Open vSwitch Git tree, then you will
    need to first create a distribution tarball by running "./boot.sh;
    ./configure; make dist" in the Git tree.
 
-2. Copy the distribution tarball into the RPM source directory.
+4. Copy the distribution tarball into the RPM source directory.
 
-3. Unpack the distribution tarball into a temporary directory and "cd"
+5. Unpack the distribution tarball into a temporary directory and "cd"
    into the root of the distribution tarball.
 
-4. To build Open vSwitch userspace, run:
+6. To build Open vSwitch userspace, run:
 
        rpmbuild -bb rhel/openvswitch.spec
 
    This produces two RPMs: "openvswitch" and "openvswitch-debuginfo".
 
-5a. On RHEL 5, to build the Open vSwitch kernel module, copy
+   If the build fails with "configure: error: source dir
+   /lib/modules/2.6.32-279.el6.x86_64/build doesn't exist" or similar,
+   then the kernel-devel package is missing or buggy.  Go back to step
+   1 or 2 and fix the problem.
+
+7a. On RHEL 5, to build the Open vSwitch kernel module, copy
     rhel/kmodtool-openvswitch-el5.sh into the RPM source directory and
     run:
 
@@ -49,7 +85,7 @@ RHEL.  On RHEL 5, the default RPM source directory is
     which is usually: "kmod-openvswitch", "kmod-openvswitch-xen", and
     "kmod-openvswitch-PAE".
 
-5b. On RHEL 6, to build the Open vSwitch kernel module, run:
+7b. On RHEL 6, to build the Open vSwitch kernel module, run:
 
        rpmbuild -bb rhel/openvswitch-kmod-rhel6.spec
 
diff --git a/INSTALL.bridge b/INSTALL.bridge
deleted file mode 100644 (file)
index af20bff..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-              Replacing a Linux Bridge with Open vSwitch
-              ==========================================
-
-This file documents how Open vSwitch may be used as a drop-in
-replacement for a Linux kernel bridge in an environment that includes
-elements that are tightly tied to the Linux bridge tools
-(e.g. "brctl") and architecture.  We recommend directly using the
-management tools provided with Open vSwitch rather than these
-compatibility hooks for environments that are not tightly tied to the
-Linux bridging tools; they are more efficient and better reflect the
-actual operation and status.
-
-Installation Procedure
-----------------------
-
-The procedure below explains how to use the Open vSwitch bridge
-compatibility support.  This procedure is written from the perspective
-of a system administrator manually loading and starting Open vSwitch
-in bridge compatibility mode, but of course in practice one would want
-to update system scripts to follow these steps.  If you do edit your
-system configuration files to start Open vSwitch at boot time, make
-sure that it starts up 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.
-
-1. Build, install, and start up the Open vSwitch kernel modules and
-   userspace programs as described in INSTALL.
-
-   It is important to run "make install", because some Open vSwitch
-   programs expect to find files in locations selected at installation
-   time.  The instructions below assume that files are installed in
-   their default locations, under /usr/local.
-
-2. Load the brcompat kernel module (which was built in step 1), e.g.:
-
-      % insmod datapath/linux/brcompat.ko
-
-   (openvswitch.ko should already have been loaded.)
-
-3. Start ovs-brcompatd:
-
-      % ovs-brcompatd --pidfile --detach
-
-   (ovsdb-server and ovs-vswitchd should already have been loaded.)
-
-4. 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 database, then notifies the ovs-vswitchd daemon of
-   the change.  For example, after executing the commands above
-   starting from an empty configuration file, "ovs-vsctl list-ports
-   br0" should show that bridge br0 contains two ports, eth0 and eth1.
index 4da1bab..2c19618 100644 (file)
@@ -43,16 +43,17 @@ EXTRA_DIST = \
        DESIGN \
        FAQ \
        INSTALL \
+       INSTALL.Debian \
        INSTALL.Fedora \
        INSTALL.KVM \
        INSTALL.Libvirt \
        INSTALL.RHEL \
        INSTALL.SSL \
        INSTALL.XenServer \
-       INSTALL.bridge \
        INSTALL.userspace \
        IntegrationGuide \
        NOTICE \
+       OPENFLOW-1.1+ \
        PORTING \
        README-gcov \
        REPORTING-BUGS \
@@ -169,6 +170,20 @@ CLEANFILES += distfiles
 endif
 .PHONY: dist-hook-git
 
+# Check that every .c file includes <config.h>.
+ALL_LOCAL += config-h-check
+config-h-check:
+       @cd $(srcdir); \
+       if test -e .git && (git --version) >/dev/null 2>&1 && \
+          git --no-pager grep -L '#include <config\.h>' `git ls-files | grep '\.c$$' | \
+               grep -vE '^datapath|^lib/sflow|^third-party'`; \
+       then \
+           echo "See above for list of violations of the rule that"; \
+           echo "every C source file must #include <config.h>."; \
+           exit 1; \
+       fi
+.PHONY: config-h-check
+
 # Check that "struct vlog_ratelimit" is always declared "static".
 ALL_LOCAL += rate-limit-check
 rate-limit-check:
@@ -212,6 +227,11 @@ install-data-local: $(INSTALL_DATA_LOCAL)
 uninstall-local: $(UNINSTALL_LOCAL)
 .PHONY: $(DIST_HOOKS) $(CLEAN_LOCAL) $(INSTALL_DATA_LOCAL) $(UNINSTALL_LOCAL)
 
+modules_install:
+if LINUX_ENABLED
+       cd datapath/linux && $(MAKE) modules_install
+endif
+
 include lib/automake.mk
 include ofproto/automake.mk
 include utilities/automake.mk
diff --git a/NEWS b/NEWS
index fa0a249..f9f7171 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,24 @@
 post-v1.9.0
 --------------------
+    - The maximum size of the MAC learning table is now configurable.
+    - New support for the VXLAN tunnel protocol (see the IETF draft here:
+      http://tools.ietf.org/html/draft-mahalingam-dutt-dcops-vxlan-02).
+    - With the Linux datapath, packets for new flows are now queued
+      separately on a per-port basis, so it should no longer be
+      possible for a large number of new flows arriving on one port to
+      prevent new flows from being processed on other ports.
+    - New "vlog/disable-rate-limit" and "vlog/enable-rate-limit" commands
+      available through ovs-appctl allow control over logging rate limits.
+    - The OpenFlow "dp_desc" may now be configured by setting the value of 
+      other-config:dp-desc in the Bridge table.
+    - Path MTU discovery is no longer supported.
+    - Backward-incompatible changes:
+      - Earlier Open vSwitch versions treated ANY as a wildcard in flow
+        syntax.  OpenFlow 1.1 adds a port named ANY, which introduces a
+        conflict.  ANY was rarely used in flow syntax, so we chose to
+        retire that meaning of ANY in favor of the OpenFlow 1.1 meaning.
+    - Inheritance of the Don't Fragment bit in IP tunnels (df_inherit) is
+      no longer supported.
 
 
 v1.9.0 - xx xxx xxxx
@@ -7,6 +26,10 @@ v1.9.0 - xx xxx xxxx
     - The tunneling code no longer assumes input and output keys are symmetric.
       If they are not, PMTUD needs to be disabled for tunneling to work. Note
       this only applies to flow-based keys.
+    - Datapath:
+      - Support for ipv6 set action.
+      - SKB mark matching and setting.
+      - support for Linux kernels up to 3.7
     - FreeBSD is now a supported platform, thanks to code contributions from
       Gaetano Catalli, Ed Maste, and Giuseppe Lettieri.
     - ovs-bugtool: New --ovs option to report only OVS related information.
@@ -17,6 +40,8 @@ v1.9.0 - xx xxx xxxx
       - Allow bitwise masking for SHA and THA fields in ARP, SLL and TLL
         fields in IPv6 neighbor discovery messages, and IPv6 flow label.
       - Adds support for writing to the metadata field for a flow.
+      - It is possible to request the OpenFlow port number with the
+        "ofport_request" column in the Interface table.
     - ovs-ofctl:
       - Commands and actions that accept port numbers now also accept keywords
         that represent those ports (such as LOCAL, NONE, and ALL).  This is
@@ -26,23 +51,43 @@ v1.9.0 - xx xxx xxxx
     - ovs-dpctl:
       - Support requesting the port number with the "port_no" option in
         the "add-if" command.
+      - The "dump-flows" and "del-flows" no longer require an argument
+        if only one datapath exists.
+    - ovs-appctl:
+      - New "dpif/dump-dps", "dpif/show", and "dpif/dump-flows" command
+        that mimic the equivalent ovs-dpctl commands.
     - ovs-pki: The "online PKI" features have been removed, along with
       the ovs-pki-cgi program that facilitated it, because of some
       alarmist insecurity claims.  We do not believe that these claims
       are true, but because we do not know of any users for this
       feature it seems better on balance to remove it.  (The ovs-pki-cgi
       program was not included in distribution packaging.)
-    - Tunnel Path MTU Discovery default value was set to 'disabled'.
+    - Tunnel Path MTU Discovery default value was set to 'disabled'.  This
+      feature is deprecated and will be removed soon.
     - ovsdb-server now enforces the immutability of immutable columns.  This
       was not enforced in earlier versions due to an oversight.
     - New support for a nonstandard form of GRE that supports a 64-bit key.
+    - The ofproto library is now responsible for assigning OpenFlow port
+      numbers.  An ofproto implementation should assign them when
+      port_construct() is called.
+    - All dpif-based bridges of a particular type share a common
+      datapath called "ovs-<type>", e.g. "ovs-system".  The ovs-dpctl
+      commands will now return information on that shared datapath.  To
+      get the equivalent bridge-specific information, use the new
+      "ovs-appctl dpif/*" commands.
+    - Tunnel header caching removed.
     - The following features are now deprecated.  They will be removed no
       earlier than February 2013.  Please email dev@openvswitch.org with
       concerns.
+        - Bridge compatibility.
         - Stable bond mode.
         - The autopath action.
         - Interface type "null".
         - Numeric values for reserved ports (see "ovs-ofctl" note above).
+        - Tunnel Path MTU Discovery.
+        - CAPWAP tunnel support.
+    - The data in the RARP packets can now be matched in the same way as the
+      data in ARP packets.
 
 
 v1.8.0 - xx xxx xxxx
diff --git a/OPENFLOW-1.1+ b/OPENFLOW-1.1+
new file mode 100644 (file)
index 0000000..2c55cfe
--- /dev/null
@@ -0,0 +1,211 @@
+                OpenFlow 1.1+ support in Open vSwitch
+                =====================================
+
+Open vSwitch support for OpenFlow 1.1, 1.2, and 1.3 is a work in
+progress.  This file describes the work still to be done.
+
+The Plan
+--------
+
+OpenFlow version support is not a build-time option.  A single build
+of Open vSwitch must be able to handle all supported versions of
+OpenFlow.  Ideally, even at runtime it should be able to support all
+protocol versions at the same time on different OpenFlow bridges (and
+perhaps even on the same bridge).
+
+At the same time, it would be a shame to litter the core of the OVS
+code with lots of ugly code concerned with the details of various
+OpenFlow protocol versions.
+
+The primary approach to compatibility is to abstract most of the
+details of the differences from the core code, by adding a protocol
+layer that translates between OF1.x and a slightly higher-level
+abstract representation.  The core of this approach is the many struct
+ofputil_* structures in lib/ofp-util.h.
+
+As a consequence of this approach, OVS cannot use OpenFlow protocol
+definitions that closely resemble those in the OpenFlow specification,
+because openflow.h in different versions of the OpenFlow specification
+defines the same identifier with different values.  Instead,
+openflow-common.h contains definitions that are common to all the
+specifications and separate protocol version-specific headers contain
+protocol-specific definitions renamed so as not to conflict,
+e.g. OFPAT10_ENQUEUE and OFPAT11_ENQUEUE for the OpenFlow 1.0 and 1.1
+values for OFPAT_ENQUEUE.  Generally, in cases of conflict, the
+protocol layer will define a more abstract OFPUTIL_* or struct
+ofputil_*.
+
+Here are the current approaches in a few tricky areas:
+
+    * Port numbering.  OpenFlow 1.0 has 16-bit port numbers and later
+      OpenFlow versions have 32-bit port numbers.  For now, OVS
+      support for later protocol versions requires all port numbers to
+      fall into the 16-bit range, translating the reserved OFPP_* port
+      numbers.
+
+    * Actions.  OpenFlow 1.0 and later versions have very different
+      ideas of actions.  OVS reconciles by translating all the
+      versions' actions (and instructions) to and from a common
+      internal representation.
+
+OpenFlow 1.1
+------------
+
+The list of remaining work items for OpenFlow 1.1 is below.  It is
+probably incomplete.
+
+    * Implement Write-Actions instruction.
+
+    * The new in_phy_port field in OFPT_PACKET_IN needs some kind of
+      implementation.  It has a sensible interpretation for tunnels
+      but in general the physical port is not in the datapath for OVS
+      so the value is not necessarily meaningful.  We might have to
+      just fix it as the same as in_port.
+
+    * On OF1.1+ flow_mods, updates by MODIFY are now much better
+      specified.  Check that OVS implements the new behavior, fix it
+      if not.
+
+    * On OF1.1+ flow_mods, DELETE now ignores buffer_id.
+
+    * OFPST_PORT and OFPST_QUEUE stats.  These differ little from
+      OF1.0 to OF1.1 (just the size of the port number field) but do
+      require abstraction (Done?).
+
+    * OFPT_TABLE_MOD stats.  This is new in OF1.1, so we need to
+      implement it.  It should be implemented so that the default OVS
+      behavior does not change.
+
+    * Document how OVS does packet buffering.
+
+    * MPLS.  Simon Horman maintains a patch series that adds this
+      feature.  It needs review and possible revision before it is
+      merged.
+
+    * SCTP.  Joe Stringer maintains a patch series that adds this
+      feature.  It has received review comments that need to be
+      addressed before it is merged.
+
+    * Match and set double-tagged VLANs (QinQ).  This requires kernel
+      work for reasonable performance.
+
+    * VLANs tagged with 88a8 Ethertype.  This requires kernel work for
+      reasonable performance.
+
+    * Groups.
+
+OpenFlow 1.2
+------------
+
+OpenFlow 1.2 support requires OpenFlow 1.1 as a prerequisite, plus the
+following additional work.  (This is based on the change log at the
+end of the OF1.2 spec.  I didn’t compare the specs carefully yet.)
+
+    * Use new OpenFlow extensible error infrastructure, on OF1.2+
+      only, instead of the OVS-specific extension used until now.
+
+    * OFPT_FLOW_MOD:
+
+        * MODIFY and MODIFY_STRICT commands now never insert new flows
+          in the table.  We will still need variations that do,
+          though, both to support older OpenFlow protocols and to get
+          sensible behavior for the internal implementation of the
+          NXAST_LEARN action.
+
+        * New flag OFPFF_RESET_COUNTS.
+
+        * New cookie field behavior.
+
+        * Add ability to delete flow in all tables.
+
+        * Update DESIGN to describe OF1.2 behavior also.
+
+    * Implement OFPT_ROLE_REQUEST.  Patch submitted by Jarno
+      Rajahalme, currently under revision.
+
+    * Add ability to turn off packet buffering with OFPCML_NO_BUFFER.
+
+OpenFlow 1.3
+------------
+
+OpenFlow 1.3 support requires OpenFlow 1.2 as a prerequisite, plus the
+following additional work.  (This is based on the change log at the
+end of the OF1.3 spec, reusing most of the section titles directly.  I
+didn’t compare the specs carefully yet.)
+
+    * Add support for multipart requests.
+
+    * Add OFPMP_TABLE_FEATURES statistics.
+
+    * More flexible table miss support.
+
+    * IPv6 extension header handling support.  Fully implementing this
+      requires kernel support.  This likely will take some careful and
+      probably time-consuming design work.  The actual coding, once
+      that is all done, is probably 2 or 3 days work.
+
+    * Per-flow meters.  Similar to IPv6 extension headers in kernel
+      and design requirements.  Might be politically difficult to add
+      directly to the kernel module, since its functionality overlaps
+      with tc.  Ideally, therefore, we could implement these somehow
+      with tc, but I haven’t investigated whether that makes sense.
+
+    * Per-connection event filtering.  OF1.3 adopted Open vSwitch’s
+      existing design for this feature so implementation should be
+      easy.
+
+    * Auxiliary connections.  These are optional, so a minimal
+      implementation would not need them.  An implementation in
+      generic code might be a week’s worth of work.  The value of an
+      implementation in generic code is questionable, though, since
+      much of the benefit of axuiliary connections is supposed to be
+      to take advantage of hardware support.  (We could make the
+      kernel module somehow send packets across the auxiliary
+      connections directly, for some kind of “hardware” support, if we
+      judged it useful enough.)
+
+    * MPLS BoS matching.  (Included in Simon's MPLS series?)
+
+    * Provider Backbone Bridge tagging.  I don’t plan to implement
+      this (but we’d accept an implementation).
+
+    * Rework tag order.  I’m not sure whether we need to do anything
+      for this.
+
+    * Duration for stats.
+
+    * On-demand flow counters.  I think this might be a real
+      optimization in some cases for the software switch.
+
+How to contribute
+-----------------
+
+If you plan to contribute code for a feature, please let everyone know
+on ovs-dev before you start work.  This will help avoid duplicating
+work.
+
+Please consider the following:
+
+    * Testing.  Please test your code.
+
+    * Unit tests.  Please consider writing some.  The tests directory
+      has many examples that you can use as a starting point.
+
+    * ovs-ofctl.  If you add a feature that is useful for some
+      ovs-ofctl command then you should add support for it there.
+
+    * Documentation.  If you add a user-visible feature, then you
+      should document it in the appropriate manpage and mention it in
+      NEWS as well.
+
+    * Coding style (see the CodingStyle file at the top of the source
+      tree).
+
+    * The patch submission guidelines (see SubmittingPatches).  I
+      recommend using “git send-email”, which automatically follows a
+      lot of those guidelines.
+
+Bug Reporting
+-------------
+
+Please report problems to bugs@openvswitch.org.
diff --git a/README b/README
index 7c680d4..dc5f05e 100644 (file)
--- a/README
+++ b/README
@@ -24,11 +24,10 @@ vSwitch supports the following features:
     * NIC bonding with or without LACP on upstream switch
     * NetFlow, sFlow(R), and mirroring for increased visibility
     * QoS (Quality of Service) configuration, plus policing
-    * GRE, GRE over IPSEC, and CAPWAP tunneling
+    * GRE, GRE over IPSEC, CAPWAP, and VXLAN tunneling
     * 802.1ag connectivity fault management
     * OpenFlow 1.0 plus numerous extensions
     * Transactional configuration database with C and Python bindings
-    * Compatibility layer for Linux bridging code
     * High-performance forwarding using a Linux kernel module
 
 The included Linux kernel module supports Linux 2.6.18 and up, with
@@ -52,11 +51,6 @@ The main components of this distribution are:
     * ovsdb-server, a lightweight database server that ovs-vswitchd
       queries to obtain its configuration.
 
-    * 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 switch kernel module.
 
     * Scripts and specs for building RPMs for Citrix XenServer and Red
@@ -88,26 +82,24 @@ Open vSwitch also provides some tools:
 What other documentation is available?
 --------------------------------------
 
-To install Open vSwitch on a regular Linux machine, read INSTALL.
-
-For answers to common questions, read FAQ.
+To install Open vSwitch on a regular Linux or FreeBSD host, please
+read INSTALL.  For specifics around installation on a specific
+platform, please see one of these files:
 
-To use Open vSwitch as a drop-in replacement for the Linux bridge,
-read INSTALL.bridge.
+    - INSTALL.Debian
+    - INSTALL.Fedora
+    - INSTALL.RHEL
+    - INSTALL.XenServer
 
-To build RPMs for installing Open vSwitch on a Citrix XenServer host
-or resource pool, read INSTALL.XenServer.
+To use Open vSwitch...
 
-To build RPMs for installing Open vSwitch on a Red Hat Enterprise
-Linux host, read INSTALL.RHEL.
+    - ...with KVM on Linux, read INSTALL, read INSTALL.KVM.
 
-To use Open vSwitch with KVM on Linux, read INSTALL, then
-INSTALL.KVM.
+    - ...with Libvirt, read INSTALL.Libvirt.
 
-To use Open vSwitch with Libvirt, read INSTALL.Libvirt.
+    - ...without using a kernel module, read INSTALL.userspace.
 
-To install Open vSwitch without using a kernel module, read
-INSTALL.userspace.
+For answers to common questions, read FAQ.
 
 To learn how to set up SSL support for Open vSwitch, read INSTALL.SSL.
 
index d502b00..02dc952 100644 (file)
@@ -144,7 +144,8 @@ AC_DEFUN([OVS_CHECK_LINUX], [
          AC_ERROR([Linux kernel in build tree $KBUILD (source tree $KSRC) is version $kversion, but version 2.6 or later is required])
        fi
     fi
-    if test ! -e "$KBUILD"/include/linux/version.h || \
+    if (test ! -e "$KBUILD"/include/linux/version.h && \
+        test ! -e "$KBUILD"/include/generated/uapi/linux/version.h)|| \
        (test ! -e "$KBUILD"/include/linux/autoconf.h && \
         test ! -e "$KBUILD"/include/generated/autoconf.h); then
        AC_MSG_ERROR([Linux kernel source in $KBUILD is not configured])
index e50e310..0428abf 100755 (executable)
@@ -190,7 +190,7 @@ def parseStruct():
         forceMatch(';')
     if size % alignment:
         shortage = alignment - (size % alignment)
-        if (structName == "struct ofp_packet_in" and
+        if (structName == "struct ofp10_packet_in" and
             shortage == 2 and
             memberName == 'data' and
             count == 0):
index db28af8..fd53001 100755 (executable)
@@ -151,7 +151,8 @@ def extract_ofp_errors(filenames):
     names = []
     domain = {}
     reverse = {}
-    for domain_name in ("OF1.0", "OF1.1", "OF1.2", "NX1.0", "NX1.1"):
+    for domain_name in ("OF1.0", "OF1.1", "OF1.2", "OF1.3",
+                        "NX1.0", "NX1.1", "NX1.2", "NX1.3"):
         domain[domain_name] = {}
         reverse[domain_name] = {}
 
@@ -225,17 +226,22 @@ def extract_ofp_errors(filenames):
                 else:
                     code = None
 
-                target_map = {"OF1.0+": ("OF1.0", "OF1.1", "OF1.2"),
-                              "OF1.1+": ("OF1.1", "OF1.2"),
-                              "OF1.2+": ("OF1.2",),
+                target_map = {"OF1.0+": ("OF1.0", "OF1.1", "OF1.2", "OF1.3"),
+                              "OF1.1+": ("OF1.1", "OF1.2", "OF1.3"),
+                              "OF1.2+": ("OF1.2", "OF1.3"),
+                              "OF1.3+": ("OF1.3",),
                               "OF1.0":  ("OF1.0",),
                               "OF1.1":  ("OF1.1",),
                               "OF1.2":  ("OF1.2",),
-                              "NX1.0+": ("OF1.0", "OF1.1", "OF1.2"),
+                              "OF1.3":  ("OF1.3",),
+                              "NX1.0+": ("OF1.0", "OF1.1", "OF1.2", "OF1.3"),
+                              "NX1.1+": ("OF1.1", "OF1.2", "OF1.3"),
+                              "NX1.2+": ("OF1.2", "OF1.3"),
+                              "NX1.3+": ("OF1.3",),
                               "NX1.0":  ("OF1.0",),
                               "NX1.1":  ("OF1.1",),
-                              "NX1.1+": ("OF1.1",),
-                              "NX1.2":  ("OF1.2",)}
+                              "NX1.2":  ("OF1.2",),
+                              "NX1.3":  ("OF1.3",)}
                 if targets not in target_map:
                     fatal("%s: unknown error domain" % targets)
                 if targets.startswith('NX') and code < 0x100:
@@ -288,7 +294,6 @@ struct ofperr_domain {
     const char *name;
     uint8_t version;
     enum ofperr (*decode)(uint16_t type, uint16_t code);
-    enum ofperr (*decode_type)(uint16_t type);
     struct pair errors[OFPERR_N_ERRORS];
 };
 
@@ -326,24 +331,6 @@ static enum ofperr
         print """\
     }
 
-    return 0;
-}
-
-static enum ofperr
-%s_decode_type(uint16_t type)
-{
-    switch (type) {""" % name
-        for enum in names:
-            if enum not in map:
-                continue
-            type_, code = map[enum]
-            if code is not None:
-                continue
-            print "    case %d:" % type_
-            print "        return OFPERR_%s;" % enum
-        print """\
-    }
-
     return 0;
 }"""
 
@@ -352,8 +339,7 @@ static const struct ofperr_domain %s = {
     "%s",
     %d,
     %s_decode,
-    %s_decode_type,
-    {""" % (name, description, version, name, name)
+    {""" % (name, description, version, name)
         for enum in names:
             if enum in map:
                 type_, code = map[enum]
@@ -369,6 +355,7 @@ static const struct ofperr_domain %s = {
     output_domain(reverse["OF1.0"], "ofperr_of10", "OpenFlow 1.0", 0x01)
     output_domain(reverse["OF1.1"], "ofperr_of11", "OpenFlow 1.1", 0x02)
     output_domain(reverse["OF1.2"], "ofperr_of12", "OpenFlow 1.2", 0x03)
+    output_domain(reverse["OF1.3"], "ofperr_of13", "OpenFlow 1.3", 0x04)
 
 if __name__ == '__main__':
     if '--help' in sys.argv:
index ad1a8a8..5eaa141 100755 (executable)
@@ -29,7 +29,9 @@ version_map = {"1.0":     (OFP10_VERSION, OFP10_VERSION),
                "1.2+":    (OFP12_VERSION, OFP13_VERSION),
                "1.3+":    (OFP13_VERSION, OFP13_VERSION),
                "1.0-1.1": (OFP10_VERSION, OFP11_VERSION),
-               "1.0-1.2": (OFP10_VERSION, OFP12_VERSION)}
+               "1.0-1.2": (OFP10_VERSION, OFP12_VERSION),
+               "1.1-1.2": (OFP11_VERSION, OFP12_VERSION),
+               "<all>":   (0x01, 0xff)}
 
 def get_line():
     global line
@@ -104,7 +106,7 @@ def extract_ofp_msgs(output_file_name):
             comment += ' %s' % line.lstrip('* \t').rstrip(' \t\r\n')
         comment = comment[:-2].rstrip()
 
-        m = re.match(r'([A-Z]+) ([-.+\d]+) \((\d+)\): ([^.]+)\.$', comment)
+        m = re.match(r'([A-Z]+) ([-.+\d]+|<all>) \((\d+)\): ([^.]+)\.$', comment)
         if not m:
             fatal("unexpected syntax between messages")
         type_, versions, number, contents = m.groups()
index 32940a5..539d5e3 100644 (file)
@@ -73,7 +73,6 @@ OVS_CHECK_SOCKET_LIBS
 OVS_CHECK_LINKER_SECTIONS
 OVS_CHECK_XENSERVER_VERSION
 OVS_CHECK_GROFF
-OVS_CHECK_BRCOMPAT
 OVS_CHECK_GNU_MAKE
 OVS_CHECK_CACHE_TIME
 
index 24c1075..281408b 100644 (file)
@@ -1,8 +1,5 @@
 # Some modules should be built and distributed, e.g. openvswitch.
 #
-# Some modules should be distributed but not built, e.g. we do not build
-# brcompat if configured without it
-#
 # Some modules should be built but not distributed, e.g. third-party
 # hwtable modules.
 both_modules = openvswitch
@@ -14,32 +11,28 @@ openvswitch_sources = \
        checksum.c \
        datapath.c \
        dp_notify.c \
-       dp_sysfs_dp.c \
-       dp_sysfs_if.c \
        flow.c \
        genl_exec.c \
        tunnel.c \
        vlan.c \
        vport.c \
        vport-capwap.c \
-       vport-generic.c \
        vport-gre.c \
        vport-internal_dev.c \
        vport-netdev.c \
-       vport-patch.c
+       vport-patch.c \
+       vport-vxlan.c
 
 openvswitch_headers = \
        checksum.h \
        compat.h \
        datapath.h \
-       dp_sysfs.h \
        flow.h \
        genl_exec.h \
        tunnel.h \
        vlan.h \
        vport.h \
        vport-capwap.h \
-       vport-generic.h \
        vport-internal_dev.h \
        vport-netdev.h
 
index 4649c05..f638ffc 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/if_arp.h>
 #include <linux/if_vlan.h>
 #include <net/ip.h>
+#include <net/ipv6.h>
 #include <net/checksum.h>
 #include <net/dsfield.h>
 
@@ -166,6 +167,54 @@ static void set_ip_addr(struct sk_buff *skb, struct iphdr *nh,
        *addr = new_addr;
 }
 
+static void update_ipv6_checksum(struct sk_buff *skb, u8 l4_proto,
+                                __be32 addr[4], const __be32 new_addr[4])
+{
+       int transport_len = skb->len - skb_transport_offset(skb);
+
+       if (l4_proto == IPPROTO_TCP) {
+               if (likely(transport_len >= sizeof(struct tcphdr)))
+                       inet_proto_csum_replace16(&tcp_hdr(skb)->check, skb,
+                                                 addr, new_addr, 1);
+       } else if (l4_proto == IPPROTO_UDP) {
+               if (likely(transport_len >= sizeof(struct udphdr))) {
+                       struct udphdr *uh = udp_hdr(skb);
+
+                       if (uh->check ||
+                           get_ip_summed(skb) == OVS_CSUM_PARTIAL) {
+                               inet_proto_csum_replace16(&uh->check, skb,
+                                                         addr, new_addr, 1);
+                               if (!uh->check)
+                                       uh->check = CSUM_MANGLED_0;
+                       }
+               }
+       }
+}
+
+static void set_ipv6_addr(struct sk_buff *skb, u8 l4_proto,
+                         __be32 addr[4], const __be32 new_addr[4],
+                         bool recalculate_csum)
+{
+       if (recalculate_csum)
+               update_ipv6_checksum(skb, l4_proto, addr, new_addr);
+
+       skb_clear_rxhash(skb);
+       memcpy(addr, new_addr, sizeof(__be32[4]));
+}
+
+static void set_ipv6_tc(struct ipv6hdr *nh, u8 tc)
+{
+       nh->priority = tc >> 4;
+       nh->flow_lbl[0] = (nh->flow_lbl[0] & 0x0F) | ((tc & 0x0F) << 4);
+}
+
+static void set_ipv6_fl(struct ipv6hdr *nh, u32 fl)
+{
+       nh->flow_lbl[0] = (nh->flow_lbl[0] & 0xF0) | (fl & 0x000F0000) >> 16;
+       nh->flow_lbl[1] = (fl & 0x0000FF00) >> 8;
+       nh->flow_lbl[2] = fl & 0x000000FF;
+}
+
 static void set_ip_ttl(struct sk_buff *skb, struct iphdr *nh, u8 new_ttl)
 {
        csum_replace2(&nh->check, htons(nh->ttl << 8), htons(new_ttl << 8));
@@ -199,6 +248,47 @@ static int set_ipv4(struct sk_buff *skb, const struct ovs_key_ipv4 *ipv4_key)
        return 0;
 }
 
+static int set_ipv6(struct sk_buff *skb, const struct ovs_key_ipv6 *ipv6_key)
+{
+       struct ipv6hdr *nh;
+       int err;
+       __be32 *saddr;
+       __be32 *daddr;
+
+       err = make_writable(skb, skb_network_offset(skb) +
+                           sizeof(struct ipv6hdr));
+       if (unlikely(err))
+               return err;
+
+       nh = ipv6_hdr(skb);
+       saddr = (__be32 *)&nh->saddr;
+       daddr = (__be32 *)&nh->daddr;
+
+       if (memcmp(ipv6_key->ipv6_src, saddr, sizeof(ipv6_key->ipv6_src)))
+               set_ipv6_addr(skb, ipv6_key->ipv6_proto, saddr,
+                             ipv6_key->ipv6_src, true);
+
+       if (memcmp(ipv6_key->ipv6_dst, daddr, sizeof(ipv6_key->ipv6_dst))) {
+               unsigned int offset = 0;
+               int flags = OVS_IP6T_FH_F_SKIP_RH;
+               bool recalc_csum = true;
+
+               if (ipv6_ext_hdr(nh->nexthdr))
+                       recalc_csum = ipv6_find_hdr(skb, &offset,
+                                                   NEXTHDR_ROUTING, NULL,
+                                                   &flags) != NEXTHDR_ROUTING;
+
+               set_ipv6_addr(skb, ipv6_key->ipv6_proto, daddr,
+                             ipv6_key->ipv6_dst, recalc_csum);
+       }
+
+       set_ipv6_tc(nh, ipv6_key->ipv6_tclass);
+       set_ipv6_fl(nh, ntohl(ipv6_key->ipv6_label));
+       nh->hop_limit = ipv6_key->ipv6_hlimit;
+
+       return 0;
+}
+
 /* Must follow make_writable() since that can move the skb data. */
 static void set_tp_port(struct sk_buff *skb, __be16 *port,
                         __be16 new_port, __sum16 *check)
@@ -290,7 +380,7 @@ static int output_userspace(struct datapath *dp, struct sk_buff *skb,
        upcall.cmd = OVS_PACKET_CMD_ACTION;
        upcall.key = &OVS_CB(skb)->flow->key;
        upcall.userdata = NULL;
-       upcall.pid = 0;
+       upcall.portid = 0;
 
        for (a = nla_data(attr), rem = nla_len(attr); rem > 0;
                 a = nla_next(a, &rem)) {
@@ -300,7 +390,7 @@ static int output_userspace(struct datapath *dp, struct sk_buff *skb,
                        break;
 
                case OVS_USERSPACE_ATTR_PID:
-                       upcall.pid = nla_get_u32(a);
+                       upcall.portid = nla_get_u32(a);
                        break;
                }
        }
@@ -345,16 +435,22 @@ static int execute_set_action(struct sk_buff *skb,
                skb->priority = nla_get_u32(nested_attr);
                break;
 
+       case OVS_KEY_ATTR_SKB_MARK:
+               skb_set_mark(skb, nla_get_u32(nested_attr));
+               break;
+
        case OVS_KEY_ATTR_TUN_ID:
-               if (!OVS_CB(skb)->tun_key) {
-                       /* If tun_key is NULL for this skb, assign it to
-                        * a value the caller passed in for action processing
-                        * and output. This can disappear once we drop support
-                        * for setting tun_id outside of tun_key.
-                        */
-                       memset(tun_key, 0, sizeof(struct ovs_key_ipv4_tunnel));
-                       OVS_CB(skb)->tun_key = tun_key;
-               }
+               /* If we're only using the TUN_ID action, store the value in a
+                * temporary instance of struct ovs_key_ipv4_tunnel on the stack.
+                * If both IPV4_TUNNEL and TUN_ID are being used together we
+                * can't write into the IPV4_TUNNEL action, so make a copy and
+                * write into that version.
+                */
+               if (!OVS_CB(skb)->tun_key)
+                       memset(tun_key, 0, sizeof(*tun_key));
+               else if (OVS_CB(skb)->tun_key != tun_key)
+                       memcpy(tun_key, OVS_CB(skb)->tun_key, sizeof(*tun_key));
+               OVS_CB(skb)->tun_key = tun_key;
 
                OVS_CB(skb)->tun_key->tun_id = nla_get_be64(nested_attr);
                break;
@@ -371,6 +467,10 @@ static int execute_set_action(struct sk_buff *skb,
                err = set_ipv4(skb, nla_data(nested_attr));
                break;
 
+       case OVS_KEY_ATTR_IPV6:
+               err = set_ipv6(skb, nla_data(nested_attr));
+               break;
+
        case OVS_KEY_ATTR_TCP:
                err = set_tcp(skb, nla_data(nested_attr));
                break;
diff --git a/datapath/brcompat_main.c b/datapath/brcompat_main.c
deleted file mode 100644 (file)
index 1671b90..0000000
+++ /dev/null
@@ -1,582 +0,0 @@
-/*
- * Copyright (c) 2007-2012 Nicira, Inc.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public
- * License as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/uaccess.h>
-#include <linux/completion.h>
-#include <linux/etherdevice.h>
-#include <linux/if_bridge.h>
-#include <linux/netdevice.h>
-#include <linux/rtnetlink.h>
-#include <net/genetlink.h>
-
-#include "openvswitch/brcompat-netlink.h"
-#include "datapath.h"
-
-static struct genl_family brc_genl_family;
-static struct genl_multicast_group brc_mc_group;
-
-/* Time to wait for ovs-vswitchd to respond to a datapath action, in
- * jiffies. */
-#define BRC_TIMEOUT (HZ * 5)
-
-/* Mutex to serialize ovs-brcompatd callbacks.  (Some callbacks naturally hold
- * br_ioctl_mutex, others hold rtnl_lock, but we can't take the former
- * ourselves and we don't want to hold the latter over a potentially long
- * period of time.) */
-static DEFINE_MUTEX(brc_serial);
-
-/* Userspace communication. */
-static DEFINE_SPINLOCK(brc_lock);    /* Ensure atomic access to these vars. */
-static DECLARE_COMPLETION(brc_done); /* Userspace signaled operation done? */
-static struct sk_buff *brc_reply;    /* Reply from userspace. */
-static u32 brc_seq;                 /* Sequence number for current op. */
-
-static struct sk_buff *brc_send_command(struct net *,
-                                       struct sk_buff *,
-                                       struct nlattr **attrs);
-static int brc_send_simple_command(struct net *, struct sk_buff *);
-
-static struct sk_buff *brc_make_request(int op, const char *bridge,
-                                       const char *port)
-{
-       struct sk_buff *skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
-       if (!skb)
-               goto error;
-
-       genlmsg_put(skb, 0, 0, &brc_genl_family, 0, op);
-
-       if (bridge && nla_put_string(skb, BRC_GENL_A_DP_NAME, bridge))
-               goto nla_put_failure;
-       if (port && nla_put_string(skb, BRC_GENL_A_PORT_NAME, port))
-               goto nla_put_failure;
-
-       return skb;
-
-nla_put_failure:
-       kfree_skb(skb);
-error:
-       return NULL;
-}
-
-static int brc_send_simple_command(struct net *net, struct sk_buff *request)
-{
-       struct nlattr *attrs[BRC_GENL_A_MAX + 1];
-       struct sk_buff *reply;
-       int error;
-
-       reply = brc_send_command(net, request, attrs);
-       if (IS_ERR(reply))
-               return PTR_ERR(reply);
-
-       error = nla_get_u32(attrs[BRC_GENL_A_ERR_CODE]);
-       kfree_skb(reply);
-       return -error;
-}
-
-static int brc_add_del_bridge(struct net *net, char __user *uname, int add)
-{
-       struct sk_buff *request;
-       char name[IFNAMSIZ];
-
-       if (!capable(CAP_NET_ADMIN))
-               return -EPERM;
-
-       if (copy_from_user(name, uname, IFNAMSIZ))
-               return -EFAULT;
-
-       name[IFNAMSIZ - 1] = 0;
-       request = brc_make_request(add ? BRC_GENL_C_DP_ADD : BRC_GENL_C_DP_DEL,
-                                  name, NULL);
-       if (!request)
-               return -ENOMEM;
-
-       return brc_send_simple_command(net, request);
-}
-
-static int brc_get_indices(struct net *net,
-                          int op, const char *br_name,
-                          int __user *uindices, int n)
-{
-       struct nlattr *attrs[BRC_GENL_A_MAX + 1];
-       struct sk_buff *request, *reply;
-       int *indices;
-       int ret;
-       int len;
-
-       if (n < 0)
-               return -EINVAL;
-       if (n >= 2048)
-               return -ENOMEM;
-
-       request = brc_make_request(op, br_name, NULL);
-       if (!request)
-               return -ENOMEM;
-
-       reply = brc_send_command(net, request, attrs);
-       ret = PTR_ERR(reply);
-       if (IS_ERR(reply))
-               goto exit;
-
-       ret = -nla_get_u32(attrs[BRC_GENL_A_ERR_CODE]);
-       if (ret < 0)
-               goto exit_free_skb;
-
-       ret = -EINVAL;
-       if (!attrs[BRC_GENL_A_IFINDEXES])
-               goto exit_free_skb;
-
-       len = nla_len(attrs[BRC_GENL_A_IFINDEXES]);
-       indices = nla_data(attrs[BRC_GENL_A_IFINDEXES]);
-       if (len % sizeof(int))
-               goto exit_free_skb;
-
-       n = min_t(int, n, len / sizeof(int));
-       ret = copy_to_user(uindices, indices, n * sizeof(int)) ? -EFAULT : n;
-
-exit_free_skb:
-       kfree_skb(reply);
-exit:
-       return ret;
-}
-
-/* Called with br_ioctl_mutex. */
-static int brc_get_bridges(struct net *net, int __user *uindices, int n)
-{
-       return brc_get_indices(net, BRC_GENL_C_GET_BRIDGES, NULL, uindices, n);
-}
-
-/* Legacy deviceless bridge ioctl's.  Called with br_ioctl_mutex. */
-static int old_deviceless(struct net *net, void __user *uarg)
-{
-       unsigned long args[3];
-
-       if (copy_from_user(args, uarg, sizeof(args)))
-               return -EFAULT;
-
-       switch (args[0]) {
-       case BRCTL_GET_BRIDGES:
-               return brc_get_bridges(net, (int __user *)args[1], args[2]);
-
-       case BRCTL_ADD_BRIDGE:
-               return brc_add_del_bridge(net, (void __user *)args[1], 1);
-       case BRCTL_DEL_BRIDGE:
-               return brc_add_del_bridge(net, (void __user *)args[1], 0);
-       }
-
-       return -EOPNOTSUPP;
-}
-
-/* Called with the br_ioctl_mutex. */
-static int
-#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,23)
-brc_ioctl_deviceless_stub(unsigned int cmd, void __user *uarg)
-{
-       struct net *net = NULL;
-#else
-brc_ioctl_deviceless_stub(struct net *net, unsigned int cmd, void __user *uarg)
-{
-#endif
-       switch (cmd) {
-       case SIOCGIFBR:
-       case SIOCSIFBR:
-               return old_deviceless(net, uarg);
-
-       case SIOCBRADDBR:
-               return brc_add_del_bridge(net, uarg, 1);
-       case SIOCBRDELBR:
-               return brc_add_del_bridge(net, uarg, 0);
-       }
-
-       return -EOPNOTSUPP;
-}
-
-static int brc_add_del_port(struct net_device *dev, int port_ifindex, int add)
-{
-       struct sk_buff *request;
-       struct net_device *port;
-       int err;
-
-       if (!capable(CAP_NET_ADMIN))
-               return -EPERM;
-
-       port = __dev_get_by_index(dev_net(dev), port_ifindex);
-       if (!port)
-               return -EINVAL;
-
-       /* Save name of dev and port because there's a race between the
-        * rtnl_unlock() and the brc_send_simple_command(). */
-       request = brc_make_request(add ? BRC_GENL_C_PORT_ADD : BRC_GENL_C_PORT_DEL,
-                                  dev->name, port->name);
-       if (!request)
-               return -ENOMEM;
-
-       rtnl_unlock();
-       err = brc_send_simple_command(dev_net(dev), request);
-       rtnl_lock();
-
-       return err;
-}
-
-static int brc_get_bridge_info(struct net_device *dev,
-                              struct __bridge_info __user *ub)
-{
-       struct __bridge_info b;
-
-       memset(&b, 0, sizeof(struct __bridge_info));
-
-       /* First two bytes are the priority, which we should skip.  This comes
-        * from struct bridge_id in br_private.h, which is unavailable to us.
-        */
-       memcpy((u8 *)&b.bridge_id + 2, dev->dev_addr, ETH_ALEN);
-       b.stp_enabled = 0;
-
-       if (copy_to_user(ub, &b, sizeof(struct __bridge_info)))
-               return -EFAULT;
-
-       return 0;
-}
-
-static int brc_get_port_list(struct net_device *dev, int __user *uindices,
-                            int num)
-{
-       int retval;
-
-       rtnl_unlock();
-       retval = brc_get_indices(dev_net(dev), BRC_GENL_C_GET_PORTS, dev->name,
-                                uindices, num);
-       rtnl_lock();
-
-       return retval;
-}
-
-/*
- * Format up to a page worth of forwarding table entries
- * userbuf -- where to copy result
- * maxnum  -- maximum number of entries desired
- *            (limited to a page for sanity)
- * offset  -- number of records to skip
- */
-static int brc_get_fdb_entries(struct net_device *dev, void __user *userbuf,
-                              unsigned long maxnum, unsigned long offset)
-{
-       struct nlattr *attrs[BRC_GENL_A_MAX + 1];
-       struct sk_buff *request, *reply;
-       int retval;
-       int len;
-
-       /* Clamp size to PAGE_SIZE, test maxnum to avoid overflow */
-       if (maxnum > PAGE_SIZE/sizeof(struct __fdb_entry))
-               maxnum = PAGE_SIZE/sizeof(struct __fdb_entry);
-
-       request = brc_make_request(BRC_GENL_C_FDB_QUERY, dev->name, NULL);
-       if (!request)
-               return -ENOMEM;
-       if (nla_put_u64(request, BRC_GENL_A_FDB_COUNT, maxnum) ||
-           nla_put_u64(request, BRC_GENL_A_FDB_SKIP, offset))
-               goto nla_put_failure;
-
-       rtnl_unlock();
-       reply = brc_send_command(dev_net(dev), request, attrs);
-       retval = PTR_ERR(reply);
-       if (IS_ERR(reply))
-               goto exit;
-
-       retval = -nla_get_u32(attrs[BRC_GENL_A_ERR_CODE]);
-       if (retval < 0)
-               goto exit_free_skb;
-
-       retval = -EINVAL;
-       if (!attrs[BRC_GENL_A_FDB_DATA])
-               goto exit_free_skb;
-       len = nla_len(attrs[BRC_GENL_A_FDB_DATA]);
-       if (len % sizeof(struct __fdb_entry) ||
-           len / sizeof(struct __fdb_entry) > maxnum)
-               goto exit_free_skb;
-
-       retval = len / sizeof(struct __fdb_entry);
-       if (copy_to_user(userbuf, nla_data(attrs[BRC_GENL_A_FDB_DATA]), len))
-               retval = -EFAULT;
-
-exit_free_skb:
-       kfree_skb(reply);
-exit:
-       rtnl_lock();
-       return retval;
-
-nla_put_failure:
-       kfree_skb(request);
-       return -ENOMEM;
-}
-
-/* Legacy ioctl's through SIOCDEVPRIVATE.  Called with rtnl_lock. */
-static int old_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
-{
-       unsigned long args[4];
-
-       if (copy_from_user(args, rq->ifr_data, sizeof(args)))
-               return -EFAULT;
-
-       switch (args[0]) {
-       case BRCTL_ADD_IF:
-               return brc_add_del_port(dev, args[1], 1);
-       case BRCTL_DEL_IF:
-               return brc_add_del_port(dev, args[1], 0);
-
-       case BRCTL_GET_BRIDGE_INFO:
-               return brc_get_bridge_info(dev, (struct __bridge_info __user *)args[1]);
-
-       case BRCTL_GET_PORT_LIST:
-               return brc_get_port_list(dev, (int __user *)args[1], args[2]);
-
-       case BRCTL_GET_FDB_ENTRIES:
-               return brc_get_fdb_entries(dev, (void __user *)args[1],
-                                          args[2], args[3]);
-       }
-
-       return -EOPNOTSUPP;
-}
-
-/* Called with the rtnl_lock. */
-static int brc_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
-{
-       int err;
-
-       switch (cmd) {
-       case SIOCDEVPRIVATE:
-               err = old_dev_ioctl(dev, rq, cmd);
-               break;
-
-       case SIOCBRADDIF:
-               return brc_add_del_port(dev, rq->ifr_ifindex, 1);
-       case SIOCBRDELIF:
-               return brc_add_del_port(dev, rq->ifr_ifindex, 0);
-
-       default:
-               err = -EOPNOTSUPP;
-               break;
-       }
-
-       return err;
-}
-
-
-static struct genl_family brc_genl_family = {
-       .id = GENL_ID_GENERATE,
-       .hdrsize = 0,
-       .name = BRC_GENL_FAMILY_NAME,
-       .version = 1,
-       .maxattr = BRC_GENL_A_MAX,
-        SET_NETNSOK
-};
-
-static int brc_genl_query(struct sk_buff *skb, struct genl_info *info)
-{
-       int err = -EINVAL;
-       struct sk_buff *ans_skb;
-       void *data;
-
-       ans_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
-       if (!ans_skb)
-               return -ENOMEM;
-
-       data = genlmsg_put_reply(ans_skb, info, &brc_genl_family,
-                                0, BRC_GENL_C_QUERY_MC);
-       if (data == NULL) {
-               err = -ENOMEM;
-               goto err;
-       }
-       if (nla_put_u32(ans_skb, BRC_GENL_A_MC_GROUP, brc_mc_group.id))
-               goto nla_put_failure;
-
-       genlmsg_end(ans_skb, data);
-       return genlmsg_reply(ans_skb, info);
-
-err:
-nla_put_failure:
-       kfree_skb(ans_skb);
-       return err;
-}
-
-/* Attribute policy: what each attribute may contain.  */
-static struct nla_policy brc_genl_policy[BRC_GENL_A_MAX + 1] = {
-       [BRC_GENL_A_ERR_CODE] = { .type = NLA_U32 },
-       [BRC_GENL_A_FDB_DATA] = { .type = NLA_UNSPEC },
-};
-
-static int brc_genl_dp_result(struct sk_buff *skb, struct genl_info *info)
-{
-       unsigned long int flags;
-       int err;
-
-       if (!info->attrs[BRC_GENL_A_ERR_CODE])
-               return -EINVAL;
-
-       skb = skb_clone(skb, GFP_KERNEL);
-       if (!skb)
-               return -ENOMEM;
-
-       spin_lock_irqsave(&brc_lock, flags);
-       if (brc_seq == info->snd_seq) {
-               brc_seq++;
-
-               kfree_skb(brc_reply);
-               brc_reply = skb;
-
-               complete(&brc_done);
-               err = 0;
-       } else {
-               kfree_skb(skb);
-               err = -ESTALE;
-       }
-       spin_unlock_irqrestore(&brc_lock, flags);
-
-       return err;
-}
-
-static struct genl_ops brc_genl_ops[] = {
-       { .cmd = BRC_GENL_C_QUERY_MC,
-         .flags = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN privelege. */
-         .policy = NULL,
-         .doit = brc_genl_query,
-       },
-       { .cmd = BRC_GENL_C_DP_RESULT,
-         .flags = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN privelege. */
-         .policy = brc_genl_policy,
-         .doit = brc_genl_dp_result,
-       },
-};
-
-static struct sk_buff *brc_send_command(struct net *net,
-                                       struct sk_buff *request,
-                                       struct nlattr **attrs)
-{
-       unsigned long int flags;
-       struct sk_buff *reply;
-       int error;
-
-       mutex_lock(&brc_serial);
-
-       /* Increment sequence number first, so that we ignore any replies
-        * to stale requests. */
-       spin_lock_irqsave(&brc_lock, flags);
-       nlmsg_hdr(request)->nlmsg_seq = ++brc_seq;
-       INIT_COMPLETION(brc_done);
-       spin_unlock_irqrestore(&brc_lock, flags);
-
-       nlmsg_end(request, nlmsg_hdr(request));
-
-       /* Send message. */
-       error = genlmsg_multicast_netns(net, request, 0,
-                                       brc_mc_group.id, GFP_KERNEL);
-       if (error < 0)
-               goto error;
-
-       /* Wait for reply. */
-       error = -ETIMEDOUT;
-       if (!wait_for_completion_timeout(&brc_done, BRC_TIMEOUT)) {
-               pr_warn("timed out waiting for userspace\n");
-               goto error;
-       }
-
-       /* Grab reply. */
-       spin_lock_irqsave(&brc_lock, flags);
-       reply = brc_reply;
-       brc_reply = NULL;
-       spin_unlock_irqrestore(&brc_lock, flags);
-
-       mutex_unlock(&brc_serial);
-
-       /* Re-parse message.  Can't fail, since it parsed correctly once
-        * already. */
-       error = nlmsg_parse(nlmsg_hdr(reply), GENL_HDRLEN,
-                           attrs, BRC_GENL_A_MAX, brc_genl_policy);
-       WARN_ON(error);
-
-       return reply;
-
-error:
-       mutex_unlock(&brc_serial);
-       return ERR_PTR(error);
-}
-
-static int __init brc_init(void)
-{
-       int err;
-
-       pr_info("Open vSwitch Bridge Compatibility, built "__DATE__" "__TIME__"\n");
-
-       /* Set the bridge ioctl handler */
-       brioctl_set(brc_ioctl_deviceless_stub);
-
-       /* Set the openvswitch device ioctl handler */
-       ovs_dp_ioctl_hook = brc_dev_ioctl;
-
-       /* Randomize the initial sequence number.  This is not a security
-        * feature; it only helps avoid crossed wires between userspace and
-        * the kernel when the module is unloaded and reloaded. */
-       brc_seq = net_random();
-
-       /* Register generic netlink family to communicate changes to
-        * userspace. */
-       err = genl_register_family_with_ops(&brc_genl_family,
-                                           brc_genl_ops, ARRAY_SIZE(brc_genl_ops));
-       if (err)
-               goto error;
-
-       strcpy(brc_mc_group.name, "brcompat");
-       err = genl_register_mc_group(&brc_genl_family, &brc_mc_group);
-       if (err < 0)
-               goto err_unregister;
-
-       return 0;
-
-err_unregister:
-       genl_unregister_family(&brc_genl_family);
-error:
-       pr_emerg("failed to install!\n");
-       return err;
-}
-
-static void brc_cleanup(void)
-{
-       /* Unregister ioctl hooks */
-       ovs_dp_ioctl_hook = NULL;
-       brioctl_set(NULL);
-
-       genl_unregister_family(&brc_genl_family);
-}
-
-module_init(brc_init);
-module_exit(brc_cleanup);
-
-MODULE_DESCRIPTION("Open vSwitch bridge compatibility");
-MODULE_AUTHOR("Nicira, Inc.");
-MODULE_LICENSE("GPL");
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)
-/*
- * In kernels 2.6.36 and later, Open vSwitch can safely coexist with
- * the Linux bridge module, but it does not make sense to load both bridge and
- * brcompat, so this prevents it.
- */
-BRIDGE_MUTUAL_EXCLUSION;
-#endif
index 2f2ffee..a440c59 100644 (file)
@@ -102,6 +102,30 @@ static inline void inet_proto_csum_replace4(__sum16 *sum, struct sk_buff *skb,
 }
 #endif
 
+#if defined(NEED_CSUM_NORMALIZE) || LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0)
+#define inet_proto_csum_replace16 rpl_inet_proto_csum_replace16
+static inline void inet_proto_csum_replace16(__sum16 *sum,
+                                            struct sk_buff *skb,
+                                            const __be32 *from,
+                                            const __be32 *to,
+                                            int pseudohdr)
+{
+       __be32 diff[] = {
+               ~from[0], ~from[1], ~from[2], ~from[3],
+               to[0], to[1], to[2], to[3],
+       };
+       if (get_ip_summed(skb) != OVS_CSUM_PARTIAL) {
+               *sum = csum_fold(csum_partial(diff, sizeof(diff),
+                                ~csum_unfold(*sum)));
+               if (get_ip_summed(skb) == OVS_CSUM_COMPLETE && pseudohdr)
+                       skb->csum = ~csum_partial(diff, sizeof(diff),
+                                                 ~skb->csum);
+       } else if (pseudohdr)
+               *sum = ~csum_fold(csum_partial(diff, sizeof(diff),
+                                 csum_unfold(*sum)));
+}
+#endif
+
 #ifdef NEED_CSUM_NORMALIZE
 static inline void update_csum_start(struct sk_buff *skb, int delta)
 {
index 3113b96..c7fd225 100644 (file)
@@ -53,26 +53,6 @@ static inline void skb_clear_rxhash(struct sk_buff *skb)
 #endif
 }
 
-/*
- * Enforces, mutual exclusion with the Linux bridge module, by declaring and
- * exporting br_should_route_hook.  Because the bridge module also exports the
- * same symbol, the module loader will refuse to load both modules at the same
- * time (e.g. "bridge: exports duplicate symbol br_should_route_hook (owned by
- * openvswitch)").
- *
- * Before Linux 2.6.36, Open vSwitch cannot safely coexist with the Linux
- * bridge module, so openvswitch uses this macro in those versions.  In
- * Linux 2.6.36 and later, Open vSwitch can coexist with the bridge module,
- * but it makes no sense to load both bridge and brcompat, so brcompat uses
- * this macro in those versions.
- *
- * The use of "typeof" here avoids the need to track changes in the type of
- * br_should_route_hook over various kernel versions.
- */
-#define BRIDGE_MUTUAL_EXCLUSION                                        \
-       typeof(br_should_route_hook) br_should_route_hook;      \
-       EXPORT_SYMBOL(br_should_route_hook)
-
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32)
 #define GENL_SOCK(net) (genl_sock)
 #define SET_NETNSOK
@@ -81,4 +61,37 @@ static inline void skb_clear_rxhash(struct sk_buff *skb)
 #define SET_NETNSOK    .netnsok = true,
 #endif
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
+#ifdef CONFIG_NETFILTER
+static inline u32 skb_get_mark(struct sk_buff *skb)
+{
+       return skb->nfmark;
+}
+
+static inline void skb_set_mark(struct sk_buff *skb, u32 mark)
+{
+       skb->nfmark = mark;
+}
+#else /* CONFIG_NETFILTER */
+static inline u32 skb_get_mark(struct sk_buff *skb)
+{
+       return 0;
+}
+
+static inline void skb_set_mark(struct sk_buff *skb, u32 mark)
+{
+}
+#endif
+#else /* before 2.6.20 */
+static inline u32 skb_get_mark(struct sk_buff *skb)
+{
+       return skb->mark;
+}
+
+static inline void skb_set_mark(struct sk_buff *skb, u32 mark)
+{
+       skb->mark = mark;
+}
+#endif /* after 2.6.20 */
+
 #endif /* compat.h */
index 65f4dc8..ed69af8 100644 (file)
@@ -61,8 +61,8 @@
 #include "vport-internal_dev.h"
 
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18) || \
-    LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0)
-#error Kernels before 2.6.18 or after 3.5 are not supported by this version of Open vSwitch.
+    LINUX_VERSION_CODE >= KERNEL_VERSION(3,8,0)
+#error Kernels before 2.6.18 or after 3.7 are not supported by this version of Open vSwitch.
 #endif
 
 #define REHASH_FLOW_INTERVAL (10 * 60 * HZ)
@@ -71,9 +71,6 @@ static DECLARE_DELAYED_WORK(rehash_flow_wq, rehash_flow_table);
 
 int ovs_net_id __read_mostly;
 
-int (*ovs_dp_ioctl_hook)(struct net_device *dev, struct ifreq *rq, int cmd);
-EXPORT_SYMBOL(ovs_dp_ioctl_hook);
-
 /**
  * DOC: Locking:
  *
@@ -141,101 +138,6 @@ static int get_dpifindex(struct datapath *dp)
        return ifindex;
 }
 
-static size_t br_nlmsg_size(void)
-{
-       return NLMSG_ALIGN(sizeof(struct ifinfomsg))
-              + nla_total_size(IFNAMSIZ) /* IFLA_IFNAME */
-              + nla_total_size(MAX_ADDR_LEN) /* IFLA_ADDRESS */
-              + nla_total_size(4) /* IFLA_MASTER */
-              + nla_total_size(4) /* IFLA_MTU */
-              + nla_total_size(1); /* IFLA_OPERSTATE */
-}
-
-/* Caller must hold RTNL lock. */
-static int dp_fill_ifinfo(struct sk_buff *skb,
-                         const struct vport *port,
-                         int event, unsigned int flags)
-{
-       struct datapath *dp = port->dp;
-       struct ifinfomsg *hdr;
-       struct nlmsghdr *nlh;
-
-       if (!port->ops->get_ifindex)
-               return -ENODEV;
-
-       nlh = nlmsg_put(skb, 0, 0, event, sizeof(*hdr), flags);
-       if (nlh == NULL)
-               return -EMSGSIZE;
-
-       hdr = nlmsg_data(nlh);
-       hdr->ifi_family = AF_BRIDGE;
-       hdr->__ifi_pad = 0;
-       hdr->ifi_type = ARPHRD_ETHER;
-       hdr->ifi_index = port->ops->get_ifindex(port);
-       hdr->ifi_flags = port->ops->get_dev_flags(port);
-       hdr->ifi_change = 0;
-
-       if (nla_put_string(skb, IFLA_IFNAME, port->ops->get_name(port)) ||
-           nla_put_u32(skb, IFLA_MASTER, get_dpifindex(dp)) ||
-           nla_put_u32(skb, IFLA_MTU, port->ops->get_mtu(port)) ||
-#ifdef IFLA_OPERSTATE
-           nla_put_u8(skb, IFLA_OPERSTATE,
-                      port->ops->is_running(port) ?
-                               port->ops->get_operstate(port) :
-                               IF_OPER_DOWN) ||
-#endif
-           nla_put(skb, IFLA_ADDRESS, ETH_ALEN, port->ops->get_addr(port)))
-               goto nla_put_failure;
-
-       return nlmsg_end(skb, nlh);
-
-nla_put_failure:
-       nlmsg_cancel(skb, nlh);
-       return -EMSGSIZE;
-}
-
-/* Caller must hold RTNL lock. */
-static void dp_ifinfo_notify(int event, struct vport *port)
-{
-       struct sk_buff *skb;
-       int err;
-
-       skb = nlmsg_new(br_nlmsg_size(), GFP_KERNEL);
-       if (!skb) {
-               err = -ENOBUFS;
-               goto err;
-       }
-
-       err = dp_fill_ifinfo(skb, port, event, 0);
-       if (err < 0) {
-               if (err == -ENODEV) {
-                       goto out;
-               } else {
-                       /* -EMSGSIZE implies BUG in br_nlmsg_size() */
-                       WARN_ON(err == -EMSGSIZE);
-                       goto err;
-               }
-       }
-
-       rtnl_notify(skb, ovs_dp_get_net(port->dp), 0, RTNLGRP_LINK, NULL, GFP_KERNEL);
-
-       return;
-err:
-       rtnl_set_sk_err(ovs_dp_get_net(port->dp), RTNLGRP_LINK, err);
-out:
-       kfree_skb(skb);
-}
-
-static void release_dp(struct kobject *kobj)
-{
-       struct datapath *dp = container_of(kobj, struct datapath, ifobj);
-       kfree(dp);
-}
-
-static struct kobj_type dp_ktype = {
-       .release = release_dp
-};
-
 static void destroy_dp_rcu(struct rcu_head *rcu)
 {
        struct datapath *dp = container_of(rcu, struct datapath, rcu);
@@ -244,7 +146,7 @@ static void destroy_dp_rcu(struct rcu_head *rcu)
        free_percpu(dp->stats_percpu);
        release_net(ovs_dp_get_net(dp));
        kfree(dp->ports);
-       kobject_put(&dp->ifobj);
+       kfree(dp);
 }
 
 static struct hlist_head *vport_hash_bucket(const struct datapath *dp,
@@ -278,7 +180,6 @@ static struct vport *new_vport(const struct vport_parms *parms)
                struct hlist_head *head = vport_hash_bucket(dp, vport->port_no);
 
                hlist_add_head_rcu(&vport->dp_hash_node, head);
-               dp_ifinfo_notify(RTM_NEWLINK, vport);
        }
        return vport;
 }
@@ -288,11 +189,6 @@ void ovs_dp_detach_port(struct vport *p)
 {
        ASSERT_RTNL();
 
-       if (p->port_no != OVSP_LOCAL)
-               ovs_dp_sysfs_del_if(p);
-
-       dp_ifinfo_notify(RTM_DELLINK, p);
-
        /* First drop references to device. */
        hlist_del_rcu(&p->dp_hash_node);
 
@@ -309,7 +205,7 @@ void ovs_dp_process_received_packet(struct vport *p, struct sk_buff *skb)
        u64 *stats_counter;
        int error;
 
-       stats = per_cpu_ptr(dp->stats_percpu, smp_processor_id());
+       stats = this_cpu_ptr(dp->stats_percpu);
 
        if (!OVS_CB(skb)->flow) {
                struct sw_flow_key key;
@@ -331,7 +227,7 @@ void ovs_dp_process_received_packet(struct vport *p, struct sk_buff *skb)
                        upcall.cmd = OVS_PACKET_CMD_MISS;
                        upcall.key = &key;
                        upcall.userdata = NULL;
-                       upcall.pid = p->upcall_pid;
+                       upcall.portid = p->upcall_portid;
                        ovs_dp_upcall(dp, skb, &upcall);
                        consume_skb(skb);
                        stats_counter = &stats->n_missed;
@@ -368,7 +264,7 @@ int ovs_dp_upcall(struct datapath *dp, struct sk_buff *skb,
        int dp_ifindex;
        int err;
 
-       if (upcall_info->pid == 0) {
+       if (upcall_info->portid == 0) {
                err = -ENOTCONN;
                goto err;
        }
@@ -391,7 +287,7 @@ int ovs_dp_upcall(struct datapath *dp, struct sk_buff *skb,
        return 0;
 
 err:
-       stats = per_cpu_ptr(dp->stats_percpu, smp_processor_id());
+       stats = this_cpu_ptr(dp->stats_percpu);
 
        u64_stats_update_begin(&stats->sync);
        stats->n_lost++;
@@ -503,7 +399,7 @@ static int queue_userspace_packet(struct net *net, int dp_ifindex,
 
        skb_copy_and_csum_dev(skb, nla_data(nla));
 
-       err = genlmsg_unicast(net, user_skb, upcall_info->pid);
+       err = genlmsg_unicast(net, user_skb, upcall_info->portid);
 
 out:
        kfree_skb(nskb);
@@ -588,12 +484,20 @@ static int validate_set(const struct nlattr *a,
        switch (key_type) {
        const struct ovs_key_ipv4 *ipv4_key;
        const struct ovs_key_ipv4_tunnel *tun_key;
+       const struct ovs_key_ipv6 *ipv6_key;
 
        case OVS_KEY_ATTR_PRIORITY:
        case OVS_KEY_ATTR_TUN_ID:
        case OVS_KEY_ATTR_ETHERNET:
                break;
 
+       case OVS_KEY_ATTR_SKB_MARK:
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) && !defined(CONFIG_NETFILTER)
+               if (nla_get_u32(ovs_key) != 0)
+                       return -EINVAL;
+#endif
+               break;
+
        case OVS_KEY_ATTR_IPV4_TUNNEL:
                tun_key = nla_data(ovs_key);
                if (!tun_key->ipv4_dst)
@@ -616,6 +520,25 @@ static int validate_set(const struct nlattr *a,
 
                break;
 
+       case OVS_KEY_ATTR_IPV6:
+               if (flow_key->eth.type != htons(ETH_P_IPV6))
+                       return -EINVAL;
+
+               if (!flow_key->ip.proto)
+                       return -EINVAL;
+
+               ipv6_key = nla_data(ovs_key);
+               if (ipv6_key->ipv6_proto != flow_key->ip.proto)
+                       return -EINVAL;
+
+               if (ipv6_key->ipv6_frag != flow_key->ip.frag)
+                       return -EINVAL;
+
+               if (ntohl(ipv6_key->ipv6_label) & 0xFFF00000)
+                       return -EINVAL;
+
+               break;
+
        case OVS_KEY_ATTR_TCP:
                if (flow_key->ip.proto != IPPROTO_TCP)
                        return -EINVAL;
@@ -788,24 +711,25 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info)
 
        err = ovs_flow_extract(packet, -1, &flow->key, &key_len);
        if (err)
-               goto err_flow_put;
+               goto err_flow_free;
 
        err = ovs_flow_metadata_from_nlattrs(flow, key_len, a[OVS_PACKET_ATTR_KEY]);
        if (err)
-               goto err_flow_put;
+               goto err_flow_free;
 
        err = validate_actions(a[OVS_PACKET_ATTR_ACTIONS], &flow->key, 0);
        if (err)
-               goto err_flow_put;
+               goto err_flow_free;
 
        acts = ovs_flow_actions_alloc(a[OVS_PACKET_ATTR_ACTIONS]);
        err = PTR_ERR(acts);
        if (IS_ERR(acts))
-               goto err_flow_put;
+               goto err_flow_free;
        rcu_assign_pointer(flow->sf_acts, acts);
 
        OVS_CB(packet)->flow = flow;
        packet->priority = flow->key.phy.priority;
+       skb_set_mark(packet, flow->key.phy.skb_mark);
 
        rcu_read_lock();
        dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex);
@@ -818,13 +742,13 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info)
        local_bh_enable();
        rcu_read_unlock();
 
-       ovs_flow_put(flow);
+       ovs_flow_free(flow);
        return err;
 
 err_unlock:
        rcu_read_unlock();
-err_flow_put:
-       ovs_flow_put(flow);
+err_flow_free:
+       ovs_flow_free(flow);
 err_kfree_skb:
        kfree_skb(packet);
 err:
@@ -892,7 +816,7 @@ static struct genl_multicast_group ovs_dp_flow_multicast_group = {
 
 /* Called with genl_lock. */
 static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp,
-                                 struct sk_buff *skb, u32 pid,
+                                 struct sk_buff *skb, u32 portid,
                                  u32 seq, u32 flags, u8 cmd)
 {
        const int skb_orig_len = skb->len;
@@ -907,7 +831,7 @@ static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp,
        sf_acts = rcu_dereference_protected(flow->sf_acts,
                                            lockdep_genl_is_held());
 
-       ovs_header = genlmsg_put(skb, pid, seq, &dp_flow_genl_family, flags, cmd);
+       ovs_header = genlmsg_put(skb, portid, seq, &dp_flow_genl_family, flags, cmd);
        if (!ovs_header)
                return -EMSGSIZE;
 
@@ -991,7 +915,7 @@ static struct sk_buff *ovs_flow_cmd_alloc_info(struct sw_flow *flow)
 
 static struct sk_buff *ovs_flow_cmd_build_info(struct sw_flow *flow,
                                               struct datapath *dp,
-                                              u32 pid, u32 seq, u8 cmd)
+                                              u32 portid, u32 seq, u8 cmd)
 {
        struct sk_buff *skb;
        int retval;
@@ -1000,7 +924,7 @@ static struct sk_buff *ovs_flow_cmd_build_info(struct sw_flow *flow,
        if (!skb)
                return ERR_PTR(-ENOMEM);
 
-       retval = ovs_flow_cmd_fill_info(flow, dp, skb, pid, seq, 0, cmd);
+       retval = ovs_flow_cmd_fill_info(flow, dp, skb, portid, seq, 0, cmd);
        BUG_ON(retval < 0);
        return skb;
 }
@@ -1080,7 +1004,7 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
                /* Put flow in bucket. */
                ovs_flow_tbl_insert(table, flow, &key, key_len);
 
-               reply = ovs_flow_cmd_build_info(flow, dp, info->snd_pid,
+               reply = ovs_flow_cmd_build_info(flow, dp, info->snd_portid,
                                                info->snd_seq,
                                                OVS_FLOW_CMD_NEW);
        } else {
@@ -1118,7 +1042,7 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
                        ovs_flow_deferred_free_acts(old_acts);
                }
 
-               reply = ovs_flow_cmd_build_info(flow, dp, info->snd_pid,
+               reply = ovs_flow_cmd_build_info(flow, dp, info->snd_portid,
                                               info->snd_seq, OVS_FLOW_CMD_NEW);
 
                /* Clear stats. */
@@ -1130,7 +1054,7 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
        }
 
        if (!IS_ERR(reply))
-               genl_notify(reply, genl_info_net(info), info->snd_pid,
+               genl_notify(reply, genl_info_net(info), info->snd_portid,
                           ovs_dp_flow_multicast_group.id, info->nlhdr,
                           GFP_KERNEL);
        else
@@ -1139,7 +1063,7 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
        return 0;
 
 error_free_flow:
-       ovs_flow_put(flow);
+       ovs_flow_free(flow);
 error:
        return error;
 }
@@ -1171,7 +1095,7 @@ static int ovs_flow_cmd_get(struct sk_buff *skb, struct genl_info *info)
        if (!flow)
                return -ENOENT;
 
-       reply = ovs_flow_cmd_build_info(flow, dp, info->snd_pid,
+       reply = ovs_flow_cmd_build_info(flow, dp, info->snd_portid,
                                        info->snd_seq, OVS_FLOW_CMD_NEW);
        if (IS_ERR(reply))
                return PTR_ERR(reply);
@@ -1213,13 +1137,13 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info)
 
        ovs_flow_tbl_remove(table, flow);
 
-       err = ovs_flow_cmd_fill_info(flow, dp, reply, info->snd_pid,
+       err = ovs_flow_cmd_fill_info(flow, dp, reply, info->snd_portid,
                                     info->snd_seq, 0, OVS_FLOW_CMD_DEL);
        BUG_ON(err < 0);
 
        ovs_flow_deferred_free(flow);
 
-       genl_notify(reply, genl_info_net(info), info->snd_pid,
+       genl_notify(reply, genl_info_net(info), info->snd_portid,
                    ovs_dp_flow_multicast_group.id, info->nlhdr, GFP_KERNEL);
        return 0;
 }
@@ -1247,7 +1171,7 @@ static int ovs_flow_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb)
                        break;
 
                if (ovs_flow_cmd_fill_info(flow, dp, skb,
-                                          NETLINK_CB(cb->skb).pid,
+                                          NETLINK_CB(cb->skb).portid,
                                           cb->nlh->nlmsg_seq, NLM_F_MULTI,
                                           OVS_FLOW_CMD_NEW) < 0)
                        break;
@@ -1303,13 +1227,13 @@ static struct genl_multicast_group ovs_dp_datapath_multicast_group = {
 };
 
 static int ovs_dp_cmd_fill_info(struct datapath *dp, struct sk_buff *skb,
-                               u32 pid, u32 seq, u32 flags, u8 cmd)
+                               u32 portid, u32 seq, u32 flags, u8 cmd)
 {
        struct ovs_header *ovs_header;
        struct ovs_dp_stats dp_stats;
        int err;
 
-       ovs_header = genlmsg_put(skb, pid, seq, &dp_datapath_genl_family,
+       ovs_header = genlmsg_put(skb, portid, seq, &dp_datapath_genl_family,
                                   flags, cmd);
        if (!ovs_header)
                goto error;
@@ -1334,7 +1258,7 @@ error:
        return -EMSGSIZE;
 }
 
-static struct sk_buff *ovs_dp_cmd_build_info(struct datapath *dp, u32 pid,
+static struct sk_buff *ovs_dp_cmd_build_info(struct datapath *dp, u32 portid,
                                             u32 seq, u8 cmd)
 {
        struct sk_buff *skb;
@@ -1344,7 +1268,7 @@ static struct sk_buff *ovs_dp_cmd_build_info(struct datapath *dp, u32 pid,
        if (!skb)
                return ERR_PTR(-ENOMEM);
 
-       retval = ovs_dp_cmd_fill_info(dp, skb, pid, seq, 0, cmd);
+       retval = ovs_dp_cmd_fill_info(dp, skb, portid, seq, 0, cmd);
        if (retval < 0) {
                kfree_skb(skb);
                return ERR_PTR(retval);
@@ -1402,11 +1326,6 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info)
        if (dp == NULL)
                goto err_unlock_rtnl;
 
-       /* Initialize kobject for bridge.  This will be added as
-        * /sys/class/net/<devname>/brif later, if sysfs is enabled. */
-       dp->ifobj.kset = NULL;
-       kobject_init(&dp->ifobj, &dp_ktype);
-
        ovs_dp_set_net(dp, hold_net(sock_net(skb->sk)));
 
        /* Allocate table. */
@@ -1437,7 +1356,7 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info)
        parms.options = NULL;
        parms.dp = dp;
        parms.port_no = OVSP_LOCAL;
-       parms.upcall_pid = nla_get_u32(a[OVS_DP_ATTR_UPCALL_PID]);
+       parms.upcall_portid = nla_get_u32(a[OVS_DP_ATTR_UPCALL_PID]);
 
        vport = new_vport(&parms);
        if (IS_ERR(vport)) {
@@ -1448,7 +1367,7 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info)
                goto err_destroy_ports_array;
        }
 
-       reply = ovs_dp_cmd_build_info(dp, info->snd_pid,
+       reply = ovs_dp_cmd_build_info(dp, info->snd_portid,
                                      info->snd_seq, OVS_DP_CMD_NEW);
        err = PTR_ERR(reply);
        if (IS_ERR(reply))
@@ -1456,11 +1375,10 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info)
 
        ovs_net = net_generic(ovs_dp_get_net(dp), ovs_net_id);
        list_add_tail(&dp->list_node, &ovs_net->dps);
-       ovs_dp_sysfs_add_dp(dp);
 
        rtnl_unlock();
 
-       genl_notify(reply, genl_info_net(info), info->snd_pid,
+       genl_notify(reply, genl_info_net(info), info->snd_portid,
                    ovs_dp_datapath_multicast_group.id, info->nlhdr,
                    GFP_KERNEL);
        return 0;
@@ -1498,7 +1416,6 @@ static void __dp_destroy(struct datapath *dp)
                                ovs_dp_detach_port(vport);
        }
 
-       ovs_dp_sysfs_del_dp(dp);
        list_del(&dp->list_node);
        ovs_dp_detach_port(ovs_vport_rtnl(dp, OVSP_LOCAL));
 
@@ -1527,7 +1444,7 @@ static int ovs_dp_cmd_del(struct sk_buff *skb, struct genl_info *info)
        if (IS_ERR(dp))
                return err;
 
-       reply = ovs_dp_cmd_build_info(dp, info->snd_pid,
+       reply = ovs_dp_cmd_build_info(dp, info->snd_portid,
                                      info->snd_seq, OVS_DP_CMD_DEL);
        err = PTR_ERR(reply);
        if (IS_ERR(reply))
@@ -1535,7 +1452,7 @@ static int ovs_dp_cmd_del(struct sk_buff *skb, struct genl_info *info)
 
        __dp_destroy(dp);
 
-       genl_notify(reply, genl_info_net(info), info->snd_pid,
+       genl_notify(reply, genl_info_net(info), info->snd_portid,
                    ovs_dp_datapath_multicast_group.id, info->nlhdr,
                    GFP_KERNEL);
 
@@ -1556,7 +1473,7 @@ static int ovs_dp_cmd_set(struct sk_buff *skb, struct genl_info *info)
        if (IS_ERR(dp))
                return PTR_ERR(dp);
 
-       reply = ovs_dp_cmd_build_info(dp, info->snd_pid,
+       reply = ovs_dp_cmd_build_info(dp, info->snd_portid,
                                      info->snd_seq, OVS_DP_CMD_NEW);
        if (IS_ERR(reply)) {
                err = PTR_ERR(reply);
@@ -1565,7 +1482,7 @@ static int ovs_dp_cmd_set(struct sk_buff *skb, struct genl_info *info)
                return 0;
        }
 
-       genl_notify(reply, genl_info_net(info), info->snd_pid,
+       genl_notify(reply, genl_info_net(info), info->snd_portid,
                    ovs_dp_datapath_multicast_group.id, info->nlhdr,
                    GFP_KERNEL);
 
@@ -1586,7 +1503,7 @@ static int ovs_dp_cmd_get(struct sk_buff *skb, struct genl_info *info)
        if (IS_ERR(dp))
                return PTR_ERR(dp);
 
-       reply = ovs_dp_cmd_build_info(dp, info->snd_pid,
+       reply = ovs_dp_cmd_build_info(dp, info->snd_portid,
                                      info->snd_seq, OVS_DP_CMD_NEW);
        if (IS_ERR(reply))
                return PTR_ERR(reply);
@@ -1603,7 +1520,7 @@ static int ovs_dp_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb)
 
        list_for_each_entry(dp, &ovs_net->dps, list_node) {
                if (i >= skip &&
-                   ovs_dp_cmd_fill_info(dp, skb, NETLINK_CB(cb->skb).pid,
+                   ovs_dp_cmd_fill_info(dp, skb, NETLINK_CB(cb->skb).portid,
                                         cb->nlh->nlmsg_seq, NLM_F_MULTI,
                                         OVS_DP_CMD_NEW) < 0)
                        break;
@@ -1669,13 +1586,13 @@ struct genl_multicast_group ovs_dp_vport_multicast_group = {
 
 /* Called with RTNL lock or RCU read lock. */
 static int ovs_vport_cmd_fill_info(struct vport *vport, struct sk_buff *skb,
-                                  u32 pid, u32 seq, u32 flags, u8 cmd)
+                                  u32 portid, u32 seq, u32 flags, u8 cmd)
 {
        struct ovs_header *ovs_header;
        struct ovs_vport_stats vport_stats;
        int err;
 
-       ovs_header = genlmsg_put(skb, pid, seq, &dp_vport_genl_family,
+       ovs_header = genlmsg_put(skb, portid, seq, &dp_vport_genl_family,
                                 flags, cmd);
        if (!ovs_header)
                return -EMSGSIZE;
@@ -1685,7 +1602,7 @@ static int ovs_vport_cmd_fill_info(struct vport *vport, struct sk_buff *skb,
        if (nla_put_u32(skb, OVS_VPORT_ATTR_PORT_NO, vport->port_no) ||
            nla_put_u32(skb, OVS_VPORT_ATTR_TYPE, vport->ops->type) ||
            nla_put_string(skb, OVS_VPORT_ATTR_NAME, vport->ops->get_name(vport)) ||
-           nla_put_u32(skb, OVS_VPORT_ATTR_UPCALL_PID, vport->upcall_pid))
+           nla_put_u32(skb, OVS_VPORT_ATTR_UPCALL_PID, vport->upcall_portid))
                goto nla_put_failure;
 
        ovs_vport_get_stats(vport, &vport_stats);
@@ -1711,7 +1628,7 @@ error:
 }
 
 /* Called with RTNL lock or RCU read lock. */
-struct sk_buff *ovs_vport_cmd_build_info(struct vport *vport, u32 pid,
+struct sk_buff *ovs_vport_cmd_build_info(struct vport *vport, u32 portid,
                                         u32 seq, u8 cmd)
 {
        struct sk_buff *skb;
@@ -1721,7 +1638,7 @@ struct sk_buff *ovs_vport_cmd_build_info(struct vport *vport, u32 pid,
        if (!skb)
                return ERR_PTR(-ENOMEM);
 
-       retval = ovs_vport_cmd_fill_info(vport, skb, pid, seq, 0, cmd);
+       retval = ovs_vport_cmd_fill_info(vport, skb, portid, seq, 0, cmd);
        if (retval < 0) {
                kfree_skb(skb);
                return ERR_PTR(retval);
@@ -1762,7 +1679,7 @@ static struct vport *lookup_vport(struct net *net,
 
                vport = ovs_vport_rtnl_rcu(dp, port_no);
                if (!vport)
-                       return ERR_PTR(-ENOENT);
+                       return ERR_PTR(-ENODEV);
                return vport;
        } else
                return ERR_PTR(-EINVAL);
@@ -1837,18 +1754,16 @@ static int ovs_vport_cmd_new(struct sk_buff *skb, struct genl_info *info)
        parms.options = a[OVS_VPORT_ATTR_OPTIONS];
        parms.dp = dp;
        parms.port_no = port_no;
-       parms.upcall_pid = nla_get_u32(a[OVS_VPORT_ATTR_UPCALL_PID]);
+       parms.upcall_portid = nla_get_u32(a[OVS_VPORT_ATTR_UPCALL_PID]);
 
        vport = new_vport(&parms);
        err = PTR_ERR(vport);
        if (IS_ERR(vport))
                goto exit_unlock;
 
-       ovs_dp_sysfs_add_if(vport);
-
        err = change_vport(vport, a);
        if (!err) {
-               reply = ovs_vport_cmd_build_info(vport, info->snd_pid,
+               reply = ovs_vport_cmd_build_info(vport, info->snd_portid,
                                                 info->snd_seq,
                                                 OVS_VPORT_CMD_NEW);
                if (IS_ERR(reply))
@@ -1858,7 +1773,7 @@ static int ovs_vport_cmd_new(struct sk_buff *skb, struct genl_info *info)
                ovs_dp_detach_port(vport);
                goto exit_unlock;
        }
-       genl_notify(reply, genl_info_net(info), info->snd_pid,
+       genl_notify(reply, genl_info_net(info), info->snd_portid,
                    ovs_dp_vport_multicast_group.id, info->nlhdr, GFP_KERNEL);
 
 exit_unlock:
@@ -1896,17 +1811,17 @@ static int ovs_vport_cmd_set(struct sk_buff *skb, struct genl_info *info)
        else
                goto exit_unlock;
        if (!err && a[OVS_VPORT_ATTR_UPCALL_PID])
-               vport->upcall_pid = nla_get_u32(a[OVS_VPORT_ATTR_UPCALL_PID]);
+               vport->upcall_portid = nla_get_u32(a[OVS_VPORT_ATTR_UPCALL_PID]);
 
-       reply = ovs_vport_cmd_build_info(vport, info->snd_pid, info->snd_seq,
-                                        OVS_VPORT_CMD_NEW);
+       reply = ovs_vport_cmd_build_info(vport, info->snd_portid,
+                                        info->snd_seq, OVS_VPORT_CMD_NEW);
        if (IS_ERR(reply)) {
                netlink_set_err(GENL_SOCK(sock_net(skb->sk)), 0,
                                ovs_dp_vport_multicast_group.id, PTR_ERR(reply));
                goto exit_unlock;
        }
 
-       genl_notify(reply, genl_info_net(info), info->snd_pid,
+       genl_notify(reply, genl_info_net(info), info->snd_portid,
                    ovs_dp_vport_multicast_group.id, info->nlhdr, GFP_KERNEL);
 
 exit_unlock:
@@ -1937,15 +1852,15 @@ static int ovs_vport_cmd_del(struct sk_buff *skb, struct genl_info *info)
                goto exit_unlock;
        }
 
-       reply = ovs_vport_cmd_build_info(vport, info->snd_pid, info->snd_seq,
-                                        OVS_VPORT_CMD_DEL);
+       reply = ovs_vport_cmd_build_info(vport, info->snd_portid,
+                                        info->snd_seq, OVS_VPORT_CMD_DEL);
        err = PTR_ERR(reply);
        if (IS_ERR(reply))
                goto exit_unlock;
 
        ovs_dp_detach_port(vport);
 
-       genl_notify(reply, genl_info_net(info), info->snd_pid,
+       genl_notify(reply, genl_info_net(info), info->snd_portid,
                    ovs_dp_vport_multicast_group.id, info->nlhdr, GFP_KERNEL);
 
 exit_unlock:
@@ -1972,8 +1887,8 @@ static int ovs_vport_cmd_get(struct sk_buff *skb, struct genl_info *info)
        if (IS_ERR(vport))
                goto exit_unlock;
 
-       reply = ovs_vport_cmd_build_info(vport, info->snd_pid, info->snd_seq,
-                                        OVS_VPORT_CMD_NEW);
+       reply = ovs_vport_cmd_build_info(vport, info->snd_portid,
+                                        info->snd_seq, OVS_VPORT_CMD_NEW);
        err = PTR_ERR(reply);
        if (IS_ERR(reply))
                goto exit_unlock;
@@ -2008,7 +1923,7 @@ static int ovs_vport_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb)
                hlist_for_each_entry_rcu(vport, n, &dp->ports[i], dp_hash_node) {
                        if (j >= skip &&
                            ovs_vport_cmd_fill_info(vport, skb,
-                                                   NETLINK_CB(cb->skb).pid,
+                                                   NETLINK_CB(cb->skb).portid,
                                                    cb->nlh->nlmsg_seq,
                                                    NLM_F_MULTI,
                                                    OVS_VPORT_CMD_NEW) < 0)
@@ -2176,10 +2091,9 @@ static struct pernet_operations ovs_net_ops = {
 
 static int __init dp_init(void)
 {
-       struct sk_buff *dummy_skb;
        int err;
 
-       BUILD_BUG_ON(sizeof(struct ovs_skb_cb) > sizeof(dummy_skb->cb));
+       BUILD_BUG_ON(sizeof(struct ovs_skb_cb) > FIELD_SIZEOF(struct sk_buff, cb));
 
        pr_info("Open vSwitch switching datapath %s, built "__DATE__" "__TIME__"\n",
                VERSION);
index c5df12d..2b93348 100644 (file)
@@ -28,7 +28,6 @@
 
 #include "checksum.h"
 #include "compat.h"
-#include "dp_sysfs.h"
 #include "flow.h"
 #include "tunnel.h"
 #include "vlan.h"
@@ -62,7 +61,6 @@ struct dp_stats_percpu {
  * struct datapath - datapath for flow-based packet switching
  * @rcu: RCU callback head for deferred destruction.
  * @list_node: Element in global 'dps' list.
- * @ifobj: Represents /sys/class/net/<devname>/brif.  Protected by RTNL.
  * @n_flows: Number of flows currently in flow table.
  * @table: Current flow table.  Protected by genl_lock and RCU.
  * @ports: Hash table for ports.  %OVSP_LOCAL port always exists.  Protected by
@@ -76,7 +74,6 @@ struct dp_stats_percpu {
 struct datapath {
        struct rcu_head rcu;
        struct list_head list_node;
-       struct kobject ifobj;
 
        /* Flow table. */
        struct flow_table __rcu *table;
@@ -125,7 +122,7 @@ struct ovs_skb_cb {
  * @key: Becomes %OVS_PACKET_ATTR_KEY.  Must be nonnull.
  * @userdata: If nonnull, its u64 value is extracted and passed to userspace as
  * %OVS_PACKET_ATTR_USERDATA.
- * @pid: Netlink PID to which packet should be sent.  If @pid is 0 then no
+ * @portid: Netlink PID to which packet should be sent.  If @portid is 0 then no
  * packet is sent and the packet is accounted in the datapath's @n_lost
  * counter.
  */
@@ -133,7 +130,7 @@ struct dp_upcall_info {
        u8 cmd;
        const struct sw_flow_key *key;
        const struct nlattr *userdata;
-       u32 pid;
+       u32 portid;
 };
 
 /**
@@ -181,7 +178,6 @@ static inline struct vport *ovs_vport_rtnl(const struct datapath *dp, int port_n
 
 extern struct notifier_block ovs_dp_device_notifier;
 extern struct genl_multicast_group ovs_dp_vport_multicast_group;
-extern int (*ovs_dp_ioctl_hook)(struct net_device *dev, struct ifreq *rq, int cmd);
 
 void ovs_dp_process_received_packet(struct vport *, struct sk_buff *);
 void ovs_dp_detach_port(struct vport *);
@@ -189,7 +185,7 @@ int ovs_dp_upcall(struct datapath *, struct sk_buff *,
                  const struct dp_upcall_info *);
 
 const char *ovs_dp_name(const struct datapath *dp);
-struct sk_buff *ovs_vport_cmd_build_info(struct vport *, u32 pid, u32 seq,
+struct sk_buff *ovs_vport_cmd_build_info(struct vport *, u32 portid, u32 seq,
                                         u8 cmd);
 
 int ovs_execute_actions(struct datapath *dp, struct sk_buff *skb);
index 13085d6..c9eeafe 100644 (file)
@@ -59,12 +59,6 @@ static int dp_device_event(struct notifier_block *unused, unsigned long event,
                }
                break;
 
-       case NETDEV_CHANGENAME:
-               if (vport->port_no != OVSP_LOCAL) {
-                       ovs_dp_sysfs_del_if(vport);
-                       ovs_dp_sysfs_add_if(vport);
-               }
-               break;
        }
 
        return NOTIFY_DONE;
diff --git a/datapath/dp_sysfs.h b/datapath/dp_sysfs.h
deleted file mode 100644 (file)
index 526b0a1..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (c) 2007-2011 Nicira, Inc.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public
- * License as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA
- */
-
-#ifndef DP_SYSFS_H
-#define DP_SYSFS_H 1
-
-struct datapath;
-struct vport;
-
-/* dp_sysfs_dp.c */
-int ovs_dp_sysfs_add_dp(struct datapath *dp);
-int ovs_dp_sysfs_del_dp(struct datapath *dp);
-
-/* dp_sysfs_if.c */
-int ovs_dp_sysfs_add_if(struct vport *p);
-int ovs_dp_sysfs_del_if(struct vport *p);
-
-#ifdef CONFIG_SYSFS
-extern struct sysfs_ops ovs_brport_sysfs_ops;
-#endif
-
-#endif /* dp_sysfs.h */
-
diff --git a/datapath/dp_sysfs_dp.c b/datapath/dp_sysfs_dp.c
deleted file mode 100644 (file)
index 3ecacd7..0000000
+++ /dev/null
@@ -1,419 +0,0 @@
-/*
- * Copyright (c) 2007-2012 Nicira, Inc.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public
- * License as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/version.h>
-
-/*
- *     Sysfs attributes of bridge for Open vSwitch
- *
- *  This has been shamelessly copied from the kernel sources.
- */
-
-#include <linux/capability.h>
-#include <linux/device.h>
-#include <linux/kernel.h>
-#include <linux/netdevice.h>
-#include <linux/if_bridge.h>
-#include <linux/rtnetlink.h>
-#include <linux/version.h>
-
-#include "dp_sysfs.h"
-#include "datapath.h"
-#include "vport-internal_dev.h"
-
-#ifdef CONFIG_SYSFS
-
-/* Hack to attempt to build on more platforms. */
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)
-#define INTERNAL_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 INTERNAL_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
-
-static struct datapath *sysfs_get_dp(struct net_device *netdev)
-{
-       struct vport *vport = ovs_internal_dev_get_vport(netdev);
-       return vport ? vport->dp : NULL;
-}
-/*
- * Common code for storing bridge parameters.
- */
-static ssize_t store_bridge_parm(DEVICE_PARAMS,
-                                const char *buf, size_t len,
-                                void (*set)(struct datapath *, unsigned long))
-{
-       char *endp;
-       unsigned long val;
-       ssize_t result = len;
-
-       if (!capable(CAP_NET_ADMIN))
-               return -EPERM;
-
-       val = simple_strtoul(buf, &endp, 0);
-       if (endp == buf)
-               return -EINVAL;
-
-       /* xxx We use a default value of 0 for all fields.  If the caller is
-        * xxx attempting to set the value to our default, just silently
-        * xxx ignore the request.
-        */
-       if (val != 0) {
-               struct datapath *dp;
-
-               rcu_read_lock();
-
-               dp = sysfs_get_dp(to_net_dev(d));
-               if (dp)
-                       pr_warning("%s: xxx writing dp parms not supported yet!\n",
-                              ovs_dp_name(dp));
-               else
-                       result = -ENODEV;
-
-               rcu_read_unlock();
-       }
-
-       return result;
-}
-
-
-static ssize_t show_forward_delay(DEVICE_PARAMS, char *buf)
-{
-       return sprintf(buf, "%d\n", 0);
-}
-
-static void set_forward_delay(struct datapath *dp, unsigned long val)
-{
-       pr_info("%s: xxx attempt to set_forward_delay()\n", ovs_dp_name(dp));
-}
-
-static ssize_t store_forward_delay(DEVICE_PARAMS,
-                                  const char *buf, size_t len)
-{
-       return store_bridge_parm(DEVICE_ARGS, buf, len, set_forward_delay);
-}
-static INTERNAL_DEVICE_ATTR(forward_delay, S_IRUGO | S_IWUSR,
-                  show_forward_delay, store_forward_delay);
-
-static ssize_t show_hello_time(DEVICE_PARAMS, char *buf)
-{
-       return sprintf(buf, "%d\n", 0);
-}
-
-static void set_hello_time(struct datapath *dp, unsigned long val)
-{
-       pr_info("%s: xxx attempt to set_hello_time()\n", ovs_dp_name(dp));
-}
-
-static ssize_t store_hello_time(DEVICE_PARAMS,
-                               const char *buf,
-                               size_t len)
-{
-       return store_bridge_parm(DEVICE_ARGS, buf, len, set_hello_time);
-}
-static INTERNAL_DEVICE_ATTR(hello_time, S_IRUGO | S_IWUSR, show_hello_time,
-                  store_hello_time);
-
-static ssize_t show_max_age(DEVICE_PARAMS, char *buf)
-{
-       return sprintf(buf, "%d\n", 0);
-}
-
-static void set_max_age(struct datapath *dp, unsigned long val)
-{
-       pr_info("%s: xxx attempt to set_max_age()\n", ovs_dp_name(dp));
-}
-
-static ssize_t store_max_age(DEVICE_PARAMS,
-                            const char *buf, size_t len)
-{
-       return store_bridge_parm(DEVICE_ARGS, buf, len, set_max_age);
-}
-static INTERNAL_DEVICE_ATTR(max_age, S_IRUGO | S_IWUSR, show_max_age, store_max_age);
-
-static ssize_t show_ageing_time(DEVICE_PARAMS, char *buf)
-{
-       return sprintf(buf, "%d\n", 0);
-}
-
-static void set_ageing_time(struct datapath *dp, unsigned long val)
-{
-       pr_info("%s: xxx attempt to set_ageing_time()\n", ovs_dp_name(dp));
-}
-
-static ssize_t store_ageing_time(DEVICE_PARAMS,
-                                const char *buf, size_t len)
-{
-       return store_bridge_parm(DEVICE_ARGS, buf, len, set_ageing_time);
-}
-static INTERNAL_DEVICE_ATTR(ageing_time, S_IRUGO | S_IWUSR, show_ageing_time,
-                  store_ageing_time);
-
-static ssize_t show_stp_state(DEVICE_PARAMS, char *buf)
-{
-       return sprintf(buf, "%d\n", 0);
-}
-
-
-static ssize_t store_stp_state(DEVICE_PARAMS,
-                              const char *buf,
-                              size_t len)
-{
-       struct datapath *dp;
-       ssize_t result = len;
-
-       rcu_read_lock();
-
-       dp = sysfs_get_dp(to_net_dev(d));
-       if (dp)
-               pr_info("%s: xxx attempt to set_stp_state()\n", ovs_dp_name(dp));
-       else
-               result = -ENODEV;
-
-       rcu_read_unlock();
-
-       return result;
-}
-static INTERNAL_DEVICE_ATTR(stp_state, S_IRUGO | S_IWUSR, show_stp_state,
-                  store_stp_state);
-
-static ssize_t show_priority(DEVICE_PARAMS, char *buf)
-{
-       return sprintf(buf, "%d\n", 0);
-}
-
-static void set_priority(struct datapath *dp, unsigned long val)
-{
-       pr_info("%s: xxx attempt to set_priority()\n", ovs_dp_name(dp));
-}
-
-static ssize_t store_priority(DEVICE_PARAMS,
-                              const char *buf, size_t len)
-{
-       return store_bridge_parm(DEVICE_ARGS, buf, len, set_priority);
-}
-static INTERNAL_DEVICE_ATTR(priority, S_IRUGO | S_IWUSR, show_priority, store_priority);
-
-static ssize_t show_root_id(DEVICE_PARAMS, char *buf)
-{
-       return sprintf(buf, "0000.010203040506\n");
-}
-static INTERNAL_DEVICE_ATTR(root_id, S_IRUGO, show_root_id, NULL);
-
-static ssize_t show_bridge_id(DEVICE_PARAMS, char *buf)
-{
-       struct vport *vport;
-       ssize_t result;
-
-       rcu_read_lock();
-
-       vport = ovs_internal_dev_get_vport(to_net_dev(d));
-       if (vport) {
-               const unsigned char *addr;
-
-               addr = vport->ops->get_addr(vport);
-               result = sprintf(buf, "%.2x%.2x.%.2x%.2x%.2x%.2x%.2x%.2x\n",
-                                0, 0, addr[0], addr[1], addr[2], addr[3],
-                                addr[4], addr[5]);
-       } else
-               result = -ENODEV;
-
-       rcu_read_unlock();
-
-       return result;
-}
-static INTERNAL_DEVICE_ATTR(bridge_id, S_IRUGO, show_bridge_id, NULL);
-
-static ssize_t show_root_port(DEVICE_PARAMS, char *buf)
-{
-       return sprintf(buf, "%d\n", 0);
-}
-static INTERNAL_DEVICE_ATTR(root_port, S_IRUGO, show_root_port, NULL);
-
-static ssize_t show_root_path_cost(DEVICE_PARAMS, char *buf)
-{
-       return sprintf(buf, "%d\n", 0);
-}
-static INTERNAL_DEVICE_ATTR(root_path_cost, S_IRUGO, show_root_path_cost, NULL);
-
-static ssize_t show_topology_change(DEVICE_PARAMS, char *buf)
-{
-       return sprintf(buf, "%d\n", 0);
-}
-static INTERNAL_DEVICE_ATTR(topology_change, S_IRUGO, show_topology_change, NULL);
-
-static ssize_t show_topology_change_detected(DEVICE_PARAMS, char *buf)
-{
-       return sprintf(buf, "%d\n", 0);
-}
-static INTERNAL_DEVICE_ATTR(topology_change_detected, S_IRUGO,
-                  show_topology_change_detected, NULL);
-
-static ssize_t show_hello_timer(DEVICE_PARAMS, char *buf)
-{
-       return sprintf(buf, "%d\n", 0);
-}
-static INTERNAL_DEVICE_ATTR(hello_timer, S_IRUGO, show_hello_timer, NULL);
-
-static ssize_t show_tcn_timer(DEVICE_PARAMS, char *buf)
-{
-       return sprintf(buf, "%d\n", 0);
-}
-static INTERNAL_DEVICE_ATTR(tcn_timer, S_IRUGO, show_tcn_timer, NULL);
-
-static ssize_t show_topology_change_timer(DEVICE_PARAMS, char *buf)
-{
-       return sprintf(buf, "%d\n", 0);
-}
-static INTERNAL_DEVICE_ATTR(topology_change_timer, S_IRUGO, show_topology_change_timer,
-                  NULL);
-
-static ssize_t show_gc_timer(DEVICE_PARAMS, char *buf)
-{
-       return sprintf(buf, "%d\n", 0);
-}
-static INTERNAL_DEVICE_ATTR(gc_timer, S_IRUGO, show_gc_timer, NULL);
-
-static ssize_t show_group_addr(DEVICE_PARAMS, char *buf)
-{
-       return sprintf(buf, "00:01:02:03:04:05\n");
-}
-
-static ssize_t store_group_addr(DEVICE_PARAMS,
-                               const char *buf, size_t len)
-{
-       struct datapath *dp;
-       ssize_t result = len;
-
-       rcu_read_lock();
-
-       dp = sysfs_get_dp(to_net_dev(d));
-       if (dp)
-               pr_info("%s: xxx attempt to store_group_addr()\n",
-                      ovs_dp_name(dp));
-       else
-               result = -ENODEV;
-
-       rcu_read_unlock();
-
-       return result;
-}
-
-static INTERNAL_DEVICE_ATTR(group_addr, S_IRUGO | S_IWUSR,
-                  show_group_addr, store_group_addr);
-
-static struct attribute *bridge_attrs[] = {
-       &DEV_ATTR(forward_delay).attr,
-       &DEV_ATTR(hello_time).attr,
-       &DEV_ATTR(max_age).attr,
-       &DEV_ATTR(ageing_time).attr,
-       &DEV_ATTR(stp_state).attr,
-       &DEV_ATTR(priority).attr,
-       &DEV_ATTR(bridge_id).attr,
-       &DEV_ATTR(root_id).attr,
-       &DEV_ATTR(root_path_cost).attr,
-       &DEV_ATTR(root_port).attr,
-       &DEV_ATTR(topology_change).attr,
-       &DEV_ATTR(topology_change_detected).attr,
-       &DEV_ATTR(hello_timer).attr,
-       &DEV_ATTR(tcn_timer).attr,
-       &DEV_ATTR(topology_change_timer).attr,
-       &DEV_ATTR(gc_timer).attr,
-       &DEV_ATTR(group_addr).attr,
-       NULL
-};
-
-static struct attribute_group bridge_group = {
-       .name = SYSFS_BRIDGE_ATTR, /* "bridge" */
-       .attrs = bridge_attrs,
-};
-
-/*
- * Add entries in sysfs onto the existing network class device
- * for the bridge.
- *   Adds a attribute group "bridge" containing tuning parameters.
- *   Sub directory to hold links to interfaces.
- *
- * Note: the ifobj exists only to be a subdirectory
- *   to hold links.  The ifobj exists in the same data structure
- *   as its parent the bridge so reference counting works.
- */
-int ovs_dp_sysfs_add_dp(struct datapath *dp)
-{
-       struct vport *vport = ovs_vport_rtnl(dp, OVSP_LOCAL);
-       struct kobject *kobj = vport->ops->get_kobj(vport);
-       int err;
-
-#ifdef CONFIG_NET_NS
-       /* Due to bug in 2.6.32 kernel, sysfs_create_group() could panic
-        * in other namespace than init_net. Following check is to avoid it. */
-       if (!kobj->sd)
-               return -ENOENT;
-#endif
-       /* Create /sys/class/net/<devname>/bridge directory. */
-       err = sysfs_create_group(kobj, &bridge_group);
-       if (err) {
-               pr_info("%s: can't create group %s/%s\n",
-                       __func__, ovs_dp_name(dp), bridge_group.name);
-               goto out1;
-       }
-
-       /* Create /sys/class/net/<devname>/brif directory. */
-       err = kobject_add(&dp->ifobj, kobj, SYSFS_BRIDGE_PORT_SUBDIR);
-       if (err) {
-               pr_info("%s: can't add kobject (directory) %s/%s\n",
-                       __func__, ovs_dp_name(dp), kobject_name(&dp->ifobj));
-               goto out2;
-       }
-       kobject_uevent(&dp->ifobj, KOBJ_ADD);
-       return 0;
-
- out2:
-       sysfs_remove_group(kobj, &bridge_group);
- out1:
-       return err;
-}
-
-int ovs_dp_sysfs_del_dp(struct datapath *dp)
-{
-       struct vport *vport = ovs_vport_rtnl(dp, OVSP_LOCAL);
-       struct kobject *kobj = vport->ops->get_kobj(vport);
-
-#ifdef CONFIG_NET_NS
-       if (!kobj->sd)
-               return 0;
-#endif
-
-       kobject_del(&dp->ifobj);
-       sysfs_remove_group(kobj, &bridge_group);
-
-       return 0;
-}
-#else /* !CONFIG_SYSFS */
-int ovs_dp_sysfs_add_dp(struct datapath *dp) { return 0; }
-int ovs_dp_sysfs_del_dp(struct datapath *dp) { return 0; }
-int dp_sysfs_add_if(struct vport *p) { return 0; }
-int dp_sysfs_del_if(struct vport *p) { return 0; }
-#endif /* !CONFIG_SYSFS */
diff --git a/datapath/dp_sysfs_if.c b/datapath/dp_sysfs_if.c
deleted file mode 100644 (file)
index 219a260..0000000
+++ /dev/null
@@ -1,275 +0,0 @@
-/*
- * Copyright (c) 2007-2012 Nicira, Inc.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public
- * License as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/capability.h>
-#include <linux/kernel.h>
-#include <linux/netdevice.h>
-#include <linux/if_bridge.h>
-#include <linux/rtnetlink.h>
-
-#include "datapath.h"
-#include "dp_sysfs.h"
-#include "vport.h"
-
-#ifdef CONFIG_SYSFS
-
-struct brport_attribute {
-       struct attribute        attr;
-       ssize_t (*show)(struct vport *, char *);
-       ssize_t (*store)(struct vport *, unsigned long);
-};
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)
-#define BRPORT_ATTR(_name, _mode, _show, _store)               \
-struct brport_attribute brport_attr_##_name = {                        \
-       .attr = {.name = __stringify(_name),                    \
-                .mode = _mode },                               \
-       .show   = _show,                                        \
-       .store  = _store,                                       \
-};
-#else
-#define BRPORT_ATTR(_name, _mode, _show, _store)               \
-struct brport_attribute brport_attr_##_name = {                        \
-       .attr = {.name = __stringify(_name),                    \
-                .mode = _mode,                                 \
-                .owner = THIS_MODULE, },                       \
-       .show   = _show,                                        \
-       .store  = _store,                                       \
-};
-#endif
-
-static ssize_t show_path_cost(struct vport *p, char *buf)
-{
-       return sprintf(buf, "%d\n", 0);
-}
-static ssize_t store_path_cost(struct vport *p, unsigned long v)
-{
-       return 0;
-}
-static BRPORT_ATTR(path_cost, S_IRUGO | S_IWUSR,
-                  show_path_cost, store_path_cost);
-
-static ssize_t show_priority(struct vport *p, char *buf)
-{
-       return sprintf(buf, "%d\n", 0);
-}
-static ssize_t store_priority(struct vport *p, unsigned long v)
-{
-       return 0;
-}
-static BRPORT_ATTR(priority, S_IRUGO | S_IWUSR,
-                        show_priority, store_priority);
-
-static ssize_t show_designated_root(struct vport *p, char *buf)
-{
-       return sprintf(buf, "0000.010203040506\n");
-}
-static BRPORT_ATTR(designated_root, S_IRUGO, show_designated_root, NULL);
-
-static ssize_t show_designated_bridge(struct vport *p, char *buf)
-{
-       return sprintf(buf, "0000.060504030201\n");
-}
-static BRPORT_ATTR(designated_bridge, S_IRUGO, show_designated_bridge, NULL);
-
-static ssize_t show_designated_port(struct vport *p, char *buf)
-{
-       return sprintf(buf, "%d\n", 0);
-}
-static BRPORT_ATTR(designated_port, S_IRUGO, show_designated_port, NULL);
-
-static ssize_t show_designated_cost(struct vport *p, char *buf)
-{
-       return sprintf(buf, "%d\n", 0);
-}
-static BRPORT_ATTR(designated_cost, S_IRUGO, show_designated_cost, NULL);
-
-static ssize_t show_port_id(struct vport *p, char *buf)
-{
-       return sprintf(buf, "0x%x\n", 0);
-}
-static BRPORT_ATTR(port_id, S_IRUGO, show_port_id, NULL);
-
-static ssize_t show_port_no(struct vport *p, char *buf)
-{
-       return sprintf(buf, "0x%x\n", p->port_no);
-}
-
-static BRPORT_ATTR(port_no, S_IRUGO, show_port_no, NULL);
-
-static ssize_t show_change_ack(struct vport *p, char *buf)
-{
-       return sprintf(buf, "%d\n", 0);
-}
-static BRPORT_ATTR(change_ack, S_IRUGO, show_change_ack, NULL);
-
-static ssize_t show_config_pending(struct vport *p, char *buf)
-{
-       return sprintf(buf, "%d\n", 0);
-}
-static BRPORT_ATTR(config_pending, S_IRUGO, show_config_pending, NULL);
-
-static ssize_t show_port_state(struct vport *p, char *buf)
-{
-       return sprintf(buf, "%d\n", 0);
-}
-static BRPORT_ATTR(state, S_IRUGO, show_port_state, NULL);
-
-static ssize_t show_message_age_timer(struct vport *p, char *buf)
-{
-       return sprintf(buf, "%d\n", 0);
-}
-static BRPORT_ATTR(message_age_timer, S_IRUGO, show_message_age_timer, NULL);
-
-static ssize_t show_forward_delay_timer(struct vport *p, char *buf)
-{
-       return sprintf(buf, "%d\n", 0);
-}
-static BRPORT_ATTR(forward_delay_timer, S_IRUGO, show_forward_delay_timer, NULL);
-
-static ssize_t show_hold_timer(struct vport *p, char *buf)
-{
-       return sprintf(buf, "%d\n", 0);
-}
-static BRPORT_ATTR(hold_timer, S_IRUGO, show_hold_timer, NULL);
-
-static struct brport_attribute *brport_attrs[] = {
-       &brport_attr_path_cost,
-       &brport_attr_priority,
-       &brport_attr_port_id,
-       &brport_attr_port_no,
-       &brport_attr_designated_root,
-       &brport_attr_designated_bridge,
-       &brport_attr_designated_port,
-       &brport_attr_designated_cost,
-       &brport_attr_state,
-       &brport_attr_change_ack,
-       &brport_attr_config_pending,
-       &brport_attr_message_age_timer,
-       &brport_attr_forward_delay_timer,
-       &brport_attr_hold_timer,
-       NULL
-};
-
-#define to_vport_attr(_at) container_of(_at, struct brport_attribute, attr)
-#define to_vport(obj)  container_of(obj, struct vport, kobj)
-
-static ssize_t brport_show(struct kobject *kobj,
-                          struct attribute *attr, char *buf)
-{
-       struct brport_attribute *brport_attr = to_vport_attr(attr);
-       struct vport *p = to_vport(kobj);
-
-       return brport_attr->show(p, buf);
-}
-
-static ssize_t brport_store(struct kobject *kobj,
-                           struct attribute *attr,
-                           const char *buf, size_t count)
-{
-       struct vport *p = to_vport(kobj);
-       ssize_t ret = -EINVAL;
-
-       if (!capable(CAP_NET_ADMIN))
-               return -EPERM;
-
-       pr_warning("%s: xxx writing port parms not supported yet!\n",
-                  ovs_dp_name(p->dp));
-
-       return ret;
-}
-
-struct sysfs_ops ovs_brport_sysfs_ops = {
-       .show = brport_show,
-       .store = brport_store,
-};
-
-/*
- * Add sysfs entries to ethernet device added to a bridge.
- * Creates a brport subdirectory with bridge attributes.
- * Puts symlink in bridge's brport subdirectory
- */
-int ovs_dp_sysfs_add_if(struct vport *p)
-{
-       struct datapath *dp = p->dp;
-       struct vport *local_port = ovs_vport_rtnl(dp, OVSP_LOCAL);
-       struct brport_attribute **a;
-       int err;
-
-       /* Create /sys/class/net/<devname>/brport directory. */
-       if (!p->ops->get_kobj)
-               return -ENOENT;
-
-#ifdef CONFIG_NET_NS
-       /* Due to bug in 2.6.32 kernel, sysfs_create_group() could panic
-        * in other namespace than init_net. Following check is to avoid it. */
-
-       if (!p->kobj.sd)
-               return -ENOENT;
-#endif
-
-       err = kobject_add(&p->kobj, p->ops->get_kobj(p),
-                         SYSFS_BRIDGE_PORT_ATTR);
-       if (err)
-               goto err;
-
-       /* Create symlink from /sys/class/net/<devname>/brport/bridge to
-        * /sys/class/net/<bridgename>. */
-       err = sysfs_create_link(&p->kobj, local_port->ops->get_kobj(local_port),
-               SYSFS_BRIDGE_PORT_LINK); /* "bridge" */
-       if (err)
-               goto err_del;
-
-       /* Populate /sys/class/net/<devname>/brport directory with files. */
-       for (a = brport_attrs; *a; ++a) {
-               err = sysfs_create_file(&p->kobj, &((*a)->attr));
-               if (err)
-                       goto err_del;
-       }
-
-       /* Create symlink from /sys/class/net/<bridgename>/brif/<devname> to
-        * /sys/class/net/<devname>/brport.  */
-       err = sysfs_create_link(&dp->ifobj, &p->kobj, p->ops->get_name(p));
-       if (err)
-               goto err_del;
-       strcpy(p->linkname, p->ops->get_name(p));
-
-       kobject_uevent(&p->kobj, KOBJ_ADD);
-
-       return 0;
-
-err_del:
-       kobject_del(&p->kobj);
-err:
-       p->linkname[0] = 0;
-       return err;
-}
-
-int ovs_dp_sysfs_del_if(struct vport *p)
-{
-       if (p->linkname[0]) {
-               sysfs_remove_link(&p->dp->ifobj, p->linkname);
-               kobject_uevent(&p->kobj, KOBJ_REMOVE);
-               kobject_del(&p->kobj);
-               p->linkname[0] = '\0';
-       }
-       return 0;
-}
-#endif /* CONFIG_SYSFS */
index c70daee..63eef77 100644 (file)
@@ -226,9 +226,7 @@ struct sw_flow *ovs_flow_alloc(void)
                return ERR_PTR(-ENOMEM);
 
        spin_lock_init(&flow->lock);
-       atomic_set(&flow->refcnt, 1);
        flow->sf_acts = NULL;
-       flow->dead = false;
 
        return flow;
 }
@@ -290,12 +288,6 @@ struct flow_table *ovs_flow_tbl_alloc(int new_size)
        return table;
 }
 
-static void flow_free(struct sw_flow *flow)
-{
-       flow->dead = true;
-       ovs_flow_put(flow);
-}
-
 void ovs_flow_tbl_destroy(struct flow_table *table)
 {
        int i;
@@ -314,7 +306,7 @@ void ovs_flow_tbl_destroy(struct flow_table *table)
 
                hlist_for_each_entry_safe(flow, node, n, head, hash_node[ver]) {
                        hlist_del_rcu(&flow->hash_node[ver]);
-                       flow_free(flow);
+                       ovs_flow_free(flow);
                }
        }
 
@@ -418,13 +410,21 @@ struct flow_table *ovs_flow_tbl_expand(struct flow_table *table)
        return __flow_tbl_rehash(table, table->n_buckets * 2);
 }
 
+void ovs_flow_free(struct sw_flow *flow)
+{
+       if (unlikely(!flow))
+               return;
+
+       kfree((struct sf_flow_acts __force *)flow->sf_acts);
+       kmem_cache_free(flow_cache, flow);
+}
+
 /* RCU callback used by ovs_flow_deferred_free. */
 static void rcu_free_flow_callback(struct rcu_head *rcu)
 {
        struct sw_flow *flow = container_of(rcu, struct sw_flow, rcu);
 
-       flow->dead = true;
-       ovs_flow_put(flow);
+       ovs_flow_free(flow);
 }
 
 /* Schedules 'flow' to be freed after the next RCU grace period.
@@ -434,22 +434,6 @@ void ovs_flow_deferred_free(struct sw_flow *flow)
        call_rcu(&flow->rcu, rcu_free_flow_callback);
 }
 
-void ovs_flow_hold(struct sw_flow *flow)
-{
-       atomic_inc(&flow->refcnt);
-}
-
-void ovs_flow_put(struct sw_flow *flow)
-{
-       if (unlikely(!flow))
-               return;
-
-       if (atomic_dec_and_test(&flow->refcnt)) {
-               kfree((struct sf_flow_acts __force *)flow->sf_acts);
-               kmem_cache_free(flow_cache, flow);
-       }
-}
-
 /* RCU callback used by ovs_flow_deferred_free_acts. */
 static void rcu_free_acts_callback(struct rcu_head *rcu)
 {
@@ -638,8 +622,9 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key,
 
        key->phy.priority = skb->priority;
        if (OVS_CB(skb)->tun_key)
-               memcpy(&key->phy.tun.tun_key, OVS_CB(skb)->tun_key, sizeof(key->phy.tun.tun_key));
+               memcpy(&key->tun_key, OVS_CB(skb)->tun_key, sizeof(key->tun_key));
        key->phy.in_port = in_port;
+       key->phy.skb_mark = skb_get_mark(skb);
 
        skb_reset_mac_header(skb);
 
@@ -725,7 +710,8 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key,
                        }
                }
 
-       } else if (key->eth.type == htons(ETH_P_ARP) && arphdr_ok(skb)) {
+       } else if ((key->eth.type == htons(ETH_P_ARP) ||
+                  key->eth.type == htons(ETH_P_RARP)) && arphdr_ok(skb)) {
                struct arp_eth_header *arp;
 
                arp = (struct arp_eth_header *)skb_network_header(skb);
@@ -799,10 +785,10 @@ static u32 ovs_flow_hash(const struct sw_flow_key *key, int key_start, int key_l
 
 static int flow_key_start(struct sw_flow_key *key)
 {
-       if (key->phy.tun.tun_key.ipv4_dst)
+       if (key->tun_key.ipv4_dst)
                return 0;
        else
-               return offsetof(struct sw_flow_key, phy.priority);
+               return offsetof(struct sw_flow_key, phy);
 }
 
 struct sw_flow *ovs_flow_tbl_lookup(struct flow_table *table,
@@ -850,6 +836,7 @@ const int ovs_key_lens[OVS_KEY_ATTR_MAX + 1] = {
        [OVS_KEY_ATTR_ENCAP] = -1,
        [OVS_KEY_ATTR_PRIORITY] = sizeof(u32),
        [OVS_KEY_ATTR_IN_PORT] = sizeof(u32),
+       [OVS_KEY_ATTR_SKB_MARK] = sizeof(u32),
        [OVS_KEY_ATTR_ETHERNET] = sizeof(struct ovs_key_ethernet),
        [OVS_KEY_ATTR_VLAN] = sizeof(__be16),
        [OVS_KEY_ATTR_ETHERTYPE] = sizeof(__be16),
@@ -861,10 +848,10 @@ const int ovs_key_lens[OVS_KEY_ATTR_MAX + 1] = {
        [OVS_KEY_ATTR_ICMPV6] = sizeof(struct ovs_key_icmpv6),
        [OVS_KEY_ATTR_ARP] = sizeof(struct ovs_key_arp),
        [OVS_KEY_ATTR_ND] = sizeof(struct ovs_key_nd),
+       [OVS_KEY_ATTR_IPV4_TUNNEL] = sizeof(struct ovs_key_ipv4_tunnel),
 
        /* Not upstream. */
        [OVS_KEY_ATTR_TUN_ID] = sizeof(__be64),
-       [OVS_KEY_ATTR_IPV4_TUNNEL] = sizeof(struct ovs_key_ipv4_tunnel),
 };
 
 static int ipv4_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_len,
@@ -1039,6 +1026,15 @@ int ovs_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_lenp,
        } else {
                swkey->phy.in_port = DP_MAX_PORTS;
        }
+       if (attrs & (1 << OVS_KEY_ATTR_SKB_MARK)) {
+               uint32_t mark = nla_get_u32(a[OVS_KEY_ATTR_SKB_MARK]);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) && !defined(CONFIG_NETFILTER)
+               if (mark != 0)
+                       return -EINVAL;
+#endif
+               swkey->phy.skb_mark = mark;
+               attrs &= ~(1 << OVS_KEY_ATTR_SKB_MARK);
+       }
 
        if (attrs & (1ULL << OVS_KEY_ATTR_TUN_ID) &&
            attrs & (1ULL << OVS_KEY_ATTR_IPV4_TUNNEL)) {
@@ -1049,21 +1045,18 @@ int ovs_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_lenp,
 
                if (!tun_key->ipv4_dst)
                        return -EINVAL;
-               if (!(tun_key->tun_flags & OVS_FLOW_TNL_F_KEY))
+               if (!(tun_key->tun_flags & OVS_TNL_F_KEY))
                        return -EINVAL;
 
                tun_id = nla_get_be64(a[OVS_KEY_ATTR_TUN_ID]);
                if (tun_id != tun_key->tun_id)
                        return -EINVAL;
 
-               memcpy(&swkey->phy.tun.tun_key, tun_key, sizeof(swkey->phy.tun.tun_key));
-               attrs &= ~(1ULL << OVS_KEY_ATTR_TUN_ID);
-               attrs &= ~(1ULL << OVS_KEY_ATTR_IPV4_TUNNEL);
-       } else if (attrs & (1ULL << OVS_KEY_ATTR_TUN_ID)) {
-               swkey->phy.tun.tun_key.tun_id = nla_get_be64(a[OVS_KEY_ATTR_TUN_ID]);
-               swkey->phy.tun.tun_key.tun_flags |= OVS_FLOW_TNL_F_KEY;
+               memcpy(&swkey->tun_key, tun_key, sizeof(swkey->tun_key));
+               memset(swkey->tun_key.pad, 0, sizeof(swkey->tun_key.pad));
 
                attrs &= ~(1ULL << OVS_KEY_ATTR_TUN_ID);
+               attrs &= ~(1ULL << OVS_KEY_ATTR_IPV4_TUNNEL);
        } else if (attrs & (1ULL << OVS_KEY_ATTR_IPV4_TUNNEL)) {
                struct ovs_key_ipv4_tunnel *tun_key;
                tun_key = nla_data(a[OVS_KEY_ATTR_IPV4_TUNNEL]);
@@ -1071,7 +1064,9 @@ int ovs_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_lenp,
                if (!tun_key->ipv4_dst)
                        return -EINVAL;
 
-               memcpy(&swkey->phy.tun.tun_key, tun_key, sizeof(swkey->phy.tun.tun_key));
+               memcpy(&swkey->tun_key, tun_key, sizeof(swkey->tun_key));
+               memset(swkey->tun_key.pad, 0, sizeof(swkey->tun_key.pad));
+
                attrs &= ~(1ULL << OVS_KEY_ATTR_IPV4_TUNNEL);
        }
 
@@ -1173,7 +1168,8 @@ int ovs_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_lenp,
                        if (err)
                                return err;
                }
-       } else if (swkey->eth.type == htons(ETH_P_ARP)) {
+       } else if (swkey->eth.type == htons(ETH_P_ARP) ||
+                  swkey->eth.type == htons(ETH_P_RARP)) {
                const struct ovs_key_arp *arp_key;
 
                if (!(attrs & (1 << OVS_KEY_ATTR_ARP)))
@@ -1213,14 +1209,15 @@ int ovs_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_lenp,
 
 int ovs_flow_metadata_from_nlattrs(struct sw_flow *flow, int key_len, const struct nlattr *attr)
 {
-       struct ovs_key_ipv4_tunnel *tun_key = &flow->key.phy.tun.tun_key;
+       struct ovs_key_ipv4_tunnel *tun_key = &flow->key.tun_key;
        const struct nlattr *nla;
        int rem;
        __be64 tun_id = 0;
 
        flow->key.phy.in_port = DP_MAX_PORTS;
        flow->key.phy.priority = 0;
-       memset(tun_key, 0, sizeof(flow->key.phy.tun.tun_key));
+       flow->key.phy.skb_mark = 0;
+       memset(tun_key, 0, sizeof(flow->key.tun_key));
 
        nla_for_each_nested(nla, attr, rem) {
                int type = nla_type(nla);
@@ -1238,23 +1235,23 @@ int ovs_flow_metadata_from_nlattrs(struct sw_flow *flow, int key_len, const stru
                                tun_id = nla_get_be64(nla);
 
                                if (tun_key->ipv4_dst) {
-                                       if (!(tun_key->tun_flags & OVS_FLOW_TNL_F_KEY))
+                                       if (!(tun_key->tun_flags & OVS_TNL_F_KEY))
                                                return -EINVAL;
                                        if (tun_key->tun_id != tun_id)
                                                return -EINVAL;
                                        break;
                                }
                                tun_key->tun_id = tun_id;
-                               tun_key->tun_flags |= OVS_FLOW_TNL_F_KEY;
+                               tun_key->tun_flags |= OVS_TNL_F_KEY;
 
                                break;
 
                        case OVS_KEY_ATTR_IPV4_TUNNEL:
-                               if (tun_key->tun_flags & OVS_FLOW_TNL_F_KEY) {
+                               if (tun_key->tun_flags & OVS_TNL_F_KEY) {
                                        tun_id = tun_key->tun_id;
 
                                        memcpy(tun_key, nla_data(nla), sizeof(*tun_key));
-                                       if (!(tun_key->tun_flags & OVS_FLOW_TNL_F_KEY))
+                                       if (!(tun_key->tun_flags & OVS_TNL_F_KEY))
                                                return -EINVAL;
 
                                        if (tun_key->tun_id != tun_id)
@@ -1271,6 +1268,14 @@ int ovs_flow_metadata_from_nlattrs(struct sw_flow *flow, int key_len, const stru
                                        return -EINVAL;
                                flow->key.phy.in_port = nla_get_u32(nla);
                                break;
+
+                       case OVS_KEY_ATTR_SKB_MARK:
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) && !defined(CONFIG_NETFILTER)
+                               if (nla_get_u32(nla) != 0)
+                                       return -EINVAL;
+#endif
+                               flow->key.phy.skb_mark = nla_get_u32(nla);
+                               break;
                        }
                }
        }
@@ -1292,22 +1297,26 @@ int ovs_flow_to_nlattrs(const struct sw_flow_key *swkey, struct sk_buff *skb)
            nla_put_u32(skb, OVS_KEY_ATTR_PRIORITY, swkey->phy.priority))
                goto nla_put_failure;
 
-       if (swkey->phy.tun.tun_key.ipv4_dst) {
+       if (swkey->tun_key.ipv4_dst) {
                struct ovs_key_ipv4_tunnel *tun_key;
                nla = nla_reserve(skb, OVS_KEY_ATTR_IPV4_TUNNEL, sizeof(*tun_key));
                if (!nla)
                        goto nla_put_failure;
                tun_key = nla_data(nla);
-               memcpy(tun_key, &swkey->phy.tun.tun_key, sizeof(*tun_key));
+               memcpy(tun_key, &swkey->tun_key, sizeof(*tun_key));
        }
-       if ((swkey->phy.tun.tun_key.tun_flags & OVS_FLOW_TNL_F_KEY) &&
-           nla_put_be64(skb, OVS_KEY_ATTR_TUN_ID, swkey->phy.tun.tun_key.tun_id))
+       if ((swkey->tun_key.tun_flags & OVS_TNL_F_KEY) &&
+           nla_put_be64(skb, OVS_KEY_ATTR_TUN_ID, swkey->tun_key.tun_id))
                goto nla_put_failure;
 
        if (swkey->phy.in_port != DP_MAX_PORTS &&
            nla_put_u32(skb, OVS_KEY_ATTR_IN_PORT, swkey->phy.in_port))
                goto nla_put_failure;
 
+       if (swkey->phy.skb_mark &&
+           nla_put_u32(skb, OVS_KEY_ATTR_SKB_MARK, swkey->phy.skb_mark))
+               goto nla_put_failure;
+
        nla = nla_reserve(skb, OVS_KEY_ATTR_ETHERNET, sizeof(*eth_key));
        if (!nla)
                goto nla_put_failure;
@@ -1361,7 +1370,8 @@ int ovs_flow_to_nlattrs(const struct sw_flow_key *swkey, struct sk_buff *skb)
                ipv6_key->ipv6_tclass = swkey->ip.tos;
                ipv6_key->ipv6_hlimit = swkey->ip.ttl;
                ipv6_key->ipv6_frag = swkey->ip.frag;
-       } else if (swkey->eth.type == htons(ETH_P_ARP)) {
+       } else if (swkey->eth.type == htons(ETH_P_ARP) ||
+                  swkey->eth.type == htons(ETH_P_RARP)) {
                struct ovs_key_arp *arp_key;
 
                nla = nla_reserve(skb, OVS_KEY_ATTR_ARP, sizeof(*arp_key));
index f4ef285..3f3624f 100644 (file)
@@ -41,11 +41,10 @@ struct sw_flow_actions {
 };
 
 struct sw_flow_key {
+       struct ovs_key_ipv4_tunnel tun_key;  /* Encapsulating tunnel key. */
        struct {
-               union {
-                       struct ovs_key_ipv4_tunnel tun_key;  /* Encapsulating tunnel key. */
-               } tun;
                u32     priority;       /* Packet QoS priority. */
+               u32     skb_mark;       /* SKB mark. */
                u16     in_port;        /* Input switch port (or DP_MAX_PORTS). */
        } phy;
        struct {
@@ -104,9 +103,6 @@ struct sw_flow {
        struct sw_flow_key key;
        struct sw_flow_actions __rcu *sf_acts;
 
-       atomic_t refcnt;
-       bool dead;
-
        spinlock_t lock;        /* Lock for values below. */
        unsigned long used;     /* Last used time (in jiffies). */
        u64 packet_count;       /* Number of packets matched. */
@@ -133,13 +129,11 @@ void ovs_flow_exit(void);
 
 struct sw_flow *ovs_flow_alloc(void);
 void ovs_flow_deferred_free(struct sw_flow *);
+void ovs_flow_free(struct sw_flow *);
 
 struct sw_flow_actions *ovs_flow_actions_alloc(const struct nlattr *);
 void ovs_flow_deferred_free_acts(struct sw_flow_actions *);
 
-void ovs_flow_hold(struct sw_flow *);
-void ovs_flow_put(struct sw_flow *);
-
 int ovs_flow_extract(struct sk_buff *, u16 in_port, struct sw_flow_key *,
                     int *key_lenp);
 void ovs_flow_used(struct sw_flow *, struct sk_buff *);
@@ -154,6 +148,7 @@ u64 ovs_flow_used_time(unsigned long flow_jiffies);
  *  OVS_KEY_ATTR_TUN_ID        8    --     4     12
  *  OVS_KEY_ATTR_IPV4_TUNNEL  24    --     4     28
  *  OVS_KEY_ATTR_IN_PORT       4    --     4      8
+ *  OVS_KEY_ATTR_SKB_MARK      4    --     4      8
  *  OVS_KEY_ATTR_ETHERNET     12    --     4     16
  *  OVS_KEY_ATTR_ETHERTYPE     2     2     4      8  (outer VLAN ethertype)
  *  OVS_KEY_ATTR_8021Q         4    --     4      8
@@ -163,9 +158,9 @@ u64 ovs_flow_used_time(unsigned long flow_jiffies);
  *  OVS_KEY_ATTR_ICMPV6        2     2     4      8
  *  OVS_KEY_ATTR_ND           28    --     4     32
  *  -------------------------------------------------
- *  total                                       184
+ *  total                                       192
  */
-#define FLOW_BUFSIZE 184
+#define FLOW_BUFSIZE 192
 
 int ovs_flow_to_nlattrs(const struct sw_flow_key *, struct sk_buff *);
 int ovs_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_lenp,
index d6de397..901b2a8 100644 (file)
@@ -4,7 +4,6 @@
 /Module.markers
 /actions.c
 /addrconf_core-openvswitch.c
-/brcompat_main.c
 /checksum.c
 /dev-openvswitch.c
 /dp_sysfs_dp.c
@@ -15,7 +14,6 @@
 /exthdrs_core.c
 /flex_array.c
 /flow.c
-/genetlink-brcompat.c
 /genetlink-openvswitch.c
 /genl_exec.c
 /ip_output-openvswitch.c
@@ -39,5 +37,6 @@
 /vport-internal_dev.c
 /vport-netdev.c
 /vport-patch.c
+/vport-vxlan.c
 /vport.c
 /workqueue.c
index 6b46c0c..2e445ac 100644 (file)
@@ -4,7 +4,6 @@ export srcdir = @abs_srcdir@
 export top_srcdir = @abs_top_srcdir@
 export KSRC = @KBUILD@
 export VERSION = @VERSION@
-export BUILD_BRCOMPAT = @BUILD_BRCOMPAT@
 
 include $(srcdir)/../Modules.mk
 include $(srcdir)/Modules.mk
@@ -53,7 +52,10 @@ endif
 
 VERSION_FILE := $(KOBJ)/include/linux/version.h
 ifeq (,$(wildcard $(VERSION_FILE)))
-  $(error Linux kernel source not configured - missing version.h)
+  VERSION_FILE := $(KOBJ)/include/generated/uapi/linux/version.h
+  ifeq (,$(wildcard $(VERSION_FILE)))
+    $(error Linux kernel source not configured - missing version.h)
+  endif
 endif
 
 CONFIG_FILE := $(KSRC)/include/generated/autoconf.h
index 8ce6115..4fabc45 100644 (file)
@@ -13,6 +13,8 @@ openvswitch_sources += \
        linux/compat/time.c     \
        linux/compat/workqueue.c
 openvswitch_headers += \
+       linux/compat/include/asm/percpu.h \
+       linux/compat/include/linux/bug.h \
        linux/compat/include/linux/compiler.h \
        linux/compat/include/linux/compiler-gcc.h \
        linux/compat/include/linux/cpumask.h \
@@ -59,6 +61,7 @@ openvswitch_headers += \
        linux/compat/include/net/checksum.h \
        linux/compat/include/net/dst.h \
        linux/compat/include/net/genetlink.h \
+       linux/compat/include/net/inet_frag.h \
        linux/compat/include/net/ip.h \
        linux/compat/include/net/ipv6.h \
        linux/compat/include/net/net_namespace.h \
@@ -66,11 +69,4 @@ openvswitch_headers += \
        linux/compat/include/net/protocol.h \
        linux/compat/include/net/route.h \
        linux/compat/include/net/sock.h \
-       linux/compat/include/net/netns/generic.h \
-       linux/compat/genetlink.inc
-
-# always distribute brcompat source regardless of local build configuration
-dist_modules += brcompat
-build_modules += $(if $(BUILD_BRCOMPAT),brcompat)
-brcompat_sources = linux/compat/genetlink-brcompat.c brcompat_main.c
-brcompat_headers =
+       linux/compat/include/net/netns/generic.h
index 658e16a..e2b4a03 100644 (file)
@@ -1,9 +1,8 @@
 #include <linux/ipv6.h>
+#include <linux/version.h>
 #include <net/ipv6.h>
 
-/* This function is upstream but not the version which supplies the
- * fragment offset.  We plan to propose the extended version.
- */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,3,0)
 int rpl_ipv6_skip_exthdr(const struct sk_buff *skb, int start,
                         u8 *nexthdrp, __be16 *frag_offp)
 {
@@ -46,3 +45,127 @@ int rpl_ipv6_skip_exthdr(const struct sk_buff *skb, int start,
        *nexthdrp = nexthdr;
        return start;
 }
+#endif /* Kernel version < 3.3 */
+
+/*
+ * find the offset to specified header or the protocol number of last header
+ * if target < 0. "last header" is transport protocol header, ESP, or
+ * "No next header".
+ *
+ * Note that *offset is used as input/output parameter. an if it is not zero,
+ * then it must be a valid offset to an inner IPv6 header. This can be used
+ * to explore inner IPv6 header, eg. ICMPv6 error messages.
+ *
+ * If target header is found, its offset is set in *offset and return protocol
+ * number. Otherwise, return -1.
+ *
+ * If the first fragment doesn't contain the final protocol header or
+ * NEXTHDR_NONE it is considered invalid.
+ *
+ * Note that non-1st fragment is special case that "the protocol number
+ * of last header" is "next header" field in Fragment header. In this case,
+ * *offset is meaningless and fragment offset is stored in *fragoff if fragoff
+ * isn't NULL.
+ *
+ * if flags is not NULL and it's a fragment, then the frag flag
+ * OVS_IP6T_FH_F_FRAG will be set. If it's an AH header, the
+ * OVS_IP6T_FH_F_AUTH flag is set and target < 0, then this function will
+ * stop at the AH header. If OVS_IP6T_FH_F_SKIP_RH flag was passed, then this
+ * function will skip all those routing headers, where segements_left was 0.
+ */
+int rpl_ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
+                 int target, unsigned short *fragoff, int *flags)
+{
+       unsigned int start = skb_network_offset(skb) + sizeof(struct ipv6hdr);
+       u8 nexthdr = ipv6_hdr(skb)->nexthdr;
+       unsigned int len;
+       bool found;
+
+       if (fragoff)
+               *fragoff = 0;
+
+       if (*offset) {
+               struct ipv6hdr _ip6, *ip6;
+
+               ip6 = skb_header_pointer(skb, *offset, sizeof(_ip6), &_ip6);
+               if (!ip6 || (ip6->version != 6)) {
+                       printk(KERN_ERR "IPv6 header not found\n");
+                       return -EBADMSG;
+               }
+               start = *offset + sizeof(struct ipv6hdr);
+               nexthdr = ip6->nexthdr;
+       }
+       len = skb->len - start;
+
+       do {
+               struct ipv6_opt_hdr _hdr, *hp;
+               unsigned int hdrlen;
+               found = (nexthdr == target);
+
+               if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) {
+                       if (target < 0)
+                               break;
+                       return -ENOENT;
+               }
+
+               hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr);
+               if (hp == NULL)
+                       return -EBADMSG;
+
+               if (nexthdr == NEXTHDR_ROUTING) {
+                       struct ipv6_rt_hdr _rh, *rh;
+
+                       rh = skb_header_pointer(skb, start, sizeof(_rh),
+                                               &_rh);
+                       if (rh == NULL)
+                               return -EBADMSG;
+
+                       if (flags && (*flags & OVS_IP6T_FH_F_SKIP_RH) &&
+                           rh->segments_left == 0)
+                               found = false;
+               }
+
+               if (nexthdr == NEXTHDR_FRAGMENT) {
+                       unsigned short _frag_off;
+                       __be16 *fp;
+
+                       if (flags)      /* Indicate that this is a fragment */
+                               *flags |= OVS_IP6T_FH_F_FRAG;
+                       fp = skb_header_pointer(skb,
+                                               start+offsetof(struct frag_hdr,
+                                                              frag_off),
+                                               sizeof(_frag_off),
+                                               &_frag_off);
+                       if (fp == NULL)
+                               return -EBADMSG;
+
+                       _frag_off = ntohs(*fp) & ~0x7;
+                       if (_frag_off) {
+                               if (target < 0 &&
+                                   ((!ipv6_ext_hdr(hp->nexthdr)) ||
+                                    hp->nexthdr == NEXTHDR_NONE)) {
+                                       if (fragoff)
+                                               *fragoff = _frag_off;
+                                       return hp->nexthdr;
+                               }
+                               return -ENOENT;
+                       }
+                       hdrlen = 8;
+               } else if (nexthdr == NEXTHDR_AUTH) {
+                       if (flags && (*flags & OVS_IP6T_FH_F_AUTH) &&
+                           (target < 0))
+                               break;
+                       hdrlen = (hp->hdrlen + 2) << 2;
+               } else
+                       hdrlen = ipv6_optlen(hp);
+
+               if (!found) {
+                       nexthdr = hp->nexthdr;
+                       len -= hdrlen;
+                       start += hdrlen;
+               }
+       } while (!found);
+
+       *offset = start;
+       return nexthdr;
+}
diff --git a/datapath/linux/compat/genetlink-brcompat.c b/datapath/linux/compat/genetlink-brcompat.c
deleted file mode 100644 (file)
index ed3a4bb..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-/* We fix grp->id to 32 so that it doesn't collide with any of the multicast
- * groups selected by openvswitch, which uses groups 16 through 31.
- * Collision isn't fatal--multicast listeners should check that the family is
- * the one that they want and discard others--but it wastes time and memory to
- * receive unwanted messages. */
-
-#define GENL_FIRST_MCGROUP 32
-#define GENL_LAST_MCGROUP  32
-
-#include "genetlink.inc"
index 3e687b7..810223b 100644 (file)
@@ -1,4 +1,149 @@
+#include <net/genetlink.h>
+#include <linux/version.h>
+
 #define GENL_FIRST_MCGROUP 16
 #define GENL_LAST_MCGROUP  31
 
-#include "genetlink.inc"
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+#include <linux/mutex.h>
+#include <linux/openvswitch.h>
+
+#include "openvswitch/datapath-compat.h"
+
+static DEFINE_MUTEX(mc_group_mutex);
+
+int genl_register_mc_group(struct genl_family *family,
+                          struct genl_multicast_group *grp)
+{
+       static int next_group = GENL_FIRST_MCGROUP;
+
+       grp->family = family;
+
+       if (!strcmp(grp->name, OVS_VPORT_MCGROUP)) {
+               grp->id = OVS_VPORT_MCGROUP_FALLBACK_ID;
+               return 0;
+       }
+
+       mutex_lock(&mc_group_mutex);
+       grp->id = next_group;
+
+       if (++next_group > GENL_LAST_MCGROUP)
+               next_group = GENL_FIRST_MCGROUP;
+       mutex_unlock(&mc_group_mutex);
+
+       return 0;
+}
+#endif /* kernel < 2.6.23 */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,31)
+/**
+ * genl_register_family_with_ops - register a generic netlink family
+ * @family: generic netlink family
+ * @ops: operations to be registered
+ * @n_ops: number of elements to register
+ *
+ * Registers the specified family and operations from the specified table.
+ * Only one family may be registered with the same family name or identifier.
+ *
+ * The family id may equal GENL_ID_GENERATE causing an unique id to
+ * be automatically generated and assigned.
+ *
+ * Either a doit or dumpit callback must be specified for every registered
+ * operation or the function will fail. Only one operation structure per
+ * command identifier may be registered.
+ *
+ * See include/net/genetlink.h for more documenation on the operations
+ * structure.
+ *
+ * This is equivalent to calling genl_register_family() followed by
+ * genl_register_ops() for every operation entry in the table taking
+ * care to unregister the family on error path.
+ *
+ * Return 0 on success or a negative error code.
+ */
+int genl_register_family_with_ops(struct genl_family *family,
+       struct genl_ops *ops, size_t n_ops)
+{
+       int err, i;
+
+       err = genl_register_family(family);
+       if (err)
+               return err;
+
+       for (i = 0; i < n_ops; ++i, ++ops) {
+               err = genl_register_ops(family, ops);
+               if (err)
+                       goto err_out;
+       }
+       return 0;
+err_out:
+       genl_unregister_family(family);
+       return err;
+}
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
+/**
+ * nlmsg_notify - send a notification netlink message
+ * @sk: netlink socket to use
+ * @skb: notification message
+ * @portid: destination netlink portid for reports or 0
+ * @group: destination multicast group or 0
+ * @report: 1 to report back, 0 to disable
+ * @flags: allocation flags
+ */
+int nlmsg_notify(struct sock *sk, struct sk_buff *skb, u32 portid,
+                unsigned int group, int report, gfp_t flags)
+{
+       int err = 0;
+
+       if (group) {
+               int exclude_portid = 0;
+
+               if (report) {
+                       atomic_inc(&skb->users);
+                       exclude_portid = portid;
+               }
+
+               /* errors reported via destination sk->sk_err, but propagate
+                * delivery errors if NETLINK_BROADCAST_ERROR flag is set */
+               err = nlmsg_multicast(sk, skb, exclude_portid, group, flags);
+       }
+
+       if (report) {
+               int err2;
+
+               err2 = nlmsg_unicast(sk, skb, portid);
+               if (!err || err == -ESRCH)
+                       err = err2;
+       }
+
+       return err;
+}
+#endif
+
+/* This is analogous to rtnl_notify() but uses genl_sock instead of rtnl.
+ *
+ * This is not (yet) in any upstream kernel. */
+void genl_notify(struct sk_buff *skb, struct net *net, u32 portid, u32 group,
+                struct nlmsghdr *nlh, gfp_t flags)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)
+       struct sock *sk = net->genl_sock;
+#else
+       struct sock *sk = genl_sock;
+#endif
+       int report = 0;
+
+       if (nlh)
+               report = nlmsg_report(nlh);
+
+       nlmsg_notify(sk, skb, portid, group, report, flags);
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30)
+/* This function wasn't exported before 2.6.30.  Lose! */
+void netlink_set_err(struct sock *ssk, u32 portid, u32 group, int code)
+{
+}
+#endif
diff --git a/datapath/linux/compat/genetlink.inc b/datapath/linux/compat/genetlink.inc
deleted file mode 100644 (file)
index bf96980..0000000
+++ /dev/null
@@ -1,148 +0,0 @@
-/* -*- c -*- */
-
-#include <net/genetlink.h>
-#include <linux/version.h>
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
-#include <linux/mutex.h>
-#include <linux/openvswitch.h>
-
-#include "openvswitch/datapath-compat.h"
-
-static DEFINE_MUTEX(mc_group_mutex);
-
-int genl_register_mc_group(struct genl_family *family,
-                          struct genl_multicast_group *grp)
-{
-       static int next_group = GENL_FIRST_MCGROUP;
-
-       grp->family = family;
-
-       if (!strcmp(grp->name, OVS_VPORT_MCGROUP)) {
-               grp->id = OVS_VPORT_MCGROUP_FALLBACK_ID;
-               return 0;
-       }
-
-       mutex_lock(&mc_group_mutex);
-       grp->id = next_group;
-
-       if (++next_group > GENL_LAST_MCGROUP)
-               next_group = GENL_FIRST_MCGROUP;
-       mutex_unlock(&mc_group_mutex);
-
-       return 0;
-}
-#endif /* kernel < 2.6.23 */
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,31)
-/**
- * genl_register_family_with_ops - register a generic netlink family
- * @family: generic netlink family
- * @ops: operations to be registered
- * @n_ops: number of elements to register
- *
- * Registers the specified family and operations from the specified table.
- * Only one family may be registered with the same family name or identifier.
- *
- * The family id may equal GENL_ID_GENERATE causing an unique id to
- * be automatically generated and assigned.
- *
- * Either a doit or dumpit callback must be specified for every registered
- * operation or the function will fail. Only one operation structure per
- * command identifier may be registered.
- *
- * See include/net/genetlink.h for more documenation on the operations
- * structure.
- *
- * This is equivalent to calling genl_register_family() followed by
- * genl_register_ops() for every operation entry in the table taking
- * care to unregister the family on error path.
- *
- * Return 0 on success or a negative error code.
- */
-int genl_register_family_with_ops(struct genl_family *family,
-       struct genl_ops *ops, size_t n_ops)
-{
-       int err, i;
-
-       err = genl_register_family(family);
-       if (err)
-               return err;
-
-       for (i = 0; i < n_ops; ++i, ++ops) {
-               err = genl_register_ops(family, ops);
-               if (err)
-                       goto err_out;
-       }
-       return 0;
-err_out:
-       genl_unregister_family(family);
-       return err;
-}
-#endif
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
-/**
- * nlmsg_notify - send a notification netlink message
- * @sk: netlink socket to use
- * @skb: notification message
- * @pid: destination netlink pid for reports or 0
- * @group: destination multicast group or 0
- * @report: 1 to report back, 0 to disable
- * @flags: allocation flags
- */
-int nlmsg_notify(struct sock *sk, struct sk_buff *skb, u32 pid,
-                unsigned int group, int report, gfp_t flags)
-{
-       int err = 0;
-
-       if (group) {
-               int exclude_pid = 0;
-
-               if (report) {
-                       atomic_inc(&skb->users);
-                       exclude_pid = pid;
-               }
-
-               /* errors reported via destination sk->sk_err, but propagate
-                * delivery errors if NETLINK_BROADCAST_ERROR flag is set */
-               err = nlmsg_multicast(sk, skb, exclude_pid, group, flags);
-       }
-
-       if (report) {
-               int err2;
-
-               err2 = nlmsg_unicast(sk, skb, pid);
-               if (!err || err == -ESRCH)
-                       err = err2;
-       }
-
-       return err;
-}
-#endif
-
-/* This is analogous to rtnl_notify() but uses genl_sock instead of rtnl.
- *
- * This is not (yet) in any upstream kernel. */
-void genl_notify(struct sk_buff *skb, struct net *net, u32 pid, u32 group,
-                struct nlmsghdr *nlh, gfp_t flags)
-{
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)
-       struct sock *sk = net->genl_sock;
-#else
-       struct sock *sk = genl_sock;
-#endif
-       int report = 0;
-
-       if (nlh)
-               report = nlmsg_report(nlh);
-
-       nlmsg_notify(sk, skb, pid, group, report, flags);
-}
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30)
-/* This function wasn't exported before 2.6.30.  Lose! */
-void netlink_set_err(struct sock *ssk, u32 pid, u32 group, int code)
-{
-}
-#endif
diff --git a/datapath/linux/compat/include/asm/percpu.h b/datapath/linux/compat/include/asm/percpu.h
new file mode 100644 (file)
index 0000000..404b937
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef __ASM_PERCPU_WRAPPER_H
+#define __ASM_PERCPU_WRAPPER_H 1
+
+#include_next <asm/percpu.h>
+
+#ifndef this_cpu_ptr
+#define this_cpu_ptr(ptr) per_cpu_ptr(ptr, smp_processor_id())
+#endif
+
+#endif
diff --git a/datapath/linux/compat/include/linux/bug.h b/datapath/linux/compat/include/linux/bug.h
new file mode 100644 (file)
index 0000000..d24e68e
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef __BUG_H_WRAPPER
+#define __BUG_H_WRAPPER 1
+
+#include_next <linux/bug.h>
+
+#ifndef BUILD_BUG_ON_NOT_POWER_OF_2
+/* Force a compilation error if a constant expression is not a power of 2 */
+#define BUILD_BUG_ON_NOT_POWER_OF_2(n)                 \
+       BUILD_BUG_ON((n) == 0 || (((n) & ((n) - 1)) != 0))
+#endif
+
+#endif
index 7f04c96..b2c3353 100644 (file)
@@ -16,4 +16,20 @@ static inline void eth_hw_addr_random(struct net_device *dev)
 }
 #endif
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,6,0)
+#define eth_mac_addr rpl_eth_mac_addr
+static inline int eth_mac_addr(struct net_device *dev, void *p)
+{
+       struct sockaddr *addr = p;
+
+       if (!is_valid_ether_addr(addr->sa_data))
+               return -EADDRNOTAVAIL;
+#ifdef NET_ADDR_RANDOM
+       dev->addr_assign_type &= ~NET_ADDR_RANDOM;
+#endif
+       memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
+       return 0;
+}
+#endif
+
 #endif
index a09f112..f53cf97 100644 (file)
@@ -18,4 +18,8 @@
 #define IFF_OVS_DATAPATH 0
 #endif
 
+#ifndef IFF_LIVE_ADDR_CHANGE
+#define IFF_LIVE_ADDR_CHANGE 0
+#endif
+
 #endif
index 812f213..069839b 100644 (file)
@@ -7,7 +7,11 @@
 #endif
 
 #include <linux/version.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,4,0)
+/* BUILD_BUG_ON_NOT_POWER_OF_2 definition */
 #include <linux/bug.h>
+#endif
+
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
 #undef pr_emerg
 #define pr_emerg(fmt, ...) \
 #define pr_warn pr_warning
 #endif
 
-#ifndef BUILD_BUG_ON_NOT_POWER_OF_2
-/* Force a compilation error if a constant expression is not a power of 2 */
-#define BUILD_BUG_ON_NOT_POWER_OF_2(n)                 \
-       BUILD_BUG_ON((n) == 0 || (((n) & ((n) - 1)) != 0))
-#endif
-
 #if defined(CONFIG_PREEMPT) && LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)
 #error "CONFIG_PREEMPT is broken before 2.6.21--see commit 4498121ca3, \"[NET]: Handle disabled preemption in gfp_any()\""
 #endif
index 0a02149..151f823 100644 (file)
@@ -6,7 +6,7 @@
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
 
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
-static inline void rtnl_notify(struct sk_buff *skb, u32 pid, u32 group,
+static inline void rtnl_notify(struct sk_buff *skb, u32 portid, u32 group,
                               struct nlmsghdr *nlh, gfp_t flags)
 {
        BUG_ON(nlh != NULL);            /* not implemented */
@@ -23,14 +23,14 @@ static inline void rtnl_set_sk_err(u32 group, int error)
 #endif
 
 /* No 'net' parameter in these versions. */
-#define rtnl_notify(skb, net, pid, group, nlh, flags) \
-                   ((void) rtnl_notify(skb, pid, group, nlh, flags))
+#define rtnl_notify(skb, net, portid, group, nlh, flags) \
+                   ((void) rtnl_notify(skb, portid, group, nlh, flags))
 #define rtnl_set_sk_err(net, group, error) \
                        (rtnl_set_sk_err(group, error))
 #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))
+#define rtnl_notify(skb, net, portid, group, nlh, flags) \
+       ((void) rtnl_notify(skb, net, portid, group, nlh, flags))
 #endif
 
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26)
index 6fe4721..6a805b5 100644 (file)
@@ -10,4 +10,9 @@ static inline struct udphdr *udp_hdr(const struct sk_buff *skb)
 }
 #endif /* HAVE_SKBUFF_HEADER_HELPERS */
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,5,0)
+static inline void udp_encap_enable(void)
+{
+}
+#endif
 #endif
index af7d5fd..ac199ec 100644 (file)
@@ -5,6 +5,17 @@
 #include <linux/netlink.h>
 #include <net/net_namespace.h>
 
+/*
+ * 15e473046cb6e5d18a4d0057e61d76315230382b renames pid to portid
+ * the affected structures are
+ * netlink_skb_parms::pid -> portid
+ * genl_info::snd_pid -> snd_portid
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0)
+#define snd_portid snd_pid
+#define portid pid
+#endif
+
 /* Very special super-nasty workaround here:
  *
  * Before 2.6.19, nlmsg_multicast() lacked a 'flags' parameter.  We work
@@ -26,7 +37,7 @@
 #define nlmsg_multicast busted_nlmsg_multicast
 #define genlmsg_multicast busted_genlmsg_multicast
 extern int busted_nlmsg_multicast(struct sock *sk, struct sk_buff *skb,
-                                 u32 pid, unsigned int group);
+                                 u32 portid, unsigned int group);
 #endif /* linux kernel < v2.6.19 */
 
 #include_next <net/genetlink.h>
@@ -86,14 +97,14 @@ static inline int genlmsg_total_size(int payload)
 #define genlmsg_multicast(s, p, g, f) \
                genlmsg_multicast_flags((s), (p), (g), (f))
 
-static inline int genlmsg_multicast_flags(struct sk_buff *skb, u32 pid,
+static inline int genlmsg_multicast_flags(struct sk_buff *skb, u32 portid,
                unsigned int group, gfp_t flags)
 {
        int err;
 
        NETLINK_CB(skb).dst_group = group;
 
-       err = netlink_broadcast(genl_sock, skb, pid, group, flags);
+       err = netlink_broadcast(genl_sock, skb, portid, group, flags);
        if (err > 0)
                err = 0;
 
@@ -102,8 +113,8 @@ static inline int genlmsg_multicast_flags(struct sk_buff *skb, u32 pid,
 #endif /* linux kernel < 2.6.19 */
 
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32)
-#define genlmsg_multicast_netns(net, skb, pid, grp, flags) \
-               genlmsg_multicast(skb, pid, grp, flags)
+#define genlmsg_multicast_netns(net, skb, portid, grp, flags) \
+               genlmsg_multicast(skb, portid, grp, flags)
 #endif
 
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
@@ -126,7 +137,7 @@ static inline void *genlmsg_put_reply(struct sk_buff *skb,
                        struct genl_info *info, struct genl_family *family,
                        int flags, u8 cmd)
 {
-       return genlmsg_put(skb, info->snd_pid, info->snd_seq, family,
+       return genlmsg_put(skb, info->snd_portid, info->snd_seq, family,
                                flags, cmd);
 }
 
@@ -137,7 +148,7 @@ static inline void *genlmsg_put_reply(struct sk_buff *skb,
  */
 static inline int genlmsg_reply(struct sk_buff *skb, struct genl_info *info)
 {
-       return genlmsg_unicast(skb, info->snd_pid);
+       return genlmsg_unicast(skb, info->snd_portid);
 }
 
 /**
@@ -157,10 +168,10 @@ int genl_register_family_with_ops(struct genl_family *family,
 #endif
 
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
-#define genl_notify(skb, net, pid, group, nlh, flags) \
-       genl_notify(skb, pid, group, nlh, flags)
+#define genl_notify(skb, net, portid, group, nlh, flags) \
+       genl_notify(skb, portid, group, nlh, flags)
 #endif
-extern void genl_notify(struct sk_buff *skb, struct net *net, u32 pid,
+extern void genl_notify(struct sk_buff *skb, struct net *net, u32 portid,
                        u32 group, struct nlmsghdr *nlh, gfp_t flags);
 
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) && \
@@ -172,6 +183,6 @@ static inline struct net *genl_info_net(struct genl_info *info)
 #endif
 
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32)
-#define genlmsg_unicast(ignore_net, skb, pid)   genlmsg_unicast(skb, pid)
+#define genlmsg_unicast(ignore_net, skb, portid)   genlmsg_unicast(skb, portid)
 #endif
 #endif /* genetlink.h */
diff --git a/datapath/linux/compat/include/net/inet_frag.h b/datapath/linux/compat/include/net/inet_frag.h
new file mode 100644 (file)
index 0000000..6767c31
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef __NET_INET_FRAG_WRAPPER_H
+#define __NET_INET_FRAG_WRAPPER_H 1
+
+#include <linux/version.h>
+#include_next <net/inet_frag.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0)
+#define inet_frag_evictor(nf, f, force)                                        \
+       do {                                                            \
+               if (force || atomic_read(&nf->mem) > nf->high_thresh) { \
+                       inet_frag_evictor(nf, f);                       \
+               }                                                       \
+       } while (0)
+#endif
+
+#endif /* inet_frag.h */
index 8bb7d65..d1e3248 100644 (file)
@@ -1,13 +1,26 @@
 #ifndef __NET_IPV6_WRAPPER_H
 #define __NET_IPV6_WRAPPER_H 1
 
+#include <linux/version.h>
+
 #include_next <net/ipv6.h>
 
-/* This function is upstream but not the version which supplies the
- * fragment offset.  We plan to propose the extended version.
- */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,3,0)
 #define ipv6_skip_exthdr rpl_ipv6_skip_exthdr
 extern int ipv6_skip_exthdr(const struct sk_buff *skb, int start,
-                               u8 *nexthdrp, __be16 *frag_offp);
+                           u8 *nexthdrp, __be16 *frag_offp);
+#endif
+
+enum {
+       OVS_IP6T_FH_F_FRAG      = (1 << 0),
+       OVS_IP6T_FH_F_AUTH      = (1 << 1),
+       OVS_IP6T_FH_F_SKIP_RH   = (1 << 2),
+};
+
+/* This function is upstream, but not the version which skips routing
+ * headers with 0 segments_left. We plan to propose the extended version. */
+#define ipv6_find_hdr rpl_ipv6_find_hdr
+extern int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
+                        int target, unsigned short *fragoff, int *fragflg);
 
 #endif
index 14041e2..9616ea9 100644 (file)
@@ -145,7 +145,7 @@ static inline int nlmsg_report(const struct nlmsghdr *nlh)
 }
 
 extern int             nlmsg_notify(struct sock *sk, struct sk_buff *skb,
-                                    u32 pid, unsigned int group, int report,
+                                    u32 portid, unsigned int group, int report,
                                     gfp_t flags);
 #endif /* linux kernel < 2.6.19 */
 
@@ -156,13 +156,13 @@ extern int                nlmsg_notify(struct sock *sk, struct sk_buff *skb,
  * argument. */
 #define nlmsg_multicast rpl_nlmsg_multicast
 static inline int nlmsg_multicast(struct sock *sk, struct sk_buff *skb,
-                                 u32 pid, unsigned int group, gfp_t flags)
+                                 u32 portid, unsigned int group, gfp_t flags)
 {
        int err;
 
        NETLINK_CB(skb).dst_group = group;
 
-       err = netlink_broadcast(sk, skb, pid, group, flags);
+       err = netlink_broadcast(sk, skb, portid, group, flags);
        if (err > 0)
                err = 0;
 
index bfb09ce..d03b708 100644 (file)
 #include "tunnel.h"
 #include "vlan.h"
 #include "vport.h"
-#include "vport-generic.h"
 #include "vport-internal_dev.h"
 
-#ifdef NEED_CACHE_TIMEOUT
-/*
- * On kernels where we can't quickly detect changes in the rest of the system
- * we use an expiration time to invalidate the cache.  A shorter expiration
- * reduces the length of time that we may potentially blackhole packets while
- * a longer time increases performance by reducing the frequency that the
- * cache needs to be rebuilt.  A variety of factors may cause the cache to be
- * invalidated before the expiration time but this is the maximum.  The time
- * is expressed in jiffies.
- */
-#define MAX_CACHE_EXP HZ
-#endif
-
-/*
- * Interval to check for and remove caches that are no longer valid.  Caches
- * are checked for validity before they are used for packet encapsulation and
- * old caches are removed at that time.  However, if no packets are sent through
- * the tunnel then the cache will never be destroyed.  Since it holds
- * references to a number of system objects, the cache will continue to use
- * system resources by not allowing those objects to be destroyed.  The cache
- * cleaner is periodically run to free invalid caches.  It does not
- * significantly affect system performance.  A lower interval will release
- * resources faster but will itself consume resources by requiring more frequent
- * checks.  A longer interval may result in messages being printed to the kernel
- * message buffer about unreleased resources.  The interval is expressed in
- * jiffies.
- */
-#define CACHE_CLEANER_INTERVAL (5 * HZ)
-
-#define CACHE_DATA_ALIGN 16
 #define PORT_TABLE_SIZE  1024
 
 static struct hlist_head *port_table __read_mostly;
-static int port_table_count;
-
-static void cache_cleaner(struct work_struct *work);
-static DECLARE_DELAYED_WORK(cache_cleaner_wq, cache_cleaner);
 
 /*
  * These are just used as an optimization: they don't require any kind of
@@ -109,60 +74,17 @@ static unsigned int multicast_ports __read_mostly;
 #define rt_dst(rt) (rt->u.dst)
 #endif
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,1,0)
-static struct hh_cache *rt_hh(struct rtable *rt)
-{
-       struct neighbour *neigh = dst_get_neighbour_noref(&rt->dst);
-       if (!neigh || !(neigh->nud_state & NUD_CONNECTED) ||
-                       !neigh->hh.hh_len)
-               return NULL;
-       return &neigh->hh;
-}
-#else
-#define rt_hh(rt) (rt_dst(rt).hh)
-#endif
-
 static struct vport *tnl_vport_to_vport(const struct tnl_vport *tnl_vport)
 {
        return vport_from_priv(tnl_vport);
 }
 
-/* This is analogous to rtnl_dereference for the tunnel cache.  It checks that
- * cache_lock is held, so it is only for update side code.
- */
-static struct tnl_cache *cache_dereference(struct tnl_vport *tnl_vport)
-{
-       return rcu_dereference_protected(tnl_vport->cache,
-                                lockdep_is_held(&tnl_vport->cache_lock));
-}
-
-static void schedule_cache_cleaner(void)
-{
-       schedule_delayed_work(&cache_cleaner_wq, CACHE_CLEANER_INTERVAL);
-}
-
-static void free_cache(struct tnl_cache *cache)
-{
-       if (!cache)
-               return;
-
-       ovs_flow_put(cache->flow);
-       ip_rt_put(cache->rt);
-       kfree(cache);
-}
-
 static void free_config_rcu(struct rcu_head *rcu)
 {
        struct tnl_mutable_config *c = container_of(rcu, struct tnl_mutable_config, rcu);
        kfree(c);
 }
 
-static void free_cache_rcu(struct rcu_head *rcu)
-{
-       struct tnl_cache *c = container_of(rcu, struct tnl_cache, rcu);
-       free_cache(c);
-}
-
 /* Frees the portion of 'mutable' that requires RTNL and thus can't happen
  * within an RCU callback.  Fortunately this part doesn't require waiting for
  * an RCU grace period.
@@ -191,18 +113,6 @@ static void assign_config_rcu(struct vport *vport,
        call_rcu(&old_config->rcu, free_config_rcu);
 }
 
-static void assign_cache_rcu(struct vport *vport, struct tnl_cache *new_cache)
-{
-       struct tnl_vport *tnl_vport = tnl_vport_priv(vport);
-       struct tnl_cache *old_cache;
-
-       old_cache = cache_dereference(tnl_vport);
-       rcu_assign_pointer(tnl_vport->cache, new_cache);
-
-       if (old_cache)
-               call_rcu(&old_cache->rcu, free_cache_rcu);
-}
-
 static unsigned int *find_port_pool(const struct tnl_mutable_config *mutable)
 {
        bool is_multicast = ipv4_is_multicast(mutable->key.daddr);
@@ -242,13 +152,9 @@ static void port_table_add_port(struct vport *vport)
        const struct tnl_mutable_config *mutable;
        u32 hash;
 
-       if (port_table_count == 0)
-               schedule_cache_cleaner();
-
        mutable = rtnl_dereference(tnl_vport->mutable);
        hash = port_hash(&mutable->key);
        hlist_add_head_rcu(&tnl_vport->hash_node, find_bucket(hash));
-       port_table_count++;
 
        (*find_port_pool(rtnl_dereference(tnl_vport->mutable)))++;
 }
@@ -274,10 +180,6 @@ static void port_table_remove_port(struct vport *vport)
 
        hlist_del_init_rcu(&tnl_vport->hash_node);
 
-       port_table_count--;
-       if (port_table_count == 0)
-               cancel_delayed_work_sync(&cache_cleaner_wq);
-
        (*find_port_pool(rtnl_dereference(tnl_vport->mutable)))--;
 }
 
@@ -370,6 +272,7 @@ struct vport *ovs_tnl_find_port(struct net *net, __be32 saddr, __be32 daddr,
        if (null_ports) {
                lookup.daddr = 0;
                lookup.saddr = 0;
+               lookup.in_key = 0;
                lookup.tunnel_type = tunnel_type;
                vport = port_table_lookup(&lookup, mutable);
                if (vport)
@@ -733,7 +636,7 @@ static bool check_mtu(struct sk_buff *skb,
        if (OVS_CB(skb)->tun_key->ipv4_dst) {
                df_inherit = false;
                pmtud = false;
-               frag_off = OVS_CB(skb)->tun_key->tun_flags & OVS_FLOW_TNL_F_DONT_FRAGMENT ?
+               frag_off = OVS_CB(skb)->tun_key->tun_flags & OVS_TNL_F_DONT_FRAGMENT ?
                                  htons(IP_DF) : 0;
        } else {
                df_inherit = mutable->flags & TNL_F_DF_INHERIT;
@@ -798,284 +701,37 @@ static bool check_mtu(struct sk_buff *skb,
        return true;
 }
 
-static void create_tunnel_header(const struct vport *vport,
-                                const struct tnl_mutable_config *mutable,
-                                const struct ovs_key_ipv4_tunnel *tun_key,
-                                const struct rtable *rt, void *header)
-{
-       struct tnl_vport *tnl_vport = tnl_vport_priv(vport);
-       struct iphdr *iph = header;
-
-       iph->version    = 4;
-       iph->ihl        = sizeof(struct iphdr) >> 2;
-       iph->frag_off   = htons(IP_DF);
-       iph->protocol   = tnl_vport->tnl_ops->ipproto;
-       iph->tos        = mutable->tos;
-       iph->daddr      = rt->rt_dst;
-       iph->saddr      = rt->rt_src;
-       iph->ttl        = mutable->ttl;
-       if (!iph->ttl)
-               iph->ttl = ip4_dst_hoplimit(&rt_dst(rt));
-
-       tnl_vport->tnl_ops->build_header(vport, mutable, tun_key, iph + 1);
-}
-
-static void *get_cached_header(const struct tnl_cache *cache)
-{
-       return (void *)cache + ALIGN(sizeof(struct tnl_cache), CACHE_DATA_ALIGN);
-}
-
-#ifdef HAVE_RT_GENID
-static inline int rt_genid(struct net *net)
-{
-       return atomic_read(&net->ipv4.rt_genid);
-}
-#endif
-
-static bool check_cache_valid(const struct tnl_cache *cache,
-                             const struct tnl_mutable_config *mutable)
-{
-       struct hh_cache *hh;
-
-       if (!cache)
-               return false;
-
-       hh = rt_hh(cache->rt);
-       return hh &&
-#ifdef NEED_CACHE_TIMEOUT
-               time_before(jiffies, cache->expiration) &&
-#endif
-#ifdef HAVE_RT_GENID
-               rt_genid(dev_net(rt_dst(cache->rt).dev)) == cache->rt->rt_genid &&
-#endif
-#ifdef HAVE_HH_SEQ
-               hh->hh_lock.sequence == cache->hh_seq &&
-#endif
-               mutable->seq == cache->mutable_seq &&
-               (!ovs_is_internal_dev(rt_dst(cache->rt).dev) ||
-               (cache->flow && !cache->flow->dead));
-}
-
-static void __cache_cleaner(struct tnl_vport *tnl_vport)
-{
-       const struct tnl_mutable_config *mutable =
-                       rcu_dereference(tnl_vport->mutable);
-       const struct tnl_cache *cache = rcu_dereference(tnl_vport->cache);
-
-       if (cache && !check_cache_valid(cache, mutable) &&
-           spin_trylock_bh(&tnl_vport->cache_lock)) {
-               assign_cache_rcu(tnl_vport_to_vport(tnl_vport), NULL);
-               spin_unlock_bh(&tnl_vport->cache_lock);
-       }
-}
-
-static void cache_cleaner(struct work_struct *work)
-{
-       int i;
-
-       schedule_cache_cleaner();
-
-       rcu_read_lock();
-       for (i = 0; i < PORT_TABLE_SIZE; i++) {
-               struct hlist_node *n;
-               struct hlist_head *bucket;
-               struct tnl_vport *tnl_vport;
-
-               bucket = &port_table[i];
-               hlist_for_each_entry_rcu(tnl_vport, n, bucket, hash_node)
-                       __cache_cleaner(tnl_vport);
-       }
-       rcu_read_unlock();
-}
-
-static void create_eth_hdr(struct tnl_cache *cache, struct hh_cache *hh)
-{
-       void *cache_data = get_cached_header(cache);
-       int hh_off;
-
-#ifdef HAVE_HH_SEQ
-       unsigned hh_seq;
-
-       do {
-               hh_seq = read_seqbegin(&hh->hh_lock);
-               hh_off = HH_DATA_ALIGN(hh->hh_len) - hh->hh_len;
-               memcpy(cache_data, (void *)hh->hh_data + hh_off, hh->hh_len);
-               cache->hh_len = hh->hh_len;
-       } while (read_seqretry(&hh->hh_lock, hh_seq));
-
-       cache->hh_seq = hh_seq;
-#else
-       read_lock(&hh->hh_lock);
-       hh_off = HH_DATA_ALIGN(hh->hh_len) - hh->hh_len;
-       memcpy(cache_data, (void *)hh->hh_data + hh_off, hh->hh_len);
-       cache->hh_len = hh->hh_len;
-       read_unlock(&hh->hh_lock);
-#endif
-}
-
-static struct tnl_cache *build_cache(struct vport *vport,
-                                    const struct tnl_mutable_config *mutable,
-                                    struct rtable *rt)
-{
-       struct tnl_vport *tnl_vport = tnl_vport_priv(vport);
-       static const struct ovs_key_ipv4_tunnel tun_key;
-       struct tnl_cache *cache;
-       void *cache_data;
-       int cache_len;
-       struct hh_cache *hh;
-       int tunnel_hlen;
-
-       if (!(mutable->flags & TNL_F_HDR_CACHE))
-               return NULL;
-
-       tunnel_hlen = tnl_vport->tnl_ops->hdr_len(mutable, &tun_key);
-       if (tunnel_hlen < 0)
-               return NULL;
-
-       tunnel_hlen += sizeof(struct iphdr);
-
-       /*
-        * If there is no entry in the ARP cache or if this device does not
-        * support hard header caching just fall back to the IP stack.
-        */
-
-       hh = rt_hh(rt);
-       if (!hh)
-               return NULL;
-
-       /*
-        * If lock is contended fall back to directly building the header.
-        * We're not going to help performance by sitting here spinning.
-        */
-       if (!spin_trylock(&tnl_vport->cache_lock))
-               return NULL;
-
-       cache = cache_dereference(tnl_vport);
-       if (check_cache_valid(cache, mutable))
-               goto unlock;
-       else
-               cache = NULL;
-
-       cache_len = LL_RESERVED_SPACE(rt_dst(rt).dev) + tunnel_hlen;
-
-       cache = kzalloc(ALIGN(sizeof(struct tnl_cache), CACHE_DATA_ALIGN) +
-                       cache_len, GFP_ATOMIC);
-       if (!cache)
-               goto unlock;
-
-       create_eth_hdr(cache, hh);
-       cache_data = get_cached_header(cache) + cache->hh_len;
-       cache->len = cache->hh_len + tunnel_hlen;
-
-       create_tunnel_header(vport, mutable, &tun_key, rt, cache_data);
-
-       cache->mutable_seq = mutable->seq;
-       cache->rt = rt;
-#ifdef NEED_CACHE_TIMEOUT
-       cache->expiration = jiffies + tnl_vport->cache_exp_interval;
-#endif
-
-       if (ovs_is_internal_dev(rt_dst(rt).dev)) {
-               struct sw_flow_key flow_key;
-               struct vport *dst_vport;
-               struct sk_buff *skb;
-               int err;
-               int flow_key_len;
-               struct sw_flow *flow;
-
-               dst_vport = ovs_internal_dev_get_vport(rt_dst(rt).dev);
-               if (!dst_vport)
-                       goto done;
-
-               skb = alloc_skb(cache->len, GFP_ATOMIC);
-               if (!skb)
-                       goto done;
-
-               __skb_put(skb, cache->len);
-               memcpy(skb->data, get_cached_header(cache), cache->len);
-
-               err = ovs_flow_extract(skb, dst_vport->port_no, &flow_key,
-                                      &flow_key_len);
-
-               consume_skb(skb);
-               if (err)
-                       goto done;
-
-               flow = ovs_flow_tbl_lookup(rcu_dereference(dst_vport->dp->table),
-                                          &flow_key, flow_key_len);
-               if (flow) {
-                       cache->flow = flow;
-                       ovs_flow_hold(flow);
-               }
-       }
-
-done:
-       assign_cache_rcu(vport, cache);
-
-unlock:
-       spin_unlock(&tnl_vport->cache_lock);
-
-       return cache;
-}
-
-static struct rtable *__find_route(const struct tnl_mutable_config *mutable,
-                                  __be32 saddr, __be32 daddr, u8 ipproto,
-                                  u8 tos)
+static struct rtable *find_route(struct net *net,
+               __be32 *saddr, __be32 daddr, u8 ipproto,
+               u8 tos)
 {
+       struct rtable *rt;
        /* Tunnel configuration keeps DSCP part of TOS bits, But Linux
         * router expect RT_TOS bits only. */
 
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)
        struct flowi fl = { .nl_u = { .ip4_u = {
                                        .daddr = daddr,
-                                       .saddr = saddr,
+                                       .saddr = *saddr,
                                        .tos   = RT_TOS(tos) } },
                                        .proto = ipproto };
-       struct rtable *rt;
 
-       if (unlikely(ip_route_output_key(port_key_get_net(&mutable->key), &rt, &fl)))
+       if (unlikely(ip_route_output_key(net, &rt, &fl)))
                return ERR_PTR(-EADDRNOTAVAIL);
-
+       *saddr = fl.nl_u.ip4_u.saddr;
        return rt;
 #else
        struct flowi4 fl = { .daddr = daddr,
-                            .saddr = saddr,
+                            .saddr = *saddr,
                             .flowi4_tos = RT_TOS(tos),
                             .flowi4_proto = ipproto };
 
-       return ip_route_output_key(port_key_get_net(&mutable->key), &fl);
+       rt = ip_route_output_key(net, &fl);
+       *saddr = fl.saddr;
+       return rt;
 #endif
 }
 
-static struct rtable *find_route(struct vport *vport,
-                                const struct tnl_mutable_config *mutable,
-                                __be32 saddr, __be32 daddr, u8 tos,
-                                struct tnl_cache **cache)
-{
-       struct tnl_vport *tnl_vport = tnl_vport_priv(vport);
-       struct tnl_cache *cur_cache = rcu_dereference(tnl_vport->cache);
-
-       *cache = NULL;
-       tos = RT_TOS(tos);
-
-       if (tos == RT_TOS(mutable->tos) &&
-           check_cache_valid(cur_cache, mutable)) {
-               *cache = cur_cache;
-               return cur_cache->rt;
-       } else {
-               struct rtable *rt;
-
-               rt = __find_route(mutable, saddr, daddr,
-                                 tnl_vport->tnl_ops->ipproto, tos);
-               if (IS_ERR(rt))
-                       return NULL;
-               if (likely(tos == RT_TOS(mutable->tos)))
-                       *cache = build_cache(vport, mutable, rt);
-
-               return rt;
-       }
-}
-
 static bool need_linearize(const struct sk_buff *skb)
 {
        int i;
@@ -1197,8 +853,6 @@ int ovs_tnl_send(struct vport *vport, struct sk_buff *skb)
        const struct tnl_mutable_config *mutable = rcu_dereference(tnl_vport->mutable);
        enum vport_err_type err = VPORT_E_TX_ERROR;
        struct rtable *rt;
-       struct dst_entry *unattached_dst = NULL;
-       struct tnl_cache *cache;
        struct ovs_key_ipv4_tunnel tun_key;
        int sent_len = 0;
        int tunnel_hlen;
@@ -1294,11 +948,10 @@ int ovs_tnl_send(struct vport *vport, struct sk_buff *skb)
        }
 
        /* Route lookup */
-       rt = find_route(vport, mutable, saddr, daddr, tos, &cache);
-       if (unlikely(!rt))
+       rt = find_route(port_key_get_net(&mutable->key), &saddr, daddr,
+                         tnl_vport->tnl_ops->ipproto, tos);
+       if (IS_ERR(rt))
                goto error_free;
-       if (unlikely(!cache))
-               unattached_dst = &rt_dst(rt);
 
        /* Reset SKB */
        nf_reset(skb);
@@ -1308,24 +961,15 @@ int ovs_tnl_send(struct vport *vport, struct sk_buff *skb)
 
        /* Offloading */
        skb = handle_offloads(skb, mutable, rt, tunnel_hlen);
-       if (IS_ERR(skb))
-               goto error;
+       if (IS_ERR(skb)) {
+               skb = NULL;
+               goto err_free_rt;
+       }
 
        /* MTU */
        if (unlikely(!check_mtu(skb, vport, mutable, rt, &frag_off, tunnel_hlen))) {
                err = VPORT_E_TX_DROPPED;
-               goto error_free;
-       }
-
-       /*
-        * If we are over the MTU, allow the IP stack to handle fragmentation.
-        * Fragmentation is a slow path anyways.
-        */
-       if (unlikely(skb->len + tunnel_hlen > dst_mtu(&rt_dst(rt)) &&
-                    cache)) {
-               unattached_dst = &rt_dst(rt);
-               dst_hold(unattached_dst);
-               cache = NULL;
+               goto err_free_rt;
        }
 
        /* TTL Fixup. */
@@ -1344,67 +988,34 @@ int ovs_tnl_send(struct vport *vport, struct sk_buff *skb)
                if (unlikely(vlan_deaccel_tag(skb)))
                        goto next;
 
-               if (likely(cache)) {
-                       skb_push(skb, cache->len);
-                       memcpy(skb->data, get_cached_header(cache), cache->len);
-                       skb_reset_mac_header(skb);
-                       skb_set_network_header(skb, cache->hh_len);
-
-               } else {
-                       skb_push(skb, tunnel_hlen);
-                       create_tunnel_header(vport, mutable, OVS_CB(skb)->tun_key, rt, skb->data);
-                       skb_reset_network_header(skb);
-
-                       if (next_skb)
-                               skb_dst_set(skb, dst_clone(unattached_dst));
-                       else {
-                               skb_dst_set(skb, unattached_dst);
-                               unattached_dst = NULL;
-                       }
-               }
-               skb_set_transport_header(skb, skb_network_offset(skb) + sizeof(struct iphdr));
+               skb_push(skb, tunnel_hlen);
+               skb_reset_network_header(skb);
+               skb_set_transport_header(skb, sizeof(struct iphdr));
 
+               if (next_skb)
+                       skb_dst_set(skb, dst_clone(&rt_dst(rt)));
+               else
+                       skb_dst_set(skb, &rt_dst(rt));
+
+               /* Push IP header. */
                iph = ip_hdr(skb);
-               iph->tos = tos;
-               iph->ttl = ttl;
-               iph->frag_off = frag_off;
+               iph->version    = 4;
+               iph->ihl        = sizeof(struct iphdr) >> 2;
+               iph->protocol   = tnl_vport->tnl_ops->ipproto;
+               iph->daddr      = daddr;
+               iph->saddr      = saddr;
+               iph->tos        = tos;
+               iph->ttl        = ttl;
+               iph->frag_off   = frag_off;
                ip_select_ident(iph, &rt_dst(rt), NULL);
 
-               skb = tnl_vport->tnl_ops->update_header(vport, mutable,
+               /* Push Tunnel header. */
+               skb = tnl_vport->tnl_ops->build_header(vport, mutable,
                                                        &rt_dst(rt), skb, tunnel_hlen);
                if (unlikely(!skb))
                        goto next;
 
-               if (likely(cache)) {
-                       int orig_len = skb->len - cache->len;
-                       struct vport *cache_vport;
-
-                       cache_vport = ovs_internal_dev_get_vport(rt_dst(rt).dev);
-                       skb->protocol = htons(ETH_P_IP);
-                       iph = ip_hdr(skb);
-                       iph->tot_len = htons(skb->len - skb_network_offset(skb));
-                       ip_send_check(iph);
-
-                       if (cache_vport) {
-                               if (unlikely(compute_ip_summed(skb, true))) {
-                                       kfree_skb(skb);
-                                       goto next;
-                               }
-
-                               OVS_CB(skb)->flow = cache->flow;
-                               ovs_vport_receive(cache_vport, skb);
-                               sent_len += orig_len;
-                       } else {
-                               int xmit_err;
-
-                               skb->dev = rt_dst(rt).dev;
-                               xmit_err = dev_queue_xmit(skb);
-
-                               if (likely(net_xmit_eval(xmit_err) == 0))
-                                       sent_len += orig_len;
-                       }
-               } else
-                       sent_len += send_frags(skb, tunnel_hlen);
+               sent_len += send_frags(skb, tunnel_hlen);
 
 next:
                skb = next_skb;
@@ -1413,14 +1024,13 @@ next:
        if (unlikely(sent_len == 0))
                ovs_vport_record_error(vport, VPORT_E_TX_DROPPED);
 
-       goto out;
+       return sent_len;
 
+err_free_rt:
+       ip_rt_put(rt);
 error_free:
        ovs_tnl_free_linked_skbs(skb);
-error:
        ovs_vport_record_error(vport, err);
-out:
-       dst_release(unattached_dst);
        return sent_len;
 }
 
@@ -1432,6 +1042,7 @@ static const struct nla_policy tnl_policy[OVS_TUNNEL_ATTR_MAX + 1] = {
        [OVS_TUNNEL_ATTR_IN_KEY]   = { .type = NLA_U64 },
        [OVS_TUNNEL_ATTR_TOS]      = { .type = NLA_U8 },
        [OVS_TUNNEL_ATTR_TTL]      = { .type = NLA_U8 },
+       [OVS_TUNNEL_ATTR_DST_PORT] = { .type = NLA_U16 },
 };
 
 /* Sets OVS_TUNNEL_ATTR_* fields in 'mutable', which must initially be
@@ -1455,11 +1066,21 @@ static int tnl_set_config(struct net *net, struct nlattr *options,
        if (err)
                return err;
 
-       if (!a[OVS_TUNNEL_ATTR_FLAGS] || !a[OVS_TUNNEL_ATTR_DST_IPV4])
-               return -EINVAL;
+       /* Process attributes possibly useful for null_ports first */
+       if (a[OVS_TUNNEL_ATTR_DST_PORT])
+               mutable->dst_port =
+                       htons(nla_get_u16(a[OVS_TUNNEL_ATTR_DST_PORT]));
 
-       mutable->flags = nla_get_u32(a[OVS_TUNNEL_ATTR_FLAGS]) & TNL_F_PUBLIC;
-       mutable->key.daddr = nla_get_be32(a[OVS_TUNNEL_ATTR_DST_IPV4]);
+       if (a[OVS_TUNNEL_ATTR_DST_IPV4])
+               mutable->key.daddr = nla_get_be32(a[OVS_TUNNEL_ATTR_DST_IPV4]);
+
+       /* Skip the rest if configuring a null_port */
+       if (!mutable->key.daddr)
+               goto out;
+
+       if (a[OVS_TUNNEL_ATTR_FLAGS])
+               mutable->flags = nla_get_u32(a[OVS_TUNNEL_ATTR_FLAGS])
+                       & TNL_F_PUBLIC;
 
        if (a[OVS_TUNNEL_ATTR_SRC_IPV4]) {
                if (ipv4_is_multicast(mutable->key.daddr))
@@ -1494,9 +1115,11 @@ static int tnl_set_config(struct net *net, struct nlattr *options,
        if (ipv4_is_multicast(mutable->key.daddr)) {
                struct net_device *dev;
                struct rtable *rt;
+               __be32 saddr = mutable->key.saddr;
 
-               rt = __find_route(mutable, mutable->key.saddr, mutable->key.daddr,
-                                 tnl_ops->ipproto, mutable->tos);
+               rt = find_route(port_key_get_net(&mutable->key),
+                            &saddr, mutable->key.daddr,
+                            tnl_ops->ipproto, mutable->tos);
                if (IS_ERR(rt))
                        return -EADDRNOTAVAIL;
                dev = rt_dst(rt).dev;
@@ -1552,13 +1175,6 @@ struct vport *ovs_tnl_create(const struct vport_parms *parms,
        if (err)
                goto error_free_mutable;
 
-       spin_lock_init(&tnl_vport->cache_lock);
-
-#ifdef NEED_CACHE_TIMEOUT
-       tnl_vport->cache_exp_interval = MAX_CACHE_EXP -
-                                      (net_random() % (MAX_CACHE_EXP / 2));
-#endif
-
        rcu_assign_pointer(tnl_vport->mutable, mutable);
 
        port_table_add_port(vport);
@@ -1619,11 +1235,19 @@ int ovs_tnl_get_options(const struct vport *vport, struct sk_buff *skb)
        const struct tnl_vport *tnl_vport = tnl_vport_priv(vport);
        const struct tnl_mutable_config *mutable = rcu_dereference_rtnl(tnl_vport->mutable);
 
-       if (nla_put_u32(skb, OVS_TUNNEL_ATTR_FLAGS,
-                     mutable->flags & TNL_F_PUBLIC) ||
-           nla_put_be32(skb, OVS_TUNNEL_ATTR_DST_IPV4, mutable->key.daddr))
+       if (mutable->dst_port && nla_put_u16(skb, OVS_TUNNEL_ATTR_DST_PORT,
+                                            ntohs(mutable->dst_port)))
                goto nla_put_failure;
 
+       /* Skip the rest for null_ports */
+       if (!mutable->key.daddr)
+               return 0;
+
+       if (nla_put_be32(skb, OVS_TUNNEL_ATTR_DST_IPV4, mutable->key.daddr))
+               goto nla_put_failure;
+       if (nla_put_u32(skb, OVS_TUNNEL_ATTR_FLAGS,
+                       mutable->flags & TNL_F_PUBLIC))
+               goto nla_put_failure;
        if (!(mutable->flags & TNL_F_IN_KEY_MATCH) &&
            nla_put_be64(skb, OVS_TUNNEL_ATTR_IN_KEY, mutable->key.in_key))
                goto nla_put_failure;
@@ -1649,7 +1273,6 @@ static void free_port_rcu(struct rcu_head *rcu)
        struct tnl_vport *tnl_vport = container_of(rcu,
                                                   struct tnl_vport, rcu);
 
-       free_cache((struct tnl_cache __force *)tnl_vport->cache);
        kfree((struct tnl_mutable __force *)tnl_vport->mutable);
        ovs_vport_free(tnl_vport_to_vport(tnl_vport));
 }
index 951a6f1..7705475 100644 (file)
@@ -42,6 +42,7 @@
 #define TNL_T_PROTO_GRE                0
 #define TNL_T_PROTO_GRE64      1
 #define TNL_T_PROTO_CAPWAP     2
+#define TNL_T_PROTO_VXLAN      3
 
 /* These flags are only needed when calling tnl_find_port(). */
 #define TNL_T_KEY_EXACT                (1 << 10)
@@ -56,7 +57,7 @@
 /* All public tunnel flags. */
 #define TNL_F_PUBLIC (TNL_F_CSUM | TNL_F_TOS_INHERIT | TNL_F_TTL_INHERIT | \
                      TNL_F_DF_INHERIT | TNL_F_DF_DEFAULT | TNL_F_PMTUD | \
-                     TNL_F_HDR_CACHE | TNL_F_IPSEC)
+                     TNL_F_IPSEC)
 
 /**
  * struct port_lookup_key - Tunnel port key, used as hash table key.
@@ -116,6 +117,7 @@ struct tnl_mutable_config {
        u32     flags;
        u8      tos;
        u8      ttl;
+       __be16  dst_port;
 
        /* Multicast configuration. */
        int     mlink;
@@ -132,104 +134,19 @@ struct tnl_ops {
         */
        int (*hdr_len)(const struct tnl_mutable_config *,
                       const struct ovs_key_ipv4_tunnel *);
-
        /*
-        * Builds the static portion of the tunnel header, which is stored in
-        * the header cache.  In general the performance of this function is
-        * not too important as we try to only call it when building the cache
-        * so it is preferable to shift as much work as possible here.  However,
-        * in some circumstances caching is disabled and this function will be
-        * called for every packet, so try not to make it too slow.
+        * Returns a linked list of SKBs with tunnel headers (multiple
+        * packets may be generated in the event of fragmentation).  Space
+        * will have already been allocated at the start of the packet equal
+        * to sizeof(struct iphdr) + value returned by hdr_len().  The IP
+        * header will have already been constructed.
         */
-       void (*build_header)(const struct vport *,
-                            const struct tnl_mutable_config *,
-                            const struct ovs_key_ipv4_tunnel *, void *header);
-
-       /*
-        * Updates the cached header of a packet to match the actual packet
-        * data.  Typical things that might need to be updated are length,
-        * checksum, etc.  The IP header will have already been updated and this
-        * is the final step before transmission.  Returns a linked list of
-        * completed SKBs (multiple packets may be generated in the event
-        * of fragmentation).
-        */
-       struct sk_buff *(*update_header)(const struct vport *,
+       struct sk_buff *(*build_header)(const struct vport *,
                                         const struct tnl_mutable_config *,
                                         struct dst_entry *, struct sk_buff *,
                                         int tunnel_hlen);
 };
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
-/*
- * On these kernels we have a fast mechanism to tell if the ARP cache for a
- * particular destination has changed.
- */
-#define HAVE_HH_SEQ
-#endif
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
-/*
- * On these kernels we have a fast mechanism to tell if the routing table
- * has changed.
- */
-#define HAVE_RT_GENID
-#endif
-#if !defined(HAVE_HH_SEQ) || !defined(HAVE_RT_GENID)
-/* If we can't detect all system changes directly we need to use a timeout. */
-#define NEED_CACHE_TIMEOUT
-#endif
-struct tnl_cache {
-       struct rcu_head rcu;
-
-       int len;                /* Length of data to be memcpy'd from cache. */
-       int hh_len;             /* Hardware hdr length, cached from hh_cache. */
-
-       /* Sequence number of mutable->seq from which this cache was
-        * generated. */
-       unsigned mutable_seq;
-
-#ifdef HAVE_HH_SEQ
-       /*
-        * The sequence number from the seqlock protecting the hardware header
-        * cache (in the ARP cache).  Since every write increments the counter
-        * this gives us an easy way to tell if it has changed.
-        */
-       unsigned hh_seq;
-#endif
-
-#ifdef NEED_CACHE_TIMEOUT
-       /*
-        * If we don't have direct mechanisms to detect all important changes in
-        * the system fall back to an expiration time.  This expiration time
-        * can be relatively short since at high rates there will be millions of
-        * packets per second, so we'll still get plenty of benefit from the
-        * cache.  Note that if something changes we may blackhole packets
-        * until the expiration time (depending on what changed and the kernel
-        * version we may be able to detect the change sooner).  Expiration is
-        * expressed as a time in jiffies.
-        */
-       unsigned long expiration;
-#endif
-
-       /*
-        * The routing table entry that is the result of looking up the tunnel
-        * endpoints.  It also contains a sequence number (called a generation
-        * ID) that can be compared to a global sequence to tell if the routing
-        * table has changed (and therefore there is a potential that this
-        * cached route has been invalidated).
-        */
-       struct rtable *rt;
-
-       /*
-        * If the output device for tunnel traffic is an OVS internal device,
-        * the flow of that datapath.  Since all tunnel traffic will have the
-        * same headers this allows us to cache the flow lookup.  NULL if the
-        * output device is not OVS or if there is no flow installed.
-        */
-       struct sw_flow *flow;
-
-       /* The cached header follows after padding for alignment. */
-};
-
 struct tnl_vport {
        struct rcu_head rcu;
        struct hlist_node hash_node;
@@ -245,19 +162,6 @@ struct tnl_vport {
         * this is not needed.
         */
        atomic_t frag_id;
-
-       spinlock_t cache_lock;
-       struct tnl_cache __rcu *cache;  /* Protected by RCU/cache_lock. */
-
-#ifdef NEED_CACHE_TIMEOUT
-       /*
-        * If we must rely on expiration time to invalidate the cache, this is
-        * the interval.  It is randomized within a range (defined by
-        * MAX_CACHE_EXP in tunnel.c) to avoid synchronized expirations caused
-        * by creation of a large number of tunnels at a one time.
-        */
-       unsigned long cache_exp_interval;
-#endif
 };
 
 struct vport *ovs_tnl_create(const struct vport_parms *, const struct vport_ops *,
@@ -297,6 +201,28 @@ static inline void tnl_tun_key_init(struct ovs_key_ipv4_tunnel *tun_key,
        tun_key->ipv4_tos = iph->tos;
        tun_key->ipv4_ttl = iph->ttl;
        tun_key->tun_flags = tun_flags;
+       memset(tun_key->pad, 0, sizeof(tun_key->pad));
+}
+
+static inline void tnl_get_param(const struct tnl_mutable_config *mutable,
+                                const struct ovs_key_ipv4_tunnel *tun_key,
+                                u32 *flags,  __be64 *out_key)
+{
+       if (tun_key->ipv4_dst) {
+               *flags = 0;
+
+               if (tun_key->tun_flags & OVS_TNL_F_KEY)
+                       *flags = TNL_F_OUT_KEY_ACTION;
+               if (tun_key->tun_flags & OVS_TNL_F_CSUM)
+                       *flags |= TNL_F_CSUM;
+               *out_key = tun_key->tun_id;
+       } else {
+               *flags = mutable->flags;
+               if (mutable->flags & TNL_F_OUT_KEY_ACTION)
+                       *out_key = tun_key->tun_id;
+               else
+                       *out_key = mutable->out_key;
+       }
 }
 
 #endif /* tunnel.h */
index 39aec42..f45d349 100644 (file)
@@ -27,7 +27,6 @@
 #include "datapath.h"
 #include "tunnel.h"
 #include "vport.h"
-#include "vport-generic.h"
 
 #define CAPWAP_SRC_PORT 58881
 #define CAPWAP_DST_PORT 58882
@@ -143,7 +142,11 @@ static struct sk_buff *defrag(struct sk_buff *, bool frag_last);
 
 static void capwap_frag_init(struct inet_frag_queue *, void *match);
 static unsigned int capwap_frag_hash(struct inet_frag_queue *);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0)
 static int capwap_frag_match(struct inet_frag_queue *, void *match);
+#else
+static bool capwap_frag_match(struct inet_frag_queue *, void *match);
+#endif
 static void capwap_frag_expire(unsigned long ifq);
 
 static struct inet_frags frag_state = {
@@ -155,28 +158,6 @@ static struct inet_frags frag_state = {
        .secret_interval = CAPWAP_FRAG_SECRET_INTERVAL,
 };
 
-static void get_capwap_param(const struct tnl_mutable_config *mutable,
-                       const struct ovs_key_ipv4_tunnel *tun_key,
-                       u32 *flags,  __be64 *out_key)
-{
-       if (tun_key->ipv4_dst) {
-               *flags = 0;
-
-               if (tun_key->tun_flags & OVS_FLOW_TNL_F_KEY)
-                       *flags = TNL_F_OUT_KEY_ACTION;
-               if (tun_key->tun_flags & OVS_FLOW_TNL_F_CSUM)
-                       *flags |= TNL_F_CSUM;
-               *out_key = tun_key->tun_id;
-       } else {
-               *flags = mutable->flags;
-               if (mutable->flags & TNL_F_OUT_KEY_ACTION)
-                       *out_key = tun_key->tun_id;
-               else
-                       *out_key = mutable->out_key;
-
-       }
-}
-
 static int capwap_hdr_len(const struct tnl_mutable_config *mutable,
                          const struct ovs_key_ipv4_tunnel *tun_key)
 {
@@ -184,7 +165,7 @@ static int capwap_hdr_len(const struct tnl_mutable_config *mutable,
        u32 flags;
        __be64 out_key;
 
-       get_capwap_param(mutable, tun_key, &flags, &out_key);
+       tnl_get_param(mutable, tun_key, &flags, &out_key);
 
        /* CAPWAP has no checksums. */
        if (flags & TNL_F_CSUM)
@@ -199,17 +180,19 @@ static int capwap_hdr_len(const struct tnl_mutable_config *mutable,
        return size;
 }
 
-static void capwap_build_header(const struct vport *vport,
-                               const struct tnl_mutable_config *mutable,
-                               const struct ovs_key_ipv4_tunnel *tun_key,
-                               void *header)
+static struct sk_buff *capwap_build_header(const struct vport *vport,
+                                           const struct tnl_mutable_config *mutable,
+                                           struct dst_entry *dst,
+                                           struct sk_buff *skb,
+                                           int tunnel_hlen)
 {
-       struct udphdr *udph = header;
+       struct ovs_key_ipv4_tunnel *tun_key = OVS_CB(skb)->tun_key;
+       struct udphdr *udph = udp_hdr(skb);
        struct capwaphdr *cwh = (struct capwaphdr *)(udph + 1);
        u32 flags;
        __be64 out_key;
 
-       get_capwap_param(mutable, tun_key, &flags, &out_key);
+       tnl_get_param(mutable, tun_key, &flags, &out_key);
 
        udph->source = htons(CAPWAP_SRC_PORT);
        udph->dest = htons(CAPWAP_DST_PORT);
@@ -218,7 +201,8 @@ static void capwap_build_header(const struct vport *vport,
        cwh->frag_id = 0;
        cwh->frag_off = 0;
 
-       if (out_key || (flags & TNL_F_OUT_KEY_ACTION)) {
+       if (out_key || flags & TNL_F_OUT_KEY_ACTION) {
+               /* first field in WSI is key */
                struct capwaphdr_wsi *wsi = (struct capwaphdr_wsi *)(cwh + 1);
 
                cwh->begin = CAPWAP_KEYED;
@@ -237,30 +221,6 @@ static void capwap_build_header(const struct vport *vport,
                /* make packet readable by old capwap code */
                cwh->begin = CAPWAP_NO_WSI;
        }
-}
-
-static struct sk_buff *capwap_update_header(const struct vport *vport,
-                                           const struct tnl_mutable_config *mutable,
-                                           struct dst_entry *dst,
-                                           struct sk_buff *skb,
-                                           int tunnel_hlen)
-{
-       const struct ovs_key_ipv4_tunnel *tun_key = OVS_CB(skb)->tun_key;
-       struct udphdr *udph = udp_hdr(skb);
-       u32 flags;
-       __be64 out_key;
-
-       get_capwap_param(mutable, tun_key, &flags, &out_key);
-
-       if (flags & TNL_F_OUT_KEY_ACTION) {
-               /* first field in WSI is key */
-               struct capwaphdr *cwh = (struct capwaphdr *)(udph + 1);
-               struct capwaphdr_wsi *wsi = (struct capwaphdr_wsi *)(cwh + 1);
-               struct capwaphdr_wsi_key *opt = (struct capwaphdr_wsi_key *)(wsi + 1);
-
-               opt->key = out_key;
-       }
-
        udph->len = htons(skb->len - skb_transport_offset(skb));
 
        if (unlikely(skb->len - skb_network_offset(skb) > dst_mtu(dst))) {
@@ -377,10 +337,12 @@ static int capwap_rcv(struct sock *sk, struct sk_buff *skb)
        }
 
        if (key_present && mutable->key.daddr &&
-                        !(mutable->flags & TNL_F_IN_KEY_MATCH))
+                        !(mutable->flags & TNL_F_IN_KEY_MATCH)) {
                key_present = false;
+               key = 0;
+       }
 
-       tnl_tun_key_init(&tun_key, iph, key, key_present ? OVS_FLOW_TNL_F_KEY : 0);
+       tnl_tun_key_init(&tun_key, iph, key, key_present ? OVS_TNL_F_KEY : 0);
        OVS_CB(skb)->tun_key = &tun_key;
 
        ovs_tnl_rcv(vport, skb);
@@ -397,7 +359,6 @@ static const struct tnl_ops capwap_tnl_ops = {
        .ipproto        = IPPROTO_UDP,
        .hdr_len        = capwap_hdr_len,
        .build_header   = capwap_build_header,
-       .update_header  = capwap_update_header,
 };
 
 static inline struct capwap_net *ovs_get_capwap_net(struct net *net)
@@ -445,7 +406,7 @@ static int init_socket(struct net *net)
        capwap_net->frag_state.low_thresh       = CAPWAP_FRAG_PRUNE_MEM;
 
        inet_frags_init_net(&capwap_net->frag_state);
-
+       udp_encap_enable();
        capwap_net->n_tunnels++;
        return 0;
 
@@ -811,8 +772,7 @@ static struct sk_buff *defrag(struct sk_buff *skb, bool frag_last)
        u16 frag_off;
        struct frag_queue *fq;
 
-       if (atomic_read(&ns_frag_state->mem) > ns_frag_state->high_thresh)
-               inet_frag_evictor(ns_frag_state, &frag_state);
+       inet_frag_evictor(ns_frag_state, &frag_state, false);
 
        match.daddr = iph->daddr;
        match.saddr = iph->saddr;
@@ -846,7 +806,11 @@ static unsigned int capwap_frag_hash(struct inet_frag_queue *ifq)
        return frag_hash(&ifq_cast(ifq)->match);
 }
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0)
 static int capwap_frag_match(struct inet_frag_queue *ifq, void *a_)
+#else
+static bool capwap_frag_match(struct inet_frag_queue *ifq, void *a_)
+#endif
 {
        struct frag_match *a = a_;
        struct frag_match *b = &ifq_cast(ifq)->match;
@@ -882,9 +846,6 @@ const struct vport_ops ovs_capwap_vport_ops = {
        .get_addr       = ovs_tnl_get_addr,
        .get_options    = ovs_tnl_get_options,
        .set_options    = ovs_tnl_set_options,
-       .get_dev_flags  = ovs_vport_gen_get_dev_flags,
-       .is_running     = ovs_vport_gen_is_running,
-       .get_operstate  = ovs_vport_gen_get_operstate,
        .send           = ovs_tnl_send,
 };
 #else
diff --git a/datapath/vport-generic.c b/datapath/vport-generic.c
deleted file mode 100644 (file)
index 09b0b7c..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (c) 2007-2011 Nicira, Inc.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public
- * License as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA
- */
-
-#include <linux/etherdevice.h>
-
-#include "vport-generic.h"
-
-unsigned ovs_vport_gen_get_dev_flags(const struct vport *vport)
-{
-       return IFF_UP | IFF_RUNNING | IFF_LOWER_UP;
-}
-
-int ovs_vport_gen_is_running(const struct vport *vport)
-{
-       return 1;
-}
-
-unsigned char ovs_vport_gen_get_operstate(const struct vport *vport)
-{
-       return IF_OPER_UP;
-}
diff --git a/datapath/vport-generic.h b/datapath/vport-generic.h
deleted file mode 100644 (file)
index 4a295c7..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (c) 2007-2011 Nicira, Inc.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public
- * License as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA
- */
-
-#ifndef VPORT_GENERIC_H
-#define VPORT_GENERIC_H 1
-
-#include "vport.h"
-
-unsigned ovs_vport_gen_get_dev_flags(const struct vport *);
-int ovs_vport_gen_is_running(const struct vport *);
-unsigned char ovs_vport_gen_get_operstate(const struct vport *);
-
-#endif /* vport-generic.h */
index d02d4ec..8ce8a35 100644 (file)
@@ -32,7 +32,6 @@
 #include "datapath.h"
 #include "tunnel.h"
 #include "vport.h"
-#include "vport-generic.h"
 
 /*
  * The GRE header is composed of a series of sections: a base and then a variable
@@ -45,39 +44,14 @@ struct gre_base_hdr {
        __be16 protocol;
 };
 
-static void get_gre_param(const struct tnl_mutable_config *mutable,
-                       const struct ovs_key_ipv4_tunnel *tun_key,
-                       u32 *flags, u32 *tunnel_type, __be64 *out_key)
-{
-       if (tun_key->ipv4_dst) {
-               *flags = 0;
-
-               if (tun_key->tun_flags & OVS_FLOW_TNL_F_KEY)
-                       *flags = TNL_F_OUT_KEY_ACTION;
-               if (tun_key->tun_flags & OVS_FLOW_TNL_F_CSUM)
-                       *flags |= TNL_F_CSUM;
-               *tunnel_type = TNL_T_PROTO_GRE;
-               *out_key = tun_key->tun_id;
-       } else {
-               *flags = mutable->flags;
-               *tunnel_type = mutable->key.tunnel_type;
-               if (mutable->flags & TNL_F_OUT_KEY_ACTION)
-                       *out_key = tun_key->tun_id;
-               else
-                       *out_key = mutable->out_key;
-
-       }
-}
-
 static int gre_hdr_len(const struct tnl_mutable_config *mutable,
                       const struct ovs_key_ipv4_tunnel *tun_key)
 {
        int len;
        u32 flags;
-       u32 tunnel_type;
        __be64 out_key;
 
-       get_gre_param(mutable, tun_key, &flags, &tunnel_type, &out_key);
+       tnl_get_param(mutable, tun_key, &flags, &out_key);
        len = GRE_HEADER_SECTION;
 
        if (flags & TNL_F_CSUM)
@@ -85,11 +59,11 @@ static int gre_hdr_len(const struct tnl_mutable_config *mutable,
 
        /* Set key for GRE64 tunnels, even when key if is zero. */
        if (out_key ||
-           tunnel_type & TNL_T_PROTO_GRE64 ||
+           mutable->key.tunnel_type & TNL_T_PROTO_GRE64 ||
            flags & TNL_F_OUT_KEY_ACTION) {
 
                len += GRE_HEADER_SECTION;
-               if (tunnel_type & TNL_T_PROTO_GRE64)
+               if (mutable->key.tunnel_type & TNL_T_PROTO_GRE64)
                        len += GRE_HEADER_SECTION;
        }
        return len;
@@ -115,80 +89,46 @@ static __be32 be64_get_high32(__be64 x)
 #endif
 }
 
-static void gre_build_header(const struct vport *vport,
-                            const struct tnl_mutable_config *mutable,
-                            const struct ovs_key_ipv4_tunnel *tun_key,
-                            void *header)
-{
-       struct gre_base_hdr *greh = header;
-       __be32 *options = (__be32 *)(greh + 1);
-       u32 flags;
-       u32 tunnel_type;
-       __be64 out_key;
-
-       get_gre_param(mutable, tun_key, &flags, &tunnel_type, &out_key);
-
-       greh->protocol = htons(ETH_P_TEB);
-       greh->flags = 0;
-
-       if (flags & TNL_F_CSUM) {
-               greh->flags |= GRE_CSUM;
-               *options = 0;
-               options++;
-       }
-
-       if (flags & TNL_F_OUT_KEY_ACTION) {
-               greh->flags |= GRE_KEY;
-               if (tunnel_type & TNL_T_PROTO_GRE64)
-                       greh->flags |= GRE_SEQ;
-
-       } else if (out_key ||
-                  tunnel_type & TNL_T_PROTO_GRE64) {
-               greh->flags |= GRE_KEY;
-               *options = be64_get_low32(out_key);
-               if (tunnel_type & TNL_T_PROTO_GRE64) {
-                       options++;
-                       *options = be64_get_high32(out_key);
-                       greh->flags |= GRE_SEQ;
-               }
-       }
-}
-
-static struct sk_buff *gre_update_header(const struct vport *vport,
+static struct sk_buff *gre_build_header(const struct vport *vport,
                                         const struct tnl_mutable_config *mutable,
                                         struct dst_entry *dst,
                                         struct sk_buff *skb,
                                         int tunnel_hlen)
 {
        u32 flags;
-       u32 tunnel_type;
        __be64 out_key;
        const struct ovs_key_ipv4_tunnel *tun_key = OVS_CB(skb)->tun_key;
        __be32 *options = (__be32 *)(skb_network_header(skb) + tunnel_hlen
                                               - GRE_HEADER_SECTION);
+       struct gre_base_hdr *greh = (struct gre_base_hdr *) skb_transport_header(skb);
 
-       get_gre_param(mutable, tun_key, &flags, &tunnel_type, &out_key);
+       tnl_get_param(mutable, tun_key, &flags, &out_key);
+
+       greh->protocol = htons(ETH_P_TEB);
+       greh->flags = 0;
 
        /* Work backwards over the options so the checksum is last. */
-       if (flags & TNL_F_OUT_KEY_ACTION) {
-               if (tunnel_type & TNL_T_PROTO_GRE64) {
+       if (out_key || flags & TNL_F_OUT_KEY_ACTION ||
+           mutable->key.tunnel_type & TNL_T_PROTO_GRE64) {
+               greh->flags |= GRE_KEY;
+               if (mutable->key.tunnel_type & TNL_T_PROTO_GRE64) {
                        /* Set higher 32 bits to seq. */
                        *options = be64_get_high32(out_key);
                        options--;
+                       greh->flags |= GRE_SEQ;
                }
                *options = be64_get_low32(out_key);
                options--;
-       } else if (out_key || tunnel_type & TNL_T_PROTO_GRE64) {
-               options--;
-               if (tunnel_type & TNL_T_PROTO_GRE64)
-                       options--;
        }
 
-       if (flags & TNL_F_CSUM)
+       if (flags & TNL_F_CSUM) {
+               greh->flags |= GRE_CSUM;
+               *options = 0;
                *(__sum16 *)options = csum_fold(skb_checksum(skb,
                                                skb_transport_offset(skb),
                                                skb->len - skb_transport_offset(skb),
                                                0));
+       }
        /*
         * Allow our local IP stack to fragment the outer packet even if the
         * DF bit is set as a last resort.  We also need to force selection of
@@ -412,19 +352,20 @@ static bool check_checksum(struct sk_buff *skb)
 }
 
 static u32 gre_flags_to_tunnel_flags(const struct tnl_mutable_config *mutable,
-                                    __be16 gre_flags)
+                                    __be16 gre_flags, __be64 *key)
 {
        u32 tunnel_flags = 0;
 
        if (gre_flags & GRE_KEY) {
-               if (mutable->key.daddr && (mutable->flags & TNL_F_IN_KEY_MATCH))
-                       tunnel_flags = OVS_FLOW_TNL_F_KEY;
-               else if (!mutable->key.daddr)
-                       tunnel_flags = OVS_FLOW_TNL_F_KEY;
+               if (mutable->flags & TNL_F_IN_KEY_MATCH ||
+                   !mutable->key.daddr)
+                       tunnel_flags = OVS_TNL_F_KEY;
+               else
+                       *key = 0;
        }
 
        if (gre_flags & GRE_CSUM)
-               tunnel_flags |= OVS_FLOW_TNL_F_CSUM;
+               tunnel_flags |= OVS_TNL_F_CSUM;
 
        return tunnel_flags;
 }
@@ -437,7 +378,8 @@ static int gre_rcv(struct sk_buff *skb)
        int hdr_len;
        struct iphdr *iph;
        struct ovs_key_ipv4_tunnel tun_key;
-       __be16 flags;
+       __be16 gre_flags;
+       u32 tnl_flags;
        __be64 key;
        u32 tunnel_type;
 
@@ -446,7 +388,7 @@ static int gre_rcv(struct sk_buff *skb)
        if (unlikely(!check_checksum(skb)))
                goto error;
 
-       hdr_len = parse_header(ip_hdr(skb), &flags, &key, &tunnel_type);
+       hdr_len = parse_header(ip_hdr(skb), &gre_flags, &key, &tunnel_type);
        if (unlikely(hdr_len < 0))
                goto error;
 
@@ -461,7 +403,8 @@ static int gre_rcv(struct sk_buff *skb)
                goto error;
        }
 
-       tnl_tun_key_init(&tun_key, iph, key, gre_flags_to_tunnel_flags(mutable, flags));
+       tnl_flags = gre_flags_to_tunnel_flags(mutable, gre_flags, &key);
+       tnl_tun_key_init(&tun_key, iph, key, tnl_flags);
        OVS_CB(skb)->tun_key = &tun_key;
 
        __skb_pull(skb, hdr_len);
@@ -480,7 +423,6 @@ static const struct tnl_ops gre_tnl_ops = {
        .ipproto        = IPPROTO_GRE,
        .hdr_len        = gre_hdr_len,
        .build_header   = gre_build_header,
-       .update_header  = gre_update_header,
 };
 
 static struct vport *gre_create(const struct vport_parms *parms)
@@ -488,12 +430,16 @@ static struct vport *gre_create(const struct vport_parms *parms)
        return ovs_tnl_create(parms, &ovs_gre_vport_ops, &gre_tnl_ops);
 }
 
+static struct vport *gre_create_ft(const struct vport_parms *parms)
+{
+       return ovs_tnl_create(parms, &ovs_gre_ft_vport_ops, &gre_tnl_ops);
+}
+
 static const struct tnl_ops gre64_tnl_ops = {
        .tunnel_type    = TNL_T_PROTO_GRE64,
        .ipproto        = IPPROTO_GRE,
        .hdr_len        = gre_hdr_len,
        .build_header   = gre_build_header,
-       .update_header  = gre_update_header,
 };
 
 static struct vport *gre_create64(const struct vport_parms *parms)
@@ -536,6 +482,21 @@ static void gre_exit(void)
        inet_del_protocol(&gre_protocol_handlers, IPPROTO_GRE);
 }
 
+const struct vport_ops ovs_gre_ft_vport_ops = {
+       .type           = OVS_VPORT_TYPE_FT_GRE,
+       .flags          = VPORT_F_TUN_ID,
+       .init           = gre_init,
+       .exit           = gre_exit,
+       .create         = gre_create_ft,
+       .destroy        = ovs_tnl_destroy,
+       .set_addr       = ovs_tnl_set_addr,
+       .get_name       = ovs_tnl_get_name,
+       .get_addr       = ovs_tnl_get_addr,
+       .get_options    = ovs_tnl_get_options,
+       .set_options    = ovs_tnl_set_options,
+       .send           = ovs_tnl_send,
+};
+
 const struct vport_ops ovs_gre_vport_ops = {
        .type           = OVS_VPORT_TYPE_GRE,
        .flags          = VPORT_F_TUN_ID,
@@ -548,9 +509,6 @@ const struct vport_ops ovs_gre_vport_ops = {
        .get_addr       = ovs_tnl_get_addr,
        .get_options    = ovs_tnl_get_options,
        .set_options    = ovs_tnl_set_options,
-       .get_dev_flags  = ovs_vport_gen_get_dev_flags,
-       .is_running     = ovs_vport_gen_is_running,
-       .get_operstate  = ovs_vport_gen_get_operstate,
        .send           = ovs_tnl_send,
 };
 
@@ -566,8 +524,5 @@ const struct vport_ops ovs_gre64_vport_ops = {
        .get_addr       = ovs_tnl_get_addr,
        .get_options    = ovs_tnl_get_options,
        .set_options    = ovs_tnl_set_options,
-       .get_dev_flags  = ovs_vport_gen_get_dev_flags,
-       .is_running     = ovs_vport_gen_is_running,
-       .get_operstate  = ovs_vport_gen_get_operstate,
        .send           = ovs_tnl_send,
 };
index 4dc2eb4..9e2e788 100644 (file)
@@ -31,7 +31,6 @@
 #include "checksum.h"
 #include "datapath.h"
 #include "vlan.h"
-#include "vport-generic.h"
 #include "vport-internal_dev.h"
 #include "vport-netdev.h"
 
@@ -84,19 +83,6 @@ static struct net_device_stats *internal_dev_sys_stats(struct net_device *netdev
        return stats;
 }
 
-static int internal_dev_mac_addr(struct net_device *dev, void *p)
-{
-       struct sockaddr *addr = p;
-
-       if (!is_valid_ether_addr(addr->sa_data))
-               return -EADDRNOTAVAIL;
-#ifdef NET_ADDR_RANDOM
-       dev->addr_assign_type &= ~NET_ADDR_RANDOM;
-#endif
-       memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
-       return 0;
-}
-
 /* Called with rcu_read_lock_bh. */
 static int internal_dev_xmit(struct sk_buff *skb, struct net_device *netdev)
 {
@@ -154,15 +140,6 @@ static int internal_dev_change_mtu(struct net_device *netdev, int new_mtu)
        return 0;
 }
 
-static int internal_dev_do_ioctl(struct net_device *dev,
-                                struct ifreq *ifr, int cmd)
-{
-       if (ovs_dp_ioctl_hook)
-               return ovs_dp_ioctl_hook(dev, ifr, cmd);
-
-       return -EOPNOTSUPP;
-}
-
 static void internal_dev_destructor(struct net_device *dev)
 {
        struct vport *vport = ovs_internal_dev_get_vport(dev);
@@ -176,8 +153,7 @@ static const struct net_device_ops internal_dev_netdev_ops = {
        .ndo_open = internal_dev_open,
        .ndo_stop = internal_dev_stop,
        .ndo_start_xmit = internal_dev_xmit,
-       .ndo_set_mac_address = internal_dev_mac_addr,
-       .ndo_do_ioctl = internal_dev_do_ioctl,
+       .ndo_set_mac_address = eth_mac_addr,
        .ndo_change_mtu = internal_dev_change_mtu,
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)
        .ndo_get_stats64 = internal_dev_get_stats,
@@ -194,7 +170,6 @@ static void do_setup(struct net_device *netdev)
 #ifdef HAVE_NET_DEVICE_OPS
        netdev->netdev_ops = &internal_dev_netdev_ops;
 #else
-       netdev->do_ioctl = internal_dev_do_ioctl;
        netdev->get_stats = internal_dev_sys_stats;
        netdev->hard_start_xmit = internal_dev_xmit;
        netdev->open = internal_dev_open;
@@ -204,6 +179,7 @@ static void do_setup(struct net_device *netdev)
 #endif
 
        netdev->priv_flags &= ~IFF_TX_SKB_SHARING;
+       netdev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
        netdev->destructor = internal_dev_destructor;
        SET_ETHTOOL_OPS(netdev, &internal_dev_ethtool_ops);
        netdev->tx_queue_len = 0;
@@ -319,12 +295,7 @@ const struct vport_ops ovs_internal_vport_ops = {
        .set_addr       = ovs_netdev_set_addr,
        .get_name       = ovs_netdev_get_name,
        .get_addr       = ovs_netdev_get_addr,
-       .get_kobj       = ovs_netdev_get_kobj,
-       .get_dev_flags  = ovs_netdev_get_dev_flags,
-       .is_running     = ovs_netdev_is_running,
-       .get_operstate  = ovs_netdev_get_operstate,
        .get_ifindex    = ovs_netdev_get_ifindex,
-       .get_mtu        = ovs_netdev_get_mtu,
        .send           = internal_dev_recv,
 };
 
index f1ecfdb..78f1493 100644 (file)
@@ -173,6 +173,15 @@ error:
        return ERR_PTR(err);
 }
 
+static void free_port_rcu(struct rcu_head *rcu)
+{
+       struct netdev_vport *netdev_vport = container_of(rcu,
+                                       struct netdev_vport, rcu);
+
+       dev_put(netdev_vport->dev);
+       ovs_vport_free(vport_from_priv(netdev_vport));
+}
+
 static void netdev_destroy(struct vport *vport)
 {
        struct netdev_vport *netdev_vport = netdev_vport_priv(vport);
@@ -181,10 +190,7 @@ static void netdev_destroy(struct vport *vport)
        netdev_rx_handler_unregister(netdev_vport->dev);
        dev_set_promiscuity(netdev_vport->dev, -1);
 
-       synchronize_rcu();
-
-       dev_put(netdev_vport->dev);
-       ovs_vport_free(vport);
+       call_rcu(&netdev_vport->rcu, free_port_rcu);
 }
 
 int ovs_netdev_set_addr(struct vport *vport, const unsigned char *addr)
@@ -210,42 +216,12 @@ const unsigned char *ovs_netdev_get_addr(const struct vport *vport)
        return netdev_vport->dev->dev_addr;
 }
 
-struct kobject *ovs_netdev_get_kobj(const struct vport *vport)
-{
-       const struct netdev_vport *netdev_vport = netdev_vport_priv(vport);
-       return &netdev_vport->dev->NETDEV_DEV_MEMBER.kobj;
-}
-
-unsigned ovs_netdev_get_dev_flags(const struct vport *vport)
-{
-       const struct netdev_vport *netdev_vport = netdev_vport_priv(vport);
-       return dev_get_flags(netdev_vport->dev);
-}
-
-int ovs_netdev_is_running(const struct vport *vport)
-{
-       const struct netdev_vport *netdev_vport = netdev_vport_priv(vport);
-       return netif_running(netdev_vport->dev);
-}
-
-unsigned char ovs_netdev_get_operstate(const struct vport *vport)
-{
-       const struct netdev_vport *netdev_vport = netdev_vport_priv(vport);
-       return netdev_vport->dev->operstate;
-}
-
 int ovs_netdev_get_ifindex(const struct vport *vport)
 {
        const struct netdev_vport *netdev_vport = netdev_vport_priv(vport);
        return netdev_vport->dev->ifindex;
 }
 
-int ovs_netdev_get_mtu(const struct vport *vport)
-{
-       const struct netdev_vport *netdev_vport = netdev_vport_priv(vport);
-       return netdev_vport->dev->mtu;
-}
-
 /* Must be called with rcu_read_lock. */
 static void netdev_port_receive(struct vport *vport, struct sk_buff *skb)
 {
@@ -409,21 +385,25 @@ const struct vport_ops ovs_netdev_vport_ops = {
        .set_addr       = ovs_netdev_set_addr,
        .get_name       = ovs_netdev_get_name,
        .get_addr       = ovs_netdev_get_addr,
-       .get_kobj       = ovs_netdev_get_kobj,
-       .get_dev_flags  = ovs_netdev_get_dev_flags,
-       .is_running     = ovs_netdev_is_running,
-       .get_operstate  = ovs_netdev_get_operstate,
        .get_ifindex    = ovs_netdev_get_ifindex,
-       .get_mtu        = ovs_netdev_get_mtu,
        .send           = netdev_send,
 };
 
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36)
 /*
- * In kernels earlier than 2.6.36, Open vSwitch cannot safely coexist with the
- * Linux bridge module, because there is only a single bridge hook function and
- * only a single br_port member in struct net_device, so this prevents loading
- * both bridge and openvswitch at the same time.
+ * Enforces, mutual exclusion with the Linux bridge module, by declaring and
+ * exporting br_should_route_hook.  Because the bridge module also exports the
+ * same symbol, the module loader will refuse to load both modules at the same
+ * time (e.g. "bridge: exports duplicate symbol br_should_route_hook (owned by
+ * openvswitch)").
+ *
+ * Before Linux 2.6.36, Open vSwitch cannot safely coexist with the Linux
+ * bridge module, so openvswitch uses this macro in those versions.  In
+ * Linux 2.6.36 and later, Open vSwitch can coexist with the bridge module.
+ *
+ * The use of "typeof" here avoids the need to track changes in the type of
+ * br_should_route_hook over various kernel versions.
  */
-BRIDGE_MUTUAL_EXCLUSION;
+typeof(br_should_route_hook) br_should_route_hook;
+EXPORT_SYMBOL(br_should_route_hook);
 #endif
index c9cf5e5..a387b8c 100644 (file)
 #define VPORT_NETDEV_H 1
 
 #include <linux/netdevice.h>
+#include <linux/rcupdate.h>
 
 #include "vport.h"
 
 struct vport *ovs_netdev_get_vport(struct net_device *dev);
 
 struct netdev_vport {
+       struct rcu_head rcu;
+
        struct net_device *dev;
 };
 
@@ -39,11 +42,6 @@ int ovs_netdev_set_addr(struct vport *, const unsigned char *addr);
 const char *ovs_netdev_get_name(const struct vport *);
 const unsigned char *ovs_netdev_get_addr(const struct vport *);
 const char *ovs_netdev_get_config(const struct vport *);
-struct kobject *ovs_netdev_get_kobj(const struct vport *);
-unsigned ovs_netdev_get_dev_flags(const struct vport *);
-int ovs_netdev_is_running(const struct vport *);
-unsigned char ovs_netdev_get_operstate(const struct vport *);
 int ovs_netdev_get_ifindex(const struct vport *);
-int ovs_netdev_get_mtu(const struct vport *);
 
 #endif /* vport_netdev.h */
index d9e8970..501eb7a 100644 (file)
@@ -25,7 +25,6 @@
 #include "compat.h"
 #include "datapath.h"
 #include "vport.h"
-#include "vport-generic.h"
 
 struct patch_config {
        struct rcu_head rcu;
@@ -312,8 +311,5 @@ const struct vport_ops ovs_patch_vport_ops = {
        .get_addr       = patch_get_addr,
        .get_options    = patch_get_options,
        .set_options    = patch_set_options,
-       .get_dev_flags  = ovs_vport_gen_get_dev_flags,
-       .is_running     = ovs_vport_gen_is_running,
-       .get_operstate  = ovs_vport_gen_get_operstate,
        .send           = patch_send,
 };
diff --git a/datapath/vport-vxlan.c b/datapath/vport-vxlan.c
new file mode 100644 (file)
index 0000000..f72b95f
--- /dev/null
@@ -0,0 +1,384 @@
+/*
+ * Copyright (c) 2011 Nicira, Inc.
+ * Copyright (c) 2012 Cisco Systems, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/version.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
+
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/list.h>
+#include <linux/net.h>
+#include <linux/udp.h>
+
+#include <net/icmp.h>
+#include <net/ip.h>
+#include <net/udp.h>
+
+#include "datapath.h"
+#include "tunnel.h"
+#include "vport.h"
+
+#define VXLAN_FLAGS 0x08000000  /* struct vxlanhdr.vx_flags required value. */
+
+/**
+ * struct vxlanhdr - VXLAN header
+ * @vx_flags: Must have the exact value %VXLAN_FLAGS.
+ * @vx_vni: VXLAN Network Identifier (VNI) in top 24 bits, low 8 bits zeroed.
+ */
+struct vxlanhdr {
+       __be32 vx_flags;
+       __be32 vx_vni;
+};
+
+#define VXLAN_HLEN (sizeof(struct udphdr) + sizeof(struct vxlanhdr))
+
+static inline int vxlan_hdr_len(const struct tnl_mutable_config *mutable,
+                               const struct ovs_key_ipv4_tunnel *tun_key)
+{
+       return VXLAN_HLEN;
+}
+
+/**
+ * struct vxlan_port - Keeps track of open UDP ports
+ * @list: list element.
+ * @port: The UDP port number in network byte order.
+ * @socket: The socket created for this port number.
+ * @count: How many ports are using this socket/port.
+ */
+struct vxlan_port {
+       struct list_head list;
+       __be16 port;
+       struct socket *vxlan_rcv_socket;
+       int count;
+};
+
+static LIST_HEAD(vxlan_ports);
+
+static struct vxlan_port *vxlan_port_exists(struct net *net, __be16 port)
+{
+       struct vxlan_port *vxlan_port;
+
+       list_for_each_entry(vxlan_port, &vxlan_ports, list) {
+               if (vxlan_port->port == port &&
+                       net_eq(sock_net(vxlan_port->vxlan_rcv_socket->sk), net))
+                       return vxlan_port;
+       }
+
+       return NULL;
+}
+
+static inline struct vxlanhdr *vxlan_hdr(const struct sk_buff *skb)
+{
+       return (struct vxlanhdr *)(udp_hdr(skb) + 1);
+}
+
+/* Compute source port for outgoing packet.
+ * Currently we use the flow hash.
+ */
+static u16 get_src_port(struct sk_buff *skb)
+{
+       int low;
+       int high;
+       unsigned int range;
+       u32 hash = OVS_CB(skb)->flow->hash;
+
+        inet_get_local_port_range(&low, &high);
+        range = (high - low) + 1;
+       return (((u64) hash * range) >> 32) + low;
+}
+
+static struct sk_buff *vxlan_build_header(const struct vport *vport,
+                                         const struct tnl_mutable_config *mutable,
+                                         struct dst_entry *dst,
+                                         struct sk_buff *skb,
+                                         int tunnel_hlen)
+{
+       struct udphdr *udph = udp_hdr(skb);
+       struct vxlanhdr *vxh = (struct vxlanhdr *)(udph + 1);
+       const struct ovs_key_ipv4_tunnel *tun_key = OVS_CB(skb)->tun_key;
+       __be64 out_key;
+       u32 flags;
+
+       tnl_get_param(mutable, tun_key, &flags, &out_key);
+
+       udph->dest = mutable->dst_port;
+       udph->source = htons(get_src_port(skb));
+       udph->check = 0;
+       udph->len = htons(skb->len - skb_transport_offset(skb));
+
+       vxh->vx_flags = htonl(VXLAN_FLAGS);
+       vxh->vx_vni = htonl(be64_to_cpu(out_key) << 8);
+
+       /*
+        * Allow our local IP stack to fragment the outer packet even if the
+        * DF bit is set as a last resort.  We also need to force selection of
+        * an IP ID here because Linux will otherwise leave it at 0 if the
+        * packet originally had DF set.
+        */
+       skb->local_df = 1;
+       __ip_select_ident(ip_hdr(skb), dst, 0);
+
+       return skb;
+}
+
+/* Called with rcu_read_lock and BH disabled. */
+static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)
+{
+       struct vport *vport;
+       struct vxlanhdr *vxh;
+       const struct tnl_mutable_config *mutable;
+       struct iphdr *iph;
+       struct ovs_key_ipv4_tunnel tun_key;
+       __be64 key;
+       u32 tunnel_flags = 0;
+
+       if (unlikely(!pskb_may_pull(skb, VXLAN_HLEN + ETH_HLEN)))
+               goto error;
+
+       vxh = vxlan_hdr(skb);
+       if (unlikely(vxh->vx_flags != htonl(VXLAN_FLAGS) ||
+                    vxh->vx_vni & htonl(0xff)))
+               goto error;
+
+       __skb_pull(skb, VXLAN_HLEN);
+       skb_postpull_rcsum(skb, skb_transport_header(skb), VXLAN_HLEN + ETH_HLEN);
+
+       key = cpu_to_be64(ntohl(vxh->vx_vni) >> 8);
+
+       iph = ip_hdr(skb);
+       vport = ovs_tnl_find_port(dev_net(skb->dev), iph->daddr, iph->saddr,
+               key, TNL_T_PROTO_VXLAN, &mutable);
+       if (unlikely(!vport)) {
+               icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
+               goto error;
+       }
+
+       if (mutable->flags & TNL_F_IN_KEY_MATCH || !mutable->key.daddr)
+               tunnel_flags = OVS_TNL_F_KEY;
+       else
+               key = 0;
+
+       /* Save outer tunnel values */
+       tnl_tun_key_init(&tun_key, iph, key, tunnel_flags);
+       OVS_CB(skb)->tun_key = &tun_key;
+
+       ovs_tnl_rcv(vport, skb);
+       goto out;
+
+error:
+       kfree_skb(skb);
+out:
+       return 0;
+}
+
+/* Random value.  Irrelevant as long as it's not 0 since we set the handler. */
+#define UDP_ENCAP_VXLAN 1
+static int vxlan_socket_init(struct vxlan_port *vxlan_port, struct net *net)
+{
+       int err;
+       struct sockaddr_in sin;
+
+       err = sock_create_kern(AF_INET, SOCK_DGRAM, 0,
+                              &vxlan_port->vxlan_rcv_socket);
+       if (err)
+               goto error;
+
+       /* release net ref. */
+       sk_change_net(vxlan_port->vxlan_rcv_socket->sk, net);
+
+       sin.sin_family = AF_INET;
+       sin.sin_addr.s_addr = htonl(INADDR_ANY);
+       sin.sin_port = vxlan_port->port;
+
+       err = kernel_bind(vxlan_port->vxlan_rcv_socket, (struct sockaddr *)&sin,
+                         sizeof(struct sockaddr_in));
+       if (err)
+               goto error_sock;
+
+       udp_sk(vxlan_port->vxlan_rcv_socket->sk)->encap_type = UDP_ENCAP_VXLAN;
+       udp_sk(vxlan_port->vxlan_rcv_socket->sk)->encap_rcv = vxlan_rcv;
+
+       udp_encap_enable();
+
+       return 0;
+
+error_sock:
+       sk_release_kernel(vxlan_port->vxlan_rcv_socket->sk);
+error:
+       pr_warn("cannot register vxlan protocol handler\n");
+       return err;
+}
+
+static void vxlan_tunnel_release(struct vxlan_port *vxlan_port)
+{
+       vxlan_port->count--;
+
+       if (vxlan_port->count == 0) {
+               /* Release old socket */
+               sk_release_kernel(vxlan_port->vxlan_rcv_socket->sk);
+               list_del(&vxlan_port->list);
+               kfree(vxlan_port);
+       }
+}
+static int vxlan_tunnel_setup(struct net *net, struct nlattr *options,
+                             struct vxlan_port **vxport)
+{
+       struct nlattr *a;
+       int err;
+       u16 dst_port;
+       struct vxlan_port *vxlan_port = NULL;
+
+       *vxport = NULL;
+
+       if (!options) {
+               err = -EINVAL;
+               goto out;
+       }
+
+       a = nla_find_nested(options, OVS_TUNNEL_ATTR_DST_PORT);
+       if (a && nla_len(a) == sizeof(u16)) {
+               dst_port = nla_get_u16(a);
+       } else {
+               /* Require destination port from userspace. */
+               err = -EINVAL;
+               goto out;
+       }
+
+       /* Verify if we already have a socket created for this port */
+       vxlan_port = vxlan_port_exists(net, htons(dst_port));
+       if (vxlan_port) {
+               vxlan_port->count++;
+               err = 0;
+               *vxport = vxlan_port;
+               goto out;
+       }
+
+       /* Add a new socket for this port */
+       vxlan_port = kzalloc(sizeof(struct vxlan_port), GFP_KERNEL);
+       if (!vxlan_port) {
+               err = -ENOMEM;
+               goto out;
+       }
+
+       vxlan_port->port = htons(dst_port);
+       vxlan_port->count = 1;
+       list_add_tail(&vxlan_port->list, &vxlan_ports);
+
+       err = vxlan_socket_init(vxlan_port, net);
+       if (err)
+               goto error;
+
+       *vxport = vxlan_port;
+       goto out;
+
+error:
+       list_del(&vxlan_port->list);
+       kfree(vxlan_port);
+out:
+       return err;
+}
+
+static int vxlan_set_options(struct vport *vport, struct nlattr *options)
+{
+       int err;
+       struct net *net = ovs_dp_get_net(vport->dp);
+       struct tnl_vport *tnl_vport = tnl_vport_priv(vport);
+       struct tnl_mutable_config *config;
+       struct vxlan_port *old_port = NULL;
+       struct vxlan_port *vxlan_port = NULL;
+
+       config = rtnl_dereference(tnl_vport->mutable);
+
+       old_port = vxlan_port_exists(net, config->dst_port);
+
+       err = vxlan_tunnel_setup(net, options, &vxlan_port);
+       if (err)
+               goto out;
+
+       err = ovs_tnl_set_options(vport, options);
+
+       if (err)
+               vxlan_tunnel_release(vxlan_port);
+       else {
+               /* Release old socket */
+               vxlan_tunnel_release(old_port);
+       }
+out:
+       return err;
+}
+
+static const struct tnl_ops ovs_vxlan_tnl_ops = {
+       .tunnel_type    = TNL_T_PROTO_VXLAN,
+       .ipproto        = IPPROTO_UDP,
+       .hdr_len        = vxlan_hdr_len,
+       .build_header   = vxlan_build_header,
+};
+
+static void vxlan_tnl_destroy(struct vport *vport)
+{
+       struct vxlan_port *vxlan_port;
+       struct tnl_vport *tnl_vport = tnl_vport_priv(vport);
+       struct tnl_mutable_config *config;
+
+       config = rtnl_dereference(tnl_vport->mutable);
+
+       vxlan_port = vxlan_port_exists(ovs_dp_get_net(vport->dp),
+                                        config->dst_port);
+
+       vxlan_tunnel_release(vxlan_port);
+
+       ovs_tnl_destroy(vport);
+}
+
+static struct vport *vxlan_tnl_create(const struct vport_parms *parms)
+{
+       int err;
+       struct vport *vport;
+       struct vxlan_port *vxlan_port = NULL;
+
+       err = vxlan_tunnel_setup(ovs_dp_get_net(parms->dp), parms->options,
+                                &vxlan_port);
+       if (err)
+               return ERR_PTR(err);
+
+       vport = ovs_tnl_create(parms, &ovs_vxlan_vport_ops, &ovs_vxlan_tnl_ops);
+
+       if (IS_ERR(vport))
+               vxlan_tunnel_release(vxlan_port);
+
+       return vport;
+}
+
+const struct vport_ops ovs_vxlan_vport_ops = {
+       .type           = OVS_VPORT_TYPE_VXLAN,
+       .flags          = VPORT_F_TUN_ID,
+       .create         = vxlan_tnl_create,
+       .destroy        = vxlan_tnl_destroy,
+       .set_addr       = ovs_tnl_set_addr,
+       .get_name       = ovs_tnl_get_name,
+       .get_addr       = ovs_tnl_get_addr,
+       .get_options    = ovs_tnl_get_options,
+       .set_options    = vxlan_set_options,
+       .send           = ovs_tnl_send,
+};
+#else
+#warning VXLAN tunneling will not be available on kernels before 2.6.26
+#endif /* Linux kernel < 2.6.26 */
index d9c8cfd..a78ebfa 100644 (file)
@@ -41,9 +41,11 @@ static const struct vport_ops *base_vport_ops_list[] = {
        &ovs_internal_vport_ops,
        &ovs_patch_vport_ops,
        &ovs_gre_vport_ops,
+       &ovs_gre_ft_vport_ops,
        &ovs_gre64_vport_ops,
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
        &ovs_capwap_vport_ops,
+       &ovs_vxlan_vport_ops,
 #endif
 };
 
@@ -149,19 +151,6 @@ struct vport *ovs_vport_locate(struct net *net, const char *name)
        return NULL;
 }
 
-static void release_vport(struct kobject *kobj)
-{
-       struct vport *p = container_of(kobj, struct vport, kobj);
-       kfree(p);
-}
-
-static struct kobj_type brport_ktype = {
-#ifdef CONFIG_SYSFS
-       .sysfs_ops = &ovs_brport_sysfs_ops,
-#endif
-       .release = release_vport
-};
-
 /**
  *     ovs_vport_alloc - allocate and initialize new vport
  *
@@ -191,15 +180,10 @@ struct vport *ovs_vport_alloc(int priv_size, const struct vport_ops *ops,
 
        vport->dp = parms->dp;
        vport->port_no = parms->port_no;
-       vport->upcall_pid = parms->upcall_pid;
+       vport->upcall_portid = parms->upcall_portid;
        vport->ops = ops;
        INIT_HLIST_NODE(&vport->dp_hash_node);
 
-       /* Initialize kobject for bridge.  This will be added as
-        * /sys/class/net/<devname>/brport later, if sysfs is enabled. */
-       vport->kobj.kset = NULL;
-       kobject_init(&vport->kobj, &brport_ktype);
-
        vport->percpu_stats = alloc_percpu(struct vport_percpu_stats);
        if (!vport->percpu_stats) {
                kfree(vport);
@@ -224,8 +208,7 @@ struct vport *ovs_vport_alloc(int priv_size, const struct vport_ops *ops,
 void ovs_vport_free(struct vport *vport)
 {
        free_percpu(vport->percpu_stats);
-
-       kobject_put(&vport->kobj);
+       kfree(vport);
 }
 
 /**
@@ -452,8 +435,7 @@ void ovs_vport_receive(struct vport *vport, struct sk_buff *skb)
 {
        struct vport_percpu_stats *stats;
 
-       stats = per_cpu_ptr(vport->percpu_stats, smp_processor_id());
-
+       stats = this_cpu_ptr(vport->percpu_stats);
        u64_stats_update_begin(&stats->sync);
        stats->rx_packets++;
        stats->rx_bytes += skb->len;
@@ -484,7 +466,7 @@ int ovs_vport_send(struct vport *vport, struct sk_buff *skb)
        if (likely(sent)) {
                struct vport_percpu_stats *stats;
 
-               stats = per_cpu_ptr(vport->percpu_stats, smp_processor_id());
+               stats = this_cpu_ptr(vport->percpu_stats);
 
                u64_stats_update_begin(&stats->sync);
                stats->tx_packets++;
index 61d5274..91f8836 100644 (file)
@@ -74,14 +74,13 @@ struct vport_err_stats {
 /**
  * struct vport - one port within a datapath
  * @rcu: RCU callback head for deferred destruction.
- * @port_no: Index into @dp's @ports array.
  * @dp: Datapath to which this port belongs.
- * @kobj: Represents /sys/class/net/<devname>/brport.
  * @linkname: The name of the link from /sys/class/net/<datapath>/brif to this
  * &struct vport.  (We keep this around so that we can delete it if the
  * device gets renamed.)  Set to the null string when no link exists.
- * @upcall_pid: The Netlink port to use for packets received on this port that
+ * @upcall_portid: The Netlink port to use for packets received on this port that
  * miss the flow table.
+ * @port_no: Index into @dp's @ports array.
  * @hash_node: Element in @dev_table hash table in vport.c.
  * @dp_hash_node: Element in @datapath->ports hash table in datapath.c.
  * @ops: Class structure.
@@ -93,11 +92,10 @@ struct vport_err_stats {
  */
 struct vport {
        struct rcu_head rcu;
-       u16 port_no;
        struct datapath *dp;
-       struct kobject kobj;
        char linkname[IFNAMSIZ];
-       u32 upcall_pid;
+       u32 upcall_portid;
+       u16 port_no;
 
        struct hlist_node hash_node;
        struct hlist_node dp_hash_node;
@@ -132,7 +130,7 @@ struct vport_parms {
        /* For ovs_vport_alloc(). */
        struct datapath *dp;
        u16 port_no;
-       u32 upcall_pid;
+       u32 upcall_portid;
 };
 
 /**
@@ -158,15 +156,8 @@ struct vport_parms {
  * @get_name: Get the device's name.
  * @get_addr: Get the device's MAC address.
  * @get_config: Get the device's configuration.
- * @get_kobj: Get the kobj associated with the device (may return null).
- * @get_dev_flags: Get the device's flags.
- * @is_running: Checks whether the device is running.
- * @get_operstate: Get the device's operating state.
  * @get_ifindex: Get the system interface index associated with the device.
  * May be null if the device does not have an ifindex.
- * @get_mtu: Get the device's MTU.  May be %NULL if the device does not have an
- * MTU (as e.g. some tunnels do not).  Must be implemented if @get_ifindex is
- * implemented.
  * @send: Send a packet on the device.  Returns the length of the packet sent.
  */
 struct vport_ops {
@@ -190,16 +181,7 @@ struct vport_ops {
        const char *(*get_name)(const struct vport *);
        const unsigned char *(*get_addr)(const struct vport *);
        void (*get_config)(const struct vport *, void *);
-       struct kobject *(*get_kobj)(const struct vport *);
-
-       unsigned (*get_dev_flags)(const struct vport *);
-       int (*is_running)(const struct vport *);
-       unsigned char (*get_operstate)(const struct vport *);
-
        int (*get_ifindex)(const struct vport *);
-
-       int (*get_mtu)(const struct vport *);
-
        int (*send)(struct vport *, struct sk_buff *);
 };
 
@@ -254,7 +236,9 @@ extern const struct vport_ops ovs_netdev_vport_ops;
 extern const struct vport_ops ovs_internal_vport_ops;
 extern const struct vport_ops ovs_patch_vport_ops;
 extern const struct vport_ops ovs_gre_vport_ops;
+extern const struct vport_ops ovs_gre_ft_vport_ops;
 extern const struct vport_ops ovs_gre64_vport_ops;
 extern const struct vport_ops ovs_capwap_vport_ops;
+extern const struct vport_ops ovs_vxlan_vport_ops;
 
 #endif /* vport.h */
index 3e1373a..1c5e09f 100644 (file)
@@ -6,7 +6,6 @@
 /files
 /nicira-switch
 /openvswitch
-/openvswitch-brcompat
 /openvswitch-common
 /openvswitch-common.copyright
 /openvswitch-controller
index b6cb12e..35c5a9e 100644 (file)
@@ -7,10 +7,6 @@ EXTRA_DIST += \
        debian/copyright.in \
        debian/dkms.conf.in \
        debian/dirs \
-       debian/openvswitch-brcompat.install \
-       debian/openvswitch-brcompat.manpages \
-       debian/openvswitch-brcompat.postinst \
-       debian/openvswitch-brcompat.postrm \
        debian/openvswitch-common.dirs \
        debian/openvswitch-common.docs \
        debian/openvswitch-common.install \
index 9ac3d5d..41c3b94 100644 (file)
@@ -1,7 +1,7 @@
 openvswitch (1.9.90-1) unstable; urgency=low
    [ Open vSwitch team ]
    * New upstream version
-     - Nothing yet!  Try NEWS...
+    - Nothing yet!  Try NEWS...
 
  -- Open vSwitch team <dev@openvswitch.org>  Wed, 24 Oct 2012 16:12:57 -0700
 
@@ -11,6 +11,10 @@ openvswitch (1.9.0-1) unstable; urgency=low
     - The tunneling code no longer assumes input and output keys are symmetric.
       If they are not, PMTUD needs to be disabled for tunneling to work. Note
       this only applies to flow-based keys.
+    - Datapath:
+      - Support for ipv6 set action.
+      - SKB mark matching and setting.
+      - support for Linux kernels up to 3.7
     - FreeBSD is now a supported platform, thanks to code contributions from
       Gaetano Catalli, Ed Maste, and Giuseppe Lettieri.
     - ovs-bugtool: New --ovs option to report only OVS related information.
@@ -36,16 +40,24 @@ openvswitch (1.9.0-1) unstable; urgency=low
       are true, but because we do not know of any users for this
       feature it seems better on balance to remove it.  (The ovs-pki-cgi
       program was not included in distribution packaging.)
+    - Tunnel Path MTU Discovery default value was set to 'disabled'.  This
+      feature is deprecated and will be removed soon.
     - ovsdb-server now enforces the immutability of immutable columns.  This
       was not enforced in earlier versions due to an oversight.
     - New support for a nonstandard form of GRE that supports a 64-bit key.
+    - Tunnel header caching removed.
     - The following features are now deprecated.  They will be removed no
       earlier than February 2013.  Please email dev@openvswitch.org with
       concerns.
+        - Bridge compatibility.
         - Stable bond mode.
         - The autopath action.
         - Interface type "null".
         - Numeric values for reserved ports (see "ovs-ofctl" note above).
+        - Tunnel Path MTU Discovery.
+        - CAPWAP tunnel support.
+    - The data in the RARP packets can now be matched in the same way as the
+      data in ARP packets.
 
  -- Open vSwitch team <dev@openvswitch.org>  Wed, 24 Oct 2012 16:10:39 -0700
 
index 15ca3eb..ae88be2 100644 (file)
@@ -135,28 +135,6 @@ Description: Open vSwitch controller implementation
  The Open vSwitch controller enables OpenFlow switches that connect to it
  to act as MAC-learning Ethernet switches.
 
-Package: openvswitch-brcompat
-Architecture: linux-any
-Depends:
- ${shlibs:Depends}, openvswitch-switch (= ${binary:Version}), ${misc:Depends}
-Recommends: bridge-utils
-Description: Open vSwitch bridge compatibility support
- Open vSwitch is a production quality, multilayer, software-based, Ethernet
- virtual switch. It is designed to enable massive network automation through
- programmatic extension, while still supporting standard management interfaces
- and protocols (e.g. NetFlow, sFlow, SPAN, RSPAN, CLI, LACP, 802.1ag). In
- addition, it is designed to support distribution across multiple physical
- servers similar to VMware's vNetwork distributed vswitch or Cisco's Nexus
- 1000V.
- .
- openvswitch-brcompat provides a way for applications that use the
- Linux bridge to gradually migrate to Open vSwitch.  Programs that
- ordinarily control the Linux bridge module, such as "brctl", instead
- control the Open vSwitch kernel-based switch.
- .
- Once this package is installed, adding BRCOMPAT=yes in
- /etc/default/openvswitch-switch enables bridge compatibility.
-
 Package: openvswitch-dbg
 Section: debug
 Architecture: linux-any
index 16e5559..7d5b7b7 100644 (file)
@@ -76,7 +76,6 @@ License:
   GNU General Public License version 2 and the Apache License Version 2.0.
 
        include/linux/openvswitch.h
-       include/openvswitch/brcompat-netlink.h
        include/openvswitch/datapath-compat.h
        include/openvswitch/tunnel.h
 
index 0d3db76..a477761 100644 (file)
@@ -2,9 +2,6 @@ PACKAGE_NAME="openvswitch"
 PACKAGE_VERSION="__VERSION__"
 MAKE="./configure --with-linux='${kernel_source_dir}' && make -C datapath/linux"
 BUILT_MODULE_NAME[0]=openvswitch
-BUILT_MODULE_NAME[1]=brcompat
 BUILT_MODULE_LOCATION[0]=datapath/linux/
-BUILT_MODULE_LOCATION[1]=datapath/linux/
 DEST_MODULE_LOCATION[0]=/kernel/drivers/net/openvswitch/
-DEST_MODULE_LOCATION[1]=/kernel/drivers/net/openvswitch/
 AUTOINSTALL=yes
diff --git a/debian/openvswitch-brcompat.install b/debian/openvswitch-brcompat.install
deleted file mode 100644 (file)
index fad09f1..0000000
+++ /dev/null
@@ -1 +0,0 @@
-_debian/vswitchd/ovs-brcompatd usr/sbin
diff --git a/debian/openvswitch-brcompat.manpages b/debian/openvswitch-brcompat.manpages
deleted file mode 100644 (file)
index 2fc0180..0000000
+++ /dev/null
@@ -1 +0,0 @@
-_debian/vswitchd/ovs-brcompatd.8
diff --git a/debian/openvswitch-brcompat.postinst b/debian/openvswitch-brcompat.postinst
deleted file mode 100755 (executable)
index 12e3b05..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/sh
-
-set -e
-
-# If openvswitch-switch is installed, and then later openvswitch-brcompat is
-# installed, make sure that ovs-brcompatd starts.
-if test X"$1" = Xconfigure && \
-   test -x /etc/init.d/openvswitch-switch && \
-   test -e /var/run/openvswitch/ovs-vswitchd.pid; then
-    invoke-rc.d openvswitch-switch start || exit $?
-fi
-
-#DEBHELPER#
-
-exit 0
-
-
diff --git a/debian/openvswitch-brcompat.postrm b/debian/openvswitch-brcompat.postrm
deleted file mode 100755 (executable)
index e21b002..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/bin/sh
-# postrm script for openvswitch-brcompat
-#
-# see: dh_installdeb(1)
-
-set -e
-
-# summary of how this script can be called:
-#        * <postrm> `remove'
-#        * <postrm> `purge'
-#        * <old-postrm> `upgrade' <new-version>
-#        * <new-postrm> `failed-upgrade' <old-version>
-#        * <new-postrm> `abort-install'
-#        * <new-postrm> `abort-install' <old-version>
-#        * <new-postrm> `abort-upgrade' <old-version>
-#        * <disappearer's-postrm> `disappear' <overwriter>
-#          <overwriter-version>
-# for details, see http://www.debian.org/doc/debian-policy/ or
-# the debian-policy package
-
-
-case "$1" in
-    purge)
-        rm -f /var/log/openvswitch/ovs-brcompatd.log* || true
-        ;;
-
-    remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
-        ;;
-
-    *)
-        echo "postrm called with unknown argument \`$1'" >&2
-        exit 1
-        ;;
-esac
-
-# dh_installdeb will replace this with shell code automatically
-# generated by other debhelper scripts.
-
-#DEBHELPER#
-
-exit 0
-
-
index 05e93a8..5969677 100644 (file)
@@ -4,6 +4,6 @@ _debian/utilities/ovs-appctl.8
 _debian/utilities/ovs-benchmark.1
 _debian/utilities/ovs-ofctl.8
 _debian/utilities/ovs-pki.8
-utilities/bugtool/ovs-bugtool.8
+_debian/utilities/bugtool/ovs-bugtool.8
 utilities/ovs-parse-backtrace.8
 utilities/ovs-parse-leaks.8
index 1d9f926..a274f7e 100644 (file)
@@ -8,7 +8,7 @@
 #   (default: 6633).  The private key, certificate, and CA certificate
 #   must be specified below.
 #
-# * "pctp:[PORT]": Listen for TCP connections on the specified PORT
+# * "ptcp:[PORT]": Listen for TCP connections on the specified PORT
 #   (default: 6633).  Not recommended for security reasons.
 #
 LISTEN="pssl:"
index 301bc73..3df711f 100755 (executable)
 . /usr/share/openvswitch/scripts/ovs-lib
 test -e /etc/default/openvswitch-switch && . /etc/default/openvswitch-switch
 
-if test X"$BRCOMPAT" = Xyes && test ! -x /usr/sbin/ovs-brcompatd; then
-    BRCOMPAT=no
-    log_warning_msg "ovs-brcompatd missing, disabling bridge compatibility"
-fi
+network_interfaces () {
+    INTERFACES="/etc/network/interfaces"
+    [ -e "${INTERFACES}" ] || return
+    bridges=`awk '{ if ($1 == "allow-ovs") { print $2; } }' "${INTERFACES}"`
+    [ -n "${bridges}" ] && $1 --allow=ovs ${bridges}
+}
 
 ovs_ctl () {
     set /usr/share/openvswitch/scripts/ovs-ctl "$@"
-    if test X"$BRCOMPAT" = Xyes; then
-        set "$@" --brcompat
-    fi
     "$@"
 }
 
@@ -72,9 +71,11 @@ start () {
     fi
     set "$@" $OVS_CTL_OPTS
     "$@" || exit $?
+    [ "$2" = "start" ] && network_interfaces ifup
 }
 
 stop () {
+    network_interfaces ifdown
     ovs_ctl stop
 }
 
index afa5dd3..d7c7796 100644 (file)
@@ -3,10 +3,6 @@
 # FORCE_COREFILES: If 'yes' then core files will be enabled.
 # FORCE_COREFILES=yes
 
-# BRCOMPAT: If 'yes' and the openvswitch-brcompat package is installed, then
-# Linux bridge compatibility will be enabled.
-# BRCOMPAT=no
-
 # OVS_CTL_OPTS: Extra options to pass to ovs-ctl.  This is, for example,
 # a suitable place to specify --ovs-vswitchd-wrapper=valgrind.
 # OVS_CTL_OPTS=
index 89feb61..5e32965 100644 (file)
@@ -115,7 +115,7 @@ struct ovs_vport_stats {
 };
 
 /* Fixed logical ports. */
-#define OVSP_LOCAL      ((__u16)0)
+#define OVSP_LOCAL      ((__u32)0)
 
 /* Packet transfer. */
 
@@ -182,6 +182,8 @@ enum ovs_vport_type {
        OVS_VPORT_TYPE_UNSPEC,
        OVS_VPORT_TYPE_NETDEV,   /* network device */
        OVS_VPORT_TYPE_INTERNAL, /* network device implemented by datapath */
+       OVS_VPORT_TYPE_FT_GRE,   /* Flow based GRE tunnel. */
+       OVS_VPORT_TYPE_VXLAN,    /* VXLAN tunnel */
        OVS_VPORT_TYPE_PATCH = 100, /* virtual tunnel connecting two vports */
        OVS_VPORT_TYPE_GRE,      /* GRE tunnel */
        OVS_VPORT_TYPE_CAPWAP,   /* CAPWAP tunnel */
@@ -279,7 +281,8 @@ enum ovs_key_attr {
        OVS_KEY_ATTR_ICMPV6,    /* struct ovs_key_icmpv6 */
        OVS_KEY_ATTR_ARP,       /* struct ovs_key_arp */
        OVS_KEY_ATTR_ND,        /* struct ovs_key_nd */
-       OVS_KEY_ATTR_IPV4_TUNNEL = 62,  /* struct ovs_key_ipv4_tunnel */
+       OVS_KEY_ATTR_SKB_MARK,  /* u32 skb mark */
+       OVS_KEY_ATTR_IPV4_TUNNEL,  /* struct ovs_key_ipv4_tunnel */
        OVS_KEY_ATTR_TUN_ID = 63,  /* be64 tunnel ID */
        __OVS_KEY_ATTR_MAX
 };
@@ -363,9 +366,9 @@ struct ovs_key_nd {
 };
 
 /* Values for ovs_key_ipv4_tunnel->tun_flags */
-#define OVS_FLOW_TNL_F_DONT_FRAGMENT (1 << 0)
-#define OVS_FLOW_TNL_F_CSUM (1 << 1)
-#define OVS_FLOW_TNL_F_KEY (1 << 2)
+#define OVS_TNL_F_DONT_FRAGMENT (1 << 0)
+#define OVS_TNL_F_CSUM (1 << 1)
+#define OVS_TNL_F_KEY (1 << 2)
 
 struct ovs_key_ipv4_tunnel {
        __be64 tun_id;
index 38e8eef..76a8f06 100644 (file)
@@ -3,6 +3,7 @@ noinst_HEADERS += \
        include/openflow/openflow-1.0.h \
        include/openflow/openflow-1.1.h \
        include/openflow/openflow-1.2.h \
+       include/openflow/openflow-1.3.h \
        include/openflow/openflow-common.h \
        include/openflow/openflow.h
 
@@ -14,23 +15,35 @@ SUFFIXES += .h .hstamp
        touch $@
 
 HSTAMP_FILES = \
-       include/openflow/nicira.hstamp \
+       include/openflow/nicira-ext.hstamp \
        include/openflow/openflow-1.0.hstamp \
        include/openflow/openflow-1.1.hstamp \
        include/openflow/openflow-1.2.hstamp \
+       include/openflow/openflow-1.3.hstamp \
+       include/openflow/openflow-common.hstamp \
        include/openflow/openflow.hstamp
 CLEANFILES += $(HSTAMP_FILES)
 ALL_LOCAL += $(HSTAMP_FILES)
 $(HSTAMP_FILES): build-aux/check-structs
 
-include/openflow/openflow-1.0.hstamp: include/openflow/openflow-common.h
-include/openflow/openflow-1.1.hstamp: include/openflow/openflow-common.h
+include/openflow/openflow-1.0.hstamp: \
+       include/openflow/openflow-common.h
+include/openflow/openflow-1.1.hstamp: \
+       include/openflow/openflow-common.h
+include/openflow/openflow-1.2.hstamp: \
+       include/openflow/openflow-common.h \
+       include/openflow/openflow-1.1.h
+include/openflow/openflow-1.3.hstamp: \
+       include/openflow/openflow-common.h \
+       include/openflow/openflow-1.1.h \
+       include/openflow/openflow-1.2.h
 include/openflow/nicira-ext.hstamp: \
+       include/openflow/openflow.h \
+       include/openflow/openflow-common.h \
        include/openflow/openflow-1.0.h \
        include/openflow/openflow-1.1.h \
        include/openflow/openflow-1.2.h \
-       include/openflow/openflow-common.h \
-       include/openflow/openflow.h
+       include/openflow/openflow-1.3.h
 endif
 
 EXTRA_DIST += build-aux/check-structs
index e9790fd..91c96b3 100644 (file)
@@ -1208,9 +1208,10 @@ OFP_ASSERT(sizeof(struct nx_action_output_reg) == 24);
  *     value, called "nxm_mask".  For each 1-bit in position J in nxm_mask, the
  *     nx_match matches only packets for which bit J in the given field's value
  *     matches bit J in nxm_value.  A 0-bit in nxm_mask causes the
- *     corresponding bits in nxm_value and the field's value to be ignored.
- *     (The sense of the nxm_mask bits is the opposite of that used by the
- *     "wildcards" member of struct ofp10_match.)
+ *     corresponding bit in nxm_value is ignored (it should be 0; Open vSwitch
+ *     may enforce this someday), as is the corresponding bit in the field's
+ *     value.  (The sense of the nxm_mask bits is the opposite of that used by
+ *     the "wildcards" member of struct ofp10_match.)
  *
  *     When nxm_hasmask is 1, nxm_length is always even.
  *
@@ -1516,7 +1517,7 @@ OFP_ASSERT(sizeof(struct nx_action_output_reg) == 24);
  * otherwise.  Only ARP opcodes between 1 and 255 should be specified for
  * matching.
  *
- * Prereqs: NXM_OF_ETH_TYPE must match 0x0806 exactly.
+ * Prereqs: NXM_OF_ETH_TYPE must match either 0x0806 or 0x8035.
  *
  * Format: 16-bit integer in network byte order.
  *
@@ -1526,7 +1527,7 @@ OFP_ASSERT(sizeof(struct nx_action_output_reg) == 24);
 /* For an Ethernet+IP ARP packet, the source or target protocol address
  * in the ARP header.  Always 0 otherwise.
  *
- * Prereqs: NXM_OF_ETH_TYPE must match 0x0806 exactly.
+ * Prereqs: NXM_OF_ETH_TYPE must match either 0x0806 or 0x8035.
  *
  * Format: 32-bit integer in network byte order.
  *
@@ -1578,9 +1579,11 @@ OFP_ASSERT(sizeof(struct nx_action_output_reg) == 24);
 
 /* Tunnel ID.
  *
- * For a packet received via GRE tunnel including a (32-bit) key, the key is
- * stored in the low 32-bits and the high bits are zeroed.  For other packets,
- * the value is 0.
+ * For a packet received via a GRE or VXLAN tunnel including a (32-bit) key, the
+ * key is stored in the low 32-bits and the high bits are zeroed.  For other
+ * packets, the value is 0.
+ *
+ * All zero bits, for packets not received via a keyed tunnel.
  *
  * Prereqs: None.
  *
@@ -1593,7 +1596,7 @@ OFP_ASSERT(sizeof(struct nx_action_output_reg) == 24);
 /* For an Ethernet+IP ARP packet, the source or target hardware address
  * in the ARP header.  Always 0 otherwise.
  *
- * Prereqs: NXM_OF_ETH_TYPE must match 0x0806 exactly.
+ * Prereqs: NXM_OF_ETH_TYPE must match either 0x0806 or 0x8035.
  *
  * Format: 48-bit Ethernet MAC address.
  *
@@ -1767,8 +1770,7 @@ OFP_ASSERT(sizeof(struct nx_action_output_reg) == 24);
 
 enum nx_flow_format {
     NXFF_OPENFLOW10 = 0,         /* Standard OpenFlow 1.0 compatible. */
-    NXFF_NXM = 2,                /* Nicira extended match. */
-    NXFF_OPENFLOW12 = 3          /* OpenFlow 1.2 format. */
+    NXFF_NXM = 2                 /* Nicira extended match. */
 };
 
 /* NXT_SET_FLOW_FORMAT request. */
index 9af7740..c30fa92 100644 (file)
@@ -51,29 +51,6 @@ enum ofp_port {
     OFPP_NONE       = 0xffff   /* Not associated with a physical port. */
 };
 
-#define OFP_DEFAULT_MISS_SEND_LEN   128
-
-enum ofp_config_flags {
-    /* Handling of IP fragments. */
-    OFPC_FRAG_NORMAL   = 0,  /* No special handling for fragments. */
-    OFPC_FRAG_DROP     = 1,  /* Drop fragments. */
-    OFPC_FRAG_REASM    = 2,  /* Reassemble (only if OFPC_IP_REASM set). */
-    OFPC_FRAG_NX_MATCH = 3,  /* Make first fragments available for matching. */
-    OFPC_FRAG_MASK     = 3,
-
-    /* TTL processing - applicable for IP and MPLS packets. */
-    OFPC_INVALID_TTL_TO_CONTROLLER = 1 << 2, /* Send packets with invalid TTL
-                                                to the controller. */
-};
-
-/* Switch configuration. */
-struct ofp_switch_config {
-    ovs_be16 flags;             /* OFPC_* flags. */
-    ovs_be16 miss_send_len;     /* Max bytes of new flow that datapath should
-                                   send to the controller. */
-};
-OFP_ASSERT(sizeof(struct ofp_switch_config) == 4);
-
 /* OpenFlow 1.0 specific capabilities supported by the datapath (struct
  * ofp_switch_features, member capabilities). */
 enum ofp10_capabilities {
@@ -175,7 +152,7 @@ struct ofp10_queue_get_config_reply {
 OFP_ASSERT(sizeof(struct ofp10_queue_get_config_reply) == 8);
 
 /* Packet received on port (datapath -> controller). */
-struct ofp_packet_in {
+struct ofp10_packet_in {
     ovs_be32 buffer_id;     /* ID assigned by datapath. */
     ovs_be16 total_len;     /* Full length of frame. */
     ovs_be16 in_port;       /* Port on which frame was received. */
@@ -188,7 +165,7 @@ struct ofp_packet_in {
                                offsetof(struct ofp_packet_in, data) ==
                                sizeof(struct ofp_packet_in) - 2. */
 };
-OFP_ASSERT(sizeof(struct ofp_packet_in) == 12);
+OFP_ASSERT(sizeof(struct ofp10_packet_in) == 12);
 
 enum ofp10_action_type {
     OFPAT10_OUTPUT,             /* Output to switch port. */
@@ -218,30 +195,8 @@ struct ofp10_action_output {
 };
 OFP_ASSERT(sizeof(struct ofp10_action_output) == 8);
 
-/* Action header for OFPAT10_VENDOR. The rest of the body is vendor-defined. */
-struct ofp_action_vendor_header {
-    ovs_be16 type;                  /* OFPAT10_VENDOR. */
-    ovs_be16 len;                   /* Length is a multiple of 8. */
-    ovs_be32 vendor;                /* Vendor ID, which takes the same form
-                                       as in "struct ofp_vendor_header". */
-};
-OFP_ASSERT(sizeof(struct ofp_action_vendor_header) == 8);
-
-/* Action header that is common to all actions.  The length includes the
- * header and any padding used to make the action 64-bit aligned.
- * NB: The length of an action *must* always be a multiple of eight. */
-struct ofp_action_header {
-    ovs_be16 type;                  /* One of OFPAT10_*. */
-    ovs_be16 len;                   /* Length of action, including this
-                                       header.  This is the length of action,
-                                       including any padding to make it
-                                       64-bit aligned. */
-    uint8_t pad[4];
-};
-OFP_ASSERT(sizeof(struct ofp_action_header) == 8);
-
 /* OFPAT10_ENQUEUE action struct: send packets to given queue on port. */
-struct ofp_action_enqueue {
+struct ofp10_action_enqueue {
     ovs_be16 type;            /* OFPAT10_ENQUEUE. */
     ovs_be16 len;             /* Len is 16. */
     ovs_be16 port;            /* Port that queue belongs. Should
@@ -250,7 +205,7 @@ struct ofp_action_enqueue {
     uint8_t pad[6];           /* Pad for 64-bit alignment. */
     ovs_be32 queue_id;        /* Where to enqueue the packets. */
 };
-OFP_ASSERT(sizeof(struct ofp_action_enqueue) == 16);
+OFP_ASSERT(sizeof(struct ofp10_action_enqueue) == 16);
 
 union ofp_action {
     ovs_be16 type;
@@ -266,7 +221,7 @@ union ofp_action {
 OFP_ASSERT(sizeof(union ofp_action) == 8);
 
 /* Send packet (controller -> datapath). */
-struct ofp_packet_out {
+struct ofp10_packet_out {
     ovs_be32 buffer_id;           /* ID assigned by datapath or UINT32_MAX. */
     ovs_be16 in_port;             /* Packet's input port (OFPP_NONE if none). */
     ovs_be16 actions_len;         /* Size of action array in bytes. */
@@ -277,10 +232,10 @@ struct ofp_packet_out {
      *     of the message length.
      */
 };
-OFP_ASSERT(sizeof(struct ofp_packet_out) == 8);
+OFP_ASSERT(sizeof(struct ofp10_packet_out) == 8);
 
 /* Flow wildcards. */
-enum ofp_flow_wildcards {
+enum ofp10_flow_wildcards {
     OFPFW10_IN_PORT    = 1 << 0,  /* Switch input port. */
     OFPFW10_DL_VLAN    = 1 << 1,  /* VLAN vid. */
     OFPFW10_DL_SRC     = 1 << 2,  /* Ethernet source address. */
@@ -319,17 +274,6 @@ enum ofp_flow_wildcards {
 #define OFPFW10_ICMP_TYPE OFPFW10_TP_SRC
 #define OFPFW10_ICMP_CODE OFPFW10_TP_DST
 
-/* Values below this cutoff are 802.3 packets and the two bytes
- * following MAC addresses are used as a frame length.  Otherwise, the
- * two bytes are used as the Ethernet type.
- */
-#define OFP_DL_TYPE_ETH2_CUTOFF   0x0600
-
-/* Value of dl_type to indicate that the frame does not include an
- * Ethernet type.
- */
-#define OFP_DL_TYPE_NOT_ETH_TYPE  0x05ff
-
 /* The VLAN id is 12-bits, so we can use the entire 16 bits to indicate
  * special conditions.  All ones indicates that 802.1Q header is not present.
  */
@@ -356,13 +300,6 @@ struct ofp10_match {
 };
 OFP_ASSERT(sizeof(struct ofp10_match) == 40);
 
-/* Value used in "idle_timeout" and "hard_timeout" to indicate that the entry
- * is permanent. */
-#define OFP_FLOW_PERMANENT 0
-
-/* By default, choose a priority in the middle. */
-#define OFP_DEFAULT_PRIORITY 0x8000
-
 enum ofp10_flow_mod_flags {
     OFPFF10_EMERG       = 1 << 2   /* Ramark this is for emergency. */
 };
@@ -391,7 +328,7 @@ struct ofp10_flow_mod {
 OFP_ASSERT(sizeof(struct ofp10_flow_mod) == 64);
 
 /* Flow removed (datapath -> controller). */
-struct ofp_flow_removed {
+struct ofp10_flow_removed {
     struct ofp10_match match; /* Description of fields. */
     ovs_be64 cookie;          /* Opaque controller-issued identifier. */
 
@@ -407,16 +344,7 @@ struct ofp_flow_removed {
     ovs_be64 packet_count;
     ovs_be64 byte_count;
 };
-OFP_ASSERT(sizeof(struct ofp_flow_removed) == 80);
-
-/* OFPT_ERROR: Error message (datapath -> controller). */
-struct ofp_error_msg {
-    ovs_be16 type;
-    ovs_be16 code;
-    uint8_t data[0];          /* Variable-length data.  Interpreted based
-                                 on the type and code. */
-};
-OFP_ASSERT(sizeof(struct ofp_error_msg) == 4);
+OFP_ASSERT(sizeof(struct ofp10_flow_removed) == 80);
 
 /* Statistics request or reply message. */
 struct ofp10_stats_msg {
@@ -427,10 +355,6 @@ struct ofp10_stats_msg {
 };
 OFP_ASSERT(sizeof(struct ofp10_stats_msg) == 12);
 
-enum ofp_stats_reply_flags {
-    OFPSF_REPLY_MORE  = 1 << 0  /* More replies to follow. */
-};
-
 /* Stats request of type OFPST_AGGREGATE or OFPST_FLOW. */
 struct ofp10_flow_stats_request {
     struct ofp10_match match; /* Fields to match. */
@@ -546,15 +470,4 @@ struct ofp10_vendor_stats_msg {
 };
 OFP_ASSERT(sizeof(struct ofp10_vendor_stats_msg) == 16);
 
-/* Vendor extension. */
-struct ofp_vendor_header {
-    struct ofp_header header;   /* Type OFPT_VENDOR. */
-    ovs_be32 vendor;            /* Vendor ID:
-                                 * - MSB 0: low-order bytes are IEEE OUI.
-                                 * - MSB != 0: defined by OpenFlow
-                                 *   consortium. */
-    /* Vendor-defined arbitrary additional data. */
-};
-OFP_ASSERT(sizeof(struct ofp_vendor_header) == 12);
-
 #endif /* openflow/openflow-1.0.h */
index 9785db4..8dfd795 100644 (file)
 #define OFPP11_MAX    0xffffff00
 #define OFPP11_OFFSET (OFPP11_MAX - OFPP_MAX)
 
+/* Reserved wildcard port used only for flow mod (delete) and flow stats
+ * requests. Selects all flows regardless of output port
+ * (including flows with no output port)
+ *
+ * Define it via OFPP_NONE (0xFFFF) so that OFPP_ANY is still an enum ofp_port
+ */
+#define OFPP_ANY OFPP_NONE
+
 /* OpenFlow 1.1 port config flags are just the common flags. */
 #define OFPPC11_ALL \
     (OFPPC_PORT_DOWN | OFPPC_NO_RECV | OFPPC_NO_FWD | OFPPC_NO_PACKET_IN)
@@ -119,6 +127,7 @@ struct ofp11_port {
     ovs_be32 curr_speed;    /* Current port bitrate in kbps. */
     ovs_be32 max_speed;     /* Max port bitrate in kbps */
 };
+OFP_ASSERT(sizeof(struct ofp11_port) == 64);
 
 /* Modify behavior of the physical port */
 struct ofp11_port_mod {
@@ -580,7 +589,8 @@ struct ofp11_flow_stats {
                                   when this is not an exact-match entry. */
     ovs_be16 idle_timeout;     /* Number of seconds idle before expiration. */
     ovs_be16 hard_timeout;     /* Number of seconds before expiration. */
-    uint8_t pad2[6];           /* Align to 64-bits. */
+    ovs_be16 flags;            /* OF 1.3: Set of OFPFF*. */
+    uint8_t  pad2[4];          /* Align to 64-bits. */
     ovs_be64 cookie;           /* Opaque controller-issued identifier. */
     ovs_be64 packet_count;     /* Number of packets in flow. */
     ovs_be64 byte_count;       /* Number of bytes in flow. */
@@ -743,7 +753,7 @@ struct ofp11_packet_in {
     ovs_be16 total_len;     /* Full length of frame. */
     uint8_t reason;         /* Reason packet is being sent (one of OFPR_*) */
     uint8_t table_id;       /* ID of the table that was looked up */
-    uint8_t data[0];        /* Ethernet frame, halfway through 32-bit word,
+    /* uint8_t data[0];        Ethernet frame, halfway through 32-bit word,
                                so the IP header is 32-bit aligned. The
                                amount of data is inferred from the length
                                field in the header. Because of padding,
index 1c3f017..5546313 100644 (file)
@@ -106,6 +106,11 @@ enum oxm12_ofb_match_fields {
     OFPXMT12_OFB_IPV6_ND_TLL,    /* Target link-layer for ND. */
     OFPXMT12_OFB_MPLS_LABEL,     /* MPLS label. */
     OFPXMT12_OFB_MPLS_TC,        /* MPLS TC. */
+    /* Following added in OpenFlow 1.3 */
+    OFPXMT12_OFB_MPLS_BOS,       /* MPLS BoS bit. */
+    OFPXMT12_OFB_PBB_ISID,       /* PBB I-SID. */
+    OFPXMT12_OFB_TUNNEL_ID,      /* Logical Port Metadata */
+    OFPXMT12_OFB_IPV6_EXTHDR,    /* IPv6 Extension Header pseudo-field */
 
     /* End Marker */
     OFPXMT12_OFB_MAX,
@@ -172,6 +177,13 @@ enum oxm12_ofb_match_fields {
 #define OXM_OF_IPV6_ND_TLL    OXM_HEADER   (OFPXMT12_OFB_IPV6_ND_TLL, 6)
 #define OXM_OF_MPLS_LABEL     OXM_HEADER   (OFPXMT12_OFB_MPLS_LABEL, 4)
 #define OXM_OF_MPLS_TC        OXM_HEADER   (OFPXMT12_OFB_MPLS_TC, 1)
+#define OXM_OF_MPLS_BOS       OXM_HEADER   (OFPXMT12_OFB_MPLS_BOS, 1)
+#define OXM_OF_PBB_ISID       OXM_HEADER   (OFPXMT12_OFB_PBB_ISID, 4)
+#define OXM_OF_PBB_ISID_W     OXM_HEADER_W (OFPXMT12_OFB_PBB_ISID, 4)
+#define OXM_OF_TUNNEL_ID      OXM_HEADER   (OFPXMT12_OFB_TUNNEL_ID, 8)
+#define OXM_OF_TUNNEL_ID_W    OXM_HEADER_W (OFPXMT12_OFB_TUNNEL_ID, 8)
+#define OXM_OF_IPV6_EXTHDR    OXM_HEADER   (OFPXMT12_OFB_IPV6_EXTHDR, 2)
+#define OXM_OF_IPV6_EXTHDR_W  OXM_HEADER_W (OFPXMT12_OFB_IPV6_EXTHDR, 2)
 
 /* The VLAN id is 12-bits, so we can use the entire 16 bits to indicate
  * special conditions.
@@ -181,6 +193,19 @@ enum ofp12_vlan_id {
     OFPVID12_NONE    = 0x0000, /* No VLAN id was set. */
 };
 
+/* Bit definitions for IPv6 Extension Header pseudo-field. */
+enum ofp12_ipv6exthdr_flags {
+    OFPIEH12_NONEXT = 1 << 0,   /* "No next header" encountered. */
+    OFPIEH12_ESP    = 1 << 1,   /* Encrypted Sec Payload header present. */
+    OFPIEH12_AUTH   = 1 << 2,   /* Authentication header present. */
+    OFPIEH12_DEST   = 1 << 3,   /* 1 or 2 dest headers present. */
+    OFPIEH12_FRAG   = 1 << 4,   /* Fragment header present. */
+    OFPIEH12_ROUTER = 1 << 5,   /* Router header present. */
+    OFPIEH12_HOP    = 1 << 6,   /* Hop-by-hop header present. */
+    OFPIEH12_UNREP  = 1 << 7,   /* Unexpected repeats encountered. */
+    OFPIEH12_UNSEQ  = 1 << 8    /* Unexpected sequencing encountered. */
+};
+
 /* Header for OXM experimenter match fields. */
 struct ofp12_oxm_experimenter_header {
     ovs_be32 oxm_header;   /* oxm_class = OFPXMC_EXPERIMENTER */
diff --git a/include/openflow/openflow-1.3.h b/include/openflow/openflow-1.3.h
new file mode 100644 (file)
index 0000000..b7b877c
--- /dev/null
@@ -0,0 +1,460 @@
+/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+* Junior University
+* Copyright (c) 2011, 2012 Open Networking Foundation
+*
+* We are making the OpenFlow specification and associated documentation
+* (Software) available for public use and benefit with the expectation
+* that others will use, modify and enhance the Software and contribute
+* those enhancements back to the community. However, since we would
+* like to make the Software available for broadest use, with as few
+* restrictions as possible permission is hereby granted, free of
+* charge, to any person obtaining a copy of this Software to deal in
+* the Software under the copyrights without restriction, including
+* without limitation the rights to use, copy, modify, merge, publish,
+* distribute, sublicense, and/or sell copies of the Software, and to
+* permit persons to whom the Software is furnished to do so, subject to
+* the following conditions:
+*
+* The above copyright notice and this permission notice shall be
+* included in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+* NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+* SOFTWARE.
+*
+* The name and trademarks of copyright holder(s) may NOT be used in
+* advertising or publicity pertaining to the Software or any
+* derivatives without specific, written prior permission.
+*/
+
+/* OpenFlow: protocol between controller and datapath. */
+
+#ifndef OPENFLOW_13_H
+#define OPENFLOW_13_H 1
+
+#include "openflow/openflow-1.2.h"
+
+/*
+ * OpenFlow 1.3 modifies the syntax of the following message types:
+ *
+ * OFPT_FEATURES_REPLY     = 6    (opf13_switch_features)
+ *                                 - new field: auxiliary_id
+ *                                 - removed: ofp_ports at the end
+ *
+ * OFPT_PACKET_IN          = 10   (ofp13_packet_in) new field: cookie
+ *
+ * OpenFlow 1.3 adds following new message types:
+ *
+ * * Asynchronous message configuration. *
+ * OFPT13_GET_ASYNC_REQUEST  = 26   (void)
+ * OFPT13_GET_ASYNC_REPLY    = 27   (ofp13_async_config)
+ * OFPT13_SET_ASYNC          = 28   (ofp13_async_config)
+ *
+ * * Meters and rate limiters configuration messages. *
+ * OFPT13_METER_MOD          = 29   (ofp13_meter_mod)
+ *
+ * OpenFlow 1.3 modifies the syntax of the following statistics message types
+ * (now called multipart message types):
+ *
+ * OFPMP13_FLOW_REPLY = 1 (struct ofp13_flow_stats[])
+ * OFPMP13_TABLE_REPLY = 3 (struct ofp13_table_stats[])
+ * OFPMP13_PORT_REPLY = 4 (struct ofp13_port_stats[])
+ * OFPMP13_QUEUE_REPLY = 5, (struct ofp13_queue_stats[])
+ * OFPMP13_GROUP_REPLY = 6, (struct ofp13_group_stats[])
+ *
+ * OpenFlow 1.3 adds the following multipart message types
+ *
+ * Meter statistics:
+ * OFPMP13_METER_REQUEST = 9, (struct ofp13_meter_multipart_request)
+ * OFPMP13_METER_REPLY = 9, (struct ofp13_meter_stats[])
+ *
+ * Meter configuration:
+ * OFPMP13_METER_CONFIG_REQUEST = 10, (struct ofp13_meter_multipart_request)
+ * OFPMP13_METER_CONFIG_REPLY = 10, (struct ofp13_meter_config[])
+ *
+ * Meter features:
+ * OFPMP13_METER_FEATURES_REQUEST = 11 (void)
+ * OFPMP13_METER_FEATURES_REPLY = 11 (struct ofp13_meter_features)
+ *
+ * Table features:
+ * OFPMP13_TABLE_FEATURES_REQUEST = 12, (struct ofp13_table_features[])
+ * OFPMP13_TABLE_FEATURES_REPLY = 12, (struct ofp13_table_features[])
+ *
+ */
+
+enum ofp13_instruction_type {
+    OFPIT13_METER = 6           /* Apply meter (rate limiter) */
+};
+
+#define OFPIT13_ALL (OFPIT11_GOTO_TABLE | OFPIT11_WRITE_METADATA |      \
+                     OFPIT11_WRITE_ACTIONS | OFPIT11_APPLY_ACTIONS |    \
+                     OFPIT11_CLEAR_ACTIONS | OFPIT13_METER)
+
+/* Instruction structure for OFPIT_METER */
+struct ofp13_instruction_meter {
+    ovs_be16 type;              /* OFPIT13_METER */
+    ovs_be16 len;               /* Length is 8. */
+    ovs_be32 meter_id;          /* Meter instance. */
+};
+OFP_ASSERT(sizeof(struct ofp13_instruction_meter) == 8);
+
+enum ofp13_action_type {
+    OFPAT13_PUSH_PBB = 26,     /* Push a new PBB service tag (I-TAG) */
+    OFPAT13_PPO_PBB  = 27      /* Pop the outer PBB service tag (I-TAG) */
+};
+
+/* enum ofp_config_flags value OFPC_INVALID_TTL_TO_CONTROLLER
+ * is deprecated in OpenFlow 1.3 */
+
+/* Flags to configure the table. Reserved for future use. */
+enum ofp13_table_config {
+    OFPTC13_DEPRECATED_MASK = 3  /* Deprecated bits */
+};
+
+/* OpenFlow 1.3 specific flags
+ * (struct ofp12_flow_mod, member flags). */
+enum ofp13_flow_mod_flags {
+    OFPFF13_NO_PKT_COUNTS = 1 << 3, /* Don't keep track of packet count. */
+    OFPFF13_NO_BYT_COUNTS = 1 << 4  /* Don't keep track of byte count. */
+};
+
+/* Common header for all meter bands */
+struct ofp13_meter_band_header {
+    ovs_be16 type;       /* One of OFPMBT_*. */
+    ovs_be16 len;        /* Length in bytes of this band. */
+    ovs_be32 rate;       /* Rate for this band. */
+    ovs_be32 burst_size; /* Size of bursts. */
+};
+OFP_ASSERT(sizeof(struct ofp13_meter_band_header) == 12);
+
+/* Meter configuration. OFPT_METER_MOD. */
+struct ofp13_meter_mod {
+    ovs_be16          command;      /* One of OFPMC_*. */
+    ovs_be16          flags;        /* Set of OFPMF_*. */
+    ovs_be32          meter_id;     /* Meter instance. */
+    /* struct ofp13_meter_band_header bands[0];  The bands length is inferred
+                                                 from the length field in the
+                                                 header. */
+};
+OFP_ASSERT(sizeof(struct ofp13_meter_mod) == 8);
+
+/* Meter numbering. Flow meters can use any number up to OFPM_MAX. */
+enum ofp13_meter {
+    /* Last usable meter. */
+    OFPM13_MAX        = 0xffff0000,
+    /* Virtual meters. */
+    OFPM13_SLOWPATH   = 0xfffffffd, /* Meter for slow datapath. */
+    OFPM13_CONTROLLER = 0xfffffffe, /* Meter for controller connection. */
+    OFPM13_ALL        = 0xffffffff, /* Represents all meters for stat requests
+                                     commands. */
+};
+
+/* Meter commands */
+enum ofp13_meter_mod_command {
+    OFPMC13_ADD,           /* New meter. */
+    OFPMC13_MODIFY,        /* Modify specified meter. */
+    OFPMC13_DELETE         /* Delete specified meter. */
+};
+
+/* Meter configuration flags */
+enum ofp13_meter_flags {
+    OFPMF13_KBPS    = 1 << 0,   /* Rate value in kb/s (kilo-bit per second). */
+    OFPMF13_PKTPS   = 1 << 1,   /* Rate value in packet/sec. */
+    OFPMF13_BURST   = 1 << 2,   /* Do burst size. */
+    OFPMF13_STATS   = 1 << 3    /* Collect statistics. */
+};
+
+/* Meter band types */
+enum ofp13_meter_band_type {
+    OFPMBT13_DROP         = 1,     /* Drop packet. */
+    OFPMBT13_DSCP_REMARK  = 2,     /* Remark DSCP in the IP header. */
+    OFPMBT13_EXPERIMENTER = 0xFFFF /* Experimenter meter band. */
+};
+
+/* OFPMBT_DROP band - drop packets */
+struct ofp13_meter_band_drop {
+    ovs_be16    type;        /* OFPMBT_DROP. */
+    ovs_be16    len;         /* Length in bytes of this band. */
+    ovs_be32    rate;        /* Rate for dropping packets. */
+    ovs_be32    burst_size;  /* Size of bursts. */
+    uint8_t     pad[4];
+};
+OFP_ASSERT(sizeof(struct ofp13_meter_band_drop) == 16);
+
+/* OFPMBT_DSCP_REMARK band - Remark DSCP in the IP header */
+struct ofp13_meter_band_dscp_remark {
+    ovs_be16    type;        /* OFPMBT_DSCP_REMARK. */
+    ovs_be16    len;         /* Length in bytes of this band. */
+    ovs_be32    rate;        /* Rate for remarking packets. */
+    ovs_be32    burst_size;  /* Size of bursts. */
+    uint8_t     prec_level;  /* Number of drop precedence level to add. */
+    uint8_t     pad[3];
+};
+OFP_ASSERT(sizeof(struct ofp13_meter_band_dscp_remark) == 16);
+
+/* OFPMBT_EXPERIMENTER band - Write actions in action set */
+struct ofp13_meter_band_experimenter {
+    ovs_be16    type;        /* OFPMBT_EXPERIMENTER. */
+    ovs_be16    len;         /* Length in bytes of this band. */
+    ovs_be32    rate;        /* Rate for dropping packets. */
+    ovs_be32    burst_size;  /* Size of bursts. */
+    ovs_be32    experimenter; /* Experimenter ID which takes the same form as
+                                 in struct ofp_experimenter_header. */
+};
+OFP_ASSERT(sizeof(struct ofp13_meter_band_experimenter) == 16);
+
+/* OF 1.3 adds MORE flag also for requests */
+enum ofp13_multipart_request_flags {
+    OFPMPF13_REQ_MORE = 1 << 0 /* More requests to follow. */
+};
+
+/* OF 1.3 splits table features off the ofp_table_stats */
+/* Body of reply to OFPMP13_TABLE request. */
+struct ofp13_table_stats {
+    uint8_t  table_id;      /* Identifier of table. Lower numbered tables are
+                               consulted first. */
+    uint8_t  pad[3];        /* Align to 32-bits. */
+    ovs_be32 active_count;  /* Number of active entries. */
+    ovs_be64 lookup_count;  /* Number of packets looked up in table. */
+    ovs_be64 matched_count; /* Number of packets that hit table. */
+};
+OFP_ASSERT(sizeof(struct ofp13_table_stats) == 24);
+
+/* Common header for all Table Feature Properties */
+struct ofp13_table_feature_prop_header {
+    ovs_be16    type;   /* One of OFPTFPT_*. */
+    ovs_be16    length; /* Length in bytes of this property. */
+};
+OFP_ASSERT(sizeof(struct ofp13_table_feature_prop_header) == 4);
+
+/* Body for ofp_multipart_request of type OFPMP_TABLE_FEATURES./
+ * Body of reply to OFPMP_TABLE_FEATURES request. */
+struct ofp13_table_features {
+    ovs_be16 length;          /* Length is padded to 64 bits. */
+    uint8_t table_id;         /* Identifier of table. Lower numbered tables
+                                 are consulted first. */
+    uint8_t pad[5];           /* Align to 64-bits. */
+    char name[OFP_MAX_TABLE_NAME_LEN];
+    ovs_be64 metadata_match;  /* Bits of metadata table can match. */
+    ovs_be64 metadata_write;  /* Bits of metadata table can write. */
+    ovs_be32 config;          /* Bitmap of OFPTC_* values */
+    ovs_be32 max_entries;     /* Max number of entries supported. */
+
+    /* Table Feature Property list */
+    /* struct ofp13_table_feature_prop_header properties[0]; */
+};
+OFP_ASSERT(sizeof(struct ofp13_table_features) == 64);
+
+/* Table Feature property types.
+ * Low order bit cleared indicates a property for a regular Flow Entry.
+ * Low order bit set indicates a property for the Table-Miss Flow Entry. */
+enum ofp13_table_feature_prop_type {
+    OFPTFPT13_INSTRUCTIONS         = 0, /* Instructions property. */
+    OFPTFPT13_INSTRUCTIONS_MISS    = 1, /* Instructions for table-miss. */
+    OFPTFPT13_NEXT_TABLES          = 2, /* Next Table property. */
+    OFPTFPT13_NEXT_TABLES_MISS     = 3, /* Next Table for table-miss. */
+    OFPTFPT13_WRITE_ACTIONS        = 4, /* Write Actions property. */
+    OFPTFPT13_WRITE_ACTIONS_MISS   = 5, /* Write Actions for table-miss. */
+    OFPTFPT13_APPLY_ACTIONS        = 6, /* Apply Actions property. */
+    OFPTFPT13_APPLY_ACTIONS_MISS   = 7, /* Apply Actions for table-miss. */
+    OFPTFPT13_MATCH                = 8, /* Match property. */
+    OFPTFPT13_WILDCARDS            = 10, /* Wildcards property. */
+    OFPTFPT13_WRITE_SETFIELD       = 12, /* Write Set-Field property. */
+    OFPTFPT13_WRITE_SETFIELD_MISS  = 13, /* Write Set-Field for table-miss. */
+    OFPTFPT13_APPLY_SETFIELD       = 14, /* Apply Set-Field property. */
+    OFPTFPT13_APPLY_SETFIELD_MISS  = 15, /* Apply Set-Field for table-miss. */
+    OFPTFPT13_EXPERIMENTER         = 0xFFFE, /* Experimenter property. */
+    OFPTFPT13_EXPERIMENTER_MISS    = 0xFFFF, /* Experimenter for table-miss. */
+};
+
+/* Instructions property */
+struct ofp13_table_feature_prop_instructions {
+    ovs_be16    type;    /* One of OFPTFPT13_INSTRUCTIONS,
+                            OFPTFPT13_INSTRUCTIONS_MISS. */
+    ovs_be16    length;  /* Length in bytes of this property. */
+    /* Followed by:
+     *   - Exactly (length - 4) bytes containing the instruction ids, then
+     *   - Exactly (length + 7)/8*8 - (length) (between 0 and 7)
+     *     bytes of all-zero bytes */
+    /* struct ofp11_instruction instruction_ids[0];  List of instructions
+                                                     without any data */
+};
+OFP_ASSERT(sizeof(struct ofp13_table_feature_prop_instructions) == 4);
+
+/* Next Tables property */
+struct ofp13_table_feature_prop_next_tables {
+    ovs_be16    type;   /* One of OFPTFPT13_NEXT_TABLES,
+                           OFPTFPT13_NEXT_TABLES_MISS. */
+    ovs_be16    length; /* Length in bytes of this property. */
+    /* Followed by:
+     *   - Exactly (length - 4) bytes containing the table_ids, then
+     *   - Exactly (length + 7)/8*8 - (length) (between 0 and 7)
+     *     bytes of all-zero bytes */
+    /* uint8_t     next_table_ids[0]; */
+};
+OFP_ASSERT(sizeof(struct ofp13_table_feature_prop_next_tables) == 4);
+
+/* Actions property */
+struct ofp13_table_feature_prop_actions {
+    ovs_be16    type;   /* One of OFPTFPT13_WRITE_ACTIONS,
+                           OFPTFPT13_WRITE_ACTIONS_MISS,
+                           OFPTFPT13_APPLY_ACTIONS,
+                           OFPTFPT13_APPLY_ACTIONS_MISS. */
+    ovs_be16    length; /* Length in bytes of this property. */
+    /* Followed by:
+     *   - Exactly (length - 4) bytes containing the action_ids, then
+     *   - Exactly (length + 7)/8*8 - (length) (between 0 and 7)
+     *     bytes of all-zero bytes */
+    /* struct ofp_action_header action_ids[0];     List of actions
+                                                   without any data */
+};
+OFP_ASSERT(sizeof(struct ofp13_table_feature_prop_actions) == 4);
+
+
+/* Match, Wildcard or Set-Field property */
+struct ofp13_table_feature_prop_oxm {
+    ovs_be16    type;   /* One of OFPTFPT13_MATCH, OFPTFPT13_WILDCARDS,
+                           OFPTFPT13_WRITE_SETFIELD,
+                           OFPTFPT13_WRITE_SETFIELD_MISS,
+                           OFPTFPT13_APPLY_SETFIELD,
+                           OFPTFPT13_APPLY_SETFIELD_MISS. */
+    ovs_be16    length; /* Length in bytes of this property. */
+    /* Followed by:
+     *   - Exactly (length - 4) bytes containing the oxm_ids, then
+     *   - Exactly (length + 7)/8*8 - (length) (between 0 and 7)
+     *     bytes of all-zero bytes */
+    /* ovs_be32    oxm_ids[0];     Array of OXM headers */
+};
+OFP_ASSERT(sizeof(struct ofp13_table_feature_prop_oxm) == 4);
+
+/* Experimenter table feature property */
+struct ofp13_table_feature_prop_experimenter {
+    ovs_be16    type;     /* One of OFPTFPT13_EXPERIMENTER,
+                             OFPTFPT13_EXPERIMENTER_MISS. */
+    ovs_be16    length;   /* Length in bytes of this property. */
+    ovs_be32    experimenter; /* Experimenter ID which takes the same form
+                                 as in struct ofp_experimenter_header. */
+    ovs_be32    exp_type;     /* Experimenter defined. */
+    /* Followed by:
+     *   - Exactly (length - 12) bytes containing the experimenter data, then
+     *   - Exactly (length + 7)/8*8 - (length) (between 0 and 7)
+     *     bytes of all-zero bytes */
+    /* ovs_be32    experimenter_data[0]; */
+};
+OFP_ASSERT(sizeof(struct ofp13_table_feature_prop_experimenter) == 12);
+
+/* Body of reply to OFPMP13_PORT request. If a counter is unsupported, set
+ * the field to all ones. */
+struct ofp13_port_stats {
+    struct ofp11_port_stats ps;
+    ovs_be32 duration_sec;    /* Time port has been alive in seconds. */
+    ovs_be32 duration_nsec;   /* Time port has been alive in nanoseconds
+                                 beyond duration_sec. */
+};
+OFP_ASSERT(sizeof(struct ofp13_port_stats) == 112);
+
+/* Body of reply to OFPMP13_QUEUE request */
+struct ofp13_queue_stats {
+    struct ofp11_queue_stats qs;
+    ovs_be32 duration_sec;    /* Time queue has been alive in seconds. */
+    ovs_be32 duration_nsec;   /* Time queue has been alive in nanoseconds
+                                 beyond duration_sec. */
+};
+OFP_ASSERT(sizeof(struct ofp13_queue_stats) == 40);
+
+/* Body of reply to OFPMP13_GROUP request */
+struct ofp13_group_stats {
+    struct ofp11_group_stats gs;
+    ovs_be32 duration_sec;    /* NEW: Time group has been alive in seconds. */
+    ovs_be32 duration_nsec;   /* NEW: Time group has been alive in nanoseconds
+                                 beyond duration_sec. */
+    /* struct ofp11_bucket_counter bucket_stats[0]; */
+};
+OFP_ASSERT(sizeof(struct ofp13_group_stats) == 40);
+
+/* Body of OFPMP13_METER and OFPMP13_METER_CONFIG requests. */
+struct ofp13_meter_multipart_request {
+    ovs_be32 meter_id;  /* Meter instance, or OFPM_ALL. */
+    uint8_t pad[4];     /* Align to 64 bits. */
+};
+OFP_ASSERT(sizeof(struct ofp13_meter_multipart_request) == 8);
+
+/* Statistics for each meter band */
+struct ofp13_meter_band_stats {
+    ovs_be64    packet_band_count;      /* Number of packets in band. */
+    ovs_be64    byte_band_count;        /* Number of bytes in band. */
+};
+OFP_ASSERT(sizeof(struct ofp13_meter_band_stats) == 16);
+
+/* Body of reply to OFPMP13_METER request. Meter statistics. */
+struct ofp13_meter_stats {
+    ovs_be32  meter_id;          /* Meter instance. */
+    ovs_be16  len;               /* Length in bytes of this stats. */
+    uint8_t   pad[6];
+    ovs_be32  flow_count;        /* Number of flows bound to meter. */
+    ovs_be64  packet_in_count;   /* Number of packets in input. */
+    ovs_be64  byte_in_count;     /* Number of bytes in input. */
+    ovs_be32  duration_sec;      /* Time meter has been alive in seconds. */
+    ovs_be32  duration_nsec;     /* Time meter has been alive in nanoseconds
+                                    beyond duration_sec. */
+    /* struct ofp13_meter_band_stats band_stats[0];  The band_stats length is
+                                             inferred from the length field. */
+};
+OFP_ASSERT(sizeof(struct ofp13_meter_stats) == 40);
+
+/* Body of reply to OFPMP13_METER_CONFIG request. Meter configuration. */
+struct ofp13_meter_config {
+    ovs_be16          length;       /* Length of this entry. */
+    ovs_be16          flags;        /* Set of OFPMC_* that apply. */
+    ovs_be32          meter_id;     /* Meter instance. */
+    /* struct ofp13_meter_band_header bands[0];   The bands length is inferred
+                                               from the length field. */
+};
+OFP_ASSERT(sizeof(struct ofp13_meter_config) == 8);
+
+/* Body of reply to OFPMP13_METER_FEATURES request. Meter features. */
+struct ofp13_meter_features {
+    ovs_be32   max_meter;     /* Maximum number of meters. */
+    ovs_be32   band_types;    /* Bitmaps of OFPMBT13_* values supported. */
+    ovs_be32   capabilities;  /* Bitmaps of "ofp13_meter_flags". */
+    uint8_t    max_bands;     /* Maximum bands per meters */
+    uint8_t    max_color;     /* Maximum color value */
+    uint8_t    pad[2];
+};
+OFP_ASSERT(sizeof(struct ofp13_meter_features) == 16);
+
+/* Asynchronous message configuration. */
+/* The body of this is the same as nx_async_config */
+/* OFPT_GET_ASYNC_REPLY or OFPT_SET_ASYNC. */
+struct ofp13_async_config {
+    ovs_be32 packet_in_mask[2];   /* Bitmasks of OFPR_* values. */
+    ovs_be32 port_status_mask[2]; /* Bitmasks of OFPPR_* values. */
+    ovs_be32 flow_removed_mask[2];/* Bitmasks of OFPRR_* values. */
+};
+OFP_ASSERT(sizeof(struct ofp13_async_config) == 24);
+
+
+/* Packet received on port (datapath -> controller). */
+struct ofp13_packet_in {
+    struct ofp12_packet_in pi;
+    ovs_be64 cookie;          /* Cookie of the flow entry that was looked up */
+    /* Followed by:
+     *   - Match
+     *   - Exactly 2 all-zero padding bytes, then
+     *   - An Ethernet frame whose length is inferred from header.length.
+     * The padding bytes preceding the Ethernet frame ensure that the IP
+     * header (if any) following the Ethernet header is 32-bit aligned.
+     */
+    /* struct ofp12_match match; */
+    /* uint8_t pad[2];         Align to 64 bit + 16 bit */
+    /* uint8_t data[0];        Ethernet frame */
+};
+OFP_ASSERT(sizeof(struct ofp13_packet_in) == 16);
+
+
+#endif /* openflow/openflow-1.3.h */
index 0bca0d2..3cc22c9 100644 (file)
@@ -75,6 +75,7 @@ enum ofp_version {
     OFP10_VERSION = 0x01,
     OFP11_VERSION = 0x02,
     OFP12_VERSION = 0x03,
+    OFP13_VERSION = 0x04
 };
 
 #define OFP_MAX_TABLE_NAME_LEN 32
@@ -85,6 +86,27 @@ enum ofp_version {
 
 #define OFP_ETH_ALEN 6          /* Bytes in an Ethernet address. */
 
+#define OFP_DEFAULT_MISS_SEND_LEN   128
+
+/* Values below this cutoff are 802.3 packets and the two bytes
+ * following MAC addresses are used as a frame length.  Otherwise, the
+ * two bytes are used as the Ethernet type.
+ */
+#define OFP_DL_TYPE_ETH2_CUTOFF   0x0600
+
+/* Value of dl_type to indicate that the frame does not include an
+ * Ethernet type.
+ */
+#define OFP_DL_TYPE_NOT_ETH_TYPE  0x05ff
+
+/* Value used in "idle_timeout" and "hard_timeout" to indicate that the entry
+ * is permanent. */
+#define OFP_FLOW_PERMANENT 0
+
+/* By default, choose a priority in the middle. */
+#define OFP_DEFAULT_PRIORITY 0x8000
+
+
 /* Header on all OpenFlow packets. */
 struct ofp_header {
     uint8_t version;    /* An OpenFlow version number, e.g. OFP10_VERSION. */
@@ -96,6 +118,39 @@ struct ofp_header {
 };
 OFP_ASSERT(sizeof(struct ofp_header) == 8);
 
+/* OFPT_ERROR: Error message (datapath -> controller). */
+struct ofp_error_msg {
+    ovs_be16 type;
+    ovs_be16 code;
+    uint8_t data[0];          /* Variable-length data.  Interpreted based
+                                 on the type and code. */
+};
+OFP_ASSERT(sizeof(struct ofp_error_msg) == 4);
+
+enum ofp_config_flags {
+    /* Handling of IP fragments. */
+    OFPC_FRAG_NORMAL   = 0,  /* No special handling for fragments. */
+    OFPC_FRAG_DROP     = 1,  /* Drop fragments. */
+    OFPC_FRAG_REASM    = 2,  /* Reassemble (only if OFPC_IP_REASM set). */
+    OFPC_FRAG_NX_MATCH = 3,  /* Make first fragments available for matching. */
+    OFPC_FRAG_MASK     = 3,
+
+    /* OFPC_INVALID_TTL_TO_CONTROLLER is deprecated in OpenFlow 1.3 */
+
+    /* TTL processing - applicable for IP and MPLS packets. */
+    OFPC_INVALID_TTL_TO_CONTROLLER = 1 << 2, /* Send packets with invalid TTL
+                                                to the controller. */
+};
+
+/* Switch configuration. */
+struct ofp_switch_config {
+    ovs_be16 flags;             /* OFPC_* flags. */
+    ovs_be16 miss_send_len;     /* Max bytes of new flow that datapath should
+                                   send to the controller. */
+};
+OFP_ASSERT(sizeof(struct ofp_switch_config) == 4);
+
+
 /* Common flags to indicate behavior of the physical port.  These flags are
  * used in ofp_port to describe the current configuration.  They are used in
  * the ofp_port_mod message to configure the port's behavior.
@@ -166,14 +221,17 @@ struct ofp_switch_features {
     ovs_be32 n_buffers;     /* Max packets buffered at once. */
 
     uint8_t n_tables;       /* Number of tables supported by datapath. */
-    uint8_t pad[3];         /* Align to 64-bits. */
+    uint8_t auxiliary_id;   /* OF 1.3: Identify auxiliary connections */
+    uint8_t pad[2];         /* Align to 64-bits. */
 
     /* Features. */
     ovs_be32 capabilities;  /* OFPC_*, OFPC10_*, OFPC11_*, OFPC12_*. */
-    ovs_be32 actions;       /* Bitmap of supported "ofp_action_type"s. */
+    ovs_be32 actions;       /* Bitmap of supported "ofp_action_type"s.
+                             * DEPRECATED in OpenFlow 1.1 */
 
     /* Followed by an array of struct ofp10_phy_port or struct ofp11_port
-     * structures.  The number is inferred from header.length. */
+     * structures.  The number is inferred from header.length.
+     * REMOVED in OpenFlow 1.3 */
 };
 OFP_ASSERT(sizeof(struct ofp_switch_features) == 24);
 
@@ -211,6 +269,29 @@ enum ofp_flow_mod_flags {
     OFPFF_CHECK_OVERLAP = 1 << 1,  /* Check for overlapping entries first. */
 };
 
+/* Action header for OFPAT10_VENDOR and OFPAT11_EXPERIMEMNTER.
+ * The rest of the body is vendor-defined. */
+struct ofp_action_vendor_header {
+    ovs_be16 type;                  /* OFPAT10_VENDOR. */
+    ovs_be16 len;                   /* Length is a multiple of 8. */
+    ovs_be32 vendor;                /* Vendor ID, which takes the same form
+                                       as in "struct ofp_vendor_header". */
+};
+OFP_ASSERT(sizeof(struct ofp_action_vendor_header) == 8);
+
+/* Action header that is common to all actions.  The length includes the
+ * header and any padding used to make the action 64-bit aligned.
+ * NB: The length of an action *must* always be a multiple of eight. */
+struct ofp_action_header {
+    ovs_be16 type;                  /* One of OFPAT10_*. */
+    ovs_be16 len;                   /* Length of action, including this
+                                       header.  This is the length of action,
+                                       including any padding to make it
+                                       64-bit aligned. */
+    uint8_t pad[4];
+};
+OFP_ASSERT(sizeof(struct ofp_action_header) == 8);
+
 /* Action structure for OFPAT10_SET_VLAN_VID and OFPAT11_SET_VLAN_VID. */
 struct ofp_action_vlan_vid {
     ovs_be16 type;                  /* Type. */
@@ -288,6 +369,10 @@ struct ofp_port_status {
 };
 OFP_ASSERT(sizeof(struct ofp_port_status) == 8);
 
+enum ofp_stats_reply_flags {
+    OFPSF_REPLY_MORE  = 1 << 0  /* More replies to follow. */
+};
+
 #define DESC_STR_LEN   256
 #define SERIAL_NUM_LEN 32
 /* Body of reply to OFPST_DESC request.  Each entry is a NULL-terminated ASCII
@@ -334,4 +419,26 @@ enum ofp_group {
     OFPG_ANY        = 0xffffffff   /* Wildcard, for flow stats requests. */
 };
 
+enum ofp_hello_elem_type {
+    OFPHET_VERSIONBITMAP          = 1, /* Bitmap of version supported. */
+};
+
+/* Common header for all Hello Elements */
+struct ofp_hello_elem_header {
+    ovs_be16    type;        /* One of OFPHET_*. */
+    ovs_be16    length;      /* Length in bytes of this element. */
+};
+OFP_ASSERT(sizeof(struct ofp_hello_elem_header) == 4);
+
+/* Vendor extension. */
+struct ofp_vendor_header {
+    struct ofp_header header;   /* Type OFPT_VENDOR or OFPT_EXPERIMENTER. */
+    ovs_be32 vendor;            /* Vendor ID:
+                                 * - MSB 0: low-order bytes are IEEE OUI.
+                                 * - MSB != 0: defined by OpenFlow
+                                 *   consortium. */
+    /* Vendor-defined arbitrary additional data. */
+};
+OFP_ASSERT(sizeof(struct ofp_vendor_header) == 12);
+
 #endif /* openflow/openflow-common.h */
index b2516c0..d5a78fe 100644 (file)
@@ -20,5 +20,6 @@
 #include "openflow/openflow-1.0.h"
 #include "openflow/openflow-1.1.h"
 #include "openflow/openflow-1.2.h"
+#include "openflow/openflow-1.3.h"
 
 #endif /* openflow/openflow.h */
index 757c765..6cd6192 100644 (file)
@@ -1,5 +1,4 @@
 noinst_HEADERS += \
-       include/openvswitch/brcompat-netlink.h \
        include/openvswitch/datapath-compat.h \
        include/openvswitch/tunnel.h \
        include/openvswitch/types.h
diff --git a/include/openvswitch/brcompat-netlink.h b/include/openvswitch/brcompat-netlink.h
deleted file mode 100644 (file)
index 7e5845a..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (c) 2008, 2009, 2011 Nicira, Inc.
- *
- * This file is offered under your choice of two licenses: Apache 2.0 or GNU
- * GPL 2.0 or later.  The permission statements for each of these licenses is
- * given below.  You may license your modifications to this file under either
- * of these licenses or both.  If you wish to license your modifications under
- * only one of these licenses, delete the permission text for the other
- * license.
- *
- * ----------------------------------------------------------------------
- * 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.
- * ----------------------------------------------------------------------
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * ----------------------------------------------------------------------
- */
-
-#ifndef OPENVSWITCH_BRCOMPAT_NETLINK_H
-#define OPENVSWITCH_BRCOMPAT_NETLINK_H 1
-
-#define BRC_GENL_FAMILY_NAME "brcompat"
-
-/* Attributes that can be attached to the datapath's netlink messages. */
-enum {
-       BRC_GENL_A_UNSPEC,
-
-       /*
-        * "K:" attributes appear in messages from the kernel to userspace.
-        * "U:" attributes appear in messages from userspace to the kernel.
-        */
-
-       /* BRC_GENL_C_DP_ADD, BRC_GENL_C_DP_DEL. */
-       BRC_GENL_A_DP_NAME,             /* K: Datapath name. */
-
-       /* BRC_GENL_C_DP_ADD, BRC_GENL_C_DP_DEL,
-          BRC_GENL_C_PORT_ADD, BRC_GENL_C_PORT_DEL. */
-       BRC_GENL_A_PORT_NAME,   /* K: Interface name. */
-
-       /* BRC_GENL_C_DP_RESULT. */
-       BRC_GENL_A_ERR_CODE,    /* U: Positive error code. */
-
-       /* BRC_GENL_C_QUERY_MC. */
-       BRC_GENL_A_MC_GROUP,    /* K: Generic netlink multicast group. */
-
-       /* BRC_GENL_C_FDB_QUERY. */
-       BRC_GENL_A_FDB_COUNT,   /* K: Number of FDB entries to read. */
-       BRC_GENL_A_FDB_SKIP,    /* K: Record offset into FDB to start reading. */
-
-       /* BRC_GENL_C_DP_RESULT. */
-       BRC_GENL_A_FDB_DATA,    /* U: FDB records. */
-       BRC_GENL_A_IFINDEXES,   /* U: "int" ifindexes of bridges or ports. */
-
-       __BRC_GENL_A_MAX,
-       BRC_GENL_A_MAX = __BRC_GENL_A_MAX - 1
-};
-
-/* Commands that can be executed on the datapath's netlink interface. */
-enum brc_genl_command {
-       BRC_GENL_C_UNSPEC,
-
-       /*
-        * "K:" messages are sent by the kernel to userspace.
-        * "U:" messages are sent by userspace to the kernel.
-        */
-       BRC_GENL_C_DP_ADD,              /* K: Datapath created. */
-       BRC_GENL_C_DP_DEL,              /* K: Datapath destroyed. */
-       BRC_GENL_C_DP_RESULT,   /* U: Return code from ovs-brcompatd. */
-       BRC_GENL_C_PORT_ADD,    /* K: Port added to datapath. */
-       BRC_GENL_C_PORT_DEL,    /* K: Port removed from datapath. */
-       BRC_GENL_C_QUERY_MC,    /* U: Get multicast group for brcompat. */
-       BRC_GENL_C_FDB_QUERY,   /* K: Read records from forwarding database. */
-       BRC_GENL_C_GET_BRIDGES, /* K: Get ifindexes of all bridges. */
-       BRC_GENL_C_GET_PORTS,   /* K: Get ifindexes of all ports on a bridge. */
-
-       __BRC_GENL_C_MAX,
-       BRC_GENL_C_MAX = __BRC_GENL_C_MAX - 1
-};
-#endif /* openvswitch/brcompat-netlink.h */
index c494791..8a53ef8 100644 (file)
 
 /* OVS_VPORT_ATTR_OPTIONS attributes for tunnels.
  *
- * OVS_TUNNEL_ATTR_FLAGS and OVS_TUNNEL_ATTR_DST_IPV4 are required.  All other
+ * OVS_TUNNEL_ATTR_DST_IPV4 is required for kernel tunnel ports, all other
  * attributes are optional.
+ * For flow-based tunnels, only the OVS_TUNNEL_ATTR_DST_PORT is useful.
  */
 enum {
        OVS_TUNNEL_ATTR_UNSPEC,
        OVS_TUNNEL_ATTR_FLAGS,    /* 32-bit TNL_F_*. */
-       OVS_TUNNEL_ATTR_DST_IPV4, /* IPv4 destination address. */
-       OVS_TUNNEL_ATTR_SRC_IPV4, /* IPv4 source address. */
+       OVS_TUNNEL_ATTR_DST_IPV4, /* Remote IPv4 address. */
+       OVS_TUNNEL_ATTR_SRC_IPV4, /* Local IPv4 address. */
        OVS_TUNNEL_ATTR_OUT_KEY,  /* __be64 key to use on output. */
        OVS_TUNNEL_ATTR_IN_KEY,   /* __be64 key to match on input. */
        OVS_TUNNEL_ATTR_TOS,      /* 8-bit TOS value. */
        OVS_TUNNEL_ATTR_TTL,      /* 8-bit TTL value. */
+       OVS_TUNNEL_ATTR_DST_PORT, /* 16-bit UDP port, used by VXLAN. */
        __OVS_TUNNEL_ATTR_MAX
 };
 
@@ -68,8 +70,8 @@ enum {
 #define TNL_F_DF_INHERIT       (1 << 3) /* Inherit DF bit from inner packet. */
 #define TNL_F_DF_DEFAULT       (1 << 4) /* Set DF bit if inherit off or
                                          * not IP. */
+/* Bit 6 is reserved since it was previously used for Tunnel header caching. */
 #define TNL_F_PMTUD            (1 << 5) /* Enable path MTU discovery. */
-#define TNL_F_HDR_CACHE                (1 << 6) /* Enable tunnel header caching. */
 #define TNL_F_IPSEC            (1 << 7) /* Traffic is IPsec encrypted. */
 
 #endif /* openvswitch/tunnel.h */
index ca80c97..d2a54de 100644 (file)
@@ -44,6 +44,13 @@ struct ip6_hdr {
 #define ip6_hlim  ip6_ctlun.ip6_un1.ip6_un1_hlim
 #define ip6_hops  ip6_ctlun.ip6_un1.ip6_un1_hlim
 
+struct ip6_rthdr {
+    uint8_t ip6r_nxt;
+    uint8_t ip6r_len;
+    uint8_t ip6r_type;
+    uint8_t ip6r_segleft;
+};
+
 struct ip6_ext {
     uint8_t ip6e_nxt;
     uint8_t ip6e_len;
index 4a25137..8d148ca 100644 (file)
@@ -113,6 +113,8 @@ lib_libopenvswitch_a_SOURCES = \
        lib/ofp-util.c \
        lib/ofp-util.def \
        lib/ofp-util.h \
+       lib/ofp-version-opt.h \
+       lib/ofp-version-opt.c \
        lib/ofpbuf.c \
        lib/ofpbuf.h \
        lib/ovsdb-data.c \
@@ -291,6 +293,7 @@ MAN_FRAGMENTS += \
        lib/daemon-syn.man \
        lib/leak-checker.man \
        lib/memory-unixctl.man \
+       lib/ofp-version.man \
        lib/ovs.tmac \
        lib/ssl-bootstrap.man \
        lib/ssl-bootstrap-syn.man \
index 25a0fa1..c05e0c8 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@
 #include <limits.h>
 #include <stdint.h>
 #include <stdlib.h>
+#include <math.h>
 
 #include "coverage.h"
 #include "dynamic-string.h"
@@ -50,7 +51,7 @@ VLOG_DEFINE_THIS_MODULE(bond);
 struct bond_entry {
     struct bond_slave *slave;   /* Assigned slave, NULL if unassigned. */
     uint64_t tx_bytes;          /* Count of bytes recently transmitted. */
-    tag_type tag;               /* Tag for entry<->slave association. */
+    tag_type tag;               /* Tag for entry<->facet association. */
     struct list list_node;      /* In bond_slave's 'entries' list. */
 };
 
@@ -494,7 +495,7 @@ static bool
 may_send_learning_packets(const struct bond *bond)
 {
     return bond->lacp_status == LACP_DISABLED
-        && bond->balance != BM_STABLE
+        && (bond->balance == BM_SLB || bond->balance == BM_AB)
         && bond->active_slave;
 }
 
@@ -742,7 +743,8 @@ bond_shift_load(struct bond_entry *hash, struct bond_slave *to,
     hash->tag = tag_create_random();
 }
 
-/* Pick and returns a bond_entry to migrate to 'to' (the least-loaded slave),
+/* Picks and returns a bond_entry to migrate from 'from' (the most heavily
+ * loaded bond slave) to a bond slave that has 'to_tx_bytes' bytes of load,
  * given that doing so must decrease the ratio of the load on the two slaves by
  * at least 0.1.  Returns NULL if there is no appropriate entry.
  *
@@ -771,8 +773,12 @@ choose_entry_to_migrate(const struct bond_slave *from, uint64_t to_tx_bytes)
         delta = e->tx_bytes;
         old_ratio = (double)from->tx_bytes / to_tx_bytes;
         new_ratio = (double)(from->tx_bytes - delta) / (to_tx_bytes + delta);
-        if (old_ratio - new_ratio > 0.1) {
-            /* Would decrease the ratio, move it. */
+        if (old_ratio - new_ratio > 0.1
+            && fabs(new_ratio - 1.0) < fabs(old_ratio - 1.0)) {
+            /* We're aiming for an ideal ratio of 1, meaning both the 'from'
+               and 'to' slave have the same load.  Therefore, we only move an
+               entry if it decreases the load on 'from', and brings us closer
+               to equal traffic load. */
             return e;
         }
     }
@@ -859,8 +865,8 @@ bond_rebalance(struct bond *bond, struct tag_set *tags)
             break;
         }
 
-        /* 'from' is carrying significantly more load than 'to', and that load
-         * is split across at least two different hashes. */
+        /* 'from' is carrying significantly more load than 'to'.  Pick a hash
+         * to move from 'from' to 'to'. */
         e = choose_entry_to_migrate(from, to->tx_bytes);
         if (e) {
             bond_shift_load(e, to, tags);
index e5d226e..d1fe524 100644 (file)
@@ -146,9 +146,7 @@ classifier_destroy(struct classifier *cls)
         struct cls_table *table, *next_table;
 
         HMAP_FOR_EACH_SAFE (table, next_table, hmap_node, &cls->tables) {
-            hmap_destroy(&table->rules);
-            hmap_remove(&cls->tables, &table->hmap_node);
-            free(table);
+            destroy_table(cls, table);
         }
         hmap_destroy(&cls->tables);
     }
index 98a83de..fb32a53 100644 (file)
@@ -112,6 +112,21 @@ recalc_csum32(ovs_be16 old_csum, ovs_be32 old_u32, ovs_be32 new_u32)
                          old_u32 >> 16, new_u32 >> 16);
 }
 
+/* Returns the new checksum for a packet in which the checksum field previously
+ * contained 'old_csum' and in which a field that contained 'old_u32[4]' was
+ * changed to contain 'new_u32[4]'. */
+ovs_be16
+recalc_csum128(ovs_be16 old_csum, ovs_be32 old_u32[4],
+               const ovs_be32 new_u32[4])
+{
+    ovs_be16 new_csum = old_csum;
+    int i;
+
+    for (i = 0; i < 4; ++i) {
+        new_csum = recalc_csum32(new_csum, old_u32[i], new_u32[i]);
+    }
+    return new_csum;
+}
 #else  /* __CHECKER__ */
 /* Making sparse happy with these functions also makes them unreadable, so
  * don't bother to show it their implementations. */
index 12402d7..6382d29 100644 (file)
@@ -28,5 +28,7 @@ uint32_t csum_continue(uint32_t partial, const void *, size_t);
 ovs_be16 csum_finish(uint32_t partial);
 ovs_be16 recalc_csum16(ovs_be16 old_csum, ovs_be16 old_u16, ovs_be16 new_u16);
 ovs_be16 recalc_csum32(ovs_be16 old_csum, ovs_be32 old_u32, ovs_be32 new_u32);
+ovs_be16 recalc_csum128(ovs_be16 old_csum, ovs_be32 old_u32[4],
+                        const ovs_be32 new_u32[4]);
 
 #endif /* csum.h */
index 71f6f81..4849196 100644 (file)
@@ -175,57 +175,63 @@ make_pidfile(void)
     int error;
 
     /* Create a temporary pidfile. */
-    tmpfile = xasprintf("%s.tmp%ld", pidfile, pid);
-    fatal_signal_add_file_to_unlink(tmpfile);
-    file = fopen(tmpfile, "w+");
+    if (overwrite_pidfile) {
+        tmpfile = xasprintf("%s.tmp%ld", pidfile, pid);
+        fatal_signal_add_file_to_unlink(tmpfile);
+    } else {
+        /* Everyone shares the same file which will be treated as a lock.  To
+         * avoid some uncomfortable race conditions, we can't set up the fatal
+         * signal unlink until we've acquired it. */
+        tmpfile = xasprintf("%s.tmp", pidfile);
+    }
+
+    file = fopen(tmpfile, "a+");
     if (!file) {
         VLOG_FATAL("%s: create failed (%s)", tmpfile, strerror(errno));
     }
 
+    error = lock_pidfile(file, F_SETLK);
+    if (error) {
+        /* Looks like we failed to acquire the lock.  Note that, if we failed
+         * for some other reason (and '!overwrite_pidfile'), we will have
+         * left 'tmpfile' as garbage in the file system. */
+        VLOG_FATAL("%s: fcntl(F_SETLK) failed (%s)", tmpfile, strerror(error));
+    }
+
+    if (!overwrite_pidfile) {
+        /* We acquired the lock.  Make sure to clean up on exit, and verify
+         * that we're allowed to create the actual pidfile. */
+        fatal_signal_add_file_to_unlink(tmpfile);
+        check_already_running();
+    }
+
     if (fstat(fileno(file), &s) == -1) {
         VLOG_FATAL("%s: fstat failed (%s)", tmpfile, strerror(errno));
     }
 
+    if (ftruncate(fileno(file), 0) == -1) {
+        VLOG_FATAL("%s: truncate failed (%s)", tmpfile, strerror(errno));
+    }
+
     fprintf(file, "%ld\n", pid);
     if (fflush(file) == EOF) {
         VLOG_FATAL("%s: write failed (%s)", tmpfile, strerror(errno));
     }
 
-    error = lock_pidfile(file, F_SETLK);
-    if (error) {
-        VLOG_FATAL("%s: fcntl(F_SETLK) failed (%s)", tmpfile, strerror(error));
-    }
+    error = rename(tmpfile, pidfile);
 
-    /* Rename or link it to the correct name. */
-    if (overwrite_pidfile) {
-        if (rename(tmpfile, pidfile) < 0) {
-            VLOG_FATAL("failed to rename \"%s\" to \"%s\" (%s)",
-                       tmpfile, pidfile, strerror(errno));
-        }
-    } else {
-        do {
-            error = link(tmpfile, pidfile) == -1 ? errno : 0;
-            if (error == EEXIST) {
-                check_already_running();
-            }
-        } while (error == EINTR || error == EEXIST);
-        if (error) {
-            VLOG_FATAL("failed to link \"%s\" as \"%s\" (%s)",
-                       tmpfile, pidfile, strerror(error));
-        }
+    /* Due to a race, 'tmpfile' may be owned by a different process, so we
+     * shouldn't delete it on exit. */
+    fatal_signal_remove_file_to_unlink(tmpfile);
+
+    if (error < 0) {
+        VLOG_FATAL("failed to rename \"%s\" to \"%s\" (%s)",
+                   tmpfile, pidfile, strerror(errno));
     }
 
     /* Ensure that the pidfile will get deleted on exit. */
     fatal_signal_add_file_to_unlink(pidfile);
 
-    /* Delete the temporary pidfile if it still exists. */
-    if (!overwrite_pidfile) {
-        error = fatal_signal_unlink_file_now(tmpfile);
-        if (error) {
-            VLOG_FATAL("%s: unlink failed (%s)", tmpfile, strerror(error));
-        }
-    }
-
     /* Clean up.
      *
      * We don't close 'file' because its file descriptor must remain open to
@@ -486,7 +492,9 @@ daemonize_start(void)
             /* Running in parent process. */
             exit(0);
         }
+
         /* Running in daemon or monitor process. */
+        setsid();
     }
 
     if (monitor) {
@@ -540,7 +548,6 @@ void
 daemonize_post_detach(void)
 {
     if (detach) {
-        setsid();
         if (chdir_) {
             ignore(chdir("/"));
         }
index 3a4a4e6..63d7afb 100644 (file)
 VLOG_DEFINE_THIS_MODULE(dpif_linux);
 enum { MAX_PORTS = USHRT_MAX };
 
-enum { N_CHANNELS = 17 };
-BUILD_ASSERT_DECL(IS_POW2(N_CHANNELS - 1));
-BUILD_ASSERT_DECL(N_CHANNELS > 1);
-BUILD_ASSERT_DECL(N_CHANNELS <= 32); /* We use a 32-bit word as a mask. */
-
 /* This ethtool flag was introduced in Linux 2.6.24, so it might be
  * missing if we have old headers. */
 #define ETH_FLAG_LRO      (1 << 15)    /* LRO is enabled */
@@ -131,76 +126,31 @@ static int dpif_linux_flow_transact(struct dpif_linux_flow *request,
 static void dpif_linux_flow_get_stats(const struct dpif_linux_flow *,
                                       struct dpif_flow_stats *);
 
-/* Packet drop monitoring.
- *
- * When kernel-to-user Netlink buffers overflow, the kernel notifies us that
- * one or more packets were dropped, but it doesn't tell us anything about
- * those packets.  However, the administrator really wants to know.  So we do
- * the next best thing, and keep track of the top sources of packets received
- * on each kernel-to-user channel, since the top sources are those that will
- * cause the buffers to overflow.
- *
- * We use a variation on the "Space-Saving" algorithm in Metwally et al.,
- * "Efficient Computation of Frequent and Top-k Elements in Data Streams", ACM
- * Transactions on Database Systems 31:3 (2006).  This algorithm yields
- * perfectly accurate results when the data stream's unique values (in this
- * case, port numbers) fit into our data structure, and degrades gracefully
- * even for challenging distributions (e.g. Zipf).
- *
- * Our implementation is very simple, without any of the special flourishes
- * described in the paper.  It avoids the need to use a hash for lookup by
- * keeping the constant factor (N_SKETCHES) very small.  The error calculations
- * in the paper make it sound like the results should still be satisfactory.
- *
- * "space-saving" and "Metwally" seem like awkward names for data structures,
- * so we call this a "sketch" even though technically that's a different sort
- * of summary structure.
- */
-
-/* One of N_SKETCHES counting elements per channel in the Metwally
- * "space-saving" algorithm. */
-enum { N_SKETCHES = 8 };        /* Number of elements per channel. */
-struct dpif_sketch {
-    uint32_t port_no;           /* Port number. */
-    unsigned int hits;          /* Number of hits. */
-    unsigned int error;         /* Upper bound on error in 'hits'. */
-};
-
-/* One of N_CHANNELS channels per dpif between the kernel and userspace. */
+/* One of the dpif channels between the kernel and userspace. */
 struct dpif_channel {
     struct nl_sock *sock;       /* Netlink socket. */
-    struct dpif_sketch sketches[N_SKETCHES]; /* From max to min 'hits'. */
     long long int last_poll;    /* Last time this channel was polled. */
 };
 
-static void update_sketch(struct dpif_channel *, uint32_t port_no);
-static void scale_sketches(struct dpif *);
 static void report_loss(struct dpif *, struct dpif_channel *);
 
-/* Interval, in milliseconds, at which to scale down the sketch values by a
- * factor of 2.  The Metwally algorithm doesn't do this, which makes sense in
- * the context it assumes, but in our situation we ought to weight recent data
- * more heavily than old data, so in my opinion this is reasonable. */
-#define SCALE_INTERVAL (60 * 1000)
-
 /* Datapath interface for the openvswitch Linux kernel module. */
 struct dpif_linux {
     struct dpif dpif;
     int dp_ifindex;
 
     /* Upcall messages. */
-    struct dpif_channel channels[N_CHANNELS];
-    uint32_t ready_mask;        /* 1-bit for each sock with unread messages. */
+    int uc_array_size;          /* Size of 'channels' and 'epoll_events'. */
+    struct dpif_channel *channels;
+    struct epoll_event *epoll_events;
     int epoll_fd;               /* epoll fd that includes channel socks. */
-    long long int next_scale;   /* Next time to scale down the sketches. */
+    int n_events;               /* Num events returned by epoll_wait(). */
+    int event_offset;           /* Offset into 'epoll_events'. */
 
     /* Change notification. */
     struct sset changed_ports;  /* Ports that have changed. */
     struct nln_notifier *port_notifier;
     bool change_error;
-
-    /* Port number allocation. */
-    uint16_t alloc_port_no;
 };
 
 static struct vlog_rate_limit error_rl = VLOG_RATE_LIMIT_INIT(9999, 5);
@@ -219,7 +169,7 @@ static int dpif_linux_init(void);
 static void open_dpif(const struct dpif_linux_dp *, struct dpif **);
 static bool dpif_linux_nln_parse(struct ofpbuf *, void *);
 static void dpif_linux_port_changed(const void *vport, void *dpif);
-static uint32_t dpif_linux_port_get_pid(const struct dpif *, uint16_t port_no);
+static uint32_t dpif_linux_port_get_pid(const struct dpif *, uint32_t port_no);
 
 static void dpif_linux_vport_to_ofpbuf(const struct dpif_linux_vport *,
                                        struct ofpbuf *);
@@ -303,8 +253,6 @@ open_dpif(const struct dpif_linux_dp *dp, struct dpif **dpifp)
     dpif_init(&dpif->dpif, &dpif_linux_class, dp->name,
               dp->dp_ifindex, dp->dp_ifindex);
 
-    dpif->next_scale = LLONG_MAX;
-
     dpif->dp_ifindex = dp->dp_ifindex;
     sset_init(&dpif->changed_ports);
     *dpifp = &dpif->dpif;
@@ -313,17 +261,108 @@ open_dpif(const struct dpif_linux_dp *dp, struct dpif **dpifp)
 static void
 destroy_channels(struct dpif_linux *dpif)
 {
-    struct dpif_channel *ch;
+    int i;
 
-    if (dpif->epoll_fd >= 0) {
-        close(dpif->epoll_fd);
-        dpif->epoll_fd = -1;
+    if (dpif->epoll_fd < 0) {
+        return;
     }
-    for (ch = dpif->channels; ch < &dpif->channels[N_CHANNELS]; ch++) {
+
+    for (i = 0; i < dpif->uc_array_size; i++ ) {
+        struct dpif_linux_vport vport_request;
+        struct dpif_channel *ch = &dpif->channels[i];
+        uint32_t upcall_pid = 0;
+
+        if (!ch->sock) {
+            continue;
+        }
+
+        /* Turn off upcalls. */
+        dpif_linux_vport_init(&vport_request);
+        vport_request.cmd = OVS_VPORT_CMD_SET;
+        vport_request.dp_ifindex = dpif->dp_ifindex;
+        vport_request.port_no = i;
+        vport_request.upcall_pid = &upcall_pid;
+        dpif_linux_vport_transact(&vport_request, NULL, NULL);
+
         nl_sock_destroy(ch->sock);
-        ch->sock = NULL;
     }
-    dpif->next_scale = LLONG_MAX;
+
+    free(dpif->channels);
+    dpif->channels = NULL;
+    dpif->uc_array_size = 0;
+
+    free(dpif->epoll_events);
+    dpif->epoll_events = NULL;
+    dpif->n_events = dpif->event_offset = 0;
+
+    close(dpif->epoll_fd);
+    dpif->epoll_fd = -1;
+}
+
+static int
+add_channel(struct dpif_linux *dpif, uint32_t port_no, struct nl_sock *sock)
+{
+    struct epoll_event event;
+
+    if (dpif->epoll_fd < 0) {
+        return 0;
+    }
+
+    /* We assume that the datapath densely chooses port numbers, which
+     * can therefore be used as an index into an array of channels. */
+    if (port_no >= dpif->uc_array_size) {
+        int new_size = port_no + 1;
+        int i;
+
+        if (new_size > 65535) {
+            VLOG_WARN_RL(&error_rl, "%s: datapath port %"PRIu32" too big",
+                         dpif_name(&dpif->dpif), port_no);
+            return EFBIG;
+        }
+
+        dpif->channels = xrealloc(dpif->channels,
+                                  new_size * sizeof *dpif->channels);
+        for (i = dpif->uc_array_size; i < new_size; i++) {
+            dpif->channels[i].sock = NULL;
+        }
+
+        dpif->epoll_events = xrealloc(dpif->epoll_events,
+                                      new_size * sizeof *dpif->epoll_events);
+        dpif->uc_array_size = new_size;
+    }
+
+    memset(&event, 0, sizeof event);
+    event.events = EPOLLIN;
+    event.data.u32 = port_no;
+    if (epoll_ctl(dpif->epoll_fd, EPOLL_CTL_ADD, nl_sock_fd(sock),
+                  &event) < 0) {
+        return errno;
+    }
+
+    dpif->channels[port_no].sock = sock;
+    dpif->channels[port_no].last_poll = LLONG_MIN;
+
+    return 0;
+}
+
+static void
+del_channel(struct dpif_linux *dpif, uint32_t port_no)
+{
+    struct dpif_channel *ch;
+
+    if (dpif->epoll_fd < 0 || port_no >= dpif->uc_array_size) {
+        return;
+    }
+
+    ch = &dpif->channels[port_no];
+    if (!ch->sock) {
+        return;
+    }
+
+    epoll_ctl(dpif->epoll_fd, EPOLL_CTL_DEL, nl_sock_fd(ch->sock), NULL);
+
+    nl_sock_destroy(ch->sock);
+    ch->sock = NULL;
 }
 
 static void
@@ -350,15 +389,8 @@ dpif_linux_destroy(struct dpif *dpif_)
 }
 
 static void
-dpif_linux_run(struct dpif *dpif_)
+dpif_linux_run(struct dpif *dpif_ OVS_UNUSED)
 {
-    struct dpif_linux *dpif = dpif_linux_cast(dpif_);
-
-    if (time_msec() >= dpif->next_scale) {
-        dpif->next_scale = time_msec() + SCALE_INTERVAL;
-        scale_sketches(dpif_);
-    }
-
     if (nln) {
         nln_run(nln);
     }
@@ -392,15 +424,24 @@ dpif_linux_get_stats(const struct dpif *dpif_, struct dpif_dp_stats *stats)
 
 static int
 dpif_linux_port_add(struct dpif *dpif_, struct netdev *netdev,
-                    uint16_t *port_nop)
+                    uint32_t *port_nop)
 {
     struct dpif_linux *dpif = dpif_linux_cast(dpif_);
     const char *name = netdev_get_name(netdev);
     const char *type = netdev_get_type(netdev);
     struct dpif_linux_vport request, reply;
     const struct ofpbuf *options;
+    struct nl_sock *sock = NULL;
+    uint32_t upcall_pid;
     struct ofpbuf *buf;
-    int error, i = 0, max_ports = MAX_PORTS;
+    int error;
+
+    if (dpif->epoll_fd >= 0) {
+        error = nl_sock_create(NETLINK_GENERIC, &sock);
+        if (error) {
+            return error;
+        }
+    }
 
     dpif_linux_vport_init(&request);
     request.cmd = OVS_VPORT_CMD_NEW;
@@ -410,6 +451,7 @@ dpif_linux_port_add(struct dpif *dpif_, struct netdev *netdev,
         VLOG_WARN_RL(&error_rl, "%s: cannot create port `%s' because it has "
                      "unsupported type `%s'",
                      dpif_name(dpif_), name, type);
+        nl_sock_destroy(sock);
         return EINVAL;
     }
     request.name = name;
@@ -424,39 +466,47 @@ dpif_linux_port_add(struct dpif *dpif_, struct netdev *netdev,
         netdev_linux_ethtool_set_flag(netdev, ETH_FLAG_LRO, "LRO", false);
     }
 
-    /* Unless a specific port was requested, loop until we find a port
-     * that isn't used. */
-    do {
-        uint32_t upcall_pid;
+    request.port_no = *port_nop;
+    upcall_pid = sock ? nl_sock_pid(sock) : 0;
+    request.upcall_pid = &upcall_pid;
 
-        request.port_no = *port_nop != UINT16_MAX ? *port_nop
-                          : ++dpif->alloc_port_no;
-        upcall_pid = dpif_linux_port_get_pid(dpif_, request.port_no);
-        request.upcall_pid = &upcall_pid;
-        error = dpif_linux_vport_transact(&request, &reply, &buf);
+    error = dpif_linux_vport_transact(&request, &reply, &buf);
+    if (!error) {
+        *port_nop = reply.port_no;
+        VLOG_DBG("%s: assigning port %"PRIu32" to netlink pid %"PRIu32,
+                 dpif_name(dpif_), reply.port_no, upcall_pid);
+    } else if (error == EBUSY && *port_nop != UINT32_MAX) {
+        VLOG_INFO("%s: requested port %"PRIu32" is in use",
+                  dpif_name(dpif_), *port_nop);
+        nl_sock_destroy(sock);
+        ofpbuf_delete(buf);
+        return error;
+    }
+    ofpbuf_delete(buf);
 
-        if (!error) {
-            *port_nop = reply.port_no;
-            VLOG_DBG("%s: assigning port %"PRIu32" to netlink pid %"PRIu32,
-                     dpif_name(dpif_), request.port_no, upcall_pid);
-        } else if (error == EFBIG) {
-            /* Older datapath has lower limit. */
-            max_ports = dpif->alloc_port_no;
-            dpif->alloc_port_no = 0;
-        } else if (error == EBUSY && *port_nop != UINT16_MAX) {
-            VLOG_INFO("%s: requested port %"PRIu16" is in use",
-                     dpif_name(dpif_), *port_nop);
-        }
+    if (sock) {
+        error = add_channel(dpif, *port_nop, sock);
+        if (error) {
+            VLOG_INFO("%s: could not add channel for port %s",
+                      dpif_name(dpif_), name);
 
-        ofpbuf_delete(buf);
-    } while ((*port_nop == UINT16_MAX) && (i++ < max_ports)
-             && (error == EBUSY || error == EFBIG));
+            /* Delete the port. */
+            dpif_linux_vport_init(&request);
+            request.cmd = OVS_VPORT_CMD_DEL;
+            request.dp_ifindex = dpif->dp_ifindex;
+            request.port_no = *port_nop;
+            dpif_linux_vport_transact(&request, NULL, NULL);
 
-    return error;
+            nl_sock_destroy(sock);
+            return error;
+        }
+    }
+
+    return 0;
 }
 
 static int
-dpif_linux_port_del(struct dpif *dpif_, uint16_t port_no)
+dpif_linux_port_del(struct dpif *dpif_, uint32_t port_no)
 {
     struct dpif_linux *dpif = dpif_linux_cast(dpif_);
     struct dpif_linux_vport vport;
@@ -468,6 +518,8 @@ dpif_linux_port_del(struct dpif *dpif_, uint16_t port_no)
     vport.port_no = port_no;
     error = dpif_linux_vport_transact(&vport, NULL, NULL);
 
+    del_channel(dpif, port_no);
+
     return error;
 }
 
@@ -492,7 +544,7 @@ dpif_linux_port_query__(const struct dpif *dpif, uint32_t port_no,
             /* A query by name reported that 'port_name' is in some datapath
              * other than 'dpif', but the caller wants to know about 'dpif'. */
             error = ENODEV;
-        } else {
+        } else if (dpif_port) {
             dpif_port->name = xstrdup(reply.name);
             dpif_port->type = xstrdup(netdev_vport_get_netdev_type(&reply));
             dpif_port->port_no = reply.port_no;
@@ -503,7 +555,7 @@ dpif_linux_port_query__(const struct dpif *dpif, uint32_t port_no,
 }
 
 static int
-dpif_linux_port_query_by_number(const struct dpif *dpif, uint16_t port_no,
+dpif_linux_port_query_by_number(const struct dpif *dpif, uint32_t port_no,
                                 struct dpif_port *dpif_port)
 {
     return dpif_linux_port_query__(dpif, port_no, NULL, dpif_port);
@@ -523,18 +575,16 @@ dpif_linux_get_max_ports(const struct dpif *dpif OVS_UNUSED)
 }
 
 static uint32_t
-dpif_linux_port_get_pid(const struct dpif *dpif_, uint16_t port_no)
+dpif_linux_port_get_pid(const struct dpif *dpif_, uint32_t port_no)
 {
     struct dpif_linux *dpif = dpif_linux_cast(dpif_);
 
     if (dpif->epoll_fd < 0) {
         return 0;
     } else {
-        int idx;
-
-        idx = (port_no != UINT16_MAX
-               ? 1 + (port_no & (N_CHANNELS - 2))
-               : 0);
+        /* The UINT32_MAX "reserved" port number uses the "ovs-system"'s
+         * channel, since it is not heavily loaded. */
+        int idx = (port_no >= dpif->uc_array_size) ? 0 : port_no;
         return nl_sock_pid(dpif->channels[idx].sock);
     }
 }
@@ -1032,35 +1082,6 @@ dpif_linux_operate(struct dpif *dpif, struct dpif_op **ops, size_t n_ops)
     }
 }
 
-static void
-set_upcall_pids(struct dpif *dpif_)
-{
-    struct dpif_linux *dpif = dpif_linux_cast(dpif_);
-    struct dpif_port_dump port_dump;
-    struct dpif_port port;
-    int error;
-
-    DPIF_PORT_FOR_EACH (&port, &port_dump, &dpif->dpif) {
-        uint32_t upcall_pid = dpif_linux_port_get_pid(dpif_, port.port_no);
-        struct dpif_linux_vport vport_request;
-
-        dpif_linux_vport_init(&vport_request);
-        vport_request.cmd = OVS_VPORT_CMD_SET;
-        vport_request.dp_ifindex = dpif->dp_ifindex;
-        vport_request.port_no = port.port_no;
-        vport_request.upcall_pid = &upcall_pid;
-        error = dpif_linux_vport_transact(&vport_request, NULL, NULL);
-        if (!error) {
-            VLOG_DBG("%s: assigning port %"PRIu32" to netlink pid %"PRIu32,
-                     dpif_name(&dpif->dpif), vport_request.port_no,
-                     upcall_pid);
-        } else {
-            VLOG_WARN_RL(&error_rl, "%s: failed to set upcall pid on port: %s",
-                         dpif_name(&dpif->dpif), strerror(error));
-        }
-    }
-}
-
 static int
 dpif_linux_recv_set(struct dpif *dpif_, bool enable)
 {
@@ -1073,44 +1094,61 @@ dpif_linux_recv_set(struct dpif *dpif_, bool enable)
     if (!enable) {
         destroy_channels(dpif);
     } else {
-        struct dpif_channel *ch;
-        int error;
+        struct dpif_port_dump port_dump;
+        struct dpif_port port;
 
-        dpif->epoll_fd = epoll_create(N_CHANNELS);
+        dpif->epoll_fd = epoll_create(10);
         if (dpif->epoll_fd < 0) {
             return errno;
         }
 
-        for (ch = dpif->channels; ch < &dpif->channels[N_CHANNELS]; ch++) {
-            int indx = ch - dpif->channels;
-            struct epoll_event event;
+        DPIF_PORT_FOR_EACH (&port, &port_dump, &dpif->dpif) {
+            struct dpif_linux_vport vport_request;
+            struct nl_sock *sock;
+            uint32_t upcall_pid;
+            int error;
 
-            error = nl_sock_create(NETLINK_GENERIC, &ch->sock);
+            error = nl_sock_create(NETLINK_GENERIC, &sock);
             if (error) {
-                destroy_channels(dpif);
                 return error;
             }
 
-            memset(&event, 0, sizeof event);
-            event.events = EPOLLIN;
-            event.data.u32 = indx;
-            if (epoll_ctl(dpif->epoll_fd, EPOLL_CTL_ADD, nl_sock_fd(ch->sock),
-                          &event) < 0) {
-                error = errno;
-                destroy_channels(dpif);
-                return error;
+            upcall_pid = nl_sock_pid(sock);
+
+            dpif_linux_vport_init(&vport_request);
+            vport_request.cmd = OVS_VPORT_CMD_SET;
+            vport_request.dp_ifindex = dpif->dp_ifindex;
+            vport_request.port_no = port.port_no;
+            vport_request.upcall_pid = &upcall_pid;
+            error = dpif_linux_vport_transact(&vport_request, NULL, NULL);
+            if (!error) {
+                VLOG_DBG("%s: assigning port %"PRIu32" to netlink pid %"PRIu32,
+                         dpif_name(&dpif->dpif), vport_request.port_no,
+                         upcall_pid);
+            } else {
+                VLOG_WARN_RL(&error_rl,
+                             "%s: failed to set upcall pid on port: %s",
+                             dpif_name(&dpif->dpif), strerror(error));
+                nl_sock_destroy(sock);
+
+                if (error == ENODEV || error == ENOENT) {
+                    /* This device isn't there, but keep trying the others. */
+                    continue;
+                } else {
+                    return error;
+                }
             }
 
-            memset(ch->sketches, 0, sizeof ch->sketches);
-            ch->last_poll = LLONG_MIN;
+            error = add_channel(dpif, port.port_no, sock);
+            if (error) {
+                VLOG_INFO("%s: could not add channel for port %s",
+                          dpif_name(dpif_), port.name);
+                nl_sock_destroy(sock);
+                return error;
+            }
         }
-
-        dpif->ready_mask = 0;
-        dpif->next_scale = time_msec() + SCALE_INTERVAL;
     }
 
-    set_upcall_pids(dpif_);
-
     return 0;
 }
 
@@ -1194,29 +1232,28 @@ dpif_linux_recv(struct dpif *dpif_, struct dpif_upcall *upcall,
        return EAGAIN;
     }
 
-    if (!dpif->ready_mask) {
-        struct epoll_event events[N_CHANNELS];
+    if (dpif->event_offset >= dpif->n_events) {
         int retval;
-        int i;
+
+        dpif->event_offset = dpif->n_events = 0;
 
         do {
-            retval = epoll_wait(dpif->epoll_fd, events, N_CHANNELS, 0);
+            retval = epoll_wait(dpif->epoll_fd, dpif->epoll_events,
+                                dpif->uc_array_size, 0);
         } while (retval < 0 && errno == EINTR);
         if (retval < 0) {
             static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
             VLOG_WARN_RL(&rl, "epoll_wait failed (%s)", strerror(errno));
-        }
-
-        for (i = 0; i < retval; i++) {
-            dpif->ready_mask |= 1u << events[i].data.u32;
+        } else if (retval > 0) {
+            dpif->n_events = retval;
         }
     }
 
-    while (dpif->ready_mask) {
-        int indx = ffs(dpif->ready_mask) - 1;
-        struct dpif_channel *ch = &dpif->channels[indx];
+    while (dpif->event_offset < dpif->n_events) {
+        int idx = dpif->epoll_events[dpif->event_offset].data.u32;
+        struct dpif_channel *ch = &dpif->channels[idx];
 
-        dpif->ready_mask &= ~(1u << indx);
+        dpif->event_offset++;
 
         for (;;) {
             int dp_ifindex;
@@ -1246,16 +1283,8 @@ dpif_linux_recv(struct dpif *dpif_, struct dpif_upcall *upcall,
 
             error = parse_odp_packet(buf, upcall, &dp_ifindex);
             if (!error && dp_ifindex == dpif->dp_ifindex) {
-                const struct nlattr *in_port;
-
-                in_port = nl_attr_find__(upcall->key, upcall->key_len,
-                                         OVS_KEY_ATTR_IN_PORT);
-                if (in_port) {
-                    update_sketch(ch, nl_attr_get_u32(in_port));
-                }
                 return 0;
-            }
-            if (error) {
+            } else if (error) {
                 return error;
             }
         }
@@ -1286,14 +1315,17 @@ dpif_linux_recv_purge(struct dpif *dpif_)
        return;
     }
 
-    for (ch = dpif->channels; ch < &dpif->channels[N_CHANNELS]; ch++) {
-        nl_sock_drain(ch->sock);
+    for (ch = dpif->channels; ch < &dpif->channels[dpif->uc_array_size]; ch++) {
+        if (ch->sock) {
+            nl_sock_drain(ch->sock);
+        }
     }
 }
 
 const struct dpif_class dpif_linux_class = {
     "system",
     dpif_linux_enumerate,
+    NULL,
     dpif_linux_open,
     dpif_linux_close,
     dpif_linux_destroy,
@@ -1388,33 +1420,6 @@ dpif_linux_is_internal_device(const char *name)
     return reply.type == OVS_VPORT_TYPE_INTERNAL;
 }
 
-int
-dpif_linux_vport_send(int dp_ifindex, uint32_t port_no,
-                      const void *data, size_t size)
-{
-    struct ofpbuf actions, key, packet;
-    struct odputil_keybuf keybuf;
-    struct dpif_execute execute;
-    struct flow flow;
-    uint64_t action;
-
-    ofpbuf_use_const(&packet, data, size);
-    flow_extract(&packet, 0, NULL, 0, &flow);
-
-    ofpbuf_use_stack(&key, &keybuf, sizeof keybuf);
-    odp_flow_key_from_flow(&key, &flow);
-
-    ofpbuf_use_stack(&actions, &action, sizeof action);
-    nl_msg_put_u32(&actions, OVS_ACTION_ATTR_OUTPUT, port_no);
-
-    execute.key = key.data;
-    execute.key_len = key.size;
-    execute.actions = actions.data;
-    execute.actions_len = actions.size;
-    execute.packet = &packet;
-    return dpif_linux_execute__(dp_ifindex, &execute);
-}
-
 static bool
 dpif_linux_nln_parse(struct ofpbuf *buf, void *vport_)
 {
@@ -1914,55 +1919,6 @@ dpif_linux_flow_get_stats(const struct dpif_linux_flow *flow,
     stats->tcp_flags = flow->tcp_flags ? *flow->tcp_flags : 0;
 }
 \f
-/* Metwally "space-saving" algorithm implementation. */
-
-/* Updates 'ch' to record that a packet was received on 'port_no'. */
-static void
-update_sketch(struct dpif_channel *ch, uint32_t port_no)
-{
-    struct dpif_sketch *sk;
-
-    /* Find an existing counting element for 'port_no' or, if none, replace the
-     * counting element with the fewest hits by 'port_no'. */
-    for (sk = ch->sketches; ; sk++) {
-        if (port_no == sk->port_no) {
-            break;
-        } else if (sk == &ch->sketches[N_SKETCHES - 1]) {
-            sk->port_no = port_no;
-            sk->error = sk->hits;
-            break;
-        }
-    }
-
-    /* Increment the hit count, then re-sort the counting elements (usually
-     * nothing needs to be done). */
-    sk->hits++;
-    while (sk > ch->sketches && sk[-1].hits > sk->hits) {
-        struct dpif_sketch tmp = sk[-1];
-        sk[-1] = *sk;
-        *sk = tmp;
-        sk--;
-    }
-}
-
-/* Divide the counts of all the the counting elements in 'dpif' by 2.  See the
- * comment on SCALE_INTERVAL. */
-static void
-scale_sketches(struct dpif *dpif_)
-{
-    struct dpif_linux *dpif = dpif_linux_cast(dpif_);
-    struct dpif_channel *ch;
-
-    for (ch = dpif->channels; ch < &dpif->channels[N_CHANNELS]; ch++) {
-        struct dpif_sketch *sk;
-
-        for (sk = ch->sketches; sk < &ch->sketches[N_SKETCHES]; sk++) {
-            sk->hits /= 2;
-            sk->error /= 2;
-        }
-    }
-}
-
 /* Logs information about a packet that was recently lost in 'ch' (in
  * 'dpif_'). */
 static void
@@ -1970,7 +1926,6 @@ report_loss(struct dpif *dpif_, struct dpif_channel *ch)
 {
     struct dpif_linux *dpif = dpif_linux_cast(dpif_);
     static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5);
-    struct dpif_sketch *sk;
     struct ds s;
 
     if (VLOG_DROP_ERR(&rl)) {
@@ -1982,25 +1937,6 @@ report_loss(struct dpif *dpif_, struct dpif_channel *ch)
         ds_put_format(&s, " (last polled %lld ms ago)",
                       time_msec() - ch->last_poll);
     }
-    ds_put_cstr(&s, ", most frequent sources are");
-    for (sk = ch->sketches; sk < &ch->sketches[N_SKETCHES]; sk++) {
-        if (sk->hits) {
-            struct dpif_port port;
-
-            ds_put_format(&s, " %"PRIu32, sk->port_no);
-            if (!dpif_port_query_by_number(dpif_, sk->port_no, &port)) {
-                ds_put_format(&s, "(%s)", port.name);
-                dpif_port_destroy(&port);
-            }
-            if (sk->error) {
-                ds_put_format(&s, ": %u to %u,",
-                              sk->hits - sk->error, sk->hits);
-            } else {
-                ds_put_format(&s, ": %u,", sk->hits);
-            }
-        }
-    }
-    ds_chomp(&s, ',');
 
     VLOG_WARN("%s: lost packet on channel %td%s",
               dpif_name(dpif_), ch - dpif->channels, ds_cstr(&s));
index c9150b0..966abc1 100644 (file)
@@ -56,7 +56,4 @@ int dpif_linux_vport_get(const char *name, struct dpif_linux_vport *reply,
 
 bool dpif_linux_is_internal_device(const char *name);
 
-int dpif_linux_vport_send(int dp_ifindex, uint32_t port_no,
-                          const void *data, size_t size);
-
 #endif /* dpif-linux.h */
index 9f7bb6e..193ffe4 100644 (file)
@@ -138,15 +138,15 @@ static struct shash dp_netdevs = SHASH_INITIALIZER(&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,
+static int get_port_by_number(struct dp_netdev *, uint32_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,
-                       const char *type, uint16_t port_no);
-static int do_del_port(struct dp_netdev *, uint16_t port_no);
+                       const char *type, uint32_t port_no);
+static int do_del_port(struct dp_netdev *, uint32_t port_no);
 static int dpif_netdev_open(const struct dpif_class *, const char *name,
                             bool create, struct dpif **);
 static int dp_netdev_output_userspace(struct dp_netdev *, const struct ofpbuf *,
@@ -181,6 +181,22 @@ dpif_netdev_enumerate(struct sset *all_dps)
     return 0;
 }
 
+static const char *
+dpif_netdev_port_open_type(const struct dpif_class *class, const char *type)
+{
+    return strcmp(type, "internal") ? type
+                  : class != &dpif_netdev_class ? "dummy"
+                  : "tap";
+}
+
+static const char *
+dpif_planetlab_port_open_type(const struct dpif_class *class, const char *type)
+{
+    return strcmp(type, "internal") ? type
+                  : class != &dpif_planetlab_class ? "dummy"
+                  : "pltap";
+}
+
 static struct dpif *
 create_dpif_netdev(struct dp_netdev *dp)
 {
@@ -197,6 +213,46 @@ create_dpif_netdev(struct dp_netdev *dp)
     return &dpif->dpif;
 }
 
+static int
+choose_port(struct dp_netdev *dp, const char *name)
+{
+    int port_no;
+
+    if (dp->class != &dpif_netdev_class && 
+        dp->class != &dpif_planetlab_class) {
+        const char *p;
+        int start_no = 0;
+
+        /* If the port name begins with "br", start the number search at
+         * 100 to make writing tests easier. */
+        if (!strncmp(name, "br", 2)) {
+            start_no = 100;
+        }
+
+        /* If the port name contains a number, try to assign that port number.
+         * This can make writing unit tests easier because port numbers are
+         * predictable. */
+        for (p = name; *p != '\0'; p++) {
+            if (isdigit((unsigned char) *p)) {
+                port_no = start_no + strtol(p, NULL, 10);
+                if (port_no > 0 && port_no < MAX_PORTS
+                    && !dp->ports[port_no]) {
+                    return port_no;
+                }
+                break;
+            }
+        }
+    }
+
+    for (port_no = 1; port_no < MAX_PORTS; port_no++) {
+        if (!dp->ports[port_no]) {
+            return port_no;
+        }
+    }
+
+    return -1;
+}
+
 static int
 create_dp_netdev(const char *name, const struct dpif_class *class,
                  struct dp_netdev **dpp)
@@ -214,6 +270,7 @@ create_dp_netdev(const char *name, const struct dpif_class *class,
     }
     hmap_init(&dp->flow_table);
     list_init(&dp->port_list);
+
     error = do_add_port(dp, name, "internal", OVSP_LOCAL);
     if (error) {
         dp_netdev_free(dp);
@@ -316,18 +373,9 @@ dpif_netdev_get_stats(const struct dpif *dpif, struct dpif_dp_stats *stats)
     return 0;
 }
 
-static const char* internal_port_type(const struct dp_netdev* dp)
-{
-       if (dp->class == &dpif_netdev_class)
-               return "tap";
-       if (dp->class == &dpif_planetlab_class)
-               return "pltap";
-       return "dummy";
-}
-
 static int
 do_add_port(struct dp_netdev *dp, const char *devname, const char *type,
-            uint16_t port_no)
+            uint32_t port_no)
 {
     struct dp_netdev_port *port;
     struct netdev *netdev;
@@ -338,7 +386,7 @@ do_add_port(struct dp_netdev *dp, const char *devname, const char *type,
     /* XXX reject devices already in some dp_netdev. */
 
     /* Open and validate network device. */
-    open_type = (strcmp(type, "internal") ? type : internal_port_type(dp));
+    open_type = dpif_netdev_port_open_type(dp->class, type);
     error = netdev_open(devname, open_type, &netdev);
     if (error) {
         return error;
@@ -377,49 +425,14 @@ do_add_port(struct dp_netdev *dp, const char *devname, const char *type,
     return 0;
 }
 
-static int
-choose_port(struct dpif *dpif, struct netdev *netdev)
-{
-    struct dp_netdev *dp = get_dp_netdev(dpif);
-    int port_no;
-
-    if (dpif->dpif_class != &dpif_netdev_class &&
-        dpif->dpif_class != &dpif_planetlab_class)
-    {
-        /* If the port name contains a number, try to assign that port number.
-         * This can make writing unit tests easier because port numbers are
-         * predictable. */
-        const char *p;
-
-        for (p = netdev_get_name(netdev); *p != '\0'; p++) {
-            if (isdigit((unsigned char) *p)) {
-                port_no = strtol(p, NULL, 10);
-                if (port_no > 0 && port_no < MAX_PORTS
-                    && !dp->ports[port_no]) {
-                    return port_no;
-                }
-                break;
-            }
-        }
-    }
-
-    for (port_no = 0; port_no < MAX_PORTS; port_no++) {
-        if (!dp->ports[port_no]) {
-            return port_no;
-        }
-    }
-
-    return -1;
-}
-
 static int
 dpif_netdev_port_add(struct dpif *dpif, struct netdev *netdev,
-                     uint16_t *port_nop)
+                     uint32_t *port_nop)
 {
     struct dp_netdev *dp = get_dp_netdev(dpif);
     int port_no;
 
-    if (*port_nop != UINT16_MAX) {
+    if (*port_nop != UINT32_MAX) {
         if (*port_nop >= MAX_PORTS) {
             return EFBIG;
         } else if (dp->ports[*port_nop]) {
@@ -427,7 +440,7 @@ dpif_netdev_port_add(struct dpif *dpif, struct netdev *netdev,
         }
         port_no = *port_nop;
     } else {
-        port_no = choose_port(dpif, netdev);
+        port_no = choose_port(dp, netdev_get_name(netdev));
     }
     if (port_no >= 0) {
         *port_nop = port_no;
@@ -438,21 +451,21 @@ dpif_netdev_port_add(struct dpif *dpif, struct netdev *netdev,
 }
 
 static int
-dpif_netdev_port_del(struct dpif *dpif, uint16_t port_no)
+dpif_netdev_port_del(struct dpif *dpif, uint32_t port_no)
 {
     struct dp_netdev *dp = get_dp_netdev(dpif);
     return port_no == OVSP_LOCAL ? EINVAL : do_del_port(dp, port_no);
 }
 
 static bool
-is_valid_port_number(uint16_t port_no)
+is_valid_port_number(uint32_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)
+                   uint32_t port_no, struct dp_netdev_port **portp)
 {
     if (!is_valid_port_number(port_no)) {
         *portp = NULL;
@@ -479,7 +492,7 @@ get_port_by_name(struct dp_netdev *dp,
 }
 
 static int
-do_del_port(struct dp_netdev *dp, uint16_t port_no)
+do_del_port(struct dp_netdev *dp, uint32_t port_no)
 {
     struct dp_netdev_port *port;
     char *name;
@@ -514,7 +527,7 @@ answer_port_query(const struct dp_netdev_port *port,
 }
 
 static int
-dpif_netdev_port_query_by_number(const struct dpif *dpif, uint16_t port_no,
+dpif_netdev_port_query_by_number(const struct dpif *dpif, uint32_t port_no,
                                  struct dpif_port *dpif_port)
 {
     struct dp_netdev *dp = get_dp_netdev(dpif);
@@ -522,7 +535,7 @@ dpif_netdev_port_query_by_number(const struct dpif *dpif, uint16_t port_no,
     int error;
 
     error = get_port_by_number(dp, port_no, &port);
-    if (!error) {
+    if (!error && dpif_port) {
         answer_port_query(port, dpif_port);
     }
     return error;
@@ -537,7 +550,7 @@ dpif_netdev_port_query_by_name(const struct dpif *dpif, const char *devname,
     int error;
 
     error = get_port_by_name(dp, devname, &port);
-    if (!error) {
+    if (!error && dpif_port) {
         answer_port_query(port, dpif_port);
     }
     return error;
@@ -666,7 +679,7 @@ static int
 dpif_netdev_flow_from_nlattrs(const struct nlattr *key, uint32_t key_len,
                               struct flow *flow)
 {
-    if (odp_flow_key_to_flow(key, key_len, flow)) {
+    if (odp_flow_key_to_flow(key, key_len, flow) != ODP_FIT_PERFECT) {
         /* This should not happen: it indicates that odp_flow_key_from_flow()
          * and odp_flow_key_to_flow() disagree on the acceptable form of a
          * flow.  Log the problem as an error, with enough details to enable
@@ -875,7 +888,7 @@ dpif_netdev_flow_dump_next(const struct dpif *dpif, void *state_,
         struct ofpbuf buf;
 
         ofpbuf_use_stack(&buf, &state->keybuf, sizeof state->keybuf);
-        odp_flow_key_from_flow(&buf, &flow->key);
+        odp_flow_key_from_flow(&buf, &flow->key, flow->key.in_port);
 
         *key = buf.data;
         *key_len = buf.size;
@@ -925,7 +938,7 @@ dpif_netdev_execute(struct dpif *dpif, const struct dpif_execute *execute)
     ofpbuf_reserve(&copy, DP_NETDEV_HEADROOM);
     ofpbuf_put(&copy, execute->packet->data, execute->packet->size);
 
-    flow_extract(&copy, 0, NULL, -1, &key);
+    flow_extract(&copy, 0, 0, NULL, -1, &key);
     error = dpif_netdev_flow_from_nlattrs(execute->key, execute->key_len,
                                           &key);
     if (!error) {
@@ -1023,7 +1036,7 @@ dp_netdev_port_input(struct dp_netdev *dp, struct dp_netdev_port *port,
     if (packet->size < ETH_HEADER_LEN) {
         return;
     }
-    flow_extract(packet, 0, NULL, odp_port_to_ofp_port(port->port_no), &key);
+    flow_extract(packet, 0, 0, NULL, port->port_no, &key);
     flow = dp_netdev_lookup_flow(dp, &key);
     if (flow) {
         dp_netdev_flow_used(flow, packet);
@@ -1086,7 +1099,7 @@ dp_netdev_set_dl(struct ofpbuf *packet, const struct ovs_key_ethernet *eth_key)
 
 static void
 dp_netdev_output_port(struct dp_netdev *dp, struct ofpbuf *packet,
-                      uint16_t out_port)
+                      uint32_t out_port)
 {
     struct dp_netdev_port *p = dp->ports[out_port];
     if (p) {
@@ -1113,7 +1126,7 @@ dp_netdev_output_userspace(struct dp_netdev *dp, const struct ofpbuf *packet,
 
     buf = &u->buf;
     ofpbuf_init(buf, ODPUTIL_FLOW_KEY_BYTES + 2 + packet->size);
-    odp_flow_key_from_flow(buf, flow);
+    odp_flow_key_from_flow(buf, flow, flow->in_port);
     key_len = buf->size;
     ofpbuf_pull(buf, key_len);
     ofpbuf_reserve(buf, 2);
@@ -1181,13 +1194,14 @@ execute_set_action(struct ofpbuf *packet, const struct nlattr *a)
 {
     enum ovs_key_attr type = nl_attr_type(a);
     const struct ovs_key_ipv4 *ipv4_key;
+    const struct ovs_key_ipv6 *ipv6_key;
     const struct ovs_key_tcp *tcp_key;
     const struct ovs_key_udp *udp_key;
 
     switch (type) {
     case OVS_KEY_ATTR_TUN_ID:
     case OVS_KEY_ATTR_PRIORITY:
-    case OVS_KEY_ATTR_IPV6:
+    case OVS_KEY_ATTR_SKB_MARK:
     case OVS_KEY_ATTR_IPV4_TUNNEL:
         /* not implemented */
         break;
@@ -1203,6 +1217,13 @@ execute_set_action(struct ofpbuf *packet, const struct nlattr *a)
                         ipv4_key->ipv4_tos, ipv4_key->ipv4_ttl);
         break;
 
+    case OVS_KEY_ATTR_IPV6:
+        ipv6_key = nl_attr_get_unspec(a, sizeof(struct ovs_key_ipv6));
+        packet_set_ipv6(packet, ipv6_key->ipv6_proto, ipv6_key->ipv6_src,
+                        ipv6_key->ipv6_dst, ipv6_key->ipv6_tclass,
+                        ipv6_key->ipv6_label, ipv6_key->ipv6_hlimit);
+        break;
+
     case OVS_KEY_ATTR_TCP:
         tcp_key = nl_attr_get_unspec(a, sizeof(struct ovs_key_tcp));
         packet_set_tcp_port(packet, tcp_key->tcp_src, tcp_key->tcp_dst);
@@ -1274,8 +1295,9 @@ dp_netdev_execute_actions(struct dp_netdev *dp,
     }
 }
 
-#define DPIF_NETDEV_CLASS_FUNCTIONS                    \
+#define DPIF_NETDEV_CLASS_FUNCTIONS(PORT_OPEN_TYPE)    \
     dpif_netdev_enumerate,                             \
+    PORT_OPEN_TYPE,                                    \
     dpif_netdev_open,                                  \
     dpif_netdev_close,                                 \
     dpif_netdev_destroy,                               \
@@ -1310,12 +1332,12 @@ dp_netdev_execute_actions(struct dp_netdev *dp,
 
 const struct dpif_class dpif_netdev_class = {
     "netdev",
-    DPIF_NETDEV_CLASS_FUNCTIONS
+    DPIF_NETDEV_CLASS_FUNCTIONS(dpif_netdev_port_open_type)
 };
 
 const struct dpif_class dpif_planetlab_class = {
     "planetlab",
-    DPIF_NETDEV_CLASS_FUNCTIONS
+    DPIF_NETDEV_CLASS_FUNCTIONS(dpif_planetlab_port_open_type)
 };
 
 static void
index 70fb9cd..44e946c 100644 (file)
@@ -80,6 +80,17 @@ struct dpif_class {
      * case this function may be a null pointer. */
     int (*enumerate)(struct sset *all_dps);
 
+    /* Returns the type to pass to netdev_open() when a dpif of class
+     * 'dpif_class' has a port of type 'type', for a few special cases
+     * when a netdev type differs from a port type.  For example, when
+     * using the userspace datapath, a port of type "internal" needs to
+     * be opened as "tap".
+     *
+     * Returns either 'type' itself or a string literal, which must not
+     * be freed. */
+    const char *(*port_open_type)(const struct dpif_class *dpif_class,
+                                  const char *type);
+
     /* Attempts to open an existing dpif called 'name', if 'create' is false,
      * or to open an existing dpif or create a new one, if 'create' is true.
      *
@@ -111,23 +122,25 @@ struct dpif_class {
     int (*get_stats)(const struct dpif *dpif, struct dpif_dp_stats *stats);
 
     /* Adds 'netdev' as a new port in 'dpif'.  If '*port_no' is not
-     * UINT16_MAX, attempts to use that as the port's port number.
+     * UINT32_MAX, attempts to use that as the port's port number.
      *
      * If port is successfully added, sets '*port_no' to the new port's
      * port number.  Returns EBUSY if caller attempted to choose a port
      * number, and it was in use. */
     int (*port_add)(struct dpif *dpif, struct netdev *netdev,
-                    uint16_t *port_no);
+                    uint32_t *port_no);
 
     /* Removes port numbered 'port_no' from 'dpif'. */
-    int (*port_del)(struct dpif *dpif, uint16_t port_no);
+    int (*port_del)(struct dpif *dpif, uint32_t port_no);
 
-    /* Queries 'dpif' for a port with the given 'port_no' or 'devname'.  Stores
-     * information about the port into '*port' if successful.
+    /* Queries 'dpif' for a port with the given 'port_no' or 'devname'.
+     * If 'port' is not null, stores information about the port into
+     * '*port' if successful.
      *
-     * The caller takes ownership of data in 'port' and must free it with
-     * dpif_port_destroy() when it is no longer needed. */
-    int (*port_query_by_number)(const struct dpif *dpif, uint16_t port_no,
+     * If 'port' is not null, the caller takes ownership of data in
+     * 'port' and must free it with dpif_port_destroy() when it is no
+     * longer needed. */
+    int (*port_query_by_number)(const struct dpif *dpif, uint32_t port_no,
                                 struct dpif_port *port);
     int (*port_query_by_name)(const struct dpif *dpif, const char *devname,
                               struct dpif_port *port);
@@ -140,7 +153,7 @@ struct dpif_class {
      * actions as the OVS_USERSPACE_ATTR_PID attribute's value, for use in
      * flows whose packets arrived on port 'port_no'.
      *
-     * A 'port_no' of UINT16_MAX should be treated as a special case.  The
+     * A 'port_no' of UINT32_MAX should be treated as a special case.  The
      * implementation should return a reserved PID, not allocated to any port,
      * that the client may use for special purposes.
      *
@@ -150,7 +163,7 @@ struct dpif_class {
      *
      * A dpif provider that doesn't have meaningful Netlink PIDs can use NULL
      * for this function.  This is equivalent to always returning 0. */
-    uint32_t (*port_get_pid)(const struct dpif *dpif, uint16_t port_no);
+    uint32_t (*port_get_pid)(const struct dpif *dpif, uint32_t port_no);
 
     /* Attempts to begin dumping the ports in a dpif.  On success, returns 0
      * and initializes '*statep' with any data needed for iteration.  On
index 68af298..18ef790 100644 (file)
@@ -373,6 +373,13 @@ dpif_base_name(const struct dpif *dpif)
     return dpif->base_name;
 }
 
+/* Returns the type of datapath 'dpif'. */
+const char *
+dpif_type(const struct dpif *dpif)
+{
+    return dpif->dpif_class->type;
+}
+
 /* Returns the fully spelled out name for the given datapath 'type'.
  *
  * Normalized type string can be compared with strcmp().  Unnormalized type
@@ -411,19 +418,36 @@ dpif_get_dp_stats(const struct dpif *dpif, struct dpif_dp_stats *stats)
     return error;
 }
 
+const char *
+dpif_port_open_type(const char *datapath_type, const char *port_type)
+{
+    struct registered_dpif_class *registered_class;
+
+    datapath_type = dpif_normalize_type(datapath_type);
+
+    registered_class = shash_find_data(&dpif_classes, datapath_type);
+    if (!registered_class
+            || !registered_class->dpif_class->port_open_type) {
+        return port_type;
+    }
+
+    return registered_class->dpif_class->port_open_type(
+                          registered_class->dpif_class, port_type);
+}
+
 /* Attempts to add 'netdev' as a port on 'dpif'.  If 'port_nop' is
- * non-null and its value is not UINT16_MAX, then attempts to use the
+ * non-null and its value is not UINT32_MAX, then attempts to use the
  * value as the port number.
  *
  * 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
+ * errno value and sets '*port_nop' to UINT32_MAX (if 'port_nop' is
  * non-null). */
 int
-dpif_port_add(struct dpif *dpif, struct netdev *netdev, uint16_t *port_nop)
+dpif_port_add(struct dpif *dpif, struct netdev *netdev, uint32_t *port_nop)
 {
     const char *netdev_name = netdev_get_name(netdev);
-    uint16_t port_no = UINT16_MAX;
+    uint32_t port_no = UINT32_MAX;
     int error;
 
     COVERAGE_INC(dpif_port_add);
@@ -434,12 +458,12 @@ dpif_port_add(struct dpif *dpif, struct netdev *netdev, uint16_t *port_nop)
 
     error = dpif->dpif_class->port_add(dpif, netdev, &port_no);
     if (!error) {
-        VLOG_DBG_RL(&dpmsg_rl, "%s: added %s as port %"PRIu16,
+        VLOG_DBG_RL(&dpmsg_rl, "%s: added %s as port %"PRIu32,
                     dpif_name(dpif), netdev_name, port_no);
     } else {
         VLOG_WARN_RL(&error_rl, "%s: failed to add %s as port: %s",
                      dpif_name(dpif), netdev_name, strerror(error));
-        port_no = UINT16_MAX;
+        port_no = UINT32_MAX;
     }
     if (port_nop) {
         *port_nop = port_no;
@@ -450,7 +474,7 @@ dpif_port_add(struct dpif *dpif, struct netdev *netdev, uint16_t *port_nop)
 /* 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)
+dpif_port_del(struct dpif *dpif, uint32_t port_no)
 {
     int error;
 
@@ -458,7 +482,7 @@ dpif_port_del(struct dpif *dpif, uint16_t port_no)
 
     error = dpif->dpif_class->port_del(dpif, port_no);
     if (!error) {
-        VLOG_DBG_RL(&dpmsg_rl, "%s: port_del(%"PRIu16")",
+        VLOG_DBG_RL(&dpmsg_rl, "%s: port_del(%"PRIu32")",
                     dpif_name(dpif), port_no);
     } else {
         log_operation(dpif, "port_del", error);
@@ -487,6 +511,20 @@ dpif_port_destroy(struct dpif_port *dpif_port)
     free(dpif_port->type);
 }
 
+/* Checks if port named 'devname' exists in 'dpif'.  If so, returns
+ * true; otherwise, returns false. */
+bool
+dpif_port_exists(const struct dpif *dpif, const char *devname)
+{
+    int error = dpif->dpif_class->port_query_by_name(dpif, devname, NULL);
+    if (error != 0 && error != ENOENT && error != ENODEV) {
+        VLOG_WARN_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
  * initializes '*port' appropriately; on failure, returns a positive errno
  * value.
@@ -494,16 +532,16 @@ dpif_port_destroy(struct dpif_port *dpif_port)
  * The caller owns the data in 'port' and must free it with
  * dpif_port_destroy() when it is no longer needed. */
 int
-dpif_port_query_by_number(const struct dpif *dpif, uint16_t port_no,
+dpif_port_query_by_number(const struct dpif *dpif, uint32_t port_no,
                           struct dpif_port *port)
 {
     int error = dpif->dpif_class->port_query_by_number(dpif, port_no, port);
     if (!error) {
-        VLOG_DBG_RL(&dpmsg_rl, "%s: port %"PRIu16" is device %s",
+        VLOG_DBG_RL(&dpmsg_rl, "%s: port %"PRIu32" is device %s",
                     dpif_name(dpif), port_no, port->name);
     } else {
         memset(port, 0, sizeof *port);
-        VLOG_WARN_RL(&error_rl, "%s: failed to query port %"PRIu16": %s",
+        VLOG_WARN_RL(&error_rl, "%s: failed to query port %"PRIu32": %s",
                      dpif_name(dpif), port_no, strerror(error));
     }
     return error;
@@ -521,7 +559,7 @@ dpif_port_query_by_name(const struct dpif *dpif, const char *devname,
 {
     int error = dpif->dpif_class->port_query_by_name(dpif, devname, port);
     if (!error) {
-        VLOG_DBG_RL(&dpmsg_rl, "%s: device %s is on port %"PRIu16,
+        VLOG_DBG_RL(&dpmsg_rl, "%s: device %s is on port %"PRIu32,
                     dpif_name(dpif), devname, port->port_no);
     } else {
         memset(port, 0, sizeof *port);
@@ -550,7 +588,7 @@ dpif_get_max_ports(const struct dpif *dpif)
  * as the OVS_USERSPACE_ATTR_PID attribute's value, for use in flows whose
  * packets arrived on port 'port_no'.
  *
- * A 'port_no' of UINT16_MAX is a special case: it returns a reserved PID, not
+ * A 'port_no' of UINT32_MAX is a special case: it returns a reserved PID, not
  * allocated to any port, that the client may use for special purposes.
  *
  * The return value is only meaningful when DPIF_UC_ACTION has been enabled in
@@ -559,7 +597,7 @@ dpif_get_max_ports(const struct dpif *dpif)
  * update all of the flows that it installed that contain
  * OVS_ACTION_ATTR_USERSPACE actions. */
 uint32_t
-dpif_port_get_pid(const struct dpif *dpif, uint16_t port_no)
+dpif_port_get_pid(const struct dpif *dpif, uint32_t port_no)
 {
     return (dpif->dpif_class->port_get_pid
             ? (dpif->dpif_class->port_get_pid)(dpif, port_no)
@@ -571,7 +609,7 @@ dpif_port_get_pid(const struct dpif *dpif, uint16_t port_no)
  * 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,
+dpif_port_get_name(struct dpif *dpif, uint32_t port_no,
                    char *name, size_t name_size)
 {
     struct dpif_port port;
index 45c78a5..a478db2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * limitations under the License.
  */
 
-
+/*
+ * dpif, the DataPath InterFace.
+ *
+ * In Open vSwitch terminology, a "datapath" is a flow-based software switch.
+ * A datapath has no intelligence of its own.  Rather, it relies entirely on
+ * its client to set up flows.  The datapath layer is core to the Open vSwitch
+ * software switch: one could say, without much exaggeration, that everything
+ * in ovs-vswitchd above dpif exists only to make the correct decisions
+ * interacting with dpif.
+ *
+ * Typically, the client of a datapath is the software switch module in
+ * "ovs-vswitchd", but other clients can be written.  The "ovs-dpctl" utility
+ * is also a (simple) client.
+ *
+ *
+ * Overview
+ * ========
+ *
+ * The terms written in quotes below are defined in later sections.
+ *
+ * When a datapath "port" receives a packet, it extracts the headers (the
+ * "flow").  If the datapath's "flow table" contains a "flow entry" whose flow
+ * is the same as the packet's, then it executes the "actions" in the flow
+ * entry and increments the flow's statistics.  If there is no matching flow
+ * entry, the datapath instead appends the packet to an "upcall" queue.
+ *
+ *
+ * Ports
+ * =====
+ *
+ * A datapath has a set of ports that are analogous to the ports on an Ethernet
+ * switch.  At the datapath level, each port has the following information
+ * associated with it:
+ *
+ *    - A name, a short string that must be unique within the host.  This is
+ *      typically a name that would be familiar to the system administrator,
+ *      e.g. "eth0" or "vif1.1", but it is otherwise arbitrary.
+ *
+ *    - A 32-bit port number that must be unique within the datapath but is
+ *      otherwise arbitrary.  The port number is the most important identifier
+ *      for a port in the datapath interface.
+ *
+ *    - A type, a short string that identifies the kind of port.  On a Linux
+ *      host, typical types are "system" (for a network device such as eth0),
+ *      "internal" (for a simulated port used to connect to the TCP/IP stack),
+ *      and "gre" (for a GRE tunnel).
+ *
+ *    - A Netlink PID (see "Upcall Queuing and Ordering" below).
+ *
+ * The dpif interface has functions for adding and deleting ports.  When a
+ * datapath implements these (e.g. as the Linux and netdev datapaths do), then
+ * Open vSwitch's ovs-vswitchd daemon can directly control what ports are used
+ * for switching.  Some datapaths might not implement them, or implement them
+ * with restrictions on the types of ports that can be added or removed
+ * (e.g. on ESX), on systems where port membership can only be changed by some
+ * external entity.
+ *
+ * Each datapath must have a port, sometimes called the "local port", whose
+ * name is the same as the datapath itself, with port number 0.  The local port
+ * cannot be deleted.
+ *
+ * Ports are available as "struct netdev"s.  To obtain a "struct netdev *" for
+ * a port named 'name' with type 'port_type', in a datapath of type
+ * 'datapath_type', call netdev_open(name, dpif_port_open_type(datapath_type,
+ * port_type).  The netdev can be used to get and set important data related to
+ * the port, such as:
+ *
+ *    - MTU (netdev_get_mtu(), netdev_set_mtu()).
+ *
+ *    - Ethernet address (netdev_get_etheraddr(), netdev_set_etheraddr()).
+ *
+ *    - Statistics such as the number of packets and bytes transmitted and
+ *      received (netdev_get_stats()).
+ *
+ *    - Carrier status (netdev_get_carrier()).
+ *
+ *    - Speed (netdev_get_features()).
+ *
+ *    - QoS queue configuration (netdev_get_queue(), netdev_set_queue() and
+ *      related functions.)
+ *
+ *    - Arbitrary port-specific configuration parameters (netdev_get_config(),
+ *      netdev_set_config()).  An example of such a parameter is the IP
+ *      endpoint for a GRE tunnel.
+ *
+ *
+ * Flow Table
+ * ==========
+ *
+ * The flow table is a hash table of "flow entries".  Each flow entry contains:
+ *
+ *    - A "flow", that is, a summary of the headers in an Ethernet packet.  The
+ *      flow is the hash key and thus must be unique within the flow table.
+ *      Flows are fine-grained entities that include L2, L3, and L4 headers.  A
+ *      single TCP connection consists of two flows, one in each direction.
+ *
+ *      In Open vSwitch userspace, "struct flow" is the typical way to describe
+ *      a flow, but the datapath interface uses a different data format to
+ *      allow ABI forward- and backward-compatibility.  datapath/README
+ *      describes the rationale and design.  Refer to OVS_KEY_ATTR_* and
+ *      "struct ovs_key_*" in include/linux/openvswitch.h for details.
+ *      lib/odp-util.h defines several functions for working with these flows.
+ *
+ *      (In case you are familiar with OpenFlow, datapath flows are analogous
+ *      to OpenFlow flow matches.  The most important difference is that
+ *      OpenFlow allows fields to be wildcarded and prioritized, whereas a
+ *      datapath's flow table is a hash table so every flow must be
+ *      exact-match, thus without priorities.)
+ *
+ *    - A list of "actions" that tell the datapath what to do with packets
+ *      within a flow.  Some examples of actions are OVS_ACTION_ATTR_OUTPUT,
+ *      which transmits the packet out a port, and OVS_ACTION_ATTR_SET, which
+ *      modifies packet headers.  Refer to OVS_ACTION_ATTR_* and "struct
+ *      ovs_action_*" in include/linux/openvswitch.h for details.
+ *      lib/odp-util.h defines several functions for working with datapath
+ *      actions.
+ *
+ *      The actions list may be empty.  This indicates that nothing should be
+ *      done to matching packets, that is, they should be dropped.
+ *
+ *      (In case you are familiar with OpenFlow, datapath actions are analogous
+ *      to OpenFlow actions.)
+ *
+ *    - Statistics: the number of packets and bytes that the flow has
+ *      processed, the last time that the flow processed a packet, and the
+ *      union of all the TCP flags in packets processed by the flow.  (The
+ *      latter is 0 if the flow is not a TCP flow.)
+ *
+ * The datapath's client manages the flow table, primarily in reaction to
+ * "upcalls" (see below).
+ *
+ *
+ * Upcalls
+ * =======
+ *
+ * A datapath sometimes needs to notify its client that a packet was received.
+ * The datapath mechanism to do this is called an "upcall".
+ *
+ * Upcalls are used in two situations:
+ *
+ *    - When a packet is received, but there is no matching flow entry in its
+ *      flow table (a flow table "miss"), this causes an upcall of type
+ *      DPIF_UC_MISS.  These are called "miss" upcalls.
+ *
+ *    - A datapath action of type OVS_ACTION_ATTR_USERSPACE causes an upcall of
+ *      type DPIF_UC_ACTION.  These are called "action" upcalls.
+ *
+ * An upcall contains an entire packet.  There is no attempt to, e.g., copy
+ * only as much of the packet as normally needed to make a forwarding decision.
+ * Such an optimization is doable, but experimental prototypes showed it to be
+ * of little benefit because an upcall typically contains the first packet of a
+ * flow, which is usually short (e.g. a TCP SYN).  Also, the entire packet can
+ * sometimes really be needed.
+ *
+ * After a client reads a given upcall, the datapath is finished with it, that
+ * is, the datapath doesn't maintain any lingering state past that point.
+ *
+ * The latency from the time that a packet arrives at a port to the time that
+ * it is received from dpif_recv() is critical in some benchmarks.  For
+ * example, if this latency is 1 ms, then a netperf TCP_CRR test, which opens
+ * and closes TCP connections one at a time as quickly as it can, cannot
+ * possibly achieve more than 500 transactions per second, since every
+ * connection consists of two flows with 1-ms latency to set up each one.
+ *
+ * To receive upcalls, a client has to enable them with dpif_recv_set().  A
+ * datapath should generally support multiple clients at once (e.g. so that one
+ * may run "ovs-dpctl show" or "ovs-dpctl dump-flows" while "ovs-vswitchd" is
+ * also running) but need not support multiple clients enabling upcalls at
+ * once.
+ *
+ *
+ * Upcall Queuing and Ordering
+ * ---------------------------
+ *
+ * The datapath's client reads upcalls one at a time by calling dpif_recv().
+ * When more than one upcall is pending, the order in which the datapath
+ * presents upcalls to its client is important.  The datapath's client does not
+ * directly control this order, so the datapath implementer must take care
+ * during design.
+ *
+ * The minimal behavior, suitable for initial testing of a datapath
+ * implementation, is that all upcalls are appended to a single queue, which is
+ * delivered to the client in order.
+ *
+ * The datapath should ensure that a high rate of upcalls from one particular
+ * port cannot cause upcalls from other sources to be dropped or unreasonably
+ * delayed.  Otherwise, one port conducting a port scan or otherwise initiating
+ * high-rate traffic spanning many flows could suppress other traffic.
+ * Ideally, the datapath should present upcalls from each port in a "round
+ * robin" manner, to ensure fairness.
+ *
+ * The client has no control over "miss" upcalls and no insight into the
+ * datapath's implementation, so the datapath is entirely responsible for
+ * queuing and delivering them.  On the other hand, the datapath has
+ * considerable freedom of implementation.  One good approach is to maintain a
+ * separate queue for each port, to prevent any given port's upcalls from
+ * interfering with other ports' upcalls.  If this is impractical, then another
+ * reasonable choice is to maintain some fixed number of queues and assign each
+ * port to one of them.  Ports assigned to the same queue can then interfere
+ * with each other, but not with ports assigned to different queues.  Other
+ * approaches are also possible.
+ *
+ * The client has some control over "action" upcalls: it can specify a 32-bit
+ * "Netlink PID" as part of the action.  This terminology comes from the Linux
+ * datapath implementation, which uses a protocol called Netlink in which a PID
+ * designates a particular socket and the upcall data is delivered to the
+ * socket's receive queue.  Generically, though, a Netlink PID identifies a
+ * queue for upcalls.  The basic requirements on the datapath are:
+ *
+ *    - The datapath must provide a Netlink PID associated with each port.  The
+ *      client can retrieve the PID with dpif_port_get_pid().
+ *
+ *    - The datapath must provide a "special" Netlink PID not associated with
+ *      any port.  dpif_port_get_pid() also provides this PID.  (ovs-vswitchd
+ *      uses this PID to queue special packets that must not be lost even if a
+ *      port is otherwise busy, such as packets used for tunnel monitoring.)
+ *
+ * The minimal behavior of dpif_port_get_pid() and the treatment of the Netlink
+ * PID in "action" upcalls is that dpif_port_get_pid() returns a constant value
+ * and all upcalls are appended to a single queue.
+ *
+ * The ideal behavior is:
+ *
+ *    - Each port has a PID that identifies the queue used for "miss" upcalls
+ *      on that port.  (Thus, if each port has its own queue for "miss"
+ *      upcalls, then each port has a different Netlink PID.)
+ *
+ *    - "miss" upcalls for a given port and "action" upcalls that specify that
+ *      port's Netlink PID add their upcalls to the same queue.  The upcalls
+ *      are delivered to the datapath's client in the order that the packets
+ *      were received, regardless of whether the upcalls are "miss" or "action"
+ *      upcalls.
+ *
+ *    - Upcalls that specify the "special" Netlink PID are queued separately.
+ *
+ *
+ * Packet Format
+ * =============
+ *
+ * The datapath interface works with packets in a particular form.  This is the
+ * form taken by packets received via upcalls (i.e. by dpif_recv()).  Packets
+ * supplied to the datapath for processing (i.e. to dpif_execute()) also take
+ * this form.
+ *
+ * A VLAN tag is represented by an 802.1Q header.  If the layer below the
+ * datapath interface uses another representation, then the datapath interface
+ * must perform conversion.
+ *
+ * The datapath interface requires all packets to fit within the MTU.  Some
+ * operating systems internally process packets larger than MTU, with features
+ * such as TSO and UFO.  When such a packet passes through the datapath
+ * interface, it must be broken into multiple MTU or smaller sized packets for
+ * presentation as upcalls.  (This does not happen often, because an upcall
+ * typically contains the first packet of a flow, which is usually short.)
+ *
+ * Some operating system TCP/IP stacks maintain packets in an unchecksummed or
+ * partially checksummed state until transmission.  The datapath interface
+ * requires all host-generated packets to be fully checksummed (e.g. IP and TCP
+ * checksums must be correct).  On such an OS, the datapath interface must fill
+ * in these checksums.
+ *
+ * Packets passed through the datapath interface must be at least 14 bytes
+ * long, that is, they must have a complete Ethernet header.  They are not
+ * required to be padded to the minimum Ethernet length.
+ *
+ *
+ * Typical Usage
+ * =============
+ *
+ * Typically, the client of a datapath begins by configuring the datapath with
+ * a set of ports.  Afterward, the client runs in a loop polling for upcalls to
+ * arrive.
+ *
+ * For each upcall received, the client examines the enclosed packet and
+ * figures out what should be done with it.  For example, if the client
+ * implements a MAC-learning switch, then it searches the forwarding database
+ * for the packet's destination MAC and VLAN and determines the set of ports to
+ * which it should be sent.  In any case, the client composes a set of datapath
+ * actions to properly dispatch the packet and then directs the datapath to
+ * execute those actions on the packet (e.g. with dpif_execute()).
+ *
+ * Most of the time, the actions that the client executed on the packet apply
+ * to every packet with the same flow.  For example, the flow includes both
+ * destination MAC and VLAN ID (and much more), so this is true for the
+ * MAC-learning switch example above.  In such a case, the client can also
+ * direct the datapath to treat any further packets in the flow in the same
+ * way, using dpif_flow_put() to add a new flow entry.
+ *
+ * Other tasks the client might need to perform, in addition to reacting to
+ * upcalls, include:
+ *
+ *    - Periodically polling flow statistics, perhaps to supply to its own
+ *      clients.
+ *
+ *    - Deleting flow entries from the datapath that haven't been used
+ *      recently, to save memory.
+ *
+ *    - Updating flow entries whose actions should change.  For example, if a
+ *      MAC learning switch learns that a MAC has moved, then it must update
+ *      the actions of flow entries that sent packets to the MAC at its old
+ *      location.
+ *
+ *    - Adding and removing ports to achieve a new configuration.
+ */
 #ifndef DPIF_H
 #define DPIF_H 1
 
@@ -57,10 +360,11 @@ void dpif_wait(struct dpif *);
 
 const char *dpif_name(const struct dpif *);
 const char *dpif_base_name(const struct dpif *);
+const char *dpif_type(const struct dpif *);
 
 int dpif_delete(struct dpif *);
 
-/* Statisticss for a dpif as a whole. */
+/* Statistics for a dpif as a whole. */
 struct dpif_dp_stats {
     uint64_t n_hit;             /* Number of flow table matches. */
     uint64_t n_missed;          /* Number of flow table misses. */
@@ -72,8 +376,10 @@ int dpif_get_dp_stats(const struct dpif *, struct dpif_dp_stats *);
 \f
 /* Port operations. */
 
-int dpif_port_add(struct dpif *, struct netdev *, uint16_t *port_nop);
-int dpif_port_del(struct dpif *, uint16_t port_no);
+const char *dpif_port_open_type(const char *datapath_type,
+                                const char *port_type);
+int dpif_port_add(struct dpif *, struct netdev *, uint32_t *port_nop);
+int dpif_port_del(struct dpif *, uint32_t port_no);
 
 /* A port within a datapath.
  *
@@ -85,14 +391,15 @@ struct dpif_port {
 };
 void dpif_port_clone(struct dpif_port *, const struct dpif_port *);
 void dpif_port_destroy(struct dpif_port *);
-int dpif_port_query_by_number(const struct dpif *, uint16_t port_no,
+bool dpif_port_exists(const struct dpif *dpif, const char *devname);
+int dpif_port_query_by_number(const struct dpif *, uint32_t port_no,
                               struct dpif_port *);
 int dpif_port_query_by_name(const struct dpif *, const char *devname,
                             struct dpif_port *);
-int dpif_port_get_name(struct dpif *, uint16_t port_no,
+int dpif_port_get_name(struct dpif *, uint32_t port_no,
                        char *name, size_t name_size);
 int dpif_get_max_ports(const struct dpif *);
-uint32_t dpif_port_get_pid(const struct dpif *, uint16_t port_no);
+uint32_t dpif_port_get_pid(const struct dpif *, uint32_t port_no);
 
 struct dpif_port_dump {
     const struct dpif *dpif;
index 06478da..a13519e 100644 (file)
@@ -335,7 +335,7 @@ invalid:
  *      present and has a correct length, and otherwise NULL.
  */
 void
-flow_extract(struct ofpbuf *packet, uint32_t skb_priority,
+flow_extract(struct ofpbuf *packet, uint32_t skb_priority, uint32_t skb_mark,
              const struct flow_tnl *tnl, uint16_t ofp_in_port,
              struct flow *flow)
 {
@@ -352,6 +352,7 @@ flow_extract(struct ofpbuf *packet, uint32_t skb_priority,
     }
     flow->in_port = ofp_in_port;
     flow->skb_priority = skb_priority;
+    flow->skb_mark = skb_mark;
 
     packet->l2 = b.data;
     packet->l3 = NULL;
@@ -374,9 +375,33 @@ flow_extract(struct ofpbuf *packet, uint32_t skb_priority,
     }
     flow->dl_type = parse_ethertype(&b);
 
-    /* Network layer. */
     packet->l3 = b.data;
-    if (flow->dl_type == htons(ETH_TYPE_IP)) {
+    flow_extract_l3_onwards(packet, flow, flow->dl_type);
+}
+
+/* Initializes l3 and higher 'flow' members from 'packet'
+ *
+ * This should be called by or after flow_extract()
+ *
+ * Initializes 'packet' header pointers as follows:
+ *
+ *    - packet->l4 to just past the IPv4 header, if one is present and has a
+ *      correct length, and otherwise NULL.
+ *
+ *    - packet->l7 to just past the TCP or UDP or ICMP header, if one is
+ *      present and has a correct length, and otherwise NULL.
+ */
+void
+flow_extract_l3_onwards(struct ofpbuf *packet, struct flow *flow,
+                        ovs_be16 dl_type)
+{
+    struct ofpbuf b;
+
+    ofpbuf_use_const(&b, packet->l3, packet->size -
+                     (size_t)((char *)packet->l3 - (char *)packet->l2));
+
+    /* Network layer. */
+    if (dl_type == htons(ETH_TYPE_IP)) {
         const struct ip_header *nh = pull_ip(&b);
         if (nh) {
             packet->l4 = b.data;
@@ -409,7 +434,7 @@ flow_extract(struct ofpbuf *packet, uint32_t skb_priority,
                 }
             }
         }
-    } else if (flow->dl_type == htons(ETH_TYPE_IPV6)) {
+    } else if (dl_type == htons(ETH_TYPE_IPV6)) {
         if (parse_ipv6(&b, flow)) {
             return;
         }
@@ -424,7 +449,8 @@ flow_extract(struct ofpbuf *packet, uint32_t skb_priority,
                 packet->l7 = b.data;
             }
         }
-    } else if (flow->dl_type == htons(ETH_TYPE_ARP)) {
+    } else if (dl_type == htons(ETH_TYPE_ARP) ||
+               dl_type == htons(ETH_TYPE_RARP)) {
         const struct arp_eth_header *arp = pull_arp(&b);
         if (arp && arp->ar_hrd == htons(1)
             && arp->ar_pro == htons(ETH_TYPE_IP)
@@ -461,7 +487,7 @@ flow_zero_wildcards(struct flow *flow, const struct flow_wildcards *wildcards)
 void
 flow_get_metadata(const struct flow *flow, struct flow_metadata *fmd)
 {
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 17);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 18);
 
     fmd->tun_id = flow->tunnel.tun_id;
     fmd->metadata = flow->metadata;
@@ -477,13 +503,57 @@ flow_to_string(const struct flow *flow)
     return ds_cstr(&ds);
 }
 
+const char *
+flow_tun_flag_to_string(uint32_t flags)
+{
+    switch (flags) {
+    case FLOW_TNL_F_DONT_FRAGMENT:
+        return "df";
+    case FLOW_TNL_F_CSUM:
+        return "csum";
+    case FLOW_TNL_F_KEY:
+        return "key";
+    default:
+        return NULL;
+    }
+}
+
+void
+format_flags(struct ds *ds, const char *(*bit_to_string)(uint32_t),
+             uint32_t flags, char del)
+{
+    uint32_t bad = 0;
+
+    if (!flags) {
+        return;
+    }
+    while (flags) {
+        uint32_t bit = rightmost_1bit(flags);
+        const char *s;
+
+        s = bit_to_string(bit);
+        if (s) {
+            ds_put_format(ds, "%s%c", s, del);
+        } else {
+            bad |= bit;
+        }
+
+        flags &= ~bit;
+    }
+
+    if (bad) {
+        ds_put_format(ds, "0x%"PRIx32"%c", bad, del);
+    }
+    ds_chomp(ds, del);
+}
+
 void
 flow_format(struct ds *ds, const struct flow *flow)
 {
     struct match match;
 
     match_wc_init(&match, flow);
-    match_format(&match, ds, flow->skb_priority);
+    match_format(&match, ds, OFP_DEFAULT_PRIORITY);
 }
 
 void
@@ -550,7 +620,7 @@ flow_wildcards_combine(struct flow_wildcards *dst,
 uint32_t
 flow_wildcards_hash(const struct flow_wildcards *wc, uint32_t basis)
 {
-    return flow_hash(&wc->masks, basis);;
+    return flow_hash(&wc->masks, basis);
 }
 
 /* Returns true if 'a' and 'b' represent the same wildcards, false if they are
@@ -767,6 +837,7 @@ flow_compose(struct ofpbuf *b, const struct flow *flow)
         b->l3 = ip = ofpbuf_put_zeros(b, sizeof *ip);
         ip->ip_ihl_ver = IP_IHL_VER(5, 4);
         ip->ip_tos = flow->nw_tos;
+        ip->ip_ttl = flow->nw_ttl;
         ip->ip_proto = flow->nw_proto;
         ip->ip_src = flow->nw_src;
         ip->ip_dst = flow->nw_dst;
@@ -808,7 +879,8 @@ flow_compose(struct ofpbuf *b, const struct flow *flow)
         ip->ip_csum = csum(ip, sizeof *ip);
     } else if (flow->dl_type == htons(ETH_TYPE_IPV6)) {
         /* XXX */
-    } else if (flow->dl_type == htons(ETH_TYPE_ARP)) {
+    } else if (flow->dl_type == htons(ETH_TYPE_ARP) ||
+               flow->dl_type == htons(ETH_TYPE_RARP)) {
         struct arp_eth_header *arp;
 
         b->l3 = arp = ofpbuf_put_zeros(b, sizeof *arp);
index 9388f20..8e79e62 100644 (file)
@@ -36,7 +36,7 @@ struct ofpbuf;
 /* This sequence number should be incremented whenever anything involving flows
  * or the wildcarding of flows changes.  This will cause build assertion
  * failures in places which likely need to be updated. */
-#define FLOW_WC_SEQ 17
+#define FLOW_WC_SEQ 18
 
 #define FLOW_N_REGS 8
 BUILD_ASSERT_DECL(FLOW_N_REGS <= NXM_NX_MAX_REGS);
@@ -56,6 +56,9 @@ BUILD_ASSERT_DECL(FLOW_NW_FRAG_LATER == NX_IP_FRAG_LATER);
 #define FLOW_TNL_F_DONT_FRAGMENT (1 << 0)
 #define FLOW_TNL_F_CSUM (1 << 1)
 #define FLOW_TNL_F_KEY (1 << 2)
+
+const char *flow_tun_flag_to_string(uint32_t flags);
+
 struct flow_tnl {
     ovs_be64 tun_id;
     ovs_be32 ip_src;
@@ -65,6 +68,14 @@ struct flow_tnl {
     uint8_t ip_ttl;
 };
 
+/*
+* A flow in the network.
+*
+* The meaning of 'in_port' is context-dependent.  In most cases, it is a
+* 16-bit OpenFlow 1.0 port number.  In the software datapath interface (dpif)
+* layer and its implementations (e.g. dpif-linux, dpif-netdev), it is instead
+* a 32-bit datapath port number.
+*/
 struct flow {
     struct flow_tnl tunnel;     /* Encapsulating tunnel parameters. */
     ovs_be64 metadata;          /* OpenFlow Metadata. */
@@ -76,7 +87,10 @@ struct flow {
     ovs_be32 nw_src;            /* IPv4 source address. */
     ovs_be32 nw_dst;            /* IPv4 destination address. */
     ovs_be32 ipv6_label;        /* IPv6 flow label. */
-    uint16_t in_port;           /* OpenFlow port number of input port. */
+    uint32_t in_port;           /* Input port. OpenFlow port number
+                                   unless in DPIF code, in which case it
+                                   is the datapath port number. */
+    uint32_t skb_mark;          /* Packet mark. */
     ovs_be16 vlan_tci;          /* If 802.1Q, TCI | VLAN_CFI; otherwise 0. */
     ovs_be16 dl_type;           /* Ethernet frame type. */
     ovs_be16 tp_src;            /* TCP/UDP source port. */
@@ -89,15 +103,15 @@ struct flow {
     uint8_t arp_tha[6];         /* ARP/ND target hardware address. */
     uint8_t nw_ttl;             /* IP TTL/Hop Limit. */
     uint8_t nw_frag;            /* FLOW_FRAG_* flags. */
-    uint8_t zeros[2];           /* Must be zero. */
+    uint8_t zeros[4];
 };
 BUILD_ASSERT_DECL(sizeof(struct flow) % 4 == 0);
 
 #define FLOW_U32S (sizeof(struct flow) / 4)
 
 /* Remember to update FLOW_WC_SEQ when changing 'struct flow'. */
-BUILD_ASSERT_DECL(sizeof(struct flow) == sizeof(struct flow_tnl) + 144 &&
-                  FLOW_WC_SEQ == 17);
+BUILD_ASSERT_DECL(sizeof(struct flow) == sizeof(struct flow_tnl) + 152 &&
+                  FLOW_WC_SEQ == 18);
 
 /* Represents the metadata fields of struct flow. */
 struct flow_metadata {
@@ -107,12 +121,17 @@ struct flow_metadata {
     uint16_t in_port;                /* OpenFlow port or zero. */
 };
 
-void flow_extract(struct ofpbuf *, uint32_t priority, const struct flow_tnl *,
-                  uint16_t in_port, struct flow *);
+void flow_extract(struct ofpbuf *, uint32_t priority, uint32_t mark,
+                  const struct flow_tnl *, uint16_t in_port, struct flow *);
+void flow_extract_l3_onwards(struct ofpbuf *, struct flow *,
+                             ovs_be16 dl_type);
 void flow_zero_wildcards(struct flow *, const struct flow_wildcards *);
 void flow_get_metadata(const struct flow *, struct flow_metadata *);
 
 char *flow_to_string(const struct flow *);
+void format_flags(struct ds *ds, const char *(*bit_to_string)(uint32_t),
+                  uint32_t flags, char del);
+
 void flow_format(struct ds *, const struct flow *);
 void flow_print(FILE *, const struct flow *);
 static inline int flow_compare_3way(const struct flow *, const struct flow *);
index 552de04..b0d8d34 100644 (file)
@@ -1102,7 +1102,7 @@ jsonrpc_session_set_dscp(struct jsonrpc_session *s,
                          reconnect_get_name(s->reconnect), strerror(error));
             }
             /*
-             * TODO:XXX race window between setting dscp to listening socket
+             * XXX race window between setting dscp to listening socket
              * and accepting socket. accepted socket may have old dscp value.
              * Ignore this race window for now.
              */
index 374d915..96857ea 100644 (file)
@@ -750,7 +750,9 @@ lacp_print_details(struct ds *ds, struct lacp *lacp)
     ds_put_format(ds, "\tsys_priority: %u\n", lacp->sys_priority);
     ds_put_cstr(ds, "\taggregation key: ");
     if (lacp->key_slave) {
-        ds_put_format(ds, "%u", lacp->key_slave->port_id);
+        ds_put_format(ds, "%u", lacp->key_slave->key
+                                ? lacp->key_slave->key
+                                : lacp->key_slave->port_id);
     } else {
         ds_put_cstr(ds, "none");
     }
index 7a60f3c..e572a34 100644 (file)
@@ -200,15 +200,19 @@ lswitch_handshake(struct lswitch *sw)
                 error = rconn_send(sw->rconn, msg, NULL);
             }
         }
+        if (protocol & usable_protocols) {
+            for (i = 0; !error && i < sw->n_default_flows; i++) {
+                msg = ofputil_encode_flow_mod(&sw->default_flows[i], protocol);
+                error = rconn_send(sw->rconn, msg, NULL);
+            }
 
-        for (i = 0; !error && i < sw->n_default_flows; i++) {
-            msg = ofputil_encode_flow_mod(&sw->default_flows[i], protocol);
-            error = rconn_send(sw->rconn, msg, NULL);
-        }
-
-        if (error) {
-            VLOG_INFO_RL(&rl, "%s: failed to queue default flows (%s)",
-                         rconn_get_name(sw->rconn), strerror(error));
+            if (error) {
+                VLOG_INFO_RL(&rl, "%s: failed to queue default flows (%s)",
+                             rconn_get_name(sw->rconn), strerror(error));
+            }
+        } else {
+            VLOG_INFO_RL(&rl, "%s: failed to set usable protocol",
+                         rconn_get_name(sw->rconn));
         }
     }
     sw->protocol = protocol;
@@ -342,6 +346,8 @@ lswitch_process_packet(struct lswitch *sw, const struct ofpbuf *msg)
     case OFPTYPE_PORT_MOD:
     case OFPTYPE_BARRIER_REQUEST:
     case OFPTYPE_BARRIER_REPLY:
+    case OFPTYPE_QUEUE_GET_CONFIG_REQUEST:
+    case OFPTYPE_QUEUE_GET_CONFIG_REPLY:
     case OFPTYPE_DESC_STATS_REQUEST:
     case OFPTYPE_DESC_STATS_REPLY:
     case OFPTYPE_FLOW_STATS_REQUEST:
@@ -362,13 +368,30 @@ lswitch_process_packet(struct lswitch *sw, const struct ofpbuf *msg)
     case OFPTYPE_FLOW_MOD_TABLE_ID:
     case OFPTYPE_SET_PACKET_IN_FORMAT:
     case OFPTYPE_FLOW_AGE:
-    case OFPTYPE_SET_ASYNC_CONFIG:
     case OFPTYPE_SET_CONTROLLER_ID:
     case OFPTYPE_FLOW_MONITOR_STATS_REQUEST:
     case OFPTYPE_FLOW_MONITOR_STATS_REPLY:
     case OFPTYPE_FLOW_MONITOR_CANCEL:
     case OFPTYPE_FLOW_MONITOR_PAUSED:
     case OFPTYPE_FLOW_MONITOR_RESUMED:
+    case OFPTYPE_GET_ASYNC_REQUEST:
+    case OFPTYPE_GET_ASYNC_REPLY:
+    case OFPTYPE_SET_ASYNC_CONFIG:
+    case OFPTYPE_METER_MOD:
+    case OFPTYPE_GROUP_REQUEST:
+    case OFPTYPE_GROUP_REPLY:
+    case OFPTYPE_GROUP_DESC_REQUEST:
+    case OFPTYPE_GROUP_DESC_REPLY:
+    case OFPTYPE_GROUP_FEATURES_REQUEST:
+    case OFPTYPE_GROUP_FEATURES_REPLY:
+    case OFPTYPE_METER_REQUEST:
+    case OFPTYPE_METER_REPLY:
+    case OFPTYPE_METER_CONFIG_REQUEST:
+    case OFPTYPE_METER_CONFIG_REPLY:
+    case OFPTYPE_METER_FEATURES_REQUEST:
+    case OFPTYPE_METER_FEATURES_REPLY:
+    case OFPTYPE_TABLE_FEATURES_REQUEST:
+    case OFPTYPE_TABLE_FEATURES_REPLY:
     default:
         if (VLOG_IS_DBG_ENABLED()) {
             char *s = ofp_to_string(msg->data, msg->size, 2);
@@ -535,7 +558,7 @@ process_packet_in(struct lswitch *sw, const struct ofp_header *oh)
 
     /* Extract flow data from 'opi' into 'flow'. */
     ofpbuf_use_const(&pkt, pi.packet, pi.packet_len);
-    flow_extract(&pkt, 0, NULL, pi.fmd.in_port, &flow);
+    flow_extract(&pkt, 0, 0, NULL, pi.fmd.in_port, &flow);
     flow.tunnel.tun_id = pi.fmd.tun_id;
 
     /* Choose output port. */
index 3c541af..f609d48 100644 (file)
@@ -110,7 +110,8 @@ normalize_idle_time(unsigned int idle_time)
 }
 
 /* Creates and returns a new MAC learning table with an initial MAC aging
- * timeout of 'idle_time' seconds. */
+ * timeout of 'idle_time' seconds and an initial maximum of MAC_DEFAULT_MAX
+ * entries. */
 struct mac_learning *
 mac_learning_create(unsigned int idle_time)
 {
@@ -122,6 +123,7 @@ mac_learning_create(unsigned int idle_time)
     ml->secret = random_uint32();
     ml->flood_vlans = NULL;
     ml->idle_time = normalize_idle_time(idle_time);
+    ml->max_entries = MAC_DEFAULT_MAX;
     return ml;
 }
 
@@ -176,6 +178,16 @@ mac_learning_set_idle_time(struct mac_learning *ml, unsigned int idle_time)
     }
 }
 
+/* Sets the maximum number of entries in 'ml' to 'max_entries', adjusting it
+ * to be within a reasonable range. */
+void
+mac_learning_set_max_entries(struct mac_learning *ml, size_t max_entries)
+{
+    ml->max_entries = (max_entries < 10 ? 10
+                       : max_entries > 1000 * 1000 ? 1000 * 1000
+                       : max_entries);
+}
+
 static bool
 is_learning_vlan(const struct mac_learning *ml, uint16_t vlan)
 {
@@ -212,7 +224,7 @@ mac_learning_insert(struct mac_learning *ml,
     if (!e) {
         uint32_t hash = mac_table_hash(ml, src_mac, vlan);
 
-        if (hmap_count(&ml->table) >= MAC_MAX) {
+        if (hmap_count(&ml->table) >= ml->max_entries) {
             get_lru(ml, &e);
             mac_learning_expire(ml, e);
         }
@@ -311,7 +323,9 @@ void
 mac_learning_run(struct mac_learning *ml, struct tag_set *set)
 {
     struct mac_entry *e;
-    while (get_lru(ml, &e) && time_now() >= e->expires) {
+    while (get_lru(ml, &e)
+           && (hmap_count(&ml->table) > ml->max_entries
+               || time_now() >= e->expires)) {
         COVERAGE_INC(mac_learning_expired);
         if (set) {
             tag_set_add(set, e->tag);
@@ -323,7 +337,9 @@ mac_learning_run(struct mac_learning *ml, struct tag_set *set)
 void
 mac_learning_wait(struct mac_learning *ml)
 {
-    if (!list_is_empty(&ml->lrus)) {
+    if (hmap_count(&ml->table) > ml->max_entries) {
+        poll_immediate_wake();
+    } else if (!list_is_empty(&ml->lrus)) {
         struct mac_entry *e = mac_entry_from_lru_node(ml->lrus.next);
         poll_timer_wait_until(e->expires * 1000LL);
     }
index 8f8fd45..284e7f6 100644 (file)
@@ -26,7 +26,8 @@
 
 struct mac_learning;
 
-#define MAC_MAX 2048
+/* Default maximum size of a MAC learning table, in entries. */
+#define MAC_DEFAULT_MAX 2048
 
 /* Time, in seconds, before expiring a mac_entry due to inactivity. */
 #define MAC_ENTRY_DEFAULT_IDLE_TIME 300
@@ -83,6 +84,7 @@ struct mac_learning {
     uint32_t secret;            /* Secret for randomizing hash table. */
     unsigned long *flood_vlans; /* Bitmap of learning disabled VLANs. */
     unsigned int idle_time;     /* Max age before deleting an entry. */
+    size_t max_entries;         /* Max number of learned MACs. */
 };
 
 /* Basics. */
@@ -96,6 +98,7 @@ void mac_learning_wait(struct mac_learning *);
 bool mac_learning_set_flood_vlans(struct mac_learning *,
                                   const unsigned long *bitmap);
 void mac_learning_set_idle_time(struct mac_learning *, unsigned int idle_time);
+void mac_learning_set_max_entries(struct mac_learning *, size_t max_entries);
 
 /* Learning. */
 bool mac_learning_may_learn(const struct mac_learning *,
index 6a4fec7..10dbdcb 100644 (file)
@@ -20,6 +20,7 @@
 #include <stdlib.h>
 #include "byte-order.h"
 #include "dynamic-string.h"
+#include "ofp-util.h"
 #include "packets.h"
 #include "vlog.h"
 
@@ -56,20 +57,33 @@ match_wc_init(struct match *match, const struct flow *flow)
         memset(&wc->masks.nw_proto, 0xff, sizeof wc->masks.nw_proto);
     }
 
+    if (flow->skb_priority) {
+        memset(&wc->masks.skb_priority, 0xff, sizeof wc->masks.skb_priority);
+    }
+
+    if (flow->skb_mark) {
+        memset(&wc->masks.skb_mark, 0xff, sizeof wc->masks.skb_mark);
+    }
+
     for (i = 0; i < FLOW_N_REGS; i++) {
         if (flow->regs[i]) {
             memset(&wc->masks.regs[i], 0xff, sizeof wc->masks.regs[i]);
         }
     }
 
-    if (flow->tunnel.ip_dst || flow->tunnel.tun_id) {
-        memset(&wc->masks.tunnel.tun_id, 0xff, sizeof wc->masks.tunnel.tun_id);
+    if (flow->tunnel.ip_dst) {
+        if (flow->tunnel.flags & FLOW_TNL_F_KEY) {
+            memset(&wc->masks.tunnel.tun_id, 0xff, sizeof wc->masks.tunnel.tun_id);
+        }
         memset(&wc->masks.tunnel.ip_src, 0xff, sizeof wc->masks.tunnel.ip_src);
         memset(&wc->masks.tunnel.ip_dst, 0xff, sizeof wc->masks.tunnel.ip_dst);
         memset(&wc->masks.tunnel.flags, 0xff, sizeof wc->masks.tunnel.flags);
         memset(&wc->masks.tunnel.ip_tos, 0xff, sizeof wc->masks.tunnel.ip_tos);
         memset(&wc->masks.tunnel.ip_ttl, 0xff, sizeof wc->masks.tunnel.ip_ttl);
+    } else if (flow->tunnel.tun_id) {
+        memset(&wc->masks.tunnel.tun_id, 0xff, sizeof wc->masks.tunnel.tun_id);
     }
+
     memset(&wc->masks.metadata, 0xff, sizeof wc->masks.metadata);
     memset(&wc->masks.in_port, 0xff, sizeof wc->masks.in_port);
     memset(&wc->masks.vlan_tci, 0xff, sizeof wc->masks.vlan_tci);
@@ -81,12 +95,14 @@ match_wc_init(struct match *match, const struct flow *flow)
         memset(&wc->masks.ipv6_dst, 0xff, sizeof wc->masks.ipv6_dst);
         memset(&wc->masks.ipv6_label, 0xff, sizeof wc->masks.ipv6_label);
     } else if (flow->dl_type == htons(ETH_TYPE_IP) ||
-               (flow->dl_type == htons(ETH_TYPE_ARP))) {
+               (flow->dl_type == htons(ETH_TYPE_ARP)) ||
+               (flow->dl_type == htons(ETH_TYPE_RARP))) {
         memset(&wc->masks.nw_src, 0xff, sizeof wc->masks.nw_src);
         memset(&wc->masks.nw_dst, 0xff, sizeof wc->masks.nw_dst);
     }
 
-    if (flow->dl_type == htons(ETH_TYPE_ARP)) {
+    if (flow->dl_type == htons(ETH_TYPE_ARP) ||
+        flow->dl_type == htons(ETH_TYPE_RARP)) {
         memset(&wc->masks.arp_sha, 0xff, sizeof wc->masks.arp_sha);
         memset(&wc->masks.arp_tha, 0xff, sizeof wc->masks.arp_tha);
     }
@@ -123,6 +139,7 @@ match_init_exact(struct match *match, const struct flow *flow)
 
     match->flow = *flow;
     match->flow.skb_priority = 0;
+    match->flow.skb_mark = 0;
     memset(&match->flow.tunnel, 0, sizeof match->flow.tunnel);
     match->flow.tunnel.tun_id = tun_id;
     flow_wildcards_init_exact(&match->wc);
@@ -192,6 +209,71 @@ match_set_tun_id_masked(struct match *match, ovs_be64 tun_id, ovs_be64 mask)
     match->flow.tunnel.tun_id = tun_id & mask;
 }
 
+void
+match_set_tun_src(struct match *match, ovs_be32 src)
+{
+    match_set_tun_src_masked(match, src, htonl(UINT32_MAX));
+}
+
+void
+match_set_tun_src_masked(struct match *match, ovs_be32 src, ovs_be32 mask)
+{
+    match->wc.masks.tunnel.ip_src = mask;
+    match->flow.tunnel.ip_src = src & mask;
+}
+
+void
+match_set_tun_dst(struct match *match, ovs_be32 dst)
+{
+    match_set_tun_dst_masked(match, dst, htonl(UINT32_MAX));
+}
+
+void
+match_set_tun_dst_masked(struct match *match, ovs_be32 dst, ovs_be32 mask)
+{
+    match->wc.masks.tunnel.ip_dst = mask;
+    match->flow.tunnel.ip_dst = dst & mask;
+}
+
+void
+match_set_tun_ttl(struct match *match, uint8_t ttl)
+{
+    match_set_tun_ttl_masked(match, ttl, UINT8_MAX);
+}
+
+void
+match_set_tun_ttl_masked(struct match *match, uint8_t ttl, uint8_t mask)
+{
+    match->wc.masks.tunnel.ip_ttl = mask;
+    match->flow.tunnel.ip_ttl = ttl & mask;
+}
+
+void
+match_set_tun_tos(struct match *match, uint8_t tos)
+{
+    match_set_tun_tos_masked(match, tos, UINT8_MAX);
+}
+
+void
+match_set_tun_tos_masked(struct match *match, uint8_t tos, uint8_t mask)
+{
+    match->wc.masks.tunnel.ip_tos = mask;
+    match->flow.tunnel.ip_tos = tos & mask;
+}
+
+void
+match_set_tun_flags(struct match *match, uint16_t flags)
+{
+    match_set_tun_flags_masked(match, flags, UINT16_MAX);
+}
+
+void
+match_set_tun_flags_masked(struct match *match, uint16_t flags, uint16_t mask)
+{
+    match->wc.masks.tunnel.flags = mask;
+    match->flow.tunnel.flags = flags & mask;
+}
+
 void
 match_set_in_port(struct match *match, uint16_t ofp_port)
 {
@@ -199,6 +281,20 @@ match_set_in_port(struct match *match, uint16_t ofp_port)
     match->flow.in_port = ofp_port;
 }
 
+void
+match_set_skb_priority(struct match *match, uint32_t skb_priority)
+{
+    match->wc.masks.skb_priority = UINT32_MAX;
+    match->flow.skb_priority = skb_priority;
+}
+
+void
+match_set_skb_mark(struct match *match, uint32_t skb_mark)
+{
+    match->wc.masks.skb_mark = UINT32_MAX;
+    match->flow.skb_mark = skb_mark;
+}
+
 void
 match_set_dl_type(struct match *match, ovs_be16 dl_type)
 {
@@ -640,6 +736,39 @@ format_be16_masked(struct ds *s, const char *name,
     }
 }
 
+static void
+format_flow_tunnel(struct ds *s, const struct match *match)
+{
+    const struct flow_wildcards *wc = &match->wc;
+    const struct flow_tnl *tnl = &match->flow.tunnel;
+
+    switch (wc->masks.tunnel.tun_id) {
+    case 0:
+        break;
+    case CONSTANT_HTONLL(UINT64_MAX):
+        ds_put_format(s, "tun_id=%#"PRIx64",", ntohll(tnl->tun_id));
+        break;
+    default:
+        ds_put_format(s, "tun_id=%#"PRIx64"/%#"PRIx64",",
+                      ntohll(tnl->tun_id),
+                      ntohll(wc->masks.tunnel.tun_id));
+        break;
+    }
+    format_ip_netmask(s, "tun_src", tnl->ip_src, wc->masks.tunnel.ip_src);
+    format_ip_netmask(s, "tun_dst", tnl->ip_dst, wc->masks.tunnel.ip_dst);
+
+    if (wc->masks.tunnel.ip_tos) {
+        ds_put_format(s, "tun_tos=%"PRIx8",", tnl->ip_tos);
+    }
+    if (wc->masks.tunnel.ip_ttl) {
+        ds_put_format(s, "tun_ttl=%"PRIu8",", tnl->ip_ttl);
+    }
+    if (wc->masks.tunnel.flags) {
+        format_flags(s, flow_tun_flag_to_string, tnl->flags, '|');
+        ds_put_char(s, ',');
+    }
+}
+
 /* Appends a string representation of 'match' to 's'.  If 'priority' is
  * different from OFP_DEFAULT_PRIORITY, includes it in 's'. */
 void
@@ -653,12 +782,20 @@ match_format(const struct match *match, struct ds *s, unsigned int priority)
 
     int i;
 
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 17);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 18);
 
     if (priority != OFP_DEFAULT_PRIORITY) {
         ds_put_format(s, "priority=%u,", priority);
     }
 
+    if (wc->masks.skb_mark) {
+        ds_put_format(s, "skb_mark=%#"PRIx32",", f->skb_mark);
+    }
+
+    if (wc->masks.skb_priority) {
+        ds_put_format(s, "skb_priority=%#"PRIx32",", f->skb_priority);
+    }
+
     if (wc->masks.dl_type) {
         skip_type = true;
         if (f->dl_type == htons(ETH_TYPE_IP)) {
@@ -695,6 +832,8 @@ match_format(const struct match *match, struct ds *s, unsigned int priority)
             }
         } else if (f->dl_type == htons(ETH_TYPE_ARP)) {
             ds_put_cstr(s, "arp,");
+        } else if (f->dl_type == htons(ETH_TYPE_RARP)) {
+            ds_put_cstr(s, "rarp,");
         } else {
             skip_type = false;
         }
@@ -712,18 +851,9 @@ match_format(const struct match *match, struct ds *s, unsigned int priority)
             break;
         }
     }
-    switch (wc->masks.tunnel.tun_id) {
-    case 0:
-        break;
-    case CONSTANT_HTONLL(UINT64_MAX):
-        ds_put_format(s, "tun_id=%#"PRIx64",", ntohll(f->tunnel.tun_id));
-        break;
-    default:
-        ds_put_format(s, "tun_id=%#"PRIx64"/%#"PRIx64",",
-                      ntohll(f->tunnel.tun_id),
-                      ntohll(wc->masks.tunnel.tun_id));
-        break;
-    }
+
+    format_flow_tunnel(s, match);
+
     switch (wc->masks.metadata) {
     case 0:
         break;
@@ -736,7 +866,9 @@ match_format(const struct match *match, struct ds *s, unsigned int priority)
         break;
     }
     if (wc->masks.in_port) {
-        ds_put_format(s, "in_port=%"PRIu16",", f->in_port);
+        ds_put_cstr(s, "in_port=");
+        ofputil_format_port(f->in_port, s);
+        ds_put_char(s, ',');
     }
     if (wc->masks.vlan_tci) {
         ovs_be16 vid_mask = wc->masks.vlan_tci & htons(VLAN_VID_MASK);
@@ -780,7 +912,8 @@ match_format(const struct match *match, struct ds *s, unsigned int priority)
                               ntohl(wc->masks.ipv6_label));
             }
         }
-    } else if (f->dl_type == htons(ETH_TYPE_ARP)) {
+    } else if (f->dl_type == htons(ETH_TYPE_ARP) ||
+               f->dl_type == htons(ETH_TYPE_RARP)) {
         format_ip_netmask(s, "arp_spa", f->nw_src, wc->masks.nw_src);
         format_ip_netmask(s, "arp_tpa", f->nw_dst, wc->masks.nw_dst);
     } else {
@@ -788,13 +921,15 @@ match_format(const struct match *match, struct ds *s, unsigned int priority)
         format_ip_netmask(s, "nw_dst", f->nw_dst, wc->masks.nw_dst);
     }
     if (!skip_proto && wc->masks.nw_proto) {
-        if (f->dl_type == htons(ETH_TYPE_ARP)) {
+        if (f->dl_type == htons(ETH_TYPE_ARP) ||
+            f->dl_type == htons(ETH_TYPE_RARP)) {
             ds_put_format(s, "arp_op=%"PRIu8",", f->nw_proto);
         } else {
             ds_put_format(s, "nw_proto=%"PRIu8",", f->nw_proto);
         }
     }
-    if (f->dl_type == htons(ETH_TYPE_ARP)) {
+    if (f->dl_type == htons(ETH_TYPE_ARP) ||
+        f->dl_type == htons(ETH_TYPE_RARP)) {
         format_eth_masked(s, "arp_sha", f->arp_sha, wc->masks.arp_sha);
         format_eth_masked(s, "arp_tha", f->arp_tha, wc->masks.arp_tha);
     }
index 28433b9..ff0b5f2 100644 (file)
@@ -50,7 +50,19 @@ void match_set_metadata_masked(struct match *,
                                ovs_be64 metadata, ovs_be64 mask);
 void match_set_tun_id(struct match *, ovs_be64 tun_id);
 void match_set_tun_id_masked(struct match *, ovs_be64 tun_id, ovs_be64 mask);
+void match_set_tun_src(struct match *match, ovs_be32 src);
+void match_set_tun_src_masked(struct match *match, ovs_be32 src, ovs_be32 mask);
+void match_set_tun_dst(struct match *match, ovs_be32 dst);
+void match_set_tun_dst_masked(struct match *match, ovs_be32 dst, ovs_be32 mask);
+void match_set_tun_ttl(struct match *match, uint8_t ttl);
+void match_set_tun_ttl_masked(struct match *match, uint8_t ttl, uint8_t mask);
+void match_set_tun_tos(struct match *match, uint8_t tos);
+void match_set_tun_tos_masked(struct match *match, uint8_t tos, uint8_t mask);
+void match_set_tun_flags(struct match *match, uint16_t flags);
+void match_set_tun_flags_masked(struct match *match, uint16_t flags, uint16_t mask);
 void match_set_in_port(struct match *, uint16_t ofp_port);
+void match_set_skb_mark(struct match *, uint32_t skb_mark);
+void match_set_skb_priority(struct match *, uint32_t skb_priority);
 void match_set_dl_type(struct match *, ovs_be16);
 void match_set_dl_src(struct match *, const uint8_t[6]);
 void match_set_dl_src_masked(struct match *, const uint8_t dl_src[6],
index 4fa05ae..e38e158 100644 (file)
@@ -54,7 +54,52 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         MFP_NONE,
         true,
         NXM_NX_TUN_ID, "NXM_NX_TUN_ID",
-        NXM_NX_TUN_ID, "NXM_NX_TUN_ID",
+        OXM_OF_TUNNEL_ID, "OXM_OF_TUNNEL_ID",
+    }, {
+        MFF_TUN_SRC, "tun_src", NULL,
+        MF_FIELD_SIZES(be32),
+        MFM_NONE,
+        MFS_IPV4,
+        MFP_NONE,
+        false,
+        0, NULL,
+        0, NULL,
+    }, {
+        MFF_TUN_DST, "tun_dst", NULL,
+        MF_FIELD_SIZES(be32),
+        MFM_NONE,
+        MFS_IPV4,
+        MFP_NONE,
+        false,
+        0, NULL,
+        0, NULL,
+    }, {
+        MFF_TUN_FLAGS, "tun_flags", NULL,
+        MF_FIELD_SIZES(be16),
+        MFM_NONE,
+        MFS_TNL_FLAGS,
+        MFP_NONE,
+        false,
+        0, NULL,
+        0, NULL,
+    }, {
+        MFF_TUN_TOS, "tun_tos", NULL,
+        MF_FIELD_SIZES(u8),
+        MFM_NONE,
+        MFS_DECIMAL,
+        MFP_NONE,
+        false,
+        0, NULL,
+        0, NULL,
+    }, {
+        MFF_TUN_TTL, "tun_ttl", NULL,
+        MF_FIELD_SIZES(u8),
+        MFM_NONE,
+        MFS_DECIMAL,
+        MFP_NONE,
+        false,
+        0, NULL,
+        0, NULL,
     }, {
         MFF_METADATA, "metadata", NULL,
         MF_FIELD_SIZES(be64),
@@ -73,6 +118,24 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         false,
         NXM_OF_IN_PORT, "NXM_OF_IN_PORT",
         OXM_OF_IN_PORT, "OXM_OF_IN_PORT",
+    }, {
+        MFF_SKB_PRIORITY, "skb_priority", NULL,
+        MF_FIELD_SIZES(be32),
+        MFM_NONE,
+        MFS_HEXADECIMAL,
+        MFP_NONE,
+        false,
+        0, NULL,
+        0, NULL,
+    }, {
+        MFF_SKB_MARK, "skb_mark", NULL,
+        MF_FIELD_SIZES(be32),
+        MFM_NONE,
+        MFS_HEXADECIMAL,
+        MFP_NONE,
+        false,
+        0, NULL,
+        0, NULL,
     },
 
 #define REGISTER(IDX)                           \
@@ -574,11 +637,20 @@ mf_is_all_wild(const struct mf_field *mf, const struct flow_wildcards *wc)
 {
     switch (mf->id) {
     case MFF_TUN_ID:
+    case MFF_TUN_SRC:
+    case MFF_TUN_DST:
+    case MFF_TUN_TOS:
+    case MFF_TUN_TTL:
+    case MFF_TUN_FLAGS:
         return !wc->masks.tunnel.tun_id;
     case MFF_METADATA:
         return !wc->masks.metadata;
     case MFF_IN_PORT:
         return !wc->masks.in_port;
+    case MFF_SKB_PRIORITY:
+        return !wc->masks.skb_priority;
+    case MFF_SKB_MARK:
+        return !wc->masks.skb_mark;
     CASE_MFF_REGS:
         return !wc->masks.regs[mf->id - MFF_REG0];
 
@@ -669,122 +741,7 @@ void
 mf_get_mask(const struct mf_field *mf, const struct flow_wildcards *wc,
             union mf_value *mask)
 {
-    switch (mf->id) {
-    case MFF_TUN_ID:
-        mask->be64 = wc->masks.tunnel.tun_id;
-        break;
-    case MFF_METADATA:
-        mask->be64 = wc->masks.metadata;
-        break;
-    case MFF_IN_PORT:
-        mask->be16 = htons(wc->masks.in_port);
-        break;
-    CASE_MFF_REGS:
-        mask->be32 = htonl(wc->masks.regs[mf->id - MFF_REG0]);
-        break;
-
-    case MFF_ETH_DST:
-        memcpy(mask->mac, wc->masks.dl_dst, ETH_ADDR_LEN);
-        break;
-    case MFF_ETH_SRC:
-        memcpy(mask->mac, wc->masks.dl_src, ETH_ADDR_LEN);
-        break;
-    case MFF_ETH_TYPE:
-        mask->be16 = wc->masks.dl_type;
-        break;
-
-    case MFF_VLAN_TCI:
-        mask->be16 = wc->masks.vlan_tci;
-        break;
-    case MFF_DL_VLAN:
-        mask->be16 = wc->masks.vlan_tci & htons(VLAN_VID_MASK);
-        break;
-    case MFF_VLAN_VID:
-        mask->be16 = wc->masks.vlan_tci & htons(VLAN_VID_MASK | VLAN_CFI);
-        break;
-    case MFF_DL_VLAN_PCP:
-    case MFF_VLAN_PCP:
-        mask->u8 = vlan_tci_to_pcp(wc->masks.vlan_tci);
-        break;
-
-    case MFF_IPV4_SRC:
-        mask->be32 = wc->masks.nw_src;
-        break;
-    case MFF_IPV4_DST:
-        mask->be32 = wc->masks.nw_dst;
-        break;
-
-    case MFF_IPV6_SRC:
-        mask->ipv6 = wc->masks.ipv6_src;
-        break;
-    case MFF_IPV6_DST:
-        mask->ipv6 = wc->masks.ipv6_dst;
-        break;
-    case MFF_IPV6_LABEL:
-        mask->be32 = wc->masks.ipv6_label;
-        break;
-
-    case MFF_IP_PROTO:
-        mask->u8 = wc->masks.nw_proto;
-        break;
-    case MFF_IP_DSCP:
-        mask->u8 = wc->masks.nw_tos & IP_DSCP_MASK;
-        break;
-    case MFF_IP_ECN:
-        mask->u8 = wc->masks.nw_tos & IP_ECN_MASK;
-        break;
-
-    case MFF_ND_TARGET:
-        mask->ipv6 = wc->masks.nd_target;
-        break;
-
-    case MFF_IP_TTL:
-        mask->u8 = wc->masks.nw_ttl;
-        break;
-    case MFF_IP_FRAG:
-        mask->u8 = wc->masks.nw_frag & FLOW_NW_FRAG_MASK;
-        break;
-
-    case MFF_ARP_OP:
-        mask->u8 = wc->masks.nw_proto;
-        break;
-    case MFF_ARP_SPA:
-        mask->be32 = wc->masks.nw_src;
-        break;
-    case MFF_ARP_TPA:
-        mask->be32 = wc->masks.nw_dst;
-        break;
-    case MFF_ARP_SHA:
-    case MFF_ND_SLL:
-        memcpy(mask->mac, wc->masks.arp_sha, ETH_ADDR_LEN);
-        break;
-    case MFF_ARP_THA:
-    case MFF_ND_TLL:
-        memcpy(mask->mac, wc->masks.arp_tha, ETH_ADDR_LEN);
-        break;
-
-    case MFF_TCP_SRC:
-    case MFF_UDP_SRC:
-        mask->be16 = wc->masks.tp_src;
-        break;
-    case MFF_TCP_DST:
-    case MFF_UDP_DST:
-        mask->be16 = wc->masks.tp_dst;
-        break;
-
-    case MFF_ICMPV4_TYPE:
-    case MFF_ICMPV6_TYPE:
-        mask->u8 = ntohs(wc->masks.tp_src);
-        break;
-    case MFF_ICMPV4_CODE:
-    case MFF_ICMPV6_CODE:
-        mask->u8 = ntohs(wc->masks.tp_dst);
-        break;
-
-    case MFF_N_IDS:
-    default:
-        NOT_REACHED();
-    }
+    mf_get_value(mf, &wc->masks, mask);
 }
 
 /* Tests whether 'mask' is a valid wildcard bit pattern for 'mf'.  Returns true
@@ -834,7 +791,8 @@ mf_are_prereqs_ok(const struct mf_field *mf, const struct flow *flow)
         return true;
 
     case MFP_ARP:
-        return flow->dl_type == htons(ETH_TYPE_ARP);
+      return (flow->dl_type == htons(ETH_TYPE_ARP) ||
+              flow->dl_type == htons(ETH_TYPE_RARP));
     case MFP_IPV4:
         return flow->dl_type == htons(ETH_TYPE_IP);
     case MFP_IPV6:
@@ -886,8 +844,15 @@ mf_is_value_valid(const struct mf_field *mf, const union mf_value *value)
 {
     switch (mf->id) {
     case MFF_TUN_ID:
+    case MFF_TUN_SRC:
+    case MFF_TUN_DST:
+    case MFF_TUN_TOS:
+    case MFF_TUN_TTL:
+    case MFF_TUN_FLAGS:
     case MFF_METADATA:
     case MFF_IN_PORT:
+    case MFF_SKB_PRIORITY:
+    case MFF_SKB_MARK:
     CASE_MFF_REGS:
     case MFF_ETH_SRC:
     case MFF_ETH_DST:
@@ -954,6 +919,22 @@ mf_get_value(const struct mf_field *mf, const struct flow *flow,
     case MFF_TUN_ID:
         value->be64 = flow->tunnel.tun_id;
         break;
+    case MFF_TUN_SRC:
+        value->be32 = flow->tunnel.ip_src;
+        break;
+    case MFF_TUN_DST:
+        value->be32 = flow->tunnel.ip_dst;
+        break;
+    case MFF_TUN_FLAGS:
+        value->be16 = htons(flow->tunnel.flags);
+        break;
+    case MFF_TUN_TTL:
+        value->u8 = flow->tunnel.ip_ttl;
+        break;
+    case MFF_TUN_TOS:
+        value->u8 = flow->tunnel.ip_tos;
+        break;
+
     case MFF_METADATA:
         value->be64 = flow->metadata;
         break;
@@ -962,6 +943,14 @@ mf_get_value(const struct mf_field *mf, const struct flow *flow,
         value->be16 = htons(flow->in_port);
         break;
 
+    case MFF_SKB_PRIORITY:
+        value->be32 = htonl(flow->skb_priority);
+        break;
+
+    case MFF_SKB_MARK:
+        value->be32 = htonl(flow->skb_mark);
+        break;
+
     CASE_MFF_REGS:
         value->be32 = htonl(flow->regs[mf->id - MFF_REG0]);
         break;
@@ -1097,6 +1086,22 @@ mf_set_value(const struct mf_field *mf,
     case MFF_TUN_ID:
         match_set_tun_id(match, value->be64);
         break;
+    case MFF_TUN_SRC:
+        match_set_tun_src(match, value->be32);
+        break;
+    case MFF_TUN_DST:
+        match_set_tun_dst(match, value->be32);
+        break;
+    case MFF_TUN_FLAGS:
+        match_set_tun_flags(match, ntohs(value->be16));
+        break;
+    case MFF_TUN_TOS:
+        match_set_tun_tos(match, value->u8);
+        break;
+    case MFF_TUN_TTL:
+        match_set_tun_ttl(match, value->u8);
+        break;
+
     case MFF_METADATA:
         match_set_metadata(match, value->be64);
         break;
@@ -1105,6 +1110,14 @@ mf_set_value(const struct mf_field *mf,
         match_set_in_port(match, ntohs(value->be16));
         break;
 
+    case MFF_SKB_PRIORITY:
+        match_set_skb_priority(match, ntohl(value->be32));
+        break;
+
+    case MFF_SKB_MARK:
+        match_set_skb_mark(match, ntohl(value->be32));
+        break;
+
     CASE_MFF_REGS:
         match_set_reg(match, mf->id - MFF_REG0, ntohl(value->be32));
         break;
@@ -1240,6 +1253,22 @@ mf_set_flow_value(const struct mf_field *mf,
     case MFF_TUN_ID:
         flow->tunnel.tun_id = value->be64;
         break;
+    case MFF_TUN_SRC:
+        flow->tunnel.ip_src = value->be32;
+        break;
+    case MFF_TUN_DST:
+        flow->tunnel.ip_dst = value->be32;
+        break;
+    case MFF_TUN_FLAGS:
+        flow->tunnel.flags = ntohs(value->be16);
+        break;
+    case MFF_TUN_TOS:
+        flow->tunnel.ip_tos = value->u8;
+        break;
+    case MFF_TUN_TTL:
+        flow->tunnel.ip_ttl = value->u8;
+        break;
+
     case MFF_METADATA:
         flow->metadata = value->be64;
         break;
@@ -1248,6 +1277,14 @@ mf_set_flow_value(const struct mf_field *mf,
         flow->in_port = ntohs(value->be16);
         break;
 
+    case MFF_SKB_PRIORITY:
+        flow->skb_priority = ntohl(value->be32);
+        break;
+
+    case MFF_SKB_MARK:
+        flow->skb_mark = ntohl(value->be32);
+        break;
+
     CASE_MFF_REGS:
         flow->regs[mf->id - MFF_REG0] = ntohl(value->be32);
         break;
@@ -1398,6 +1435,22 @@ mf_set_wild(const struct mf_field *mf, struct match *match)
     case MFF_TUN_ID:
         match_set_tun_id_masked(match, htonll(0), htonll(0));
         break;
+    case MFF_TUN_SRC:
+        match_set_tun_src_masked(match, htonl(0), htonl(0));
+        break;
+    case MFF_TUN_DST:
+        match_set_tun_dst_masked(match, htonl(0), htonl(0));
+        break;
+    case MFF_TUN_FLAGS:
+        match_set_tun_flags_masked(match, 0, 0);
+        break;
+    case MFF_TUN_TOS:
+        match_set_tun_tos_masked(match, 0, 0);
+        break;
+    case MFF_TUN_TTL:
+        match_set_tun_ttl_masked(match, 0, 0);
+        break;
+
     case MFF_METADATA:
         match_set_metadata_masked(match, htonll(0), htonll(0));
 
@@ -1406,6 +1459,16 @@ mf_set_wild(const struct mf_field *mf, struct match *match)
         match->wc.masks.in_port = 0;
         break;
 
+    case MFF_SKB_PRIORITY:
+        match->flow.skb_priority = 0;
+        match->wc.masks.skb_priority = 0;
+        break;
+
+    case MFF_SKB_MARK:
+        match->flow.skb_mark = 0;
+        match->wc.masks.skb_mark = 0;
+        break;
+
     CASE_MFF_REGS:
         match_set_reg_masked(match, mf->id - MFF_REG0, 0, 0);
         break;
@@ -1560,6 +1623,8 @@ mf_set(const struct mf_field *mf,
 
     switch (mf->id) {
     case MFF_IN_PORT:
+    case MFF_SKB_MARK:
+    case MFF_SKB_PRIORITY:
     case MFF_ETH_TYPE:
     case MFF_DL_VLAN:
     case MFF_DL_VLAN_PCP:
@@ -1578,6 +1643,22 @@ mf_set(const struct mf_field *mf,
     case MFF_TUN_ID:
         match_set_tun_id_masked(match, value->be64, mask->be64);
         break;
+    case MFF_TUN_SRC:
+        match_set_tun_src_masked(match, value->be32, mask->be32);
+        break;
+    case MFF_TUN_DST:
+        match_set_tun_dst_masked(match, value->be32, mask->be32);
+        break;
+    case MFF_TUN_FLAGS:
+        match_set_tun_flags_masked(match, ntohs(value->be16), ntohs(mask->be16));
+        break;
+    case MFF_TUN_TTL:
+        match_set_tun_ttl_masked(match, value->u8, mask->u8);
+        break;
+    case MFF_TUN_TOS:
+        match_set_tun_tos_masked(match, value->u8, mask->u8);
+        break;
+
     case MFF_METADATA:
         match_set_metadata_masked(match, value->be64, mask->be64);
         break;
@@ -1736,8 +1817,15 @@ mf_random_value(const struct mf_field *mf, union mf_value *value)
 
     switch (mf->id) {
     case MFF_TUN_ID:
+    case MFF_TUN_SRC:
+    case MFF_TUN_DST:
+    case MFF_TUN_TOS:
+    case MFF_TUN_TTL:
+    case MFF_TUN_FLAGS:
     case MFF_METADATA:
     case MFF_IN_PORT:
+    case MFF_SKB_MARK:
+    case MFF_SKB_PRIORITY:
     CASE_MFF_REGS:
     case MFF_ETH_SRC:
     case MFF_ETH_DST:
@@ -1941,7 +2029,10 @@ mf_from_ofp_port_string(const struct mf_field *mf, const char *s,
     uint16_t port;
 
     assert(mf->n_bytes == sizeof(ovs_be16));
-    if (ofputil_port_from_string(s, &port)) {
+    if (*s == '-') {
+        return xasprintf("%s: negative values not supported for %s",
+                         s, mf->name);
+    } else if (ofputil_port_from_string(s, &port)) {
         *valuep = htons(port);
         *maskp = htons(UINT16_MAX);
         return NULL;
@@ -1994,13 +2085,77 @@ mf_from_frag_string(const char *s, uint8_t *valuep, uint8_t *maskp)
                      "\"yes\", \"first\", \"later\", \"not_first\"", s);
 }
 
+static int
+parse_flow_tun_flags(const char *s_, const char *(*bit_to_string)(uint32_t),
+                     ovs_be16 *res)
+{
+    uint32_t result = 0;
+    char *save_ptr = NULL;
+    char *name;
+    int rc = 0;
+    char *s = xstrdup(s_);
+
+    for (name = strtok_r((char *)s, " |", &save_ptr); name;
+         name = strtok_r(NULL, " |", &save_ptr)) {
+        int name_len;
+        unsigned long long int flags;
+        uint32_t bit;
+        int n0;
+
+        if (sscanf(name, "%lli%n", &flags, &n0) > 0 && n0 > 0) {
+            result |= flags;
+            continue;
+        }
+        name_len = strlen(name);
+        for (bit = 1; bit; bit <<= 1) {
+            const char *fname = bit_to_string(bit);
+            size_t len;
+
+            if (!fname) {
+                continue;
+            }
+
+            len = strlen(fname);
+            if (len != name_len) {
+                continue;
+            }
+            if (!strncmp(name, fname, len)) {
+                result |= bit;
+                break;
+            }
+        }
+
+        if (!bit) {
+            rc = -ENOENT;
+            goto out;
+        }
+    }
+
+    *res = htons(result);
+out:
+    free(s);
+    return rc;
+}
+
+static char *
+mf_from_tun_flags_string(const char *s, ovs_be16 *valuep, ovs_be16 *maskp)
+{
+    if (!parse_flow_tun_flags(s, flow_tun_flag_to_string, valuep)) {
+        *maskp = htons(UINT16_MAX);
+        return NULL;
+    }
+
+    return xasprintf("%s: unknown tunnel flags (valid flags are \"df\", "
+                     "\"csum\", \"key\"", s);
+}
+
 /* Parses 's', a string value for field 'mf', into 'value' and 'mask'.  Returns
  * NULL if successful, otherwise a malloc()'d string describing the error. */
 char *
 mf_parse(const struct mf_field *mf, const char *s,
          union mf_value *value, union mf_value *mask)
 {
-    if (!strcasecmp(s, "any") || !strcmp(s, "*")) {
+    if (!strcmp(s, "*")) {
         memset(value, 0, mf->n_bytes);
         memset(mask, 0, mf->n_bytes);
         return NULL;
@@ -2026,6 +2181,10 @@ mf_parse(const struct mf_field *mf, const char *s,
 
     case MFS_FRAG:
         return mf_from_frag_string(s, &value->u8, &mask->u8);
+
+    case MFS_TNL_FLAGS:
+        assert(mf->n_bytes == sizeof(ovs_be16));
+        return mf_from_tun_flags_string(s, &value->be16, &mask->be16);
     }
     NOT_REACHED();
 }
@@ -2103,6 +2262,12 @@ mf_format_frag_string(const uint8_t *valuep, const uint8_t *maskp,
     ds_put_cstr(s, "<error>");
 }
 
+static void
+mf_format_tnl_flags_string(const ovs_be16 *valuep, struct ds *s)
+{
+    format_flags(s, flow_tun_flag_to_string, ntohs(*valuep), '|');
+}
+
 /* Appends to 's' a string representation of field 'mf' whose value is in
  * 'value' and 'mask'.  'mask' may be NULL to indicate an exact match. */
 void
@@ -2148,6 +2313,10 @@ mf_format(const struct mf_field *mf,
         mf_format_frag_string(&value->u8, &mask->u8, s);
         break;
 
+    case MFS_TNL_FLAGS:
+        mf_format_tnl_flags_string(&value->be16, s);
+        break;
+
     default:
         NOT_REACHED();
     }
index 60bfeca..3675883 100644 (file)
@@ -32,8 +32,15 @@ struct match;
 enum mf_field_id {
     /* Metadata. */
     MFF_TUN_ID,                 /* be64 */
+    MFF_TUN_SRC,                /* be32 */
+    MFF_TUN_DST,                /* be32 */
+    MFF_TUN_FLAGS,              /* be16 */
+    MFF_TUN_TTL,                /* u8 */
+    MFF_TUN_TOS,                /* u8 */
     MFF_METADATA,               /* be64 */
     MFF_IN_PORT,                /* be16 */
+    MFF_SKB_PRIORITY,           /* be32 */
+    MFF_SKB_MARK,               /* be32 */
 
 #if FLOW_N_REGS > 0
     MFF_REG0,                   /* be32 */
@@ -195,7 +202,8 @@ enum mf_string {
     MFS_IPV4,
     MFS_IPV6,
     MFS_OFP_PORT,               /* An OpenFlow port number or name. */
-    MFS_FRAG                    /* no, yes, first, later, not_later */
+    MFS_FRAG,                   /* no, yes, first, later, not_later */
+    MFS_TNL_FLAGS,              /* FLOW_TNL_F_* flags */
 };
 
 struct mf_field {
index f8b1188..9094d04 100644 (file)
@@ -17,7 +17,6 @@
 #include <config.h>
 
 #include <stdlib.h>
-#include <config.h>
 #include <assert.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -399,7 +398,6 @@ netdev_bsd_destroy(struct netdev_dev *netdev_dev_)
 static int
 netdev_bsd_open_system(struct netdev_dev *netdev_dev_, struct netdev **netdevp)
 {
-    struct netdev_dev_bsd *netdev_dev = netdev_dev_bsd_cast(netdev_dev_);
     struct netdev_bsd *netdev;
     int error;
     enum netdev_flags flags;
@@ -415,15 +413,6 @@ netdev_bsd_open_system(struct netdev_dev *netdev_dev_, struct netdev **netdevp)
         goto error;
     }
 
-    /* The first user that opens a tap port(from dpif_create_and_open()) will
-     * receive the file descriptor associated with the tap device. Instead, the
-     * following users will open the tap device as a normal 'system' device. */
-    if (!strcmp(netdev_dev_get_type(netdev_dev_), "tap") &&
-            !netdev_dev->tap_opened) {
-        netdev_dev->tap_opened = true;
-        netdev->netdev_fd = netdev_dev->tap_fd;
-    }
-
     *netdevp = &netdev->netdev;
     return 0;
 
@@ -451,6 +440,9 @@ static int
 netdev_bsd_listen(struct netdev *netdev_)
 {
     struct netdev_bsd *netdev = netdev_bsd_cast(netdev_);
+    struct netdev_dev_bsd *netdev_dev =
+                              netdev_dev_bsd_cast(netdev_get_dev(netdev_));
+
     char errbuf[PCAP_ERRBUF_SIZE];
     int error;
     int fd = -1;
@@ -460,6 +452,13 @@ netdev_bsd_listen(struct netdev *netdev_)
         return 0;
     }
 
+    if (!strcmp(netdev_get_type(netdev_), "tap") &&
+            !netdev_dev->tap_opened) {
+        netdev->netdev_fd = netdev_dev->tap_fd;
+        netdev_dev->tap_opened = true;
+        return 0;
+    }
+
     /* open the pcap device. The device is opened in non-promiscuous mode
      * because the interface flags are manually set by the caller. */
     errbuf[0] = '\0';
@@ -1298,7 +1297,7 @@ const struct netdev_class netdev_bsd_class = {
     netdev_bsd_get_in6,
     NULL, /* add_router */
     NULL, /* get_next_hop */
-    NULL, /* get_drv_info */
+    NULL, /* get_status */
     NULL, /* arp_lookup */
 
     netdev_bsd_update_flags,
@@ -1358,7 +1357,7 @@ const struct netdev_class netdev_tap_class = {
     netdev_bsd_get_in6,
     NULL, /* add_router */
     NULL, /* get_next_hop */
-    NULL, /* get_drv_info */
+    NULL, /* get_status */
     NULL, /* arp_lookup */
 
     netdev_bsd_update_flags,
index 6aa4084..4e97f55 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2010, 2011, 2012, 2013 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -319,6 +319,7 @@ static const struct netdev_class dummy_class = {
     netdev_dummy_destroy,
     NULL,                       /* get_config */
     NULL,                       /* set_config */
+    NULL,                       /* get_tunnel_config */
 
     netdev_dummy_open,
     netdev_dummy_close,
@@ -362,7 +363,7 @@ static const struct netdev_class dummy_class = {
     NULL,                       /* get_in6 */
     NULL,                       /* add_router */
     NULL,                       /* get_next_hop */
-    NULL,                       /* get_drv_info */
+    NULL,                       /* get_status */
     NULL,                       /* arp_lookup */
 
     netdev_dummy_update_flags,
index 412a92d..e1936fa 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -497,28 +497,6 @@ netdev_linux_wait(void)
     netdev_linux_miimon_wait();
 }
 
-static int
-netdev_linux_get_drvinfo(struct netdev_dev_linux *netdev_dev)
-{
-
-    int error;
-
-    if (netdev_dev->cache_valid & VALID_DRVINFO) {
-        return 0;
-    }
-
-    COVERAGE_INC(netdev_get_ethtool);
-    memset(&netdev_dev->drvinfo, 0, sizeof netdev_dev->drvinfo);
-    error = netdev_linux_do_ethtool(netdev_dev->netdev_dev.name,
-                                    (struct ethtool_cmd *)&netdev_dev->drvinfo,
-                                    ETHTOOL_GDRVINFO,
-                                    "ETHTOOL_GDRVINFO");
-    if (!error) {
-        netdev_dev->cache_valid |= VALID_DRVINFO;
-    }
-    return error;
-}
-
 static void
 netdev_dev_linux_changed(struct netdev_dev_linux *dev,
                          unsigned int ifi_flags,
@@ -743,7 +721,6 @@ netdev_linux_destroy(struct netdev_dev *netdev_dev_)
 static int
 netdev_linux_open(struct netdev_dev *netdev_dev_, struct netdev **netdevp)
 {
-    struct netdev_dev_linux *netdev_dev = netdev_dev_linux_cast(netdev_dev_);
     struct netdev_linux *netdev;
     enum netdev_flags flags;
     int error;
@@ -768,17 +745,6 @@ netdev_linux_open(struct netdev_dev *netdev_dev_, struct netdev **netdevp)
         }
     }
 
-    if (!strcmp(netdev_dev_get_type(netdev_dev_), "tap") &&
-        !netdev_dev->state.tap.opened) {
-
-        /* We assume that the first user of the tap device is the primary user
-         * and give them the tap FD.  Subsequent users probably just expect
-         * this to be a system device so open it normally to avoid send/receive
-         * directions appearing to be reversed. */
-        netdev->fd = netdev_dev->state.tap.fd;
-        netdev_dev->state.tap.opened = true;
-    }
-
     *netdevp = &netdev->netdev;
     return 0;
 
@@ -803,6 +769,8 @@ static int
 netdev_linux_listen(struct netdev *netdev_)
 {
     struct netdev_linux *netdev = netdev_linux_cast(netdev_);
+    struct netdev_dev_linux *netdev_dev =
+                                netdev_dev_linux_cast(netdev_get_dev(netdev_));
     struct sockaddr_ll sll;
     int ifindex;
     int error;
@@ -812,6 +780,13 @@ netdev_linux_listen(struct netdev *netdev_)
         return 0;
     }
 
+    if (!strcmp(netdev_get_type(netdev_), "tap")
+        && !netdev_dev->state.tap.opened) {
+        netdev->fd = netdev_dev->state.tap.fd;
+        netdev_dev->state.tap.opened = true;
+        return 0;
+    }
+
     /* Create file descriptor. */
     fd = socket(PF_PACKET, SOCK_RAW, 0);
     if (fd < 0) {
@@ -1028,6 +1003,7 @@ netdev_linux_set_etheraddr(struct netdev *netdev_,
     struct netdev_dev_linux *netdev_dev =
                                 netdev_dev_linux_cast(netdev_get_dev(netdev_));
     int error;
+    bool up_again = false;
 
     if (netdev_dev->cache_valid & VALID_ETHERADDR) {
         if (netdev_dev->ether_addr_error) {
@@ -1039,6 +1015,15 @@ netdev_linux_set_etheraddr(struct netdev *netdev_,
         netdev_dev->cache_valid &= ~VALID_ETHERADDR;
     }
 
+    /* Tap devices must be brought down before setting the address. */
+    if (!strcmp(netdev_get_type(netdev_), "tap")) {
+        enum netdev_flags flags;
+
+        if (!netdev_get_flags(netdev_, &flags) && (flags & NETDEV_UP)) {
+            netdev_turn_flags_off(netdev_, NETDEV_UP, false);
+            up_again = true;
+        }
+    }
     error = set_etheraddr(netdev_get_name(netdev_), mac);
     if (!error || error == ENODEV) {
         netdev_dev->ether_addr_error = error;
@@ -1048,6 +1033,10 @@ netdev_linux_set_etheraddr(struct netdev *netdev_,
         }
     }
 
+    if (up_again) {
+        netdev_turn_flags_on(netdev_, NETDEV_UP, false);
+    }
+
     return error;
 }
 
@@ -1333,7 +1322,7 @@ get_stats_via_vport(const struct netdev *netdev_,
         int error;
 
         error = netdev_vport_get_stats(netdev_, stats);
-        if (error) {
+        if (error && error != ENOENT) {
             VLOG_WARN_RL(&rl, "%s: obtaining netdev stats via vport failed "
                          "(%s)", netdev_get_name(netdev_), strerror(error));
         }
@@ -1488,6 +1477,41 @@ netdev_internal_get_stats(const struct netdev *netdev_,
     return netdev_dev->vport_stats_error;
 }
 
+static int
+netdev_internal_set_stats(struct netdev *netdev,
+                          const struct netdev_stats *stats)
+{
+    struct ovs_vport_stats vport_stats;
+    struct dpif_linux_vport vport;
+    int err;
+
+    vport_stats.rx_packets = stats->rx_packets;
+    vport_stats.tx_packets = stats->tx_packets;
+    vport_stats.rx_bytes = stats->rx_bytes;
+    vport_stats.tx_bytes = stats->tx_bytes;
+    vport_stats.rx_errors = stats->rx_errors;
+    vport_stats.tx_errors = stats->tx_errors;
+    vport_stats.rx_dropped = stats->rx_dropped;
+    vport_stats.tx_dropped = stats->tx_dropped;
+
+    dpif_linux_vport_init(&vport);
+    vport.cmd = OVS_VPORT_CMD_SET;
+    vport.name = netdev_get_name(netdev);
+    vport.stats = &vport_stats;
+
+    err = dpif_linux_vport_transact(&vport, NULL, NULL);
+
+    /* If the vport layer doesn't know about the device, that doesn't mean it
+     * doesn't exist (after all were able to open it when netdev_open() was
+     * called), it just means that it isn't attached and we'll be getting
+     * stats a different way. */
+    if (err == ENODEV) {
+        err = EOPNOTSUPP;
+    }
+
+    return err;
+}
+
 static void
 netdev_linux_read_features(struct netdev_dev_linux *netdev_dev)
 {
@@ -2277,13 +2301,26 @@ netdev_linux_get_next_hop(const struct in_addr *host, struct in_addr *next_hop,
 }
 
 static int
-netdev_linux_get_drv_info(const struct netdev *netdev, struct smap *smap)
+netdev_linux_get_status(const struct netdev *netdev, struct smap *smap)
 {
-    int error;
-    struct netdev_dev_linux *netdev_dev =
-                                netdev_dev_linux_cast(netdev_get_dev(netdev));
+    struct netdev_dev_linux *netdev_dev;
+    int error = 0;
+
+    netdev_dev = netdev_dev_linux_cast(netdev_get_dev(netdev));
+    if (!(netdev_dev->cache_valid & VALID_DRVINFO)) {
+        struct ethtool_cmd *cmd = (struct ethtool_cmd *) &netdev_dev->drvinfo;
+
+        COVERAGE_INC(netdev_get_ethtool);
+        memset(&netdev_dev->drvinfo, 0, sizeof netdev_dev->drvinfo);
+        error = netdev_linux_do_ethtool(netdev_dev->netdev_dev.name,
+                                        cmd,
+                                        ETHTOOL_GDRVINFO,
+                                        "ETHTOOL_GDRVINFO");
+        if (!error) {
+            netdev_dev->cache_valid |= VALID_DRVINFO;
+        }
+    }
 
-    error = netdev_linux_get_drvinfo(netdev_dev);
     if (!error) {
         smap_add(smap, "driver_name", netdev_dev->drvinfo.driver);
         smap_add(smap, "driver_version", netdev_dev->drvinfo.version);
@@ -2293,8 +2330,8 @@ netdev_linux_get_drv_info(const struct netdev *netdev, struct smap *smap)
 }
 
 static int
-netdev_internal_get_drv_info(const struct netdev *netdev OVS_UNUSED,
-                             struct smap *smap)
+netdev_internal_get_status(const struct netdev *netdev OVS_UNUSED,
+                           struct smap *smap)
 {
     smap_add(smap, "driver_name", "openvswitch");
     return 0;
@@ -2327,7 +2364,7 @@ netdev_linux_arp_lookup(const struct netdev *netdev,
         memcpy(mac, r.arp_ha.sa_data, ETH_ADDR_LEN);
     } else if (retval != ENXIO) {
         VLOG_WARN_RL(&rl, "%s: could not look up ARP entry for "IP_FMT": %s",
-                     netdev_get_name(netdev), IP_ARGS(&ip), strerror(retval));
+                     netdev_get_name(netdev), IP_ARGS(ip), strerror(retval));
     }
     return retval;
 }
@@ -2396,6 +2433,7 @@ netdev_linux_change_seq(const struct netdev *netdev)
     netdev_linux_destroy,                                       \
     NULL,                       /* get_config */                \
     NULL,                       /* set_config */                \
+    NULL,                       /* get_tunnel_config */         \
                                                                 \
     netdev_linux_open,                                          \
     netdev_linux_close,                                         \
@@ -2454,7 +2492,7 @@ const struct netdev_class netdev_linux_class =
         netdev_linux_get_stats,
         NULL,                    /* set_stats */
         netdev_linux_get_features,
-        netdev_linux_get_drv_info);
+        netdev_linux_get_status);
 
 const struct netdev_class netdev_tap_class =
     NETDEV_LINUX_CLASS(
@@ -2463,16 +2501,16 @@ const struct netdev_class netdev_tap_class =
         netdev_tap_get_stats,
         NULL,                   /* set_stats */
         netdev_linux_get_features,
-        netdev_linux_get_drv_info);
+        netdev_linux_get_status);
 
 const struct netdev_class netdev_internal_class =
     NETDEV_LINUX_CLASS(
         "internal",
         netdev_linux_create,
         netdev_internal_get_stats,
-        netdev_vport_set_stats,
+        netdev_internal_set_stats,
         NULL,                  /* get_features */
-        netdev_internal_get_drv_info);
+        netdev_internal_get_status);
 \f
 /* HTB traffic control class. */
 
@@ -2668,7 +2706,7 @@ htb_parse_qdisc_details__(struct netdev *netdev,
         enum netdev_features current;
 
         netdev_get_features(netdev, &current, NULL, NULL, NULL);
-        hc->max_rate = netdev_features_to_bps(current) / 8;
+        hc->max_rate = netdev_features_to_bps(current, 100 * 1000 * 1000) / 8;
     }
     hc->min_rate = hc->max_rate;
     hc->burst = 0;
@@ -3147,7 +3185,7 @@ hfsc_parse_qdisc_details__(struct netdev *netdev, const struct smap *details,
         enum netdev_features current;
 
         netdev_get_features(netdev, &current, NULL, NULL, NULL);
-        max_rate = netdev_features_to_bps(current) / 8;
+        max_rate = netdev_features_to_bps(current, 100 * 1000 * 1000) / 8;
     }
 
     class->min_rate = max_rate;
index f1b123a..6644c8d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -139,6 +139,13 @@ struct netdev_class {
      * pointer. */
     int (*set_config)(struct netdev_dev *netdev_dev, const struct smap *args);
 
+    /* Returns the tunnel configuration of 'netdev_dev'.  If 'netdev_dev' is
+     * not a tunnel, returns null.
+     *
+     * If this function would always return null, it may be null instead. */
+    const struct netdev_tunnel_config *
+        (*get_tunnel_config)(const struct netdev_dev *netdev_dev);
+
     /* Attempts to open a network device.  On success, sets 'netdevp'
      * to the new network device. */
     int (*open)(struct netdev_dev *netdev_dev, struct netdev **netdevp);
@@ -546,16 +553,16 @@ struct netdev_class {
 
     /* Retrieves driver information of the device.
      *
-     * Populates 'sh' with key-value pairs representing the status of the
-     * device.  Driver info is a set of key-value string pairs
-     * representing netdev type specific information.  For more information see
+     * Populates 'smap' with key-value pairs representing the status of the
+     * device.  'smap' is a set of key-value string pairs representing netdev
+     * type specific information.  For more information see
      * ovs-vswitchd.conf.db(5).
      *
      * The caller is responsible for destroying 'smap' and its data.
      *
      * This function may be set to null if it would always return EOPNOTSUPP
      * anyhow. */
-    int (*get_drv_info)(const struct netdev *netdev, struct smap *smap);
+    int (*get_status)(const struct netdev *netdev, struct smap *smap);
 
     /* Looks up the ARP table entry for 'ip' on 'netdev' and stores the
      * corresponding MAC address in 'mac'.  A return value of ENXIO, in
index 4168959..3308544 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2010, 2011, 2012, 2013 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 
 VLOG_DEFINE_THIS_MODULE(netdev_vport);
 
+/* Default to the OTV port, per the VXLAN IETF draft. */
+#define VXLAN_DST_PORT 8472
+
+#define DEFAULT_TTL 64
+
 struct netdev_dev_vport {
     struct netdev_dev netdev_dev;
     struct ofpbuf *options;
-    int dp_ifindex;             /* -1 if unknown. */
-    uint32_t port_no;           /* UINT32_MAX if unknown. */
     unsigned int change_seq;
-};
-
-struct netdev_vport {
-    struct netdev netdev;
+    uint8_t etheraddr[ETH_ADDR_LEN];
+    struct netdev_tunnel_config tnl_cfg;
 };
 
 struct vport_class {
     enum ovs_vport_type type;
     struct netdev_class netdev_class;
     int (*parse_config)(const char *name, const char *type,
-                        const struct smap *args, struct ofpbuf *options);
+                        const struct smap *args, struct ofpbuf *options,
+                        struct netdev_tunnel_config *tnl_cfg);
     int (*unparse_config)(const char *name, const char *type,
                           const struct nlattr *options, size_t options_len,
                           struct smap *args);
@@ -80,8 +82,6 @@ static int tnl_port_config_from_nlattr(const struct nlattr *options,
                                        size_t options_len,
                                        struct nlattr *a[OVS_TUNNEL_ATTR_MAX + 1]);
 
-static const char *netdev_vport_get_tnl_iface(const struct netdev *netdev);
-
 static bool
 is_vport_class(const struct netdev_class *class)
 {
@@ -102,12 +102,16 @@ netdev_dev_vport_cast(const struct netdev_dev *netdev_dev)
     return CONTAINER_OF(netdev_dev, struct netdev_dev_vport, netdev_dev);
 }
 
-static struct netdev_vport *
-netdev_vport_cast(const struct netdev *netdev)
+static struct netdev_dev_vport *
+netdev_vport_get_dev(const struct netdev *netdev)
 {
-    struct netdev_dev *netdev_dev = netdev_get_dev(netdev);
-    assert(is_vport_class(netdev_dev_get_class(netdev_dev)));
-    return CONTAINER_OF(netdev, struct netdev_vport, netdev);
+    return netdev_dev_vport_cast(netdev_get_dev(netdev));
+}
+
+static const struct netdev_tunnel_config *
+get_netdev_tunnel_config(const struct netdev_dev *netdev_dev)
+{
+    return &netdev_dev_vport_cast(netdev_dev)->tnl_cfg;
 }
 
 /* If 'netdev' is a vport netdev, returns an ofpbuf that contains Netlink
@@ -136,6 +140,12 @@ netdev_vport_get_vport_type(const struct netdev *netdev)
             : OVS_VPORT_TYPE_UNSPEC);
 }
 
+static uint32_t
+get_u32_or_zero(const struct nlattr *a)
+{
+    return a ? nl_attr_get_u32(a) : 0;
+}
+
 const char *
 netdev_vport_get_netdev_type(const struct dpif_linux_vport *vport)
 {
@@ -159,7 +169,7 @@ netdev_vport_get_netdev_type(const struct dpif_linux_vport *vport)
                                         a)) {
             break;
         }
-        return (nl_attr_get_u32(a[OVS_TUNNEL_ATTR_FLAGS]) & TNL_F_IPSEC
+        return (get_u32_or_zero(a[OVS_TUNNEL_ATTR_FLAGS]) & TNL_F_IPSEC
                 ? "ipsec_gre" : "gre");
 
     case OVS_VPORT_TYPE_GRE64:
@@ -167,12 +177,16 @@ netdev_vport_get_netdev_type(const struct dpif_linux_vport *vport)
                                         a)) {
             break;
         }
-        return (nl_attr_get_u32(a[OVS_TUNNEL_ATTR_FLAGS]) & TNL_F_IPSEC
+        return (get_u32_or_zero(a[OVS_TUNNEL_ATTR_FLAGS]) & TNL_F_IPSEC
                 ? "ipsec_gre64" : "gre64");
 
     case OVS_VPORT_TYPE_CAPWAP:
         return "capwap";
 
+    case OVS_VPORT_TYPE_VXLAN:
+        return "vxlan";
+
+    case OVS_VPORT_TYPE_FT_GRE:
     case __OVS_VPORT_TYPE_MAX:
         break;
     }
@@ -188,12 +202,10 @@ netdev_vport_create(const struct netdev_class *netdev_class, const char *name,
 {
     struct netdev_dev_vport *dev;
 
-    dev = xmalloc(sizeof *dev);
+    dev = xzalloc(sizeof *dev);
     netdev_dev_init(&dev->netdev_dev, name, netdev_class);
-    dev->options = NULL;
-    dev->dp_ifindex = -1;
-    dev->port_no = UINT32_MAX;
     dev->change_seq = 1;
+    eth_addr_random(dev->etheraddr);
 
     *netdev_devp = &dev->netdev_dev;
     route_table_register();
@@ -212,21 +224,16 @@ netdev_vport_destroy(struct netdev_dev *netdev_dev_)
 }
 
 static int
-netdev_vport_open(struct netdev_dev *netdev_dev_, struct netdev **netdevp)
+netdev_vport_open(struct netdev_dev *netdev_dev, struct netdev **netdevp)
 {
-    struct netdev_vport *netdev;
-
-    netdev = xmalloc(sizeof *netdev);
-    netdev_init(&netdev->netdev, netdev_dev_);
-
-    *netdevp = &netdev->netdev;
+    *netdevp = xmalloc(sizeof **netdevp);
+    netdev_init(*netdevp, netdev_dev);
     return 0;
 }
 
 static void
-netdev_vport_close(struct netdev *netdev_)
+netdev_vport_close(struct netdev *netdev)
 {
-    struct netdev_vport *netdev = netdev_vport_cast(netdev_);
     free(netdev);
 }
 
@@ -251,8 +258,6 @@ netdev_vport_get_config(struct netdev_dev *dev_, struct smap *args)
         }
 
         dev->options = ofpbuf_clone_data(reply.options, reply.options_len);
-        dev->dp_ifindex = reply.dp_ifindex;
-        dev->port_no = reply.port_no;
         ofpbuf_delete(buf);
     }
 
@@ -274,12 +279,13 @@ netdev_vport_set_config(struct netdev_dev *dev_, const struct smap *args)
     const struct vport_class *vport_class = vport_class_cast(netdev_class);
     struct netdev_dev_vport *dev = netdev_dev_vport_cast(dev_);
     const char *name = netdev_dev_get_name(dev_);
+    struct netdev_tunnel_config tnl_cfg;
     struct ofpbuf *options;
     int error;
 
     options = ofpbuf_new(64);
     error = vport_class->parse_config(name, netdev_dev_get_type(dev_),
-                                      args, options);
+                                      args, options, &tnl_cfg);
     if (!error
         && (!dev->options
             || options->size != dev->options->size
@@ -298,6 +304,7 @@ netdev_vport_set_config(struct netdev_dev *dev_, const struct smap *args)
              * dpif_port_add()). */
             ofpbuf_delete(dev->options);
             dev->options = options;
+            dev->tnl_cfg = tnl_cfg;
             options = NULL;
             error = 0;
         }
@@ -307,69 +314,21 @@ netdev_vport_set_config(struct netdev_dev *dev_, const struct smap *args)
     return error;
 }
 
-static int
-netdev_vport_send(struct netdev *netdev, const void *data, size_t size)
-{
-    struct netdev_dev *dev_ = netdev_get_dev(netdev);
-    struct netdev_dev_vport *dev = netdev_dev_vport_cast(dev_);
-
-    if (dev->dp_ifindex == -1) {
-        const char *name = netdev_get_name(netdev);
-        struct dpif_linux_vport reply;
-        struct ofpbuf *buf;
-        int error;
-
-        error = dpif_linux_vport_get(name, &reply, &buf);
-        if (error) {
-            VLOG_ERR_RL(&rl, "%s: failed to query vport for send (%s)",
-                        name, strerror(error));
-            return error;
-        }
-        dev->dp_ifindex = reply.dp_ifindex;
-        dev->port_no = reply.port_no;
-        ofpbuf_delete(buf);
-    }
-
-    return dpif_linux_vport_send(dev->dp_ifindex, dev->port_no, data, size);
-}
-
 static int
 netdev_vport_set_etheraddr(struct netdev *netdev,
                            const uint8_t mac[ETH_ADDR_LEN])
 {
-    struct dpif_linux_vport vport;
-    int error;
-
-    dpif_linux_vport_init(&vport);
-    vport.cmd = OVS_VPORT_CMD_SET;
-    vport.name = netdev_get_name(netdev);
-    vport.address = mac;
-
-    error = dpif_linux_vport_transact(&vport, NULL, NULL);
-    if (!error) {
-        netdev_vport_poll_notify(netdev);
-    }
-    return error;
+    memcpy(netdev_vport_get_dev(netdev)->etheraddr, mac, ETH_ADDR_LEN);
+    netdev_vport_poll_notify(netdev);
+    return 0;
 }
 
 static int
 netdev_vport_get_etheraddr(const struct netdev *netdev,
                            uint8_t mac[ETH_ADDR_LEN])
 {
-    struct dpif_linux_vport reply;
-    struct ofpbuf *buf;
-    int error;
-
-    error = dpif_linux_vport_get(netdev_get_name(netdev), &reply, &buf);
-    if (!error) {
-        if (reply.address) {
-            memcpy(mac, reply.address, ETH_ADDR_LEN);
-        } else {
-            error = EOPNOTSUPP;
-        }
-        ofpbuf_delete(buf);
-    }
-    return error;
+    memcpy(mac, netdev_vport_get_dev(netdev)->etheraddr, ETH_ADDR_LEN);
+    return 0;
 }
 
 /* Copies 'src' into 'dst', performing format conversion in the process.
@@ -402,21 +361,6 @@ netdev_stats_from_ovs_vport_stats(struct netdev_stats *dst,
     dst->tx_window_errors = 0;
 }
 
-/* Copies 'src' into 'dst', performing format conversion in the process. */
-static void
-netdev_stats_to_ovs_vport_stats(struct ovs_vport_stats *dst,
-                                const struct netdev_stats *src)
-{
-    dst->rx_packets = src->rx_packets;
-    dst->tx_packets = src->tx_packets;
-    dst->rx_bytes = src->rx_bytes;
-    dst->tx_bytes = src->tx_bytes;
-    dst->rx_errors = src->rx_errors;
-    dst->tx_errors = src->tx_errors;
-    dst->rx_dropped = src->rx_dropped;
-    dst->tx_dropped = src->tx_dropped;
-}
-
 int
 netdev_vport_get_stats(const struct netdev *netdev, struct netdev_stats *stats)
 {
@@ -439,39 +383,14 @@ netdev_vport_get_stats(const struct netdev *netdev, struct netdev_stats *stats)
     return 0;
 }
 
-int
-netdev_vport_set_stats(struct netdev *netdev, const struct netdev_stats *stats)
-{
-    struct ovs_vport_stats rtnl_stats;
-    struct dpif_linux_vport vport;
-    int err;
-
-    netdev_stats_to_ovs_vport_stats(&rtnl_stats, stats);
-
-    dpif_linux_vport_init(&vport);
-    vport.cmd = OVS_VPORT_CMD_SET;
-    vport.name = netdev_get_name(netdev);
-    vport.stats = &rtnl_stats;
-
-    err = dpif_linux_vport_transact(&vport, NULL, NULL);
-
-    /* If the vport layer doesn't know about the device, that doesn't mean it
-     * doesn't exist (after all were able to open it when netdev_open() was
-     * called), it just means that it isn't attached and we'll be getting
-     * stats a different way. */
-    if (err == ENODEV) {
-        err = EOPNOTSUPP;
-    }
-
-    return err;
-}
-
 static int
-netdev_vport_get_drv_info(const struct netdev *netdev, struct smap *smap)
+tunnel_get_status(const struct netdev *netdev, struct smap *smap)
 {
-    const char *iface = netdev_vport_get_tnl_iface(netdev);
+    static char iface[IFNAMSIZ];
+    ovs_be32 route;
 
-    if (iface) {
+    route = netdev_vport_get_dev(netdev)->tnl_cfg.ip_dst;
+    if (route_table_get_name(route, iface)) {
         struct netdev *egress_netdev;
 
         smap_add(smap, "tunnel_egress_iface", iface);
@@ -502,7 +421,7 @@ netdev_vport_update_flags(struct netdev *netdev OVS_UNUSED,
 static unsigned int
 netdev_vport_change_seq(const struct netdev *netdev)
 {
-    return netdev_dev_vport_cast(netdev_get_dev(netdev))->change_seq;
+    return netdev_vport_get_dev(netdev)->change_seq;
 }
 
 static void
@@ -517,37 +436,12 @@ netdev_vport_wait(void)
     route_table_wait();
 }
 \f
-/* get_tnl_iface() implementation. */
-static const char *
-netdev_vport_get_tnl_iface(const struct netdev *netdev)
-{
-    struct nlattr *a[OVS_TUNNEL_ATTR_MAX + 1];
-    ovs_be32 route;
-    struct netdev_dev_vport *ndv;
-    static char name[IFNAMSIZ];
-
-    ndv = netdev_dev_vport_cast(netdev_get_dev(netdev));
-    if (tnl_port_config_from_nlattr(ndv->options->data, ndv->options->size,
-                                    a)) {
-        return NULL;
-    }
-    route = nl_attr_get_be32(a[OVS_TUNNEL_ATTR_DST_IPV4]);
-
-    if (route_table_get_name(route, name)) {
-        return name;
-    }
-
-    return NULL;
-}
-\f
 /* Helper functions. */
 
 static void
 netdev_vport_poll_notify(const struct netdev *netdev)
 {
-    struct netdev_dev_vport *ndv;
-
-    ndv = netdev_dev_vport_cast(netdev_get_dev(netdev));
+    struct netdev_dev_vport *ndv = netdev_vport_get_dev(netdev);
 
     ndv->change_seq++;
     if (!ndv->change_seq) {
@@ -557,48 +451,58 @@ netdev_vport_poll_notify(const struct netdev *netdev)
 \f
 /* Code specific to individual vport types. */
 
-static void
-set_key(const struct smap *args, const char *name, uint16_t type,
-        struct ofpbuf *options)
+static ovs_be64
+parse_key(const struct smap *args, const char *name,
+          bool *present, bool *flow)
 {
     const char *s;
 
+    *present = false;
+    *flow = false;
+
     s = smap_get(args, name);
     if (!s) {
         s = smap_get(args, "key");
         if (!s) {
-            s = "0";
+            return 0;
         }
     }
 
+    *present = true;
+
     if (!strcmp(s, "flow")) {
-        /* This is the default if no attribute is present. */
+        *flow = true;
+        return 0;
     } else {
-        nl_msg_put_be64(options, type, htonll(strtoull(s, NULL, 0)));
+        return htonll(strtoull(s, NULL, 0));
     }
 }
 
 static int
 parse_tunnel_config(const char *name, const char *type,
-                    const struct smap *args, struct ofpbuf *options)
+                    const struct smap *args, struct ofpbuf *options,
+                    struct netdev_tunnel_config *tnl_cfg_)
 {
-    bool is_gre = false;
-    bool is_ipsec = false;
+    bool ipsec_mech_set, needs_dst_port, has_csum;
+    struct netdev_tunnel_config tnl_cfg;
     struct smap_node *node;
-    bool ipsec_mech_set = false;
-    ovs_be32 daddr = htonl(0);
-    ovs_be32 saddr = htonl(0);
-    uint32_t flags;
+    uint8_t flags;
+
+    flags = TNL_F_DF_DEFAULT;
+    has_csum = strstr(type, "gre");
+    ipsec_mech_set = false;
+    memset(&tnl_cfg, 0, sizeof tnl_cfg);
+
+    if (!strcmp(type, "capwap")) {
+        VLOG_WARN_ONCE("CAPWAP tunnel support is deprecated.");
+    }
 
-    flags = TNL_F_DF_DEFAULT | TNL_F_HDR_CACHE;
-    if (!strcmp(type, "gre") || !strcmp(type, "gre64")) {
-        is_gre = true;
-    } else if (!strcmp(type, "ipsec_gre") || !strcmp(type, "ipsec_gre64")) {
-        is_gre = true;
-        is_ipsec = true;
+    needs_dst_port = !strcmp(type, "vxlan");
+    tnl_cfg.ipsec = strstr(type, "ipsec");
+    if (tnl_cfg.ipsec) {
         flags |= TNL_F_IPSEC;
-        flags &= ~TNL_F_HDR_CACHE;
     }
+    tnl_cfg.dont_fragment = true;
 
     SMAP_FOR_EACH (node, args) {
         if (!strcmp(node->key, "remote_ip")) {
@@ -606,24 +510,26 @@ parse_tunnel_config(const char *name, const char *type,
             if (lookup_ip(node->value, &in_addr)) {
                 VLOG_WARN("%s: bad %s 'remote_ip'", name, type);
             } else {
-                daddr = in_addr.s_addr;
+                tnl_cfg.ip_dst = in_addr.s_addr;
             }
         } else if (!strcmp(node->key, "local_ip")) {
             struct in_addr in_addr;
             if (lookup_ip(node->value, &in_addr)) {
                 VLOG_WARN("%s: bad %s 'local_ip'", name, type);
             } else {
-                saddr = in_addr.s_addr;
+                tnl_cfg.ip_src = in_addr.s_addr;
             }
         } else if (!strcmp(node->key, "tos")) {
             if (!strcmp(node->value, "inherit")) {
                 flags |= TNL_F_TOS_INHERIT;
+                tnl_cfg.tos_inherit = true;
             } else {
                 char *endptr;
                 int tos;
                 tos = strtol(node->value, &endptr, 0);
                 if (*endptr == '\0' && tos == (tos & IP_DSCP_MASK)) {
                     nl_msg_put_u8(options, OVS_TUNNEL_ATTR_TOS, tos);
+                    tnl_cfg.tos = tos;
                 } else {
                     VLOG_WARN("%s: invalid TOS %s", name, node->value);
                 }
@@ -631,30 +537,26 @@ parse_tunnel_config(const char *name, const char *type,
         } else if (!strcmp(node->key, "ttl")) {
             if (!strcmp(node->value, "inherit")) {
                 flags |= TNL_F_TTL_INHERIT;
+                tnl_cfg.ttl_inherit = true;
             } else {
                 nl_msg_put_u8(options, OVS_TUNNEL_ATTR_TTL, atoi(node->value));
+                tnl_cfg.ttl = atoi(node->value);
             }
-        } else if (!strcmp(node->key, "csum") && is_gre) {
+        } else if (!strcmp(node->key, "dst_port") && needs_dst_port) {
+            tnl_cfg.dst_port = htons(atoi(node->value));
+            nl_msg_put_u16(options, OVS_TUNNEL_ATTR_DST_PORT,
+                           atoi(node->value));
+        } else if (!strcmp(node->key, "csum") && has_csum) {
             if (!strcmp(node->value, "true")) {
                 flags |= TNL_F_CSUM;
-            }
-        } else if (!strcmp(node->key, "df_inherit")) {
-            if (!strcmp(node->value, "true")) {
-                flags |= TNL_F_DF_INHERIT;
+                tnl_cfg.csum = true;
             }
         } else if (!strcmp(node->key, "df_default")) {
             if (!strcmp(node->value, "false")) {
                 flags &= ~TNL_F_DF_DEFAULT;
+                tnl_cfg.dont_fragment = false;
             }
-        } else if (!strcmp(node->key, "pmtud")) {
-            if (!strcmp(node->value, "true")) {
-                flags |= TNL_F_PMTUD;
-            }
-        } else if (!strcmp(node->key, "header_cache")) {
-            if (!strcmp(node->value, "false")) {
-                flags &= ~TNL_F_HDR_CACHE;
-            }
-        } else if (!strcmp(node->key, "peer_cert") && is_ipsec) {
+        } else if (!strcmp(node->key, "peer_cert") && tnl_cfg.ipsec) {
             if (smap_get(args, "certificate")) {
                 ipsec_mech_set = true;
             } else {
@@ -674,9 +576,9 @@ parse_tunnel_config(const char *name, const char *type,
                 }
                 ipsec_mech_set = true;
             }
-        } else if (!strcmp(node->key, "psk") && is_ipsec) {
+        } else if (!strcmp(node->key, "psk") && tnl_cfg.ipsec) {
             ipsec_mech_set = true;
-        } else if (is_ipsec
+        } else if (tnl_cfg.ipsec
                 && (!strcmp(node->key, "certificate")
                     || !strcmp(node->key, "private_key")
                     || !strcmp(node->key, "use_ssl_cert"))) {
@@ -690,7 +592,13 @@ parse_tunnel_config(const char *name, const char *type,
         }
     }
 
-    if (is_ipsec) {
+    /* Add a default destination port for VXLAN if none specified. */
+    if (needs_dst_port && !tnl_cfg.dst_port) {
+        nl_msg_put_u16(options, OVS_TUNNEL_ATTR_DST_PORT, VXLAN_DST_PORT);
+        tnl_cfg.dst_port = htons(VXLAN_DST_PORT);
+    }
+
+    if (tnl_cfg.ipsec) {
         static pid_t pid = 0;
         if (pid <= 0) {
             char *file_name = xasprintf("%s/%s", ovs_rundir(),
@@ -717,26 +625,43 @@ parse_tunnel_config(const char *name, const char *type,
         }
     }
 
-    set_key(args, "in_key", OVS_TUNNEL_ATTR_IN_KEY, options);
-    set_key(args, "out_key", OVS_TUNNEL_ATTR_OUT_KEY, options);
-
-    if (!daddr) {
+    if (!tnl_cfg.ip_dst) {
         VLOG_ERR("%s: %s type requires valid 'remote_ip' argument",
                  name, type);
         return EINVAL;
     }
-    nl_msg_put_be32(options, OVS_TUNNEL_ATTR_DST_IPV4, daddr);
+    nl_msg_put_be32(options, OVS_TUNNEL_ATTR_DST_IPV4, tnl_cfg.ip_dst);
 
-    if (saddr) {
-        if (ip_is_multicast(daddr)) {
+    if (tnl_cfg.ip_src) {
+        if (ip_is_multicast(tnl_cfg.ip_dst)) {
             VLOG_WARN("%s: remote_ip is multicast, ignoring local_ip", name);
+            tnl_cfg.ip_src = 0;
         } else {
-            nl_msg_put_be32(options, OVS_TUNNEL_ATTR_SRC_IPV4, saddr);
+            nl_msg_put_be32(options, OVS_TUNNEL_ATTR_SRC_IPV4, tnl_cfg.ip_src);
         }
     }
 
+    if (!tnl_cfg.ttl) {
+        tnl_cfg.ttl = DEFAULT_TTL;
+    }
+
+    tnl_cfg.in_key = parse_key(args, "in_key",
+                               &tnl_cfg.in_key_present,
+                               &tnl_cfg.in_key_flow);
+    if (tnl_cfg.in_key_present && !tnl_cfg.in_key_flow) {
+        nl_msg_put_be64(options, OVS_TUNNEL_ATTR_IN_KEY, tnl_cfg.in_key);
+    }
+
+    tnl_cfg.out_key = parse_key(args, "out_key",
+                               &tnl_cfg.out_key_present,
+                               &tnl_cfg.out_key_flow);
+    if (tnl_cfg.out_key_present && !tnl_cfg.out_key_flow) {
+        nl_msg_put_be64(options, OVS_TUNNEL_ATTR_OUT_KEY, tnl_cfg.out_key);
+    }
     nl_msg_put_u32(options, OVS_TUNNEL_ATTR_FLAGS, flags);
 
+    *tnl_cfg_ = tnl_cfg;
+
     return 0;
 }
 
@@ -745,13 +670,14 @@ tnl_port_config_from_nlattr(const struct nlattr *options, size_t options_len,
                             struct nlattr *a[OVS_TUNNEL_ATTR_MAX + 1])
 {
     static const struct nl_policy ovs_tunnel_policy[] = {
-        [OVS_TUNNEL_ATTR_FLAGS] = { .type = NL_A_U32 },
-        [OVS_TUNNEL_ATTR_DST_IPV4] = { .type = NL_A_BE32 },
+        [OVS_TUNNEL_ATTR_FLAGS] = { .type = NL_A_U32, .optional = true },
+        [OVS_TUNNEL_ATTR_DST_IPV4] = { .type = NL_A_BE32, .optional = true },
         [OVS_TUNNEL_ATTR_SRC_IPV4] = { .type = NL_A_BE32, .optional = true },
         [OVS_TUNNEL_ATTR_IN_KEY] = { .type = NL_A_BE64, .optional = true },
         [OVS_TUNNEL_ATTR_OUT_KEY] = { .type = NL_A_BE64, .optional = true },
         [OVS_TUNNEL_ATTR_TOS] = { .type = NL_A_U8, .optional = true },
         [OVS_TUNNEL_ATTR_TTL] = { .type = NL_A_U8, .optional = true },
+        [OVS_TUNNEL_ATTR_DST_PORT] = { .type = NL_A_U16, .optional = true },
     };
     struct ofpbuf buf;
 
@@ -775,7 +701,6 @@ unparse_tunnel_config(const char *name OVS_UNUSED, const char *type OVS_UNUSED,
                       struct smap *args)
 {
     struct nlattr *a[OVS_TUNNEL_ATTR_MAX + 1];
-    ovs_be32 daddr;
     uint32_t flags;
     int error;
 
@@ -784,18 +709,14 @@ unparse_tunnel_config(const char *name OVS_UNUSED, const char *type OVS_UNUSED,
         return error;
     }
 
-    flags = nl_attr_get_u32(a[OVS_TUNNEL_ATTR_FLAGS]);
-    if (!(flags & TNL_F_HDR_CACHE) == !(flags & TNL_F_IPSEC)) {
-        smap_add(args, "header_cache",
-                 flags & TNL_F_HDR_CACHE ? "true" : "false");
+    if (a[OVS_TUNNEL_ATTR_DST_IPV4]) {
+        ovs_be32 daddr = nl_attr_get_be32(a[OVS_TUNNEL_ATTR_DST_IPV4]);
+        smap_add_format(args, "remote_ip", IP_FMT, IP_ARGS(daddr));
     }
 
-    daddr = nl_attr_get_be32(a[OVS_TUNNEL_ATTR_DST_IPV4]);
-    smap_add_format(args, "remote_ip", IP_FMT, IP_ARGS(&daddr));
-
     if (a[OVS_TUNNEL_ATTR_SRC_IPV4]) {
         ovs_be32 saddr = nl_attr_get_be32(a[OVS_TUNNEL_ATTR_SRC_IPV4]);
-        smap_add_format(args, "local_ip", IP_FMT, IP_ARGS(&saddr));
+        smap_add_format(args, "local_ip", IP_FMT, IP_ARGS(saddr));
     }
 
     if (!a[OVS_TUNNEL_ATTR_IN_KEY] && !a[OVS_TUNNEL_ATTR_OUT_KEY]) {
@@ -821,6 +742,8 @@ unparse_tunnel_config(const char *name OVS_UNUSED, const char *type OVS_UNUSED,
         }
     }
 
+    flags = get_u32_or_zero(a[OVS_TUNNEL_ATTR_FLAGS]);
+
     if (flags & TNL_F_TTL_INHERIT) {
         smap_add(args, "ttl", "inherit");
     } else if (a[OVS_TUNNEL_ATTR_TTL]) {
@@ -835,28 +758,37 @@ unparse_tunnel_config(const char *name OVS_UNUSED, const char *type OVS_UNUSED,
         smap_add_format(args, "tos", "0x%x", tos);
     }
 
+    if (a[OVS_TUNNEL_ATTR_DST_PORT]) {
+        uint16_t dst_port = nl_attr_get_u16(a[OVS_TUNNEL_ATTR_DST_PORT]);
+        if (dst_port != VXLAN_DST_PORT) {
+            smap_add_format(args, "dst_port", "%d", dst_port);
+        }
+    }
+
     if (flags & TNL_F_CSUM) {
         smap_add(args, "csum", "true");
     }
     if (flags & TNL_F_DF_INHERIT) {
+        /* Shouldn't happen as "df_inherit" is no longer supported.  However,
+         * for completeness we report it if it's there. */
         smap_add(args, "df_inherit", "true");
     }
     if (!(flags & TNL_F_DF_DEFAULT)) {
         smap_add(args, "df_default", "false");
     }
-    if (flags & TNL_F_PMTUD) {
-        smap_add(args, "pmtud", "true");
-    }
 
     return 0;
 }
 
 static int
 parse_patch_config(const char *name, const char *type OVS_UNUSED,
-                   const struct smap *args, struct ofpbuf *options)
+                   const struct smap *args, struct ofpbuf *options,
+                   struct netdev_tunnel_config *tnl_cfg)
 {
     const char *peer;
 
+    memset(tnl_cfg, 0, sizeof *tnl_cfg);
+
     peer = smap_get(args, "peer");
     if (!peer) {
         VLOG_ERR("%s: patch type requires valid 'peer' argument", name);
@@ -907,7 +839,7 @@ unparse_patch_config(const char *name OVS_UNUSED, const char *type OVS_UNUSED,
     return 0;
 }
 \f
-#define VPORT_FUNCTIONS(GET_STATUS)                         \
+#define VPORT_FUNCTIONS(GET_TUNNEL_CONFIG, GET_STATUS)      \
     NULL,                                                   \
     netdev_vport_run,                                       \
     netdev_vport_wait,                                      \
@@ -916,6 +848,7 @@ unparse_patch_config(const char *name OVS_UNUSED, const char *type OVS_UNUSED,
     netdev_vport_destroy,                                   \
     netdev_vport_get_config,                                \
     netdev_vport_set_config,                                \
+    GET_TUNNEL_CONFIG,                                      \
                                                             \
     netdev_vport_open,                                      \
     netdev_vport_close,                                     \
@@ -925,7 +858,7 @@ unparse_patch_config(const char *name OVS_UNUSED, const char *type OVS_UNUSED,
     NULL,                       /* recv_wait */             \
     NULL,                       /* drain */                 \
                                                             \
-    netdev_vport_send,          /* send */                  \
+    NULL,                       /* send */                  \
     NULL,                       /* send_wait */             \
                                                             \
     netdev_vport_set_etheraddr,                             \
@@ -937,7 +870,7 @@ unparse_patch_config(const char *name OVS_UNUSED, const char *type OVS_UNUSED,
     NULL,                       /* get_carrier_resets */    \
     NULL,                       /* get_miimon */            \
     netdev_vport_get_stats,                                 \
-    netdev_vport_set_stats,                                 \
+    NULL,                       /* set_stats */             \
                                                             \
     NULL,                       /* get_features */          \
     NULL,                       /* set_advertisements */    \
@@ -966,32 +899,25 @@ unparse_patch_config(const char *name OVS_UNUSED, const char *type OVS_UNUSED,
                                                             \
     netdev_vport_change_seq
 
+#define TUNNEL_CLASS(NAME, VPORT_TYPE)                      \
+    { VPORT_TYPE,                                           \
+        { NAME, VPORT_FUNCTIONS(get_netdev_tunnel_config,   \
+                                tunnel_get_status) },       \
+            parse_tunnel_config, unparse_tunnel_config }
+
 void
 netdev_vport_register(void)
 {
     static const struct vport_class vport_classes[] = {
-        { OVS_VPORT_TYPE_GRE,
-          { "gre", VPORT_FUNCTIONS(netdev_vport_get_drv_info) },
-          parse_tunnel_config, unparse_tunnel_config },
-
-        { OVS_VPORT_TYPE_GRE,
-          { "ipsec_gre", VPORT_FUNCTIONS(netdev_vport_get_drv_info) },
-          parse_tunnel_config, unparse_tunnel_config },
-
-        { OVS_VPORT_TYPE_GRE64,
-          { "gre64", VPORT_FUNCTIONS(netdev_vport_get_drv_info) },
-          parse_tunnel_config, unparse_tunnel_config },
-
-        { OVS_VPORT_TYPE_GRE64,
-          { "ipsec_gre64", VPORT_FUNCTIONS(netdev_vport_get_drv_info) },
-          parse_tunnel_config, unparse_tunnel_config },
-
-        { OVS_VPORT_TYPE_CAPWAP,
-          { "capwap", VPORT_FUNCTIONS(netdev_vport_get_drv_info) },
-          parse_tunnel_config, unparse_tunnel_config },
+        TUNNEL_CLASS("gre", OVS_VPORT_TYPE_GRE),
+        TUNNEL_CLASS("ipsec_gre", OVS_VPORT_TYPE_GRE),
+        TUNNEL_CLASS("gre64", OVS_VPORT_TYPE_GRE64),
+        TUNNEL_CLASS("ipsec_gre64", OVS_VPORT_TYPE_GRE64),
+        TUNNEL_CLASS("capwap", OVS_VPORT_TYPE_CAPWAP),
+        TUNNEL_CLASS("vxlan", OVS_VPORT_TYPE_VXLAN),
 
         { OVS_VPORT_TYPE_PATCH,
-          { "patch", VPORT_FUNCTIONS(NULL) },
+          { "patch", VPORT_FUNCTIONS(NULL, NULL) },
           parse_patch_config, unparse_patch_config }
     };
 
index d96a318..31c1198 100644 (file)
@@ -17,6 +17,9 @@
 #ifndef NETDEV_VPORT_H
 #define NETDEV_VPORT_H 1
 
+#include <stdbool.h>
+#include "openvswitch/types.h"
+
 struct dpif_linux_vport;
 struct netdev;
 struct netdev_stats;
@@ -29,6 +32,5 @@ enum ovs_vport_type netdev_vport_get_vport_type(const struct netdev *);
 const char *netdev_vport_get_netdev_type(const struct dpif_linux_vport *);
 
 int netdev_vport_get_stats(const struct netdev *, struct netdev_stats *);
-int netdev_vport_set_stats(struct netdev *, const struct netdev_stats *);
 
 #endif /* netdev-vport.h */
index cf1d691..ef1a6b9 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -292,6 +292,18 @@ netdev_get_config(const struct netdev *netdev, struct smap *args)
     return error;
 }
 
+const struct netdev_tunnel_config *
+netdev_get_tunnel_config(const struct netdev *netdev)
+{
+    struct netdev_dev *netdev_dev = netdev_get_dev(netdev);
+
+    if (netdev_dev->netdev_class->get_tunnel_config) {
+        return netdev_dev->netdev_class->get_tunnel_config(netdev_dev);
+    } else {
+        return NULL;
+    }
+}
+
 /* Closes and destroys 'netdev'. */
 void
 netdev_close(struct netdev *netdev)
@@ -622,9 +634,10 @@ netdev_get_features(const struct netdev *netdev,
 
 /* Returns the maximum speed of a network connection that has the NETDEV_F_*
  * bits in 'features', in bits per second.  If no bits that indicate a speed
- * are set in 'features', assumes 100Mbps. */
+ * are set in 'features', returns 'default_bps'. */
 uint64_t
-netdev_features_to_bps(enum netdev_features features)
+netdev_features_to_bps(enum netdev_features features,
+                       uint64_t default_bps)
 {
     enum {
         F_1000000MB = NETDEV_F_1TB_FD,
@@ -643,7 +656,7 @@ netdev_features_to_bps(enum netdev_features features)
             : features & F_1000MB    ? UINT64_C(1000000000)
             : features & F_100MB     ? UINT64_C(100000000)
             : features & F_10MB      ? UINT64_C(10000000)
-                                     : UINT64_C(100000000));
+                                     : default_bps);
 }
 
 /* Returns true if any of the NETDEV_F_* bits that indicate a full-duplex link
@@ -772,12 +785,12 @@ netdev_get_next_hop(const struct netdev *netdev,
  * information may be used to populate the status column of the Interface table
  * as defined in ovs-vswitchd.conf.db(5). */
 int
-netdev_get_drv_info(const struct netdev *netdev, struct smap *smap)
+netdev_get_status(const struct netdev *netdev, struct smap *smap)
 {
     struct netdev_dev *dev = netdev_get_dev(netdev);
 
-    return (dev->netdev_class->get_drv_info
-            ? dev->netdev_class->get_drv_info(netdev, smap)
+    return (dev->netdev_class->get_status
+            ? dev->netdev_class->get_status(netdev, smap)
             : EOPNOTSUPP);
 }
 
index d2cc8b5..a544131 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -75,6 +75,32 @@ struct netdev_stats {
     uint64_t tx_window_errors;
 };
 
+/* Configuration specific to tunnels. */
+struct netdev_tunnel_config {
+    bool in_key_present;
+    bool in_key_flow;
+    ovs_be64 in_key;
+
+    bool out_key_present;
+    bool out_key_flow;
+    ovs_be64 out_key;
+
+    ovs_be16 dst_port;
+
+    ovs_be32 ip_src;
+    ovs_be32 ip_dst;
+
+    uint8_t ttl;
+    bool ttl_inherit;
+
+    uint8_t tos;
+    bool tos_inherit;
+
+    bool csum;
+    bool ipsec;
+    bool dont_fragment;
+};
+
 struct netdev;
 struct netdev_class;
 
@@ -95,6 +121,8 @@ void netdev_parse_name(const char *netdev_name, char **name, char **type);
 /* Options. */
 int netdev_set_config(struct netdev *, const struct smap *args);
 int netdev_get_config(const struct netdev *, struct smap *);
+const struct netdev_tunnel_config *
+    netdev_get_tunnel_config(const struct netdev *);
 
 /* Basic properties. */
 const char *netdev_get_name(const struct netdev *);
@@ -146,7 +174,8 @@ int netdev_get_features(const struct netdev *,
                         enum netdev_features *advertised,
                         enum netdev_features *supported,
                         enum netdev_features *peer);
-uint64_t netdev_features_to_bps(enum netdev_features features);
+uint64_t netdev_features_to_bps(enum netdev_features features,
+                                uint64_t default_bps);
 bool netdev_features_is_full_duplex(enum netdev_features features);
 int netdev_set_advertisements(struct netdev *, enum netdev_features advertise);
 
@@ -159,7 +188,7 @@ int netdev_get_in6(const struct netdev *, struct in6_addr *);
 int netdev_add_router(struct netdev *, struct in_addr router);
 int netdev_get_next_hop(const struct netdev *, const struct in_addr *host,
                         struct in_addr *next_hop, char **);
-int netdev_get_drv_info(const struct netdev *, struct smap *);
+int netdev_get_status(const struct netdev *, struct smap *);
 int netdev_arp_lookup(const struct netdev *, ovs_be32 ip, uint8_t mac[6]);
 
 int netdev_get_flags(const struct netdev *, enum netdev_flags *);
index 9918994..ecaab05 100644 (file)
@@ -90,6 +90,31 @@ nx_entry_ok(const void *p, unsigned int match_len)
     return header;
 }
 
+/* Given NXM/OXM value 'value' and mask 'mask', each 'width' bytes long,
+ * checks for any 1-bit in the value where there is a 0-bit in the mask.  If it
+ * finds one, logs a warning. */
+static void
+check_mask_consistency(const uint8_t *p, const struct mf_field *mf)
+{
+    unsigned int width = mf->n_bytes;
+    const uint8_t *value = p + 4;
+    const uint8_t *mask = p + 4 + width;
+    unsigned int i;
+
+    for (i = 0; i < width; i++) {
+        if (value[i] & ~mask[i]) {
+            if (!VLOG_DROP_WARN(&rl)) {
+                char *s = nx_match_to_string(p, width * 2 + 4);
+                VLOG_WARN_RL(&rl, "NXM/OXM entry %s has 1-bits in value for "
+                             "bits wildcarded by the mask.  (Future versions "
+                             "of OVS may report this as an OpenFlow error.)",
+                             s);
+                break;
+            }
+        }
+    }
+}
+
 static enum ofperr
 nx_pull_raw(const uint8_t *p, unsigned int match_len, bool strict,
             struct match *match, ovs_be64 *cookie, ovs_be64 *cookie_mask)
@@ -141,6 +166,7 @@ nx_pull_raw(const uint8_t *p, unsigned int match_len, bool strict,
                     error = OFPERR_OFPBMC_BAD_MASK;
                 } else {
                     error = 0;
+                    check_mask_consistency(p, mf);
                     mf_set(mf, &value, &mask, match);
                 }
             }
@@ -547,7 +573,7 @@ nx_put_raw(struct ofpbuf *b, bool oxm, const struct match *match,
     int match_len;
     int i;
 
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 17);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 18);
 
     /* Metadata. */
     if (match->wc.masks.in_port) {
@@ -626,7 +652,8 @@ nx_put_raw(struct ofpbuf *b, bool oxm, const struct match *match,
                                    flow->arp_tha, match->wc.masks.arp_tha);
             }
         }
-    } else if (flow->dl_type == htons(ETH_TYPE_ARP)) {
+    } else if (flow->dl_type == htons(ETH_TYPE_ARP) ||
+               flow->dl_type == htons(ETH_TYPE_RARP)) {
         /* ARP. */
         if (match->wc.masks.nw_proto) {
             nxm_put_16(b, oxm ? OXM_OF_ARP_OP : NXM_OF_ARP_OP,
@@ -643,8 +670,8 @@ nx_put_raw(struct ofpbuf *b, bool oxm, const struct match *match,
     }
 
     /* Tunnel ID. */
-    nxm_put_64m(b, NXM_NX_TUN_ID, flow->tunnel.tun_id,
-                match->wc.masks.tunnel.tun_id);
+    nxm_put_64m(b, oxm ? OXM_OF_TUNNEL_ID : NXM_NX_TUN_ID,
+               flow->tunnel.tun_id, match->wc.masks.tunnel.tun_id);
 
     /* Registers. */
     for (i = 0; i < FLOW_N_REGS; i++) {
@@ -1201,6 +1228,7 @@ nxm_reg_load_to_nxast(const struct ofpact_reg_load *load,
         struct ofp_header *oh = (struct ofp_header *)openflow->l2;
 
         switch(oh->version) {
+        case OFP13_VERSION:
         case OFP12_VERSION:
             set_field_to_ofast(load, openflow);
             break;
index 9ed17ed..e2f21da 100644 (file)
@@ -92,7 +92,8 @@ ovs_key_attr_to_string(enum ovs_key_attr attr)
     switch (attr) {
     case OVS_KEY_ATTR_UNSPEC: return "unspec";
     case OVS_KEY_ATTR_ENCAP: return "encap";
-    case OVS_KEY_ATTR_PRIORITY: return "priority";
+    case OVS_KEY_ATTR_PRIORITY: return "skb_priority";
+    case OVS_KEY_ATTR_SKB_MARK: return "skb_mark";
     case OVS_KEY_ATTR_TUN_ID: return "tun_id";
     case OVS_KEY_ATTR_IPV4_TUNNEL: return "ipv4_tunnel";
     case OVS_KEY_ATTR_IN_PORT: return "in_port";
@@ -167,8 +168,10 @@ format_odp_sample_action(struct ds *ds, const struct nlattr *attr)
 }
 
 static const char *
-slow_path_reason_to_string(enum slow_path_reason bit)
+slow_path_reason_to_string(uint32_t data)
 {
+    enum slow_path_reason bit = (enum slow_path_reason) data;
+
     switch (bit) {
     case SLOW_CFM:
         return "cfm";
@@ -187,29 +190,54 @@ slow_path_reason_to_string(enum slow_path_reason bit)
     }
 }
 
-static void
-format_slow_path_reason(struct ds *ds, uint32_t slow)
+static int
+parse_flags(const char *s, const char *(*bit_to_string)(uint32_t),
+            uint32_t *res)
 {
-    uint32_t bad = 0;
+    uint32_t result = 0;
+    int n = 0;
 
-    while (slow) {
-        uint32_t bit = rightmost_1bit(slow);
-        const char *s;
+    if (s[n] != '(') {
+        return -EINVAL;
+    }
+    n++;
 
-        s = slow_path_reason_to_string(bit);
-        if (s) {
-            ds_put_format(ds, "%s,", s);
-        } else {
-            bad |= bit;
+    while (s[n] != ')') {
+        unsigned long long int flags;
+        uint32_t bit;
+        int n0;
+
+        if (sscanf(&s[n], "%lli%n", &flags, &n0) > 0 && n0 > 0) {
+            n += n0 + (s[n + n0] == ',');
+            result |= flags;
+            continue;
         }
 
-        slow &= ~bit;
-    }
+        for (bit = 1; bit; bit <<= 1) {
+            const char *name = bit_to_string(bit);
+            size_t len;
 
-    if (bad) {
-        ds_put_format(ds, "0x%"PRIx32",", bad);
+            if (!name) {
+                continue;
+            }
+
+            len = strlen(name);
+            if (!strncmp(s + n, name, len) &&
+                (s[n + len] == ',' || s[n + len] == ')')) {
+                result |= bit;
+                n += len + (s[n + len] == ',');
+                break;
+            }
+        }
+
+        if (!bit) {
+            return -EINVAL;
+        }
     }
-    ds_chomp(ds, ',');
+    n++;
+
+    *res = result;
+    return n;
 }
 
 static void
@@ -246,10 +274,9 @@ format_odp_userspace_action(struct ds *ds, const struct nlattr *attr)
 
         case USER_ACTION_COOKIE_SLOW_PATH:
             ds_put_cstr(ds, ",slow_path(");
-            if (cookie.slow_path.reason) {
-                format_slow_path_reason(ds, cookie.slow_path.reason);
-            }
-            ds_put_char(ds, ')');
+            format_flags(ds, slow_path_reason_to_string,
+                         cookie.slow_path.reason, ',');
+            ds_put_format(ds, ")");
             break;
 
         case USER_ACTION_COOKIE_UNSPEC:
@@ -415,39 +442,25 @@ parse_odp_action(const char *s, const struct simap *port_names,
             cookie.sflow.output = output;
             odp_put_userspace_action(pid, &cookie, actions);
             return n;
-        } else if (sscanf(s, "userspace(pid=%lli,slow_path(%n", &pid, &n) > 0
+        } else if (sscanf(s, "userspace(pid=%lli,slow_path%n", &pid, &n) > 0
                    && n > 0) {
             union user_action_cookie cookie;
+            int res;
 
             cookie.type = USER_ACTION_COOKIE_SLOW_PATH;
             cookie.slow_path.unused = 0;
             cookie.slow_path.reason = 0;
 
-            while (s[n] != ')') {
-                uint32_t bit;
-
-                for (bit = 1; bit; bit <<= 1) {
-                    const char *reason = slow_path_reason_to_string(bit);
-                    size_t len = strlen(reason);
-
-                    if (reason
-                        && !strncmp(s + n, reason, len)
-                        && (s[n + len] == ',' || s[n + len] == ')'))
-                    {
-                        cookie.slow_path.reason |= bit;
-                        n += len + (s[n + len] == ',');
-                        break;
-                    }
-                }
-
-                if (!bit) {
-                    return -EINVAL;
-                }
+            res = parse_flags(&s[n], slow_path_reason_to_string,
+                              &cookie.slow_path.reason);
+            if (res < 0) {
+                return res;
             }
-            if (s[n + 1] != ')') {
+            n += res;
+            if (s[n] != ')') {
                 return -EINVAL;
             }
-            n += 2;
+            n++;
 
             odp_put_userspace_action(pid, &cookie, actions);
             return n;
@@ -602,6 +615,7 @@ odp_flow_key_attr_len(uint16_t type)
     switch ((enum ovs_key_attr) type) {
     case OVS_KEY_ATTR_ENCAP: return -2;
     case OVS_KEY_ATTR_PRIORITY: return 4;
+    case OVS_KEY_ATTR_SKB_MARK: return 4;
     case OVS_KEY_ATTR_TUN_ID: return 8;
     case OVS_KEY_ATTR_IPV4_TUNNEL: return sizeof(struct ovs_key_ipv4_tunnel);
     case OVS_KEY_ATTR_IN_PORT: return 4;
@@ -658,6 +672,21 @@ ovs_frag_type_to_string(enum ovs_frag_type type)
     }
 }
 
+static const char *
+odp_tun_flag_to_string(uint32_t flags)
+{
+    switch (flags) {
+    case OVS_TNL_F_DONT_FRAGMENT:
+        return "df";
+    case OVS_TNL_F_CSUM:
+        return "csum";
+    case OVS_TNL_F_KEY:
+        return "key";
+    default:
+        return NULL;
+    }
+}
+
 static void
 format_odp_key_attr(const struct nlattr *a, struct ds *ds)
 {
@@ -694,7 +723,11 @@ format_odp_key_attr(const struct nlattr *a, struct ds *ds)
         break;
 
     case OVS_KEY_ATTR_PRIORITY:
-        ds_put_format(ds, "(%"PRIu32")", nl_attr_get_u32(a));
+        ds_put_format(ds, "(%#"PRIx32")", nl_attr_get_u32(a));
+        break;
+
+    case OVS_KEY_ATTR_SKB_MARK:
+        ds_put_format(ds, "(%#"PRIx32")", nl_attr_get_u32(a));
         break;
 
     case OVS_KEY_ATTR_TUN_ID:
@@ -703,12 +736,16 @@ format_odp_key_attr(const struct nlattr *a, struct ds *ds)
 
     case OVS_KEY_ATTR_IPV4_TUNNEL:
         ipv4_tun_key = nl_attr_get(a);
-        ds_put_format(ds, "(tun_id=0x%"PRIx64",flags=0x%"PRIx32
-                      ",src="IP_FMT",dst="IP_FMT",tos=0x%"PRIx8",ttl=%"PRIu8")",
-                      ntohll(ipv4_tun_key->tun_id), ipv4_tun_key->tun_flags,
-                      IP_ARGS(&ipv4_tun_key->ipv4_src),
-                      IP_ARGS(&ipv4_tun_key->ipv4_dst),
+        ds_put_format(ds, "(tun_id=0x%"PRIx64",src="IP_FMT",dst="IP_FMT","
+                      "tos=0x%"PRIx8",ttl=%"PRIu8",flags(",
+                      ntohll(ipv4_tun_key->tun_id),
+                      IP_ARGS(ipv4_tun_key->ipv4_src),
+                      IP_ARGS(ipv4_tun_key->ipv4_dst),
                       ipv4_tun_key->ipv4_tos, ipv4_tun_key->ipv4_ttl);
+
+        format_flags(ds, odp_tun_flag_to_string,
+                     ipv4_tun_key->tun_flags, ',');
+        ds_put_format(ds, "))");
         break;
 
     case OVS_KEY_ATTR_IN_PORT:
@@ -737,8 +774,8 @@ format_odp_key_attr(const struct nlattr *a, struct ds *ds)
         ipv4_key = nl_attr_get(a);
         ds_put_format(ds, "(src="IP_FMT",dst="IP_FMT",proto=%"PRIu8
                       ",tos=%#"PRIx8",ttl=%"PRIu8",frag=%s)",
-                      IP_ARGS(&ipv4_key->ipv4_src),
-                      IP_ARGS(&ipv4_key->ipv4_dst),
+                      IP_ARGS(ipv4_key->ipv4_src),
+                      IP_ARGS(ipv4_key->ipv4_dst),
                       ipv4_key->ipv4_proto, ipv4_key->ipv4_tos,
                       ipv4_key->ipv4_ttl,
                       ovs_frag_type_to_string(ipv4_key->ipv4_frag));
@@ -789,7 +826,7 @@ format_odp_key_attr(const struct nlattr *a, struct ds *ds)
         arp_key = nl_attr_get(a);
         ds_put_format(ds, "(sip="IP_FMT",tip="IP_FMT",op=%"PRIu16","
                       "sha="ETH_ADDR_FMT",tha="ETH_ADDR_FMT")",
-                      IP_ARGS(&arp_key->arp_sip), IP_ARGS(&arp_key->arp_tip),
+                      IP_ARGS(arp_key->arp_sip), IP_ARGS(arp_key->arp_tip),
                       ntohs(arp_key->arp_op), ETH_ADDR_ARGS(arp_key->arp_sha),
                       ETH_ADDR_ARGS(arp_key->arp_tha));
         break;
@@ -906,12 +943,22 @@ parse_odp_key_attr(const char *s, const struct simap *port_names,
         unsigned long long int priority;
         int n = -1;
 
-        if (sscanf(s, "priority(%lli)%n", &priority, &n) > 0 && n > 0) {
+        if (sscanf(s, "skb_priority(%llx)%n", &priority, &n) > 0 && n > 0) {
             nl_msg_put_u32(key, OVS_KEY_ATTR_PRIORITY, priority);
             return n;
         }
     }
 
+    {
+        unsigned long long int mark;
+        int n = -1;
+
+        if (sscanf(s, "skb_mark(%llx)%n", &mark, &n) > 0 && n > 0) {
+            nl_msg_put_u32(key, OVS_KEY_ATTR_SKB_MARK, mark);
+            return n;
+        }
+    }
+
     {
         char tun_id_s[32];
         int n = -1;
@@ -924,6 +971,42 @@ parse_odp_key_attr(const char *s, const struct simap *port_names,
         }
     }
 
+    {
+        char tun_id_s[32];
+        int tos, ttl;
+        struct ovs_key_ipv4_tunnel tun_key;
+        int n = -1;
+
+        if (sscanf(s, "ipv4_tunnel(tun_id=%31[x0123456789abcdefABCDEF],"
+                   "src="IP_SCAN_FMT",dst="IP_SCAN_FMT
+                   ",tos=%i,ttl=%i,flags%n", tun_id_s,
+                    IP_SCAN_ARGS(&tun_key.ipv4_src),
+                    IP_SCAN_ARGS(&tun_key.ipv4_dst), &tos, &ttl,
+                    &n) > 0 && n > 0) {
+            int res;
+
+            tun_key.tun_id = htonll(strtoull(tun_id_s, NULL, 0));
+            tun_key.ipv4_tos = tos;
+            tun_key.ipv4_ttl = ttl;
+
+            res = parse_flags(&s[n], odp_tun_flag_to_string,
+                              &tun_key.tun_flags);
+            if (res < 0) {
+                return res;
+            }
+            n += res;
+            if (s[n] != ')') {
+                return -EINVAL;
+            }
+            n++;
+
+            memset(&tun_key.pad, 0, sizeof tun_key.pad);
+            nl_msg_put_unspec(key, OVS_KEY_ATTR_IPV4_TUNNEL, &tun_key,
+                              sizeof tun_key);
+            return n;
+        }
+    }
+
     {
         unsigned long long int in_port;
         int n = -1;
@@ -1259,12 +1342,38 @@ ovs_to_odp_frag(uint8_t nw_frag)
           : OVS_FRAG_TYPE_LATER);
 }
 
+/* The set of kernel flags we understand. Used to detect if ODP_FIT_TOO_MUCH */
+#define OVS_TNL_F_KNOWN_MASK \
+    (OVS_TNL_F_DONT_FRAGMENT | OVS_TNL_F_CSUM | OVS_TNL_F_KEY)
+
+/* These allow the flow/kernel view of the flags to change in future */
+static uint32_t
+flow_to_odp_flags(uint16_t flags)
+{
+    return (flags & FLOW_TNL_F_DONT_FRAGMENT ? OVS_TNL_F_DONT_FRAGMENT : 0)
+        | (flags & FLOW_TNL_F_CSUM ? OVS_TNL_F_CSUM : 0)
+        | (flags & FLOW_TNL_F_KEY ? OVS_TNL_F_KEY : 0);
+}
+
+static uint16_t
+odp_to_flow_flags(uint32_t tun_flags)
+{
+    return (tun_flags & OVS_TNL_F_DONT_FRAGMENT ? FLOW_TNL_F_DONT_FRAGMENT : 0)
+        | (tun_flags & OVS_TNL_F_CSUM ? FLOW_TNL_F_CSUM : 0)
+        | (tun_flags & OVS_TNL_F_KEY ? FLOW_TNL_F_KEY : 0);
+}
+
 /* Appends a representation of 'flow' as OVS_KEY_ATTR_* attributes to 'buf'.
+ * 'flow->in_port' is ignored (since it is likely to be an OpenFlow port
+ * number rather than a datapath port number).  Instead, if 'odp_in_port'
+ * is anything other than OVSP_NONE, it is included in 'buf' as the input
+ * port.
  *
  * 'buf' must have at least ODPUTIL_FLOW_KEY_BYTES bytes of space, or be
  * capable of being expanded to allow for that much space. */
 void
-odp_flow_key_from_flow(struct ofpbuf *buf, const struct flow *flow)
+odp_flow_key_from_flow(struct ofpbuf *buf, const struct flow *flow,
+                       uint32_t odp_in_port)
 {
     struct ovs_key_ethernet *eth_key;
     size_t encap;
@@ -1273,13 +1382,29 @@ odp_flow_key_from_flow(struct ofpbuf *buf, const struct flow *flow)
         nl_msg_put_u32(buf, OVS_KEY_ATTR_PRIORITY, flow->skb_priority);
     }
 
-    if (flow->tunnel.tun_id != htonll(0)) {
+    if (flow->tunnel.ip_dst) {
+        struct ovs_key_ipv4_tunnel *ipv4_tun_key;
+
+        ipv4_tun_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_IPV4_TUNNEL,
+                                            sizeof *ipv4_tun_key);
+        /* layouts differ, flags has different size */
+        ipv4_tun_key->tun_id = flow->tunnel.tun_id;
+        ipv4_tun_key->tun_flags = flow_to_odp_flags(flow->tunnel.flags);
+        ipv4_tun_key->ipv4_src = flow->tunnel.ip_src;
+        ipv4_tun_key->ipv4_dst = flow->tunnel.ip_dst;
+        ipv4_tun_key->ipv4_tos = flow->tunnel.ip_tos;
+        ipv4_tun_key->ipv4_ttl = flow->tunnel.ip_ttl;
+        memset(ipv4_tun_key->pad, 0, sizeof ipv4_tun_key->pad);
+    } else if (flow->tunnel.tun_id != htonll(0)) {
         nl_msg_put_be64(buf, OVS_KEY_ATTR_TUN_ID, flow->tunnel.tun_id);
     }
 
-    if (flow->in_port != OFPP_NONE && flow->in_port != OFPP_CONTROLLER) {
-        nl_msg_put_u32(buf, OVS_KEY_ATTR_IN_PORT,
-                       ofp_port_to_odp_port(flow->in_port));
+    if (flow->skb_mark) {
+        nl_msg_put_u32(buf, OVS_KEY_ATTR_SKB_MARK, flow->skb_mark);
+    }
+
+    if (odp_in_port != OVSP_NONE) {
+        nl_msg_put_u32(buf, OVS_KEY_ATTR_IN_PORT, odp_in_port);
     }
 
     eth_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_ETHERNET,
@@ -1327,7 +1452,8 @@ odp_flow_key_from_flow(struct ofpbuf *buf, const struct flow *flow)
         ipv6_key->ipv6_tclass = flow->nw_tos;
         ipv6_key->ipv6_hlimit = flow->nw_ttl;
         ipv6_key->ipv6_frag = ovs_to_odp_frag(flow->nw_frag);
-    } else if (flow->dl_type == htons(ETH_TYPE_ARP)) {
+    } else if (flow->dl_type == htons(ETH_TYPE_ARP) ||
+               flow->dl_type == htons(ETH_TYPE_RARP)) {
         struct ovs_key_arp *arp_key;
 
         arp_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_ARP,
@@ -1583,7 +1709,8 @@ parse_l3_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
                 return ODP_FIT_ERROR;
             }
         }
-    } else if (flow->dl_type == htons(ETH_TYPE_ARP)) {
+    } else if (flow->dl_type == htons(ETH_TYPE_ARP) ||
+               flow->dl_type == htons(ETH_TYPE_RARP)) {
         expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ARP;
         if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ARP)) {
             const struct ovs_key_arp *arp_key;
@@ -1733,6 +1860,10 @@ parse_8021q_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
  * structure in 'flow'.  Returns an ODP_FIT_* value that indicates how well
  * 'key' fits our expectations for what a flow key should contain.
  *
+ * The 'in_port' will be the datapath's understanding of the port.  The
+ * caller will need to translate with odp_port_to_ofp_port() if the
+ * OpenFlow port is needed.
+ *
  * This function doesn't take the packet itself as an argument because none of
  * the currently understood OVS_KEY_ATTR_* attributes require it.  Currently,
  * it is always possible to infer which additional attribute(s) should appear
@@ -1744,7 +1875,6 @@ enum odp_key_fitness
 odp_flow_key_to_flow(const struct nlattr *key, size_t key_len,
                      struct flow *flow)
 {
-    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
     const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1];
     uint64_t expected_attrs;
     uint64_t present_attrs;
@@ -1765,22 +1895,41 @@ odp_flow_key_to_flow(const struct nlattr *key, size_t key_len,
         expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_PRIORITY;
     }
 
+    if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_SKB_MARK)) {
+        flow->skb_mark = nl_attr_get_u32(attrs[OVS_KEY_ATTR_SKB_MARK]);
+        expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_SKB_MARK;
+    }
+
     if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_TUN_ID)) {
         flow->tunnel.tun_id = nl_attr_get_be64(attrs[OVS_KEY_ATTR_TUN_ID]);
         expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_TUN_ID;
     }
 
-    if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_IN_PORT)) {
-        uint32_t in_port = nl_attr_get_u32(attrs[OVS_KEY_ATTR_IN_PORT]);
-        if (in_port >= UINT16_MAX || in_port >= OFPP_MAX) {
-            VLOG_ERR_RL(&rl, "in_port %"PRIu32" out of supported range",
-                        in_port);
-            return ODP_FIT_ERROR;
+    if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_IPV4_TUNNEL)) {
+        const struct ovs_key_ipv4_tunnel *ipv4_tun_key;
+
+        ipv4_tun_key = nl_attr_get(attrs[OVS_KEY_ATTR_IPV4_TUNNEL]);
+
+        flow->tunnel.tun_id = ipv4_tun_key->tun_id;
+        flow->tunnel.ip_src = ipv4_tun_key->ipv4_src;
+        flow->tunnel.ip_dst = ipv4_tun_key->ipv4_dst;
+        flow->tunnel.flags = odp_to_flow_flags(ipv4_tun_key->tun_flags);
+        flow->tunnel.ip_tos = ipv4_tun_key->ipv4_tos;
+        flow->tunnel.ip_ttl = ipv4_tun_key->ipv4_ttl;
+
+        /* Allow this to show up as unexpected, if there are unknown flags,
+         * eventually resulting in ODP_FIT_TOO_MUCH.
+         * OVS_TNL_F_KNOWN_MASK defined locally above. */
+        if (!(ipv4_tun_key->tun_flags & ~OVS_TNL_F_KNOWN_MASK)) {
+            expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_IPV4_TUNNEL;
         }
-        flow->in_port = odp_port_to_ofp_port(in_port);
+    }
+
+    if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_IN_PORT)) {
+        flow->in_port = nl_attr_get_u32(attrs[OVS_KEY_ATTR_IN_PORT]);
         expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_IN_PORT;
     } else {
-        flow->in_port = OFPP_NONE;
+        flow->in_port = OVSP_NONE;
     }
 
     /* Ethernet header. */
@@ -1858,16 +2007,32 @@ commit_set_action(struct ofpbuf *odp_actions, enum ovs_key_attr key_type,
 }
 
 static void
-commit_set_tun_id_action(const struct flow *flow, struct flow *base,
+commit_set_tunnel_action(const struct flow *flow, struct flow *base,
                          struct ofpbuf *odp_actions)
 {
-    if (base->tunnel.tun_id == flow->tunnel.tun_id) {
+    if (!memcmp(&base->tunnel, &flow->tunnel, sizeof base->tunnel)) {
         return;
     }
-    base->tunnel.tun_id = flow->tunnel.tun_id;
+    memcpy(&base->tunnel, &flow->tunnel, sizeof base->tunnel);
+
+    /* A valid IPV4_TUNNEL must have non-zero ip_dst. */
+    if (flow->tunnel.ip_dst) {
+        struct ovs_key_ipv4_tunnel ipv4_tun_key;
 
-    commit_set_action(odp_actions, OVS_KEY_ATTR_TUN_ID,
-                      &base->tunnel.tun_id, sizeof(base->tunnel.tun_id));
+        ipv4_tun_key.tun_id = base->tunnel.tun_id;
+        ipv4_tun_key.tun_flags = flow_to_odp_flags(base->tunnel.flags);
+        ipv4_tun_key.ipv4_src = base->tunnel.ip_src;
+        ipv4_tun_key.ipv4_dst = base->tunnel.ip_dst;
+        ipv4_tun_key.ipv4_tos = base->tunnel.ip_tos;
+        ipv4_tun_key.ipv4_ttl = base->tunnel.ip_ttl;
+        memset(&ipv4_tun_key.pad, 0, sizeof ipv4_tun_key.pad);
+
+        commit_set_action(odp_actions, OVS_KEY_ATTR_IPV4_TUNNEL,
+                          &ipv4_tun_key, sizeof ipv4_tun_key);
+    } else {
+        commit_set_action(odp_actions, OVS_KEY_ATTR_TUN_ID,
+                          &base->tunnel.tun_id, sizeof base->tunnel.tun_id);
+    }
 }
 
 static void
@@ -2031,6 +2196,18 @@ commit_set_priority_action(const struct flow *flow, struct flow *base,
                       &base->skb_priority, sizeof(base->skb_priority));
 }
 
+static void
+commit_set_skb_mark_action(const struct flow *flow, struct flow *base,
+                           struct ofpbuf *odp_actions)
+{
+    if (base->skb_mark == flow->skb_mark) {
+        return;
+    }
+    base->skb_mark = flow->skb_mark;
+
+    commit_set_action(odp_actions, OVS_KEY_ATTR_SKB_MARK,
+                      &base->skb_mark, sizeof(base->skb_mark));
+}
 /* If any of the flow key data that ODP actions can modify are different in
  * 'base' and 'flow', appends ODP actions to 'odp_actions' that change the flow
  * key from 'base' into 'flow', and then changes 'base' the same way. */
@@ -2038,10 +2215,11 @@ void
 commit_odp_actions(const struct flow *flow, struct flow *base,
                    struct ofpbuf *odp_actions)
 {
-    commit_set_tun_id_action(flow, base, odp_actions);
+    commit_set_tunnel_action(flow, base, odp_actions);
     commit_set_ether_addr_action(flow, base, odp_actions);
     commit_vlan_action(flow, base, odp_actions);
     commit_set_nw_action(flow, base, odp_actions);
     commit_set_port_action(flow, base, odp_actions);
     commit_set_priority_action(flow, base, odp_actions);
+    commit_set_skb_mark_action(flow, base, odp_actions);
 }
index 57073ba..9d38f33 100644 (file)
@@ -32,33 +32,7 @@ struct nlattr;
 struct ofpbuf;
 struct simap;
 
-#define OVSP_NONE UINT16_MAX
-
-static inline uint16_t
-ofp_port_to_odp_port(uint16_t ofp_port)
-{
-    switch (ofp_port) {
-    case OFPP_LOCAL:
-        return OVSP_LOCAL;
-    case OFPP_NONE:
-        return OVSP_NONE;
-    default:
-        return ofp_port;
-    }
-}
-
-static inline uint16_t
-odp_port_to_ofp_port(uint16_t odp_port)
-{
-    switch (odp_port) {
-    case OVSP_LOCAL:
-        return OFPP_LOCAL;
-    case OVSP_NONE:
-        return OFPP_NONE;
-    default:
-        return odp_port;
-    }
-}
+#define OVSP_NONE UINT32_MAX
 
 void format_odp_actions(struct ds *, const struct nlattr *odp_actions,
                         size_t actions_len);
@@ -82,6 +56,7 @@ int odp_actions_from_string(const char *, const struct simap *port_names,
  *  OVS_KEY_ATTR_TUN_ID        8    --     4     12
  *  OVS_KEY_ATTR_IPV4_TUNNEL  24    --     4     28
  *  OVS_KEY_ATTR_IN_PORT       4    --     4      8
+ *  OVS_KEY_ATTR_SKB_MARK      4    --     4      8
  *  OVS_KEY_ATTR_ETHERNET     12    --     4     16
  *  OVS_KEY_ATTR_ETHERTYPE     2     2     4      8  (outer VLAN ethertype)
  *  OVS_KEY_ATTR_8021Q         4    --     4      8
@@ -91,12 +66,12 @@ int odp_actions_from_string(const char *, const struct simap *port_names,
  *  OVS_KEY_ATTR_ICMPV6        2     2     4      8
  *  OVS_KEY_ATTR_ND           28    --     4     32
  *  -------------------------------------------------
- *  total                                       184
+ *  total                                       192
  *
  * We include some slack space in case the calculation isn't quite right or we
  * add another field and forget to adjust this value.
  */
-#define ODPUTIL_FLOW_KEY_BYTES 200
+#define ODPUTIL_FLOW_KEY_BYTES 256
 
 /* A buffer with sufficient size and alignment to hold an nlattr-formatted flow
  * key.  An array of "struct nlattr" might not, in theory, be sufficiently
@@ -109,7 +84,8 @@ void odp_flow_key_format(const struct nlattr *, size_t, struct ds *);
 int odp_flow_key_from_string(const char *s, const struct simap *port_names,
                              struct ofpbuf *);
 
-void odp_flow_key_from_flow(struct ofpbuf *, const struct flow *);
+void odp_flow_key_from_flow(struct ofpbuf *, const struct flow *,
+                            uint32_t odp_in_port);
 
 uint32_t odp_flow_key_hash(const struct nlattr *, size_t);
 
index 170e796..1bc8a9c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -49,7 +49,7 @@ output_from_openflow10(const struct ofp10_action_output *oao,
 }
 
 static enum ofperr
-enqueue_from_openflow10(const struct ofp_action_enqueue *oae,
+enqueue_from_openflow10(const struct ofp10_action_enqueue *oae,
                         struct ofpbuf *out)
 {
     struct ofpact_enqueue *enqueue;
@@ -479,7 +479,7 @@ ofpact_from_openflow10(const union ofp_action *a, struct ofpbuf *out)
         break;
 
     case OFPUTIL_OFPAT10_ENQUEUE:
-        error = enqueue_from_openflow10((const struct ofp_action_enqueue *) a,
+        error = enqueue_from_openflow10((const struct ofp10_action_enqueue *) a,
                                         out);
         break;
 
@@ -717,8 +717,8 @@ ofpact_from_openflow11(const union ofp_action *a, struct ofpbuf *out)
     case OFPUTIL_OFPAT11_PUSH_VLAN:
         if (((const struct ofp11_action_push *)a)->ethertype !=
             htons(ETH_TYPE_VLAN_8021Q)) {
-            /* TODO:XXX 802.1AD(QinQ) isn't supported at the moment */
-            return OFPERR_OFPET_BAD_ACTION;
+            /* XXX 802.1AD(QinQ) isn't supported at the moment */
+            return OFPERR_OFPBAC_BAD_ARGUMENT;
         }
         ofpact_put_PUSH_VLAN(out);
         break;
@@ -727,6 +727,11 @@ ofpact_from_openflow11(const union ofp_action *a, struct ofpbuf *out)
         ofpact_put_STRIP_VLAN(out);
         break;
 
+    case OFPUTIL_OFPAT11_SET_QUEUE:
+        ofpact_put_SET_QUEUE(out)->queue_id =
+            ntohl(((const struct ofp11_action_set_queue *)a)->queue_id);
+        break;
+
     case OFPUTIL_OFPAT11_SET_DL_SRC:
         memcpy(ofpact_put_SET_ETH_SRC(out)->mac,
                ((const struct ofp_action_dl_addr *) a)->dl_addr, ETH_ADDR_LEN);
@@ -912,7 +917,9 @@ decode_openflow11_instructions(const struct ofp11_instruction insts[],
         }
 
         if (out[type]) {
-            return OFPERR_OFPIT_BAD_INSTRUCTION;
+            return OFPERR_OFPBAC_UNSUPPORTED_ORDER; /* No specific code for
+                                                     * a duplicate instruction
+                                                     * exist */
         }
         out[type] = inst;
     }
@@ -1009,7 +1016,7 @@ ofpacts_pull_openflow11_instructions(struct ofpbuf *openflow,
             insts[OVSINST_OFPIT11_CLEAR_ACTIONS]);
         ofpact_put_CLEAR_ACTIONS(ofpacts);
     }
-    /* TODO:XXX Write-Actions */
+    /* XXX Write-Actions */
     if (insts[OVSINST_OFPIT11_WRITE_METADATA]) {
         const struct ofp11_instruction_write_metadata *oiwm;
         struct ofpact_metadata *om;
@@ -1146,24 +1153,38 @@ enum ofperr
 ofpacts_verify(const struct ofpact ofpacts[], size_t ofpacts_len)
 {
     const struct ofpact *a;
-    const struct ofpact_metadata *om = NULL;
+    enum ovs_instruction_type inst;
 
+    inst = OVSINST_OFPIT11_APPLY_ACTIONS;
     OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
-        if (om) {
-            if (a->type == OFPACT_WRITE_METADATA) {
-                VLOG_WARN("duplicate write_metadata instruction specified");
-                /* should be OFPERR_OFPET_BAD_ACTION? */
-                return OFPERR_OFPBAC_UNSUPPORTED_ORDER;
+        enum ovs_instruction_type next;
+
+        if (a->type == OFPACT_CLEAR_ACTIONS) {
+            next = OVSINST_OFPIT11_CLEAR_ACTIONS;
+        } else if (a->type == OFPACT_WRITE_METADATA) {
+            next = OVSINST_OFPIT11_WRITE_METADATA;
+        } else if (a->type == OFPACT_GOTO_TABLE) {
+            next = OVSINST_OFPIT11_GOTO_TABLE;
+        } else {
+            next = OVSINST_OFPIT11_APPLY_ACTIONS;
+        }
+
+        if (inst != OVSINST_OFPIT11_APPLY_ACTIONS && next <= inst) {
+            const char *name = ofpact_instruction_name_from_type(inst);
+            const char *next_name = ofpact_instruction_name_from_type(next);
+
+            if (next == inst) {
+                VLOG_WARN("duplicate %s instruction not allowed, for OpenFlow "
+                          "1.1+ compatibility", name);
             } else {
-                VLOG_WARN("write_metadata instruction must be specified after "
-                          "other instructions/actions");
-                return OFPERR_OFPBAC_UNSUPPORTED_ORDER;
+                VLOG_WARN("invalid instruction ordering: %s must appear "
+                          "before %s, for OpenFlow 1.1+ compatibility",
+                          next_name, name);
             }
+            return OFPERR_OFPBAC_UNSUPPORTED_ORDER;
         }
 
-        if (a->type == OFPACT_WRITE_METADATA) {
-            om = (const struct ofpact_metadata *) a;
-        }
+        inst = next;
     }
 
     return 0;
@@ -1399,7 +1420,7 @@ static void
 ofpact_enqueue_to_openflow10(const struct ofpact_enqueue *enqueue,
                              struct ofpbuf *out)
 {
-    struct ofp_action_enqueue *oae;
+    struct ofp10_action_enqueue *oae;
 
     oae = ofputil_put_OFPAT10_ENQUEUE(out);
     oae->port = htons(enqueue->port);
@@ -1470,7 +1491,7 @@ ofpact_to_openflow10(const struct ofpact *a, struct ofpbuf *out)
     case OFPACT_PUSH_VLAN:
     case OFPACT_CLEAR_ACTIONS:
     case OFPACT_GOTO_TABLE:
-        /* TODO:XXX */
+        /* XXX */
         break;
 
     case OFPACT_CONTROLLER:
@@ -1561,11 +1582,16 @@ ofpact_to_openflow11(const struct ofpact *a, struct ofpbuf *out)
         break;
 
     case OFPACT_PUSH_VLAN:
-        /* TODO:XXX ETH_TYPE_VLAN_8021AD case */
+        /* XXX ETH_TYPE_VLAN_8021AD case */
         ofputil_put_OFPAT11_PUSH_VLAN(out)->ethertype =
             htons(ETH_TYPE_VLAN_8021Q);
         break;
 
+    case OFPACT_SET_QUEUE:
+        ofputil_put_OFPAT11_SET_QUEUE(out)->queue_id
+            = htonl(ofpact_get_SET_QUEUE(a)->queue_id);
+        break;
+
     case OFPACT_SET_ETH_SRC:
         memcpy(ofputil_put_OFPAT11_SET_DL_SRC(out)->dl_addr,
                ofpact_get_SET_ETH_SRC(a)->mac, ETH_ADDR_LEN);
@@ -1619,7 +1645,6 @@ ofpact_to_openflow11(const struct ofpact *a, struct ofpbuf *out)
     case OFPACT_REG_MOVE:
     case OFPACT_REG_LOAD:
     case OFPACT_SET_TUNNEL:
-    case OFPACT_SET_QUEUE:
     case OFPACT_POP_QUEUE:
     case OFPACT_FIN_TIMEOUT:
     case OFPACT_RESUBMIT:
@@ -1672,7 +1697,7 @@ ofpacts_put_openflow11_instructions(const struct ofpact ofpacts[],
     const struct ofpact *a;
 
     OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
-        /* TODO:XXX Write-Actions */
+        /* XXX Write-Actions */
 
         if (a->type == OFPACT_CLEAR_ACTIONS) {
             instruction_put_OFPIT11_CLEAR_ACTIONS(openflow);
@@ -1914,7 +1939,7 @@ ofpact_format(const struct ofpact *a, struct ds *s)
         break;
 
     case OFPACT_PUSH_VLAN:
-        /* TODO:XXX 802.1AD case*/
+        /* XXX 802.1AD case*/
         ds_put_format(s, "push_vlan:%#"PRIx16, ETH_TYPE_VLAN_8021Q);
         break;
 
@@ -1930,12 +1955,12 @@ ofpact_format(const struct ofpact *a, struct ds *s)
 
     case OFPACT_SET_IPV4_SRC:
         ds_put_format(s, "mod_nw_src:"IP_FMT,
-                      IP_ARGS(&ofpact_get_SET_IPV4_SRC(a)->ipv4));
+                      IP_ARGS(ofpact_get_SET_IPV4_SRC(a)->ipv4));
         break;
 
     case OFPACT_SET_IPV4_DST:
         ds_put_format(s, "mod_nw_dst:"IP_FMT,
-                      IP_ARGS(&ofpact_get_SET_IPV4_DST(a)->ipv4));
+                      IP_ARGS(ofpact_get_SET_IPV4_DST(a)->ipv4));
         break;
 
     case OFPACT_SET_IPV4_DSCP:
@@ -2069,7 +2094,7 @@ ofpacts_format(const struct ofpact *ofpacts, size_t ofpacts_len,
                 ds_put_cstr(string, ",");
             }
 
-            /* TODO:XXX write-actions */
+            /* XXX write-actions */
             ofpact_format(a, string);
         }
     }
index b6cf4ba..3989040 100644 (file)
@@ -91,7 +91,7 @@
     DEFINE_OFPACT(EXIT,            ofpact_null,          ofpact)    \
                                                                     \
     /* Instructions */                                              \
-    /* TODO:XXX Write-Actions */                                    \
+    /* XXX Write-Actions */                                         \
     DEFINE_OFPACT(WRITE_METADATA,  ofpact_metadata,      ofpact)    \
     DEFINE_OFPACT(CLEAR_ACTIONS,   ofpact_null,          ofpact)    \
     DEFINE_OFPACT(GOTO_TABLE,      ofpact_goto_table,    ofpact)
@@ -595,7 +595,7 @@ enum {
 static inline bool
 ofpact_is_instruction(const struct ofpact *a)
 {
-    /* TODO:XXX Write-Actions */
+    /* XXX Write-Actions */
     return a->type == OFPACT_CLEAR_ACTIONS
         || a->type == OFPACT_WRITE_METADATA
         || a->type == OFPACT_GOTO_TABLE;
index 2c71312..6b3a42c 100644 (file)
@@ -31,6 +31,8 @@ ofperr_domain_from_version(enum ofp_version version)
         return &ofperr_of11;
     case OFP12_VERSION:
         return &ofperr_of12;
+    case OFP13_VERSION:
+        return &ofperr_of13;
     default:
         return NULL;
     }
@@ -51,40 +53,6 @@ ofperr_is_valid(enum ofperr error)
     return error >= OFPERR_OFS && error < OFPERR_OFS + OFPERR_N_ERRORS;
 }
 
-/* Returns true if 'error' is a valid OFPERR_* value that designates a whole
- * category of errors instead of a particular error, e.g. if it is an
- * OFPERR_OFPET_* value, and false otherwise.  */
-bool
-ofperr_is_category(enum ofperr error)
-{
-    return (ofperr_is_valid(error)
-            && ofperr_of10.errors[error - OFPERR_OFS].code == -1
-            && ofperr_of11.errors[error - OFPERR_OFS].code == -1);
-}
-
-/* Returns true if 'error' is a valid OFPERR_* value that is a Nicira
- * extension, e.g. if it is an OFPERR_NX* value, and false otherwise. */
-bool
-ofperr_is_nx_extension(enum ofperr error)
-{
-    return (ofperr_is_valid(error)
-            && (ofperr_of10.errors[error - OFPERR_OFS].code >= 0x100 ||
-                ofperr_of11.errors[error - OFPERR_OFS].code >= 0x100));
-}
-
-/* Returns true if 'error' can be encoded as an OpenFlow error message in
- * 'domain', false otherwise.
- *
- * A given error may not be encodable in some domains because each OpenFlow
- * version tends to introduce new errors and retire some old ones. */
-bool
-ofperr_is_encodable(enum ofperr error, enum ofp_version version)
-{
-    const struct ofperr_domain *domain = ofperr_domain_from_version(version);
-    return (ofperr_is_valid(error)
-            && domain && domain->errors[error - OFPERR_OFS].code >= 0);
-}
-
 /* Returns the OFPERR_* value that corresponds to 'type' and 'code' within
  * 'version', or 0 if either no such OFPERR_* value exists or 'version' is
  * unknown. */
@@ -95,16 +63,6 @@ ofperr_decode(enum ofp_version version, uint16_t type, uint16_t code)
     return domain ? domain->decode(type, code) : 0;
 }
 
-/* Returns the OFPERR_* value that corresponds to the category 'type' within
- * 'version', or 0 if either no such OFPERR_* value exists or 'version' is
- * unknown. */
-enum ofperr
-ofperr_decode_type(enum ofp_version version, uint16_t type)
-{
-    const struct ofperr_domain *domain = ofperr_domain_from_version(version);
-    return domain ? domain->decode_type(type) : 0;
-}
-
 /* Returns the name of 'error', e.g. "OFPBRC_BAD_TYPE" if 'error' is
  * OFPBRC_BAD_TYPE, or "<invalid>" if 'error' is not a valid OFPERR_* value.
  *
@@ -160,37 +118,34 @@ static struct ofpbuf *
 ofperr_encode_msg__(enum ofperr error, enum ofp_version ofp_version,
                     ovs_be32 xid, const void *data, size_t data_len)
 {
+    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+    const struct ofperr_domain *domain;
     struct ofp_error_msg *oem;
     const struct pair *pair;
     struct ofpbuf *buf;
-    const struct ofperr_domain *domain;
 
+    /* Get the error domain for 'ofp_version', or fall back to OF1.0. */
     domain = ofperr_domain_from_version(ofp_version);
     if (!domain) {
-        return NULL;
+        VLOG_ERR_RL(&rl, "cannot encode error for unknown OpenFlow "
+                    "version 0x%02x", ofp_version);
+        domain = &ofperr_of10;
     }
 
-    if (!ofperr_is_encodable(error, ofp_version)) {
-        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
-
-        if (!ofperr_is_valid(error)) {
-            /* 'error' seems likely to be a system errno value. */
-            VLOG_WARN_RL(&rl, "invalid OpenFlow error code %d (%s)",
-                         error, strerror(error));
-        } else {
-            const char *s = ofperr_get_name(error);
-            if (ofperr_is_category(error)) {
-                VLOG_WARN_RL(&rl, "cannot encode error category (%s)", s);
-            } else {
-                VLOG_WARN_RL(&rl, "cannot encode %s for %s", s, domain->name);
-            }
-        }
-
-        return NULL;
+    /* Make sure 'error' is valid in 'domain', or use a fallback error. */
+    if (!ofperr_is_valid(error)) {
+        /* 'error' seems likely to be a system errno value. */
+        VLOG_ERR_RL(&rl, "invalid OpenFlow error code %d (%s)",
+                    error, strerror(error));
+        error = OFPERR_NXBRC_UNENCODABLE_ERROR;
+    } else if (domain->errors[error - OFPERR_OFS].code < 0) {
+        VLOG_ERR_RL(&rl, "cannot encode %s for %s",
+                    ofperr_get_name(error), domain->name);
+        error = OFPERR_NXBRC_UNENCODABLE_ERROR;
     }
 
     pair = ofperr_get_pair__(error, domain);
-    if (!ofperr_is_nx_extension(error)) {
+    if (pair->code < 0x100) {
         buf = ofpraw_alloc_xid(OFPRAW_OFPT_ERROR, domain->version, xid,
                                sizeof *oem + data_len);
 
@@ -214,6 +169,7 @@ ofperr_encode_msg__(enum ofperr error, enum ofp_version ofp_version,
     }
 
     ofpbuf_put(buf, data, data_len);
+    ofpmsg_update_length(buf);
 
     return buf;
 }
@@ -226,9 +182,6 @@ ofperr_encode_msg__(enum ofperr error, enum ofp_version ofp_version,
  * The error reply will contain an initial subsequence of 'oh', up to
  * 'oh->length' or 64 bytes, whichever is shorter.
  *
- * Returns NULL if 'error' is not an OpenFlow error code or if 'error' cannot
- * be encoded as OpenFlow version 'oh->version'.
- *
  * This function isn't appropriate for encoding OFPET_HELLO_FAILED error
  * messages.  Use ofperr_encode_hello() instead. */
 struct ofpbuf *
@@ -245,24 +198,11 @@ ofperr_encode_reply(enum ofperr error, const struct ofp_header *oh)
  *
  * If 'version' is an unknown version then OFP10_VERSION is used.
  * OFPET_HELLO_FAILED error messages are supposed to be backward-compatible,
- * so in theory this should work.
- *
- * Returns NULL if 'error' is not an OpenFlow error code or if 'error' cannot
- * be encoded in 'domain'. */
+ * so in theory this should work. */
 struct ofpbuf *
 ofperr_encode_hello(enum ofperr error, enum ofp_version ofp_version,
                     const char *s)
 {
-    switch (ofp_version) {
-    case OFP10_VERSION:
-    case OFP11_VERSION:
-    case OFP12_VERSION:
-        break;
-
-    default:
-        ofp_version = OFP10_VERSION;
-    }
-
     return ofperr_encode_msg__(error, ofp_version, htonl(0), s, strlen(s));
 }
 
@@ -292,7 +232,7 @@ ofperr_get_code(enum ofperr error, enum ofp_version version)
     return domain ? ofperr_get_pair__(error, domain)->code : -1;
 }
 
-/* Tries to decodes 'oh', which should be an OpenFlow OFPT_ERROR message.
+/* Tries to decode 'oh', which should be an OpenFlow OFPT_ERROR message.
  * Returns an OFPERR_* constant on success, 0 on failure.
  *
  * If 'payload' is nonnull, on success '*payload' is initialized to the
@@ -338,12 +278,8 @@ ofperr_decode_msg(const struct ofp_header *oh, struct ofpbuf *payload)
         code = ntohs(nve->code);
     }
 
-    /* Translate the error type and code into an ofperr.
-     * If we don't know the error type and code, at least try for the type. */
+    /* Translate the error type and code into an ofperr. */
     error = ofperr_decode(oh->version, type, code);
-    if (!error) {
-        error = ofperr_decode_type(oh->version, type);
-    }
     if (error && payload) {
         ofpbuf_use_const(payload, b.data, b.size);
     }
index ffac8aa..593241d 100644 (file)
@@ -68,11 +68,8 @@ enum ofperr {
 /* ## OFPET_HELLO_FAILED ## */
 /* ## ------------------ ## */
 
-    /* OF1.0+(0).  Hello protocol failed. */
-    OFPERR_OFPET_HELLO_FAILED = OFPERR_OFS,
-
     /* OF1.0+(0,0).  No compatible version. */
-    OFPERR_OFPHFC_INCOMPATIBLE,
+    OFPERR_OFPHFC_INCOMPATIBLE = OFPERR_OFS,
 
     /* OF1.0+(0,1).  Permissions error. */
     OFPERR_OFPHFC_EPERM,
@@ -81,9 +78,6 @@ enum ofperr {
 /* ## OFPET_BAD_REQUEST ## */
 /* ## ----------------- ## */
 
-    /* OF1.0+(1).  Request was not understood. */
-    OFPERR_OFPET_BAD_REQUEST,
-
     /* OF1.0+(1,0).  ofp_header.version not supported. */
     OFPERR_OFPBRC_BAD_VERSION,
 
@@ -130,6 +124,9 @@ enum ofperr {
     /* OF1.2+(1,12).  Invalid packet in packet-out. */
     OFPERR_OFPBRC_BAD_PACKET,
 
+    /* OF1.3+(1,13).  Multipart request overflowed the assigned buffer. */
+    OFPERR_OFPBRC_MULTIPART_BUFFER_OVERFLOW,
+
     /* NX1.0+(1,256).  Invalid NXM flow match. */
     OFPERR_NXBRC_NXM_INVALID,
 
@@ -158,13 +155,19 @@ enum ofperr {
      * the id of any existing monitor. */
     OFPERR_NXBRC_FM_BAD_ID,
 
+    /* NX1.0+(1,520).  The 'event' in an NXST_FLOW_MONITOR reply does not
+     * specify one of the NXFME_ABBREV, NXFME_ADD, NXFME_DELETE, or
+     * NXFME_MODIFY. */
+    OFPERR_NXBRC_FM_BAD_EVENT,
+
+    /* NX1.0+(1,521).  The error that occurred cannot be represented in this
+     * OpenFlow version. */
+    OFPERR_NXBRC_UNENCODABLE_ERROR,
+
 /* ## ---------------- ## */
 /* ## OFPET_BAD_ACTION ## */
 /* ## ---------------- ## */
 
-    /* OF1.0+(2).  Error in action description. */
-    OFPERR_OFPET_BAD_ACTION,
-
     /* OF1.0+(2,0).  Unknown action type. */
     OFPERR_OFPBAC_BAD_TYPE,
 
@@ -221,9 +224,6 @@ enum ofperr {
 /* ## OFPET_BAD_INSTRUCTION ## */
 /* ## --------------------- ## */
 
-    /* OF1.1+(3).  Error in instruction list. */
-    OFPERR_OFPIT_BAD_INSTRUCTION,
-
     /* OF1.1+(3,0).  Unknown instruction. */
     OFPERR_OFPBIC_UNKNOWN_INST,
 
@@ -255,9 +255,6 @@ enum ofperr {
 /* ## OFPET_BAD_MATCH ## */
 /* ## --------------- ## */
 
-    /* OF1.1+(4).  Error in match. */
-    OFPERR_OFPET_BAD_MATCH,
-
     /* OF1.1+(4,0).  Unsupported match type specified by the match */
     OFPERR_OFPBMC_BAD_TYPE,
 
@@ -303,9 +300,6 @@ enum ofperr {
 /* ## OFPET_FLOW_MOD_FAILED ## */
 /* ## --------------------- ## */
 
-    /* OF1.0(3), OF1.1+(5).  Problem modifying flow entry. */
-    OFPERR_OFPET_FLOW_MOD_FAILED,
-
     /* OF1.1+(5,0).  Unspecified error. */
     OFPERR_OFPFMFC_UNKNOWN,
 
@@ -351,9 +345,6 @@ enum ofperr {
 /* ## OFPET_GROUP_MOD_FAILED ## */
 /* ## ---------------------- ## */
 
-    /* OF1.1+(6).  Problem modifying group entry. */
-    OFPERR_OFPET_GROUP_MOD_FAILED,
-
     /* OF1.1+(6,0).  Group not added because a group ADD attempted to replace
      * an already-present group. */
     OFPERR_OFPGMFC_GROUP_EXISTS,
@@ -409,9 +400,6 @@ enum ofperr {
 /* ## OFPET_PORT_MOD_FAILED ## */
 /* ## --------------------- ## */
 
-    /* OF1.0(4), OF1.1+(7).  OFPT_PORT_MOD failed. */
-    OFPERR_OFPET_PORT_MOD_FAILED,
-
     /* OF1.0(4,0), OF1.1+(7,0).  Specified port does not exist. */
     OFPERR_OFPPMFC_BAD_PORT,
 
@@ -432,9 +420,6 @@ enum ofperr {
 /* ## OFPET_TABLE_MOD_FAILED ## */
 /* ## ---------------------- ## */
 
-    /* OF1.1+(8).  Table mod request failed. */
-    OFPERR_OFPET_TABLE_MOD_FAILED,
-
     /* OF1.1+(8,0).  Specified table does not exist. */
     OFPERR_OFPTMFC_BAD_TABLE,
 
@@ -448,9 +433,6 @@ enum ofperr {
 /* ## OFPET_QUEUE_OP_FAILED ## */
 /* ## --------------------- ## */
 
-    /* OF1.0(5), OF1.1+(9).  Queue operation failed. */
-    OFPERR_OFPET_QUEUE_OP_FAILED,
-
     /* OF1.0(5,0), OF1.1+(9,0).  Invalid port (or port does not exist). */
     OFPERR_OFPQOFC_BAD_PORT,
 
@@ -464,9 +446,6 @@ enum ofperr {
 /* ## OFPET_SWITCH_CONFIG_FAILED ## */
 /* ## -------------------------- ## */
 
-    /* OF1.1+(10).  Switch config request failed. */
-    OFPERR_OFPET_SWITCH_CONFIG_FAILED,
-
     /* OF1.1+(10,0).  Specified flags is invalid. */
     OFPERR_OFPSCFC_BAD_FLAGS,
 
@@ -480,9 +459,6 @@ enum ofperr {
 /* ## OFPET_ROLE_REQUEST_FAILED ## */
 /* ## ------------------------- ## */
 
-    /* OF1.2+(11).  Controller Role request failed. */
-    OFPERR_OFPET_ROLE_REQUEST_FAILED,
-
     /* OF1.2+(11,0).  Stale Message: old generation_id. */
     OFPERR_OFPRRFC_STALE,
 
@@ -492,23 +468,81 @@ enum ofperr {
     /* NX1.0(1,513), NX1.1(1,513), OF1.2+(11,2).  Invalid role. */
     OFPERR_OFPRRFC_BAD_ROLE,
 
+/* ## ---------------------- ## */
+/* ## OFPET_METER_MOD_FAILED ## */
+/* ## ---------------------- ## */
+
+    /* OF1.3+(12,0).  Unspecified error. */
+    OFPERR_OFPMMFC_UNKNOWN,
+
+    /* OF1.3+(12,1).  Meter not added because a Meter ADD attempted to
+     * replace an existing Meter. */
+    OFPERR_OFPMMFC_METER_EXISTS,
+
+    /* OF1.3+(12,2).  Meter not added because Meter specified is invalid. */
+    OFPERR_OFPMMFC_INVALID_METER,
+
+    /* OF1.3+(12,3).  Meter not modified because a Meter MODIFY attempted
+     * to modify a non-existent Meter. */
+    OFPERR_OFPMMFC_UNKNOWN_METER,
+
+    /* OF1.3+(12,4).  Unsupported or unknown command. */
+    OFPERR_OFPMMFC_BAD_COMMAND,
+
+    /* OF1.3+(12,5).  Flag configuration unsupported. */
+    OFPERR_OFPMMFC_BAD_FLAGS,
+
+    /* OF1.3+(12,6).  Rate unsupported. */
+    OFPERR_OFPMMFC_BAD_RATE,
+
+    /* OF1.3+(12,7).  Burst size unsupported. */
+    OFPERR_OFPMMFC_BAD_BURST,
+
+    /* OF1.3+(12,8).  Band unsupported. */
+    OFPERR_OFPMMFC_BAD_BAND,
+
+    /* OF1.3+(12,9).  Band value unsupported. */
+    OFPERR_OFPMMFC_BAD_BAND_VALUE,
+
+    /* OF1.3+(12,10).  No more meters available. */
+    OFPERR_OFPMMFC_OUT_OF_METERS,
+
+    /* OF1.3+(12,11).  The maximum number of properties for a meter has
+     * been exceeded. */
+    OFPERR_OFPMMFC_OUT_OF_BANDS,
+
+/* ## --------------------------- ## */
+/* ## OFPET_TABLE_FEATURES_FAILED ## */
+/* ## --------------------------- ## */
+
+    /* OF1.3+(13,0).  Specified table does not exist. */
+    OFPERR_OFPTFFC_BAD_TABLE,
+
+    /* OF1.3+(13,1).  Invalid metadata mask. */
+    OFPERR_OFPTFFC_BAD_METADATA,
+
+    /* OF1.3+(13,2).  Unknown property type. */
+    OFPERR_OFPTFFC_BAD_TYPE,
+
+    /* OF1.3+(13,3).  Length problem in properties. */
+    OFPERR_OFPTFFC_BAD_LEN,
+
+    /* OF1.3+(13,4).  Unsupported property value. */
+    OFPERR_OFPTFFC_BAD_ARGUMENT,
+
+    /* OF1.3+(13,5).  Permissions error. */
+    OFPERR_OFPTFFC_EPERM,
+
 /* ## ------------------ ## */
 /* ## OFPET_EXPERIMENTER ## */
 /* ## ------------------ ## */
-
-    /* OF1.2+(0xffff).  Experimenter error messages. */
-    OFPERR_OFPET_EXPERIMENTER,
 };
 
 const char *ofperr_domain_get_name(enum ofp_version);
 
 bool ofperr_is_valid(enum ofperr);
-bool ofperr_is_category(enum ofperr);
-bool ofperr_is_nx_extension(enum ofperr);
-bool ofperr_is_encodable(enum ofperr, enum ofp_version);
 
 enum ofperr ofperr_decode(enum ofp_version, uint16_t type, uint16_t code);
-enum ofperr ofperr_decode_type(enum ofp_version, uint16_t type);
 enum ofperr ofperr_from_name(const char *);
 
 enum ofperr ofperr_decode_msg(const struct ofp_header *,
index 00e1a84..d0f5da6 100644 (file)
@@ -265,6 +265,7 @@ ofphdrs_is_stat(const struct ofphdrs *hdrs)
                 hdrs->type == OFPT10_STATS_REPLY);
     case OFP11_VERSION:
     case OFP12_VERSION:
+    case OFP13_VERSION:
         return (hdrs->type == OFPT11_STATS_REQUEST ||
                 hdrs->type == OFPT11_STATS_REPLY);
     }
@@ -291,6 +292,7 @@ ofphdrs_len(const struct ofphdrs *hdrs)
 
     case OFP11_VERSION:
     case OFP12_VERSION:
+    case OFP13_VERSION:
         if (hdrs->type == OFPT11_STATS_REQUEST ||
             hdrs->type == OFPT11_STATS_REPLY) {
             return (hdrs->stat == OFPST_VENDOR
@@ -704,6 +706,7 @@ ofpraw_stats_request_to_reply(enum ofpraw raw, uint8_t version)
         break;
     case OFP11_VERSION:
     case OFP12_VERSION:
+    case OFP13_VERSION:
         assert(hdrs.type == OFPT11_STATS_REQUEST);
         hdrs.type = OFPT11_STATS_REPLY;
         break;
@@ -880,6 +883,7 @@ ofpmp_flags__(const struct ofp_header *oh)
         return &((struct ofp10_stats_msg *) oh)->flags;
     case OFP11_VERSION:
     case OFP12_VERSION:
+    case OFP13_VERSION:
         return &((struct ofp11_stats_msg *) oh)->flags;
     default:
         NOT_REACHED();
index f8ac4cd..2db4fc9 100644 (file)
@@ -92,27 +92,46 @@ struct list;
  *      to <member>" indicates this, e.g. "struct ofp11_packet_in up to data".
  */
 enum ofpraw {
-/* Standard messages. */
+/* Immutable standard messages.
+ *
+ * The OpenFlow standard promises to preserve these messages and their numbers
+ * in future versions, so we mark them as <all>, which covers every OpenFlow
+ * version numbered 0x01...0xff, rather than as OF1.0+, which covers only
+ * OpenFlow versions that we otherwise implement.
+ *
+ * Without <all> here, then we would fail to decode "hello" messages that
+ * announce a version higher than we understand, even though there still could
+ * be a version in common with the peer that we do understand.  The <all>
+ * keyword is less useful for the other messages, because our OpenFlow channels
+ * accept only OpenFlow messages with a previously negotiated version.
+ */
 
-    /* OFPT 1.0+ (0): uint8_t[]. */
+    /* OFPT <all> (0): uint8_t[]. */
     OFPRAW_OFPT_HELLO,
 
-    /* OFPT 1.0+ (1): struct ofp_error_msg, uint8_t[]. */
+    /* OFPT <all> (1): struct ofp_error_msg, uint8_t[]. */
     OFPRAW_OFPT_ERROR,
 
-    /* OFPT 1.0+ (2): uint8_t[]. */
+    /* OFPT <all> (2): uint8_t[]. */
     OFPRAW_OFPT_ECHO_REQUEST,
 
-    /* OFPT 1.0+ (3): uint8_t[]. */
+    /* OFPT <all> (3): uint8_t[]. */
     OFPRAW_OFPT_ECHO_REPLY,
 
+/* Other standard messages.
+ *
+ * The meanings of these messages can (and often do) change from one version
+ * of OpenFlow to another. */
+
     /* OFPT 1.0+ (5): void. */
     OFPRAW_OFPT_FEATURES_REQUEST,
 
     /* OFPT 1.0 (6): struct ofp_switch_features, struct ofp10_phy_port[]. */
     OFPRAW_OFPT10_FEATURES_REPLY,
-    /* OFPT 1.1+ (6): struct ofp_switch_features, struct ofp11_port[]. */
+    /* OFPT 1.1-1.2 (6): struct ofp_switch_features, struct ofp11_port[]. */
     OFPRAW_OFPT11_FEATURES_REPLY,
+    /* OFPT 1.3+ (6): struct ofp_switch_features. */
+    OFPRAW_OFPT13_FEATURES_REPLY,
 
     /* OFPT 1.0+ (7): void. */
     OFPRAW_OFPT_GET_CONFIG_REQUEST,
@@ -123,16 +142,18 @@ enum ofpraw {
     /* OFPT 1.0+ (9): struct ofp_switch_config. */
     OFPRAW_OFPT_SET_CONFIG,
 
-    /* OFPT 1.0 (10): struct ofp_packet_in up to data, uint8_t[]. */
+    /* OFPT 1.0 (10): struct ofp10_packet_in up to data, uint8_t[]. */
     OFPRAW_OFPT10_PACKET_IN,
-    /* OFPT 1.1 (10): struct ofp11_packet_in up to data, uint8_t[]. */
+    /* OFPT 1.1 (10): struct ofp11_packet_in, uint8_t[]. */
     OFPRAW_OFPT11_PACKET_IN,
     /* OFPT 1.2 (10): struct ofp12_packet_in, uint8_t[]. */
     OFPRAW_OFPT12_PACKET_IN,
+    /* OFPT 1.3 (10): struct ofp13_packet_in, uint8_t[]. */
+    OFPRAW_OFPT13_PACKET_IN,
     /* NXT 1.0+ (17): struct nx_packet_in, uint8_t[]. */
     OFPRAW_NXT_PACKET_IN,
 
-    /* OFPT 1.0 (11): struct ofp_flow_removed. */
+    /* OFPT 1.0 (11): struct ofp10_flow_removed. */
     OFPRAW_OFPT10_FLOW_REMOVED,
     /* OFPT 1.1+ (11): struct ofp11_flow_removed, uint8_t[8][]. */
     OFPRAW_OFPT11_FLOW_REMOVED,
@@ -144,7 +165,7 @@ enum ofpraw {
     /* OFPT 1.1+ (12): struct ofp_port_status, struct ofp11_port. */
     OFPRAW_OFPT11_PORT_STATUS,
 
-    /* OFPT 1.0 (13): struct ofp_packet_out, uint8_t[]. */
+    /* OFPT 1.0 (13): struct ofp10_packet_out, uint8_t[]. */
     OFPRAW_OFPT10_PACKET_OUT,
     /* OFPT 1.1+ (13): struct ofp11_packet_out, uint8_t[]. */
     OFPRAW_OFPT11_PACKET_OUT,
@@ -171,6 +192,34 @@ enum ofpraw {
     /* OFPT 1.1+ (21): void. */
     OFPRAW_OFPT11_BARRIER_REPLY,
 
+    /* OFPT 1.1+ (22): struct ofp11_queue_get_config_request. */
+    OFPRAW_OFPT11_QUEUE_GET_CONFIG_REQUEST,
+
+    /* OFPT 1.1+ (23): struct ofp11_queue_get_config_reply, struct ofp_packet_queue[]. */
+    OFPRAW_OFPT11_QUEUE_GET_CONFIG_REPLY,
+
+    /* OFPT 1.2+ (24): struct ofp12_role_request. */
+    OFPRAW_OFPT12_ROLE_REQUEST,
+    /* NXT 1.0+ (10): struct nx_role_request. */
+    OFPRAW_NXT_ROLE_REQUEST,
+
+    /* OFPT 1.2+ (25): struct ofp12_role_request. */
+    OFPRAW_OFPT12_ROLE_REPLY,
+    /* NXT 1.0+ (11): struct nx_role_request. */
+    OFPRAW_NXT_ROLE_REPLY,
+
+    /* OFPT 1.3+ (26): void. */
+    OFPRAW_OFPT13_GET_ASYNC_REQUEST,
+    /* OFPT 1.3+ (27): struct ofp13_async_config. */
+    OFPRAW_OFPT13_GET_ASYNC_REPLY,
+    /* OFPT 1.3+ (28): struct ofp13_async_config. */
+    OFPRAW_OFPT13_SET_ASYNC,
+    /* NXT 1.0+ (19): struct nx_async_config. */
+    OFPRAW_NXT_SET_ASYNC_CONFIG,
+
+    /* OFPT 1.3+ (29): struct ofp13_meter_mod. */
+    OFPRAW_OFPT13_METER_MOD,
+
 /* Standard statistics. */
 
     /* OFPST 1.0+ (0): void. */
@@ -188,8 +237,10 @@ enum ofpraw {
 
     /* OFPST 1.0 (1): uint8_t[]. */
     OFPRAW_OFPST10_FLOW_REPLY,
-    /* OFPST 1.1+ (1): uint8_t[]. */
+    /* OFPST 1.1-1.2 (1): uint8_t[]. */
     OFPRAW_OFPST11_FLOW_REPLY,
+    /* OFPST 1.3+ (1): uint8_t[]. */
+    OFPRAW_OFPST13_FLOW_REPLY,
     /* NXST 1.0 (0): uint8_t[]. */
     OFPRAW_NXST_FLOW_REPLY,
 
@@ -205,7 +256,7 @@ enum ofpraw {
     /* NXST 1.0 (1): struct ofp_aggregate_stats_reply. */
     OFPRAW_NXST_AGGREGATE_REPLY,
 
-    /* OFPST 1.0-1.2 (3): void. */
+    /* OFPST 1.0+ (3): void. */
     OFPRAW_OFPST_TABLE_REQUEST,
 
     /* OFPST 1.0 (3): struct ofp10_table_stats[]. */
@@ -214,6 +265,8 @@ enum ofpraw {
     OFPRAW_OFPST11_TABLE_REPLY,
     /* OFPST 1.2 (3): struct ofp12_table_stats[]. */
     OFPRAW_OFPST12_TABLE_REPLY,
+    /* OFPST 1.3 (3): struct ofp13_table_stats[]. */
+    OFPRAW_OFPST13_TABLE_REPLY,
 
     /* OFPST 1.0 (4): struct ofp10_port_stats_request. */
     OFPRAW_OFPST10_PORT_REQUEST,
@@ -222,8 +275,10 @@ enum ofpraw {
 
     /* OFPST 1.0 (4): struct ofp10_port_stats[]. */
     OFPRAW_OFPST10_PORT_REPLY,
-    /* OFPST 1.1+ (4): struct ofp11_port_stats[]. */
+    /* OFPST 1.1-1.2 (4): struct ofp11_port_stats[]. */
     OFPRAW_OFPST11_PORT_REPLY,
+    /* OFPST 1.3+ (4): struct ofp13_port_stats[]. */
+    OFPRAW_OFPST13_PORT_REPLY,
 
     /* OFPST 1.0 (5): struct ofp10_queue_stats_request. */
     OFPRAW_OFPST10_QUEUE_REQUEST,
@@ -232,8 +287,54 @@ enum ofpraw {
 
     /* OFPST 1.0 (5): struct ofp10_queue_stats[]. */
     OFPRAW_OFPST10_QUEUE_REPLY,
-    /* OFPST 1.1+ (5): struct ofp11_queue_stats[]. */
+    /* OFPST 1.1-1.2 (5): struct ofp11_queue_stats[]. */
     OFPRAW_OFPST11_QUEUE_REPLY,
+    /* OFPST 1.3+ (5): struct ofp13_queue_stats[]. */
+    OFPRAW_OFPST13_QUEUE_REPLY,
+
+    /* OFPST 1.1+ (6): struct ofp11_group_stats_request. */
+    OFPRAW_OFPST11_GROUP_REQUEST,
+
+    /* OFPST 1.1-1.2 (6): struct ofp11_group_stats[]. */
+    OFPRAW_OFPST11_GROUP_REPLY,
+    /* OFPST 1.3 (6): struct ofp13_group_stats[]. */
+    OFPRAW_OFPST13_GROUP_REPLY,
+
+    /* OFPST 1.1+ (7): void. */
+    OFPRAW_OFPST11_GROUP_DESC_REQUEST,
+
+    /* OFPST 1.1+ (7): struct ofp11_group_desc_stats[]. */
+    OFPRAW_OFPST11_GROUP_DESC_REPLY,
+
+    /* OFPST 1.2+ (8): void. */
+    OFPRAW_OFPST12_GROUP_FEATURES_REQUEST,
+
+    /* OFPST 1.2+ (8): struct ofp12_group_features_stats. */
+    OFPRAW_OFPST12_GROUP_FEATURES_REPLY,
+
+    /* OFPST 1.3+ (9): struct ofp13_meter_multipart_request. */
+    OFPRAW_OFPST13_METER_REQUEST,
+
+    /* OFPST 1.3+ (9): struct ofp13_meter_stats[]. */
+    OFPRAW_OFPST13_METER_REPLY,
+
+    /* OFPST 1.3+ (10): struct ofp13_meter_multipart_request. */
+    OFPRAW_OFPST13_METER_CONFIG_REQUEST,
+
+    /* OFPST 1.3+ (10): struct ofp13_meter_config[]. */
+    OFPRAW_OFPST13_METER_CONFIG_REPLY,
+
+    /* OFPST 1.3+ (11): void. */
+    OFPRAW_OFPST13_METER_FEATURES_REQUEST,
+
+    /* OFPST 1.3+ (11): struct ofp13_meter_features. */
+    OFPRAW_OFPST13_METER_FEATURES_REPLY,
+
+    /* OFPST 1.3+ (12): struct ofp13_table_features[]. */
+    OFPRAW_OFPST13_TABLE_FEATURES_REQUEST,
+
+    /* OFPST 1.3+ (12): struct ofp13_table_features[]. */
+    OFPRAW_OFPST13_TABLE_FEATURES_REPLY,
 
     /* OFPST 1.0+ (13): void. */
     OFPRAW_OFPST_PORT_DESC_REQUEST,
@@ -248,13 +349,7 @@ enum ofpraw {
  * Nicira extensions that correspond to standard OpenFlow messages are listed
  * alongside the standard versions above. */
 
-    /* NXT 1.0+ (10): struct nx_role_request. */
-    OFPRAW_NXT_ROLE_REQUEST,
-
-    /* NXT 1.0+ (11): struct nx_role_request. */
-    OFPRAW_NXT_ROLE_REPLY,
-
-    /* NXT 1.0+ (12): struct nx_set_flow_format. */
+    /* NXT 1.0 (12): struct nx_set_flow_format. */
     OFPRAW_NXT_SET_FLOW_FORMAT,
 
     /* NXT 1.0+ (15): struct nx_flow_mod_table_id. */
@@ -266,9 +361,6 @@ enum ofpraw {
     /* NXT 1.0+ (18): void. */
     OFPRAW_NXT_FLOW_AGE,
 
-    /* NXT 1.0+ (19): struct nx_async_config. */
-    OFPRAW_NXT_SET_ASYNC_CONFIG,
-
     /* NXT 1.0+ (20): struct nx_controller_id. */
     OFPRAW_NXT_SET_CONTROLLER_ID,
 
@@ -343,7 +435,8 @@ enum ofptype {
     /* Switch configuration messages. */
     OFPTYPE_FEATURES_REQUEST,    /* OFPRAW_OFPT_FEATURES_REQUEST. */
     OFPTYPE_FEATURES_REPLY,      /* OFPRAW_OFPT10_FEATURES_REPLY.
-                                  * OFPRAW_OFPT11_FEATURES_REPLY. */
+                                  * OFPRAW_OFPT11_FEATURES_REPLY.
+                                  * OFPRAW_OFPT13_FEATURES_REPLY. */
     OFPTYPE_GET_CONFIG_REQUEST,  /* OFPRAW_OFPT_GET_CONFIG_REQUEST. */
     OFPTYPE_GET_CONFIG_REPLY,    /* OFPRAW_OFPT_GET_CONFIG_REPLY. */
     OFPTYPE_SET_CONFIG,          /* OFPRAW_OFPT_SET_CONFIG. */
@@ -352,6 +445,7 @@ enum ofptype {
     OFPTYPE_PACKET_IN,           /* OFPRAW_OFPT10_PACKET_IN.
                                   * OFPRAW_OFPT11_PACKET_IN.
                                   * OFPRAW_OFPT12_PACKET_IN.
+                                  * OFPRAW_OFPT13_PACKET_IN.
                                   * OFPRAW_NXT_PACKET_IN. */
     OFPTYPE_FLOW_REMOVED,        /* OFPRAW_OFPT10_FLOW_REMOVED.
                                   * OFPRAW_OFPT11_FLOW_REMOVED.
@@ -374,6 +468,25 @@ enum ofptype {
     OFPTYPE_BARRIER_REPLY,       /* OFPRAW_OFPT10_BARRIER_REPLY.
                                   * OFPRAW_OFPT11_BARRIER_REPLY. */
 
+    /* Queue Configuration messages. */
+    OFPTYPE_QUEUE_GET_CONFIG_REQUEST, /* OFPRAW_OFPT11_QUEUE_GET_CONFIG_REQUEST. */
+    OFPTYPE_QUEUE_GET_CONFIG_REPLY, /* OFPRAW_OFPT11_QUEUE_GET_CONFIG_REPLY. */
+
+    /* Controller role change request messages. */
+    OFPTYPE_ROLE_REQUEST,         /* OFPRAW_OFPT12_ROLE_REQUEST.
+                                   * OFPRAW_NXT_ROLE_REQUEST. */
+    OFPTYPE_ROLE_REPLY,           /* OFPRAW_OFPT12_ROLE_REPLY.
+                                   * OFPRAW_NXT_ROLE_REPLY. */
+
+    /* Asynchronous message configuration. */
+    OFPTYPE_GET_ASYNC_REQUEST,    /* OFPRAW_OFPT13_GET_ASYNC_REQUEST. */
+    OFPTYPE_GET_ASYNC_REPLY,      /* OFPRAW_OFPT13_GET_ASYNC_REPLY. */
+    OFPTYPE_SET_ASYNC_CONFIG,     /* OFPRAW_NXT_SET_ASYNC_CONFIG.
+                                   * OFPRAW_OFPT13_SET_ASYNC. */
+
+    /* Meters and rate limiters configuration messages. */
+    OFPTYPE_METER_MOD,            /* OFPRAW_OFPT13_METER_MOD. */
+
     /* Statistics. */
     OFPTYPE_DESC_STATS_REQUEST,      /* OFPRAW_OFPST_DESC_REQUEST. */
     OFPTYPE_DESC_STATS_REPLY,        /* OFPRAW_OFPST_DESC_REPLY. */
@@ -382,6 +495,7 @@ enum ofptype {
                                       * OFPRAW_NXST_FLOW_REQUEST. */
     OFPTYPE_FLOW_STATS_REPLY,        /* OFPRAW_OFPST10_FLOW_REPLY.
                                       * OFPRAW_OFPST11_FLOW_REPLY.
+                                      * OFPRAW_OFPST13_FLOW_REPLY.
                                       * OFPRAW_NXST_FLOW_REPLY. */
     OFPTYPE_AGGREGATE_STATS_REQUEST, /* OFPRAW_OFPST10_AGGREGATE_REQUEST.
                                       * OFPRAW_OFPST11_AGGREGATE_REQUEST.
@@ -391,28 +505,58 @@ enum ofptype {
     OFPTYPE_TABLE_STATS_REQUEST,     /* OFPRAW_OFPST_TABLE_REQUEST. */
     OFPTYPE_TABLE_STATS_REPLY,       /* OFPRAW_OFPST10_TABLE_REPLY.
                                       * OFPRAW_OFPST11_TABLE_REPLY.
-                                      * OFPRAW_OFPST12_TABLE_REPLY. */
+                                      * OFPRAW_OFPST12_TABLE_REPLY.
+                                      * OFPRAW_OFPST13_TABLE_REPLY. */
     OFPTYPE_PORT_STATS_REQUEST,      /* OFPRAW_OFPST10_PORT_REQUEST.
                                       * OFPRAW_OFPST11_PORT_REQUEST. */
     OFPTYPE_PORT_STATS_REPLY,        /* OFPRAW_OFPST10_PORT_REPLY.
-                                      * OFPRAW_OFPST11_PORT_REPLY. */
+                                      * OFPRAW_OFPST11_PORT_REPLY.
+                                      * OFPRAW_OFPST13_PORT_REPLY. */
     OFPTYPE_QUEUE_STATS_REQUEST,     /* OFPRAW_OFPST10_QUEUE_REQUEST.
                                       * OFPRAW_OFPST11_QUEUE_REQUEST. */
     OFPTYPE_QUEUE_STATS_REPLY,       /* OFPRAW_OFPST10_QUEUE_REPLY.
-                                      * OFPRAW_OFPST11_QUEUE_REPLY. */
+                                      * OFPRAW_OFPST11_QUEUE_REPLY.
+                                      * OFPRAW_OFPST13_QUEUE_REPLY. */
+
+    OFPTYPE_GROUP_REQUEST,           /* OFPRAW_OFPST11_GROUP_REQUEST. */
+
+    OFPTYPE_GROUP_REPLY,             /* OFPRAW_OFPST11_GROUP_REPLY.
+                                      * OFPRAW_OFPST13_GROUP_REPLY. */
+
+    OFPTYPE_GROUP_DESC_REQUEST,      /* OFPRAW_OFPST11_GROUP_DESC_REQUEST. */
+
+    OFPTYPE_GROUP_DESC_REPLY,        /* OFPRAW_OFPST11_GROUP_DESC_REPLY. */
+
+    OFPTYPE_GROUP_FEATURES_REQUEST, /* OFPRAW_OFPST12_GROUP_FEATURES_REQUEST. */
+
+    OFPTYPE_GROUP_FEATURES_REPLY,    /* OFPRAW_OFPST12_GROUP_FEATURES_REPLY. */
+
+    OFPTYPE_METER_REQUEST,           /* OFPRAW_OFPST13_METER_REQUEST. */
+
+    OFPTYPE_METER_REPLY,             /* OFPRAW_OFPST13_METER_REPLY. */
+
+    OFPTYPE_METER_CONFIG_REQUEST,    /* OFPRAW_OFPST13_METER_CONFIG_REQUEST. */
+
+    OFPTYPE_METER_CONFIG_REPLY,      /* OFPRAW_OFPST13_METER_CONFIG_REPLY. */
+
+    OFPTYPE_METER_FEATURES_REQUEST, /* OFPRAW_OFPST13_METER_FEATURES_REQUEST. */
+
+    OFPTYPE_METER_FEATURES_REPLY,    /* OFPRAW_OFPST13_METER_FEATURES_REPLY. */
+
+    OFPTYPE_TABLE_FEATURES_REQUEST, /* OFPRAW_OFPST13_TABLE_FEATURES_REQUEST. */
+
+    OFPTYPE_TABLE_FEATURES_REPLY,    /* OFPRAW_OFPST13_TABLE_FEATURES_REPLY. */
+
     OFPTYPE_PORT_DESC_STATS_REQUEST, /* OFPRAW_OFPST_PORT_DESC_REQUEST. */
 
     OFPTYPE_PORT_DESC_STATS_REPLY,   /* OFPRAW_OFPST10_PORT_DESC_REPLY.
                                       * OFPRAW_OFPST11_PORT_DESC_REPLY. */
 
     /* Nicira extensions. */
-    OFPTYPE_ROLE_REQUEST,         /* OFPRAW_NXT_ROLE_REQUEST. */
-    OFPTYPE_ROLE_REPLY,           /* OFPRAW_NXT_ROLE_REPLY. */
     OFPTYPE_SET_FLOW_FORMAT,      /* OFPRAW_NXT_SET_FLOW_FORMAT. */
     OFPTYPE_FLOW_MOD_TABLE_ID,    /* OFPRAW_NXT_FLOW_MOD_TABLE_ID. */
     OFPTYPE_SET_PACKET_IN_FORMAT, /* OFPRAW_NXT_SET_PACKET_IN_FORMAT. */
     OFPTYPE_FLOW_AGE,             /* OFPRAW_NXT_FLOW_AGE. */
-    OFPTYPE_SET_ASYNC_CONFIG,     /* OFPRAW_NXT_SET_ASYNC_CONFIG. */
     OFPTYPE_SET_CONTROLLER_ID,    /* OFPRAW_NXT_SET_CONTROLLER_ID. */
 
     /* Flow monitor extension. */
index dedfb7e..1d0ab85 100644 (file)
@@ -428,12 +428,17 @@ parse_named_action(enum ofputil_action_code code, const struct flow *flow,
     case OFPUTIL_OFPAT11_PUSH_VLAN:
         ethertype = str_to_u16(arg, "ethertype");
         if (ethertype != ETH_TYPE_VLAN_8021Q) {
-            /* TODO:XXXX ETH_TYPE_VLAN_8021AD case isn't supported */
+            /* XXX ETH_TYPE_VLAN_8021AD case isn't supported */
             ovs_fatal(0, "%s: not a valid VLAN ethertype", arg);
         }
         ofpact_put_PUSH_VLAN(ofpacts);
         break;
 
+    case OFPUTIL_OFPAT11_SET_QUEUE:
+        ofpact_put_SET_QUEUE(ofpacts)->queue_id = str_to_u32(arg);
+        break;
+
+
     case OFPUTIL_OFPAT10_SET_DL_SRC:
     case OFPUTIL_OFPAT11_SET_DL_SRC:
         str_to_mac(arg, ofpact_put_SET_ETH_SRC(ofpacts)->mac);
@@ -624,7 +629,7 @@ parse_named_instruction(enum ovs_instruction_type type,
         break;
 
     case OVSINST_OFPIT11_WRITE_ACTIONS:
-        /* TODO:XXX */
+        /* XXX */
         ovs_fatal(0, "instruction write-actions is not supported yet");
         break;
 
@@ -720,7 +725,8 @@ parse_protocol(const char *name, const struct protocol **p_out)
         { "icmp6", ETH_TYPE_IPV6, IPPROTO_ICMPV6 },
         { "tcp6", ETH_TYPE_IPV6, IPPROTO_TCP },
         { "udp6", ETH_TYPE_IPV6, IPPROTO_UDP },
-    };
+        { "rarp", ETH_TYPE_RARP, 0},
+};
     const struct protocol *p;
 
     for (p = protocols; p < &protocols[ARRAY_SIZE(protocols)]; p++) {
@@ -828,7 +834,7 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
     fm->idle_timeout = OFP_FLOW_PERMANENT;
     fm->hard_timeout = OFP_FLOW_PERMANENT;
     fm->buffer_id = UINT32_MAX;
-    fm->out_port = OFPP_NONE;
+    fm->out_port = OFPP_ANY;
     fm->flags = 0;
     if (fields & F_ACTIONS) {
         act_str = strstr(string, "action");
@@ -857,6 +863,12 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
             fm->flags |= OFPFF_SEND_FLOW_REM;
         } else if (fields & F_FLAGS && !strcmp(name, "check_overlap")) {
             fm->flags |= OFPFF_CHECK_OVERLAP;
+        } else if (fields & F_FLAGS && !strcmp(name, "reset_counts")) {
+            fm->flags |= OFPFF12_RESET_COUNTS;
+        } else if (fields & F_FLAGS && !strcmp(name, "no_packet_counts")) {
+            fm->flags |= OFPFF13_NO_PKT_COUNTS;
+        } else if (fields & F_FLAGS && !strcmp(name, "no_byte_counts")) {
+            fm->flags |= OFPFF13_NO_BYT_COUNTS;
         } else {
             char *value;
 
index 8654783..b97180e 100644 (file)
@@ -63,7 +63,7 @@ ofp_packet_to_string(const void *data, size_t len)
     struct flow flow;
 
     ofpbuf_use_const(&buf, data, len);
-    flow_extract(&buf, 0, NULL, 0, &flow);
+    flow_extract(&buf, 0, 0, NULL, 0, &flow);
     flow_format(&ds, &flow);
 
     if (buf.l7) {
@@ -492,8 +492,13 @@ ofp_print_switch_features(struct ds *string, const struct ofp_header *oh)
     }
 
     ds_put_format(string, " dpid:%016"PRIx64"\n", features.datapath_id);
-    ds_put_format(string, "n_tables:%"PRIu8", n_buffers:%"PRIu32"\n",
+
+    ds_put_format(string, "n_tables:%"PRIu8", n_buffers:%"PRIu32,
                   features.n_tables, features.n_buffers);
+    if (features.auxiliary_id) {
+        ds_put_format(string, ", auxiliary_id:%"PRIu8, features.auxiliary_id);
+    }
+    ds_put_char(string, '\n');
 
     ds_put_cstr(string, "capabilities: ");
     ofp_print_bit_names(string, features.capabilities,
@@ -510,6 +515,8 @@ ofp_print_switch_features(struct ds *string, const struct ofp_header *oh)
     case OFP11_VERSION:
     case OFP12_VERSION:
         break;
+    case OFP13_VERSION:
+        return; /* no ports in ofp13_switch_features */
     default:
         NOT_REACHED();
     }
@@ -562,6 +569,22 @@ static void print_wild(struct ds *string, const char *leader, int is_wild,
     ds_put_char(string, ',');
 }
 
+static void
+print_wild_port(struct ds *string, const char *leader, int is_wild,
+                int verbosity, uint16_t port)
+{
+    if (is_wild && verbosity < 2) {
+        return;
+    }
+    ds_put_cstr(string, leader);
+    if (!is_wild) {
+        ofputil_format_port(port, string);
+    } else {
+        ds_put_char(string, '*');
+    }
+    ds_put_char(string, ',');
+}
+
 static void
 print_ip_netmask(struct ds *string, const char *leader, ovs_be32 ip,
                  uint32_t wild_bits, int verbosity)
@@ -571,7 +594,7 @@ print_ip_netmask(struct ds *string, const char *leader, ovs_be32 ip,
     }
     ds_put_cstr(string, leader);
     if (wild_bits < 32) {
-        ds_put_format(string, IP_FMT, IP_ARGS(&ip));
+        ds_put_format(string, IP_FMT, IP_ARGS(ip));
         if (wild_bits) {
             ds_put_format(string, "/%d", 32 - wild_bits);
         }
@@ -617,12 +640,14 @@ ofp10_match_to_string(const struct ofp10_match *om, int verbosity)
             }
         } else if (om->dl_type == htons(ETH_TYPE_ARP)) {
             ds_put_cstr(&f, "arp,");
+        } else if (om->dl_type == htons(ETH_TYPE_RARP)){
+            ds_put_cstr(&f, "rarp,");
         } else {
             skip_type = false;
         }
     }
-    print_wild(&f, "in_port=", w & OFPFW10_IN_PORT, verbosity,
-               "%d", ntohs(om->in_port));
+    print_wild_port(&f, "in_port=", w & OFPFW10_IN_PORT, verbosity,
+                    ntohs(om->in_port));
     print_wild(&f, "dl_vlan=", w & OFPFW10_DL_VLAN, verbosity,
                "%d", ntohs(om->dl_vlan));
     print_wild(&f, "dl_vlan_pcp=", w & OFPFW10_DL_VLAN_PCP, verbosity,
@@ -642,7 +667,8 @@ ofp10_match_to_string(const struct ofp10_match *om, int verbosity)
                      (w & OFPFW10_NW_DST_MASK) >> OFPFW10_NW_DST_SHIFT,
                      verbosity);
     if (!skip_proto) {
-        if (om->dl_type == htons(ETH_TYPE_ARP)) {
+        if (om->dl_type == htons(ETH_TYPE_ARP) ||
+            om->dl_type == htons(ETH_TYPE_RARP)) {
             print_wild(&f, "arp_op=", w & OFPFW10_NW_PROTO, verbosity,
                        "%u", om->nw_proto);
         } else {
@@ -669,6 +695,33 @@ ofp10_match_to_string(const struct ofp10_match *om, int verbosity)
     return ds_cstr(&f);
 }
 
+static void
+ofp_print_flow_flags(struct ds *s, uint16_t flags)
+{
+    if (flags & OFPFF_SEND_FLOW_REM) {
+        ds_put_cstr(s, "send_flow_rem ");
+    }
+    if (flags & OFPFF_CHECK_OVERLAP) {
+        ds_put_cstr(s, "check_overlap ");
+    }
+    if (flags & OFPFF12_RESET_COUNTS) {
+        ds_put_cstr(s, "reset_counts ");
+    }
+    if (flags & OFPFF13_NO_PKT_COUNTS) {
+        ds_put_cstr(s, "no_packet_counts ");
+    }
+    if (flags & OFPFF13_NO_BYT_COUNTS) {
+        ds_put_cstr(s, "no_byte_counts ");
+    }
+
+    flags &= ~(OFPFF_SEND_FLOW_REM | OFPFF_CHECK_OVERLAP
+               | OFPFF12_RESET_COUNTS
+               | OFPFF13_NO_PKT_COUNTS | OFPFF13_NO_BYT_COUNTS);
+    if (flags) {
+        ds_put_format(s, "flags:0x%"PRIx16" ", flags);
+    }
+}
+
 static void
 ofp_print_flow_mod(struct ds *s, const struct ofp_header *oh, int verbosity)
 {
@@ -762,28 +815,13 @@ ofp_print_flow_mod(struct ds *s, const struct ofp_header *oh, int verbosity)
     if (fm.buffer_id != UINT32_MAX) {
         ds_put_format(s, "buf:0x%"PRIx32" ", fm.buffer_id);
     }
-    if (fm.out_port != OFPP_NONE) {
+    if (fm.out_port != OFPP_ANY) {
         ds_put_format(s, "out_port:");
         ofputil_format_port(fm.out_port, s);
         ds_put_char(s, ' ');
     }
     if (fm.flags != 0) {
-        uint16_t flags = fm.flags;
-
-        if (flags & OFPFF_SEND_FLOW_REM) {
-            ds_put_cstr(s, "send_flow_rem ");
-        }
-        if (flags & OFPFF_CHECK_OVERLAP) {
-            ds_put_cstr(s, "check_overlap ");
-        }
-        if (flags & OFPFF10_EMERG) {
-            ds_put_cstr(s, "emerg ");
-        }
-
-        flags &= ~(OFPFF_SEND_FLOW_REM | OFPFF_CHECK_OVERLAP | OFPFF10_EMERG);
-        if (flags) {
-            ds_put_format(s, "flags:0x%"PRIx16" ", flags);
-        }
+        ofp_print_flow_flags(s, fm.flags);
     }
 
     ofpacts_format(fm.ofpacts, fm.ofpacts_len, s);
@@ -874,20 +912,22 @@ ofp_print_port_mod(struct ds *string, const struct ofp_header *oh)
         return;
     }
 
-    ds_put_format(string, "port: %"PRIu16": addr:"ETH_ADDR_FMT"\n",
-                  pm.port_no, ETH_ADDR_ARGS(pm.hw_addr));
+    ds_put_cstr(string, "port: ");
+    ofputil_format_port(pm.port_no, string);
+    ds_put_format(string, ": addr:"ETH_ADDR_FMT"\n",
+                  ETH_ADDR_ARGS(pm.hw_addr));
 
-    ds_put_format(string, "     config: ");
+    ds_put_cstr(string, "     config: ");
     ofp_print_port_config(string, pm.config);
 
-    ds_put_format(string, "     mask:   ");
+    ds_put_cstr(string, "     mask:   ");
     ofp_print_port_config(string, pm.mask);
 
-    ds_put_format(string, "     advertise: ");
+    ds_put_cstr(string, "     advertise: ");
     if (pm.advertise) {
         ofp_print_port_features(string, pm.advertise);
     } else {
-        ds_put_format(string, "UNCHANGED\n");
+        ds_put_cstr(string, "UNCHANGED\n");
     }
 }
 
@@ -900,6 +940,23 @@ ofp_print_error(struct ds *string, enum ofperr error)
     ds_put_format(string, "***decode error: %s***\n", ofperr_get_name(error));
 }
 
+static void
+ofp_print_hello(struct ds *string, const struct ofp_header *oh)
+{
+    uint32_t allowed_versions;
+    bool ok;
+
+    ok = ofputil_decode_hello(oh, &allowed_versions);
+
+    ds_put_cstr(string, "\n version bitmap: ");
+    ofputil_format_version_bitmap(string, allowed_versions);
+
+    if (!ok) {
+        ds_put_cstr(string, "\n unknown data in hello:\n");
+        ds_put_hex_dump(string, oh, ntohs(oh->length), 0, true);
+    }
+}
+
 static void
 ofp_print_error_msg(struct ds *string, const struct ofp_header *oh)
 {
@@ -983,7 +1040,7 @@ ofp_print_flow_stats_request(struct ds *string, const struct ofp_header *oh)
         ds_put_format(string, " table=%"PRIu8, fsr.table_id);
     }
 
-    if (fsr.out_port != OFPP_NONE) {
+    if (fsr.out_port != OFPP_ANY) {
         ds_put_cstr(string, " out_port=");
         ofputil_format_port(fsr.out_port, string);
     }
@@ -1008,6 +1065,9 @@ ofp_print_flow_stats(struct ds *string, struct ofputil_flow_stats *fs)
     if (fs->hard_timeout != OFP_FLOW_PERMANENT) {
         ds_put_format(string, "hard_timeout=%"PRIu16", ", fs->hard_timeout);
     }
+    if (fs->flags) {
+        ofp_print_flow_flags(string, fs->flags);
+    }
     if (fs->idle_age >= 0) {
         ds_put_format(string, "idle_age=%d, ", fs->idle_age);
     }
@@ -1093,7 +1153,8 @@ ofp_print_ofpst_port_request(struct ds *string, const struct ofp_header *oh)
         return;
     }
 
-    ds_put_format(string, " port_no=%2"PRIu16, ofp10_port);
+    ds_put_cstr(string, " port_no=");
+    ofputil_format_port(ofp10_port, string);
 }
 
 static void
@@ -1120,7 +1181,11 @@ ofp_print_ofpst_port_reply(struct ds *string, const struct ofp_header *oh,
             return;
         }
 
-        ds_put_format(string, "  port %2"PRIu16, ps.port_no);
+        ds_put_cstr(string, "  port ");
+        if (ps.port_no < 10) {
+            ds_put_char(string, ' ');
+        }
+        ofputil_format_port(ps.port_no, string);
 
         ds_put_cstr(string, ": rx ");
         print_port_stat(string, "pkts=", ps.stats.rx_packets, 1);
@@ -1146,6 +1211,11 @@ ofp_print_one_ofpst_table_reply(struct ds *string, enum ofp_version ofp_version,
 {
     char name_[OFP_MAX_TABLE_NAME_LEN + 1];
 
+    /* ofp13_table_stats is different */
+    if (ofp_version > OFP12_VERSION) {
+        return;
+    }
+
     ovs_strlcpy(name_, name, sizeof name_);
 
     ds_put_format(string, "  %d: %-8s: ", ts->table_id, name_);
@@ -1189,6 +1259,36 @@ ofp_print_one_ofpst_table_reply(struct ds *string, enum ofp_version ofp_version,
                   ntohll(ts->metadata_write));
 }
 
+static void
+ofp_print_ofpst_table_reply13(struct ds *string, const struct ofp_header *oh,
+                              int verbosity)
+{
+    struct ofp13_table_stats *ts;
+    struct ofpbuf b;
+    size_t n;
+
+    ofpbuf_use_const(&b, oh, ntohs(oh->length));
+    ofpraw_pull_assert(&b);
+
+    n = b.size / sizeof *ts;
+    ds_put_format(string, " %zu tables\n", n);
+    if (verbosity < 1) {
+        return;
+    }
+
+    for (;;) {
+        ts = ofpbuf_try_pull(&b, sizeof *ts);
+        if (!ts) {
+            return;
+        }
+        ds_put_format(string,
+                      "  %d: active=%"PRIu32", lookup=%"PRIu64  \
+                      ", matched=%"PRIu64"\n",
+                      ts->table_id, ntohl(ts->active_count),
+                      ntohll(ts->lookup_count), ntohll(ts->matched_count));
+    }
+}
+
 static void
 ofp_print_ofpst_table_reply12(struct ds *string, const struct ofp_header *oh,
                               int verbosity)
@@ -1296,6 +1396,10 @@ ofp_print_ofpst_table_reply(struct ds *string, const struct ofp_header *oh,
                             int verbosity)
 {
     switch ((enum ofp_version)oh->version) {
+    case OFP13_VERSION:
+        ofp_print_ofpst_table_reply13(string, oh, verbosity);
+        break;
+
     case OFP12_VERSION:
         ofp_print_ofpst_table_reply12(string, oh, verbosity);
         break;
@@ -1430,20 +1534,39 @@ ofp_print_echo(struct ds *string, const struct ofp_header *oh, int verbosity)
 }
 
 static void
-ofp_print_nxt_role_message(struct ds *string,
-                           const struct nx_role_request *nrr)
+ofp_print_role_message(struct ds *string, const struct ofp_header *oh)
 {
-    unsigned int role = ntohl(nrr->role);
+    struct ofputil_role_request rr;
+    enum ofperr error;
+
+    error = ofputil_decode_role_message(oh, &rr);
+    if (error) {
+        ofp_print_error(string, error);
+        return;
+    }
 
     ds_put_cstr(string, " role=");
-    if (role == NX_ROLE_OTHER) {
-        ds_put_cstr(string, "other");
-    } else if (role == NX_ROLE_MASTER) {
+    if (rr.request_current_role_only) {
+        ds_put_cstr(string, "nochange");
+        return;
+    }
+
+    switch (rr.role) {
+    case NX_ROLE_OTHER:
+        ds_put_cstr(string, "equal"); /* OF 1.2 wording */
+        break;
+    case NX_ROLE_MASTER:
         ds_put_cstr(string, "master");
-    } else if (role == NX_ROLE_SLAVE) {
+        break;
+    case NX_ROLE_SLAVE:
         ds_put_cstr(string, "slave");
-    } else {
-        ds_put_format(string, "%u", role);
+        break;
+    default:
+        NOT_REACHED();
+    }
+
+    if (rr.have_generation_id) {
+        ds_put_format(string, " generation_id=%"PRIu64, rr.generation_id);
     }
 }
 
@@ -1702,6 +1825,9 @@ ofp_print_version(const struct ofp_header *oh,
     case OFP12_VERSION:
         ds_put_cstr(string, " (OF1.2)");
         break;
+    case OFP13_VERSION:
+        ds_put_cstr(string, " (OF1.3)");
+        break;
     default:
         ds_put_format(string, " (OF 0x%02"PRIx8")", oh->version);
         break;
@@ -1717,6 +1843,12 @@ ofp_header_to_string__(const struct ofp_header *oh, enum ofpraw raw,
     ofp_print_version(oh, string);
 }
 
+static void
+ofp_print_not_implemented(struct ds *string)
+{
+    ds_put_cstr(string, "NOT IMPLEMENTED YET!\n");
+}
+
 static void
 ofp_to_string__(const struct ofp_header *oh, enum ofpraw raw,
                 struct ds *string, int verbosity)
@@ -1725,10 +1857,32 @@ ofp_to_string__(const struct ofp_header *oh, enum ofpraw raw,
 
     ofp_header_to_string__(oh, raw, string);
     switch (ofptype_from_ofpraw(raw)) {
+
+        /* FIXME: Change the following once they are implemented: */
+    case OFPTYPE_QUEUE_GET_CONFIG_REQUEST:
+    case OFPTYPE_QUEUE_GET_CONFIG_REPLY:
+    case OFPTYPE_GET_ASYNC_REQUEST:
+    case OFPTYPE_GET_ASYNC_REPLY:
+    case OFPTYPE_METER_MOD:
+    case OFPTYPE_GROUP_REQUEST:
+    case OFPTYPE_GROUP_REPLY:
+    case OFPTYPE_GROUP_DESC_REQUEST:
+    case OFPTYPE_GROUP_DESC_REPLY:
+    case OFPTYPE_GROUP_FEATURES_REQUEST:
+    case OFPTYPE_GROUP_FEATURES_REPLY:
+    case OFPTYPE_METER_REQUEST:
+    case OFPTYPE_METER_REPLY:
+    case OFPTYPE_METER_CONFIG_REQUEST:
+    case OFPTYPE_METER_CONFIG_REPLY:
+    case OFPTYPE_METER_FEATURES_REQUEST:
+    case OFPTYPE_METER_FEATURES_REPLY:
+    case OFPTYPE_TABLE_FEATURES_REQUEST:
+    case OFPTYPE_TABLE_FEATURES_REPLY:
+        ofp_print_not_implemented(string);
+        break;
+
     case OFPTYPE_HELLO:
-        ds_put_char(string, '\n');
-        ds_put_hex_dump(string, oh + 1, ntohs(oh->length) - sizeof *oh,
-                        0, true);
+        ofp_print_hello(string, oh);
         break;
 
     case OFPTYPE_ERROR:
@@ -1783,6 +1937,11 @@ ofp_to_string__(const struct ofp_header *oh, enum ofpraw raw,
     case OFPTYPE_BARRIER_REPLY:
         break;
 
+    case OFPTYPE_ROLE_REQUEST:
+    case OFPTYPE_ROLE_REPLY:
+        ofp_print_role_message(string, oh);
+        break;
+
     case OFPTYPE_DESC_STATS_REQUEST:
     case OFPTYPE_PORT_DESC_STATS_REQUEST:
         ofp_print_stats_request(string, oh);
@@ -1843,11 +2002,6 @@ ofp_to_string__(const struct ofp_header *oh, enum ofpraw raw,
         ofp_print_ofpst_port_desc_reply(string, oh);
         break;
 
-    case OFPTYPE_ROLE_REQUEST:
-    case OFPTYPE_ROLE_REPLY:
-        ofp_print_nxt_role_message(string, ofpmsg_body(oh));
-        break;
-
     case OFPTYPE_FLOW_MOD_TABLE_ID:
         ofp_print_nxt_flow_mod_table_id(string, ofpmsg_body(oh));
         break;
index 34255da..9c9aaef 100644 (file)
@@ -16,6 +16,7 @@
 
 #include <config.h>
 #include "ofp-print.h"
+#include <ctype.h>
 #include <errno.h>
 #include <inttypes.h>
 #include <sys/types.h>
@@ -84,7 +85,7 @@ ofputil_netmask_to_wcbits(ovs_be32 netmask)
 void
 ofputil_wildcard_from_ofpfw10(uint32_t ofpfw, struct flow_wildcards *wc)
 {
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 17);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 18);
 
     /* Initialize most of wc. */
     flow_wildcards_init_catchall(wc);
@@ -305,7 +306,7 @@ ofputil_match_from_ofp11_match(const struct ofp11_match *ofmatch,
     uint16_t wc = ntohl(ofmatch->wildcards);
     uint8_t dl_src_mask[ETH_ADDR_LEN];
     uint8_t dl_dst_mask[ETH_ADDR_LEN];
-    bool ipv4, arp;
+    bool ipv4, arp, rarp;
     int i;
 
     match_init_catchall(match);
@@ -370,6 +371,7 @@ ofputil_match_from_ofp11_match(const struct ofp11_match *ofmatch,
 
     ipv4 = match->flow.dl_type == htons(ETH_TYPE_IP);
     arp = match->flow.dl_type == htons(ETH_TYPE_ARP);
+    rarp = match->flow.dl_type == htons(ETH_TYPE_RARP);
 
     if (ipv4 && !(wc & OFPFW11_NW_TOS)) {
         if (ofmatch->nw_tos & ~IP_DSCP_MASK) {
@@ -380,7 +382,7 @@ ofputil_match_from_ofp11_match(const struct ofp11_match *ofmatch,
         match_set_nw_dscp(match, ofmatch->nw_tos);
     }
 
-    if (ipv4 || arp) {
+    if (ipv4 || arp || rarp) {
         if (!(wc & OFPFW11_NW_PROTO)) {
             match_set_nw_proto(match, ofmatch->nw_proto);
         }
@@ -579,55 +581,104 @@ struct proto_abbrev {
 /* Most users really don't care about some of the differences between
  * protocols.  These abbreviations help with that. */
 static const struct proto_abbrev proto_abbrevs[] = {
-    { OFPUTIL_P_ANY,      "any" },
-    { OFPUTIL_P_OF10_ANY, "OpenFlow10" },
-    { OFPUTIL_P_NXM_ANY,  "NXM" },
+    { OFPUTIL_P_ANY,          "any" },
+    { OFPUTIL_P_OF10_STD_ANY, "OpenFlow10" },
+    { OFPUTIL_P_OF10_NXM_ANY, "NXM" },
+    { OFPUTIL_P_ANY_OXM,      "OXM" },
 };
 #define N_PROTO_ABBREVS ARRAY_SIZE(proto_abbrevs)
 
 enum ofputil_protocol ofputil_flow_dump_protocols[] = {
-    OFPUTIL_P_NXM,
-    OFPUTIL_P_OF10,
+    OFPUTIL_P_OF13_OXM,
+    OFPUTIL_P_OF12_OXM,
+    OFPUTIL_P_OF10_NXM,
+    OFPUTIL_P_OF10_STD,
 };
 size_t ofputil_n_flow_dump_protocols = ARRAY_SIZE(ofputil_flow_dump_protocols);
 
-/* Returns the ofputil_protocol that is initially in effect on an OpenFlow
- * connection that has negotiated the given 'version'.  'version' should
- * normally be an 8-bit OpenFlow version identifier (e.g. 0x01 for OpenFlow
- * 1.0, 0x02 for OpenFlow 1.1).  Returns 0 if 'version' is not supported or
- * outside the valid range.  */
+/* Returns the set of ofputil_protocols that are supported with the given
+ * OpenFlow 'version'.  'version' should normally be an 8-bit OpenFlow version
+ * identifier (e.g. 0x01 for OpenFlow 1.0, 0x02 for OpenFlow 1.1).  Returns 0
+ * if 'version' is not supported or outside the valid range.  */
 enum ofputil_protocol
-ofputil_protocol_from_ofp_version(enum ofp_version version)
+ofputil_protocols_from_ofp_version(enum ofp_version version)
 {
     switch (version) {
     case OFP10_VERSION:
-        return OFPUTIL_P_OF10;
+        return OFPUTIL_P_OF10_STD_ANY | OFPUTIL_P_OF10_NXM_ANY;
     case OFP12_VERSION:
-        return OFPUTIL_P_OF12;
+        return OFPUTIL_P_OF12_OXM;
+    case OFP13_VERSION:
+        return OFPUTIL_P_OF13_OXM;
     case OFP11_VERSION:
     default:
         return 0;
     }
 }
 
+/* Returns the ofputil_protocol that is initially in effect on an OpenFlow
+ * connection that has negotiated the given 'version'.  'version' should
+ * normally be an 8-bit OpenFlow version identifier (e.g. 0x01 for OpenFlow
+ * 1.0, 0x02 for OpenFlow 1.1).  Returns 0 if 'version' is not supported or
+ * outside the valid range.  */
+enum ofputil_protocol
+ofputil_protocol_from_ofp_version(enum ofp_version version)
+{
+    return rightmost_1bit(ofputil_protocols_from_ofp_version(version));
+}
+
 /* Returns the OpenFlow protocol version number (e.g. OFP10_VERSION,
- * OFP11_VERSION or OFP12_VERSION) that corresponds to 'protocol'. */
+ * etc.) that corresponds to 'protocol'. */
 enum ofp_version
 ofputil_protocol_to_ofp_version(enum ofputil_protocol protocol)
 {
     switch (protocol) {
-    case OFPUTIL_P_OF10:
-    case OFPUTIL_P_OF10_TID:
-    case OFPUTIL_P_NXM:
-    case OFPUTIL_P_NXM_TID:
+    case OFPUTIL_P_OF10_STD:
+    case OFPUTIL_P_OF10_STD_TID:
+    case OFPUTIL_P_OF10_NXM:
+    case OFPUTIL_P_OF10_NXM_TID:
         return OFP10_VERSION;
-    case OFPUTIL_P_OF12:
+    case OFPUTIL_P_OF12_OXM:
         return OFP12_VERSION;
+    case OFPUTIL_P_OF13_OXM:
+        return OFP13_VERSION;
     }
 
     NOT_REACHED();
 }
 
+/* Returns a bitmap of OpenFlow versions that are supported by at
+ * least one of the 'protocols'. */
+uint32_t
+ofputil_protocols_to_version_bitmap(enum ofputil_protocol protocols)
+{
+    uint32_t bitmap = 0;
+
+    for (; protocols; protocols = zero_rightmost_1bit(protocols)) {
+        enum ofputil_protocol protocol = rightmost_1bit(protocols);
+
+        bitmap |= 1u << ofputil_protocol_to_ofp_version(protocol);
+    }
+
+    return bitmap;
+}
+
+/* Returns the set of protocols that are supported on top of the
+ * OpenFlow versions included in 'bitmap'. */
+enum ofputil_protocol
+ofputil_protocols_from_version_bitmap(uint32_t bitmap)
+{
+    enum ofputil_protocol protocols = 0;
+
+    for (; bitmap; bitmap = zero_rightmost_1bit(bitmap)) {
+        enum ofp_version version = rightmost_1bit_idx(bitmap);
+
+        protocols |= ofputil_protocols_from_ofp_version(version);
+    }
+
+    return protocols;
+}
+
 /* Returns true if 'protocol' is a single OFPUTIL_P_* value, false
  * otherwise. */
 bool
@@ -650,16 +701,19 @@ enum ofputil_protocol
 ofputil_protocol_set_tid(enum ofputil_protocol protocol, bool enable)
 {
     switch (protocol) {
-    case OFPUTIL_P_OF10:
-    case OFPUTIL_P_OF10_TID:
-        return enable ? OFPUTIL_P_OF10_TID : OFPUTIL_P_OF10;
+    case OFPUTIL_P_OF10_STD:
+    case OFPUTIL_P_OF10_STD_TID:
+        return enable ? OFPUTIL_P_OF10_STD_TID : OFPUTIL_P_OF10_STD;
 
-    case OFPUTIL_P_NXM:
-    case OFPUTIL_P_NXM_TID:
-        return enable ? OFPUTIL_P_NXM_TID : OFPUTIL_P_NXM;
+    case OFPUTIL_P_OF10_NXM:
+    case OFPUTIL_P_OF10_NXM_TID:
+        return enable ? OFPUTIL_P_OF10_NXM_TID : OFPUTIL_P_OF10_NXM;
 
-    case OFPUTIL_P_OF12:
-        return OFPUTIL_P_OF12;
+    case OFPUTIL_P_OF12_OXM:
+        return OFPUTIL_P_OF12_OXM;
+
+    case OFPUTIL_P_OF13_OXM:
+        return OFPUTIL_P_OF13_OXM;
 
     default:
         NOT_REACHED();
@@ -684,16 +738,19 @@ ofputil_protocol_set_base(enum ofputil_protocol cur,
     bool tid = (cur & OFPUTIL_P_TID) != 0;
 
     switch (new_base) {
-    case OFPUTIL_P_OF10:
-    case OFPUTIL_P_OF10_TID:
-        return ofputil_protocol_set_tid(OFPUTIL_P_OF10, tid);
+    case OFPUTIL_P_OF10_STD:
+    case OFPUTIL_P_OF10_STD_TID:
+        return ofputil_protocol_set_tid(OFPUTIL_P_OF10_STD, tid);
+
+    case OFPUTIL_P_OF10_NXM:
+    case OFPUTIL_P_OF10_NXM_TID:
+        return ofputil_protocol_set_tid(OFPUTIL_P_OF10_NXM, tid);
 
-    case OFPUTIL_P_NXM:
-    case OFPUTIL_P_NXM_TID:
-        return ofputil_protocol_set_tid(OFPUTIL_P_NXM, tid);
+    case OFPUTIL_P_OF12_OXM:
+        return ofputil_protocol_set_tid(OFPUTIL_P_OF12_OXM, tid);
 
-    case OFPUTIL_P_OF12:
-        return ofputil_protocol_set_tid(OFPUTIL_P_OF12, tid);
+    case OFPUTIL_P_OF13_OXM:
+        return ofputil_protocol_set_tid(OFPUTIL_P_OF13_OXM, tid);
 
     default:
         NOT_REACHED();
@@ -711,20 +768,23 @@ ofputil_protocol_to_string(enum ofputil_protocol protocol)
     /* Use a "switch" statement for single-bit names so that we get a compiler
      * warning if we forget any. */
     switch (protocol) {
-    case OFPUTIL_P_NXM:
+    case OFPUTIL_P_OF10_NXM:
         return "NXM-table_id";
 
-    case OFPUTIL_P_NXM_TID:
+    case OFPUTIL_P_OF10_NXM_TID:
         return "NXM+table_id";
 
-    case OFPUTIL_P_OF10:
+    case OFPUTIL_P_OF10_STD:
         return "OpenFlow10-table_id";
 
-    case OFPUTIL_P_OF10_TID:
+    case OFPUTIL_P_OF10_STD_TID:
         return "OpenFlow10+table_id";
 
-    case OFPUTIL_P_OF12:
-        return NULL;
+    case OFPUTIL_P_OF12_OXM:
+        return "OXM-OpenFlow12";
+
+    case OFPUTIL_P_OF13_OXM:
+        return "OXM-OpenFlow13";
     }
 
     /* Check abbreviations. */
@@ -845,6 +905,96 @@ ofputil_protocols_from_string(const char *s)
     return protocols;
 }
 
+static int
+ofputil_version_from_string(const char *s)
+{
+    if (!strcasecmp(s, "OpenFlow10")) {
+        return OFP10_VERSION;
+    }
+    if (!strcasecmp(s, "OpenFlow11")) {
+        return OFP11_VERSION;
+    }
+    if (!strcasecmp(s, "OpenFlow12")) {
+        return OFP12_VERSION;
+    }
+    if (!strcasecmp(s, "OpenFlow13")) {
+        return OFP13_VERSION;
+    }
+    return 0;
+}
+
+static bool
+is_delimiter(char c)
+{
+    return isspace(c) || c == ',';
+}
+
+uint32_t
+ofputil_versions_from_string(const char *s)
+{
+    size_t i = 0;
+    uint32_t bitmap = 0;
+
+    while (s[i]) {
+        size_t j;
+        int version;
+        char *key;
+
+        if (is_delimiter(s[i])) {
+            i++;
+            continue;
+        }
+        j = 0;
+        while (s[i + j] && !is_delimiter(s[i + j])) {
+            j++;
+        }
+        key = xmemdup0(s + i, j);
+        version = ofputil_version_from_string(key);
+        if (!version) {
+            VLOG_FATAL("Unknown OpenFlow version: \"%s\"", key);
+        }
+        free(key);
+        bitmap |= 1u << version;
+        i += j;
+    }
+
+    return bitmap;
+}
+
+uint32_t
+ofputil_versions_from_strings(char ** const s, size_t count)
+{
+    uint32_t bitmap = 0;
+
+    while (count--) {
+        int version = ofputil_version_from_string(s[count]);
+        if (!version) {
+            VLOG_WARN("Unknown OpenFlow version: \"%s\"", s[count]);
+        } else {
+            bitmap |= 1u << version;
+        }
+    }
+
+    return bitmap;
+}
+
+const char *
+ofputil_version_to_string(enum ofp_version ofp_version)
+{
+    switch (ofp_version) {
+    case OFP10_VERSION:
+        return "OpenFlow10";
+    case OFP11_VERSION:
+        return "OpenFlow11";
+    case OFP12_VERSION:
+        return "OpenFlow12";
+    case OFP13_VERSION:
+        return "OpenFlow13";
+    default:
+        NOT_REACHED();
+    }
+}
+
 bool
 ofputil_packet_in_format_is_valid(enum nx_packet_in_format packet_in_format)
 {
@@ -891,6 +1041,16 @@ regs_fully_wildcarded(const struct flow_wildcards *wc)
     return true;
 }
 
+static bool
+tun_parms_fully_wildcarded(const struct flow_wildcards *wc)
+{
+    return (!wc->masks.tunnel.ip_src &&
+            !wc->masks.tunnel.ip_dst &&
+            !wc->masks.tunnel.ip_ttl &&
+            !wc->masks.tunnel.ip_tos &&
+            !wc->masks.tunnel.flags);
+}
+
 /* Returns a bit-mask of ofputil_protocols that can be used for sending 'match'
  * to a switch (e.g. to add or remove a flow).  Only NXM can handle tunnel IDs,
  * registers, or fixing the Ethernet multicast bit.  Otherwise, it's better to
@@ -900,111 +1060,302 @@ ofputil_usable_protocols(const struct match *match)
 {
     const struct flow_wildcards *wc = &match->wc;
 
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 17);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 18);
 
-    /* NXM and OF1.1+ supports bitwise matching on ethernet addresses. */
+    /* tunnel params other than tun_id can't be sent in a flow_mod */
+    if (!tun_parms_fully_wildcarded(wc)) {
+        return OFPUTIL_P_NONE;
+    }
+
+    /* skb_mark and skb_priority can't be sent in a flow_mod */
+    if (wc->masks.skb_mark || wc->masks.skb_priority) {
+        return OFPUTIL_P_NONE;
+    }
+
+    /* NXM, OXM, and OF1.1 support bitwise matching on ethernet addresses. */
     if (!eth_mask_is_exact(wc->masks.dl_src)
         && !eth_addr_is_zero(wc->masks.dl_src)) {
-        return OFPUTIL_P_NXM_ANY;
+        return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM
+            | OFPUTIL_P_OF13_OXM;
     }
     if (!eth_mask_is_exact(wc->masks.dl_dst)
         && !eth_addr_is_zero(wc->masks.dl_dst)) {
-        return OFPUTIL_P_NXM_ANY;
+        return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM
+            | OFPUTIL_P_OF13_OXM;
     }
 
-    /* NXM and OF1.1+ support matching metadata. */
+    /* NXM, OXM, and OF1.1+ support matching metadata. */
     if (wc->masks.metadata != htonll(0)) {
-        return OFPUTIL_P_NXM_ANY;
+        return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM
+            | OFPUTIL_P_OF13_OXM;
     }
 
-    /* Only NXM supports matching ARP hardware addresses. */
+    /* NXM and OXM support matching ARP hardware addresses. */
     if (!eth_addr_is_zero(wc->masks.arp_sha) ||
         !eth_addr_is_zero(wc->masks.arp_tha)) {
-        return OFPUTIL_P_NXM_ANY;
+        return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM
+            | OFPUTIL_P_OF13_OXM;
     }
 
-    /* Only NXM supports matching IPv6 traffic. */
+    /* NXM and OXM support matching IPv6 traffic. */
     if (match->flow.dl_type == htons(ETH_TYPE_IPV6)) {
-        return OFPUTIL_P_NXM_ANY;
+        return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM
+            | OFPUTIL_P_OF13_OXM;
     }
 
-    /* Only NXM supports matching registers. */
+    /* NXM and OXM support matching registers. */
     if (!regs_fully_wildcarded(wc)) {
-        return OFPUTIL_P_NXM_ANY;
+        return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM
+            | OFPUTIL_P_OF13_OXM;
     }
 
-    /* Only NXM supports matching tun_id. */
+    /* NXM and OXM support matching tun_id. */
     if (wc->masks.tunnel.tun_id != htonll(0)) {
-        return OFPUTIL_P_NXM_ANY;
+        return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM
+            | OFPUTIL_P_OF13_OXM;
     }
 
-    /* Only NXM supports matching fragments. */
+    /* NXM and OXM support matching fragments. */
     if (wc->masks.nw_frag) {
-        return OFPUTIL_P_NXM_ANY;
+        return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM
+            | OFPUTIL_P_OF13_OXM;
     }
 
-    /* Only NXM supports matching IPv6 flow label. */
+    /* NXM and OXM support matching IPv6 flow label. */
     if (wc->masks.ipv6_label) {
-        return OFPUTIL_P_NXM_ANY;
+        return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM
+            | OFPUTIL_P_OF13_OXM;
     }
 
-    /* Only NXM supports matching IP ECN bits. */
+    /* NXM and OXM support matching IP ECN bits. */
     if (wc->masks.nw_tos & IP_ECN_MASK) {
-        return OFPUTIL_P_NXM_ANY;
+        return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM
+            | OFPUTIL_P_OF13_OXM;
     }
 
-    /* Only NXM supports matching IP TTL/hop limit. */
+    /* NXM and OXM support matching IP TTL/hop limit. */
     if (wc->masks.nw_ttl) {
-        return OFPUTIL_P_NXM_ANY;
+        return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM
+            | OFPUTIL_P_OF13_OXM;
     }
 
-    /* Only NXM supports non-CIDR IPv4 address masks. */
+    /* NXM and OXM support non-CIDR IPv4 address masks. */
     if (!ip_is_cidr(wc->masks.nw_src) || !ip_is_cidr(wc->masks.nw_dst)) {
-        return OFPUTIL_P_NXM_ANY;
+        return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM
+            | OFPUTIL_P_OF13_OXM;
     }
 
-    /* Only NXM supports bitwise matching on transport port. */
+    /* NXM and OXM support bitwise matching on transport port. */
     if ((wc->masks.tp_src && wc->masks.tp_src != htons(UINT16_MAX)) ||
         (wc->masks.tp_dst && wc->masks.tp_dst != htons(UINT16_MAX))) {
-        return OFPUTIL_P_NXM_ANY;
+        return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM
+            | OFPUTIL_P_OF13_OXM;
     }
 
     /* Other formats can express this rule. */
     return OFPUTIL_P_ANY;
 }
 
+void
+ofputil_format_version(struct ds *msg, enum ofp_version version)
+{
+    ds_put_format(msg, "0x%02x", version);
+}
+
+void
+ofputil_format_version_name(struct ds *msg, enum ofp_version version)
+{
+    ds_put_cstr(msg, ofputil_version_to_string(version));
+}
+
+static void
+ofputil_format_version_bitmap__(struct ds *msg, uint32_t bitmap,
+                                void (*format_version)(struct ds *msg,
+                                                       enum ofp_version))
+{
+    while (bitmap) {
+        format_version(msg, raw_ctz(bitmap));
+        bitmap = zero_rightmost_1bit(bitmap);
+        if (bitmap) {
+            ds_put_cstr(msg, ", ");
+        }
+    }
+}
+
+void
+ofputil_format_version_bitmap(struct ds *msg, uint32_t bitmap)
+{
+    ofputil_format_version_bitmap__(msg, bitmap, ofputil_format_version);
+}
+
+void
+ofputil_format_version_bitmap_names(struct ds *msg, uint32_t bitmap)
+{
+    ofputil_format_version_bitmap__(msg, bitmap, ofputil_format_version_name);
+}
+
+static bool
+ofputil_decode_hello_bitmap(const struct ofp_hello_elem_header *oheh,
+                            uint32_t *allowed_versionsp)
+{
+    uint16_t bitmap_len = ntohs(oheh->length) - sizeof *oheh;
+    const ovs_be32 *bitmap = (const ovs_be32 *) (oheh + 1);
+    uint32_t allowed_versions;
+
+    if (!bitmap_len || bitmap_len % sizeof *bitmap) {
+        return false;
+    }
+
+    /* Only use the first 32-bit element of the bitmap as that is all the
+     * current implementation supports.  Subsequent elements are ignored which
+     * should have no effect on session negotiation until Open vSwtich supports
+     * wire-protocol versions greater than 31.
+     */
+    allowed_versions = ntohl(bitmap[0]);
+
+    if (allowed_versions & 1) {
+        /* There's no OpenFlow version 0. */
+        VLOG_WARN_RL(&bad_ofmsg_rl, "peer claims to support invalid OpenFlow "
+                     "version 0x00");
+        allowed_versions &= ~1u;
+    }
+
+    if (!allowed_versions) {
+        VLOG_WARN_RL(&bad_ofmsg_rl, "peer does not support any OpenFlow "
+                     "version (between 0x01 and 0x1f)");
+        return false;
+    }
+
+    *allowed_versionsp = allowed_versions;
+    return true;
+}
+
+static uint32_t
+version_bitmap_from_version(uint8_t ofp_version)
+{
+    return ((ofp_version < 32 ? 1u << ofp_version : 0) - 1) << 1;
+}
+
+/* Decodes OpenFlow OFPT_HELLO message 'oh', storing into '*allowed_versions'
+ * the set of OpenFlow versions for which 'oh' announces support.
+ *
+ * Because of how OpenFlow defines OFPT_HELLO messages, this function is always
+ * successful, and thus '*allowed_versions' is always initialized.  However, it
+ * returns false if 'oh' contains some data that could not be fully understood,
+ * true if 'oh' was completely parsed. */
+bool
+ofputil_decode_hello(const struct ofp_header *oh, uint32_t *allowed_versions)
+{
+    struct ofpbuf msg;
+    bool ok = true;
+
+    ofpbuf_use_const(&msg, oh, ntohs(oh->length));
+    ofpbuf_pull(&msg, sizeof *oh);
+
+    *allowed_versions = version_bitmap_from_version(oh->version);
+    while (msg.size) {
+        const struct ofp_hello_elem_header *oheh;
+        unsigned int len;
+
+        if (msg.size < sizeof *oheh) {
+            return false;
+        }
+
+        oheh = msg.data;
+        len = ntohs(oheh->length);
+        if (len < sizeof *oheh || !ofpbuf_try_pull(&msg, ROUND_UP(len, 8))) {
+            return false;
+        }
+
+        if (oheh->type != htons(OFPHET_VERSIONBITMAP)
+            || !ofputil_decode_hello_bitmap(oheh, allowed_versions)) {
+            ok = false;
+        }
+    }
+
+    return ok;
+}
+
+/* Returns true if 'allowed_versions' needs to be accompanied by a version
+ * bitmap to be correctly expressed in an OFPT_HELLO message. */
+static inline bool
+should_send_version_bitmap(uint32_t allowed_versions)
+{
+    return !is_pow2((allowed_versions >> 1) + 1);
+}
+
+/* Create an OFPT_HELLO message that expresses support for the OpenFlow
+ * versions in the 'allowed_versions' bitmaps and returns the message. */
+struct ofpbuf *
+ofputil_encode_hello(uint32_t allowed_versions)
+{
+    enum ofp_version ofp_version;
+    struct ofpbuf *msg;
+
+    ofp_version = leftmost_1bit_idx(allowed_versions);
+    msg = ofpraw_alloc(OFPRAW_OFPT_HELLO, ofp_version, 0);
+
+    if (should_send_version_bitmap(allowed_versions)) {
+        struct ofp_hello_elem_header *oheh;
+        uint16_t map_len;
+
+        map_len = sizeof allowed_versions;
+        oheh = ofpbuf_put_zeros(msg, ROUND_UP(map_len + sizeof *oheh, 8));
+        oheh->type = htons(OFPHET_VERSIONBITMAP);
+        oheh->length = htons(map_len + sizeof *oheh);
+        *(ovs_be32 *)(oheh + 1) = htonl(allowed_versions);
+
+        ofpmsg_update_length(msg);
+    }
+
+    return msg;
+}
+
 /* Returns an OpenFlow message that, sent on an OpenFlow connection whose
  * protocol is 'current', at least partly transitions the protocol to 'want'.
  * Stores in '*next' the protocol that will be in effect on the OpenFlow
  * connection if the switch processes the returned message correctly.  (If
  * '*next != want' then the caller will have to iterate.)
  *
- * If 'current == want', returns NULL and stores 'current' in '*next'. */
+ * If 'current == want', or if it is not possible to transition from 'current'
+ * to 'want' (because, for example, 'current' and 'want' use different OpenFlow
+ * protocol versions), returns NULL and stores 'current' in '*next'. */
 struct ofpbuf *
 ofputil_encode_set_protocol(enum ofputil_protocol current,
                             enum ofputil_protocol want,
                             enum ofputil_protocol *next)
 {
+    enum ofp_version cur_version, want_version;
     enum ofputil_protocol cur_base, want_base;
     bool cur_tid, want_tid;
 
+    cur_version = ofputil_protocol_to_ofp_version(current);
+    want_version = ofputil_protocol_to_ofp_version(want);
+    if (cur_version != want_version) {
+        *next = current;
+        return NULL;
+    }
+
     cur_base = ofputil_protocol_to_base(current);
     want_base = ofputil_protocol_to_base(want);
     if (cur_base != want_base) {
         *next = ofputil_protocol_set_base(current, want_base);
 
         switch (want_base) {
-        case OFPUTIL_P_NXM:
+        case OFPUTIL_P_OF10_NXM:
             return ofputil_encode_nx_set_flow_format(NXFF_NXM);
 
-        case OFPUTIL_P_OF10:
+        case OFPUTIL_P_OF10_STD:
             return ofputil_encode_nx_set_flow_format(NXFF_OPENFLOW10);
 
-        case OFPUTIL_P_OF12:
-            return ofputil_encode_nx_set_flow_format(NXFF_OPENFLOW12);
+        case OFPUTIL_P_OF12_OXM:
+        case OFPUTIL_P_OF13_OXM:
+            /* There are only one of each OpenFlow 1.2+ protocols and we already
+             * verified above that we're not trying to change versions. */
+            NOT_REACHED();
 
-        case OFPUTIL_P_OF10_TID:
-        case OFPUTIL_P_NXM_TID:
+        case OFPUTIL_P_OF10_STD_TID:
+        case OFPUTIL_P_OF10_NXM_TID:
             NOT_REACHED();
         }
     }
@@ -1046,13 +1397,10 @@ ofputil_nx_flow_format_to_protocol(enum nx_flow_format flow_format)
 {
     switch (flow_format) {
     case NXFF_OPENFLOW10:
-        return OFPUTIL_P_OF10;
+        return OFPUTIL_P_OF10_STD;
 
     case NXFF_NXM:
-        return OFPUTIL_P_NXM;
-
-    case NXFF_OPENFLOW12:
-        return OFPUTIL_P_OF12;
+        return OFPUTIL_P_OF10_NXM;
 
     default:
         return 0;
@@ -1076,8 +1424,6 @@ ofputil_nx_flow_format_to_string(enum nx_flow_format flow_format)
         return "openflow10";
     case NXFF_NXM:
         return "nxm";
-    case NXFF_OPENFLOW12:
-        return "openflow12";
     default:
         NOT_REACHED();
     }
@@ -1170,7 +1516,9 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
         if (error) {
             return error;
         }
-        if (ofm->out_group != htonl(OFPG_ANY)) {
+        if ((ofm->command == OFPFC_DELETE
+             || ofm->command == OFPFC_DELETE_STRICT)
+            && ofm->out_group != htonl(OFPG_ANY)) {
             return OFPERR_OFPFMFC_UNKNOWN;
         }
         fm->flags = ntohs(ofm->flags);
@@ -1244,6 +1592,18 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
             NOT_REACHED();
         }
 
+        if (fm->flags & OFPFF10_EMERG) {
+            /* We do not support the OpenFlow 1.0 emergency flow cache, which
+             * is not required in OpenFlow 1.0.1 and removed from OpenFlow 1.1.
+             *
+             * OpenFlow 1.0 specifies the error code to use when idle_timeout
+             * or hard_timeout is nonzero.  Otherwise, there is no good error
+             * code, so just state that the flow table is full. */
+            return (fm->hard_timeout || fm->idle_timeout
+                    ? OFPERR_OFPFMFC_BAD_EMERG_TIMEOUT
+                    : OFPERR_OFPFMFC_TABLE_FULL);
+        }
+
         if (protocol & OFPUTIL_P_TID) {
             fm->command = command & 0xff;
             fm->table_id = command >> 8;
@@ -1277,10 +1637,12 @@ ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm,
     struct ofpbuf *msg;
 
     switch (protocol) {
-    case OFPUTIL_P_OF12: {
+    case OFPUTIL_P_OF12_OXM:
+    case OFPUTIL_P_OF13_OXM: {
         struct ofp11_flow_mod *ofm;
 
-        msg = ofpraw_alloc(OFPRAW_OFPT11_FLOW_MOD, OFP12_VERSION,
+        msg = ofpraw_alloc(OFPRAW_OFPT11_FLOW_MOD, 
+                           ofputil_protocol_to_ofp_version(protocol),
                            NXM_TYPICAL_LEN + fm->ofpacts_len);
         ofm = ofpbuf_put_zeros(msg, sizeof *ofm);
         if (fm->command == OFPFC_ADD) {
@@ -1303,8 +1665,8 @@ ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm,
         break;
     }
 
-    case OFPUTIL_P_OF10:
-    case OFPUTIL_P_OF10_TID: {
+    case OFPUTIL_P_OF10_STD:
+    case OFPUTIL_P_OF10_STD_TID: {
         struct ofp10_flow_mod *ofm;
 
         msg = ofpraw_alloc(OFPRAW_OFPT10_FLOW_MOD, OFP10_VERSION,
@@ -1323,8 +1685,8 @@ ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm,
         break;
     }
 
-    case OFPUTIL_P_NXM:
-    case OFPUTIL_P_NXM_TID: {
+    case OFPUTIL_P_OF10_NXM:
+    case OFPUTIL_P_OF10_NXM_TID: {
         struct nx_flow_mod *nfm;
         int match_len;
 
@@ -1375,12 +1737,12 @@ ofputil_flow_mod_usable_protocols(const struct ofputil_flow_mod *fms,
             usable_protocols &= OFPUTIL_P_TID;
         }
 
-        /* Matching of the cookie is only supported through NXM. */
+        /* Matching of the cookie is only supported through NXM or OF1.1+. */
         if (fm->cookie_mask != htonll(0)) {
-            usable_protocols &= OFPUTIL_P_NXM_ANY;
+            usable_protocols &= OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM
+                | OFPUTIL_P_OF13_OXM;
         }
     }
-    assert(usable_protocols);
 
     return usable_protocols;
 }
@@ -1498,13 +1860,15 @@ ofputil_encode_flow_stats_request(const struct ofputil_flow_stats_request *fsr,
     enum ofpraw raw;
 
     switch (protocol) {
-    case OFPUTIL_P_OF12: {
+    case OFPUTIL_P_OF12_OXM:
+    case OFPUTIL_P_OF13_OXM: {
         struct ofp11_flow_stats_request *ofsr;
 
         raw = (fsr->aggregate
                ? OFPRAW_OFPST11_AGGREGATE_REQUEST
                : OFPRAW_OFPST11_FLOW_REQUEST);
-        msg = ofpraw_alloc(raw, OFP12_VERSION, NXM_TYPICAL_LEN);
+        msg = ofpraw_alloc(raw, ofputil_protocol_to_ofp_version(protocol),
+                          NXM_TYPICAL_LEN);
         ofsr = ofpbuf_put_zeros(msg, sizeof *ofsr);
         ofsr->table_id = fsr->table_id;
         ofsr->out_port = ofputil_port_to_ofp11(fsr->out_port);
@@ -1515,8 +1879,8 @@ ofputil_encode_flow_stats_request(const struct ofputil_flow_stats_request *fsr,
         break;
     }
 
-    case OFPUTIL_P_OF10:
-    case OFPUTIL_P_OF10_TID: {
+    case OFPUTIL_P_OF10_STD:
+    case OFPUTIL_P_OF10_STD_TID: {
         struct ofp10_flow_stats_request *ofsr;
 
         raw = (fsr->aggregate
@@ -1530,8 +1894,8 @@ ofputil_encode_flow_stats_request(const struct ofputil_flow_stats_request *fsr,
         break;
     }
 
-    case OFPUTIL_P_NXM:
-    case OFPUTIL_P_NXM_TID: {
+    case OFPUTIL_P_OF10_NXM:
+    case OFPUTIL_P_OF10_NXM_TID: {
         struct nx_flow_stats_request *nfsr;
         int match_len;
 
@@ -1569,7 +1933,8 @@ ofputil_flow_stats_request_usable_protocols(
 
     usable_protocols = ofputil_usable_protocols(&fsr->match);
     if (fsr->cookie_mask != htonll(0)) {
-        usable_protocols &= OFPUTIL_P_NXM_ANY;
+        usable_protocols &= OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM
+            | OFPUTIL_P_OF13_OXM;
     }
     return usable_protocols;
 }
@@ -1612,7 +1977,8 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs,
 
     if (!msg->size) {
         return EOF;
-    } else if (raw == OFPRAW_OFPST11_FLOW_REPLY) {
+    } else if (raw == OFPRAW_OFPST11_FLOW_REPLY
+               || raw == OFPRAW_OFPST13_FLOW_REPLY) {
         const struct ofp11_flow_stats *ofs;
         size_t length;
         uint16_t padded_match_len;
@@ -1648,6 +2014,7 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs,
         fs->duration_nsec = ntohl(ofs->duration_nsec);
         fs->idle_timeout = ntohs(ofs->idle_timeout);
         fs->hard_timeout = ntohs(ofs->hard_timeout);
+        fs->flags = (raw == OFPRAW_OFPST13_FLOW_REPLY) ? ntohs(ofs->flags) : 0;
         fs->idle_age = -1;
         fs->hard_age = -1;
         fs->cookie = ofs->cookie;
@@ -1687,6 +2054,7 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs,
         fs->hard_age = -1;
         fs->packet_count = ntohll(get_32aligned_be64(&ofs->packet_count));
         fs->byte_count = ntohll(get_32aligned_be64(&ofs->byte_count));
+        fs->flags = 0;
     } else if (raw == OFPRAW_NXST_FLOW_REPLY) {
         const struct nx_flow_stats *nfs;
         size_t match_len, actions_len, length;
@@ -1733,6 +2101,7 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs,
         }
         fs->packet_count = ntohll(nfs->packet_count);
         fs->byte_count = ntohll(nfs->byte_count);
+        fs->flags = 0;
     } else {
         NOT_REACHED();
     }
@@ -1765,7 +2134,7 @@ ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *fs,
     enum ofpraw raw;
 
     ofpraw_decode_partial(&raw, reply->data, reply->size);
-    if (raw == OFPRAW_OFPST11_FLOW_REPLY) {
+    if (raw == OFPRAW_OFPST11_FLOW_REPLY || raw == OFPRAW_OFPST13_FLOW_REPLY) {
         struct ofp11_flow_stats *ofs;
 
         ofpbuf_put_uninit(reply, sizeof *ofs);
@@ -1782,6 +2151,7 @@ ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *fs,
         ofs->priority = htons(fs->priority);
         ofs->idle_timeout = htons(fs->idle_timeout);
         ofs->hard_timeout = htons(fs->hard_timeout);
+        ofs->flags = (raw == OFPRAW_OFPST13_FLOW_REPLY) ? htons(fs->flags) : 0;
         memset(ofs->pad2, 0, sizeof ofs->pad2);
         ofs->cookie = fs->cookie;
         ofs->packet_count = htonll(unknown_to_zero(fs->packet_count));
@@ -1925,7 +2295,7 @@ ofputil_decode_flow_removed(struct ofputil_flow_removed *fr,
         fr->packet_count = ntohll(ofr->packet_count);
         fr->byte_count = ntohll(ofr->byte_count);
     } else if (raw == OFPRAW_OFPT10_FLOW_REMOVED) {
-        const struct ofp_flow_removed *ofr;
+        const struct ofp10_flow_removed *ofr;
 
         ofr = ofpbuf_pull(&b, sizeof *ofr);
 
@@ -1981,7 +2351,8 @@ ofputil_encode_flow_removed(const struct ofputil_flow_removed *fr,
     struct ofpbuf *msg;
 
     switch (protocol) {
-    case OFPUTIL_P_OF12: {
+    case OFPUTIL_P_OF12_OXM:
+    case OFPUTIL_P_OF13_OXM: {
         struct ofp12_flow_removed *ofr;
 
         msg = ofpraw_alloc_xid(OFPRAW_OFPT11_FLOW_REMOVED,
@@ -2002,9 +2373,9 @@ ofputil_encode_flow_removed(const struct ofputil_flow_removed *fr,
         break;
     }
 
-    case OFPUTIL_P_OF10:
-    case OFPUTIL_P_OF10_TID: {
-        struct ofp_flow_removed *ofr;
+    case OFPUTIL_P_OF10_STD:
+    case OFPUTIL_P_OF10_STD_TID: {
+        struct ofp10_flow_removed *ofr;
 
         msg = ofpraw_alloc_xid(OFPRAW_OFPT10_FLOW_REMOVED, OFP10_VERSION,
                                htonl(0), 0);
@@ -2021,8 +2392,8 @@ ofputil_encode_flow_removed(const struct ofputil_flow_removed *fr,
         break;
     }
 
-    case OFPUTIL_P_NXM:
-    case OFPUTIL_P_NXM_TID: {
+    case OFPUTIL_P_OF10_NXM:
+    case OFPUTIL_P_OF10_NXM_TID: {
         struct nx_flow_removed *nfr;
         int match_len;
 
@@ -2075,12 +2446,19 @@ ofputil_decode_packet_in(struct ofputil_packet_in *pin,
 
     ofpbuf_use_const(&b, oh, ntohs(oh->length));
     raw = ofpraw_pull_assert(&b);
-    if (raw == OFPRAW_OFPT12_PACKET_IN) {
-        const struct ofp12_packet_in *opi;
+    if (raw == OFPRAW_OFPT13_PACKET_IN || raw == OFPRAW_OFPT12_PACKET_IN) {
+        const struct ofp13_packet_in *opi;
         struct match match;
         int error;
+        size_t packet_in_size;
 
-        opi = ofpbuf_pull(&b, sizeof *opi);
+        if (raw == OFPRAW_OFPT12_PACKET_IN) {
+            packet_in_size = sizeof (struct ofp12_packet_in);
+        } else {
+            packet_in_size = sizeof (struct ofp13_packet_in);
+        }
+
+        opi = ofpbuf_pull(&b, packet_in_size);
         error = oxm_pull_match_loose(&b, &match);
         if (error) {
             return error;
@@ -2090,17 +2468,20 @@ ofputil_decode_packet_in(struct ofputil_packet_in *pin,
             return OFPERR_OFPBRC_BAD_LEN;
         }
 
-        pin->reason = opi->reason;
-        pin->table_id = opi->table_id;
+        pin->reason = opi->pi.reason;
+        pin->table_id = opi->pi.table_id;
+        pin->buffer_id = ntohl(opi->pi.buffer_id);
+        pin->total_len = ntohs(opi->pi.total_len);
 
-        pin->buffer_id = ntohl(opi->buffer_id);
-        pin->total_len = ntohs(opi->total_len);
+        if (raw == OFPRAW_OFPT13_PACKET_IN) {
+            pin->cookie = opi->cookie;
+        }
 
         ofputil_decode_packet_in_finish(pin, &match, &b);
     } else if (raw == OFPRAW_OFPT10_PACKET_IN) {
-        const struct ofp_packet_in *opi;
+        const struct ofp10_packet_in *opi;
 
-        opi = ofpbuf_pull(&b, offsetof(struct ofp_packet_in, data));
+        opi = ofpbuf_pull(&b, offsetof(struct ofp10_packet_in, data));
 
         pin->packet = opi->data;
         pin->packet_len = b.size;
@@ -2174,32 +2555,48 @@ ofputil_encode_packet_in(const struct ofputil_packet_in *pin,
     struct ofpbuf *packet;
 
     /* Add OFPT_PACKET_IN. */
-    if (protocol == OFPUTIL_P_OF12) {
-        struct ofp12_packet_in *opi;
+    if (protocol == OFPUTIL_P_OF13_OXM || protocol == OFPUTIL_P_OF12_OXM) {
+        struct ofp13_packet_in *opi;
         struct match match;
+        enum ofpraw packet_in_raw;
+        enum ofp_version packet_in_version;
+        size_t packet_in_size;
+
+        if (protocol == OFPUTIL_P_OF12_OXM) {
+            packet_in_raw = OFPRAW_OFPT12_PACKET_IN;
+            packet_in_version = OFP12_VERSION;
+            packet_in_size = sizeof (struct ofp12_packet_in);
+        } else {
+            packet_in_raw = OFPRAW_OFPT13_PACKET_IN;
+            packet_in_version = OFP13_VERSION;
+            packet_in_size = sizeof (struct ofp13_packet_in);
+        }
 
         ofputil_packet_in_to_match(pin, &match);
 
         /* The final argument is just an estimate of the space required. */
-        packet = ofpraw_alloc_xid(OFPRAW_OFPT12_PACKET_IN, OFP12_VERSION,
+        packet = ofpraw_alloc_xid(packet_in_raw, packet_in_version,
                                   htonl(0), (sizeof(struct flow_metadata) * 2
                                              + 2 + send_len));
-        ofpbuf_put_zeros(packet, sizeof *opi);
+        ofpbuf_put_zeros(packet, packet_in_size);
         oxm_put_match(packet, &match);
         ofpbuf_put_zeros(packet, 2);
         ofpbuf_put(packet, pin->packet, send_len);
 
         opi = packet->l3;
-        opi->buffer_id = htonl(pin->buffer_id);
-        opi->total_len = htons(pin->total_len);
-        opi->reason = pin->reason;
-        opi->table_id = pin->table_id;
-   } else if (packet_in_format == NXPIF_OPENFLOW10) {
-        struct ofp_packet_in *opi;
+        opi->pi.buffer_id = htonl(pin->buffer_id);
+        opi->pi.total_len = htons(pin->total_len);
+        opi->pi.reason = pin->reason;
+        opi->pi.table_id = pin->table_id;
+        if (protocol == OFPUTIL_P_OF13_OXM) {
+            opi->cookie = pin->cookie;
+        }
+    } else if (packet_in_format == NXPIF_OPENFLOW10) {
+        struct ofp10_packet_in *opi;
 
         packet = ofpraw_alloc_xid(OFPRAW_OFPT10_PACKET_IN, OFP10_VERSION,
                                   htonl(0), send_len);
-        opi = ofpbuf_put_zeros(packet, offsetof(struct ofp_packet_in, data));
+        opi = ofpbuf_put_zeros(packet, offsetof(struct ofp10_packet_in, data));
         opi->total_len = htons(pin->total_len);
         opi->in_port = htons(pin->fmd.in_port);
         opi->reason = pin->reason;
@@ -2308,7 +2705,7 @@ ofputil_decode_packet_out(struct ofputil_packet_out *po,
         }
     } else if (raw == OFPRAW_OFPT10_PACKET_OUT) {
         enum ofperr error;
-        const struct ofp_packet_out *opo = ofpbuf_pull(&b, sizeof *opo);
+        const struct ofp10_packet_out *opo = ofpbuf_pull(&b, sizeof *opo);
 
         po->buffer_id = ntohl(opo->buffer_id);
         po->in_port = ntohs(opo->in_port);
@@ -2420,8 +2817,8 @@ ofputil_decode_ofp10_phy_port(struct ofputil_phy_port *pp,
     pp->supported = netdev_port_features_from_ofp10(opp->supported);
     pp->peer = netdev_port_features_from_ofp10(opp->peer);
 
-    pp->curr_speed = netdev_features_to_bps(pp->curr) / 1000;
-    pp->max_speed = netdev_features_to_bps(pp->supported) / 1000;
+    pp->curr_speed = netdev_features_to_bps(pp->curr, 0) / 1000;
+    pp->max_speed = netdev_features_to_bps(pp->supported, 0) / 1000;
 
     return 0;
 }
@@ -2463,6 +2860,7 @@ ofputil_get_phy_port_size(enum ofp_version ofp_version)
         return sizeof(struct ofp10_phy_port);
     case OFP11_VERSION:
     case OFP12_VERSION:
+    case OFP13_VERSION:
         return sizeof(struct ofp11_port);
     default:
         NOT_REACHED();
@@ -2525,7 +2923,8 @@ ofputil_put_phy_port(enum ofp_version ofp_version,
     }
 
     case OFP11_VERSION:
-    case OFP12_VERSION: {
+    case OFP12_VERSION:
+    case OFP13_VERSION: {
         struct ofp11_port *op;
         if (b->size + sizeof *op <= UINT16_MAX) {
             op = ofpbuf_put_uninit(b, sizeof *op);
@@ -2554,7 +2953,8 @@ ofputil_append_port_desc_stats_reply(enum ofp_version ofp_version,
     }
 
     case OFP11_VERSION:
-    case OFP12_VERSION: {
+    case OFP12_VERSION:
+    case OFP13_VERSION: {
         struct ofp11_port *op;
 
         op = ofpmp_append(replies, sizeof *op);
@@ -2623,6 +3023,7 @@ ofputil_capabilities_mask(enum ofp_version ofp_version)
     case OFP11_VERSION:
         return OFPC_COMMON | OFPC_ARP_MATCH_IP;
     case OFP12_VERSION:
+    case OFP13_VERSION:
         return OFPC_COMMON | OFPC12_PORT_BLOCKED;
     default:
         /* Caller needs to check osf->header.version itself */
@@ -2650,6 +3051,7 @@ ofputil_decode_switch_features(const struct ofp_header *oh,
     features->datapath_id = ntohll(osf->datapath_id);
     features->n_buffers = ntohl(osf->n_buffers);
     features->n_tables = osf->n_tables;
+    features->auxiliary_id = 0;
 
     features->capabilities = ntohl(osf->capabilities) &
         ofputil_capabilities_mask(oh->version);
@@ -2663,11 +3065,15 @@ ofputil_decode_switch_features(const struct ofp_header *oh,
             features->capabilities |= OFPUTIL_C_STP;
         }
         features->actions = decode_action_bits(osf->actions, of10_action_bits);
-    } else if (raw == OFPRAW_OFPT11_FEATURES_REPLY) {
+    } else if (raw == OFPRAW_OFPT11_FEATURES_REPLY
+               || raw == OFPRAW_OFPT13_FEATURES_REPLY) {
         if (osf->capabilities & htonl(OFPC11_GROUP_STATS)) {
             features->capabilities |= OFPUTIL_C_GROUP_STATS;
         }
         features->actions = 0;
+        if (raw == OFPRAW_OFPT13_FEATURES_REPLY) {
+            features->auxiliary_id = osf->auxiliary_id;
+        }
     } else {
         return OFPERR_OFPBRC_BAD_VERSION;
     }
@@ -2744,6 +3150,9 @@ ofputil_encode_switch_features(const struct ofputil_switch_features *features,
     case OFP12_VERSION:
         raw = OFPRAW_OFPT11_FEATURES_REPLY;
         break;
+    case OFP13_VERSION:
+        raw = OFPRAW_OFPT13_FEATURES_REPLY;
+        break;
     default:
         NOT_REACHED();
     }
@@ -2763,6 +3172,9 @@ ofputil_encode_switch_features(const struct ofputil_switch_features *features,
         }
         osf->actions = encode_action_bits(features->actions, of10_action_bits);
         break;
+    case OFP13_VERSION:
+        osf->auxiliary_id = features->auxiliary_id;
+        /* fall through */
     case OFP11_VERSION:
     case OFP12_VERSION:
         if (features->capabilities & OFPUTIL_C_GROUP_STATS) {
@@ -2785,7 +3197,9 @@ ofputil_put_switch_features_port(const struct ofputil_phy_port *pp,
 {
     const struct ofp_header *oh = b->data;
 
-    ofputil_put_phy_port(oh->version, pp, b);
+    if (oh->version < OFP13_VERSION) {
+        ofputil_put_phy_port(oh->version, pp, b);
+    }
 }
 \f
 /* ofputil_port_status */
@@ -2836,6 +3250,7 @@ ofputil_encode_port_status(const struct ofputil_port_status *ps,
 
     case OFP11_VERSION:
     case OFP12_VERSION:
+    case OFP13_VERSION:
         raw = OFPRAW_OFPT11_PORT_STATUS;
         break;
 
@@ -2919,7 +3334,8 @@ ofputil_encode_port_mod(const struct ofputil_port_mod *pm,
     }
 
     case OFP11_VERSION:
-    case OFP12_VERSION: {
+    case OFP12_VERSION:
+    case OFP13_VERSION: {
         struct ofp11_port_mod *opm;
 
         b = ofpraw_alloc(OFPRAW_OFPT11_PORT_MOD, ofp_version, 0);
@@ -2939,6 +3355,104 @@ ofputil_encode_port_mod(const struct ofputil_port_mod *pm,
     return b;
 }
 \f
+/* ofputil_role_request */
+
+/* Decodes the OpenFlow "role request" or "role reply" message in '*oh' into
+ * an abstract form in '*rr'.  Returns 0 if successful, otherwise an
+ * OFPERR_* value. */
+enum ofperr
+ofputil_decode_role_message(const struct ofp_header *oh,
+                            struct ofputil_role_request *rr)
+{
+    const struct ofp12_role_request *orr = ofpmsg_body(oh);
+    uint32_t role = ntohl(orr->role);
+    struct ofpbuf b;
+    enum ofpraw raw;
+
+    memset(rr, 0, sizeof *rr);
+
+    ofpbuf_use_const(&b, oh, ntohs(oh->length));
+    raw = ofpraw_pull_assert(&b);
+
+    if (raw == OFPRAW_OFPT12_ROLE_REQUEST
+        || raw == OFPRAW_OFPT12_ROLE_REPLY) {
+
+        if (raw == OFPRAW_OFPT12_ROLE_REQUEST) {
+            if (role == OFPCR12_ROLE_NOCHANGE) {
+                rr->request_current_role_only = true;
+                return 0;
+            }
+            if (role == OFPCR12_ROLE_MASTER || role == OFPCR12_ROLE_SLAVE) {
+                rr->generation_id = ntohll(orr->generation_id);
+                rr->have_generation_id = true;
+            }
+        }
+
+        /* Map to enum nx_role */
+        role -= 1; /* OFPCR12_ROLE_MASTER -> NX_ROLE_MASTER etc. */
+    } else if (raw != OFPRAW_NXT_ROLE_REQUEST
+               && raw != OFPRAW_NXT_ROLE_REPLY) {
+        return OFPERR_OFPBRC_BAD_TYPE;
+    }
+
+    if (role != NX_ROLE_OTHER && role != NX_ROLE_MASTER
+        && role != NX_ROLE_SLAVE) {
+        return OFPERR_OFPRRFC_BAD_ROLE;
+    }
+
+    rr->role = role;
+    return 0;
+}
+
+/* Returns an encoded form of a role reply suitable for the "request" in a
+ * buffer owned by the caller. */
+struct ofpbuf *
+ofputil_encode_role_reply(const struct ofp_header *request,
+                          enum nx_role role)
+{
+    struct ofp12_role_request *reply;
+    struct ofpbuf *buf;
+    size_t reply_size;
+
+    struct ofpbuf b;
+    enum ofpraw raw;
+
+    ofpbuf_use_const(&b, request, ntohs(request->length));
+    raw = ofpraw_pull_assert(&b);
+    if (raw == OFPRAW_OFPT12_ROLE_REQUEST) {
+        reply_size = sizeof (struct ofp12_role_request);
+        raw = OFPRAW_OFPT12_ROLE_REPLY;
+    }
+    else if (raw == OFPRAW_NXT_ROLE_REQUEST) {
+        reply_size = sizeof (struct nx_role_request);
+        raw = OFPRAW_NXT_ROLE_REPLY;
+    } else {
+        NOT_REACHED();
+    }
+
+    buf = ofpraw_alloc_reply(raw, request, 0);
+    reply = ofpbuf_put_zeros(buf, reply_size);
+
+    if (raw == OFPRAW_OFPT12_ROLE_REPLY) {
+        /* Map to OpenFlow enum ofp12_controller_role */
+        role += 1; /* NX_ROLE_MASTER -> OFPCR12_ROLE_MASTER etc. */
+        /*
+         * OpenFlow specification does not specify use of generation_id field
+         * on reply messages.  Intuitively, it would seem a good idea to return
+         * the current value.  However, the current value is undefined
+         * initially, and there is no way to insert an undefined value in the
+         * message.  Therefore we leave the generation_id zeroed on reply
+         * messages.
+         *
+         * A request for clarification has been filed with the Open Networking
+         * Foundation as EXT-272.
+         */
+    }
+    reply->role = htonl(role);
+
+    return buf;
+}
+\f
 /* Table stats. */
 
 static void
@@ -2946,7 +3460,7 @@ ofputil_put_ofp10_table_stats(const struct ofp12_table_stats *in,
                               struct ofpbuf *buf)
 {
     struct wc_map {
-        enum ofp_flow_wildcards wc10;
+        enum ofp10_flow_wildcards wc10;
         enum oxm12_ofb_match_fields mf12;
     };
 
@@ -2968,9 +3482,9 @@ ofputil_put_ofp10_table_stats(const struct ofp12_table_stats *in,
     struct ofp10_table_stats *out;
     const struct wc_map *p;
 
-    out = ofpbuf_put_uninit(buf, sizeof *out);
+    out = ofpbuf_put_zeros(buf, sizeof *out);
     out->table_id = in->table_id;
-    strcpy(out->name, in->name);
+    ovs_strlcpy(out->name, in->name, sizeof out->name);
     out->wildcards = 0;
     for (p = wc_map; p < &wc_map[ARRAY_SIZE(wc_map)]; p++) {
         if (in->wildcards & htonll(1ULL << p->mf12)) {
@@ -3028,9 +3542,9 @@ ofputil_put_ofp11_table_stats(const struct ofp12_table_stats *in,
 {
     struct ofp11_table_stats *out;
 
-    out = ofpbuf_put_uninit(buf, sizeof *out);
+    out = ofpbuf_put_zeros(buf, sizeof *out);
     out->table_id = in->table_id;
-    strcpy(out->name, in->name);
+    ovs_strlcpy(out->name, in->name, sizeof out->name);
     out->wildcards = oxm12_to_ofp11_flow_match_fields(in->wildcards);
     out->match = oxm12_to_ofp11_flow_match_fields(in->match);
     out->instructions = in->instructions;
@@ -3043,6 +3557,22 @@ ofputil_put_ofp11_table_stats(const struct ofp12_table_stats *in,
     out->matched_count = in->matched_count;
 }
 
+static void
+ofputil_put_ofp13_table_stats(const struct ofp12_table_stats *in,
+                              struct ofpbuf *buf)
+{
+    struct ofp13_table_stats *out;
+
+    /* OF 1.3 splits table features off the ofp_table_stats,
+     * so there is not much here. */
+
+    out = ofpbuf_put_uninit(buf, sizeof *out);
+    out->table_id = in->table_id;
+    out->active_count = in->active_count;
+    out->lookup_count = in->lookup_count;
+    out->matched_count = in->matched_count;
+}
+
 struct ofpbuf *
 ofputil_encode_table_stats_reply(const struct ofp12_table_stats stats[], int n,
                                  const struct ofp_header *request)
@@ -3069,6 +3599,12 @@ ofputil_encode_table_stats_reply(const struct ofp12_table_stats stats[], int n,
         ofpbuf_put(reply, stats, n * sizeof *stats);
         break;
 
+    case OFP13_VERSION:
+        for (i = 0; i < n; i++) {
+            ofputil_put_ofp13_table_stats(&stats[i], reply);
+        }
+        break;
+
     default:
         NOT_REACHED();
     }
@@ -3252,7 +3788,7 @@ ofputil_decode_flow_update(struct ofputil_flow_update *update,
         VLOG_WARN_RL(&bad_ofmsg_rl,
                      "NXST_FLOW_MONITOR reply has bad event %"PRIu16,
                      ntohs(nfuh->event));
-        return OFPERR_OFPET_BAD_REQUEST;
+        return OFPERR_NXBRC_FM_BAD_EVENT;
     }
 
 bad_len:
@@ -3349,7 +3885,7 @@ ofputil_encode_packet_out(const struct ofputil_packet_out *po,
 
     switch (ofp_version) {
     case OFP10_VERSION: {
-        struct ofp_packet_out *opo;
+        struct ofp10_packet_out *opo;
         size_t actions_ofs;
 
         msg = ofpraw_alloc(OFPRAW_OFPT10_PACKET_OUT, OFP10_VERSION, size);
@@ -3365,7 +3901,8 @@ ofputil_encode_packet_out(const struct ofputil_packet_out *po,
     }
 
     case OFP11_VERSION:
-    case OFP12_VERSION: {
+    case OFP12_VERSION:
+    case OFP13_VERSION: {
         struct ofp11_packet_out *opo;
         size_t len;
 
@@ -3423,6 +3960,7 @@ ofputil_encode_barrier_request(enum ofp_version ofp_version)
     enum ofpraw type;
 
     switch (ofp_version) {
+    case OFP13_VERSION:
     case OFP12_VERSION:
     case OFP11_VERSION:
         type = OFPRAW_OFPT11_BARRIER_REQUEST;
@@ -3540,6 +4078,11 @@ ofputil_check_output_port(uint16_t port, int max_ports)
         OFPUTIL_NAMED_PORT(ALL)                 \
         OFPUTIL_NAMED_PORT(CONTROLLER)          \
         OFPUTIL_NAMED_PORT(LOCAL)               \
+        OFPUTIL_NAMED_PORT(ANY)
+
+/* For backwards compatibility, so that "none" is recognized as OFPP_ANY */
+#define OFPUTIL_NAMED_PORTS_WITH_NONE           \
+        OFPUTIL_NAMED_PORTS                     \
         OFPUTIL_NAMED_PORT(NONE)
 
 /* Stores the port number represented by 's' into '*portp'.  's' may be an
@@ -3585,7 +4128,7 @@ ofputil_port_from_string(const char *s, uint16_t *portp)
             return true;
         } else if (port32 < OFPP11_MAX) {
             VLOG_WARN("port %u is outside the supported range 0 through "
-                      "%"PRIx16"or 0x%x through 0x%"PRIx32, port32,
+                      "%"PRIx16" or 0x%x through 0x%"PRIx32, port32,
                       UINT16_MAX, (unsigned int) OFPP11_MAX, UINT32_MAX);
             return false;
         } else {
@@ -3599,7 +4142,7 @@ ofputil_port_from_string(const char *s, uint16_t *portp)
         };
         static const struct pair pairs[] = {
 #define OFPUTIL_NAMED_PORT(NAME) {#NAME, OFPP_##NAME},
-            OFPUTIL_NAMED_PORTS
+            OFPUTIL_NAMED_PORTS_WITH_NONE
 #undef OFPUTIL_NAMED_PORT
         };
         const struct pair *p;
@@ -3649,7 +4192,8 @@ ofputil_pull_phy_port(enum ofp_version ofp_version, struct ofpbuf *b,
         return opp ? ofputil_decode_ofp10_phy_port(pp, opp) : EOF;
     }
     case OFP11_VERSION:
-    case OFP12_VERSION: {
+    case OFP12_VERSION:
+    case OFP13_VERSION: {
         const struct ofp11_port *op = ofpbuf_try_pull(b, sizeof *op);
         return op ? ofputil_decode_ofp11_port(pp, op) : EOF;
     }
@@ -3789,7 +4333,8 @@ ofputil_normalize_match__(struct match *match, bool may_log)
                 may_match |= MAY_ND_TARGET | MAY_ARP_THA;
             }
         }
-    } else if (match->flow.dl_type == htons(ETH_TYPE_ARP)) {
+    } else if (match->flow.dl_type == htons(ETH_TYPE_ARP) ||
+               match->flow.dl_type == htons(ETH_TYPE_RARP)) {
         may_match = MAY_NW_PROTO | MAY_NW_ADDR | MAY_ARP_SHA | MAY_ARP_THA;
     } else {
         may_match = 0;
@@ -3947,7 +4492,7 @@ ofputil_parse_key_value(char **stringp, char **keyp, char **valuep)
 }
 
 /* Encode a dump ports request for 'port', the encoded message
- * will be fore Open Flow version 'ofp_version'. Returns message
+ * will be for Open Flow version 'ofp_version'. Returns message
  * as a struct ofpbuf. Returns encoded message on success, NULL on error */
 struct ofpbuf *
 ofputil_encode_dump_ports_request(enum ofp_version ofp_version, int16_t port)
@@ -3963,7 +4508,8 @@ ofputil_encode_dump_ports_request(enum ofp_version ofp_version, int16_t port)
         break;
     }
     case OFP11_VERSION:
-    case OFP12_VERSION: {
+    case OFP12_VERSION:
+    case OFP13_VERSION: {
         struct ofp11_port_stats_request *req;
         request = ofpraw_alloc(OFPRAW_OFPST11_PORT_REQUEST, ofp_version, 0);
         req = ofpbuf_put_zeros(request, sizeof *req);
@@ -4017,6 +4563,19 @@ ofputil_port_stats_to_ofp11(const struct ofputil_port_stats *ops,
     ps11->collisions = htonll(ops->stats.collisions);
 }
 
+static void
+ofputil_port_stats_to_ofp13(const struct ofputil_port_stats *ops,
+                            struct ofp13_port_stats *ps13)
+{
+    ofputil_port_stats_to_ofp11(ops, &ps13->ps);
+
+    /* OF 1.3 adds duration fields */
+    /* FIXME: Need to implement port alive duration (sec + nsec) */
+    ps13->duration_sec = htonl(~0);
+    ps13->duration_nsec = htonl(~0);
+}
+
+
 /* Encode a ports stat for 'ops' and append it to 'replies'. */
 void
 ofputil_append_port_stat(struct list *replies,
@@ -4026,6 +4585,11 @@ ofputil_append_port_stat(struct list *replies,
     struct ofp_header *oh = msg->data;
 
     switch ((enum ofp_version)oh->version) {
+    case OFP13_VERSION: {
+        struct ofp13_port_stats *reply = ofpmp_append(replies, sizeof *reply);
+        ofputil_port_stats_to_ofp13(ops, reply);
+        break;
+    }
     case OFP12_VERSION:
     case OFP11_VERSION: {
         struct ofp11_port_stats *reply = ofpmp_append(replies, sizeof *reply);
@@ -4096,6 +4660,21 @@ ofputil_port_stats_from_ofp11(struct ofputil_port_stats *ops,
     return 0;
 }
 
+static enum ofperr
+ofputil_port_stats_from_ofp13(struct ofputil_port_stats *ops,
+                              const struct ofp13_port_stats *ps13)
+{
+    enum ofperr error =
+        ofputil_port_stats_from_ofp11(ops, &ps13->ps);
+    if (!error) {
+        /* FIXME: Get ps13->duration_sec and ps13->duration_nsec,
+         * Add to netdev_stats? */
+    }
+
+    return error;
+}
+
+
 /* Returns the number of port stats elements in OFPTYPE_PORT_STATS_REPLY
  * message 'oh'. */
 size_t
@@ -4136,14 +4715,20 @@ ofputil_decode_port_stats(struct ofputil_port_stats *ps, struct ofpbuf *msg)
 
     if (!msg->size) {
         return EOF;
+    } else if (raw == OFPRAW_OFPST13_PORT_REPLY) {
+        const struct ofp13_port_stats *ps13;
+
+        ps13 = ofpbuf_try_pull(msg, sizeof *ps13);
+        if (!ps13) {
+            goto bad_len;
+        }
+        return ofputil_port_stats_from_ofp13(ps, ps13);
     } else if (raw == OFPRAW_OFPST11_PORT_REPLY) {
         const struct ofp11_port_stats *ps11;
 
         ps11 = ofpbuf_try_pull(msg, sizeof *ps11);
         if (!ps11) {
-            VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_PORT reply has %zu leftover "
-                         "bytes at end", msg->size);
-            return OFPERR_OFPBRC_BAD_LEN;
+            goto bad_len;
         }
         return ofputil_port_stats_from_ofp11(ps, ps11);
     } else if (raw == OFPRAW_OFPST10_PORT_REPLY) {
@@ -4151,15 +4736,17 @@ ofputil_decode_port_stats(struct ofputil_port_stats *ps, struct ofpbuf *msg)
 
         ps10 = ofpbuf_try_pull(msg, sizeof *ps10);
         if (!ps10) {
-            VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_PORT reply has %zu leftover "
-                         "bytes at end", msg->size);
-            return OFPERR_OFPBRC_BAD_LEN;
+            goto bad_len;
         }
         return ofputil_port_stats_from_ofp10(ps, ps10);
     } else {
         NOT_REACHED();
     }
 
+ bad_len:
+    VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_PORT reply has %zu leftover "
+                 "bytes at end", msg->size);
+    return OFPERR_OFPBRC_BAD_LEN;
 }
 
 /* Parse a port status request message into a 16 bit OpenFlow 1.0
@@ -4170,6 +4757,7 @@ ofputil_decode_port_stats_request(const struct ofp_header *request,
                                   uint16_t *ofp10_port)
 {
     switch ((enum ofp_version)request->version) {
+    case OFP13_VERSION:
     case OFP12_VERSION:
     case OFP11_VERSION: {
         const struct ofp11_port_stats_request *psr11 = ofpmsg_body(request);
@@ -4194,6 +4782,7 @@ ofputil_decode_queue_stats_request(const struct ofp_header *request,
                                    struct ofputil_queue_stats_request *oqsr)
 {
     switch ((enum ofp_version)request->version) {
+    case OFP13_VERSION:
     case OFP12_VERSION:
     case OFP11_VERSION: {
         const struct ofp11_queue_stats_request *qsr11 = ofpmsg_body(request);
@@ -4202,9 +4791,13 @@ ofputil_decode_queue_stats_request(const struct ofp_header *request,
     }
 
     case OFP10_VERSION: {
-        const struct ofp10_queue_stats_request *qsr11 = ofpmsg_body(request);
-        oqsr->queue_id = ntohl(qsr11->queue_id);
-        oqsr->port_no = ntohs(qsr11->port_no);
+        const struct ofp10_queue_stats_request *qsr10 = ofpmsg_body(request);
+        oqsr->queue_id = ntohl(qsr10->queue_id);
+        oqsr->port_no = ntohs(qsr10->port_no);
+        /* OF 1.0 uses OFPP_ALL for OFPP_ANY */
+        if (oqsr->port_no == OFPP_ALL) {
+            oqsr->port_no = OFPP_ANY;
+        }
         return 0;
     }
 
@@ -4224,7 +4817,8 @@ ofputil_encode_queue_stats_request(enum ofp_version ofp_version,
 
     switch (ofp_version) {
     case OFP11_VERSION:
-    case OFP12_VERSION: {
+    case OFP12_VERSION:
+    case OFP13_VERSION: {
         struct ofp11_queue_stats_request *req;
         request = ofpraw_alloc(OFPRAW_OFPST11_QUEUE_REQUEST, ofp_version, 0);
         req = ofpbuf_put_zeros(request, sizeof *req);
@@ -4236,7 +4830,9 @@ ofputil_encode_queue_stats_request(enum ofp_version ofp_version,
         struct ofp10_queue_stats_request *req;
         request = ofpraw_alloc(OFPRAW_OFPST10_QUEUE_REQUEST, ofp_version, 0);
         req = ofpbuf_put_zeros(request, sizeof *req);
-        req->port_no = htons(oqsr->port_no);
+        /* OpenFlow 1.0 needs OFPP_ALL instead of OFPP_ANY */
+        req->port_no = htons(oqsr->port_no == OFPP_ANY
+                             ? OFPP_ALL : oqsr->port_no);
         req->queue_id = htonl(oqsr->queue_id);
         break;
     }
@@ -4294,6 +4890,20 @@ ofputil_queue_stats_from_ofp11(struct ofputil_queue_stats *oqs,
     return 0;
 }
 
+static enum ofperr
+ofputil_queue_stats_from_ofp13(struct ofputil_queue_stats *oqs,
+                               const struct ofp13_queue_stats *qs13)
+{
+    enum ofperr error
+        = ofputil_queue_stats_from_ofp11(oqs, &qs13->qs);
+    if (!error) {
+        /* FIXME: Get qs13->duration_sec and qs13->duration_nsec,
+         * Add to netdev_queue_stats? */
+    }
+
+    return error;
+}
+
 /* Converts an OFPST_QUEUE_STATS reply in 'msg' into an abstract
  * ofputil_queue_stats in 'qs'.
  *
@@ -4319,14 +4929,20 @@ ofputil_decode_queue_stats(struct ofputil_queue_stats *qs, struct ofpbuf *msg)
 
     if (!msg->size) {
         return EOF;
+    } else if (raw == OFPRAW_OFPST13_QUEUE_REPLY) {
+        const struct ofp13_queue_stats *qs13;
+
+        qs13 = ofpbuf_try_pull(msg, sizeof *qs13);
+        if (!qs13) {
+            goto bad_len;
+        }
+        return ofputil_queue_stats_from_ofp13(qs, qs13);
     } else if (raw == OFPRAW_OFPST11_QUEUE_REPLY) {
         const struct ofp11_queue_stats *qs11;
 
         qs11 = ofpbuf_try_pull(msg, sizeof *qs11);
         if (!qs11) {
-            VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_QUEUE reply has %zu leftover "
-                         "bytes at end", msg->size);
-            return OFPERR_OFPBRC_BAD_LEN;
+            goto bad_len;
         }
         return ofputil_queue_stats_from_ofp11(qs, qs11);
     } else if (raw == OFPRAW_OFPST10_QUEUE_REPLY) {
@@ -4334,14 +4950,17 @@ ofputil_decode_queue_stats(struct ofputil_queue_stats *qs, struct ofpbuf *msg)
 
         qs10 = ofpbuf_try_pull(msg, sizeof *qs10);
         if (!qs10) {
-            VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_QUEUE reply has %zu leftover "
-                         "bytes at end", msg->size);
-            return OFPERR_OFPBRC_BAD_LEN;
+            goto bad_len;
         }
         return ofputil_queue_stats_from_ofp10(qs, qs10);
     } else {
         NOT_REACHED();
     }
+
+ bad_len:
+    VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_QUEUE reply has %zu leftover "
+                 "bytes at end", msg->size);
+    return OFPERR_OFPBRC_BAD_LEN;
 }
 
 static void
@@ -4367,6 +4986,17 @@ ofputil_queue_stats_to_ofp11(const struct ofputil_queue_stats *oqs,
     qs11->tx_errors = htonll(oqs->stats.tx_errors);
 }
 
+static void
+ofputil_queue_stats_to_ofp13(const struct ofputil_queue_stats *oqs,
+                             struct ofp13_queue_stats *qs13)
+{
+    ofputil_queue_stats_to_ofp11(oqs, &qs13->qs);
+    /* OF 1.3 adds duration fields */
+    /* FIXME: Need to implement queue alive duration (sec + nsec) */
+    qs13->duration_sec = htonl(~0);
+    qs13->duration_nsec = htonl(~0);
+}
+
 /* Encode a queue stat for 'oqs' and append it to 'replies'. */
 void
 ofputil_append_queue_stat(struct list *replies,
@@ -4376,15 +5006,21 @@ ofputil_append_queue_stat(struct list *replies,
     struct ofp_header *oh = msg->data;
 
     switch ((enum ofp_version)oh->version) {
+    case OFP13_VERSION: {
+        struct ofp13_queue_stats *reply = ofpmp_append(replies, sizeof *reply);
+        ofputil_queue_stats_to_ofp13(oqs, reply);
+        break;
+    }
+
     case OFP12_VERSION:
     case OFP11_VERSION: {
-        struct ofp11_queue_stats *reply = ofpmp_append(replies, sizeof *reply);;
+        struct ofp11_queue_stats *reply = ofpmp_append(replies, sizeof *reply);
         ofputil_queue_stats_to_ofp11(oqs, reply);
         break;
     }
 
     case OFP10_VERSION: {
-        struct ofp10_queue_stats *reply = ofpmp_append(replies, sizeof *reply);;
+        struct ofp10_queue_stats *reply = ofpmp_append(replies, sizeof *reply);
         ofputil_queue_stats_to_ofp10(oqs, reply);
         break;
     }
index 39575ba..6d08d8a 100644 (file)
@@ -14,7 +14,7 @@ OFPAT10_ACTION(OFPAT10_SET_NW_DST,   ofp_action_nw_addr,  "mod_nw_dst")
 OFPAT10_ACTION(OFPAT10_SET_NW_TOS,   ofp_action_nw_tos,   "mod_nw_tos")
 OFPAT10_ACTION(OFPAT10_SET_TP_SRC,   ofp_action_tp_port,  "mod_tp_src")
 OFPAT10_ACTION(OFPAT10_SET_TP_DST,   ofp_action_tp_port,  "mod_tp_dst")
-OFPAT10_ACTION(OFPAT10_ENQUEUE,      ofp_action_enqueue,  "enqueue")
+OFPAT10_ACTION(OFPAT10_ENQUEUE,      ofp10_action_enqueue,  "enqueue")
 
 #ifndef OFPAT11_ACTION
 #define OFPAT11_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME)
@@ -32,7 +32,7 @@ OFPAT11_ACTION(OFPAT11_SET_TP_SRC,   ofp_action_tp_port,  0, "mod_tp_src")
 OFPAT11_ACTION(OFPAT11_SET_TP_DST,   ofp_action_tp_port,  0, "mod_tp_dst")
 OFPAT11_ACTION(OFPAT11_PUSH_VLAN,    ofp11_action_push,   0, "push_vlan")
 OFPAT11_ACTION(OFPAT11_POP_VLAN,     ofp_action_header,   0, "pop_vlan")
-//OFPAT11_ACTION(OFPAT11_SET_QUEUE,    ofp11_action_set_queue, 0, "set_queue")
+OFPAT11_ACTION(OFPAT11_SET_QUEUE,    ofp11_action_set_queue, 0, "set_queue")
 //OFPAT11_ACTION(OFPAT11_SET_NW_TTL,   ofp11_action_nw_ttl, 0, "set_nw_ttl")
 OFPAT11_ACTION(OFPAT11_DEC_NW_TTL,   ofp_action_header,   0, NULL)
 OFPAT11_ACTION(OFPAT12_SET_FIELD,    ofp12_action_set_field, 1, "set_field")
index b6268da..f8c4260 100644 (file)
@@ -45,12 +45,15 @@ ovs_be32 ofputil_wcbits_to_netmask(int wcbits);
 int ofputil_netmask_to_wcbits(ovs_be32 netmask);
 
 /* Protocols.
+ *
+ * A "protocol" is an OpenFlow version plus, for some OpenFlow versions,
+ * a bit extra about the flow match format in use.
  *
  * These are arranged from most portable to least portable, or alternatively
- * from least powerful to most powerful.  Formats earlier on the list are more
- * likely to be understood for the purpose of making requests, but formats
- * later on the list are more likely to accurately describe a flow within a
- * switch.
+ * from least powerful to most powerful.  Protocols earlier on the list are
+ * more likely to be understood for the purpose of making requests, but
+ * protocol later on the list are more likely to accurately describe a flow
+ * within a switch.
  *
  * On any given OpenFlow connection, a single protocol is in effect at any
  * given time.  These values use separate bits only because that makes it easy
@@ -58,33 +61,49 @@ int ofputil_netmask_to_wcbits(ovs_be32 netmask);
  * to implement set union and intersection.
  */
 enum ofputil_protocol {
-    /* OpenFlow 1.0-based protocols. */
-    OFPUTIL_P_OF10     = 1 << 0, /* OpenFlow 1.0 flow format. */
-    OFPUTIL_P_OF10_TID = 1 << 1, /* OF1.0 + flow_mod_table_id extension. */
-#define OFPUTIL_P_OF10_ANY (OFPUTIL_P_OF10 | OFPUTIL_P_OF10_TID)
-
-    /* OpenFlow 1.0 with NXM-based flow formats. */
-    OFPUTIL_P_NXM      = 1 << 2, /* Nicira extended match. */
-    OFPUTIL_P_NXM_TID  = 1 << 3, /* NXM + flow_mod_table_id extension. */
-#define OFPUTIL_P_NXM_ANY (OFPUTIL_P_NXM | OFPUTIL_P_NXM_TID)
-
-    /* OpenFlow 1.2 */
-    OFPUTIL_P_OF12      = 1 << 4, /* OpenFlow 1.2 flow format. */
+    /* OpenFlow 1.0 protocols.
+     *
+     * The "STD" protocols use the standard OpenFlow 1.0 flow format.
+     * The "NXM" protocols use the Nicira Extensible Match (NXM) flow format.
+     *
+     * The protocols with "TID" mean that the nx_flow_mod_table_id Nicira
+     * extension has been enabled.  The other protocols have it disabled.
+     */
+#define OFPUTIL_P_NONE 0
+    OFPUTIL_P_OF10_STD     = 1 << 0,
+    OFPUTIL_P_OF10_STD_TID = 1 << 1,
+    OFPUTIL_P_OF10_NXM     = 1 << 2,
+    OFPUTIL_P_OF10_NXM_TID = 1 << 3,
+#define OFPUTIL_P_OF10_STD_ANY (OFPUTIL_P_OF10_STD | OFPUTIL_P_OF10_STD_TID)
+#define OFPUTIL_P_OF10_NXM_ANY (OFPUTIL_P_OF10_NXM | OFPUTIL_P_OF10_NXM_TID)
+
+    /* OpenFlow 1.2+ protocols (only one variant each).
+     *
+     * These use the standard OpenFlow Extensible Match (OXM) flow format.
+     *
+     * OpenFlow 1.2+ always operates with an equivalent of the
+     * nx_flow_mod_table_id Nicira extension enabled, so there is no "TID"
+     * variant. */
+    OFPUTIL_P_OF12_OXM      = 1 << 4,
+    OFPUTIL_P_OF13_OXM      = 1 << 5,
+#define OFPUTIL_P_ANY_OXM (OFPUTIL_P_OF12_OXM | OFPUTIL_P_OF13_OXM)
 
     /* All protocols. */
-#define OFPUTIL_P_ANY (OFPUTIL_P_OF10_ANY | OFPUTIL_P_NXM_ANY)
+#define OFPUTIL_P_ANY ((1 << 6) - 1)
 
     /* Protocols in which a specific table may be specified in flow_mods. */
-#define OFPUTIL_P_TID (OFPUTIL_P_OF10_TID | OFPUTIL_P_NXM_TID)
+#define OFPUTIL_P_TID (OFPUTIL_P_OF10_STD_TID | \
+                       OFPUTIL_P_OF10_NXM_TID | \
+                       OFPUTIL_P_ANY_OXM)
 };
 
 /* Protocols to use for flow dumps, from most to least preferred. */
 extern enum ofputil_protocol ofputil_flow_dump_protocols[];
 extern size_t ofputil_n_flow_dump_protocols;
 
-enum ofputil_protocol
-ofputil_protocol_from_ofp_version(enum ofp_version version);
-enum ofp_version  ofputil_protocol_to_ofp_version(enum ofputil_protocol);
+enum ofputil_protocol ofputil_protocol_from_ofp_version(enum ofp_version);
+enum ofputil_protocol ofputil_protocols_from_ofp_version(enum ofp_version);
+enum ofp_version ofputil_protocol_to_ofp_version(enum ofputil_protocol);
 
 bool ofputil_protocol_is_valid(enum ofputil_protocol);
 enum ofputil_protocol ofputil_protocol_set_tid(enum ofputil_protocol,
@@ -98,6 +117,40 @@ char *ofputil_protocols_to_string(enum ofputil_protocol);
 enum ofputil_protocol ofputil_protocols_from_string(const char *);
 enum ofputil_protocol ofputil_usable_protocols(const struct match *);
 
+void ofputil_format_version(struct ds *, enum ofp_version);
+void ofputil_format_version_name(struct ds *, enum ofp_version);
+
+/* A bitmap of version numbers
+ *
+ * Bit offsets correspond to ofp_version numbers which in turn correspond to
+ * wire-protocol numbers for Open Flow versions..  E.g. (1u << OFP11_VERSION)
+ * is the mask for Open Flow 1.1.  If the bit for a version is set then it is
+ * allowed, otherwise it is disallowed. */
+
+void ofputil_format_version_bitmap(struct ds *msg, uint32_t bitmap);
+void ofputil_format_version_bitmap_names(struct ds *msg, uint32_t bitmap);
+
+uint32_t ofputil_protocols_to_version_bitmap(enum ofputil_protocol);
+enum ofputil_protocol ofputil_protocols_from_version_bitmap(uint32_t bitmap);
+
+/* Bitmap of OpenFlow versions that Open vSwitch supports. */
+#define OFPUTIL_SUPPORTED_VERSIONS \
+    ((1u << OFP10_VERSION) | (1u << OFP12_VERSION) | (1u << OFP13_VERSION))
+
+/* Bitmap of OpenFlow versions to enable by default (a subset of
+ * OFPUTIL_SUPPORTED_VERSIONS). */
+#define OFPUTIL_DEFAULT_VERSIONS (1u << OFP10_VERSION)
+
+enum ofputil_protocol ofputil_protocols_from_string(const char *s);
+
+const char *ofputil_version_to_string(enum ofp_version ofp_version);
+uint32_t ofputil_versions_from_string(const char *s);
+uint32_t ofputil_versions_from_strings(char ** const s, size_t count);
+
+bool ofputil_decode_hello(const struct ofp_header *,
+                          uint32_t *allowed_versions);
+struct ofpbuf *ofputil_encode_hello(uint32_t version_bitmap);
+
 struct ofpbuf *ofputil_encode_set_protocol(enum ofputil_protocol current,
                                            enum ofputil_protocol want,
                                            enum ofputil_protocol *next);
@@ -215,6 +268,7 @@ struct ofputil_flow_stats {
     uint64_t byte_count;        /* Byte count, UINT64_MAX if unknown. */
     struct ofpact *ofpacts;
     size_t ofpacts_len;
+    uint16_t flags;             /* Added for OF 1.3 */
 };
 
 int ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *,
@@ -351,7 +405,7 @@ struct ofputil_phy_port {
 };
 
 enum ofputil_capabilities {
-    /* OpenFlow 1.0, 1.1 and 1.2 share these values for these capabilities. */
+    /* OpenFlow 1.0, 1.1, 1.2, and 1.3 share these capability values. */
     OFPUTIL_C_FLOW_STATS     = 1 << 0,  /* Flow statistics. */
     OFPUTIL_C_TABLE_STATS    = 1 << 1,  /* Table statistics. */
     OFPUTIL_C_PORT_STATS     = 1 << 2,  /* Port statistics. */
@@ -364,10 +418,10 @@ enum ofputil_capabilities {
     /* OpenFlow 1.0 only. */
     OFPUTIL_C_STP            = 1 << 3,  /* 802.1d spanning tree. */
 
-    /* OpenFlow 1.1 and 1.2 share this capability. */
+    /* OpenFlow 1.1, 1.2, and 1.3 share this capability. */
     OFPUTIL_C_GROUP_STATS    = 1 << 4,  /* Group statistics. */
 
-    /* OpenFlow 1.2 only */
+    /* OpenFlow 1.2 and 1.3 share this capability */
     OFPUTIL_C_PORT_BLOCKED   = 1 << 8,  /* Switch will block looping ports */
 };
 
@@ -407,6 +461,7 @@ struct ofputil_switch_features {
     uint64_t datapath_id;       /* Datapath unique ID. */
     uint32_t n_buffers;         /* Max packets buffered at once. */
     uint8_t n_tables;           /* Number of tables supported by datapath. */
+    uint8_t auxiliary_id;       /* Identify auxiliary connections */
     enum ofputil_capabilities capabilities;
     enum ofputil_action_bitmap actions;
 };
@@ -452,6 +507,19 @@ enum ofperr ofputil_decode_port_mod(const struct ofp_header *,
 struct ofpbuf *ofputil_encode_port_mod(const struct ofputil_port_mod *,
                                        enum ofputil_protocol);
 
+/* Abstract ofp_role_request and reply. */
+struct ofputil_role_request {
+    bool request_current_role_only; /* no role change */
+    bool have_generation_id;
+    enum nx_role role;
+    uint64_t generation_id;
+};
+
+enum ofperr ofputil_decode_role_message(const struct ofp_header *,
+                                        struct ofputil_role_request *);
+struct ofpbuf *ofputil_encode_role_reply(const struct ofp_header *,
+                                         enum nx_role current_role);
+
 /* Abstract table stats.
  *
  * For now we use ofp12_table_stats as a superset of the other protocol
@@ -638,7 +706,7 @@ enum ofperr ofputil_decode_port_stats_request(const struct ofp_header *request,
                                               uint16_t *ofp10_port);
 
 struct ofputil_queue_stats_request {
-    uint16_t port_no;
+    uint16_t port_no;           /* OFPP_ANY means "all ports". */
     uint32_t queue_id;
 };
 
diff --git a/lib/ofp-version-opt.c b/lib/ofp-version-opt.c
new file mode 100644 (file)
index 0000000..35d79e6
--- /dev/null
@@ -0,0 +1,42 @@
+#include <config.h>
+#include "ofp-util.h"
+#include "ofp-version-opt.h"
+#include "vlog.h"
+#include "dynamic-string.h"
+
+VLOG_DEFINE_THIS_MODULE(ofp_version);
+
+static uint32_t allowed_versions = 0;
+
+uint32_t
+get_allowed_ofp_versions(void)
+{
+    return allowed_versions ? allowed_versions : OFPUTIL_DEFAULT_VERSIONS;
+}
+
+void
+set_allowed_ofp_versions(const char *string)
+{
+    allowed_versions = ofputil_versions_from_string(string);
+}
+
+void
+mask_allowed_ofp_versions(uint32_t bitmap)
+{
+    allowed_versions &= bitmap;
+}
+
+void
+ofp_version_usage(void)
+{
+    struct ds msg = DS_EMPTY_INITIALIZER;
+
+    ofputil_format_version_bitmap_names(&msg, OFPUTIL_DEFAULT_VERSIONS);
+    printf(
+        "\nOpen Flow Version options:\n"
+        "  -V, --version           display version information\n"
+        "  -O, --protocols         set allowed Open Flow versions\n"
+        "                          (default: %s)\n",
+        ds_cstr(&msg));
+    ds_destroy(&msg);
+}
diff --git a/lib/ofp-version-opt.h b/lib/ofp-version-opt.h
new file mode 100644 (file)
index 0000000..6bf5eed
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef OFP_VERSION_H
+#define OFP_VERSION_H 1
+
+#include <openflow/openflow-common.h>
+#include "util.h"
+#include "ofp-util.h"
+
+#define OFP_VERSION_LONG_OPTIONS                                \
+        {"version",     no_argument, NULL, 'V'},                \
+        {"protocols", required_argument, NULL, 'O'}
+
+#define OFP_VERSION_OPTION_HANDLERS                             \
+        case 'V':                                               \
+            ovs_print_version(OFP10_VERSION, OFP13_VERSION);    \
+            exit(EXIT_SUCCESS);                                 \
+                                                                \
+        case 'O':                                               \
+            set_allowed_ofp_versions(optarg);                   \
+            break;
+
+uint32_t get_allowed_ofp_versions(void);
+void set_allowed_ofp_versions(const char *string);
+void mask_allowed_ofp_versions(uint32_t);
+void ofp_version_usage(void);
+
+#endif
diff --git a/lib/ofp-version.man b/lib/ofp-version.man
new file mode 100644 (file)
index 0000000..54fa166
--- /dev/null
@@ -0,0 +1,33 @@
+.de IQ
+.  br
+.  ns
+.  IP "\\$1"
+..
+.IP "\fB\-O \fR[\fIversion\fR[\fB,\fIversion\fR]...]\fR"
+.IQ "\fB\-\-protocols=\fR[\fIversion\fR[\fB,\fIversion\fR]...]\fR"
+Sets the OpenFlow protocol versions that are allowed when establishing
+an OpenFlow session.
+.
+.IP
+The following versions are considered to be ready for general use.
+These protocol versions are enabled by default:
+.
+.RS
+.IP \(bu
+\fBOpenFlow10\fR, for OpenFlow 1.0.
+.RE
+.
+.IP
+Support for the following protocol versions is provided for testing
+and development purposes.  They are not enabled by default:
+.
+.RS
+.IP \(bu
+\fBOpenFlow11\fR, for OpenFlow 1.1.
+.
+.IP \(bu
+\fBOpenFlow12\fR, for OpenFlow 1.2.
+.
+.IP \(bu
+\fBOpenFlow13\fR, for OpenFlow 1.3.
+.RE
index 16f4fe6..812d1af 100644 (file)
@@ -20,6 +20,7 @@
 #include <arpa/inet.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
+#include <netinet/ip6.h>
 #include <stdlib.h>
 #include "byte-order.h"
 #include "csum.h"
@@ -272,12 +273,12 @@ ip_count_cidr_bits(ovs_be32 netmask)
 void
 ip_format_masked(ovs_be32 ip, ovs_be32 mask, struct ds *s)
 {
-    ds_put_format(s, IP_FMT, IP_ARGS(&ip));
+    ds_put_format(s, IP_FMT, IP_ARGS(ip));
     if (mask != htonl(UINT32_MAX)) {
         if (ip_is_cidr(mask)) {
             ds_put_format(s, "/%d", ip_count_cidr_bits(mask));
         } else {
-            ds_put_format(s, "/"IP_FMT, IP_ARGS(&mask));
+            ds_put_format(s, "/"IP_FMT, IP_ARGS(mask));
         }
     }
 }
@@ -472,6 +473,133 @@ packet_set_ipv4_addr(struct ofpbuf *packet, ovs_be32 *addr, ovs_be32 new_addr)
     *addr = new_addr;
 }
 
+/* Returns true, if packet contains at least one routing header where
+ * segements_left > 0.
+ *
+ * This function assumes that L3 and L4 markers are set in the packet. */
+static bool
+packet_rh_present(struct ofpbuf *packet)
+{
+    const struct ip6_hdr *nh;
+    int nexthdr;
+    size_t len;
+    size_t remaining;
+    uint8_t *data = packet->l3;
+
+    remaining = (uint8_t *)packet->l4 - (uint8_t *)packet->l3;
+
+    if (remaining < sizeof *nh) {
+        return false;
+    }
+    nh = (struct ip6_hdr *)data;
+    data += sizeof *nh;
+    remaining -= sizeof *nh;
+    nexthdr = nh->ip6_nxt;
+
+    while (1) {
+        if ((nexthdr != IPPROTO_HOPOPTS)
+                && (nexthdr != IPPROTO_ROUTING)
+                && (nexthdr != IPPROTO_DSTOPTS)
+                && (nexthdr != IPPROTO_AH)
+                && (nexthdr != IPPROTO_FRAGMENT)) {
+            /* It's either a terminal header (e.g., TCP, UDP) or one we
+             * don't understand.  In either case, we're done with the
+             * packet, so use it to fill in 'nw_proto'. */
+            break;
+        }
+
+        /* We only verify that at least 8 bytes of the next header are
+         * available, but many of these headers are longer.  Ensure that
+         * accesses within the extension header are within those first 8
+         * bytes. All extension headers are required to be at least 8
+         * bytes. */
+        if (remaining < 8) {
+            return false;
+        }
+
+        if (nexthdr == IPPROTO_AH) {
+            /* A standard AH definition isn't available, but the fields
+             * we care about are in the same location as the generic
+             * option header--only the header length is calculated
+             * differently. */
+            const struct ip6_ext *ext_hdr = (struct ip6_ext *)data;
+
+            nexthdr = ext_hdr->ip6e_nxt;
+            len = (ext_hdr->ip6e_len + 2) * 4;
+        } else if (nexthdr == IPPROTO_FRAGMENT) {
+            const struct ip6_frag *frag_hdr = (struct ip6_frag *)data;
+
+            nexthdr = frag_hdr->ip6f_nxt;
+            len = sizeof *frag_hdr;
+        } else if (nexthdr == IPPROTO_ROUTING) {
+            const struct ip6_rthdr *rh = (struct ip6_rthdr *)data;
+
+            if (rh->ip6r_segleft > 0) {
+                return true;
+            }
+
+            nexthdr = rh->ip6r_nxt;
+            len = (rh->ip6r_len + 1) * 8;
+        } else {
+            const struct ip6_ext *ext_hdr = (struct ip6_ext *)data;
+
+            nexthdr = ext_hdr->ip6e_nxt;
+            len = (ext_hdr->ip6e_len + 1) * 8;
+        }
+
+        if (remaining < len) {
+            return false;
+        }
+        remaining -= len;
+        data += len;
+    }
+
+    return false;
+}
+
+static void
+packet_update_csum128(struct ofpbuf *packet, uint8_t proto,
+                     ovs_be32 addr[4], const ovs_be32 new_addr[4])
+{
+    if (proto == IPPROTO_TCP && packet->l7) {
+        struct tcp_header *th = packet->l4;
+
+        th->tcp_csum = recalc_csum128(th->tcp_csum, addr, new_addr);
+    } else if (proto == IPPROTO_UDP && packet->l7) {
+        struct udp_header *uh = packet->l4;
+
+        if (uh->udp_csum) {
+            uh->udp_csum = recalc_csum128(uh->udp_csum, addr, new_addr);
+            if (!uh->udp_csum) {
+                uh->udp_csum = htons(0xffff);
+            }
+        }
+    }
+}
+
+static void
+packet_set_ipv6_addr(struct ofpbuf *packet, uint8_t proto,
+                     struct in6_addr *addr, const ovs_be32 new_addr[4],
+                     bool recalculate_csum)
+{
+    if (recalculate_csum) {
+        packet_update_csum128(packet, proto, (ovs_be32 *)addr, new_addr);
+    }
+    memcpy(addr, new_addr, sizeof(*addr));
+}
+
+static void
+packet_set_ipv6_flow_label(ovs_be32 *flow_label, ovs_be32 flow_key)
+{
+    *flow_label = (*flow_label & htonl(~IPV6_LABEL_MASK)) | flow_key;
+}
+
+static void
+packet_set_ipv6_tc(ovs_be32 *flow_label, uint8_t tc)
+{
+    *flow_label = (*flow_label & htonl(0xF00FFFFF)) | htonl(tc << 20);
+}
+
 /* Modifies the IPv4 header fields of 'packet' to be consistent with 'src',
  * 'dst', 'tos', and 'ttl'.  Updates 'packet''s L4 checksums as appropriate.
  * 'packet' must contain a valid IPv4 packet with correctly populated l[347]
@@ -507,6 +635,33 @@ packet_set_ipv4(struct ofpbuf *packet, ovs_be32 src, ovs_be32 dst,
     }
 }
 
+/* Modifies the IPv6 header fields of 'packet' to be consistent with 'src',
+ * 'dst', 'traffic class', and 'next hop'.  Updates 'packet''s L4 checksums as
+ * appropriate. 'packet' must contain a valid IPv6 packet with correctly
+ * populated l[347] markers. */
+void
+packet_set_ipv6(struct ofpbuf *packet, uint8_t proto, const ovs_be32 src[4],
+                const ovs_be32 dst[4], uint8_t key_tc, ovs_be32 key_fl,
+                uint8_t key_hl)
+{
+    struct ip6_hdr *nh = packet->l3;
+
+    if (memcmp(&nh->ip6_src, src, sizeof(ovs_be32[4]))) {
+        packet_set_ipv6_addr(packet, proto, &nh->ip6_src, src, true);
+    }
+
+    if (memcmp(&nh->ip6_dst, dst, sizeof(ovs_be32[4]))) {
+        packet_set_ipv6_addr(packet, proto, &nh->ip6_dst, dst,
+                             !packet_rh_present(packet));
+    }
+
+    packet_set_ipv6_tc(&nh->ip6_flow, key_tc);
+
+    packet_set_ipv6_flow_label(&nh->ip6_flow, key_fl);
+
+    nh->ip6_hlim = key_hl;
+}
+
 static void
 packet_set_port(ovs_be16 *port, ovs_be16 new_port, ovs_be16 *csum)
 {
index e550be0..7e2d4e9 100644 (file)
@@ -272,16 +272,12 @@ struct vlan_eth_header {
 } __attribute__((packed));
 BUILD_ASSERT_DECL(VLAN_ETH_HEADER_LEN == sizeof(struct vlan_eth_header));
 
-/* The "(void) (ip)[0]" below has no effect on the value, since it's the first
- * argument of a comma expression, but it makes sure that 'ip' is a pointer.
- * This is useful since a common mistake is to pass an integer instead of a
- * pointer to IP_ARGS. */
-#define IP_FMT "%"PRIu8".%"PRIu8".%"PRIu8".%"PRIu8
+#define IP_FMT "%"PRIu32".%"PRIu32".%"PRIu32".%"PRIu32
 #define IP_ARGS(ip)                             \
-        ((void) (ip)[0], ((uint8_t *) ip)[0]),  \
-        ((uint8_t *) ip)[1],                    \
-        ((uint8_t *) ip)[2],                    \
-        ((uint8_t *) ip)[3]
+    ntohl(ip) >> 24,                            \
+    (ntohl(ip) >> 16) & 0xff,                   \
+    (ntohl(ip) >> 8) & 0xff,                    \
+    ntohl(ip) & 0xff
 
 /* Example:
  *
@@ -490,6 +486,9 @@ void *snap_compose(struct ofpbuf *, const uint8_t eth_dst[ETH_ADDR_LEN],
                    unsigned int oui, uint16_t snap_type, size_t size);
 void packet_set_ipv4(struct ofpbuf *, ovs_be32 src, ovs_be32 dst, uint8_t tos,
                      uint8_t ttl);
+void packet_set_ipv6(struct ofpbuf *, uint8_t proto, const ovs_be32 src[4],
+                     const ovs_be32 dst[4], uint8_t tc,
+                     ovs_be32 fl, uint8_t hlmit);
 void packet_set_tcp_port(struct ofpbuf *, ovs_be16 src, ovs_be16 dst);
 void packet_set_udp_port(struct ofpbuf *, ovs_be16 src, ovs_be16 dst);
 
index ddf578c..002f367 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -131,8 +131,15 @@ struct rconn {
 #define MAX_MONITORS 8
     struct vconn *monitors[8];
     size_t n_monitors;
+
+    uint32_t allowed_versions;
 };
 
+uint32_t rconn_get_allowed_versions(const struct rconn *rconn)
+{
+    return rconn->allowed_versions;
+}
+
 static unsigned int elapsed_in_this_state(const struct rconn *);
 static unsigned int timeout(const struct rconn *);
 static bool timed_out(const struct rconn *);
@@ -163,9 +170,17 @@ static bool rconn_logging_connection_attempts__(const struct rconn *);
  * 8 seconds is used.
  *
  * The new rconn is initially unconnected.  Use rconn_connect() or
- * rconn_connect_unreliably() to connect it. */
+ * rconn_connect_unreliably() to connect it.
+ *
+ * Connections made by the rconn will automatically negotiate an OpenFlow
+ * protocol version acceptable to both peers on the connection.  The version
+ * negotiated will be one of those in the 'allowed_versions' bitmap: version
+ * 'x' is allowed if allowed_versions & (1 << x) is nonzero.  (The underlying
+ * vconn will treat an 'allowed_versions' of 0 as OFPUTIL_DEFAULT_VERSIONS.)
+ */
 struct rconn *
-rconn_create(int probe_interval, int max_backoff, uint8_t dscp)
+rconn_create(int probe_interval, int max_backoff, uint8_t dscp,
+             uint32_t allowed_versions)
 {
     struct rconn *rc = xzalloc(sizeof *rc);
 
@@ -203,6 +218,7 @@ rconn_create(int probe_interval, int max_backoff, uint8_t dscp)
     rconn_set_dscp(rc, dscp);
 
     rc->n_monitors = 0;
+    rc->allowed_versions = allowed_versions;
 
     return rc;
 }
@@ -354,7 +370,8 @@ reconnect(struct rconn *rc)
         VLOG_INFO("%s: connecting...", rc->name);
     }
     rc->n_attempted_connections++;
-    retval = vconn_open(rc->target, OFP10_VERSION, &rc->vconn, rc->dscp);
+    retval = vconn_open(rc->target, rc->allowed_versions, rc->dscp,
+                        &rc->vconn);
     if (!retval) {
         rc->remote_ip = vconn_get_remote_ip(rc->vconn);
         rc->local_ip = vconn_get_local_ip(rc->vconn);
@@ -632,14 +649,13 @@ int
 rconn_send_with_limit(struct rconn *rc, struct ofpbuf *b,
                       struct rconn_packet_counter *counter, int queue_limit)
 {
-    int retval;
-    retval = (counter->n_packets >= queue_limit
-              ? EAGAIN
-              : rconn_send(rc, b, counter));
-    if (retval) {
+    if (counter->n_packets < queue_limit) {
+        return rconn_send(rc, b, counter);
+    } else {
         COVERAGE_INC(rconn_overflow);
+        ofpbuf_delete(b);
+        return EAGAIN;
     }
-    return retval;
 }
 
 /* Returns the total number of packets successfully sent on the underlying
@@ -657,6 +673,14 @@ void
 rconn_add_monitor(struct rconn *rc, struct vconn *vconn)
 {
     if (rc->n_monitors < ARRAY_SIZE(rc->monitors)) {
+        int version = vconn_get_version(rc->vconn);
+
+        /* Override the allowed versions of the snoop vconn so that
+         * only the version of the controller connection is allowed.
+         * This is because the snoop will see the same messages as the
+         * controller */
+        vconn_set_allowed_versions(vconn, 1u << version);
+
         VLOG_INFO("new monitor connection from %s", vconn_get_name(vconn));
         rc->monitors[rc->n_monitors++] = vconn;
     } else {
@@ -1096,6 +1120,26 @@ is_admitted_msg(const struct ofpbuf *b)
     case OFPTYPE_GET_CONFIG_REQUEST:
     case OFPTYPE_GET_CONFIG_REPLY:
     case OFPTYPE_SET_CONFIG:
+        /* FIXME: Change the following once they are implemented: */
+    case OFPTYPE_QUEUE_GET_CONFIG_REQUEST:
+    case OFPTYPE_QUEUE_GET_CONFIG_REPLY:
+    case OFPTYPE_GET_ASYNC_REQUEST:
+    case OFPTYPE_GET_ASYNC_REPLY:
+    case OFPTYPE_METER_MOD:
+    case OFPTYPE_GROUP_REQUEST:
+    case OFPTYPE_GROUP_REPLY:
+    case OFPTYPE_GROUP_DESC_REQUEST:
+    case OFPTYPE_GROUP_DESC_REPLY:
+    case OFPTYPE_GROUP_FEATURES_REQUEST:
+    case OFPTYPE_GROUP_FEATURES_REPLY:
+    case OFPTYPE_METER_REQUEST:
+    case OFPTYPE_METER_REPLY:
+    case OFPTYPE_METER_CONFIG_REQUEST:
+    case OFPTYPE_METER_CONFIG_REPLY:
+    case OFPTYPE_METER_FEATURES_REQUEST:
+    case OFPTYPE_METER_FEATURES_REPLY:
+    case OFPTYPE_TABLE_FEATURES_REQUEST:
+    case OFPTYPE_TABLE_FEATURES_REPLY:
         return false;
 
     case OFPTYPE_PACKET_IN:
index d10cef7..aa30238 100644 (file)
@@ -38,8 +38,10 @@ struct vconn;
 struct rconn_packet_counter;
 
 struct rconn *rconn_create(int inactivity_probe_interval,
-                          int max_backoff, uint8_t dscp);
+                          int max_backoff, uint8_t dscp,
+                          uint32_t allowed_versions);
 void rconn_set_dscp(struct rconn *rc, uint8_t dscp);
+uint32_t rconn_get_allowed_versions(const struct rconn *);
 uint8_t rconn_get_dscp(const struct rconn *rc);
 void rconn_set_max_backoff(struct rconn *, int max_backoff);
 int rconn_get_max_backoff(const struct rconn *);
index a37dfe4..c2f74d0 100644 (file)
@@ -205,22 +205,10 @@ lookup_hostname(const char *host_name, struct in_addr *addr)
             : EINVAL);
 }
 
-/* Returns the error condition associated with socket 'fd' and resets the
- * socket's error status. */
-int
-get_socket_error(int fd)
-{
-    int error;
-
-    if (getsockopt_int(fd, SOL_SOCKET, SO_ERROR, "SO_ERROR", &error)) {
-        error = errno;
-    }
-    return error;
-}
-
 int
 check_connection_completion(int fd)
 {
+    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 10);
     struct pollfd pfd;
     int retval;
 
@@ -230,9 +218,17 @@ check_connection_completion(int fd)
         retval = poll(&pfd, 1, 0);
     } while (retval < 0 && errno == EINTR);
     if (retval == 1) {
-        return get_socket_error(fd);
+        if (pfd.revents & POLLERR) {
+            ssize_t n = send(fd, "", 1, MSG_DONTWAIT);
+            if (n < 0) {
+                return errno;
+            } else {
+                VLOG_ERR_RL(&rl, "poll return POLLERR but send succeeded");
+                return EPROTO;
+            }
+        }
+        return 0;
     } else if (retval < 0) {
-        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 10);
         VLOG_ERR_RL(&rl, "poll: %s", strerror(errno));
         return errno;
     } else {
@@ -612,6 +608,7 @@ exit:
         }
     } else if (fd >= 0) {
         close(fd);
+        fd = -1;
     }
     *fdp = fd;
     return error;
@@ -944,7 +941,7 @@ describe_sockaddr(struct ds *string, int fd,
 
             memcpy(&sin, &ss, sizeof sin);
             ds_put_format(string, IP_FMT":%"PRIu16,
-                          IP_ARGS(&sin.sin_addr.s_addr), ntohs(sin.sin_port));
+                          IP_ARGS(sin.sin_addr.s_addr), ntohs(sin.sin_port));
         } else if (ss.ss_family == AF_UNIX) {
             struct sockaddr_un sun;
             const char *null;
@@ -1123,7 +1120,7 @@ send_iovec_and_fds(int sock,
 
         msg.msg_name = NULL;
         msg.msg_namelen = 0;
-        msg.msg_iov = (struct iovec *) iovs;
+        msg.msg_iov = CONST_CAST(struct iovec *, iovs);
         msg.msg_iovlen = n_iovs;
         msg.msg_control = &cmsg.cm;
         msg.msg_controllen = CMSG_SPACE(n_fds * sizeof *fds);
index a00b32e..5bf8529 100644 (file)
@@ -36,7 +36,6 @@ int lookup_ipv6(const char *host_name, struct in6_addr *address);
 
 int lookup_hostname(const char *host_name, struct in_addr *);
 
-int get_socket_error(int sock);
 int get_socket_rcvbuf(int sock);
 int check_connection_completion(int fd);
 int drain_rcvbuf(int fd);
index ee4224a..04039a7 100644 (file)
@@ -251,3 +251,22 @@ sset_equals(const struct sset *a, const struct sset *b)
 
     return true;
 }
+
+/* Returns the next node in 'set' in hash order, or NULL if no nodes remain in
+ * 'set'.  Uses '*bucketp' and '*offsetp' to determine where to begin
+ * iteration, and stores new values to pass on the next iteration into them
+ * before returning.
+ *
+ * It's better to use plain SSET_FOR_EACH and related functions, since they are
+ * faster and better at dealing with ssets that change during iteration.
+ *
+ * Before beginning iteration, store 0 into '*bucketp' and '*offsetp'.
+ */
+struct sset_node *
+sset_at_position(const struct sset *set, uint32_t *bucketp, uint32_t *offsetp)
+{
+    struct hmap_node *hmap_node;
+
+    hmap_node = hmap_at_position(&set->map, bucketp, offsetp);
+    return SSET_NODE_FROM_HMAP_NODE(hmap_node);
+}
index f63f4ab..327074c 100644 (file)
@@ -64,6 +64,8 @@ char *sset_pop(struct sset *);
 struct sset_node *sset_find(const struct sset *, const char *);
 bool sset_contains(const struct sset *, const char *);
 bool sset_equals(const struct sset *, const struct sset *);
+struct sset_node *sset_at_position(const struct sset *,
+                                   uint32_t *bucketp, uint32_t *offsetp);
 
 /* Iteration macros. */
 #define SSET_FOR_EACH(NAME, SSET)               \
index 0ca5b18..184b3ff 100644 (file)
@@ -805,7 +805,7 @@ pssl_open(const char *name OVS_UNUSED, char *suffix, struct pstream **pstreamp,
         return -fd;
     }
     sprintf(bound_name, "pssl:%"PRIu16":"IP_FMT,
-            ntohs(sin.sin_port), IP_ARGS(&sin.sin_addr.s_addr));
+            ntohs(sin.sin_port), IP_ARGS(sin.sin_addr.s_addr));
 
     pssl = xmalloc(sizeof *pssl);
     pstream_init(&pssl->pstream, &pssl_pstream_class, bound_name);
@@ -847,7 +847,7 @@ pssl_accept(struct pstream *pstream, struct stream **new_streamp)
         return error;
     }
 
-    sprintf(name, "ssl:"IP_FMT, IP_ARGS(&sin.sin_addr));
+    sprintf(name, "ssl:"IP_FMT, IP_ARGS(sin.sin_addr.s_addr));
     if (sin.sin_port != htons(OFP_SSL_PORT)) {
         sprintf(strchr(name, '\0'), ":%"PRIu16, ntohs(sin.sin_port));
     }
index 05601e1..0384c42 100644 (file)
@@ -116,7 +116,7 @@ ptcp_open(const char *name OVS_UNUSED, char *suffix, struct pstream **pstreamp,
     }
 
     sprintf(bound_name, "ptcp:%"PRIu16":"IP_FMT,
-            ntohs(sin.sin_port), IP_ARGS(&sin.sin_addr.s_addr));
+            ntohs(sin.sin_port), IP_ARGS(sin.sin_addr.s_addr));
     return new_fd_pstream(bound_name, fd, ptcp_accept, set_dscp, NULL,
                           pstreamp);
 }
@@ -129,7 +129,7 @@ ptcp_accept(int fd, const struct sockaddr *sa, size_t sa_len,
     char name[128];
 
     if (sa_len == sizeof(struct sockaddr_in) && sin->sin_family == AF_INET) {
-        sprintf(name, "tcp:"IP_FMT, IP_ARGS(&sin->sin_addr));
+        sprintf(name, "tcp:"IP_FMT, IP_ARGS(sin->sin_addr.s_addr));
         sprintf(strchr(name, '\0'), ":%"PRIu16, ntohs(sin->sin_port));
     } else {
         strcpy(name, "tcp");
index 4feefdf..6dee17d 100644 (file)
@@ -48,7 +48,7 @@ unix_open(const char *name, char *suffix, struct stream **streamp,
 
     fd = make_unix_socket(SOCK_STREAM, true, NULL, connect_path);
     if (fd < 0) {
-        VLOG_WARN("%s: connection failed (%s)", connect_path, strerror(-fd));
+        VLOG_DBG("%s: connection failed (%s)", connect_path, strerror(-fd));
         return -fd;
     }
 
index a1f4bd7..70f3691 100644 (file)
@@ -96,22 +96,6 @@ is_pow2(uintmax_t x)
     return IS_POW2(x);
 }
 
-/* Returns the rightmost 1-bit in 'x' (e.g. 01011000 => 00001000), or 0 if 'x'
- * is 0. */
-static inline uintmax_t
-rightmost_1bit(uintmax_t x)
-{
-    return x & -x;
-}
-
-/* Returns 'x' with its rightmost 1-bit changed to a zero (e.g. 01011000 =>
- * 01010000), or 0 if 'x' is 0. */
-static inline uintmax_t
-zero_rightmost_1bit(uintmax_t x)
-{
-    return x & (x - 1);
-}
-
 #ifndef MIN
 #define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
 #endif
@@ -242,6 +226,8 @@ char *xreadlink(const char *filename);
 char *follow_symlinks(const char *filename);
 
 void ignore(bool x OVS_UNUSED);
+\f
+/* Bitwise tests. */
 
 /* Returns the number of trailing 0-bits in 'n'.  Undefined if 'n' == 0.
  *
@@ -270,6 +256,43 @@ int log_2_floor(uint32_t);
 int log_2_ceil(uint32_t);
 int popcount(uint32_t);
 
+/* Returns the rightmost 1-bit in 'x' (e.g. 01011000 => 00001000), or 0 if 'x'
+ * is 0. */
+static inline uintmax_t
+rightmost_1bit(uintmax_t x)
+{
+    return x & -x;
+}
+
+/* Returns 'x' with its rightmost 1-bit changed to a zero (e.g. 01011000 =>
+ * 01010000), or 0 if 'x' is 0. */
+static inline uintmax_t
+zero_rightmost_1bit(uintmax_t x)
+{
+    return x & (x - 1);
+}
+
+/* Returns the index of the rightmost 1-bit in 'x' (e.g. 01011000 => 3), or 32
+ * if 'x' is 0.
+ *
+ * Unlike the other functions for rightmost 1-bits, this function only works
+ * with 32-bit integers. */
+static inline uint32_t
+rightmost_1bit_idx(uint32_t x)
+{
+    return x ? ctz(x) : 32;
+}
+
+/* Returns the index of the rightmost 1-bit in 'x' (e.g. 01011000 => 6), or 32
+ * if 'x' is 0.
+ *
+ * This function only works with 32-bit integers. */
+static inline uint32_t
+leftmost_1bit_idx(uint32_t x)
+{
+    return x ? log_2_floor(x) : 32;
+}
+\f
 bool is_all_zeros(const uint8_t *, size_t);
 bool is_all_ones(const uint8_t *, size_t);
 void bitwise_copy(const void *src, unsigned int src_len, unsigned int src_ofs,
index 54ec2e6..a26d9c5 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -33,7 +33,8 @@ struct vconn {
     struct vconn_class *class;
     int state;
     int error;
-    enum ofp_version min_version;
+    uint32_t allowed_versions;
+    uint32_t peer_versions;
     enum ofp_version version;
     ovs_be32 remote_ip;
     ovs_be16 remote_port;
@@ -43,7 +44,8 @@ struct vconn {
 };
 
 void vconn_init(struct vconn *, struct vconn_class *, int connect_status,
-                const char *name);
+                const char *name, uint32_t allowed_versions);
+void vconn_free_data(struct vconn *vconn);
 void vconn_set_remote_ip(struct vconn *, ovs_be32 remote_ip);
 void vconn_set_remote_port(struct vconn *, ovs_be16 remote_port);
 void vconn_set_local_ip(struct vconn *, ovs_be32 local_ip);
@@ -62,6 +64,9 @@ struct vconn_class {
      * connection name provided by the user, e.g. "tcp:1.2.3.4".  This name is
      * useful for error messages but must not be modified.
      *
+     * 'allowed_verions' is the OpenFlow versions that may be
+     * negotiated for a connection.
+     *
      * 'suffix' is a copy of 'name' following the colon and may be modified.
      * 'dscp' is the DSCP value that the new connection should use in the IP
      * packets it sends.
@@ -73,8 +78,8 @@ struct vconn_class {
      * If the connection cannot be completed immediately, it should return
      * EAGAIN (not EINPROGRESS, as returned by the connect system call) and
      * continue the connection in the background. */
-    int (*open)(const char *name, char *suffix, struct vconn **vconnp,
-                uint8_t dscp);
+    int (*open)(const char *name, uint32_t allowed_versions,
+                char *suffix, struct vconn **vconnp, uint8_t dscp);
 
     /* Closes 'vconn' and frees associated memory. */
     void (*close)(struct vconn *vconn);
@@ -135,9 +140,11 @@ struct vconn_class {
 struct pvconn {
     struct pvconn_class *class;
     char *name;
+    uint32_t allowed_versions;
 };
 
-void pvconn_init(struct pvconn *, struct pvconn_class *, const char *name);
+void pvconn_init(struct pvconn *pvconn, struct pvconn_class *class,
+                 const char *name, uint32_t allowed_versions);
 static inline void pvconn_assert_class(const struct pvconn *pvconn,
                                        const struct pvconn_class *class)
 {
@@ -152,6 +159,9 @@ struct pvconn_class {
      * full connection name provided by the user, e.g. "ptcp:1234".  This name
      * is useful for error messages but must not be modified.
      *
+     * 'allowed_versions' is the OpenFlow protocol versions that may
+     * be negotiated for a session.
+     *
      * 'suffix' is a copy of 'name' following the colon and may be modified.
      * 'dscp' is the DSCP value that the new connection should use in the IP
      * packets it sends.
@@ -163,8 +173,8 @@ struct pvconn_class {
      * completed immediately, it should return EAGAIN (not EINPROGRESS, as
      * returned by the connect system call) and continue the connection in the
      * background. */
-    int (*listen)(const char *name, char *suffix, struct pvconn **pvconnp,
-                  uint8_t dscp);
+    int (*listen)(const char *name, uint32_t allowed_versions,
+                  char *suffix, struct pvconn **pvconnp, uint8_t dscp);
 
     /* Closes 'pvconn' and frees associated memory. */
     void (*close)(struct pvconn *pvconn);
index d707e06..38ce374 100644 (file)
@@ -54,13 +54,14 @@ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(10, 25);
 static void vconn_stream_clear_txbuf(struct vconn_stream *);
 
 static struct vconn *
-vconn_stream_new(struct stream *stream, int connect_status)
+vconn_stream_new(struct stream *stream, int connect_status,
+                 uint32_t allowed_versions)
 {
     struct vconn_stream *s;
 
     s = xmalloc(sizeof *s);
     vconn_init(&s->vconn, &stream_vconn_class, connect_status,
-               stream_get_name(stream));
+               stream_get_name(stream), allowed_versions);
     s->stream = stream;
     s->txbuf = NULL;
     s->rxbuf = NULL;
@@ -77,8 +78,8 @@ vconn_stream_new(struct stream *stream, int connect_status)
  *
  * Returns 0 if successful, otherwise a positive errno value. */
 static int
-vconn_stream_open(const char *name, char *suffix OVS_UNUSED,
-                  struct vconn **vconnp, uint8_t dscp)
+vconn_stream_open(const char *name, uint32_t allowed_versions,
+                  char *suffix OVS_UNUSED, struct vconn **vconnp, uint8_t dscp)
 {
     struct stream *stream;
     int error;
@@ -88,7 +89,7 @@ vconn_stream_open(const char *name, char *suffix OVS_UNUSED,
     if (!error) {
         error = stream_connect(stream);
         if (!error || error == EAGAIN) {
-            *vconnp = vconn_stream_new(stream, error);
+            *vconnp = vconn_stream_new(stream, error, allowed_versions);
             return 0;
         }
     }
@@ -310,8 +311,9 @@ pvconn_pstream_cast(struct pvconn *pvconn)
  * Returns 0 if successful, otherwise a positive errno value.  (The current
  * implementation never fails.) */
 static int
-pvconn_pstream_listen(const char *name, char *suffix OVS_UNUSED,
-                      struct pvconn **pvconnp, uint8_t dscp)
+pvconn_pstream_listen(const char *name, uint32_t allowed_versions,
+                      char *suffix OVS_UNUSED, struct pvconn **pvconnp,
+                      uint8_t dscp)
 {
     struct pvconn_pstream *ps;
     struct pstream *pstream;
@@ -324,7 +326,7 @@ pvconn_pstream_listen(const char *name, char *suffix OVS_UNUSED,
     }
 
     ps = xmalloc(sizeof *ps);
-    pvconn_init(&ps->pvconn, &pstream_pvconn_class, name);
+    pvconn_init(&ps->pvconn, &pstream_pvconn_class, name, allowed_versions);
     ps->pstream = pstream;
     *pvconnp = &ps->pvconn;
     return 0;
@@ -354,7 +356,7 @@ pvconn_pstream_accept(struct pvconn *pvconn, struct vconn **new_vconnp)
         return error;
     }
 
-    *new_vconnp = vconn_stream_new(stream, 0);
+    *new_vconnp = vconn_stream_new(stream, 0, pvconn->allowed_versions);
     return 0;
 }
 
index 9271f4f..a3792ec 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -215,14 +215,16 @@ vconn_verify_name(const char *name)
  *
  * The vconn will automatically negotiate an OpenFlow protocol version
  * acceptable to both peers on the connection.  The version negotiated will be
- * no lower than 'min_version' and no higher than OFP10_VERSION.
+ * one of those in the 'allowed_versions' bitmap: version 'x' is allowed if
+ * allowed_versions & (1 << x) is nonzero.  If 'allowed_versions' is zero, then
+ * OFPUTIL_DEFAULT_VERSIONS are allowed.
  *
  * Returns 0 if successful, otherwise a positive errno value.  If successful,
  * stores a pointer to the new connection in '*vconnp', otherwise a null
  * pointer.  */
 int
-vconn_open(const char *name, int min_version, struct vconn **vconnp,
-           uint8_t dscp)
+vconn_open(const char *name, uint32_t allowed_versions, uint8_t dscp,
+           struct vconn **vconnp)
 {
     struct vconn_class *class;
     struct vconn *vconn;
@@ -232,6 +234,10 @@ vconn_open(const char *name, int min_version, struct vconn **vconnp,
     COVERAGE_INC(vconn_open);
     check_vconn_classes();
 
+    if (!allowed_versions) {
+        allowed_versions = OFPUTIL_DEFAULT_VERSIONS;
+    }
+
     /* Look up the class. */
     error = vconn_lookup_class(name, &class);
     if (!class) {
@@ -240,7 +246,7 @@ vconn_open(const char *name, int min_version, struct vconn **vconnp,
 
     /* Call class's "open" function. */
     suffix_copy = xstrdup(strchr(name, ':') + 1);
-    error = class->open(name, suffix_copy, &vconn, dscp);
+    error = class->open(name, allowed_versions, suffix_copy, &vconn, dscp);
     free(suffix_copy);
     if (error) {
         goto error;
@@ -248,7 +254,6 @@ vconn_open(const char *name, int min_version, struct vconn **vconnp,
 
     /* Success. */
     assert(vconn->state != VCS_CONNECTING || vconn->class->connect);
-    vconn->min_version = min_version;
     *vconnp = vconn;
     return 0;
 
@@ -290,7 +295,7 @@ vconn_run_wait(struct vconn *vconn)
 }
 
 int
-vconn_open_block(const char *name, enum ofp_version min_version,
+vconn_open_block(const char *name, uint32_t allowed_versions, uint8_t dscp,
                  struct vconn **vconnp)
 {
     struct vconn *vconn;
@@ -298,7 +303,7 @@ vconn_open_block(const char *name, enum ofp_version min_version,
 
     fatal_signal_run();
 
-    error = vconn_open(name, min_version, &vconn, DSCP_DEFAULT);
+    error = vconn_open(name, allowed_versions, dscp, &vconn);
     if (!error) {
         error = vconn_connect_block(vconn);
     }
@@ -330,6 +335,22 @@ vconn_get_name(const struct vconn *vconn)
     return vconn->name;
 }
 
+/* Returns the allowed_versions of 'vconn', that is,
+ * the allowed_versions passed to vconn_open(). */
+uint32_t
+vconn_get_allowed_versions(const struct vconn *vconn)
+{
+    return vconn->allowed_versions;
+}
+
+/* Sets the allowed_versions of 'vconn', overriding
+ * the allowed_versions passed to vconn_open(). */
+void
+vconn_set_allowed_versions(struct vconn *vconn, uint32_t allowed_versions)
+{
+    vconn->allowed_versions = allowed_versions;
+}
+
 /* Returns the IP address of the peer, or 0 if the peer is not connected over
  * an IP-based protocol or if its IP address is not yet known. */
 ovs_be32
@@ -393,7 +414,7 @@ vcs_send_hello(struct vconn *vconn)
     struct ofpbuf *b;
     int retval;
 
-    b = ofpraw_alloc(OFPRAW_OFPT_HELLO, OFP10_VERSION, 0);
+    b = ofputil_encode_hello(vconn->allowed_versions);
     retval = do_send(vconn, b);
     if (!retval) {
         vconn->state = VCS_RECV_HELLO;
@@ -406,6 +427,28 @@ vcs_send_hello(struct vconn *vconn)
     }
 }
 
+static char *
+version_bitmap_to_string(uint32_t bitmap)
+{
+    struct ds s;
+
+    ds_init(&s);
+    if (!bitmap) {
+        ds_put_cstr(&s, "no versions");
+    } else if (is_pow2(bitmap)) {
+        ds_put_cstr(&s, "version ");
+        ofputil_format_version(&s, leftmost_1bit_idx(bitmap));
+    } else if (is_pow2((bitmap >> 1) + 1)) {
+        ds_put_cstr(&s, "version ");
+        ofputil_format_version(&s, leftmost_1bit_idx(bitmap));
+        ds_put_cstr(&s, "and earlier");
+    } else {
+        ds_put_cstr(&s, "versions ");
+        ofputil_format_version_bitmap(&s, bitmap);
+    }
+    return ds_steal_cstr(&s);
+}
+
 static void
 vcs_recv_hello(struct vconn *vconn)
 {
@@ -414,37 +457,45 @@ vcs_recv_hello(struct vconn *vconn)
 
     retval = do_recv(vconn, &b);
     if (!retval) {
-        const struct ofp_header *oh = b->data;
         enum ofptype type;
         enum ofperr error;
 
         error = ofptype_decode(&type, b->data);
         if (!error && type == OFPTYPE_HELLO) {
-            if (b->size > sizeof *oh) {
+            char *peer_s, *local_s;
+            uint32_t common_versions;
+
+            if (!ofputil_decode_hello(b->data, &vconn->peer_versions)) {
                 struct ds msg = DS_EMPTY_INITIALIZER;
-                ds_put_format(&msg, "%s: extra-long hello:\n", vconn->name);
+                ds_put_format(&msg, "%s: unknown data in hello:\n",
+                              vconn->name);
                 ds_put_hex_dump(&msg, b->data, b->size, 0, true);
                 VLOG_WARN_RL(&bad_ofmsg_rl, "%s", ds_cstr(&msg));
                 ds_destroy(&msg);
             }
 
-            vconn->version = MIN(OFP10_VERSION, oh->version);
-            if (vconn->version < vconn->min_version) {
+            local_s = version_bitmap_to_string(vconn->allowed_versions);
+            peer_s = version_bitmap_to_string(vconn->peer_versions);
+
+            common_versions = vconn->peer_versions & vconn->allowed_versions;
+            if (!common_versions) {
+                vconn->version = leftmost_1bit_idx(vconn->peer_versions);
                 VLOG_WARN_RL(&bad_ofmsg_rl,
-                             "%s: version negotiation failed: we support "
-                             "versions 0x%02x to 0x%02x inclusive but peer "
-                             "supports no later than version 0x%02"PRIx8,
-                             vconn->name, vconn->min_version, OFP10_VERSION,
-                             oh->version);
+                             "%s: version negotiation failed (we support "
+                             "%s, peer supports %s)",
+                             vconn->name, local_s, peer_s);
                 vconn->state = VCS_SEND_ERROR;
             } else {
+                vconn->version = leftmost_1bit_idx(common_versions);
                 VLOG_DBG("%s: negotiated OpenFlow version 0x%02x "
-                         "(we support versions 0x%02x to 0x%02x inclusive, "
-                         "peer no later than version 0x%02"PRIx8")",
-                         vconn->name, vconn->version, vconn->min_version,
-                         OFP10_VERSION, oh->version);
+                         "(we support %s, peer supports %s)", vconn->name,
+                         vconn->version, local_s, peer_s);
                 vconn->state = VCS_CONNECTED;
             }
+
+            free(local_s);
+            free(peer_s);
+
             ofpbuf_delete(b);
             return;
         } else {
@@ -470,10 +521,15 @@ vcs_send_error(struct vconn *vconn)
     struct ofpbuf *b;
     char s[128];
     int retval;
+    char *local_s, *peer_s;
+
+    local_s = version_bitmap_to_string(vconn->allowed_versions);
+    peer_s = version_bitmap_to_string(vconn->peer_versions);
+    snprintf(s, sizeof s, "We support %s, you support %s, no common versions.",
+             local_s, peer_s);
+    free(peer_s);
+    free(local_s);
 
-    snprintf(s, sizeof s, "We support versions 0x%02x to 0x%02x inclusive but "
-             "you support no later than version 0x%02"PRIx8".",
-             vconn->min_version, OFP12_VERSION, vconn->version);
     b = ofperr_encode_hello(OFPERR_OFPHFC_INCOMPATIBLE, vconn->version, s);
     retval = do_send(vconn, b);
     if (retval) {
@@ -494,7 +550,6 @@ vconn_connect(struct vconn *vconn)
 {
     enum vconn_state last_state;
 
-    assert(vconn->min_version > 0);
     do {
         last_state = vconn->state;
         switch (vconn->state) {
@@ -921,11 +976,18 @@ pvconn_verify_name(const char *name)
  * connection name in the form "TYPE:ARGS", where TYPE is an passive vconn
  * class's name and ARGS are vconn class-specific.
  *
+ * vconns accepted by the pvconn will automatically negotiate an OpenFlow
+ * protocol version acceptable to both peers on the connection.  The version
+ * negotiated will be one of those in the 'allowed_versions' bitmap: version
+ * 'x' is allowed if allowed_versions & (1 << x) is nonzero.  If
+ * 'allowed_versions' is zero, then OFPUTIL_DEFAULT_VERSIONS are allowed.
+ *
  * Returns 0 if successful, otherwise a positive errno value.  If successful,
  * stores a pointer to the new connection in '*pvconnp', otherwise a null
  * pointer.  */
 int
-pvconn_open(const char *name, struct pvconn **pvconnp, uint8_t dscp)
+pvconn_open(const char *name, uint32_t allowed_versions, uint8_t dscp,
+            struct pvconn **pvconnp)
 {
     struct pvconn_class *class;
     struct pvconn *pvconn;
@@ -934,6 +996,10 @@ pvconn_open(const char *name, struct pvconn **pvconnp, uint8_t dscp)
 
     check_vconn_classes();
 
+    if (!allowed_versions) {
+        allowed_versions = OFPUTIL_DEFAULT_VERSIONS;
+    }
+
     /* Look up the class. */
     error = pvconn_lookup_class(name, &class);
     if (!class) {
@@ -942,7 +1008,7 @@ pvconn_open(const char *name, struct pvconn **pvconnp, uint8_t dscp)
 
     /* Call class's "open" function. */
     suffix_copy = xstrdup(strchr(name, ':') + 1);
-    error = class->listen(name, suffix_copy, &pvconn, dscp);
+    error = class->listen(name, allowed_versions, suffix_copy, &pvconn, dscp);
     free(suffix_copy);
     if (error) {
         goto error;
@@ -982,12 +1048,12 @@ pvconn_close(struct pvconn *pvconn)
  *
  * The new vconn will automatically negotiate an OpenFlow protocol version
  * acceptable to both peers on the connection.  The version negotiated will be
- * no lower than 'min_version' and no higher than OFP10_VERSION.
+ * no lower than 'min_version' and no higher than 'max_version'.
  *
  * pvconn_accept() will not block waiting for a connection.  If no connection
  * is ready to be accepted, it returns EAGAIN immediately. */
 int
-pvconn_accept(struct pvconn *pvconn, int min_version, struct vconn **new_vconn)
+pvconn_accept(struct pvconn *pvconn, struct vconn **new_vconn)
 {
     int retval = (pvconn->class->accept)(pvconn, new_vconn);
     if (retval) {
@@ -995,7 +1061,6 @@ pvconn_accept(struct pvconn *pvconn, int min_version, struct vconn **new_vconn)
     } else {
         assert((*new_vconn)->state != VCS_CONNECTING
                || (*new_vconn)->class->connect);
-        (*new_vconn)->min_version = min_version;
     }
     return retval;
 }
@@ -1025,7 +1090,7 @@ pvconn_wait(struct pvconn *pvconn)
  * The caller retains ownership of 'name'. */
 void
 vconn_init(struct vconn *vconn, struct vconn_class *class, int connect_status,
-           const char *name)
+           const char *name, uint32_t allowed_versions)
 {
     vconn->class = class;
     vconn->state = (connect_status == EAGAIN ? VCS_CONNECTING
@@ -1033,7 +1098,7 @@ vconn_init(struct vconn *vconn, struct vconn_class *class, int connect_status,
                     : VCS_DISCONNECTED);
     vconn->error = connect_status;
     vconn->version = 0;
-    vconn->min_version = 0;
+    vconn->allowed_versions = allowed_versions;
     vconn->remote_ip = 0;
     vconn->remote_port = 0;
     vconn->local_ip = 0;
@@ -1067,9 +1132,10 @@ vconn_set_local_port(struct vconn *vconn, ovs_be16 port)
 }
 
 void
-pvconn_init(struct pvconn *pvconn, struct pvconn_class *class,
-            const char *name)
+pvconn_init(struct pvconn *pvconn,  struct pvconn_class *class,
+            const char *name, uint32_t allowed_versions)
 {
     pvconn->class = class;
     pvconn->name = xstrdup(name);
+    pvconn->allowed_versions = allowed_versions;
 }
index 1a0bc60..81bdcc9 100644 (file)
@@ -34,10 +34,13 @@ void vconn_usage(bool active, bool passive, bool bootstrap);
 
 /* Active vconns: virtual connections to OpenFlow devices. */
 int vconn_verify_name(const char *name);
-int vconn_open(const char *name, int min_version,
-               struct vconn **, uint8_t dscp);
+int vconn_open(const char *name, uint32_t allowed_versions, uint8_t dscp,
+               struct vconn **vconnp);
 void vconn_close(struct vconn *);
 const char *vconn_get_name(const struct vconn *);
+uint32_t vconn_get_allowed_versions(const struct vconn *vconn);
+void vconn_set_allowed_versions(struct vconn *vconn,
+                                uint32_t allowed_versions);
 ovs_be32 vconn_get_remote_ip(const struct vconn *);
 ovs_be16 vconn_get_remote_port(const struct vconn *);
 ovs_be32 vconn_get_local_ip(const struct vconn *);
@@ -55,7 +58,7 @@ int vconn_transact_multiple_noreply(struct vconn *, struct list *requests,
 void vconn_run(struct vconn *);
 void vconn_run_wait(struct vconn *);
 
-int vconn_open_block(const char *name, enum ofp_version min_version,
+int vconn_open_block(const char *name, uint32_t allowed_versions, uint8_t dscp,
                      struct vconn **);
 int vconn_connect_block(struct vconn *);
 int vconn_send_block(struct vconn *, struct ofpbuf *);
@@ -73,10 +76,11 @@ void vconn_send_wait(struct vconn *);
 
 /* Passive vconns: virtual listeners for incoming OpenFlow connections. */
 int pvconn_verify_name(const char *name);
-int pvconn_open(const char *name, struct pvconn **, uint8_t dscp);
+int pvconn_open(const char *name, uint32_t allowed_versions, uint8_t dscp,
+                struct pvconn **pvconnp);
 const char *pvconn_get_name(const struct pvconn *);
 void pvconn_close(struct pvconn *);
-int pvconn_accept(struct pvconn *, int min_version, struct vconn **);
+int pvconn_accept(struct pvconn *, struct vconn **);
 void pvconn_wait(struct pvconn *);
 
 #ifdef __cplusplus
index 567ac0e..4ead3e2 100644 (file)
@@ -1,3 +1,8 @@
+.de IQ
+.  br
+.  ns
+.  IP "\\$1"
+..
 .SS "VLOG COMMANDS"
 These commands manage \fB\*(PN\fR's logging settings.
 .IP "\fBvlog/set\fR [\fIspec\fR]"
@@ -48,3 +53,20 @@ after rotating log files, to cause a new log file to be used.)
 .IP
 This has no effect unless \fB\*(PN\fR was invoked with the
 \fB\-\-log\-file\fR option.
+.
+.IP "\fBvlog/disable\-rate\-limit \fR[\fImodule\fR]..."
+.IQ "\fBvlog/enable\-rate\-limit \fR[\fImodule\fR]..."
+By default, \fB\*(PN\fR limits the rate at which certain messages can
+be logged.  When a message would appear more frequently than the
+limit, it is suppressed.  This saves disk space, makes logs easier to
+read, and speeds up execution, but occasionally troubleshooting
+requires more detail.  Therefore, \fBvlog/disable\-rate\-limit\fR
+allows rate limits to be disabled at the level of an individual log
+module.  Specify one or more module names, as displayed by the
+\fBvlog/list\fR command.  Specifying either no module names at all or
+the keyword \fBany\fR disables rate limits for every log module.
+.
+.IP
+The \fBvlog/enable\-rate\-limit\fR command, whose syntax is the same
+as \fBvlog/disable\-rate\-limit\fR, can be used to re-enable a rate
+limit that was previously disabled.
index 0bd9bd1..2587cde 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -28,6 +28,7 @@
 #include <syslog.h>
 #include <time.h>
 #include <unistd.h>
+#include "coverage.h"
 #include "dirs.h"
 #include "dynamic-string.h"
 #include "ofpbuf.h"
@@ -40,6 +41,8 @@
 
 VLOG_DEFINE_THIS_MODULE(vlog);
 
+COVERAGE_DEFINE(vlog_recursive);
+
 /* Name for each logging level. */
 static const char *level_names[VLL_N_LEVELS] = {
 #define VLOG_LEVEL(NAME, SYSLOG_LEVEL) #NAME,
@@ -480,6 +483,55 @@ vlog_unixctl_reopen(struct unixctl_conn *conn, int argc OVS_UNUSED,
     }
 }
 
+static void
+set_all_rate_limits(bool enable)
+{
+    struct vlog_module **mp;
+
+    for (mp = vlog_modules; mp < &vlog_modules[n_vlog_modules]; mp++) {
+        (*mp)->honor_rate_limits = enable;
+    }
+}
+
+static void
+set_rate_limits(struct unixctl_conn *conn, int argc,
+                const char *argv[], bool enable)
+{
+    if (argc > 1) {
+        int i;
+
+        for (i = 1; i < argc; i++) {
+            if (!strcasecmp(argv[i], "ANY")) {
+                set_all_rate_limits(enable);
+            } else {
+                struct vlog_module *module = vlog_module_from_name(argv[i]);
+                if (!module) {
+                    unixctl_command_reply_error(conn, "unknown module");
+                    return;
+                }
+                module->honor_rate_limits = enable;
+            }
+        }
+    } else {
+        set_all_rate_limits(enable);
+    }
+    unixctl_command_reply(conn, NULL);
+}
+
+static void
+vlog_enable_rate_limit(struct unixctl_conn *conn, int argc,
+                       const char *argv[], void *aux OVS_UNUSED)
+{
+    set_rate_limits(conn, argc, argv, true);
+}
+
+static void
+vlog_disable_rate_limit(struct unixctl_conn *conn, int argc,
+                       const char *argv[], void *aux OVS_UNUSED)
+{
+    set_rate_limits(conn, argc, argv, false);
+}
+
 /* Initializes the logging subsystem and registers its unixctl server
  * commands. */
 void
@@ -515,6 +567,10 @@ vlog_init(void)
         "vlog/set", "{spec | PATTERN:facility:pattern}",
         1, INT_MAX, vlog_unixctl_set, NULL);
     unixctl_command_register("vlog/list", "", 0, 0, vlog_unixctl_list, NULL);
+    unixctl_command_register("vlog/enable-rate-limit", "[module]...",
+                             0, INT_MAX, vlog_enable_rate_limit, NULL);
+    unixctl_command_register("vlog/disable-rate-limit", "[module]...",
+                             0, INT_MAX, vlog_disable_rate_limit, NULL);
     unixctl_command_register("vlog/reopen", "", 0, 0,
                              vlog_unixctl_reopen, NULL);
 }
@@ -543,12 +599,20 @@ vlog_get_levels(void)
     ds_put_format(&s, "                 -------    ------    ------\n");
 
     for (mp = vlog_modules; mp < &vlog_modules[n_vlog_modules]; mp++) {
-        line = xasprintf("%-16s  %4s       %4s       %4s\n",
-           vlog_get_module_name(*mp),
-           vlog_get_level_name(vlog_get_level(*mp, VLF_CONSOLE)),
-           vlog_get_level_name(vlog_get_level(*mp, VLF_SYSLOG)),
-           vlog_get_level_name(vlog_get_level(*mp, VLF_FILE)));
-        svec_add_nocopy(&lines, line);
+        struct ds line;
+
+        ds_init(&line);
+        ds_put_format(&line, "%-16s  %4s       %4s       %4s",
+                      vlog_get_module_name(*mp),
+                      vlog_get_level_name(vlog_get_level(*mp, VLF_CONSOLE)),
+                      vlog_get_level_name(vlog_get_level(*mp, VLF_SYSLOG)),
+                      vlog_get_level_name(vlog_get_level(*mp, VLF_FILE)));
+        if (!(*mp)->honor_rate_limits) {
+            ds_put_cstr(&line, "    (rate limiting disabled)");
+        }
+        ds_put_char(&line, '\n');
+
+        svec_add_nocopy(&lines, ds_steal_cstr(&line));
     }
 
     svec_sort(&lines);
@@ -826,6 +890,10 @@ bool
 vlog_should_drop(const struct vlog_module *module, enum vlog_level level,
                  struct vlog_rate_limit *rl)
 {
+    if (!module->honor_rate_limits) {
+        return false;
+    }
+
     if (!vlog_is_enabled(module, level)) {
         return true;
     }
@@ -887,13 +955,26 @@ static void
 vlog_write_file(struct ds *s)
 {
     if (worker_is_running()) {
-        worker_request(s->string, s->length,
-                       &log_fd, vlog_async_inited ? 0 : 1,
-                       vlog_async_write_request_cb, NULL, NULL);
-        vlog_async_inited = true;
-    } else {
-        ignore(write(log_fd, s->string, s->length));
+        static bool in_worker_request = false;
+        if (!in_worker_request) {
+            in_worker_request = true;
+
+            worker_request(s->string, s->length,
+                           &log_fd, vlog_async_inited ? 0 : 1,
+                           vlog_async_write_request_cb, NULL, NULL);
+            vlog_async_inited = true;
+
+            in_worker_request = false;
+            return;
+        } else {
+            /* We've been entered recursively.  This can happen if
+             * worker_request(), or a function that it calls, tries to log
+             * something.  We can't call worker_request() recursively, so fall
+             * back to writing the log file directly. */
+            COVERAGE_INC(vlog_recursive);
+        }
     }
+    ignore(write(log_fd, s->string, s->length));
 }
 
 static void
index 9570b0e..2595772 100644 (file)
@@ -71,6 +71,7 @@ struct vlog_module {
     const char *name;             /* User-visible name. */
     int levels[VLF_N_FACILITIES]; /* Minimum log level for each facility. */
     int min_level;                /* Minimum log level for any facility. */
+    bool honor_rate_limits;       /* Set false to ignore rate limits. */
 };
 
 /* Creates and initializes a global instance of a module named MODULE. */
@@ -241,9 +242,10 @@ void vlog_usage(void);
         extern struct vlog_module VLM_##MODULE;                         \
         struct vlog_module VLM_##MODULE =                               \
         {                                                               \
-            #MODULE,                                      /* name */    \
+            #MODULE,                                        /* name */  \
             { [ 0 ... VLF_N_FACILITIES - 1] = VLL_INFO }, /* levels */  \
-            VLL_INFO,                                     /* min_level */ \
+            VLL_INFO,                                  /* min_level */  \
+            true                               /* honor_rate_limits */  \
         };
 
 #ifdef  __cplusplus
index f2b896e..b6d268d 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012 Nicira, Inc.
+/* Copyright (c) 2012, 2013 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -198,6 +198,7 @@ worker_send_iovec(const struct iovec iovs[], size_t n_iovs,
     size_t sent = 0;
 
     for (;;) {
+        struct pollfd pfd;
         int error;
 
         /* Try to send the rest of the request. */
@@ -210,8 +211,21 @@ worker_send_iovec(const struct iovec iovs[], size_t n_iovs,
         /* Process replies to avoid deadlock. */
         worker_run();
 
-        poll_fd_wait(client_sock, POLLIN | POLLOUT);
-        poll_block();
+        /* Wait for 'client_sock' to become ready before trying again.  We
+         * can't use poll_block() because it sometimes calls into vlog, which
+         * calls indirectly into worker_send_iovec().  To be usable here,
+         * poll_block() would therefore need to be reentrant, but it isn't
+         * (calling it recursively causes memory corruption and an eventual
+         * crash). */
+        pfd.fd = client_sock;
+        pfd.events = POLLIN | POLLOUT;
+        do {
+            error = poll(&pfd, 1, -1) < 0 ? errno : 0;
+        } while (error == EINTR);
+        if (error) {
+            worker_broke();
+            VLOG_ABORT("poll failed (%s)", strerror(error));
+        }
     }
 }
 
@@ -223,11 +237,14 @@ worker_request_iovec(const struct iovec iovs[], size_t n_iovs,
                      worker_request_func *request_cb,
                      worker_reply_func *reply_cb, void *aux)
 {
+    static bool recursing = false;
     struct worker_request rq;
     struct iovec *all_iovs;
     int error;
 
     assert(worker_is_running());
+    assert(!recursing);
+    recursing = true;
 
     rq.request_len = iovec_len(iovs, n_iovs);
     rq.request_cb = request_cb;
@@ -241,6 +258,8 @@ worker_request_iovec(const struct iovec iovs[], size_t n_iovs,
         VLOG_ABORT("send failed (%s)", strerror(error));
     }
     free(all_iovs);
+
+    recursing = false;
 }
 
 /* Closes the client socket, if any, so that worker_is_running() will return
@@ -428,8 +447,6 @@ rxbuf_run(struct rxbuf *rx, int sock, size_t header_len)
             }
         }
     }
-
-    return EAGAIN;
 }
 
 static struct iovec *
index 7469011..59cc933 100644 (file)
@@ -390,23 +390,3 @@ AC_DEFUN([OVS_CHECK_GROFF],
        ovs_cv_groff=no
      fi])
    AM_CONDITIONAL([HAVE_GROFF], [test "$ovs_cv_groff" = yes])])
-
-dnl Checks for --disable-brcompat and undefines BUILD_BRCOMPAT if it is specified.
-AC_DEFUN([OVS_CHECK_BRCOMPAT],
-  [AC_ARG_ENABLE(
-     [brcompat],
-     [AC_HELP_STRING([--disable-brcompat],
-                     [Disable building brcompat])],
-     [case "${enableval}" in
-        (yes) brcompat=true ;;
-        (no)  brcompat=false ;;
-        (*) AC_MSG_ERROR([bad value ${enableval} for --enable-brcompat]) ;;
-      esac],
-     [brcompat=true])
-   if test x$brcompat = xtrue; then
-      BUILD_BRCOMPAT=yes
-   else
-      BUILD_BRCOMPAT=""
-   fi
-   AC_SUBST([BUILD_BRCOMPAT])
-   AM_CONDITIONAL([BUILD_BRCOMPAT], [test x$brcompat = xtrue])])
index 01700c3..f89fc17 100644 (file)
@@ -82,6 +82,10 @@ lib/common.man:
 lib/vlog-syn.man:
 lib/vlog.man:
 
+utilities/bugtool/ovs-bugtool.8: \
+       utilities/bugtool/ovs-bugtool.8.in
+utilities/bugtool/ovs-bugtool.8.in:
+
 utilities/ovs-appctl.8: \
        utilities/ovs-appctl.8.in \
        lib/common.man
@@ -134,12 +138,14 @@ utilities/ovs-ofctl.8: \
        utilities/ovs-ofctl.8.in \
        lib/common.man \
        lib/daemon.man \
+       lib/ofp-version.man \
        lib/ssl.man \
        lib/vconn-active.man \
        lib/vlog.man
 utilities/ovs-ofctl.8.in:
 lib/common.man:
 lib/daemon.man:
+lib/ofp-version.man:
 lib/ssl.man:
 lib/vconn-active.man:
 lib/vlog.man:
@@ -218,18 +224,6 @@ ovsdb/remote-active.man:
 ovsdb/remote-passive.man:
 ovsdb/remote-passive.man:
 
-vswitchd/ovs-brcompatd.8: \
-       vswitchd/ovs-brcompatd.8.in \
-       lib/common.man \
-       lib/daemon.man \
-       lib/leak-checker.man \
-       lib/vlog.man
-vswitchd/ovs-brcompatd.8.in:
-lib/common.man:
-lib/daemon.man:
-lib/leak-checker.man:
-lib/vlog.man:
-
 vswitchd/ovs-vswitchd.8: \
        vswitchd/ovs-vswitchd.8.in \
        lib/common.man \
@@ -241,6 +235,7 @@ vswitchd/ovs-vswitchd.8: \
        lib/stress-unixctl.man \
        lib/vlog-unixctl.man \
        lib/vlog.man \
+       ofproto/ofproto-dpif-unixctl.man \
        ofproto/ofproto-unixctl.man \
        ovsdb/remote-active.man \
        ovsdb/remote-passive.man
@@ -254,6 +249,7 @@ lib/ssl.man:
 lib/stress-unixctl.man:
 lib/vlog-unixctl.man:
 lib/vlog.man:
+ofproto/ofproto-dpif-unixctl.man:
 ofproto/ofproto-unixctl.man:
 ovsdb/remote-active.man:
 ovsdb/remote-passive.man:
index ab889af..9088292 100644 (file)
@@ -31,4 +31,4 @@ ofproto_libofproto_a_SOURCES = \
        ofproto/pinsched.c \
        ofproto/pinsched.h
 
-MAN_FRAGMENTS += ofproto/ofproto-unixctl.man
+MAN_FRAGMENTS += ofproto/ofproto-unixctl.man ofproto/ofproto-dpif-unixctl.man
index 05e69c7..56971ce 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -133,11 +133,14 @@ struct ofservice {
     int burst_limit;            /* Limit on accumulating packet credits. */
     bool enable_async_msgs;     /* Initially enable async messages? */
     uint8_t dscp;               /* DSCP Value for controller connection */
+    uint32_t allowed_versions;  /* OpenFlow protocol versions that may
+                                 * be negotiated for a session. */
 };
 
 static void ofservice_reconfigure(struct ofservice *,
                                   const struct ofproto_controller *);
-static int ofservice_create(struct connmgr *, const char *target, uint8_t dscp);
+static int ofservice_create(struct connmgr *mgr, const char *target,
+                            uint32_t allowed_versions, uint8_t dscp);
 static void ofservice_destroy(struct connmgr *, struct ofservice *);
 static struct ofservice *ofservice_lookup(struct connmgr *,
                                           const char *target);
@@ -151,6 +154,9 @@ struct connmgr {
     /* OpenFlow connections. */
     struct hmap controllers;   /* Controller "struct ofconn"s. */
     struct list all_conns;     /* Contains "struct ofconn"s. */
+    uint64_t master_election_id; /* monotonically increasing sequence number
+                                  * for master election */
+    bool master_election_id_defined;
 
     /* OpenFlow listeners. */
     struct hmap services;       /* Contains "struct ofservice"s. */
@@ -190,6 +196,8 @@ connmgr_create(struct ofproto *ofproto,
 
     hmap_init(&mgr->controllers);
     list_init(&mgr->all_conns);
+    mgr->master_election_id = 0;
+    mgr->master_election_id_defined = false;
 
     hmap_init(&mgr->services);
     mgr->snoops = NULL;
@@ -289,13 +297,14 @@ connmgr_run(struct connmgr *mgr,
         struct vconn *vconn;
         int retval;
 
-        retval = pvconn_accept(ofservice->pvconn, OFP10_VERSION, &vconn);
+        retval = pvconn_accept(ofservice->pvconn, &vconn);
         if (!retval) {
             struct rconn *rconn;
             char *name;
 
             /* Passing default value for creation of the rconn */
-            rconn = rconn_create(ofservice->probe_interval, 0, ofservice->dscp);
+            rconn = rconn_create(ofservice->probe_interval, 0, ofservice->dscp,
+                                 vconn_get_allowed_versions(vconn));
             name = ofconn_make_name(mgr, vconn_get_name(vconn));
             rconn_connect_unreliably(rconn, vconn, name);
             free(name);
@@ -313,7 +322,7 @@ connmgr_run(struct connmgr *mgr,
         struct vconn *vconn;
         int retval;
 
-        retval = pvconn_accept(mgr->snoops[i], OFP10_VERSION, &vconn);
+        retval = pvconn_accept(mgr->snoops[i], &vconn);
         if (!retval) {
             add_snooper(mgr, vconn);
         } else if (retval != EAGAIN) {
@@ -398,7 +407,8 @@ connmgr_retry(struct connmgr *mgr)
 \f
 /* OpenFlow configuration. */
 
-static void add_controller(struct connmgr *, const char *target, uint8_t dscp);
+static void add_controller(struct connmgr *, const char *target, uint8_t dscp,
+                           uint32_t allowed_versions);
 static struct ofconn *find_controller_by_target(struct connmgr *,
                                                 const char *target);
 static void update_fail_open(struct connmgr *);
@@ -489,7 +499,7 @@ connmgr_free_controller_info(struct shash *info)
 void
 connmgr_set_controllers(struct connmgr *mgr,
                         const struct ofproto_controller *controllers,
-                        size_t n_controllers)
+                        size_t n_controllers, uint32_t allowed_versions)
 {
     bool had_controllers = connmgr_has_controllers(mgr);
     struct shash new_controllers;
@@ -504,16 +514,37 @@ connmgr_set_controllers(struct connmgr *mgr,
         const struct ofproto_controller *c = &controllers[i];
 
         if (!vconn_verify_name(c->target)) {
-            if (!find_controller_by_target(mgr, c->target)) {
+            bool add = false;
+            ofconn = find_controller_by_target(mgr, c->target);
+            if (!ofconn) {
                 VLOG_INFO("%s: added primary controller \"%s\"",
                           mgr->name, c->target);
-                add_controller(mgr, c->target, c->dscp);
+                add = true;
+            } else if (rconn_get_allowed_versions(ofconn->rconn) !=
+                       allowed_versions) {
+                VLOG_INFO("%s: re-added primary controller \"%s\"",
+                          mgr->name, c->target);
+                add = true;
+                ofconn_destroy(ofconn);
+            }
+            if (add) {
+                add_controller(mgr, c->target, c->dscp, allowed_versions);
             }
         } else if (!pvconn_verify_name(c->target)) {
-            if (!ofservice_lookup(mgr, c->target)) {
+            bool add = false;
+            ofservice = ofservice_lookup(mgr, c->target);
+            if (!ofservice) {
                 VLOG_INFO("%s: added service controller \"%s\"",
                           mgr->name, c->target);
-                ofservice_create(mgr, c->target, c->dscp);
+                add = true;
+            } else if (ofservice->allowed_versions != allowed_versions) {
+                VLOG_INFO("%s: re-added service controller \"%s\"",
+                          mgr->name, c->target);
+                ofservice_destroy(mgr, ofservice);
+                add = true;
+            }
+            if (add) {
+                ofservice_create(mgr, c->target, allowed_versions, c->dscp);
             }
         } else {
             VLOG_WARN_RL(&rl, "%s: unsupported controller \"%s\"",
@@ -608,12 +639,14 @@ connmgr_has_snoops(const struct connmgr *mgr)
 /* Creates a new controller for 'target' in 'mgr'.  update_controller() needs
  * to be called later to finish the new ofconn's configuration. */
 static void
-add_controller(struct connmgr *mgr, const char *target, uint8_t dscp)
+add_controller(struct connmgr *mgr, const char *target, uint8_t dscp,
+               uint32_t allowed_versions)
 {
     char *name = ofconn_make_name(mgr, target);
     struct ofconn *ofconn;
 
-    ofconn = ofconn_create(mgr, rconn_create(5, 8, dscp), OFCONN_PRIMARY, true);
+    ofconn = ofconn_create(mgr, rconn_create(5, 8, dscp, allowed_versions),
+                           OFCONN_PRIMARY, true);
     ofconn->pktbuf = pktbuf_create();
     rconn_connect(ofconn->rconn, target, name);
     hmap_insert(&mgr->controllers, &ofconn->hmap_node, hash_string(target, 0));
@@ -720,8 +753,7 @@ set_pvconns(struct pvconn ***pvconnsp, size_t *n_pvconnsp,
     SSET_FOR_EACH (name, sset) {
         struct pvconn *pvconn;
         int error;
-
-        error = pvconn_open(name, &pvconn, 0);
+        error = pvconn_open(name, 0, 0, &pvconn);
         if (!error) {
             pvconns[n_pvconns++] = pvconn;
         } else {
@@ -790,6 +822,26 @@ ofconn_get_type(const struct ofconn *ofconn)
     return ofconn->type;
 }
 
+/* Sets the master election id.
+ *
+ * Returns true if successful, false if the id is stale
+ */
+bool
+ofconn_set_master_election_id(struct ofconn *ofconn, uint64_t id)
+{
+    if (ofconn->connmgr->master_election_id_defined
+        &&
+        /* Unsigned difference interpreted as a two's complement signed
+         * value */
+        (int64_t)(id - ofconn->connmgr->master_election_id) < 0) {
+        return false;
+    }
+    ofconn->connmgr->master_election_id = id;
+    ofconn->connmgr->master_election_id_defined = true;
+
+    return true;
+}
+
 /* Returns the role configured for 'ofconn'.
  *
  * The default role, if no other role has been set, is NX_ROLE_OTHER. */
@@ -836,10 +888,24 @@ ofconn_get_invalid_ttl_to_controller(struct ofconn *ofconn)
 
 /* Returns the currently configured protocol for 'ofconn', one of OFPUTIL_P_*.
  *
- * The default, if no other format has been set, is OFPUTIL_P_OPENFLOW10. */
+ * Returns OFPUTIL_P_NONE, which is not a valid protocol, if 'ofconn' hasn't
+ * completed version negotiation.  This can't happen if at least one OpenFlow
+ * message, other than OFPT_HELLO, has been received on the connection (such as
+ * in ofproto.c's message handling code), since version negotiation is a
+ * prerequisite for starting to receive messages.  This means that
+ * OFPUTIL_P_NONE is a special case that most callers need not worry about. */
 enum ofputil_protocol
-ofconn_get_protocol(struct ofconn *ofconn)
-{
+ofconn_get_protocol(const struct ofconn *ofconn)
+{
+    if (ofconn->protocol == OFPUTIL_P_NONE &&
+        rconn_is_connected(ofconn->rconn)) {
+        int version = rconn_get_version(ofconn->rconn);
+        if (version > 0) {
+            ofconn_set_protocol(CONST_CAST(struct ofconn *, ofconn),
+                                ofputil_protocol_from_ofp_version(version));
+        }
+    }
+
     return ofconn->protocol;
 }
 
@@ -935,29 +1001,26 @@ void
 ofconn_send_error(const struct ofconn *ofconn,
                   const struct ofp_header *request, enum ofperr error)
 {
+    static struct vlog_rate_limit err_rl = VLOG_RATE_LIMIT_INIT(10, 10);
     struct ofpbuf *reply;
 
     reply = ofperr_encode_reply(error, request);
-    if (reply) {
-        static struct vlog_rate_limit err_rl = VLOG_RATE_LIMIT_INIT(10, 10);
-
-        if (!VLOG_DROP_INFO(&err_rl)) {
-            const char *type_name;
-            size_t request_len;
-            enum ofpraw raw;
-
-            request_len = ntohs(request->length);
-            type_name = (!ofpraw_decode_partial(&raw, request,
-                                                MIN(64, request_len))
-                         ? ofpraw_get_name(raw)
-                         : "invalid");
-
-            VLOG_INFO("%s: sending %s error reply to %s message",
-                      rconn_get_name(ofconn->rconn), ofperr_to_string(error),
-                      type_name);
-        }
-        ofconn_send_reply(ofconn, reply);
+    if (!VLOG_DROP_INFO(&err_rl)) {
+        const char *type_name;
+        size_t request_len;
+        enum ofpraw raw;
+
+        request_len = ntohs(request->length);
+        type_name = (!ofpraw_decode_partial(&raw, request,
+                                            MIN(64, request_len))
+                     ? ofpraw_get_name(raw)
+                     : "invalid");
+
+        VLOG_INFO("%s: sending %s error reply to %s message",
+                  rconn_get_name(ofconn->rconn), ofperr_to_string(error),
+                  type_name);
     }
+    ofconn_send_reply(ofconn, reply);
 }
 
 /* Same as pktbuf_retrieve(), using the pktbuf owned by 'ofconn'. */
@@ -1030,7 +1093,7 @@ ofconn_flush(struct ofconn *ofconn)
     int i;
 
     ofconn->role = NX_ROLE_OTHER;
-    ofconn->protocol = OFPUTIL_P_OF10;
+    ofconn_set_protocol(ofconn, OFPUTIL_P_NONE);
     ofconn->packet_in_format = NXPIF_OPENFLOW10;
 
     /* Disassociate 'ofconn' from all of the ofopgroups that it initiated that
@@ -1230,7 +1293,8 @@ ofconn_receives_async_msg(const struct ofconn *ofconn,
     assert(reason < 32);
     assert((unsigned int) type < OAM_N_TYPES);
 
-    if (!rconn_is_connected(ofconn->rconn)) {
+    if (ofconn_get_protocol(ofconn) == OFPUTIL_P_NONE
+        || !rconn_is_connected(ofconn->rconn)) {
         return false;
     }
 
@@ -1313,7 +1377,7 @@ connmgr_send_port_status(struct connmgr *mgr,
         if (ofconn_receives_async_msg(ofconn, OAM_PORT_STATUS, reason)) {
             struct ofpbuf *msg;
 
-            msg = ofputil_encode_port_status(&ps, ofconn->protocol);
+            msg = ofputil_encode_port_status(&ps, ofconn_get_protocol(ofconn));
             ofconn_send(ofconn, msg, NULL);
         }
     }
@@ -1336,7 +1400,7 @@ connmgr_send_flow_removed(struct connmgr *mgr,
              * also prevents new flows from being added (and expiring).  (It
              * also prevents processing OpenFlow requests that would not add
              * new flows, so it is imperfect.) */
-            msg = ofputil_encode_flow_removed(fr, ofconn->protocol);
+            msg = ofputil_encode_flow_removed(fr, ofconn_get_protocol(ofconn));
             ofconn_send_reply(ofconn, msg);
         }
     }
@@ -1407,7 +1471,7 @@ schedule_packet_in(struct ofconn *ofconn, struct ofputil_packet_in pin)
      * while (until a later call to pinsched_run()). */
     pinsched_send(ofconn->schedulers[pin.reason == OFPR_NO_MATCH ? 0 : 1],
                   pin.fmd.in_port,
-                  ofputil_encode_packet_in(&pin, ofconn->protocol,
+                  ofputil_encode_packet_in(&pin, ofconn_get_protocol(ofconn),
                                            ofconn->packet_in_format),
                   do_send_packet_in, ofconn);
 }
@@ -1574,10 +1638,12 @@ connmgr_msg_in_hook(struct connmgr *mgr, const struct flow *flow,
 
 bool
 connmgr_may_set_up_flow(struct connmgr *mgr, const struct flow *flow,
+                        uint32_t local_odp_port,
                         const struct nlattr *odp_actions,
                         size_t actions_len)
 {
-    return !mgr->in_band || in_band_rule_check(flow, odp_actions, actions_len);
+    return !mgr->in_band || in_band_rule_check(flow, local_odp_port,
+                                               odp_actions, actions_len);
 }
 \f
 /* Fail-open and in-band implementation. */
@@ -1619,13 +1685,14 @@ connmgr_flushed(struct connmgr *mgr)
  * ofservice_reconfigure() must be called to fully configure the new
  * ofservice. */
 static int
-ofservice_create(struct connmgr *mgr, const char *target, uint8_t dscp)
+ofservice_create(struct connmgr *mgr, const char *target,
+                 uint32_t allowed_versions, uint8_t dscp)
 {
     struct ofservice *ofservice;
     struct pvconn *pvconn;
     int error;
 
-    error = pvconn_open(target, &pvconn, dscp);
+    error = pvconn_open(target, allowed_versions, dscp, &pvconn);
     if (error) {
         return error;
     }
@@ -1633,6 +1700,7 @@ ofservice_create(struct connmgr *mgr, const char *target, uint8_t dscp)
     ofservice = xzalloc(sizeof *ofservice);
     hmap_insert(&mgr->services, &ofservice->node, hash_string(target, 0));
     ofservice->pvconn = pvconn;
+    ofservice->allowed_versions = allowed_versions;
 
     return 0;
 }
@@ -1741,6 +1809,7 @@ void
 ofmonitor_destroy(struct ofmonitor *m)
 {
     if (m) {
+        minimatch_destroy(&m->match);
         hmap_remove(&m->ofconn->monitors, &m->ofconn_node);
         free(m);
     }
index 9a080f2..6ce413e 100644 (file)
@@ -85,7 +85,8 @@ bool connmgr_has_controllers(const struct connmgr *);
 void connmgr_get_controller_info(struct connmgr *, struct shash *);
 void connmgr_free_controller_info(struct shash *);
 void connmgr_set_controllers(struct connmgr *,
-                             const struct ofproto_controller[], size_t n);
+                             const struct ofproto_controller[], size_t n,
+                             uint32_t allowed_versions);
 void connmgr_reconnect(const struct connmgr *);
 
 int connmgr_set_snoops(struct connmgr *, const struct sset *snoops);
@@ -95,10 +96,11 @@ void connmgr_get_snoops(const struct connmgr *, struct sset *snoops);
 /* Individual connections to OpenFlow controllers. */
 enum ofconn_type ofconn_get_type(const struct ofconn *);
 
+bool ofconn_set_master_election_id(struct ofconn *, uint64_t);
 enum nx_role ofconn_get_role(const struct ofconn *);
 void ofconn_set_role(struct ofconn *, enum nx_role);
 
-enum ofputil_protocol ofconn_get_protocol(struct ofconn *);
+enum ofputil_protocol ofconn_get_protocol(const struct ofconn *);
 void ofconn_set_protocol(struct ofconn *, enum ofputil_protocol);
 
 enum nx_packet_in_format ofconn_get_packet_in_format(struct ofconn *);
@@ -156,6 +158,7 @@ void connmgr_set_in_band_queue(struct connmgr *, int queue_id);
 bool connmgr_msg_in_hook(struct connmgr *, const struct flow *,
                          const struct ofpbuf *packet);
 bool connmgr_may_set_up_flow(struct connmgr *, const struct flow *,
+                             uint32_t local_odp_port,
                              const struct nlattr *odp_actions,
                              size_t actions_len);
 
index 0e4754c..3b98005 100644 (file)
@@ -120,7 +120,7 @@ refresh_remote(struct in_band *ib, struct in_band_remote *r)
                                  &next_hop_inaddr, &next_hop_dev);
     if (retval) {
         VLOG_WARN("cannot find route for controller ("IP_FMT"): %s",
-                  IP_ARGS(&r->remote_addr.sin_addr), strerror(retval));
+                  IP_ARGS(r->remote_addr.sin_addr.s_addr), strerror(retval));
         return 1;
     }
     if (!next_hop_inaddr.s_addr) {
@@ -137,7 +137,7 @@ refresh_remote(struct in_band *ib, struct in_band_remote *r)
         if (retval) {
             VLOG_WARN_RL(&rl, "cannot open netdev %s (next hop "
                          "to controller "IP_FMT"): %s",
-                         next_hop_dev, IP_ARGS(&r->remote_addr.sin_addr),
+                         next_hop_dev, IP_ARGS(r->remote_addr.sin_addr.s_addr),
                          strerror(retval));
             free(next_hop_dev);
             return 1;
@@ -150,7 +150,7 @@ refresh_remote(struct in_band *ib, struct in_band_remote *r)
                                r->remote_mac);
     if (retval) {
         VLOG_DBG_RL(&rl, "cannot look up remote MAC address ("IP_FMT"): %s",
-                    IP_ARGS(&next_hop_inaddr.s_addr), strerror(retval));
+                    IP_ARGS(next_hop_inaddr.s_addr), strerror(retval));
     }
 
     /* If we don't have a MAC address, then refresh quickly, since we probably
@@ -256,7 +256,7 @@ in_band_msg_in_hook(struct in_band *in_band, const struct flow *flow,
 /* Returns true if the rule that would match 'flow' with 'actions' is
  * allowed to be set up in the datapath. */
 bool
-in_band_rule_check(const struct flow *flow,
+in_band_rule_check(const struct flow *flow, uint32_t local_odp_port,
                    const struct nlattr *actions, size_t actions_len)
 {
     /* Don't allow flows that would prevent DHCP replies from being seen
@@ -270,7 +270,7 @@ in_band_rule_check(const struct flow *flow,
 
         NL_ATTR_FOR_EACH_UNSAFE (a, left, actions, actions_len) {
             if (nl_attr_type(a) == OVS_ACTION_ATTR_OUTPUT
-                && nl_attr_get_u32(a) == OVSP_LOCAL) {
+                && nl_attr_get_u32(a) == local_odp_port) {
                 return true;
             }
         }
index 7b610cb..71de6ff 100644 (file)
@@ -41,7 +41,7 @@ void in_band_wait(struct in_band *);
 
 bool in_band_msg_in_hook(struct in_band *, const struct flow *,
                          const struct ofpbuf *packet);
-bool in_band_rule_check(const struct flow *,
+bool in_band_rule_check(const struct flow *, uint32_t local_odp_port,
                         const struct nlattr *odp_actions, size_t actions_len);
 
 #endif /* in-band.h */
index 506dadb..a2ada30 100644 (file)
@@ -71,6 +71,7 @@ governor_destroy(struct governor *g)
 {
     if (g) {
         VLOG_INFO("%s: disengaging", g->name);
+        free(g->name);
         free(g->table);
         free(g);
     }
index 23f5498..69362ab 100644 (file)
@@ -46,13 +46,13 @@ struct dpif_sflow_port {
     struct hmap_node hmap_node; /* In struct dpif_sflow's "ports" hmap. */
     SFLDataSource_instance dsi; /* sFlow library's notion of port number. */
     struct ofport *ofport;      /* To retrive port stats. */
+    uint32_t odp_port;
 };
 
 struct dpif_sflow {
     struct collectors *collectors;
     SFLAgent *sflow_agent;
     struct ofproto_sflow_options *options;
-    struct dpif *dpif;
     time_t next_tick;
     size_t n_flood, n_all;
     struct hmap ports;          /* Contains "struct dpif_sflow_port"s. */
@@ -142,13 +142,13 @@ sflow_agent_send_packet_cb(void *ds_, SFLAgent *agent OVS_UNUSED,
 }
 
 static struct dpif_sflow_port *
-dpif_sflow_find_port(const struct dpif_sflow *ds, uint16_t odp_port)
+dpif_sflow_find_port(const struct dpif_sflow *ds, uint32_t odp_port)
 {
     struct dpif_sflow_port *dsp;
 
     HMAP_FOR_EACH_IN_BUCKET (dsp, hmap_node,
                              hash_int(odp_port, 0), &ds->ports) {
-        if (ofp_port_to_odp_port(dsp->ofport->ofp_port) == odp_port) {
+        if (dsp->odp_port == odp_port) {
             return dsp;
         }
     }
@@ -179,7 +179,7 @@ sflow_agent_get_counters(void *ds_, SFLPoller *poller,
     if (!netdev_get_features(dsp->ofport->netdev, &current, NULL, NULL, NULL)) {
         /* The values of ifDirection come from MAU MIB (RFC 2668): 0 = unknown,
            1 = full-duplex, 2 = half-duplex, 3 = in, 4=out */
-        counters->ifSpeed = netdev_features_to_bps(current);
+        counters->ifSpeed = netdev_features_to_bps(current, 0);
         counters->ifDirection = (netdev_features_is_full_duplex(current)
                                  ? 1 : 2);
     } else {
@@ -293,12 +293,11 @@ dpif_sflow_is_enabled(const struct dpif_sflow *ds)
 }
 
 struct dpif_sflow *
-dpif_sflow_create(struct dpif *dpif)
+dpif_sflow_create(void)
 {
     struct dpif_sflow *ds;
 
     ds = xcalloc(1, sizeof *ds);
-    ds->dpif = dpif;
     ds->next_tick = time_now() + 1;
     hmap_init(&ds->ports);
     ds->probability = 0;
@@ -339,8 +338,7 @@ dpif_sflow_add_poller(struct dpif_sflow *ds, struct dpif_sflow_port *dsp)
                                             sflow_agent_get_counters);
     sfl_poller_set_sFlowCpInterval(poller, ds->options->polling_interval);
     sfl_poller_set_sFlowCpReceiver(poller, RECEIVER_INDEX);
-    sfl_poller_set_bridgePort(poller,
-                              ofp_port_to_odp_port(dsp->ofport->ofp_port));
+    sfl_poller_set_bridgePort(poller, dsp->odp_port);
 }
 
 static void
@@ -353,10 +351,10 @@ dpif_sflow_add_sampler(struct dpif_sflow *ds, struct dpif_sflow_port *dsp)
 }
 
 void
-dpif_sflow_add_port(struct dpif_sflow *ds, struct ofport *ofport)
+dpif_sflow_add_port(struct dpif_sflow *ds, struct ofport *ofport,
+                    uint32_t odp_port)
 {
     struct dpif_sflow_port *dsp;
-    uint16_t odp_port = ofp_port_to_odp_port(ofport->ofp_port);
     uint32_t ifindex;
 
     dpif_sflow_del_port(ds, odp_port);
@@ -368,6 +366,7 @@ dpif_sflow_add_port(struct dpif_sflow *ds, struct ofport *ofport)
         ifindex = (ds->sflow_agent->subId << 16) + odp_port;
     }
     dsp->ofport = ofport;
+    dsp->odp_port = odp_port;
     SFL_DS_SET(dsp->dsi, 0, ifindex, 0);
     hmap_insert(&ds->ports, &dsp->hmap_node, hash_int(odp_port, 0));
 
@@ -390,7 +389,7 @@ dpif_sflow_del_port__(struct dpif_sflow *ds, struct dpif_sflow_port *dsp)
 }
 
 void
-dpif_sflow_del_port(struct dpif_sflow *ds, uint16_t odp_port)
+dpif_sflow_del_port(struct dpif_sflow *ds, uint32_t odp_port)
 {
     struct dpif_sflow_port *dsp = dpif_sflow_find_port(ds, odp_port);
     if (dsp) {
@@ -483,7 +482,7 @@ dpif_sflow_set_options(struct dpif_sflow *ds,
 
 int
 dpif_sflow_odp_port_to_ifindex(const struct dpif_sflow *ds,
-                               uint16_t odp_port)
+                               uint32_t odp_port)
 {
     struct dpif_sflow_port *dsp = dpif_sflow_find_port(ds, odp_port);
     return dsp ? SFL_DS_INDEX(dsp->dsi) : 0;
@@ -491,7 +490,7 @@ dpif_sflow_odp_port_to_ifindex(const struct dpif_sflow *ds,
 
 void
 dpif_sflow_received(struct dpif_sflow *ds, struct ofpbuf *packet,
-                    const struct flow *flow,
+                    const struct flow *flow, uint32_t odp_in_port,
                     const union user_action_cookie *cookie)
 {
     SFL_FLOW_SAMPLE_TYPE fs;
@@ -507,7 +506,7 @@ dpif_sflow_received(struct dpif_sflow *ds, struct ofpbuf *packet,
     /* Build a flow sample */
     memset(&fs, 0, sizeof fs);
 
-    in_dsp = dpif_sflow_find_port(ds, ofp_port_to_odp_port(flow->in_port));
+    in_dsp = dpif_sflow_find_port(ds, odp_in_port);
     if (!in_dsp) {
         return;
     }
index 6af8dc0..02a0f17 100644 (file)
@@ -28,7 +28,7 @@ struct flow;
 struct ofproto_sflow_options;
 struct ofport;
 
-struct dpif_sflow *dpif_sflow_create(struct dpif *);
+struct dpif_sflow *dpif_sflow_create(void);
 uint32_t dpif_sflow_get_probability(const struct dpif_sflow *);
 
 void dpif_sflow_destroy(struct dpif_sflow *);
@@ -37,8 +37,9 @@ void dpif_sflow_set_options(struct dpif_sflow *,
 void dpif_sflow_clear(struct dpif_sflow *);
 bool dpif_sflow_is_enabled(const struct dpif_sflow *);
 
-void dpif_sflow_add_port(struct dpif_sflow *ds, struct ofport *ofport);
-void dpif_sflow_del_port(struct dpif_sflow *, uint16_t ovs_port);
+void dpif_sflow_add_port(struct dpif_sflow *ds, struct ofport *ofport,
+                         uint32_t odp_port);
+void dpif_sflow_del_port(struct dpif_sflow *, uint32_t odp_port);
 
 void dpif_sflow_run(struct dpif_sflow *);
 void dpif_sflow_wait(struct dpif_sflow *);
@@ -46,8 +47,9 @@ void dpif_sflow_wait(struct dpif_sflow *);
 void dpif_sflow_received(struct dpif_sflow *,
                          struct ofpbuf *,
                          const struct flow *,
+                         uint32_t odp_port,
                          const union user_action_cookie *);
 
-int dpif_sflow_odp_port_to_ifindex(const struct dpif_sflow *, uint16_t);
+int dpif_sflow_odp_port_to_ifindex(const struct dpif_sflow *, uint32_t);
 
 #endif /* ofproto/ofproto-dpif-sflow.h */
diff --git a/ofproto/ofproto-dpif-unixctl.man b/ofproto/ofproto-dpif-unixctl.man
new file mode 100644 (file)
index 0000000..daacd3a
--- /dev/null
@@ -0,0 +1,34 @@
+.SS "DATAPATH COMMANDS"
+These commands manage logical datapaths.  They are are similar to the
+equivalent \fBovs\-dpctl\fR commands.
+.
+.IP "\fBdpif/dump\-dps\fR"
+Prints the name of each configured datapath on a separate line.
+.
+.IP "\fBdpif/show\fR [\fIdp\fR...]"
+Prints a summary of configured datapaths, including statistics and a
+list of connected ports.  The port information includes the OpenFlow
+port number, datapath port number, and the type.  (The local port is
+identified as OpenFlow port 65534.)
+.IP
+If one or more datapaths are specified, information on only those
+datapaths are displayed.  Otherwise, information about all configured
+datapaths are shown.
+.
+.IP "\fBdpif/dump\-flows \fIdp\fR"
+Prints to the console all flow entries in datapath \fIdp\fR's
+flow table.
+.IP
+This command is primarily useful for debugging Open vSwitch.  The flow
+table entries that it displays are not OpenFlow flow entries.  Instead,
+they are different and considerably simpler flows maintained by the
+datapath module.  If you wish to see the OpenFlow flow entries, use
+\fBovs\-ofctl dump\-flows\fR.
+.
+.IP "\fBdpif/del\-flows \fIdp\fR"
+Deletes all flow entries from datapath \fIdp\fR's flow table and
+underlying datapath implementation (e.g., kernel datapath module).
+.IP
+This command is primarily useful for debugging Open vSwitch.  As
+discussed in \fBdpif/dump\-flows\fR, these entries are
+not OpenFlow flow entries.
index fea4dac..55b4e48 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -49,6 +49,7 @@
 #include "ofproto-dpif-sflow.h"
 #include "poll-loop.h"
 #include "simap.h"
+#include "smap.h"
 #include "timer.h"
 #include "unaligned.h"
 #include "unixctl.h"
@@ -75,6 +76,7 @@ BUILD_ASSERT_DECL(N_TABLES >= 2 && N_TABLES <= 255);
 
 struct ofport_dpif;
 struct ofproto_dpif;
+struct flow_miss;
 
 struct rule_dpif {
     struct rule up;
@@ -277,7 +279,7 @@ struct action_xlate_ctx {
     uint32_t orig_skb_priority; /* Priority when packet arrived. */
     uint8_t table_id;           /* OpenFlow table ID where flow was found. */
     uint32_t sflow_n_outputs;   /* Number of output ports. */
-    uint16_t sflow_odp_port;    /* Output port for composing sFlow action. */
+    uint32_t sflow_odp_port;    /* Output port for composing sFlow action. */
     uint16_t user_cookie_offset;/* Used for user_action_cookie fixup. */
     bool exit;                  /* No further actions should be processed. */
     struct flow orig_flow;      /* Copy of original flow. */
@@ -365,16 +367,27 @@ struct subfacet {
      * splinters can cause it to differ.  This value should be removed when
      * the VLAN splinters feature is no longer needed.  */
     ovs_be16 initial_tci;       /* Initial VLAN TCI value. */
+
+    /* Datapath port the packet arrived on.  This is needed to remove
+     * flows for ports that are no longer part of the bridge.  Since the
+     * flow definition only has the OpenFlow port number and the port is
+     * no longer part of the bridge, we can't determine the datapath port
+     * number needed to delete the flow from the datapath. */
+    uint32_t odp_in_port;
 };
 
-static struct subfacet *subfacet_create(struct facet *, enum odp_key_fitness,
-                                        const struct nlattr *key,
-                                        size_t key_len, ovs_be16 initial_tci,
+#define SUBFACET_DESTROY_MAX_BATCH 50
+
+static struct subfacet *subfacet_create(struct facet *, struct flow_miss *miss,
                                         long long int now);
 static struct subfacet *subfacet_find(struct ofproto_dpif *,
-                                      const struct nlattr *key, size_t key_len);
+                                      const struct nlattr *key, size_t key_len,
+                                      uint32_t key_hash,
+                                      const struct flow *flow);
 static void subfacet_destroy(struct subfacet *);
 static void subfacet_destroy__(struct subfacet *);
+static void subfacet_destroy_batch(struct ofproto_dpif *,
+                                   struct subfacet **, int n);
 static void subfacet_get_key(struct subfacet *, struct odputil_keybuf *,
                              struct ofpbuf *key);
 static void subfacet_reset_dp_stats(struct subfacet *,
@@ -485,6 +498,7 @@ static void facet_account(struct facet *);
 static bool facet_is_controller_flow(struct facet *);
 
 struct ofport_dpif {
+    struct hmap_node odp_port_node; /* In dpif_backer's "odp_to_ofport_map". */
     struct ofport up;
 
     uint32_t odp_port;
@@ -543,6 +557,11 @@ static bool vsp_adjust_flow(const struct ofproto_dpif *, struct flow *);
 static void vsp_remove(struct ofport_dpif *);
 static void vsp_add(struct ofport_dpif *, uint16_t realdev_ofp_port, int vid);
 
+static uint32_t ofp_port_to_odp_port(const struct ofproto_dpif *,
+                                     uint16_t ofp_port);
+static uint16_t odp_port_to_ofp_port(const struct ofproto_dpif *,
+                                     uint32_t odp_port);
+
 static struct ofport_dpif *
 ofport_dpif_cast(const struct ofport *ofport)
 {
@@ -593,10 +612,25 @@ COVERAGE_DEFINE(rev_port_toggled);
 COVERAGE_DEFINE(rev_flow_table);
 COVERAGE_DEFINE(rev_inconsistency);
 
+/* All datapaths of a given type share a single dpif backer instance. */
+struct dpif_backer {
+    char *type;
+    int refcount;
+    struct dpif *dpif;
+    struct timer next_expiration;
+    struct hmap odp_to_ofport_map; /* ODP port to ofport mapping. */
+};
+
+/* All existing ofproto_backer instances, indexed by ofproto->up.type. */
+static struct shash all_dpif_backers = SHASH_INITIALIZER(&all_dpif_backers);
+
+static struct ofport_dpif *
+odp_port_to_ofport(const struct dpif_backer *, uint32_t odp_port);
+
 struct ofproto_dpif {
     struct hmap_node all_ofproto_dpifs_node; /* In 'all_ofproto_dpifs'. */
     struct ofproto up;
-    struct dpif *dpif;
+    struct dpif_backer *backer;
 
     /* Special OpenFlow rules. */
     struct rule_dpif *miss_rule; /* Sends flow table misses to controller. */
@@ -614,9 +648,6 @@ struct ofproto_dpif {
     bool has_mirrors;
     bool has_bonded_bundles;
 
-    /* Expiration. */
-    struct timer next_expiration;
-
     /* Facets. */
     struct hmap facets;
     struct hmap subfacets;
@@ -641,6 +672,11 @@ struct ofproto_dpif {
     /* VLAN splinters. */
     struct hmap realdev_vid_map; /* (realdev,vid) -> vlandev. */
     struct hmap vlandev_map;     /* vlandev -> (realdev,vid). */
+
+    /* Ports. */
+    struct sset ports;             /* Set of port names. */
+    struct sset port_poll_set;     /* Queued names for port_poll() reply. */
+    int port_poll_errno;           /* Last errno for port_poll() reply. */
 };
 
 /* Defer flow mod completion until "ovs-appctl ofproto/unclog"?  (Useful only
@@ -673,10 +709,10 @@ static void update_learning_table(struct ofproto_dpif *,
                                   struct ofbundle *);
 /* Upcalls. */
 #define FLOW_MISS_MAX_BATCH 50
-static int handle_upcalls(struct ofproto_dpif *, unsigned int max_batch);
+static int handle_upcalls(struct dpif_backer *, unsigned int max_batch);
 
 /* Flow expiration. */
-static int expire(struct ofproto_dpif *);
+static int expire(struct dpif_backer *);
 
 /* NetFlow. */
 static void send_netflow_active_timeouts(struct ofproto_dpif *);
@@ -690,9 +726,30 @@ static void add_mirror_actions(struct action_xlate_ctx *ctx,
                                const struct flow *flow);
 /* Global variables. */
 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+
+/* Initial mappings of port to bridge mappings. */
+static struct shash init_ofp_ports = SHASH_INITIALIZER(&init_ofp_ports);
 \f
 /* Factory functions. */
 
+static void
+init(const struct shash *iface_hints)
+{
+    struct shash_node *node;
+
+    /* Make a local copy, since we don't own 'iface_hints' elements. */
+    SHASH_FOR_EACH(node, iface_hints) {
+        const struct iface_hint *orig_hint = node->data;
+        struct iface_hint *new_hint = xmalloc(sizeof *new_hint);
+
+        new_hint->br_name = xstrdup(orig_hint->br_name);
+        new_hint->br_type = xstrdup(orig_hint->br_type);
+        new_hint->ofp_port = orig_hint->ofp_port;
+
+        shash_add(&init_ofp_ports, node->name, new_hint);
+    }
+}
+
 static void
 enumerate_types(struct sset *types)
 {
@@ -702,7 +759,17 @@ enumerate_types(struct sset *types)
 static int
 enumerate_names(const char *type, struct sset *names)
 {
-    return dp_enumerate_names(type, names);
+    struct ofproto_dpif *ofproto;
+
+    sset_clear(names);
+    HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
+        if (strcmp(type, ofproto->up.type)) {
+            continue;
+        }
+        sset_add(names, ofproto->up.name);
+    }
+
+    return 0;
 }
 
 static int
@@ -719,6 +786,147 @@ del(const char *type, const char *name)
     return error;
 }
 \f
+static const char *
+port_open_type(const char *datapath_type, const char *port_type)
+{
+    return dpif_port_open_type(datapath_type, port_type);
+}
+
+/* Type functions. */
+
+static struct ofproto_dpif *
+lookup_ofproto_dpif_by_port_name(const char *name)
+{
+    struct ofproto_dpif *ofproto;
+
+    HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
+        if (sset_contains(&ofproto->ports, name)) {
+            return ofproto;
+        }
+    }
+
+    return NULL;
+}
+
+static int
+type_run(const char *type)
+{
+    struct dpif_backer *backer;
+    char *devname;
+    int error;
+
+    backer = shash_find_data(&all_dpif_backers, type);
+    if (!backer) {
+        /* This is not necessarily a problem, since backers are only
+         * created on demand. */
+        return 0;
+    }
+
+    dpif_run(backer->dpif);
+
+    if (timer_expired(&backer->next_expiration)) {
+        int delay = expire(backer);
+        timer_set_duration(&backer->next_expiration, delay);
+    }
+
+    /* Check for port changes in the dpif. */
+    while ((error = dpif_port_poll(backer->dpif, &devname)) == 0) {
+        struct ofproto_dpif *ofproto;
+        struct dpif_port port;
+
+        /* Don't report on the datapath's device. */
+        if (!strcmp(devname, dpif_base_name(backer->dpif))) {
+            goto next;
+        }
+
+        ofproto = lookup_ofproto_dpif_by_port_name(devname);
+        if (dpif_port_query_by_name(backer->dpif, devname, &port)) {
+            /* The port was removed.  If we know the datapath,
+             * report it through poll_set().  If we don't, it may be
+             * notifying us of a removal we initiated, so ignore it.
+             * If there's a pending ENOBUFS, let it stand, since
+             * everything will be reevaluated. */
+            if (ofproto && ofproto->port_poll_errno != ENOBUFS) {
+                sset_add(&ofproto->port_poll_set, devname);
+                ofproto->port_poll_errno = 0;
+            }
+        } else if (!ofproto) {
+            /* The port was added, but we don't know with which
+             * ofproto we should associate it.  Delete it. */
+            dpif_port_del(backer->dpif, port.port_no);
+        }
+        dpif_port_destroy(&port);
+
+    next:
+        free(devname);
+    }
+
+    if (error != EAGAIN) {
+        struct ofproto_dpif *ofproto;
+
+        /* There was some sort of error, so propagate it to all
+         * ofprotos that use this backer. */
+        HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node,
+                       &all_ofproto_dpifs) {
+            if (ofproto->backer == backer) {
+                sset_clear(&ofproto->port_poll_set);
+                ofproto->port_poll_errno = error;
+            }
+        }
+    }
+
+    return 0;
+}
+
+static int
+type_run_fast(const char *type)
+{
+    struct dpif_backer *backer;
+    unsigned int work;
+
+    backer = shash_find_data(&all_dpif_backers, type);
+    if (!backer) {
+        /* This is not necessarily a problem, since backers are only
+         * created on demand. */
+        return 0;
+    }
+
+    /* Handle one or more batches of upcalls, until there's nothing left to do
+     * or until we do a fixed total amount of work.
+     *
+     * We do work in batches because it can be much cheaper to set up a number
+     * of flows and fire off their patches all at once.  We do multiple batches
+     * because in some cases handling a packet can cause another packet to be
+     * queued almost immediately as part of the return flow.  Both
+     * optimizations can make major improvements on some benchmarks and
+     * presumably for real traffic as well. */
+    work = 0;
+    while (work < FLOW_MISS_MAX_BATCH) {
+        int retval = handle_upcalls(backer, FLOW_MISS_MAX_BATCH - work);
+        if (retval <= 0) {
+            return -retval;
+        }
+        work += retval;
+    }
+
+    return 0;
+}
+
+static void
+type_wait(const char *type)
+{
+    struct dpif_backer *backer;
+
+    backer = shash_find_data(&all_dpif_backers, type);
+    if (!backer) {
+        /* This is not necessarily a problem, since backers are only
+         * created on demand. */
+        return;
+    }
+
+    timer_wait(&backer->next_expiration);
+}
+\f
 /* Basic life-cycle. */
 
 static int add_internal_flows(struct ofproto_dpif *);
@@ -737,36 +945,146 @@ dealloc(struct ofproto *ofproto_)
     free(ofproto);
 }
 
+static void
+close_dpif_backer(struct dpif_backer *backer)
+{
+    struct shash_node *node;
+
+    assert(backer->refcount > 0);
+
+    if (--backer->refcount) {
+        return;
+    }
+
+    hmap_destroy(&backer->odp_to_ofport_map);
+    node = shash_find(&all_dpif_backers, backer->type);
+    free(backer->type);
+    shash_delete(&all_dpif_backers, node);
+    dpif_close(backer->dpif);
+
+    free(backer);
+}
+
+/* Datapath port slated for removal from datapath. */
+struct odp_garbage {
+    struct list list_node;
+    uint32_t odp_port;
+};
+
+static int
+open_dpif_backer(const char *type, struct dpif_backer **backerp)
+{
+    struct dpif_backer *backer;
+    struct dpif_port_dump port_dump;
+    struct dpif_port port;
+    struct shash_node *node;
+    struct list garbage_list;
+    struct odp_garbage *garbage, *next;
+    struct sset names;
+    char *backer_name;
+    const char *name;
+    int error;
+
+    backer = shash_find_data(&all_dpif_backers, type);
+    if (backer) {
+        backer->refcount++;
+        *backerp = backer;
+        return 0;
+    }
+
+    backer_name = xasprintf("ovs-%s", type);
+
+    /* Remove any existing datapaths, since we assume we're the only
+     * userspace controlling the datapath. */
+    sset_init(&names);
+    dp_enumerate_names(type, &names);
+    SSET_FOR_EACH(name, &names) {
+        struct dpif *old_dpif;
+
+        /* Don't remove our backer if it exists. */
+        if (!strcmp(name, backer_name)) {
+            continue;
+        }
+
+        if (dpif_open(name, type, &old_dpif)) {
+            VLOG_WARN("couldn't open old datapath %s to remove it", name);
+        } else {
+            dpif_delete(old_dpif);
+            dpif_close(old_dpif);
+        }
+    }
+    sset_destroy(&names);
+
+    backer = xmalloc(sizeof *backer);
+
+    error = dpif_create_and_open(backer_name, type, &backer->dpif);
+    free(backer_name);
+    if (error) {
+        VLOG_ERR("failed to open datapath of type %s: %s", type,
+                 strerror(error));
+        return error;
+    }
+
+    backer->type = xstrdup(type);
+    backer->refcount = 1;
+    hmap_init(&backer->odp_to_ofport_map);
+    timer_set_duration(&backer->next_expiration, 1000);
+    *backerp = backer;
+
+    dpif_flow_flush(backer->dpif);
+
+    /* Loop through the ports already on the datapath and remove any
+     * that we don't need anymore. */
+    list_init(&garbage_list);
+    dpif_port_dump_start(&port_dump, backer->dpif);
+    while (dpif_port_dump_next(&port_dump, &port)) {
+        node = shash_find(&init_ofp_ports, port.name);
+        if (!node && strcmp(port.name, dpif_base_name(backer->dpif))) {
+            garbage = xmalloc(sizeof *garbage);
+            garbage->odp_port = port.port_no;
+            list_push_front(&garbage_list, &garbage->list_node);
+        }
+    }
+    dpif_port_dump_done(&port_dump);
+
+    LIST_FOR_EACH_SAFE (garbage, next, list_node, &garbage_list) {
+        dpif_port_del(backer->dpif, garbage->odp_port);
+        list_remove(&garbage->list_node);
+        free(garbage);
+    }
+
+    shash_add(&all_dpif_backers, type, backer);
+
+    error = dpif_recv_set(backer->dpif, true);
+    if (error) {
+        VLOG_ERR("failed to listen on datapath of type %s: %s",
+                 type, strerror(error));
+        close_dpif_backer(backer);
+        return error;
+    }
+
+    return error;
+}
+
 static int
 construct(struct ofproto *ofproto_)
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
-    const char *name = ofproto->up.name;
+    struct shash_node *node, *next;
     int max_ports;
     int error;
     int i;
 
-    error = dpif_create_and_open(name, ofproto->up.type, &ofproto->dpif);
+    error = open_dpif_backer(ofproto->up.type, &ofproto->backer);
     if (error) {
-        VLOG_ERR("failed to open datapath %s: %s", name, strerror(error));
         return error;
     }
 
-    max_ports = dpif_get_max_ports(ofproto->dpif);
+    max_ports = dpif_get_max_ports(ofproto->backer->dpif);
     ofproto_init_max_ports(ofproto_, MIN(max_ports, OFPP_MAX));
 
     ofproto->n_matches = 0;
 
-    dpif_flow_flush(ofproto->dpif);
-    dpif_recv_purge(ofproto->dpif);
-
-    error = dpif_recv_set(ofproto->dpif, true);
-    if (error) {
-        VLOG_ERR("failed to listen on datapath %s: %s", name, strerror(error));
-        dpif_close(ofproto->dpif);
-        return error;
-    }
-
     ofproto->netflow = NULL;
     ofproto->sflow = NULL;
     ofproto->stp = NULL;
@@ -777,8 +1095,6 @@ construct(struct ofproto *ofproto_)
     }
     ofproto->has_bonded_bundles = false;
 
-    timer_set_duration(&ofproto->next_expiration, 1000);
-
     hmap_init(&ofproto->facets);
     hmap_init(&ofproto->subfacets);
     ofproto->governor = NULL;
@@ -803,6 +1119,26 @@ construct(struct ofproto *ofproto_)
     hmap_init(&ofproto->vlandev_map);
     hmap_init(&ofproto->realdev_vid_map);
 
+    sset_init(&ofproto->ports);
+    sset_init(&ofproto->port_poll_set);
+    ofproto->port_poll_errno = 0;
+
+    SHASH_FOR_EACH_SAFE (node, next, &init_ofp_ports) {
+        struct iface_hint *iface_hint = node->data;
+
+        if (!strcmp(iface_hint->br_name, ofproto->up.name)) {
+            /* Check if the datapath already has this port. */
+            if (dpif_port_exists(ofproto->backer->dpif, node->name)) {
+                sset_add(&ofproto->ports, node->name);
+            }
+
+            free(iface_hint->br_name);
+            free(iface_hint->br_type);
+            free(iface_hint);
+            shash_delete(&init_ofp_ports, node);
+        }
+    }
+
     hmap_insert(&all_ofproto_dpifs, &ofproto->all_ofproto_dpifs_node,
                 hash_string(ofproto->up.name, 0));
     memset(&ofproto->stats, 0, sizeof ofproto->stats);
@@ -927,7 +1263,10 @@ destruct(struct ofproto *ofproto_)
     hmap_destroy(&ofproto->vlandev_map);
     hmap_destroy(&ofproto->realdev_vid_map);
 
-    dpif_close(ofproto->dpif);
+    sset_destroy(&ofproto->ports);
+    sset_destroy(&ofproto->port_poll_set);
+
+    close_dpif_backer(ofproto->backer);
 }
 
 static int
@@ -935,29 +1274,11 @@ run_fast(struct ofproto *ofproto_)
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
     struct ofport_dpif *ofport;
-    unsigned int work;
 
     HMAP_FOR_EACH (ofport, up.hmap_node, &ofproto->up.ports) {
         port_run_fast(ofport);
     }
 
-    /* Handle one or more batches of upcalls, until there's nothing left to do
-     * or until we do a fixed total amount of work.
-     *
-     * We do work in batches because it can be much cheaper to set up a number
-     * of flows and fire off their patches all at once.  We do multiple batches
-     * because in some cases handling a packet can cause another packet to be
-     * queued almost immediately as part of the return flow.  Both
-     * optimizations can make major improvements on some benchmarks and
-     * presumably for real traffic as well. */
-    work = 0;
-    while (work < FLOW_MISS_MAX_BATCH) {
-        int retval = handle_upcalls(ofproto, FLOW_MISS_MAX_BATCH - work);
-        if (retval <= 0) {
-            return -retval;
-        }
-        work += retval;
-    }
     return 0;
 }
 
@@ -972,18 +1293,12 @@ run(struct ofproto *ofproto_)
     if (!clogged) {
         complete_operations(ofproto);
     }
-    dpif_run(ofproto->dpif);
 
     error = run_fast(ofproto_);
     if (error) {
         return error;
     }
 
-    if (timer_expired(&ofproto->next_expiration)) {
-        int delay = expire(ofproto);
-        timer_set_duration(&ofproto->next_expiration, delay);
-    }
-
     if (ofproto->netflow) {
         if (netflow_run(ofproto->netflow)) {
             send_netflow_active_timeouts(ofproto);
@@ -1075,8 +1390,8 @@ wait(struct ofproto *ofproto_)
         poll_immediate_wake();
     }
 
-    dpif_wait(ofproto->dpif);
-    dpif_recv_wait(ofproto->dpif);
+    dpif_wait(ofproto->backer->dpif);
+    dpif_recv_wait(ofproto->backer->dpif);
     if (ofproto->sflow) {
         dpif_sflow_wait(ofproto->sflow);
     }
@@ -1098,8 +1413,6 @@ wait(struct ofproto *ofproto_)
         /* Shouldn't happen, but if it does just go around again. */
         VLOG_DBG_RL(&rl, "need revalidate in ofproto_wait_cb()");
         poll_immediate_wake();
-    } else {
-        timer_wait(&ofproto->next_expiration);
     }
     if (ofproto->governor) {
         governor_wait(ofproto->governor);
@@ -1119,23 +1432,27 @@ static void
 flush(struct ofproto *ofproto_)
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
-    struct facet *facet, *next_facet;
-
-    HMAP_FOR_EACH_SAFE (facet, next_facet, hmap_node, &ofproto->facets) {
-        /* Mark the facet as not installed so that facet_remove() doesn't
-         * bother trying to uninstall it.  There is no point in uninstalling it
-         * individually since we are about to blow away all the facets with
-         * dpif_flow_flush(). */
-        struct subfacet *subfacet;
+    struct subfacet *subfacet, *next_subfacet;
+    struct subfacet *batch[SUBFACET_DESTROY_MAX_BATCH];
+    int n_batch;
 
-        LIST_FOR_EACH (subfacet, list_node, &facet->subfacets) {
-            subfacet->path = SF_NOT_INSTALLED;
-            subfacet->dp_packet_count = 0;
-            subfacet->dp_byte_count = 0;
+    n_batch = 0;
+    HMAP_FOR_EACH_SAFE (subfacet, next_subfacet, hmap_node,
+                        &ofproto->subfacets) {
+        if (subfacet->path != SF_NOT_INSTALLED) {
+            batch[n_batch++] = subfacet;
+            if (n_batch >= SUBFACET_DESTROY_MAX_BATCH) {
+                subfacet_destroy_batch(ofproto, batch, n_batch);
+                n_batch = 0;
+            }
+        } else {
+            subfacet_destroy(subfacet);
         }
-        facet_remove(facet);
     }
-    dpif_flow_flush(ofproto->dpif);
+
+    if (n_batch > 0) {
+        subfacet_destroy_batch(ofproto, batch, n_batch);
+    }
 }
 
 static void
@@ -1165,7 +1482,8 @@ get_tables(struct ofproto *ofproto_, struct ofp12_table_stats *ots)
 
     strcpy(ots->name, "classifier");
 
-    dpif_get_dp_stats(ofproto->dpif, &s);
+    dpif_get_dp_stats(ofproto->backer->dpif, &s);
+
     ots->lookup_count = htonll(s.n_hit + s.n_missed);
     ots->matched_count = htonll(s.n_hit + ofproto->n_matches);
 }
@@ -1189,9 +1507,10 @@ port_construct(struct ofport *port_)
 {
     struct ofport_dpif *port = ofport_dpif_cast(port_);
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(port->up.ofproto);
+    struct dpif_port dpif_port;
+    int error;
 
     ofproto->need_revalidate = REV_RECONFIGURE;
-    port->odp_port = ofp_port_to_odp_port(port->up.ofp_port);
     port->bundle = NULL;
     port->cfm = NULL;
     port->tag = tag_create_random();
@@ -1203,8 +1522,28 @@ port_construct(struct ofport *port_)
     port->vlandev_vid = 0;
     port->carrier_seq = netdev_get_carrier_resets(port->up.netdev);
 
+    error = dpif_port_query_by_name(ofproto->backer->dpif,
+                                    netdev_get_name(port->up.netdev),
+                                    &dpif_port);
+    if (error) {
+        return error;
+    }
+
+    port->odp_port = dpif_port.port_no;
+
+    /* Sanity-check that a mapping doesn't already exist.  This
+     * shouldn't happen. */
+    if (odp_port_to_ofp_port(ofproto, port->odp_port) != OFPP_NONE) {
+        VLOG_ERR("port %s already has an OpenFlow port number\n",
+                 dpif_port.name);
+        return EBUSY;
+    }
+
+    hmap_insert(&ofproto->backer->odp_to_ofport_map, &port->odp_port_node,
+                hash_int(port->odp_port, 0));
+
     if (ofproto->sflow) {
-        dpif_sflow_add_port(ofproto->sflow, port_);
+        dpif_sflow_add_port(ofproto->sflow, port_, port->odp_port);
     }
 
     return 0;
@@ -1215,7 +1554,18 @@ port_destruct(struct ofport *port_)
 {
     struct ofport_dpif *port = ofport_dpif_cast(port_);
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(port->up.ofproto);
+    const char *devname = netdev_get_name(port->up.netdev);
+
+    if (dpif_port_exists(ofproto->backer->dpif, devname)) {
+        /* The underlying device is still there, so delete it.  This
+         * happens when the ofproto is being destroyed, since the caller
+         * assumes that removal of attached ports will happen as part of
+         * destruction. */
+        dpif_port_del(ofproto->backer->dpif, port->odp_port);
+    }
 
+    sset_find_and_delete(&ofproto->ports, devname);
+    hmap_remove(&ofproto->backer->odp_to_ofport_map, &port->odp_port_node);
     ofproto->need_revalidate = REV_RECONFIGURE;
     bundle_remove(port_);
     set_cfm(port_, NULL);
@@ -1266,9 +1616,9 @@ set_sflow(struct ofproto *ofproto_,
         if (!ds) {
             struct ofport_dpif *ofport;
 
-            ds = ofproto->sflow = dpif_sflow_create(ofproto->dpif);
+            ds = ofproto->sflow = dpif_sflow_create();
             HMAP_FOR_EACH (ofport, up.hmap_node, &ofproto->up.ports) {
-                dpif_sflow_add_port(ds, &ofport->up);
+                dpif_sflow_add_port(ds, &ofport->up, ofport->odp_port);
             }
             ofproto->need_revalidate = REV_RECONFIGURE;
         }
@@ -1643,7 +1993,7 @@ set_queues(struct ofport *ofport_,
         uint8_t dscp;
 
         dscp = (qdscp_list[i].dscp << 2) & IP_DSCP_MASK;
-        if (dpif_queue_to_priority(ofproto->dpif, qdscp_list[i].queue,
+        if (dpif_queue_to_priority(ofproto->backer->dpif, qdscp_list[i].queue,
                                    &priority)) {
             continue;
         }
@@ -2422,10 +2772,12 @@ forward_bpdu_changed(struct ofproto *ofproto_)
 }
 
 static void
-set_mac_idle_time(struct ofproto *ofproto_, unsigned int idle_time)
+set_mac_table_config(struct ofproto *ofproto_, unsigned int idle_time,
+                     size_t max_entries)
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
     mac_learning_set_idle_time(ofproto->ml, idle_time);
+    mac_learning_set_max_entries(ofproto->ml, max_entries);
 }
 \f
 /* Ports. */
@@ -2440,16 +2792,18 @@ get_ofp_port(const struct ofproto_dpif *ofproto, uint16_t ofp_port)
 static struct ofport_dpif *
 get_odp_port(const struct ofproto_dpif *ofproto, uint32_t odp_port)
 {
-    return get_ofp_port(ofproto, odp_port_to_ofp_port(odp_port));
+    struct ofport_dpif *port = odp_port_to_ofport(ofproto->backer, odp_port);
+    return port && &ofproto->up == port->up.ofproto ? port : NULL;
 }
 
 static void
-ofproto_port_from_dpif_port(struct ofproto_port *ofproto_port,
+ofproto_port_from_dpif_port(struct ofproto_dpif *ofproto,
+                            struct ofproto_port *ofproto_port,
                             struct dpif_port *dpif_port)
 {
     ofproto_port->name = dpif_port->name;
     ofproto_port->type = dpif_port->type;
-    ofproto_port->ofp_port = odp_port_to_ofp_port(dpif_port->port_no);
+    ofproto_port->ofp_port = odp_port_to_ofp_port(ofproto, dpif_port->port_no);
 }
 
 static void
@@ -2520,23 +2874,27 @@ port_query_by_name(const struct ofproto *ofproto_, const char *devname,
     struct dpif_port dpif_port;
     int error;
 
-    error = dpif_port_query_by_name(ofproto->dpif, devname, &dpif_port);
+    if (!sset_contains(&ofproto->ports, devname)) {
+        return ENODEV;
+    }
+    error = dpif_port_query_by_name(ofproto->backer->dpif,
+                                    devname, &dpif_port);
     if (!error) {
-        ofproto_port_from_dpif_port(ofproto_port, &dpif_port);
+        ofproto_port_from_dpif_port(ofproto, ofproto_port, &dpif_port);
     }
     return error;
 }
 
 static int
-port_add(struct ofproto *ofproto_, struct netdev *netdev, uint16_t *ofp_portp)
+port_add(struct ofproto *ofproto_, struct netdev *netdev)
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
-    uint16_t odp_port = UINT16_MAX;
+    uint32_t odp_port = UINT32_MAX;
     int error;
 
-    error = dpif_port_add(ofproto->dpif, netdev, &odp_port);
+    error = dpif_port_add(ofproto->backer->dpif, netdev, &odp_port);
     if (!error) {
-        *ofp_portp = odp_port_to_ofp_port(odp_port);
+        sset_add(&ofproto->ports, netdev_get_name(netdev));
     }
     return error;
 }
@@ -2545,9 +2903,12 @@ static int
 port_del(struct ofproto *ofproto_, uint16_t ofp_port)
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
-    int error;
+    uint32_t odp_port = ofp_port_to_odp_port(ofproto, ofp_port);
+    int error = 0;
 
-    error = dpif_port_del(ofproto->dpif, ofp_port_to_odp_port(ofp_port));
+    if (odp_port != OFPP_NONE) {
+        error = dpif_port_del(ofproto->backer->dpif, odp_port);
+    }
     if (!error) {
         struct ofport_dpif *ofport = get_ofp_port(ofproto, ofp_port);
         if (ofport) {
@@ -2569,7 +2930,7 @@ port_get_stats(const struct ofport *ofport_, struct netdev_stats *stats)
 
     error = netdev_get_stats(ofport->up.netdev, stats);
 
-    if (!error && ofport->odp_port == OVSP_LOCAL) {
+    if (!error && ofport_->ofp_port == OFPP_LOCAL) {
         struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport->up.ofproto);
 
         /* ofproto->stats.tx_packets represents packets that we created
@@ -2619,19 +2980,18 @@ ofproto_update_local_port_stats(const struct ofproto *ofproto_,
 }
 
 struct port_dump_state {
-    struct dpif_port_dump dump;
-    bool done;
+    uint32_t bucket;
+    uint32_t offset;
 };
 
 static int
-port_dump_start(const struct ofproto *ofproto_, void **statep)
+port_dump_start(const struct ofproto *ofproto_ OVS_UNUSED, void **statep)
 {
-    struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
     struct port_dump_state *state;
 
     *statep = state = xmalloc(sizeof *state);
-    dpif_port_dump_start(&state->dump, ofproto->dpif);
-    state->done = false;
+    state->bucket = 0;
+    state->offset = 0;
     return 0;
 }
 
@@ -2639,17 +2999,21 @@ static int
 port_dump_next(const struct ofproto *ofproto_ OVS_UNUSED, void *state_,
                struct ofproto_port *port)
 {
+    struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
     struct port_dump_state *state = state_;
-    struct dpif_port dpif_port;
+    struct sset_node *node;
 
-    if (dpif_port_dump_next(&state->dump, &dpif_port)) {
-        ofproto_port_from_dpif_port(port, &dpif_port);
-        return 0;
-    } else {
-        int error = dpif_port_dump_done(&state->dump);
-        state->done = true;
-        return error ? error : EOF;
+    while ((node = sset_at_position(&ofproto->ports, &state->bucket,
+                               &state->offset))) {
+        int error;
+
+        error = port_query_by_name(ofproto_, node->name, port);
+        if (error != ENODEV) {
+            return error;
+        }
     }
+
+    return EOF;
 }
 
 static int
@@ -2657,9 +3021,6 @@ port_dump_done(const struct ofproto *ofproto_ OVS_UNUSED, void *state_)
 {
     struct port_dump_state *state = state_;
 
-    if (!state->done) {
-        dpif_port_dump_done(&state->dump);
-    }
     free(state);
     return 0;
 }
@@ -2668,14 +3029,26 @@ static int
 port_poll(const struct ofproto *ofproto_, char **devnamep)
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
-    return dpif_port_poll(ofproto->dpif, devnamep);
+
+    if (ofproto->port_poll_errno) {
+        int error = ofproto->port_poll_errno;
+        ofproto->port_poll_errno = 0;
+        return error;
+    }
+
+    if (sset_is_empty(&ofproto->port_poll_set)) {
+        return EAGAIN;
+    }
+
+    *devnamep = sset_pop(&ofproto->port_poll_set);
+    return 0;
 }
 
 static void
 port_poll_wait(const struct ofproto *ofproto_)
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
-    dpif_port_poll_wait(ofproto->dpif);
+    dpif_port_poll_wait(ofproto->backer->dpif);
 }
 
 static int
@@ -2700,6 +3073,7 @@ port_is_lacp_current(const struct ofport *ofport_)
  * It's possible to batch more than that, but the benefit might be minimal. */
 struct flow_miss {
     struct hmap_node hmap_node;
+    struct ofproto_dpif *ofproto;
     struct flow flow;
     enum odp_key_fitness key_fitness;
     const struct nlattr *key;
@@ -2707,6 +3081,7 @@ struct flow_miss {
     ovs_be16 initial_tci;
     struct list packets;
     enum dpif_upcall_type upcall_type;
+    uint32_t odp_in_port;
 };
 
 struct flow_miss_op {
@@ -2926,9 +3301,7 @@ handle_flow_miss_with_facet(struct flow_miss *miss, struct facet *facet,
     struct subfacet *subfacet;
     struct ofpbuf *packet;
 
-    subfacet = subfacet_create(facet,
-                               miss->key_fitness, miss->key, miss->key_len,
-                               miss->initial_tci, now);
+    subfacet = subfacet_create(facet, miss, now);
 
     LIST_FOR_EACH (packet, list_node, &miss->packets) {
         struct flow_miss_op *op = &ops[*n_ops];
@@ -2989,12 +3362,13 @@ handle_flow_miss_with_facet(struct flow_miss *miss, struct facet *facet,
     }
 }
 
-/* Handles flow miss 'miss' on 'ofproto'.  May add any required datapath
- * operations to 'ops', incrementing '*n_ops' for each new op. */
+/* Handles flow miss 'miss'.  May add any required datapath operations
+ * to 'ops', incrementing '*n_ops' for each new op. */
 static void
-handle_flow_miss(struct ofproto_dpif *ofproto, struct flow_miss *miss,
-                 struct flow_miss_op *ops, size_t *n_ops)
+handle_flow_miss(struct flow_miss *miss, struct flow_miss_op *ops,
+                 size_t *n_ops)
 {
+    struct ofproto_dpif *ofproto = miss->ofproto;
     struct facet *facet;
     long long int now;
     uint32_t hash;
@@ -3020,37 +3394,68 @@ handle_flow_miss(struct ofproto_dpif *ofproto, struct flow_miss *miss,
     handle_flow_miss_with_facet(miss, facet, now, ops, n_ops);
 }
 
-/* Like odp_flow_key_to_flow(), this function converts the 'key_len' bytes of
- * OVS_KEY_ATTR_* attributes in 'key' to a flow structure in 'flow' and returns
- * an ODP_FIT_* value that indicates how well 'key' fits our expectations for
- * what a flow key should contain.
+/* Given a datpath, packet, and flow metadata ('backer', 'packet', and 'key'
+ * respectively), populates 'flow' with the result of odp_flow_key_to_flow().
+ * Optionally, if nonnull, populates 'fitnessp' with the fitness of 'flow' as
+ * returned by odp_flow_key_to_flow().  Also, optionally populates 'ofproto'
+ * with the ofproto_dpif, and 'odp_in_port' with the datapath in_port, that
+ * 'packet' ingressed.
  *
- * This function also includes some logic to help make VLAN splinters
- * transparent to the rest of the upcall processing logic.  In particular, if
- * the extracted in_port is a VLAN splinter port, it replaces flow->in_port by
- * the "real" port, sets flow->vlan_tci correctly for the VLAN of the VLAN
- * splinter port, and pushes a VLAN header onto 'packet' (if it is nonnull).
+ * If 'ofproto' is nonnull, requires 'flow''s in_port to exist.  Otherwise sets
+ * 'flow''s in_port to OFPP_NONE.
  *
- * Sets '*initial_tci' to the VLAN TCI with which the packet was really
- * received, that is, the actual VLAN TCI extracted by odp_flow_key_to_flow().
- * (This differs from the value returned in flow->vlan_tci only for packets
- * received on VLAN splinters.)
- */
-static enum odp_key_fitness
-ofproto_dpif_extract_flow_key(const struct ofproto_dpif *ofproto,
-                              const struct nlattr *key, size_t key_len,
-                              struct flow *flow, ovs_be16 *initial_tci,
-                              struct ofpbuf *packet)
+ * This function does post-processing on data returned from
+ * odp_flow_key_to_flow() to help make VLAN splinters transparent to the rest
+ * of the upcall processing logic.  In particular, if the extracted in_port is
+ * a VLAN splinter port, it replaces flow->in_port by the "real" port, sets
+ * flow->vlan_tci correctly for the VLAN of the VLAN splinter port, and pushes
+ * a VLAN header onto 'packet' (if it is nonnull).
+ *
+ * Optionally, if nonnull, sets '*initial_tci' to the VLAN TCI with which the
+ * packet was really received, that is, the actual VLAN TCI extracted by
+ * odp_flow_key_to_flow().  (This differs from the value returned in
+ * flow->vlan_tci only for packets received on VLAN splinters.)
+ *
+ * Returns 0 if successful, ENODEV if the parsed flow has no associated ofport,
+ * or some other positive errno if there are other problems. */
+static int
+ofproto_receive(const struct dpif_backer *backer, struct ofpbuf *packet,
+                const struct nlattr *key, size_t key_len,
+                struct flow *flow, enum odp_key_fitness *fitnessp,
+                struct ofproto_dpif **ofproto, uint32_t *odp_in_port,
+                ovs_be16 *initial_tci)
 {
+    const struct ofport_dpif *port;
     enum odp_key_fitness fitness;
+    int error;
 
     fitness = odp_flow_key_to_flow(key, key_len, flow);
     if (fitness == ODP_FIT_ERROR) {
-        return fitness;
+        error = EINVAL;
+        goto exit;
     }
-    *initial_tci = flow->vlan_tci;
 
-    if (vsp_adjust_flow(ofproto, flow)) {
+    if (initial_tci) {
+        *initial_tci = flow->vlan_tci;
+    }
+
+    if (odp_in_port) {
+        *odp_in_port = flow->in_port;
+    }
+
+    port = odp_port_to_ofport(backer, flow->in_port);
+    if (!port) {
+        flow->in_port = OFPP_NONE;
+        error = ofproto ? ENODEV : 0;
+        goto exit;
+    }
+
+    if (ofproto) {
+        *ofproto = ofproto_dpif_cast(port->up.ofproto);
+    }
+
+    flow->in_port = port->up.ofp_port;
+    if (vsp_adjust_flow(ofproto_dpif_cast(port->up.ofproto), flow)) {
         if (packet) {
             /* Make the packet resemble the flow, so that it gets sent to an
              * OpenFlow controller properly, so that it looks correct for
@@ -3074,12 +3479,17 @@ ofproto_dpif_extract_flow_key(const struct ofproto_dpif *ofproto,
             fitness = ODP_FIT_TOO_MUCH;
         }
     }
+    error = 0;
 
-    return fitness;
+exit:
+    if (fitnessp) {
+        *fitnessp = fitness;
+    }
+    return error;
 }
 
 static void
-handle_miss_upcalls(struct ofproto_dpif *ofproto, struct dpif_upcall *upcalls,
+handle_miss_upcalls(struct dpif_backer *backer, struct dpif_upcall *upcalls,
                     size_t n_upcalls)
 {
     struct dpif_upcall *upcall;
@@ -3106,18 +3516,27 @@ handle_miss_upcalls(struct ofproto_dpif *ofproto, struct dpif_upcall *upcalls,
     for (upcall = upcalls; upcall < &upcalls[n_upcalls]; upcall++) {
         struct flow_miss *miss = &misses[n_misses];
         struct flow_miss *existing_miss;
+        struct ofproto_dpif *ofproto;
+        uint32_t odp_in_port;
         struct flow flow;
         uint32_t hash;
+        int error;
 
-        /* Obtain metadata and check userspace/kernel agreement on flow match,
-         * then set 'flow''s header pointers. */
-        miss->key_fitness = ofproto_dpif_extract_flow_key(
-            ofproto, upcall->key, upcall->key_len,
-            &flow, &miss->initial_tci, upcall->packet);
-        if (miss->key_fitness == ODP_FIT_ERROR) {
+        error = ofproto_receive(backer, upcall->packet, upcall->key,
+                                upcall->key_len, &flow, &miss->key_fitness,
+                                &ofproto, &odp_in_port, &miss->initial_tci);
+        if (error == ENODEV) {
+            /* Received packet on port for which we couldn't associate
+             * an ofproto.  This can happen if a port is removed while
+             * traffic is being received.  Print a rate-limited message
+             * in case it happens frequently. */
+            VLOG_INFO_RL(&rl, "received packet on unassociated port %"PRIu32,
+                         flow.in_port);
+        }
+        if (error) {
             continue;
         }
-        flow_extract(upcall->packet, flow.skb_priority,
+        flow_extract(upcall->packet, flow.skb_priority, flow.skb_mark,
                      &flow.tunnel, flow.in_port, &miss->flow);
 
         /* Add other packets to a to-do list. */
@@ -3125,9 +3544,11 @@ handle_miss_upcalls(struct ofproto_dpif *ofproto, struct dpif_upcall *upcalls,
         existing_miss = flow_miss_find(&todo, &miss->flow, hash);
         if (!existing_miss) {
             hmap_insert(&todo, &miss->hmap_node, hash);
+            miss->ofproto = ofproto;
             miss->key = upcall->key;
             miss->key_len = upcall->key_len;
             miss->upcall_type = upcall->type;
+            miss->odp_in_port = odp_in_port;
             list_init(&miss->packets);
 
             n_misses++;
@@ -3141,7 +3562,7 @@ handle_miss_upcalls(struct ofproto_dpif *ofproto, struct dpif_upcall *upcalls,
      * operations to batch. */
     n_ops = 0;
     HMAP_FOR_EACH (miss, hmap_node, &todo) {
-        handle_flow_miss(ofproto, miss, flow_miss_ops, &n_ops);
+        handle_flow_miss(miss, flow_miss_ops, &n_ops);
     }
     assert(n_ops <= ARRAY_SIZE(flow_miss_ops));
 
@@ -3149,7 +3570,7 @@ handle_miss_upcalls(struct ofproto_dpif *ofproto, struct dpif_upcall *upcalls,
     for (i = 0; i < n_ops; i++) {
         dpif_ops[i] = &flow_miss_ops[i].dpif_op;
     }
-    dpif_operate(ofproto->dpif, dpif_ops, n_ops);
+    dpif_operate(backer->dpif, dpif_ops, n_ops);
 
     /* Free memory and update facets. */
     for (i = 0; i < n_ops; i++) {
@@ -3210,27 +3631,27 @@ classify_upcall(const struct dpif_upcall *upcall)
 }
 
 static void
-handle_sflow_upcall(struct ofproto_dpif *ofproto,
+handle_sflow_upcall(struct dpif_backer *backer,
                     const struct dpif_upcall *upcall)
 {
+    struct ofproto_dpif *ofproto;
     union user_action_cookie cookie;
-    enum odp_key_fitness fitness;
-    ovs_be16 initial_tci;
     struct flow flow;
+    uint32_t odp_in_port;
 
-    fitness = ofproto_dpif_extract_flow_key(ofproto, upcall->key,
-                                            upcall->key_len, &flow,
-                                            &initial_tci, upcall->packet);
-    if (fitness == ODP_FIT_ERROR) {
+    if (ofproto_receive(backer, upcall->packet, upcall->key, upcall->key_len,
+                        &flow, NULL, &ofproto, &odp_in_port, NULL)
+        || !ofproto->sflow) {
         return;
     }
 
     memcpy(&cookie, &upcall->userdata, sizeof(cookie));
-    dpif_sflow_received(ofproto->sflow, upcall->packet, &flow, &cookie);
+    dpif_sflow_received(ofproto->sflow, upcall->packet, &flow,
+                        odp_in_port, &cookie);
 }
 
 static int
-handle_upcalls(struct ofproto_dpif *ofproto, unsigned int max_batch)
+handle_upcalls(struct dpif_backer *backer, unsigned int max_batch)
 {
     struct dpif_upcall misses[FLOW_MISS_MAX_BATCH];
     struct ofpbuf miss_bufs[FLOW_MISS_MAX_BATCH];
@@ -3249,7 +3670,7 @@ handle_upcalls(struct ofproto_dpif *ofproto, unsigned int max_batch)
 
         ofpbuf_use_stub(buf, miss_buf_stubs[n_misses],
                         sizeof miss_buf_stubs[n_misses]);
-        error = dpif_recv(ofproto->dpif, upcall, buf);
+        error = dpif_recv(backer->dpif, upcall, buf);
         if (error) {
             ofpbuf_uninit(buf);
             break;
@@ -3262,9 +3683,7 @@ handle_upcalls(struct ofproto_dpif *ofproto, unsigned int max_batch)
             break;
 
         case SFLOW_UPCALL:
-            if (ofproto->sflow) {
-                handle_sflow_upcall(ofproto, upcall);
-            }
+            handle_sflow_upcall(backer, upcall);
             ofpbuf_uninit(buf);
             break;
 
@@ -3275,7 +3694,7 @@ handle_upcalls(struct ofproto_dpif *ofproto, unsigned int max_batch)
     }
 
     /* Handle deferred MISS_UPCALL processing. */
-    handle_miss_upcalls(ofproto, misses, n_misses);
+    handle_miss_upcalls(backer, misses, n_misses);
     for (i = 0; i < n_misses; i++) {
         ofpbuf_uninit(&miss_bufs[i]);
     }
@@ -3286,7 +3705,7 @@ handle_upcalls(struct ofproto_dpif *ofproto, unsigned int max_batch)
 /* Flow expiration. */
 
 static int subfacet_max_idle(const struct ofproto_dpif *);
-static void update_stats(struct ofproto_dpif *);
+static void update_stats(struct dpif_backer *);
 static void rule_expire(struct rule_dpif *);
 static void expire_subfacets(struct ofproto_dpif *, int dp_max_idle);
 
@@ -3297,42 +3716,54 @@ static void expire_subfacets(struct ofproto_dpif *, int dp_max_idle);
  *
  * Returns the number of milliseconds after which it should be called again. */
 static int
-expire(struct ofproto_dpif *ofproto)
+expire(struct dpif_backer *backer)
 {
-    struct rule_dpif *rule, *next_rule;
-    struct oftable *table;
-    int dp_max_idle;
+    struct ofproto_dpif *ofproto;
+    int max_idle = INT32_MAX;
 
-    /* Update stats for each flow in the datapath. */
-    update_stats(ofproto);
+    /* Update stats for each flow in the backer. */
+    update_stats(backer);
 
-    /* Expire subfacets that have been idle too long. */
-    dp_max_idle = subfacet_max_idle(ofproto);
-    expire_subfacets(ofproto, dp_max_idle);
+    HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
+        struct rule_dpif *rule, *next_rule;
+        struct oftable *table;
+        int dp_max_idle;
 
-    /* Expire OpenFlow flows whose idle_timeout or hard_timeout has passed. */
-    OFPROTO_FOR_EACH_TABLE (table, &ofproto->up) {
-        struct cls_cursor cursor;
+        if (ofproto->backer != backer) {
+            continue;
+        }
 
-        cls_cursor_init(&cursor, &table->cls, NULL);
-        CLS_CURSOR_FOR_EACH_SAFE (rule, next_rule, up.cr, &cursor) {
-            rule_expire(rule);
+        /* Expire subfacets that have been idle too long. */
+        dp_max_idle = subfacet_max_idle(ofproto);
+        expire_subfacets(ofproto, dp_max_idle);
+
+        max_idle = MIN(max_idle, dp_max_idle);
+
+        /* Expire OpenFlow flows whose idle_timeout or hard_timeout
+         * has passed. */
+        OFPROTO_FOR_EACH_TABLE (table, &ofproto->up) {
+            struct cls_cursor cursor;
+
+            cls_cursor_init(&cursor, &table->cls, NULL);
+            CLS_CURSOR_FOR_EACH_SAFE (rule, next_rule, up.cr, &cursor) {
+                rule_expire(rule);
+            }
         }
-    }
 
-    /* All outstanding data in existing flows has been accounted, so it's a
-     * good time to do bond rebalancing. */
-    if (ofproto->has_bonded_bundles) {
-        struct ofbundle *bundle;
+        /* All outstanding data in existing flows has been accounted, so it's a
+         * good time to do bond rebalancing. */
+        if (ofproto->has_bonded_bundles) {
+            struct ofbundle *bundle;
 
-        HMAP_FOR_EACH (bundle, hmap_node, &ofproto->bundles) {
-            if (bundle->bond) {
-                bond_rebalance(bundle->bond, &ofproto->revalidate_set);
+            HMAP_FOR_EACH (bundle, hmap_node, &ofproto->bundles) {
+                if (bundle->bond) {
+                    bond_rebalance(bundle->bond, &ofproto->revalidate_set);
+                }
             }
         }
     }
 
-    return MIN(dp_max_idle, 1000);
+    return MIN(max_idle, 1000);
 }
 
 /* Updates flow table statistics given that the datapath just reported 'stats'
@@ -3373,7 +3804,7 @@ update_subfacet_stats(struct subfacet *subfacet,
 /* 'key' with length 'key_len' bytes is a flow in 'dpif' that we know nothing
  * about, or a flow that shouldn't be installed but was anyway.  Delete it. */
 static void
-delete_unexpected_flow(struct dpif *dpif,
+delete_unexpected_flow(struct ofproto_dpif *ofproto,
                        const struct nlattr *key, size_t key_len)
 {
     if (!VLOG_DROP_WARN(&rl)) {
@@ -3381,12 +3812,12 @@ delete_unexpected_flow(struct dpif *dpif,
 
         ds_init(&s);
         odp_flow_key_format(key, key_len, &s);
-        VLOG_WARN("unexpected flow from datapath %s", ds_cstr(&s));
+        VLOG_WARN("unexpected flow on %s: %s", ofproto->up.name, ds_cstr(&s));
         ds_destroy(&s);
     }
 
     COVERAGE_INC(facet_unexpected);
-    dpif_flow_del(dpif, key, key_len, NULL);
+    dpif_flow_del(ofproto->backer->dpif, key, key_len, NULL);
 }
 
 /* Update 'packet_count', 'byte_count', and 'used' members of installed facets.
@@ -3401,18 +3832,41 @@ delete_unexpected_flow(struct dpif *dpif,
  * datapath do not justify the benefit of having perfectly accurate statistics.
  */
 static void
-update_stats(struct ofproto_dpif *p)
+update_stats(struct dpif_backer *backer)
 {
     const struct dpif_flow_stats *stats;
     struct dpif_flow_dump dump;
     const struct nlattr *key;
     size_t key_len;
 
-    dpif_flow_dump_start(&dump, p->dpif);
+    dpif_flow_dump_start(&dump, backer->dpif);
     while (dpif_flow_dump_next(&dump, &key, &key_len, NULL, NULL, &stats)) {
+        struct flow flow;
         struct subfacet *subfacet;
+        enum odp_key_fitness fitness;
+        struct ofproto_dpif *ofproto;
+        struct ofport_dpif *port;
+        uint32_t key_hash;
+
+        fitness = odp_flow_key_to_flow(key, key_len, &flow);
+        if (fitness == ODP_FIT_ERROR) {
+            continue;
+        }
 
-        subfacet = subfacet_find(p, key, key_len);
+        port = odp_port_to_ofport(backer, flow.in_port);
+        if (!port) {
+            /* This flow is for a port for which we couldn't associate an
+             * ofproto.  This can happen if a port is removed while
+             * traffic is being received.  Ignore this flow, since it
+             * will get timed out. */
+            continue;
+        }
+
+        ofproto = ofproto_dpif_cast(port->up.ofproto);
+        flow.in_port = port->up.ofp_port;
+        key_hash = odp_flow_key_hash(key, key_len);
+
+        subfacet = subfacet_find(ofproto, key, key_len, key_hash, &flow);
         switch (subfacet ? subfacet->path : SF_NOT_INSTALLED) {
         case SF_FAST_PATH:
             update_subfacet_stats(subfacet, stats);
@@ -3424,7 +3878,7 @@ update_stats(struct ofproto_dpif *p)
 
         case SF_NOT_INSTALLED:
         default:
-            delete_unexpected_flow(p->dpif, key, key_len);
+            delete_unexpected_flow(ofproto, key, key_len);
             break;
         }
     }
@@ -3519,35 +3973,6 @@ subfacet_max_idle(const struct ofproto_dpif *ofproto)
     return bucket * BUCKET_WIDTH;
 }
 
-enum { EXPIRE_MAX_BATCH = 50 };
-
-static void
-expire_batch(struct ofproto_dpif *ofproto, struct subfacet **subfacets, int n)
-{
-    struct odputil_keybuf keybufs[EXPIRE_MAX_BATCH];
-    struct dpif_op ops[EXPIRE_MAX_BATCH];
-    struct dpif_op *opsp[EXPIRE_MAX_BATCH];
-    struct ofpbuf keys[EXPIRE_MAX_BATCH];
-    struct dpif_flow_stats stats[EXPIRE_MAX_BATCH];
-    int i;
-
-    for (i = 0; i < n; i++) {
-        ops[i].type = DPIF_OP_FLOW_DEL;
-        subfacet_get_key(subfacets[i], &keybufs[i], &keys[i]);
-        ops[i].u.flow_del.key = keys[i].data;
-        ops[i].u.flow_del.key_len = keys[i].size;
-        ops[i].u.flow_del.stats = &stats[i];
-        opsp[i] = &ops[i];
-    }
-
-    dpif_operate(ofproto->dpif, opsp, n);
-    for (i = 0; i < n; i++) {
-        subfacet_reset_dp_stats(subfacets[i], &stats[i]);
-        subfacets[i]->path = SF_NOT_INSTALLED;
-        subfacet_destroy(subfacets[i]);
-    }
-}
-
 static void
 expire_subfacets(struct ofproto_dpif *ofproto, int dp_max_idle)
 {
@@ -3559,7 +3984,7 @@ expire_subfacets(struct ofproto_dpif *ofproto, int dp_max_idle)
     long long int special_cutoff = time_msec() - 10000;
 
     struct subfacet *subfacet, *next_subfacet;
-    struct subfacet *batch[EXPIRE_MAX_BATCH];
+    struct subfacet *batch[SUBFACET_DESTROY_MAX_BATCH];
     int n_batch;
 
     n_batch = 0;
@@ -3573,8 +3998,8 @@ expire_subfacets(struct ofproto_dpif *ofproto, int dp_max_idle)
         if (subfacet->used < cutoff) {
             if (subfacet->path != SF_NOT_INSTALLED) {
                 batch[n_batch++] = subfacet;
-                if (n_batch >= EXPIRE_MAX_BATCH) {
-                    expire_batch(ofproto, batch, n_batch);
+                if (n_batch >= SUBFACET_DESTROY_MAX_BATCH) {
+                    subfacet_destroy_batch(ofproto, batch, n_batch);
                     n_batch = 0;
                 }
             } else {
@@ -3584,7 +4009,7 @@ expire_subfacets(struct ofproto_dpif *ofproto, int dp_max_idle)
     }
 
     if (n_batch > 0) {
-        expire_batch(ofproto, batch, n_batch);
+        subfacet_destroy_batch(ofproto, batch, n_batch);
     }
 }
 
@@ -3677,9 +4102,10 @@ execute_odp_actions(struct ofproto_dpif *ofproto, const struct flow *flow,
     int error;
 
     ofpbuf_use_stack(&key, &keybuf, sizeof keybuf);
-    odp_flow_key_from_flow(&key, flow);
+    odp_flow_key_from_flow(&key, flow,
+                           ofp_port_to_odp_port(ofproto, flow->in_port));
 
-    error = dpif_execute(ofproto->dpif, key.data, key.size,
+    error = dpif_execute(ofproto->backer->dpif, key.data, key.size,
                          odp_actions, actions_len, packet);
 
     ofpbuf_delete(packet);
@@ -4237,9 +4663,9 @@ flow_push_stats(struct rule_dpif *rule,
 /* Subfacets. */
 
 static struct subfacet *
-subfacet_find__(struct ofproto_dpif *ofproto,
-                const struct nlattr *key, size_t key_len, uint32_t key_hash,
-                const struct flow *flow)
+subfacet_find(struct ofproto_dpif *ofproto,
+              const struct nlattr *key, size_t key_len, uint32_t key_hash,
+              const struct flow *flow)
 {
     struct subfacet *subfacet;
 
@@ -4257,26 +4683,31 @@ subfacet_find__(struct ofproto_dpif *ofproto,
 }
 
 /* Searches 'facet' (within 'ofproto') for a subfacet with the specified
- * 'key_fitness', 'key', and 'key_len'.  Returns the existing subfacet if
- * there is one, otherwise creates and returns a new subfacet.
+ * 'key_fitness', 'key', and 'key_len' members in 'miss'.  Returns the
+ * existing subfacet if there is one, otherwise creates and returns a
+ * new subfacet.
  *
  * If the returned subfacet is new, then subfacet->actions will be NULL, in
  * which case the caller must populate the actions with
  * subfacet_make_actions(). */
 static struct subfacet *
-subfacet_create(struct facet *facet, enum odp_key_fitness key_fitness,
-                const struct nlattr *key, size_t key_len,
-                ovs_be16 initial_tci, long long int now)
+subfacet_create(struct facet *facet, struct flow_miss *miss,
+                long long int now)
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(facet->rule->up.ofproto);
-    uint32_t key_hash = odp_flow_key_hash(key, key_len);
+    enum odp_key_fitness key_fitness = miss->key_fitness;
+    const struct nlattr *key = miss->key;
+    size_t key_len = miss->key_len;
+    uint32_t key_hash;
     struct subfacet *subfacet;
 
+    key_hash = odp_flow_key_hash(key, key_len);
+
     if (list_is_empty(&facet->subfacets)) {
         subfacet = &facet->one_subfacet;
     } else {
-        subfacet = subfacet_find__(ofproto, key, key_len, key_hash,
-                                   &facet->flow);
+        subfacet = subfacet_find(ofproto, key, key_len, key_hash,
+                                 &facet->flow);
         if (subfacet) {
             if (subfacet->facet == facet) {
                 return subfacet;
@@ -4310,29 +4741,12 @@ subfacet_create(struct facet *facet, enum odp_key_fitness key_fitness,
                       ? SLOW_MATCH
                       : 0);
     subfacet->path = SF_NOT_INSTALLED;
-    subfacet->initial_tci = initial_tci;
+    subfacet->initial_tci = miss->initial_tci;
+    subfacet->odp_in_port = miss->odp_in_port;
 
     return subfacet;
 }
 
-/* Searches 'ofproto' for a subfacet with the given 'key', 'key_len', and
- * 'flow'.  Returns the subfacet if one exists, otherwise NULL. */
-static struct subfacet *
-subfacet_find(struct ofproto_dpif *ofproto,
-              const struct nlattr *key, size_t key_len)
-{
-    uint32_t key_hash = odp_flow_key_hash(key, key_len);
-    enum odp_key_fitness fitness;
-    struct flow flow;
-
-    fitness = odp_flow_key_to_flow(key, key_len, &flow);
-    if (fitness == ODP_FIT_ERROR) {
-        return NULL;
-    }
-
-    return subfacet_find__(ofproto, key, key_len, key_hash, &flow);
-}
-
 /* Uninstalls 'subfacet' from the datapath, if it is installed, removes it from
  * its facet within 'ofproto', and frees it. */
 static void
@@ -4366,6 +4780,34 @@ subfacet_destroy(struct subfacet *subfacet)
     }
 }
 
+static void
+subfacet_destroy_batch(struct ofproto_dpif *ofproto,
+                       struct subfacet **subfacets, int n)
+{
+    struct odputil_keybuf keybufs[SUBFACET_DESTROY_MAX_BATCH];
+    struct dpif_op ops[SUBFACET_DESTROY_MAX_BATCH];
+    struct dpif_op *opsp[SUBFACET_DESTROY_MAX_BATCH];
+    struct ofpbuf keys[SUBFACET_DESTROY_MAX_BATCH];
+    struct dpif_flow_stats stats[SUBFACET_DESTROY_MAX_BATCH];
+    int i;
+
+    for (i = 0; i < n; i++) {
+        ops[i].type = DPIF_OP_FLOW_DEL;
+        subfacet_get_key(subfacets[i], &keybufs[i], &keys[i]);
+        ops[i].u.flow_del.key = keys[i].data;
+        ops[i].u.flow_del.key_len = keys[i].size;
+        ops[i].u.flow_del.stats = &stats[i];
+        opsp[i] = &ops[i];
+    }
+
+    dpif_operate(ofproto->backer->dpif, opsp, n);
+    for (i = 0; i < n; i++) {
+        subfacet_reset_dp_stats(subfacets[i], &stats[i]);
+        subfacets[i]->path = SF_NOT_INSTALLED;
+        subfacet_destroy(subfacets[i]);
+    }
+}
+
 /* Initializes 'key' with the sequence of OVS_KEY_ATTR_* Netlink attributes
  * that can be used to refer to 'subfacet'.  The caller must provide 'keybuf'
  * for use as temporary storage. */
@@ -4373,9 +4815,12 @@ static void
 subfacet_get_key(struct subfacet *subfacet, struct odputil_keybuf *keybuf,
                  struct ofpbuf *key)
 {
+
     if (!subfacet->key) {
+        struct flow *flow = &subfacet->facet->flow;
+
         ofpbuf_use_stack(key, keybuf, sizeof *keybuf);
-        odp_flow_key_from_flow(key, &subfacet->facet->flow);
+        odp_flow_key_from_flow(key, flow, subfacet->odp_in_port);
     } else {
         ofpbuf_use_const(key, subfacet->key, subfacet->key_len);
     }
@@ -4446,7 +4891,7 @@ subfacet_install(struct subfacet *subfacet,
     }
 
     subfacet_get_key(subfacet, &keybuf, &key);
-    ret = dpif_flow_put(ofproto->dpif, flags, key.data, key.size,
+    ret = dpif_flow_put(ofproto->backer->dpif, flags, key.data, key.size,
                         actions, actions_len, stats);
 
     if (stats) {
@@ -4479,7 +4924,8 @@ subfacet_uninstall(struct subfacet *subfacet)
         int error;
 
         subfacet_get_key(subfacet, &keybuf, &key);
-        error = dpif_flow_del(ofproto->dpif, key.data, key.size, &stats);
+        error = dpif_flow_del(ofproto->backer->dpif,
+                              key.data, key.size, &stats);
         subfacet_reset_dp_stats(subfacet, &stats);
         if (!error) {
             subfacet_update_stats(subfacet, &stats);
@@ -4760,11 +5206,11 @@ send_packet(const struct ofport_dpif *ofport, struct ofpbuf *packet)
     const struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport->up.ofproto);
     struct ofpbuf key, odp_actions;
     struct odputil_keybuf keybuf;
-    uint16_t odp_port;
+    uint32_t odp_port;
     struct flow flow;
     int error;
 
-    flow_extract(packet, 0, NULL, 0, &flow);
+    flow_extract(packet, 0, 0, NULL, OFPP_LOCAL, &flow);
     odp_port = vsp_realdev_to_vlandev(ofproto, ofport->odp_port,
                                       flow.vlan_tci);
     if (odp_port != ofport->odp_port) {
@@ -4773,13 +5219,14 @@ send_packet(const struct ofport_dpif *ofport, struct ofpbuf *packet)
     }
 
     ofpbuf_use_stack(&key, &keybuf, sizeof keybuf);
-    odp_flow_key_from_flow(&key, &flow);
+    odp_flow_key_from_flow(&key, &flow,
+                           ofp_port_to_odp_port(ofproto, flow.in_port));
 
     ofpbuf_init(&odp_actions, 32);
     compose_sflow_action(ofproto, &odp_actions, &flow, odp_port);
 
     nl_msg_put_u32(&odp_actions, OVS_ACTION_ATTR_OUTPUT, odp_port);
-    error = dpif_execute(ofproto->dpif,
+    error = dpif_execute(ofproto->backer->dpif,
                          key.data, key.size,
                          odp_actions.data, odp_actions.size,
                          packet);
@@ -4824,7 +5271,7 @@ compose_slow_path(const struct ofproto_dpif *ofproto, const struct flow *flow,
 
     ofpbuf_use_stack(&buf, stub, stub_size);
     if (slow & (SLOW_CFM | SLOW_LACP | SLOW_STP)) {
-        uint32_t pid = dpif_port_get_pid(ofproto->dpif, UINT16_MAX);
+        uint32_t pid = dpif_port_get_pid(ofproto->backer->dpif, UINT32_MAX);
         odp_put_userspace_action(pid, &cookie, &buf);
     } else {
         put_userspace_action(ofproto, &buf, flow, &cookie);
@@ -4841,8 +5288,8 @@ put_userspace_action(const struct ofproto_dpif *ofproto,
 {
     uint32_t pid;
 
-    pid = dpif_port_get_pid(ofproto->dpif,
-                            ofp_port_to_odp_port(flow->in_port));
+    pid = dpif_port_get_pid(ofproto->backer->dpif,
+                            ofp_port_to_odp_port(ofproto, flow->in_port));
 
     return odp_put_userspace_action(pid, cookie, odp_actions);
 }
@@ -4950,31 +5397,27 @@ compose_output_action__(struct action_xlate_ctx *ctx, uint16_t ofp_port,
                         bool check_stp)
 {
     const struct ofport_dpif *ofport = get_ofp_port(ctx->ofproto, ofp_port);
-    uint16_t odp_port = ofp_port_to_odp_port(ofp_port);
+    uint32_t odp_port = ofp_port_to_odp_port(ctx->ofproto, ofp_port);
     ovs_be16 flow_vlan_tci = ctx->flow.vlan_tci;
     uint8_t flow_nw_tos = ctx->flow.nw_tos;
-    uint16_t out_port;
-
-    if (ofport) {
-        struct priority_to_dscp *pdscp;
+    struct priority_to_dscp *pdscp;
+    uint32_t out_port;
 
-        if (ofport->up.pp.config & OFPUTIL_PC_NO_FWD) {
-            xlate_report(ctx, "OFPPC_NO_FWD set, skipping output");
-            return;
-        } else if (check_stp && !stp_forward_in_state(ofport->stp_state)) {
-            xlate_report(ctx, "STP not in forwarding state, skipping output");
-            return;
-        }
+    if (!ofport) {
+        xlate_report(ctx, "Nonexistent output port");
+        return;
+    } else if (ofport->up.pp.config & OFPUTIL_PC_NO_FWD) {
+        xlate_report(ctx, "OFPPC_NO_FWD set, skipping output");
+        return;
+    } else if (check_stp && !stp_forward_in_state(ofport->stp_state)) {
+        xlate_report(ctx, "STP not in forwarding state, skipping output");
+        return;
+    }
 
-        pdscp = get_priority(ofport, ctx->flow.skb_priority);
-        if (pdscp) {
-            ctx->flow.nw_tos &= ~IP_DSCP_MASK;
-            ctx->flow.nw_tos |= pdscp->dscp;
-        }
-    } else {
-        /* We may not have an ofport record for this port, but it doesn't hurt
-         * to allow forwarding to it anyhow.  Maybe such a port will appear
-         * later and we're pre-populating the flow table.  */
+    pdscp = get_priority(ofport, ctx->flow.skb_priority);
+    if (pdscp) {
+        ctx->flow.nw_tos &= ~IP_DSCP_MASK;
+        ctx->flow.nw_tos |= pdscp->dscp;
     }
 
     out_port = vsp_realdev_to_vlandev(ctx->ofproto, odp_port,
@@ -5037,7 +5480,7 @@ xlate_table_action(struct action_xlate_ctx *ctx,
         }
 
         if (rule == NULL && may_packet_in) {
-            /* TODO:XXX
+            /* XXX
              * check if table configuration flags
              * OFPTC_TABLE_MISS_CONTROLLER, default.
              * OFPTC_TABLE_MISS_CONTINUE,
@@ -5272,7 +5715,8 @@ xlate_enqueue_action(struct action_xlate_ctx *ctx,
     int error;
 
     /* Translate queue to priority. */
-    error = dpif_queue_to_priority(ctx->ofproto->dpif, queue_id, &priority);
+    error = dpif_queue_to_priority(ctx->ofproto->backer->dpif,
+                                   queue_id, &priority);
     if (error) {
         /* Fall back to ordinary output action. */
         xlate_output_action(ctx, enqueue->port, 0, false);
@@ -5305,7 +5749,8 @@ xlate_set_queue_action(struct action_xlate_ctx *ctx, uint32_t queue_id)
 {
     uint32_t skb_priority;
 
-    if (!dpif_queue_to_priority(ctx->ofproto->dpif, queue_id, &skb_priority)) {
+    if (!dpif_queue_to_priority(ctx->ofproto->backer->dpif,
+                                queue_id, &skb_priority)) {
         ctx->flow.skb_priority = skb_priority;
     } else {
         /* Couldn't translate queue to a priority.  Nothing to do.  A warning
@@ -5501,7 +5946,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
             break;
 
         case OFPACT_PUSH_VLAN:
-            /* TODO:XXX 802.1AD(QinQ) */
+            /* XXX 802.1AD(QinQ) */
             ctx->flow.vlan_tci = htons(VLAN_CFI);
             break;
 
@@ -5607,7 +6052,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
             break;
 
         case OFPACT_CLEAR_ACTIONS:
-            /* TODO:XXX
+            /* XXX
              * Nothing to do because writa-actions is not supported for now.
              * When writa-actions is supported, clear-actions also must
              * be supported at the same time.
@@ -5621,7 +6066,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
             break;
 
         case OFPACT_GOTO_TABLE: {
-            /* TODO:XXX remove recursion */
+            /* XXX remove recursion */
             /* It is assumed that goto-table is last action */
             struct ofpact_goto_table *ogt = ofpact_get_GOTO_TABLE(a);
             assert(ctx->table_id < ogt->table_id);
@@ -5649,11 +6094,35 @@ action_xlate_ctx_init(struct action_xlate_ctx *ctx,
                       ovs_be16 initial_tci, struct rule_dpif *rule,
                       uint8_t tcp_flags, const struct ofpbuf *packet)
 {
+    ovs_be64 initial_tun_id = flow->tunnel.tun_id;
+
+    /* Flow initialization rules:
+     * - 'base_flow' must match the kernel's view of the packet at the
+     *   time that action processing starts.  'flow' represents any
+     *   transformations we wish to make through actions.
+     * - By default 'base_flow' and 'flow' are the same since the input
+     *   packet matches the output before any actions are applied.
+     * - When using VLAN splinters, 'base_flow''s VLAN is set to the value
+     *   of the received packet as seen by the kernel.  If we later output
+     *   to another device without any modifications this will cause us to
+     *   insert a new tag since the original one was stripped off by the
+     *   VLAN device.
+     * - Tunnel 'flow' is largely cleared when transitioning between
+     *   the input and output stages since it does not make sense to output
+     *   a packet with the exact headers that it was received with (i.e.
+     *   the destination IP is us).  The one exception is the tun_id, which
+     *   is preserved to allow use in later resubmit lookups and loads into
+     *   registers.
+     * - Tunnel 'base_flow' is completely cleared since that is what the
+     *   kernel does.  If we wish to maintain the original values an action
+     *   needs to be generated. */
+
     ctx->ofproto = ofproto;
     ctx->flow = *flow;
+    memset(&ctx->flow.tunnel, 0, sizeof ctx->flow.tunnel);
     ctx->base_flow = ctx->flow;
-    memset(&ctx->base_flow.tunnel, 0, sizeof ctx->base_flow.tunnel);
     ctx->base_flow.vlan_tci = initial_tci;
+    ctx->flow.tunnel.tun_id = initial_tun_id;
     ctx->rule = rule;
     ctx->packet = packet;
     ctx->may_learn = packet != NULL;
@@ -5735,6 +6204,7 @@ xlate_actions(struct action_xlate_ctx *ctx,
     } else {
         static struct vlog_rate_limit trace_rl = VLOG_RATE_LIMIT_INIT(1, 1);
         ovs_be16 initial_tci = ctx->base_flow.vlan_tci;
+        uint32_t local_odp_port;
 
         add_sflow_action(ctx);
         do_xlate_actions(ofpacts, ofpacts_len, ctx);
@@ -5755,7 +6225,9 @@ xlate_actions(struct action_xlate_ctx *ctx,
             }
         }
 
+        local_odp_port = ofp_port_to_odp_port(ctx->ofproto, OFPP_LOCAL);
         if (!connmgr_may_set_up_flow(ctx->ofproto->up.connmgr, &ctx->flow,
+                                     local_odp_port,
                                      ctx->odp_actions->data,
                                      ctx->odp_actions->size)) {
             ctx->slow |= SLOW_IN_BAND;
@@ -6477,7 +6949,8 @@ packet_out(struct ofproto *ofproto_, struct ofpbuf *packet,
     struct ofpbuf odp_actions;
 
     ofpbuf_use_stack(&key, &keybuf, sizeof keybuf);
-    odp_flow_key_from_flow(&key, flow);
+    odp_flow_key_from_flow(&key, flow,
+                           ofp_port_to_odp_port(ofproto, flow->in_port));
 
     dpif_flow_stats_extract(flow, packet, time_msec(), &stats);
 
@@ -6488,7 +6961,7 @@ packet_out(struct ofproto *ofproto_, struct ofpbuf *packet,
     ofpbuf_use_stub(&odp_actions,
                     odp_actions_stub, sizeof odp_actions_stub);
     xlate_actions(&ctx, ofpacts, ofpacts_len, &odp_actions);
-    dpif_execute(ofproto->dpif, key.data, key.size,
+    dpif_execute(ofproto->backer->dpif, key.data, key.size,
                  odp_actions.data, odp_actions.size, packet);
     ofpbuf_uninit(&odp_actions);
 
@@ -6521,7 +6994,7 @@ get_netflow_ids(const struct ofproto *ofproto_,
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
 
-    dpif_get_netflow_ids(ofproto->dpif, engine_type, engine_id);
+    dpif_get_netflow_ids(ofproto->backer->dpif, engine_type, engine_id);
 }
 
 static void
@@ -6762,11 +7235,13 @@ ofproto_unixctl_trace(struct unixctl_conn *conn, int argc, const char *argv[],
                 goto exit;
             }
 
-            /* Convert odp_key to flow. */
-            error = ofproto_dpif_extract_flow_key(ofproto, odp_key.data,
-                                                  odp_key.size, &flow,
-                                                  &initial_tci, NULL);
-            if (error == ODP_FIT_ERROR) {
+            /* XXX: Since we allow the user to specify an ofproto, it's
+             * possible they will specify a different ofproto than the one the
+             * port actually belongs too.  Ideally we should simply remove the
+             * ability to specify the ofproto. */
+            if (ofproto_receive(ofproto->backer, NULL, odp_key.data,
+                                odp_key.size, &flow, NULL, NULL, NULL,
+                                &initial_tci)) {
                 unixctl_command_reply_error(conn, "Invalid flow");
                 goto exit;
             }
@@ -6781,7 +7256,6 @@ ofproto_unixctl_trace(struct unixctl_conn *conn, int argc, const char *argv[],
             }
 
             initial_tci = flow.vlan_tci;
-            vsp_adjust_flow(ofproto, &flow);
         }
 
         /* Generate a packet, if requested. */
@@ -6789,15 +7263,17 @@ ofproto_unixctl_trace(struct unixctl_conn *conn, int argc, const char *argv[],
             packet = ofpbuf_new(0);
             flow_compose(packet, &flow);
         }
-    } else if (argc == 6) {
-        /* ofproto/trace dpname priority tun_id in_port packet */
+    } else if (argc == 7) {
+        /* ofproto/trace dpname priority tun_id in_port mark packet */
         const char *priority_s = argv[2];
         const char *tun_id_s = argv[3];
         const char *in_port_s = argv[4];
-        const char *packet_s = argv[5];
-        uint16_t in_port = ofp_port_to_odp_port(atoi(in_port_s));
+        const char *mark_s = argv[5];
+        const char *packet_s = argv[6];
+        uint32_t in_port = atoi(in_port_s);
         ovs_be64 tun_id = htonll(strtoull(tun_id_s, NULL, 0));
         uint32_t priority = atoi(priority_s);
+        uint32_t mark = atoi(mark_s);
         const char *msg;
 
         msg = eth_from_hex(packet_s, &packet);
@@ -6811,7 +7287,7 @@ ofproto_unixctl_trace(struct unixctl_conn *conn, int argc, const char *argv[],
         ds_put_cstr(&result, s);
         free(s);
 
-        flow_extract(packet, priority, NULL, in_port, &flow);
+        flow_extract(packet, priority, mark, NULL, in_port, &flow);
         flow.tunnel.tun_id = tun_id;
         initial_tci = flow.vlan_tci;
     } else {
@@ -6989,6 +7465,212 @@ ofproto_dpif_self_check(struct unixctl_conn *conn,
     ds_destroy(&reply);
 }
 
+/* Store the current ofprotos in 'ofproto_shash'.  Returns a sorted list
+ * of the 'ofproto_shash' nodes.  It is the responsibility of the caller
+ * to destroy 'ofproto_shash' and free the returned value. */
+static const struct shash_node **
+get_ofprotos(struct shash *ofproto_shash)
+{
+    const struct ofproto_dpif *ofproto;
+
+    HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
+        char *name = xasprintf("%s@%s", ofproto->up.type, ofproto->up.name);
+        shash_add_nocopy(ofproto_shash, name, ofproto);
+    }
+
+    return shash_sort(ofproto_shash);
+}
+
+static void
+ofproto_unixctl_dpif_dump_dps(struct unixctl_conn *conn, int argc OVS_UNUSED,
+                              const char *argv[] OVS_UNUSED,
+                              void *aux OVS_UNUSED)
+{
+    struct ds ds = DS_EMPTY_INITIALIZER;
+    struct shash ofproto_shash;
+    const struct shash_node **sorted_ofprotos;
+    int i;
+
+    shash_init(&ofproto_shash);
+    sorted_ofprotos = get_ofprotos(&ofproto_shash);
+    for (i = 0; i < shash_count(&ofproto_shash); i++) {
+        const struct shash_node *node = sorted_ofprotos[i];
+        ds_put_format(&ds, "%s\n", node->name);
+    }
+
+    shash_destroy(&ofproto_shash);
+    free(sorted_ofprotos);
+
+    unixctl_command_reply(conn, ds_cstr(&ds));
+    ds_destroy(&ds);
+}
+
+static void
+show_dp_format(const struct ofproto_dpif *ofproto, struct ds *ds)
+{
+    struct dpif_dp_stats s;
+    const struct shash_node **ports;
+    int i;
+
+    dpif_get_dp_stats(ofproto->backer->dpif, &s);
+
+    ds_put_format(ds, "%s (%s):\n", ofproto->up.name,
+                  dpif_name(ofproto->backer->dpif));
+    /* xxx It would be better to show bridge-specific stats instead
+     * xxx of dp ones. */
+    ds_put_format(ds,
+                  "\tlookups: hit:%"PRIu64" missed:%"PRIu64" lost:%"PRIu64"\n",
+                  s.n_hit, s.n_missed, s.n_lost);
+    ds_put_format(ds, "\tflows: %zu\n",
+                  hmap_count(&ofproto->subfacets));
+
+    ports = shash_sort(&ofproto->up.port_by_name);
+    for (i = 0; i < shash_count(&ofproto->up.port_by_name); i++) {
+        const struct shash_node *node = ports[i];
+        struct ofport *ofport = node->data;
+        const char *name = netdev_get_name(ofport->netdev);
+        const char *type = netdev_get_type(ofport->netdev);
+
+        ds_put_format(ds, "\t%s %u/%u:", name, ofport->ofp_port,
+                      ofp_port_to_odp_port(ofproto, ofport->ofp_port));
+        if (strcmp(type, "system")) {
+            struct netdev *netdev;
+            int error;
+
+            ds_put_format(ds, " (%s", type);
+
+            error = netdev_open(name, type, &netdev);
+            if (!error) {
+                struct smap config;
+
+                smap_init(&config);
+                error = netdev_get_config(netdev, &config);
+                if (!error) {
+                    const struct smap_node **nodes;
+                    size_t i;
+
+                    nodes = smap_sort(&config);
+                    for (i = 0; i < smap_count(&config); i++) {
+                        const struct smap_node *node = nodes[i];
+                        ds_put_format(ds, "%c %s=%s", i ? ',' : ':',
+                                      node->key, node->value);
+                    }
+                    free(nodes);
+                }
+                smap_destroy(&config);
+
+                netdev_close(netdev);
+            }
+            ds_put_char(ds, ')');
+        }
+        ds_put_char(ds, '\n');
+    }
+    free(ports);
+}
+
+static void
+ofproto_unixctl_dpif_show(struct unixctl_conn *conn, int argc,
+                          const char *argv[], void *aux OVS_UNUSED)
+{
+    struct ds ds = DS_EMPTY_INITIALIZER;
+    const struct ofproto_dpif *ofproto;
+
+    if (argc > 1) {
+        int i;
+        for (i = 1; i < argc; i++) {
+            ofproto = ofproto_dpif_lookup(argv[i]);
+            if (!ofproto) {
+                ds_put_format(&ds, "Unknown bridge %s (use dpif/dump-dps "
+                                   "for help)", argv[i]);
+                unixctl_command_reply_error(conn, ds_cstr(&ds));
+                return;
+            }
+            show_dp_format(ofproto, &ds);
+        }
+    } else {
+        struct shash ofproto_shash;
+        const struct shash_node **sorted_ofprotos;
+        int i;
+
+        shash_init(&ofproto_shash);
+        sorted_ofprotos = get_ofprotos(&ofproto_shash);
+        for (i = 0; i < shash_count(&ofproto_shash); i++) {
+            const struct shash_node *node = sorted_ofprotos[i];
+            show_dp_format(node->data, &ds);
+        }
+
+        shash_destroy(&ofproto_shash);
+        free(sorted_ofprotos);
+    }
+
+    unixctl_command_reply(conn, ds_cstr(&ds));
+    ds_destroy(&ds);
+}
+
+static void
+ofproto_unixctl_dpif_dump_flows(struct unixctl_conn *conn,
+                                int argc OVS_UNUSED, const char *argv[],
+                                void *aux OVS_UNUSED)
+{
+    struct ds ds = DS_EMPTY_INITIALIZER;
+    const struct ofproto_dpif *ofproto;
+    struct subfacet *subfacet;
+
+    ofproto = ofproto_dpif_lookup(argv[1]);
+    if (!ofproto) {
+        unixctl_command_reply_error(conn, "no such bridge");
+        return;
+    }
+
+    HMAP_FOR_EACH (subfacet, hmap_node, &ofproto->subfacets) {
+        struct odputil_keybuf keybuf;
+        struct ofpbuf key;
+
+        subfacet_get_key(subfacet, &keybuf, &key);
+        odp_flow_key_format(key.data, key.size, &ds);
+
+        ds_put_format(&ds, ", packets:%"PRIu64", bytes:%"PRIu64", used:",
+                      subfacet->dp_packet_count, subfacet->dp_byte_count);
+        if (subfacet->used) {
+            ds_put_format(&ds, "%.3fs",
+                          (time_msec() - subfacet->used) / 1000.0);
+        } else {
+            ds_put_format(&ds, "never");
+        }
+        if (subfacet->facet->tcp_flags) {
+            ds_put_cstr(&ds, ", flags:");
+            packet_format_tcp_flags(&ds, subfacet->facet->tcp_flags);
+        }
+
+        ds_put_cstr(&ds, ", actions:");
+        format_odp_actions(&ds, subfacet->actions, subfacet->actions_len);
+        ds_put_char(&ds, '\n');
+    }
+
+    unixctl_command_reply(conn, ds_cstr(&ds));
+    ds_destroy(&ds);
+}
+
+static void
+ofproto_unixctl_dpif_del_flows(struct unixctl_conn *conn,
+                               int argc OVS_UNUSED, const char *argv[],
+                               void *aux OVS_UNUSED)
+{
+    struct ds ds = DS_EMPTY_INITIALIZER;
+    struct ofproto_dpif *ofproto;
+
+    ofproto = ofproto_dpif_lookup(argv[1]);
+    if (!ofproto) {
+        unixctl_command_reply_error(conn, "no such bridge");
+        return;
+    }
+
+    flush(&ofproto->up);
+
+    unixctl_command_reply(conn, ds_cstr(&ds));
+    ds_destroy(&ds);
+}
+
 static void
 ofproto_dpif_unixctl_init(void)
 {
@@ -7000,8 +7682,8 @@ ofproto_dpif_unixctl_init(void)
 
     unixctl_command_register(
         "ofproto/trace",
-        "bridge {tun_id in_port packet | odp_flow [-generate]}",
-        2, 5, ofproto_unixctl_trace, NULL);
+        "bridge {priority tun_id in_port mark packet | odp_flow [-generate]}",
+        2, 6, ofproto_unixctl_trace, NULL);
     unixctl_command_register("fdb/flush", "[bridge]", 0, 1,
                              ofproto_unixctl_fdb_flush, NULL);
     unixctl_command_register("fdb/show", "bridge", 1, 1,
@@ -7012,6 +7694,14 @@ ofproto_dpif_unixctl_init(void)
                              ofproto_dpif_unclog, NULL);
     unixctl_command_register("ofproto/self-check", "[bridge]", 0, 1,
                              ofproto_dpif_self_check, NULL);
+    unixctl_command_register("dpif/dump-dps", "", 0, 0,
+                             ofproto_unixctl_dpif_dump_dps, NULL);
+    unixctl_command_register("dpif/show", "[bridge]", 0, INT_MAX,
+                             ofproto_unixctl_dpif_show, NULL);
+    unixctl_command_register("dpif/dump-flows", "bridge", 1, 1,
+                             ofproto_unixctl_dpif_dump_flows, NULL);
+    unixctl_command_register("dpif/del-flows", "bridge", 1, 1,
+                             ofproto_unixctl_dpif_del_flows, NULL);
 }
 \f
 /* Linux VLAN device support (e.g. "eth0.10" for VLAN 10.)
@@ -7071,16 +7761,17 @@ vsp_realdev_to_vlandev(const struct ofproto_dpif *ofproto,
                        uint32_t realdev_odp_port, ovs_be16 vlan_tci)
 {
     if (!hmap_is_empty(&ofproto->realdev_vid_map)) {
-        uint16_t realdev_ofp_port = odp_port_to_ofp_port(realdev_odp_port);
+        uint16_t realdev_ofp_port;
         int vid = vlan_tci_to_vid(vlan_tci);
         const struct vlan_splinter *vsp;
 
+        realdev_ofp_port = odp_port_to_ofp_port(ofproto, realdev_odp_port);
         HMAP_FOR_EACH_WITH_HASH (vsp, realdev_vid_node,
                                  hash_realdev_vid(realdev_ofp_port, vid),
                                  &ofproto->realdev_vid_map) {
             if (vsp->realdev_ofp_port == realdev_ofp_port
                 && vsp->vid == vid) {
-                return ofp_port_to_odp_port(vsp->vlandev_ofp_port);
+                return ofp_port_to_odp_port(ofproto, vsp->vlandev_ofp_port);
             }
         }
     }
@@ -7195,11 +7886,52 @@ vsp_add(struct ofport_dpif *port, uint16_t realdev_ofp_port, int vid)
         VLOG_ERR("duplicate vlan device record");
     }
 }
-\f
+
+static uint32_t
+ofp_port_to_odp_port(const struct ofproto_dpif *ofproto, uint16_t ofp_port)
+{
+    const struct ofport_dpif *ofport = get_ofp_port(ofproto, ofp_port);
+    return ofport ? ofport->odp_port : OVSP_NONE;
+}
+
+static struct ofport_dpif *
+odp_port_to_ofport(const struct dpif_backer *backer, uint32_t odp_port)
+{
+    struct ofport_dpif *port;
+
+    HMAP_FOR_EACH_IN_BUCKET (port, odp_port_node,
+                             hash_int(odp_port, 0),
+                             &backer->odp_to_ofport_map) {
+        if (port->odp_port == odp_port) {
+            return port;
+        }
+    }
+
+    return NULL;
+}
+
+static uint16_t
+odp_port_to_ofp_port(const struct ofproto_dpif *ofproto, uint32_t odp_port)
+{
+    struct ofport_dpif *port;
+
+    port = odp_port_to_ofport(ofproto->backer, odp_port);
+    if (port && &ofproto->up == port->up.ofproto) {
+        return port->up.ofp_port;
+    } else {
+        return OFPP_NONE;
+    }
+}
+
 const struct ofproto_class ofproto_dpif_class = {
+    init,
     enumerate_types,
     enumerate_names,
     del,
+    port_open_type,
+    type_run,
+    type_run_fast,
+    type_wait,
     alloc,
     construct,
     destruct,
@@ -7257,6 +7989,6 @@ const struct ofproto_class ofproto_dpif_class = {
     set_flood_vlans,
     is_mirror_output_bundle,
     forward_bpdu_changed,
-    set_mac_idle_time,
+    set_mac_table_config,
     set_realdev,
 };
index a62473b..f2274ef 100644 (file)
 #include "ofp-errors.h"
 #include "ofp-util.h"
 #include "shash.h"
+#include "simap.h"
 #include "timeval.h"
 
 struct match;
 struct ofpact;
 struct ofputil_flow_mod;
-struct simap;
 
 /* An OpenFlow switch.
  *
@@ -52,16 +52,19 @@ struct ofproto {
                                        * ofproto-dpif implementation */
     bool forward_bpdu;          /* Option to allow forwarding of BPDU frames
                                  * when NORMAL action is invoked. */
-    char *mfr_desc;             /* Manufacturer. */
-    char *hw_desc;              /* Hardware. */
-    char *sw_desc;              /* Software version. */
-    char *serial_desc;          /* Serial number. */
-    char *dp_desc;              /* Datapath description. */
+    char *mfr_desc;             /* Manufacturer (NULL for default)b. */
+    char *hw_desc;              /* Hardware (NULL for default). */
+    char *sw_desc;              /* Software version (NULL for default). */
+    char *serial_desc;          /* Serial number (NULL for default). */
+    char *dp_desc;              /* Datapath description (NULL for default). */
     enum ofp_config_flags frag_handling; /* One of OFPC_*.  */
 
     /* Datapath. */
     struct hmap ports;          /* Contains "struct ofport"s. */
     struct shash port_by_name;
+    unsigned long *ofp_port_ids;/* Bitmap of used OpenFlow port numbers. */
+    struct simap ofp_requests;  /* OpenFlow port number requests. */
+    uint16_t alloc_port_no;     /* Last allocated OpenFlow port number. */
     uint16_t max_ports;         /* Max possible OpenFlow port num, plus one. */
 
     /* Flow tables. */
@@ -116,6 +119,29 @@ struct ofport {
 
 void ofproto_port_set_state(struct ofport *, enum ofputil_port_state);
 
+/* OpenFlow table flags:
+ *
+ *   - "Hidden" tables are not included in OpenFlow operations that operate on
+ *     "all tables".  For example, a request for flow stats on all tables will
+ *     omit flows in hidden tables, table stats requests will omit the table
+ *     entirely, and the switch features reply will not count the hidden table.
+ *
+ *     However, operations that specifically name the particular table still
+ *     operate on it.  For example, flow_mods and flow stats requests on a
+ *     hidden table work.
+ *
+ *     To avoid gaps in table IDs (which have unclear validity in OpenFlow),
+ *     hidden tables must be the highest-numbered tables that a provider
+ *     implements.
+ *
+ *   - "Read-only" tables can't be changed through OpenFlow operations.  (At
+ *     the moment all flow table operations go effectively through OpenFlow, so
+ *     this means that read-only tables can't be changed at all after the
+ *     read-only flag is set.)
+ *
+ * The generic ofproto layer never sets these flags.  An ofproto provider can
+ * set them if it is appropriate.
+ */
 enum oftable_flags {
     OFTABLE_HIDDEN = 1 << 0,   /* Hide from most OpenFlow operations. */
     OFTABLE_READONLY = 1 << 1  /* Don't allow OpenFlow to change this table. */
@@ -320,6 +346,16 @@ struct ofproto_class {
 /* ## Factory Functions ## */
 /* ## ----------------- ## */
 
+    /* Initializes provider.  The caller may pass in 'iface_hints',
+     * which contains an shash of "struct iface_hint" elements indexed
+     * by the interface's name.  The provider may use these hints to
+     * describe the startup configuration in order to reinitialize its
+     * state.  The caller owns the provided data, so a provider must
+     * make copies of anything required.  An ofproto provider must
+     * remove any existing state that is not described by the hint, and
+     * may choose to remove it all. */
+    void (*init)(const struct shash *iface_hints);
+
     /* Enumerates the types of all support ofproto types into 'types'.  The
      * caller has already initialized 'types' and other ofproto classes might
      * already have added names to it. */
@@ -346,6 +382,48 @@ struct ofproto_class {
      */
     int (*del)(const char *type, const char *name);
 
+    /* Returns the type to pass to netdev_open() when a datapath of type
+     * 'datapath_type' has a port of type 'port_type', for a few special
+     * cases when a netdev type differs from a port type.  For example,
+     * when using the userspace datapath, a port of type "internal"
+     * needs to be opened as "tap".
+     *
+     * Returns either 'type' itself or a string literal, which must not
+     * be freed. */
+    const char *(*port_open_type)(const char *datapath_type,
+                                  const char *port_type);
+
+/* ## ------------------------ ## */
+/* ## Top-Level type Functions ## */
+/* ## ------------------------ ## */
+
+    /* Performs any periodic activity required on ofprotos of type
+     * 'type'.
+     *
+     * An ofproto provider may implement it or not, depending on whether
+     * it needs type-level maintenance.
+     *
+     * Returns 0 if successful, otherwise a positive errno value. */
+    int (*type_run)(const char *type);
+
+    /* Performs periodic activity required on ofprotos of type 'type'
+     * that needs to be done with the least possible latency.
+     *
+     * This is run multiple times per main loop.  An ofproto provider may
+     * implement it or not, according to whether it provides a performance
+     * boost for that ofproto implementation.
+     *
+     * Returns 0 if successful, otherwise a positive errno value. */
+    int (*type_run_fast)(const char *type);
+
+    /* Causes the poll loop to wake up when a type 'type''s 'run'
+     * function needs to be called, e.g. by calling the timer or fd
+     * waiting functions in poll-loop.h.
+     *
+     * An ofproto provider may implement it or not, depending on whether
+     * it needs type-level maintenance. */
+    void (*type_wait)(const char *type);
+
 /* ## --------------------------- ## */
 /* ## Top-Level ofproto Functions ## */
 /* ## --------------------------- ## */
@@ -526,6 +604,8 @@ struct ofproto_class {
     /* Life-cycle functions for a "struct ofport" (see "Life Cycle" above).
      *
      * ->port_construct() should not modify any base members of the ofport.
+     * An ofproto implementation should use the 'ofp_port' member of
+     * "struct ofport" as the OpenFlow port number.
      *
      * ofports are managed by the base ofproto code.  The ofproto
      * implementation should only create and destroy them in response to calls
@@ -584,14 +664,14 @@ struct ofproto_class {
                               const char *devname, struct ofproto_port *port);
 
     /* Attempts to add 'netdev' as a port on 'ofproto'.  Returns 0 if
-     * successful, otherwise a positive errno value.  If successful, sets
-     * '*ofp_portp' to the new port's port number.
+     * successful, otherwise a positive errno value.  The caller should
+     * inform the implementation of the OpenFlow port through the
+     * ->port_construct() method.
      *
      * It doesn't matter whether the new port will be returned by a later call
      * to ->port_poll(); the implementation may do whatever is more
      * convenient. */
-    int (*port_add)(struct ofproto *ofproto, struct netdev *netdev,
-                    uint16_t *ofp_portp);
+    int (*port_add)(struct ofproto *ofproto, struct netdev *netdev);
 
     /* Deletes port number 'ofp_port' from the datapath for 'ofproto'.  Returns
      * 0 if successful, otherwise a positive errno value.
@@ -609,11 +689,9 @@ struct ofproto_class {
      *
      * The client might not be entirely in control of the ports within an
      * ofproto.  Some hardware implementations, for example, might have a fixed
-     * set of ports in a datapath, and the Linux datapath allows the system
-     * administrator to externally add and remove ports with ovs-dpctl.  For
-     * this reason, the client needs a way to iterate through all the ports
-     * that are actually in a datapath.  These functions provide that
-     * functionality.
+     * set of ports in a datapath.  For this reason, the client needs a way to
+     * iterate through all the ports that are actually in a datapath.  These
+     * functions provide that functionality.
      *
      * The 'state' pointer provides the implementation a place to
      * keep track of its position.  Its format is opaque to the caller.
@@ -1201,9 +1279,14 @@ struct ofproto_class {
      * will be invoked. */
     void (*forward_bpdu_changed)(struct ofproto *ofproto);
 
-    /* Sets the MAC aging timeout for the OFPP_NORMAL action to 'idle_time',
-     * in seconds. */
-    void (*set_mac_idle_time)(struct ofproto *ofproto, unsigned int idle_time);
+    /* Sets the MAC aging timeout for the OFPP_NORMAL action to 'idle_time', in
+     * seconds, and the maximum number of MAC table entries to
+     * 'max_entries'.
+     *
+     * An implementation that doesn't support configuring these features may
+     * set this function to NULL or implement it as a no-op. */
+    void (*set_mac_table_config)(struct ofproto *ofproto,
+                                 unsigned int idle_time, size_t max_entries);
 
 /* Linux VLAN device support (e.g. "eth0.10" for VLAN 10.)
  *
index 3e36fe6..8890343 100644 (file)
@@ -6,7 +6,7 @@ These commands manage the core OpenFlow switch implementation (called
 Lists the names of the running ofproto instances.  These are the names
 that may be used on \fBofproto/trace\fR.
 .
-.IP "\fBofproto/trace \fIswitch priority tun_id in_port packet\fR"
+.IP "\fBofproto/trace \fIswitch priority tun_id in_port mark packet\fR"
 .IQ "\fBofproto/trace \fIswitch flow \fB\-generate\fR"
 Traces the path of an imaginary packet through \fIswitch\fR.  Both
 forms require \fIswitch\fR, the switch on which the packet arrived
@@ -21,6 +21,8 @@ The tunnel ID on which the packet arrived.  Use
 .IP "\fIin_port\fR"
 The OpenFlow port on which the packet arrived.  Use \fB65534\fR if the
 packet arrived on \fBOFPP_LOCAL\fR, the local port.
+.IP "\fImark\fR"
+SKB mark of the packet. Use \fB0\fR if Netfilter marks are not used.
 .IP "\fIpacket\fR"
 A sequence of hex digits specifying the packet's contents.  An
 Ethernet frame is at least 14 bytes long, so there must be at least 28
index 2fb2fc8..8e2ea6b 100644 (file)
@@ -221,16 +221,43 @@ static size_t allocated_ofproto_classes;
 /* Map from datapath name to struct ofproto, for use by unixctl commands. */
 static struct hmap all_ofprotos = HMAP_INITIALIZER(&all_ofprotos);
 
+/* Initial mappings of port to OpenFlow number mappings. */
+static struct shash init_ofp_ports = SHASH_INITIALIZER(&init_ofp_ports);
+
 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
 
-static void
-ofproto_initialize(void)
+/* Must be called to initialize the ofproto library.
+ *
+ * The caller may pass in 'iface_hints', which contains an shash of
+ * "iface_hint" elements indexed by the interface's name.  The provider
+ * may use these hints to describe the startup configuration in order to
+ * reinitialize its state.  The caller owns the provided data, so a
+ * provider will make copies of anything required.  An ofproto provider
+ * will remove any existing state that is not described by the hint, and
+ * may choose to remove it all. */
+void
+ofproto_init(const struct shash *iface_hints)
 {
-    static bool inited;
+    struct shash_node *node;
+    size_t i;
+
+    ofproto_class_register(&ofproto_dpif_class);
+
+    /* Make a local copy, since we don't own 'iface_hints' elements. */
+    SHASH_FOR_EACH(node, iface_hints) {
+        const struct iface_hint *orig_hint = node->data;
+        struct iface_hint *new_hint = xmalloc(sizeof *new_hint);
+        const char *br_type = ofproto_normalize_type(orig_hint->br_type);
+
+        new_hint->br_name = xstrdup(orig_hint->br_name);
+        new_hint->br_type = xstrdup(br_type);
+        new_hint->ofp_port = orig_hint->ofp_port;
+
+        shash_add(&init_ofp_ports, node->name, new_hint);
+    }
 
-    if (!inited) {
-        inited = true;
-        ofproto_class_register(&ofproto_dpif_class);
+    for (i = 0; i < n_ofproto_classes; i++) {
+        ofproto_classes[i]->init(&init_ofp_ports);
     }
 }
 
@@ -242,7 +269,6 @@ ofproto_class_find__(const char *type)
 {
     size_t i;
 
-    ofproto_initialize();
     for (i = 0; i < n_ofproto_classes; i++) {
         const struct ofproto_class *class = ofproto_classes[i];
         struct sset types;
@@ -313,7 +339,6 @@ ofproto_enumerate_types(struct sset *types)
 {
     size_t i;
 
-    ofproto_initialize();
     for (i = 0; i < n_ofproto_classes; i++) {
         ofproto_classes[i]->enumerate_types(types);
     }
@@ -349,10 +374,10 @@ ofproto_create(const char *datapath_name, const char *datapath_type,
     const struct ofproto_class *class;
     struct ofproto *ofproto;
     int error;
+    int i;
 
     *ofprotop = NULL;
 
-    ofproto_initialize();
     ofproto_unixctl_init();
 
     datapath_type = ofproto_normalize_type(datapath_type);
@@ -382,14 +407,15 @@ ofproto_create(const char *datapath_name, const char *datapath_type,
                                         OFPROTO_FLOW_EVICTION_THRESHOLD_DEFAULT);
     ofproto->forward_bpdu = false;
     ofproto->fallback_dpid = pick_fallback_dpid();
-    ofproto->mfr_desc = xstrdup(DEFAULT_MFR_DESC);
-    ofproto->hw_desc = xstrdup(DEFAULT_HW_DESC);
-    ofproto->sw_desc = xstrdup(DEFAULT_SW_DESC);
-    ofproto->serial_desc = xstrdup(DEFAULT_SERIAL_DESC);
-    ofproto->dp_desc = xstrdup(DEFAULT_DP_DESC);
+    ofproto->mfr_desc = NULL;
+    ofproto->hw_desc = NULL;
+    ofproto->sw_desc = NULL;
+    ofproto->serial_desc = NULL;
+    ofproto->dp_desc = NULL;
     ofproto->frag_handling = OFPC_FRAG_NORMAL;
     hmap_init(&ofproto->ports);
     shash_init(&ofproto->port_by_name);
+    simap_init(&ofproto->ofp_requests);
     ofproto->max_ports = OFPP_MAX;
     ofproto->tables = NULL;
     ofproto->n_tables = 0;
@@ -414,7 +440,19 @@ ofproto_create(const char *datapath_name, const char *datapath_type,
         return error;
     }
 
+    /* The "max_ports" member should have been set by ->construct(ofproto).
+     * Port 0 is not a valid OpenFlow port, so mark that as unavailable. */
+    ofproto->ofp_port_ids = bitmap_allocate(ofproto->max_ports);
+    bitmap_set1(ofproto->ofp_port_ids, 0);
+
+    /* Check that hidden tables, if any, are at the end. */
     assert(ofproto->n_tables);
+    for (i = 0; i + 1 < ofproto->n_tables; i++) {
+        enum oftable_flags flags = ofproto->tables[i].flags;
+        enum oftable_flags next_flags = ofproto->tables[i + 1].flags;
+
+        assert(!(flags & OFTABLE_HIDDEN) || next_flags & OFTABLE_HIDDEN);
+    }
 
     ofproto->datapath_id = pick_datapath_id(ofproto);
     init_ports(ofproto);
@@ -480,9 +518,10 @@ ofproto_set_datapath_id(struct ofproto *p, uint64_t datapath_id)
 void
 ofproto_set_controllers(struct ofproto *p,
                         const struct ofproto_controller *controllers,
-                        size_t n_controllers)
+                        size_t n_controllers, uint32_t allowed_versions)
 {
-    connmgr_set_controllers(p->connmgr, controllers, n_controllers);
+    connmgr_set_controllers(p->connmgr, controllers, n_controllers,
+                            allowed_versions);
 }
 
 void
@@ -546,63 +585,23 @@ ofproto_set_forward_bpdu(struct ofproto *ofproto, bool forward_bpdu)
 }
 
 /* Sets the MAC aging timeout for the OFPP_NORMAL action on 'ofproto' to
- * 'idle_time', in seconds. */
+ * 'idle_time', in seconds, and the maximum number of MAC table entries to
+ * 'max_entries'. */
 void
-ofproto_set_mac_idle_time(struct ofproto *ofproto, unsigned idle_time)
+ofproto_set_mac_table_config(struct ofproto *ofproto, unsigned idle_time,
+                             size_t max_entries)
 {
-    if (ofproto->ofproto_class->set_mac_idle_time) {
-        ofproto->ofproto_class->set_mac_idle_time(ofproto, idle_time);
+    if (ofproto->ofproto_class->set_mac_table_config) {
+        ofproto->ofproto_class->set_mac_table_config(ofproto, idle_time,
+                                                     max_entries);
     }
 }
 
 void
-ofproto_set_desc(struct ofproto *p,
-                 const char *mfr_desc, const char *hw_desc,
-                 const char *sw_desc, const char *serial_desc,
-                 const char *dp_desc)
+ofproto_set_dp_desc(struct ofproto *p, const char *dp_desc)
 {
-    struct ofp_desc_stats *ods;
-
-    if (mfr_desc) {
-        if (strlen(mfr_desc) >= sizeof ods->mfr_desc) {
-            VLOG_WARN("%s: truncating mfr_desc, must be less than %zu bytes",
-                      p->name, sizeof ods->mfr_desc);
-        }
-        free(p->mfr_desc);
-        p->mfr_desc = xstrdup(mfr_desc);
-    }
-    if (hw_desc) {
-        if (strlen(hw_desc) >= sizeof ods->hw_desc) {
-            VLOG_WARN("%s: truncating hw_desc, must be less than %zu bytes",
-                      p->name, sizeof ods->hw_desc);
-        }
-        free(p->hw_desc);
-        p->hw_desc = xstrdup(hw_desc);
-    }
-    if (sw_desc) {
-        if (strlen(sw_desc) >= sizeof ods->sw_desc) {
-            VLOG_WARN("%s: truncating sw_desc, must be less than %zu bytes",
-                      p->name, sizeof ods->sw_desc);
-        }
-        free(p->sw_desc);
-        p->sw_desc = xstrdup(sw_desc);
-    }
-    if (serial_desc) {
-        if (strlen(serial_desc) >= sizeof ods->serial_num) {
-            VLOG_WARN("%s: truncating serial_desc, must be less than %zu "
-                      "bytes", p->name, sizeof ods->serial_num);
-        }
-        free(p->serial_desc);
-        p->serial_desc = xstrdup(serial_desc);
-    }
-    if (dp_desc) {
-        if (strlen(dp_desc) >= sizeof ods->dp_desc) {
-            VLOG_WARN("%s: truncating dp_desc, must be less than %zu bytes",
-                      p->name, sizeof ods->dp_desc);
-        }
-        free(p->dp_desc);
-        p->dp_desc = xstrdup(dp_desc);
-    }
+    free(p->dp_desc);
+    p->dp_desc = dp_desc ? xstrdup(dp_desc) : NULL;
 }
 
 int
@@ -1008,6 +1007,8 @@ ofproto_destroy__(struct ofproto *ofproto)
     free(ofproto->dp_desc);
     hmap_destroy(&ofproto->ports);
     shash_destroy(&ofproto->port_by_name);
+    bitmap_free(ofproto->ofp_port_ids);
+    simap_destroy(&ofproto->ofp_requests);
 
     OFPROTO_FOR_EACH_TABLE (table, ofproto) {
         oftable_destroy(table);
@@ -1065,6 +1066,53 @@ process_port_change(struct ofproto *ofproto, int error, char *devname)
     }
 }
 
+int
+ofproto_type_run(const char *datapath_type)
+{
+    const struct ofproto_class *class;
+    int error;
+
+    datapath_type = ofproto_normalize_type(datapath_type);
+    class = ofproto_class_find__(datapath_type);
+
+    error = class->type_run ? class->type_run(datapath_type) : 0;
+    if (error && error != EAGAIN) {
+        VLOG_ERR_RL(&rl, "%s: type_run failed (%s)",
+                    datapath_type, strerror(error));
+    }
+    return error;
+}
+
+int
+ofproto_type_run_fast(const char *datapath_type)
+{
+    const struct ofproto_class *class;
+    int error;
+
+    datapath_type = ofproto_normalize_type(datapath_type);
+    class = ofproto_class_find__(datapath_type);
+
+    error = class->type_run_fast ? class->type_run_fast(datapath_type) : 0;
+    if (error && error != EAGAIN) {
+        VLOG_ERR_RL(&rl, "%s: type_run_fast failed (%s)",
+                    datapath_type, strerror(error));
+    }
+    return error;
+}
+
+void
+ofproto_type_wait(const char *datapath_type)
+{
+    const struct ofproto_class *class;
+
+    datapath_type = ofproto_normalize_type(datapath_type);
+    class = ofproto_class_find__(datapath_type);
+
+    if (class->type_wait) {
+        class->type_wait(datapath_type);
+    }
+}
+
 int
 ofproto_run(struct ofproto *p)
 {
@@ -1345,23 +1393,59 @@ ofproto_port_dump_done(struct ofproto_port_dump *dump)
     return dump->error == EOF ? 0 : dump->error;
 }
 
-/* Attempts to add 'netdev' as a port on 'ofproto'.  If successful, returns 0
- * and sets '*ofp_portp' to the new port's OpenFlow port number (if 'ofp_portp'
- * is non-null).  On failure, returns a positive errno value and sets
- * '*ofp_portp' to OFPP_NONE (if 'ofp_portp' is non-null). */
+/* Returns the type to pass to netdev_open() when a datapath of type
+ * 'datapath_type' has a port of type 'port_type', for a few special
+ * cases when a netdev type differs from a port type.  For example, when
+ * using the userspace datapath, a port of type "internal" needs to be
+ * opened as "tap".
+ *
+ * Returns either 'type' itself or a string literal, which must not be
+ * freed. */
+const char *
+ofproto_port_open_type(const char *datapath_type, const char *port_type)
+{
+    const struct ofproto_class *class;
+
+    datapath_type = ofproto_normalize_type(datapath_type);
+    class = ofproto_class_find__(datapath_type);
+    if (!class) {
+        return port_type;
+    }
+
+    return (class->port_open_type
+            ? class->port_open_type(datapath_type, port_type)
+            : port_type);
+}
+
+/* Attempts to add 'netdev' as a port on 'ofproto'.  If 'ofp_portp' is
+ * non-null and '*ofp_portp' is not OFPP_NONE, attempts to use that as
+ * the port's OpenFlow port number.
+ *
+ * If successful, returns 0 and sets '*ofp_portp' to the new port's
+ * OpenFlow port number (if 'ofp_portp' is non-null).  On failure,
+ * returns a positive errno value and sets '*ofp_portp' to OFPP_NONE (if
+ * 'ofp_portp' is non-null). */
 int
 ofproto_port_add(struct ofproto *ofproto, struct netdev *netdev,
                  uint16_t *ofp_portp)
 {
-    uint16_t ofp_port;
+    uint16_t ofp_port = ofp_portp ? *ofp_portp : OFPP_NONE;
     int error;
 
-    error = ofproto->ofproto_class->port_add(ofproto, netdev, &ofp_port);
+    error = ofproto->ofproto_class->port_add(ofproto, netdev);
     if (!error) {
-        update_port(ofproto, netdev_get_name(netdev));
+        const char *netdev_name = netdev_get_name(netdev);
+
+        simap_put(&ofproto->ofp_requests, netdev_name, ofp_port);
+        update_port(ofproto, netdev_name);
     }
     if (ofp_portp) {
-        *ofp_portp = error ? OFPP_NONE : ofp_port;
+        struct ofproto_port ofproto_port;
+
+        ofproto_port_query_by_name(ofproto, netdev_get_name(netdev),
+                                   &ofproto_port);
+        *ofp_portp = error ? OFPP_NONE : ofproto_port.ofp_port;
+        ofproto_port_destroy(&ofproto_port);
     }
     return error;
 }
@@ -1392,8 +1476,14 @@ ofproto_port_del(struct ofproto *ofproto, uint16_t ofp_port)
 {
     struct ofport *ofport = ofproto_get_port(ofproto, ofp_port);
     const char *name = ofport ? netdev_get_name(ofport->netdev) : "<unknown>";
+    struct simap_node *ofp_request_node;
     int error;
 
+    ofp_request_node = simap_find(&ofproto->ofp_requests, name);
+    if (ofp_request_node) {
+        simap_delete(&ofproto->ofp_requests, ofp_request_node);
+    }
+
     error = ofproto->ofproto_class->port_del(ofproto, ofp_port);
     if (!error && ofport) {
         /* 'name' is the netdev's name and update_port() is going to close the
@@ -1519,12 +1609,50 @@ reinit_ports(struct ofproto *p)
     sset_destroy(&devnames);
 }
 
+static uint16_t
+alloc_ofp_port(struct ofproto *ofproto, const char *netdev_name)
+{
+    uint16_t ofp_port;
+    uint16_t end_port_no = ofproto->alloc_port_no;
+
+    ofp_port = simap_get(&ofproto->ofp_requests, netdev_name);
+    ofp_port = ofp_port ? ofp_port : OFPP_NONE;
+
+    if (ofp_port >= ofproto->max_ports
+            || bitmap_is_set(ofproto->ofp_port_ids, ofp_port)) {
+        /* Search for a free OpenFlow port number.  We try not to
+         * immediately reuse them to prevent problems due to old
+         * flows. */
+        for (;;) {
+            if (++ofproto->alloc_port_no >= ofproto->max_ports) {
+                ofproto->alloc_port_no = 0;
+            }
+            if (!bitmap_is_set(ofproto->ofp_port_ids,
+                               ofproto->alloc_port_no)) {
+                ofp_port = ofproto->alloc_port_no;
+                break;
+            }
+            if (ofproto->alloc_port_no == end_port_no) {
+                return OFPP_NONE;
+            }
+        }
+    }
+    bitmap_set1(ofproto->ofp_port_ids, ofp_port);
+    return ofp_port;
+}
+
+static void
+dealloc_ofp_port(const struct ofproto *ofproto, uint16_t ofp_port)
+{
+    bitmap_set0(ofproto->ofp_port_ids, ofp_port);
+}
+
 /* Opens and returns a netdev for 'ofproto_port' in 'ofproto', or a null
  * pointer if the netdev cannot be opened.  On success, also fills in
  * 'opp'.  */
 static struct netdev *
-ofport_open(const struct ofproto *ofproto,
-            const struct ofproto_port *ofproto_port,
+ofport_open(struct ofproto *ofproto,
+            struct ofproto_port *ofproto_port,
             struct ofputil_phy_port *pp)
 {
     enum netdev_flags flags;
@@ -1541,6 +1669,14 @@ ofport_open(const struct ofproto *ofproto,
         return NULL;
     }
 
+    if (ofproto_port->ofp_port == OFPP_NONE) {
+        if (!strcmp(ofproto->name, ofproto_port->name)) {
+            ofproto_port->ofp_port = OFPP_LOCAL;
+        } else {
+            ofproto_port->ofp_port = alloc_ofp_port(ofproto,
+                                                    ofproto_port->name);
+        }
+    }
     pp->port_no = ofproto_port->ofp_port;
     netdev_get_etheraddr(netdev, pp->hw_addr);
     ovs_strlcpy(pp->name, ofproto_port->name, sizeof pp->name);
@@ -1549,8 +1685,8 @@ ofport_open(const struct ofproto *ofproto,
     pp->state = netdev_get_carrier(netdev) ? 0 : OFPUTIL_PS_LINK_DOWN;
     netdev_get_features(netdev, &pp->curr, &pp->advertised,
                         &pp->supported, &pp->peer);
-    pp->curr_speed = netdev_features_to_bps(pp->curr);
-    pp->max_speed = netdev_features_to_bps(pp->supported);
+    pp->curr_speed = netdev_features_to_bps(pp->curr, 0);
+    pp->max_speed = netdev_features_to_bps(pp->supported, 0);
 
     return netdev;
 }
@@ -1710,6 +1846,7 @@ static void
 ofport_destroy(struct ofport *port)
 {
     if (port) {
+        dealloc_ofp_port(port->ofproto, port->ofp_port);
         port->ofproto->ofproto_class->port_destruct(port);
         ofport_destroy__(port);
      }
@@ -1803,19 +1940,25 @@ init_ports(struct ofproto *p)
 {
     struct ofproto_port_dump dump;
     struct ofproto_port ofproto_port;
+    struct shash_node *node, *next;
 
     OFPROTO_PORT_FOR_EACH (&ofproto_port, &dump, p) {
-        uint16_t ofp_port = ofproto_port.ofp_port;
-        if (ofproto_get_port(p, ofp_port)) {
-            VLOG_WARN_RL(&rl, "%s: ignoring duplicate port %"PRIu16" "
-                         "in datapath", p->name, ofp_port);
-        } else if (shash_find(&p->port_by_name, ofproto_port.name)) {
+        const char *name = ofproto_port.name;
+
+        if (shash_find(&p->port_by_name, name)) {
             VLOG_WARN_RL(&rl, "%s: ignoring duplicate device %s in datapath",
-                         p->name, ofproto_port.name);
+                         p->name, name);
         } else {
             struct ofputil_phy_port pp;
             struct netdev *netdev;
 
+            /* Check if an OpenFlow port number had been requested. */
+            node = shash_find(&init_ofp_ports, name);
+            if (node) {
+                const struct iface_hint *iface_hint = node->data;
+                simap_put(&p->ofp_requests, name, iface_hint->ofp_port);
+            }
+
             netdev = ofport_open(p, &ofproto_port, &pp);
             if (netdev) {
                 ofport_install(p, netdev, &pp);
@@ -1823,6 +1966,17 @@ init_ports(struct ofproto *p)
         }
     }
 
+    SHASH_FOR_EACH_SAFE(node, next, &init_ofp_ports) {
+        struct iface_hint *iface_hint = node->data;
+
+        if (!strcmp(iface_hint->br_name, p->name)) {
+            free(iface_hint->br_name);
+            free(iface_hint->br_type);
+            free(iface_hint);
+            shash_delete(&init_ofp_ports, node);
+        }
+    }
+
     return 0;
 }
 
@@ -1928,7 +2082,7 @@ ofproto_rule_destroy(struct rule *rule)
 bool
 ofproto_rule_has_out_port(const struct rule *rule, uint16_t port)
 {
-    return (port == OFPP_NONE
+    return (port == OFPP_ANY
             || ofpacts_output_to_port(rule->ofpacts, rule->ofpacts_len, port));
 }
 
@@ -1957,7 +2111,7 @@ ofoperation_has_out_port(const struct ofoperation *op, uint16_t out_port)
 
 /* Executes the actions indicated by 'rule' on 'packet' and credits 'rule''s
  * statistics appropriately.  'packet' must have at least sizeof(struct
- * ofp_packet_in) bytes of headroom.
+ * ofp10_packet_in) bytes of headroom.
  *
  * 'packet' doesn't necessarily have to match 'rule'.  'rule' will be credited
  * with statistics for 'packet' either way.
@@ -1968,9 +2122,9 @@ rule_execute(struct rule *rule, uint16_t in_port, struct ofpbuf *packet)
 {
     struct flow flow;
 
-    assert(ofpbuf_headroom(packet) >= sizeof(struct ofp_packet_in));
+    assert(ofpbuf_headroom(packet) >= sizeof(struct ofp10_packet_in));
 
-    flow_extract(packet, 0, NULL, in_port, &flow);
+    flow_extract(packet, 0, 0, NULL, in_port, &flow);
     return rule->ofproto->ofproto_class->rule_execute(rule, &flow, packet);
 }
 
@@ -2012,20 +2166,33 @@ handle_features_request(struct ofconn *ofconn, const struct ofp_header *oh)
     struct ofport *port;
     bool arp_match_ip;
     struct ofpbuf *b;
+    int n_tables;
+    int i;
 
     ofproto->ofproto_class->get_features(ofproto, &arp_match_ip,
                                          &features.actions);
     assert(features.actions & OFPUTIL_A_OUTPUT); /* sanity check */
 
+    /* Count only non-hidden tables in the number of tables.  (Hidden tables,
+     * if present, are always at the end.) */
+    n_tables = ofproto->n_tables;
+    for (i = 0; i < ofproto->n_tables; i++) {
+        if (ofproto->tables[i].flags & OFTABLE_HIDDEN) {
+            n_tables = i;
+            break;
+        }
+    }
+
     features.datapath_id = ofproto->datapath_id;
     features.n_buffers = pktbuf_capacity();
-    features.n_tables = ofproto->n_tables;
+    features.n_tables = n_tables;
     features.capabilities = (OFPUTIL_C_FLOW_STATS | OFPUTIL_C_TABLE_STATS |
                              OFPUTIL_C_PORT_STATS | OFPUTIL_C_QUEUE_STATS);
     if (arp_match_ip) {
         features.capabilities |= OFPUTIL_C_ARP_MATCH_IP;
     }
-
+    /* FIXME: Fill in proper features.auxiliary_id for auxiliary connections */
+    features.auxiliary_id = 0;
     b = ofputil_encode_switch_features(&features, ofconn_get_protocol(ofconn),
                                        oh->xid);
     HMAP_FOR_EACH (port, hmap_node, &ofproto->ports) {
@@ -2048,7 +2215,9 @@ handle_get_config_request(struct ofconn *ofconn, const struct ofp_header *oh)
     buf = ofpraw_alloc_reply(OFPRAW_OFPT_GET_CONFIG_REPLY, oh, 0);
     osc = ofpbuf_put_uninit(buf, sizeof *osc);
     flags = ofproto->frag_handling;
-    if (ofconn_get_invalid_ttl_to_controller(ofconn)) {
+    /* OFPC_INVALID_TTL_TO_CONTROLLER is deprecated in OF 1.3 */
+    if (oh->version < OFP13_VERSION
+        && ofconn_get_invalid_ttl_to_controller(ofconn)) {
         flags |= OFPC_INVALID_TTL_TO_CONTROLLER;
     }
     osc->flags = htons(flags);
@@ -2081,8 +2250,10 @@ handle_set_config(struct ofconn *ofconn, const struct ofp_header *oh)
             }
         }
     }
+    /* OFPC_INVALID_TTL_TO_CONTROLLER is deprecated in OF 1.3 */
     ofconn_set_invalid_ttl_to_controller(ofconn,
-             (flags & OFPC_INVALID_TTL_TO_CONTROLLER));
+             (oh->version < OFP13_VERSION
+              && flags & OFPC_INVALID_TTL_TO_CONTROLLER));
 
     ofconn_set_miss_send_len(ofconn, ntohs(osc->miss_send_len));
 
@@ -2147,7 +2318,7 @@ handle_packet_out(struct ofconn *ofconn, const struct ofp_header *oh)
     }
 
     /* Verify actions against packet, then send packet if successful. */
-    flow_extract(payload, 0, NULL, po.in_port, &flow);
+    flow_extract(payload, 0, 0, NULL, po.in_port, &flow);
     error = ofpacts_check(po.ofpacts, po.ofpacts_len, &flow, p->max_ports);
     if (!error) {
         error = p->ofproto_class->packet_out(p, payload, &flow,
@@ -2221,17 +2392,29 @@ static enum ofperr
 handle_desc_stats_request(struct ofconn *ofconn,
                           const struct ofp_header *request)
 {
+    static const char *default_mfr_desc = "Nicira, Inc.";
+    static const char *default_hw_desc = "Open vSwitch";
+    static const char *default_sw_desc = VERSION;
+    static const char *default_serial_desc = "None";
+    static const char *default_dp_desc = "None";
+
     struct ofproto *p = ofconn_get_ofproto(ofconn);
     struct ofp_desc_stats *ods;
     struct ofpbuf *msg;
 
     msg = ofpraw_alloc_stats_reply(request, 0);
     ods = ofpbuf_put_zeros(msg, sizeof *ods);
-    ovs_strlcpy(ods->mfr_desc, p->mfr_desc, sizeof ods->mfr_desc);
-    ovs_strlcpy(ods->hw_desc, p->hw_desc, sizeof ods->hw_desc);
-    ovs_strlcpy(ods->sw_desc, p->sw_desc, sizeof ods->sw_desc);
-    ovs_strlcpy(ods->serial_num, p->serial_desc, sizeof ods->serial_num);
-    ovs_strlcpy(ods->dp_desc, p->dp_desc, sizeof ods->dp_desc);
+    ovs_strlcpy(ods->mfr_desc, p->mfr_desc ? p->mfr_desc : default_mfr_desc,
+                sizeof ods->mfr_desc);
+    ovs_strlcpy(ods->hw_desc, p->hw_desc ? p->hw_desc : default_hw_desc,
+                sizeof ods->hw_desc);
+    ovs_strlcpy(ods->sw_desc, p->sw_desc ? p->sw_desc : default_sw_desc,
+                sizeof ods->sw_desc);
+    ovs_strlcpy(ods->serial_num,
+                p->serial_desc ? p->serial_desc : default_serial_desc,
+                sizeof ods->serial_num);
+    ovs_strlcpy(ods->dp_desc, p->dp_desc ? p->dp_desc : default_dp_desc,
+                sizeof ods->dp_desc);
     ofconn_send_reply(ofconn, msg);
 
     return 0;
@@ -2244,6 +2427,7 @@ handle_table_stats_request(struct ofconn *ofconn,
     struct ofproto *p = ofconn_get_ofproto(ofconn);
     struct ofp12_table_stats *ots;
     struct ofpbuf *msg;
+    int n_tables;
     size_t i;
 
     /* Set up default values.
@@ -2272,9 +2456,16 @@ handle_table_stats_request(struct ofconn *ofconn,
 
     p->ofproto_class->get_tables(p, ots);
 
+    /* Post-process the tables, dropping hidden tables. */
+    n_tables = p->n_tables;
     for (i = 0; i < p->n_tables; i++) {
         const struct oftable *table = &p->tables[i];
 
+        if (table->flags & OFTABLE_HIDDEN) {
+            n_tables = i;
+            break;
+        }
+
         if (table->name) {
             ovs_strzcpy(ots[i].name, table->name, sizeof ots[i].name);
         }
@@ -2284,7 +2475,7 @@ handle_table_stats_request(struct ofconn *ofconn,
         }
     }
 
-    msg = ofputil_encode_table_stats_reply(ots, p->n_tables, request);
+    msg = ofputil_encode_table_stats_reply(ots, n_tables, request);
     ofconn_send_reply(ofconn, msg);
 
     free(ots);
@@ -2321,7 +2512,7 @@ handle_port_stats_request(struct ofconn *ofconn,
     }
 
     ofpmp_init(&replies, request);
-    if (port_no != OFPP_NONE) {
+    if (port_no != OFPP_ANY) {
         port = ofproto_get_port(p, port_no);
         if (port) {
             append_port_stat(port, &replies);
@@ -2438,7 +2629,7 @@ next_matching_table(const struct ofproto *ofproto,
  * OpenFlow OFPFC_MODIFY and OFPFC_DELETE requests and puts them on list
  * 'rules'.
  *
- * If 'out_port' is anything other than OFPP_NONE, then only rules that output
+ * If 'out_port' is anything other than OFPP_ANY, then only rules that output
  * to 'out_port' are included.
  *
  * Hidden rules are always omitted.
@@ -2489,7 +2680,7 @@ exit:
  * OpenFlow OFPFC_MODIFY_STRICT and OFPFC_DELETE_STRICT requests and puts them
  * on list 'rules'.
  *
- * If 'out_port' is anything other than OFPP_NONE, then only rules that output
+ * If 'out_port' is anything other than OFPP_ANY, then only rules that output
  * to 'out_port' are included.
  *
  * Hidden rules are always omitted.
@@ -2587,6 +2778,12 @@ handle_flow_stats_request(struct ofconn *ofconn,
                                                &fs.byte_count);
         fs.ofpacts = rule->ofpacts;
         fs.ofpacts_len = rule->ofpacts_len;
+        fs.flags = 0;
+        if (rule->send_flow_removed) {
+            fs.flags |= OFPFF_SEND_FLOW_REM;
+            /* FIXME: Implement OF 1.3 flags OFPFF13_NO_PKT_COUNTS
+               and OFPFF13_NO_BYT_COUNTS */
+        }
         ofputil_append_flow_stats_reply(&fs, &replies);
     }
     ofconn_send_replies(ofconn, &replies);
@@ -2833,7 +3030,7 @@ handle_queue_stats_request(struct ofconn *ofconn,
         return error;
     }
 
-    if (oqsr.port_no == OFPP_ALL) {
+    if (oqsr.port_no == OFPP_ANY) {
         error = OFPERR_OFPQOFC_BAD_QUEUE;
         HMAP_FOR_EACH (port, hmap_node, &ofproto->ports) {
             if (!handle_queue_stats_for_port(port, oqsr.queue_id, &cbdata)) {
@@ -2953,6 +3150,8 @@ add_flow(struct ofproto *ofproto, struct ofconn *ofconn,
         return OFPERR_OFPFMFC_OVERLAP;
     }
 
+    /* FIXME: Implement OFPFF12_RESET_COUNTS */
+
     rule->ofproto = ofproto;
     rule->pending = NULL;
     rule->flow_cookie = fm->new_cookie;
@@ -2961,6 +3160,8 @@ add_flow(struct ofproto *ofproto, struct ofconn *ofconn,
     rule->hard_timeout = fm->hard_timeout;
     rule->table_id = table - ofproto->tables;
     rule->send_flow_removed = (fm->flags & OFPFF_SEND_FLOW_REM) != 0;
+    /* FIXME: Implement OF 1.3 flags OFPFF13_NO_PKT_COUNTS
+       and OFPFF13_NO_BYT_COUNTS */
     rule->ofpacts = xmemdup(fm->ofpacts, fm->ofpacts_len);
     rule->ofpacts_len = fm->ofpacts_len;
     rule->evictable = true;
@@ -3046,6 +3247,8 @@ modify_flows__(struct ofproto *ofproto, struct ofconn *ofconn,
         bool actions_changed;
         ovs_be64 new_cookie;
 
+        /* FIXME: Implement OFPFF12_RESET_COUNTS */
+
         if (rule_is_modifiable(rule)) {
             /* At least one rule is modifiable, don't report EPERM error. */
             error = 0;
@@ -3106,7 +3309,7 @@ modify_flows_loose(struct ofproto *ofproto, struct ofconn *ofconn,
 
     error = collect_rules_loose(ofproto, fm->table_id, &fm->match,
                                 fm->cookie, fm->cookie_mask,
-                                OFPP_NONE, &rules);
+                                OFPP_ANY, &rules);
     if (error) {
         return error;
     } else if (list_is_empty(&rules)) {
@@ -3131,7 +3334,7 @@ modify_flow_strict(struct ofproto *ofproto, struct ofconn *ofconn,
 
     error = collect_rules_strict(ofproto, fm->table_id, &fm->match,
                                  fm->priority, fm->cookie, fm->cookie_mask,
-                                 OFPP_NONE, &rules);
+                                 OFPP_ANY, &rules);
 
     if (error) {
         return error;
@@ -3295,17 +3498,6 @@ handle_flow_mod(struct ofconn *ofconn, const struct ofp_header *oh)
     ofpbuf_use_stub(&ofpacts, ofpacts_stub, sizeof ofpacts_stub);
     error = ofputil_decode_flow_mod(&fm, oh, ofconn_get_protocol(ofconn),
                                     &ofpacts);
-    if (error) {
-        goto exit_free_ofpacts;
-    }
-
-    if (fm.flags & OFPFF10_EMERG) {
-        /* We do not support the OpenFlow 1.0 emergency flow cache, which
-         * is not required in OpenFlow 1.0.1 and removed from OpenFlow 1.1.
-         * There is no good error code, so just state that the flow table
-         * is full. */
-        error = OFPERR_OFPFMFC_TABLE_FULL;
-    }
     if (!error) {
         error = ofpacts_check(fm.ofpacts, fm.ofpacts_len,
                               &fm.match.flow, ofproto->max_ports);
@@ -3388,27 +3580,38 @@ handle_flow_mod__(struct ofproto *ofproto, struct ofconn *ofconn,
 static enum ofperr
 handle_role_request(struct ofconn *ofconn, const struct ofp_header *oh)
 {
-    const struct nx_role_request *nrr = ofpmsg_body(oh);
-    struct nx_role_request *reply;
+    struct ofputil_role_request rr;
     struct ofpbuf *buf;
     uint32_t role;
+    enum ofperr error;
+
+    error = ofputil_decode_role_message(oh, &rr);
+    if (error) {
+        return error;
+    }
 
-    role = ntohl(nrr->role);
-    if (role != NX_ROLE_OTHER && role != NX_ROLE_MASTER
-        && role != NX_ROLE_SLAVE) {
-        return OFPERR_OFPRRFC_BAD_ROLE;
+    if (rr.request_current_role_only) {
+        role = ofconn_get_role(ofconn); /* NX_ROLE_* */
+        goto reply;
     }
 
+    role = rr.role;
+
     if (ofconn_get_role(ofconn) != role
         && ofconn_has_pending_opgroups(ofconn)) {
         return OFPROTO_POSTPONE;
     }
 
+    if (rr.have_generation_id) {
+        if (!ofconn_set_master_election_id(ofconn, rr.generation_id)) {
+            return OFPERR_OFPRRFC_STALE;
+        }
+    }
+
     ofconn_set_role(ofconn, role);
 
-    buf = ofpraw_alloc_reply(OFPRAW_NXT_ROLE_REPLY, oh, 0);
-    reply = ofpbuf_put_zeros(buf, sizeof *reply);
-    reply->role = htonl(role);
+reply:
+    buf = ofputil_encode_role_reply(oh, role);
     ofconn_send_reply(ofconn, buf);
 
     return 0;
@@ -3819,14 +4022,14 @@ handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg)
     case OFPTYPE_BARRIER_REQUEST:
         return handle_barrier_request(ofconn, oh);
 
+    case OFPTYPE_ROLE_REQUEST:
+        return handle_role_request(ofconn, oh);
+
         /* OpenFlow replies. */
     case OFPTYPE_ECHO_REPLY:
         return 0;
 
         /* Nicira extension requests. */
-    case OFPTYPE_ROLE_REQUEST:
-        return handle_role_request(ofconn, oh);
-
     case OFPTYPE_FLOW_MOD_TABLE_ID:
         return handle_nxt_flow_mod_table_id(ofconn, oh);
 
@@ -3874,6 +4077,19 @@ handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg)
     case OFPTYPE_FLOW_MONITOR_STATS_REQUEST:
         return handle_flow_monitor_request(ofconn, oh);
 
+        /* FIXME: Change the following once they are implemented: */
+    case OFPTYPE_QUEUE_GET_CONFIG_REQUEST:
+    case OFPTYPE_GET_ASYNC_REQUEST:
+    case OFPTYPE_METER_MOD:
+    case OFPTYPE_GROUP_REQUEST:
+    case OFPTYPE_GROUP_DESC_REQUEST:
+    case OFPTYPE_GROUP_FEATURES_REQUEST:
+    case OFPTYPE_METER_REQUEST:
+    case OFPTYPE_METER_CONFIG_REQUEST:
+    case OFPTYPE_METER_FEATURES_REQUEST:
+    case OFPTYPE_TABLE_FEATURES_REQUEST:
+        return OFPERR_OFPBRC_BAD_TYPE;
+
     case OFPTYPE_HELLO:
     case OFPTYPE_ERROR:
     case OFPTYPE_FEATURES_REPLY:
@@ -3882,6 +4098,7 @@ handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg)
     case OFPTYPE_FLOW_REMOVED:
     case OFPTYPE_PORT_STATUS:
     case OFPTYPE_BARRIER_REPLY:
+    case OFPTYPE_QUEUE_GET_CONFIG_REPLY:
     case OFPTYPE_DESC_STATS_REPLY:
     case OFPTYPE_FLOW_STATS_REPLY:
     case OFPTYPE_QUEUE_STATS_REPLY:
@@ -3893,6 +4110,14 @@ handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg)
     case OFPTYPE_FLOW_MONITOR_PAUSED:
     case OFPTYPE_FLOW_MONITOR_RESUMED:
     case OFPTYPE_FLOW_MONITOR_STATS_REPLY:
+    case OFPTYPE_GET_ASYNC_REPLY:
+    case OFPTYPE_GROUP_REPLY:
+    case OFPTYPE_GROUP_DESC_REPLY:
+    case OFPTYPE_GROUP_FEATURES_REPLY:
+    case OFPTYPE_METER_REPLY:
+    case OFPTYPE_METER_CONFIG_REPLY:
+    case OFPTYPE_METER_FEATURES_REPLY:
+    case OFPTYPE_TABLE_FEATURES_REPLY:
     default:
         return OFPERR_OFPBRC_BAD_TYPE;
     }
index 5599cd6..413472a 100644 (file)
@@ -132,18 +132,26 @@ struct ofproto_controller {
     uint8_t dscp;               /* DSCP value for controller connection. */
 };
 
-#define DEFAULT_MFR_DESC "Nicira, Inc."
-#define DEFAULT_HW_DESC "Open vSwitch"
-#define DEFAULT_SW_DESC VERSION
-#define DEFAULT_SERIAL_DESC "None"
-#define DEFAULT_DP_DESC "None"
-
 void ofproto_enumerate_types(struct sset *types);
 const char *ofproto_normalize_type(const char *);
 
 int ofproto_enumerate_names(const char *type, struct sset *names);
 void ofproto_parse_name(const char *name, char **dp_name, char **dp_type);
 
+/* An interface hint element, which is used by ofproto_init() to
+ * describe the caller's understanding of the startup state. */
+struct iface_hint {
+    char *br_name;              /* Name of owning bridge. */
+    char *br_type;              /* Type of owning bridge. */
+    uint16_t ofp_port;          /* OpenFlow port number. */
+};
+
+void ofproto_init(const struct shash *iface_hints);
+
+int ofproto_type_run(const char *datapath_type);
+int ofproto_type_run_fast(const char *datapath_type);
+void ofproto_type_wait(const char *datapath_type);
+
 int ofproto_create(const char *datapath, const char *datapath_type,
                    struct ofproto **ofprotop);
 void ofproto_destroy(struct ofproto *);
@@ -193,6 +201,8 @@ int ofproto_port_dump_done(struct ofproto_port_dump *);
 #define OFPROTO_FLOW_EVICTION_THRESHOLD_DEFAULT  1000
 #define OFPROTO_FLOW_EVICTION_THRESHOLD_MIN 100
 
+const char *ofproto_port_open_type(const char *datapath_type,
+                                   const char *port_type);
 int ofproto_port_add(struct ofproto *, struct netdev *, uint16_t *ofp_portp);
 int ofproto_port_del(struct ofproto *, uint16_t ofp_port);
 int ofproto_port_get_stats(const struct ofport *, struct netdev_stats *stats);
@@ -204,7 +214,8 @@ int ofproto_port_query_by_name(const struct ofproto *, const char *devname,
 uint64_t ofproto_get_datapath_id(const struct ofproto *);
 void ofproto_set_datapath_id(struct ofproto *, uint64_t datapath_id);
 void ofproto_set_controllers(struct ofproto *,
-                             const struct ofproto_controller *, size_t n);
+                             const struct ofproto_controller *, size_t n,
+                             uint32_t allowed_versions);
 void ofproto_set_fail_mode(struct ofproto *, enum ofproto_fail_mode fail_mode);
 void ofproto_reconnect_controllers(struct ofproto *);
 void ofproto_set_extra_in_band_remotes(struct ofproto *,
@@ -212,11 +223,9 @@ void ofproto_set_extra_in_band_remotes(struct ofproto *,
 void ofproto_set_in_band_queue(struct ofproto *, int queue_id);
 void ofproto_set_flow_eviction_threshold(struct ofproto *, unsigned threshold);
 void ofproto_set_forward_bpdu(struct ofproto *, bool forward_bpdu);
-void ofproto_set_mac_idle_time(struct ofproto *, unsigned idle_time);
-void ofproto_set_desc(struct ofproto *,
-                      const char *mfr_desc, const char *hw_desc,
-                      const char *sw_desc, const char *serial_desc,
-                      const char *dp_desc);
+void ofproto_set_mac_table_config(struct ofproto *, unsigned idle_time,
+                                  size_t max_entries);
+void ofproto_set_dp_desc(struct ofproto *, const char *dp_desc);
 int ofproto_set_snoops(struct ofproto *, const struct sset *snoops);
 int ofproto_set_netflow(struct ofproto *,
                         const struct netflow_options *nf_options);
index 71be34a..902b19d 100644 (file)
@@ -120,7 +120,7 @@ pktbuf_save(struct pktbuf *pb, const void *buffer, size_t buffer_size,
         p->cookie = 0;
     }
     p->buffer = ofpbuf_clone_data_with_headroom(buffer, buffer_size,
-                                                sizeof(struct ofp_packet_in));
+                                                sizeof(struct ofp10_packet_in));
 
 
     p->timeout = time_msec() + OVERWRITE_MSECS;
@@ -165,7 +165,7 @@ pktbuf_get_null(void)
  *
  * 'in_port' may be NULL if the input port is not of interest.
  *
- * A returned packet will have at least sizeof(struct ofp_packet_in) bytes of
+ * A returned packet will have at least sizeof(struct ofp10_packet_in) bytes of
  * headroom.
  *
  * On failure, stores NULL in in '*bufferp' and UINT16_MAX in '*in_port'. */
index 1149efd..8e1c030 100644 (file)
@@ -532,7 +532,7 @@ ovsdb_jsonrpc_session_set_all_options(
             remote->dscp = options->dscp;
         }
         /*
-         * TODO:XXX race window between setting dscp to listening socket
+         * XXX race window between setting dscp to listening socket
          * and accepting socket. Accepted socket may have old dscp value.
          * Ignore this race window for now.
          */
index a813478..53f3579 100644 (file)
@@ -5,7 +5,7 @@
 .  IP "\\$1"
 ..
 .\" -*- nroff -*-
-.TH ovsdb\-client 1 "November 2009" "Open vSwitch" "Open vSwitch Manual"
+.TH ovsdb\-client 1 "@VERSION@" "Open vSwitch" "Open vSwitch Manual"
 .\" This program's name:
 .ds PN ovsdb\-client
 .\" SSL peer program's name:
index 7b783e1..aa4fae2 100755 (executable)
@@ -265,7 +265,7 @@ def docsToNroff(schemaFile, xmlFile, erFile, title=None):
     # Putting '\" p as the first line tells "man" that the manpage
     # needs to be preprocessed by "pic".
     s = r''''\" p
-.TH %s 5 "%s" "Open vSwitch" "Open vSwitch Manual"
+.TH @VERSION@ 5 "%s" "Open vSwitch" "Open vSwitch Manual"
 .\" -*- nroff -*-
 .de TQ
 .  br
@@ -280,7 +280,8 @@ def docsToNroff(schemaFile, xmlFile, erFile, title=None):
 ..
 .SH NAME
 %s \- %s database schema
-''' % (title, d.strftime("%B %Y"), textToNroff(title), schema.name)
+.PP
+''' % (title, textToNroff(title), schema.name)
 
     tables = ""
     introNodes = []
index 242c98b..9d42471 100644 (file)
@@ -1,5 +1,5 @@
 .\" -*- nroff -*-
-.TH ovsdb\-server 1 "November 2009" "Open vSwitch" "Open vSwitch Manual"
+.TH ovsdb\-server 1 "@VERSION@" "Open vSwitch" "Open vSwitch Manual"
 .\" This program's name:
 .ds PN ovsdb\-server
 .\" SSL peer program's name:
index 69548c2..6032d73 100644 (file)
@@ -523,6 +523,10 @@ write_string_string_column(struct ovsdb_row *row, const char *column_name,
     datum = get_datum(row, column_name, OVSDB_TYPE_STRING, OVSDB_TYPE_STRING,
                       UINT_MAX);
     if (!datum) {
+        for (i = 0; i < n; i++) {
+            free(keys[i]);
+            free(values[i]);
+        }
         return;
     }
 
index f9a3661..a2f1f22 100644 (file)
@@ -5,7 +5,7 @@
 .  IP "\\$1"
 ..
 .\" -*- nroff -*-
-.TH ovsdb\-tool 1 "November 2009" "Open vSwitch" "Open vSwitch Manual"
+.TH ovsdb\-tool 1 "@VERSION@" "Open vSwitch" "Open vSwitch Manual"
 .ds PN ovsdb\-tool
 .
 .SH NAME
index 650d250..f74d7f0 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2010, 2011 Nicira, Inc.
+# Copyright (c) 2010, 2011, 2012 Nicira, Inc.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -362,7 +362,9 @@ def daemonize_start():
         if _fork_and_wait_for_startup() > 0:
             # Running in parent process.
             sys.exit(0)
+
         # Running in daemon or monitor process.
+        os.setsid()
 
     if _monitor:
         saved_daemonize_fd = _daemonize_fd
@@ -384,7 +386,6 @@ def daemonize_complete():
     _fork_notify_startup(_daemonize_fd)
 
     if _detach:
-        os.setsid()
         if _chdir:
             os.chdir("/")
         _close_standard_fds()
index c04c9b3..7d15f3e 100644 (file)
@@ -20,6 +20,11 @@ import socket
 
 vlog = ovs.vlog.Vlog("poller")
 
+POLLIN = 0x001
+POLLOUT = 0x004
+POLLERR = 0x008
+POLLHUP = 0x010
+POLLNVAL = 0x020
 
 # eventlet/gevent doesn't support select.poll. If select.poll is used,
 # python interpreter is blocked as a whole instead of switching from the
@@ -39,12 +44,12 @@ class _SelectSelect(object):
         if isinstance(fd, socket.socket):
             fd = fd.fileno()
         assert isinstance(fd, int)
-        if events & select.POLLIN:
+        if events & POLLIN:
             self.rlist.append(fd)
-            events &= ~select.POLLIN
-        if events & select.POLLOUT:
+            events &= ~POLLIN
+        if events & POLLOUT:
             self.wlist.append(fd)
-            events &= ~select.POLLOUT
+            events &= ~POLLOUT
         if events:
             self.xlist.append(fd)
 
@@ -63,13 +68,13 @@ class _SelectSelect(object):
         # events_dict[fd] |= event
         events_dict = {}
         for fd in rlist:
-            events_dict[fd] = events_dict.get(fd, 0) | select.POLLIN
+            events_dict[fd] = events_dict.get(fd, 0) | POLLIN
         for fd in wlist:
-            events_dict[fd] = events_dict.get(fd, 0) | select.POLLOUT
+            events_dict[fd] = events_dict.get(fd, 0) | POLLOUT
         for fd in xlist:
-            events_dict[fd] = events_dict.get(fd, 0) | (select.POLLERR |
-                                                        select.POLLHUP |
-                                                        select.POLLNVAL)
+            events_dict[fd] = events_dict.get(fd, 0) | (POLLERR |
+                                                        POLLHUP |
+                                                        POLLNVAL)
         return events_dict.items()
 
 
@@ -168,15 +173,15 @@ class Poller(object):
             for fd, revents in events:
                 if revents != 0:
                     s = ""
-                    if revents & select.POLLIN:
+                    if revents & POLLIN:
                         s += "[POLLIN]"
-                    if revents & select.POLLOUT:
+                    if revents & POLLOUT:
                         s += "[POLLOUT]"
-                    if revents & select.POLLERR:
+                    if revents & POLLERR:
                         s += "[POLLERR]"
-                    if revents & select.POLLHUP:
+                    if revents & POLLHUP:
                         s += "[POLLHUP]"
-                    if revents & select.POLLNVAL:
+                    if revents & POLLNVAL:
                         s += "[POLLNVAL]"
                     vlog.dbg("%s on fd %d" % (s, fd))
 
index dd45fe4..8fecbc7 100644 (file)
@@ -77,9 +77,23 @@ def make_unix_socket(style, nonblock, bind_path, connect_path):
 
 def check_connection_completion(sock):
     p = ovs.poller.SelectPoll()
-    p.register(sock, select.POLLOUT)
-    if len(p.poll(0)) == 1:
-        return get_socket_error(sock)
+    p.register(sock, ovs.poller.POLLOUT)
+    pfds = p.poll(0)
+    if len(pfds) == 1:
+        revents = pfds[0][1]
+        if revents & ovs.poller.POLLERR:
+            try:
+                # The following should raise an exception.
+                socket.send("\0", socket.MSG_DONTWAIT)
+
+                # (Here's where we end up if it didn't.)
+                # XXX rate-limit
+                vlog.err("poll return POLLERR but send succeeded")
+                return errno.EPROTO
+            except socket.error, e:
+                return get_exception_errno(e)
+        else:
+            return 0
     else:
         return errno.EAGAIN
 
@@ -119,12 +133,6 @@ def inet_open_active(style, target, default_port, dscp):
         return get_exception_errno(e), None
 
 
-def get_socket_error(sock):
-    """Returns the errno value associated with 'socket' (0 if no error) and
-    resets the socket's error status."""
-    return sock.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR)
-
-
 def get_exception_errno(e):
     """A lot of methods on Python socket objects raise socket.error, but that
     exception is documented as having two completely different forms of
@@ -182,7 +190,7 @@ def set_nonblocking(sock):
         sock.setblocking(0)
     except socket.error, e:
         vlog.err("could not set nonblocking mode on socket: %s"
-                 % os.strerror(get_socket_error(e)))
+                 % os.strerror(get_exception_errno(e)))
 
 
 def set_dscp(sock, dscp):
index dad6848..c640ebf 100644 (file)
@@ -14,7 +14,6 @@
 
 import errno
 import os
-import select
 import socket
 
 import ovs.poller
@@ -162,15 +161,17 @@ class Stream(object):
         is complete, returns 0 if the connection was successful or a positive
         errno value if it failed.  If the connection is still in progress,
         returns errno.EAGAIN."""
-        last_state = -1         # Always differs from initial self.state
-        while self.state != last_state:
-            last_state = self.state
-            if self.state == Stream.__S_CONNECTING:
-                self.__scs_connecting()
-            elif self.state == Stream.__S_CONNECTED:
-                return 0
-            elif self.state == Stream.__S_DISCONNECTED:
-                return self.error
+
+        if self.state == Stream.__S_CONNECTING:
+            self.__scs_connecting()
+
+        if self.state == Stream.__S_CONNECTING:
+            return errno.EAGAIN
+        elif self.state == Stream.__S_CONNECTED:
+            return 0
+        else:
+            assert self.state == Stream.__S_DISCONNECTED
+            return self.error
 
     def recv(self, n):
         """Tries to receive up to 'n' bytes from this stream.  Returns a
@@ -236,9 +237,9 @@ class Stream(object):
         if self.state == Stream.__S_CONNECTING:
             wait = Stream.W_CONNECT
         if wait == Stream.W_RECV:
-            poller.fd_wait(self.socket, select.POLLIN)
+            poller.fd_wait(self.socket, ovs.poller.POLLIN)
         else:
-            poller.fd_wait(self.socket, select.POLLOUT)
+            poller.fd_wait(self.socket, ovs.poller.POLLOUT)
 
     def connect_wait(self, poller):
         self.wait(poller, Stream.W_CONNECT)
@@ -324,7 +325,7 @@ class PassiveStream(object):
                 return error, None
 
     def wait(self, poller):
-        poller.fd_wait(self.socket, select.POLLIN)
+        poller.fd_wait(self.socket, ovs.poller.POLLIN)
 
     def __del__(self):
         # Don't delete the file: we might have forked.
index af332c0..a25e624 100755 (executable)
@@ -45,9 +45,6 @@ start () {
     if test X"$VSWITCHD_MLOCKALL" != X; then
        set "$@" --mlockall="$VSWITCHD_MLOCKALL"
     fi
-    if test X"$BRCOMPAT" = Xyes; then
-       set "$@" --brcompat
-    fi
     set "$@" $OVS_CTL_OPTS
     "$@"
 
index c427879..b6ccf56 100755 (executable)
@@ -47,6 +47,7 @@ case "$TYPE" in
                if [ "${OVSBOOTPROTO}" != "dhcp" ] && [ -z "${OVSINTF}" ]; then
                        ${OTHERSCRIPT} ${CONFIG}
                fi
+               [ -n "${STP}" ] && ovs-vsctl --no-wait set bridge "${DEVICE}" stp_enable="${STP}"
                ;;
        OVSPort)
                /sbin/ifup "$OVS_BRIDGE"
index 7918fd5..6c225f7 100644 (file)
@@ -145,7 +145,6 @@ systemctl start openvswitch.service
 /usr/share/openvswitch/scripts/ovs-check-dead-ifs
 /usr/share/openvswitch/scripts/ovs-lib
 %config /usr/share/openvswitch/vswitch.ovsschema
-/usr/sbin/ovs-brcompatd
 /usr/sbin/ovs-bugtool
 /usr/sbin/ovs-vswitchd
 /usr/sbin/ovsdb-server
@@ -167,7 +166,6 @@ systemctl start openvswitch.service
 %doc /usr/share/man/man5/ovs-vswitchd.conf.db.5.gz
 %doc /usr/share/man/man8/ovs-appctl.8.gz
 %doc /usr/share/man/man8/ovs-bugtool.8.gz
-%doc /usr/share/man/man8/ovs-brcompatd.8.gz
 %doc /usr/share/man/man8/ovs-dpctl.8.gz
 %doc /usr/share/man/man8/ovs-ofctl.8.gz
 %doc /usr/share/man/man8/ovs-parse-backtrace.8.gz
index af75ddd..16a8c73 100644 (file)
@@ -56,7 +56,6 @@ depmod %{kernel}
 %files
 %defattr(-,root,root)
 /lib/modules/%{kernel}/kernel/extra/openvswitch/openvswitch.ko
-/lib/modules/%{kernel}/kernel/extra/openvswitch/brcompat.ko
 
 %changelog
 * Wed Sep 21 2011 Kyle Mestery <kmestery@cisco.com>
index ff598b9..9f40881 100644 (file)
@@ -124,7 +124,6 @@ exit 0
 /usr/bin/ovs-vsctl
 /usr/bin/ovsdb-client
 /usr/bin/ovsdb-tool
-/usr/sbin/ovs-brcompatd
 /usr/sbin/ovs-bugtool
 /usr/sbin/ovs-vswitchd
 /usr/sbin/ovsdb-server
@@ -136,7 +135,6 @@ exit 0
 /usr/share/man/man1/ovsdb-tool.1.gz
 /usr/share/man/man5/ovs-vswitchd.conf.db.5.gz
 /usr/share/man/man8/ovs-appctl.8.gz
-/usr/share/man/man8/ovs-brcompatd.8.gz
 /usr/share/man/man8/ovs-bugtool.8.gz
 /usr/share/man/man8/ovs-ctl.8.gz
 /usr/share/man/man8/ovs-dpctl.8.gz
index cad1f53..2c08452 100644 (file)
@@ -19,9 +19,6 @@
 #     concurrent VM import operations.
 # VSWITCHD_MLOCKALL=yes
 
-# BRCOMPAT: If 'yes' compatibility mode will be enabled.
-# BRCOMPAT=yes
-
 # OVS_CTL_OPTS: Extra options to pass to ovs-ctl.  This is, for example,
 # a suitable place to specify --ovs-vswitchd-wrapper=valgrind.
 # OVS_CTL_OPTS=
index 2977f76..732839c 100644 (file)
@@ -16,6 +16,7 @@ TESTSUITE_AT = \
        tests/daemon-py.at \
        tests/ofp-actions.at \
        tests/ofp-print.at \
+       tests/ofp-util.at \
        tests/ofp-errors.at \
        tests/ovs-ofctl.at \
        tests/odp.at \
index da82f51..47c1d32 100644 (file)
@@ -24,7 +24,7 @@ table=0 actions=learn(table=1,hard_timeout=10, NXM_OF_VLAN_TCI[0..11],output:NXM
 table=1 priority=0 actions=flood
 ]])
 AT_CHECK([ovs-ofctl parse-flows flows.txt], [0],
-[[usable protocols: OpenFlow10+table_id,NXM+table_id
+[[usable protocols: OXM,OpenFlow10+table_id,NXM+table_id
 chosen protocol: OpenFlow10+table_id
 OFPT_FLOW_MOD (xid=0x1): ADD table:255 actions=learn(table=1,in_port=99,NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],load:NXM_OF_IN_PORT[]->NXM_NX_REG1[16..31])
 OFPT_FLOW_MOD (xid=0x2): ADD table:255 actions=learn(table=1,NXM_OF_VLAN_TCI[0..11],NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],output:NXM_OF_IN_PORT[])
@@ -61,9 +61,9 @@ AT_CLEANUP
 
 AT_SETUP([learning action - standard VLAN+MAC learning])
 OVS_VSWITCHD_START(
-  [add-port br0 eth0 -- set Interface eth0 type=dummy -- \
-   add-port br0 eth1 -- set Interface eth1 type=dummy -- \
-   add-port br0 eth2 -- set Interface eth2 type=dummy])
+  [add-port br0 p1 -- set Interface p1 type=dummy ofport_request=1 -- \
+   add-port br0 p2 -- set Interface p2 type=dummy ofport_request=2 -- \
+   add-port br0 p3 -- set Interface p3 type=dummy ofport_request=3])
 # Set up flow table for VLAN+MAC learning.
 AT_DATA([flows.txt], [[
 table=0 actions=learn(table=1, hard_timeout=60, NXM_OF_VLAN_TCI[0..11], NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[], output:NXM_OF_IN_PORT[]), resubmit(,1)
@@ -72,9 +72,14 @@ table=1 priority=0 actions=flood
 AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
 
 # Trace an ARP packet arriving on port 3, to create a MAC learning entry.
-AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(3),eth(src=50:54:00:00:00:05,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=192.168.0.1,tip=192.168.0.2,op=1,sha=50:54:00:00:00:05,tha=00:00:00:00:00:00)' -generate], [0], [stdout])
-AT_CHECK([tail -1 stdout], [0], [Datapath actions: 2,0,1
-])
+flow="in_port(3),eth(src=50:54:00:00:00:05,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=192.168.0.1,tip=192.168.0.2,op=1,sha=50:54:00:00:00:05,tha=00:00:00:00:00:00)"
+AT_CHECK([ovs-appctl ofproto/trace br0 "$flow" -generate], [0], [stdout])
+actual=`tail -1 stdout | sed 's/Datapath actions: //'`
+
+expected="1,2,100"
+AT_CHECK([ovs-dpctl normalize-actions "$flow" "$expected"], [0], [stdout])
+mv stdout expout
+AT_CHECK([ovs-dpctl normalize-actions "$flow" "$actual"], [0], [expout])
 
 # Check for the MAC learning entry.
 AT_CHECK([ovs-ofctl dump-flows br0 table=1 | ofctl_strip | sort], [0], [dnl
@@ -98,9 +103,14 @@ NXST_FLOW reply:
 ])
 
 # Trace a packet arrival that updates the first learned MAC entry.
-AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(2),eth(src=50:54:00:00:00:05,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=192.168.0.1,tip=192.168.0.2,op=1,sha=50:54:00:00:00:05,tha=00:00:00:00:00:00)' -generate], [0], [stdout])
-AT_CHECK([tail -1 stdout], [0], [Datapath actions: 3,0,1
-])
+flow="in_port(2),eth(src=50:54:00:00:00:05,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=192.168.0.1,tip=192.168.0.2,op=1,sha=50:54:00:00:00:05,tha=00:00:00:00:00:00)"
+AT_CHECK([ovs-appctl ofproto/trace br0 "$flow" -generate], [0], [stdout])
+actual=`tail -1 stdout | sed 's/Datapath actions: //'`
+
+expected="1,3,100"
+AT_CHECK([ovs-dpctl normalize-actions "$flow" "$expected"], [0], [stdout])
+mv stdout expout
+AT_CHECK([ovs-dpctl normalize-actions "$flow" "$actual"], [0], [expout])
 
 # Check that the MAC learning entry was updated.
 AT_CHECK([ovs-ofctl dump-flows br0 table=1 | ofctl_strip | sort], [0], [dnl
@@ -114,16 +124,21 @@ AT_CLEANUP
 
 AT_SETUP([learning action - TCPv4 port learning])
 OVS_VSWITCHD_START(
-  [add-port br0 eth0 -- set Interface eth0 type=dummy -- \
-   add-port br0 eth1 -- set Interface eth1 type=dummy -- \
-   add-port br0 eth2 -- set Interface eth2 type=dummy])
+  [add-port br0 p1 -- set Interface p1 type=dummy -- \
+   add-port br0 p2 -- set Interface p2 type=dummy -- \
+   add-port br0 p3 -- set Interface p3 type=dummy])
 # Set up flow table for TCPv4 port learning.
 AT_CHECK([[ovs-ofctl add-flow br0 'table=0 tcp actions=learn(table=1, hard_timeout=60, eth_type=0x800, nw_proto=6, NXM_OF_IP_SRC[]=NXM_OF_IP_DST[], NXM_OF_IP_DST[]=NXM_OF_IP_SRC[], NXM_OF_TCP_SRC[]=NXM_OF_TCP_DST[], NXM_OF_TCP_DST[]=NXM_OF_TCP_SRC[]), flood']])
 
 # Trace a TCPv4 packet arriving on port 3.
-AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(3),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:06),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=6,tos=0,ttl=64,frag=no),tcp(src=40000,dst=80)' -generate], [0], [stdout])
-AT_CHECK([tail -1 stdout], [0], [Datapath actions: 2,0,1
-])
+flow="in_port(3),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:06),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=6,tos=0,ttl=64,frag=no),tcp(src=40000,dst=80)"
+AT_CHECK([ovs-appctl ofproto/trace br0 "$flow" -generate], [0], [stdout])
+actual=`tail -1 stdout | sed 's/Datapath actions: //'`
+
+expected="1,2,100"
+AT_CHECK([ovs-dpctl normalize-actions "$flow" "$expected"], [0], [stdout])
+mv stdout expout
+AT_CHECK([ovs-dpctl normalize-actions "$flow" "$actual"], [0], [expout])
 
 # Check for the learning entry.
 AT_CHECK([ovs-ofctl dump-flows br0 table=1 | ofctl_strip | sort], [0], [dnl
@@ -135,18 +150,23 @@ AT_CLEANUP
 
 AT_SETUP([learning action - TCPv6 port learning])
 OVS_VSWITCHD_START(
-  [add-port br0 eth0 -- set Interface eth0 type=dummy -- \
-   add-port br0 eth1 -- set Interface eth1 type=dummy -- \
-   add-port br0 eth2 -- set Interface eth2 type=dummy])
+  [add-port br0 p1 -- set Interface p1 type=dummy -- \
+   add-port br0 p2 -- set Interface p2 type=dummy -- \
+   add-port br0 p3 -- set Interface p3 type=dummy])
 # Set up flow table for TCPv6 port learning.
 # Also add a 128-bit-wide "load" action and a 128-bit literal match to check
 # that they work.
 AT_CHECK([[ovs-ofctl add-flow br0 'table=0 tcp6 actions=learn(table=1, hard_timeout=60, eth_type=0x86dd, nw_proto=6, NXM_NX_IPV6_SRC[]=NXM_NX_IPV6_DST[], ipv6_dst=2001:0db8:85a3:0000:0000:8a2e:0370:7334, NXM_OF_TCP_SRC[]=NXM_OF_TCP_DST[], NXM_OF_TCP_DST[]=NXM_OF_TCP_SRC[], load(0x20010db885a308d313198a2e03707348->NXM_NX_IPV6_DST[])), flood']])
 
 # Trace a TCPv6 packet arriving on port 3.
-AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(3),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:06),eth_type(0x86dd),ipv6(src=fec0::2,dst=fec0::1,label=0,proto=6,tclass=0,hlimit=255,frag=no),tcp(src=40000,dst=80)' -generate], [0], [stdout])
-AT_CHECK([tail -1 stdout], [0], [Datapath actions: 2,0,1
-])
+flow="in_port(3),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:06),eth_type(0x86dd),ipv6(src=fec0::2,dst=fec0::1,label=0,proto=6,tclass=0,hlimit=255,frag=no),tcp(src=40000,dst=80)"
+AT_CHECK([ovs-appctl ofproto/trace br0 "$flow" -generate], [0], [stdout])
+actual=`tail -1 stdout | sed 's/Datapath actions: //'`
+
+expected="1,2,100"
+AT_CHECK([ovs-dpctl normalize-actions "$flow" "$expected"], [0], [stdout])
+mv stdout expout
+AT_CHECK([ovs-dpctl normalize-actions "$flow" "$actual"], [0], [expout])
 
 # Check for the learning entry.
 AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
@@ -161,7 +181,8 @@ AT_SETUP([learning action - fin_timeout feature])
 # This is a totally artificial use of the "learn" action.  The only purpose
 # is to check that specifying fin_idle_timeout or fin_hard_timeout causes
 # a corresponding fin_timeout action to end up in the learned flows.
-OVS_VSWITCHD_START
+OVS_VSWITCHD_START(
+    [add-port br0 p1 -- set Interface p1 type=dummy ofport_request=1])
 AT_CHECK([[ovs-ofctl add-flow br0 'actions=learn(fin_hard_timeout=10, fin_idle_timeout=5, NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[], output:NXM_OF_IN_PORT[])']])
 AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(1),eth(src=50:54:00:00:00:05,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=192.168.0.1,tip=192.168.0.2,op=1,sha=50:54:00:00:00:05,tha=00:00:00:00:00:00)' -generate], [0], [ignore])
 AT_CHECK([ovs-ofctl dump-flows br0 table=1 | ofctl_strip], [0],
index 9617af2..009ac36 100644 (file)
@@ -24,14 +24,15 @@ in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv
 in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=58,tclass=0,hlimit=128,frag=no),icmpv6(type=136,code=0),nd(target=::3,tll=00:0a:0b:0c:0d:0e)
 in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=58,tclass=0,hlimit=128,frag=no),icmpv6(type=136,code=0),nd(target=::3,sll=00:05:06:07:08:09,tll=00:0a:0b:0c:0d:0e)
 in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0806),arp(sip=1.2.3.4,tip=5.6.7.8,op=1,sha=00:0f:10:11:12:13,tha=00:14:15:16:17:18)
+skb_mark(0x1234),in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=58,tclass=0,hlimit=128,frag=no),icmpv6(type=136,code=0),nd(target=::3,sll=00:05:06:07:08:09,tll=00:0a:0b:0c:0d:0e)
 ])
 
 (echo '# Valid forms without tun_id or VLAN header.'
  cat odp-base.txt
 
  echo
- echo '# Valid forms with tun_id header.'
- sed 's/^/tun_id(0x7f10354),/' odp-base.txt
+ echo '# Valid forms with tunnel header.'
+ sed 's/^/ipv4_tunnel(tun_id=0x7f10354,src=10.10.10.10,dst=20.20.20.20,tos=0x0,ttl=64,flags(csum,key)),/' odp-base.txt
 
  echo
  echo '# Valid forms with VLAN header.'
@@ -40,17 +41,17 @@ s/$/)/' odp-base.txt
 
  echo
  echo '# Valid forms with QoS priority.'
- sed 's/^/priority(1234),/' odp-base.txt
+ sed 's/^/skb_priority(0x1234),/' odp-base.txt
 
  echo
- echo '# Valid forms with tun_id and VLAN headers.'
- sed 's/^/tun_id(0xfedcba9876543210),/
+ echo '# Valid forms with tunnel and VLAN headers.'
+ sed 's/^/ipv4_tunnel(tun_id=0xfedcba9876543210,src=10.0.0.1,dst=10.0.0.2,tos=0x8,ttl=128,flags()),/
 s/\(eth([[^)]]*)\),*/\1,eth_type(0x8100),vlan(vid=99,pcp=7),encap(/
 s/$/)/' odp-base.txt
 
  echo
- echo '# Valid forms with QOS priority, tun_id, and VLAN headers.'
- sed 's/^/priority(1234),tun_id(0xfedcba9876543210),/
+ echo '# Valid forms with QOS priority, tunnel, and VLAN headers.'
+ sed 's/^/skb_priority(0x1234),ipv4_tunnel(tun_id=0xfedcba9876543210,src=10.10.10.10,dst=20.20.20.20,tos=0x8,ttl=64,flags(key)),/
 s/\(eth([[^)]]*)\),*/\1,eth_type(0x8100),vlan(vid=99,pcp=7),encap(/
 s/$/)/' odp-base.txt
 
@@ -91,6 +92,9 @@ push_vlan(tpid=0x9100,vid=13,pcp=5)
 push_vlan(tpid=0x9100,vid=13,pcp=5,cfi=0)
 pop_vlan
 sample(sample=9.7%,actions(1,2,3,push_vlan(vid=1,pcp=2)))
+set(ipv4_tunnel(tun_id=0xabcdef1234567890,src=1.1.1.1,dst=2.2.2.2,tos=0x0,ttl=64,flags(df,csum,key)))
+set(ipv4_tunnel(tun_id=0xabcdef1234567890,src=1.1.1.1,dst=2.2.2.2,tos=0x0,ttl=64,flags(df,csum,key,0x20)))
+set(ipv4_tunnel(tun_id=0xabcdef1234567890,src=1.1.1.1,dst=2.2.2.2,tos=0x0,ttl=64,flags()))
 ])
 AT_CHECK_UNQUOTED([test-odp parse-actions < actions.txt], [0],
   [`cat actions.txt`
index e232435..aa51e08 100644 (file)
@@ -169,6 +169,9 @@ AT_DATA([test-data], [dnl
 # actions=strip_vlan
 0012 0008 00000000
 
+# actions=set_queue:2309737729
+0015 0008 89abcd01
+
 dnl 802.1ad isn't supported at the moment
 dnl # actions=push_vlan:0x88a8
 dnl 0011 0008 88a8 0000
@@ -181,9 +184,6 @@ ffff 0010 00002320 0001 0005 00000000
 # actions=set_tunnel:0x12345678
 ffff 0010 00002320 0002 0000 12345678
 
-# actions=set_queue:2309737729
-ffff 0010 00002320 0004 0000 89abcd01
-
 # actions=pop_queue
 ffff 0010 00002320 0005 000000000000
 
@@ -240,12 +240,12 @@ dnl action instead, so parse-ofp11-actions will recognise and drop this action.
 ffff 0020 00002320 0016 000000000000 fedcba9876543210 ffffffffffffffff
 
 dnl Write-Metadata duplicated.
-& ofp_actions|WARN|duplicate write_metadata instruction specified
+& ofp_actions|WARN|duplicate write_metadata instruction not allowed, for OpenFlow 1.1+ compatibility
 # bad OF1.1 actions: OFPBAC_UNSUPPORTED_ORDER
 ffff 0020 00002320 0016 000000000000 fedcba9876543210 ffffffffffffffff ffff 0020 00002320 0016 000000000000 fedcba9876543210 ffffffffffffffff
 
 dnl Write-Metadata in wrong position.
-& ofp_actions|WARN|write_metadata instruction must be specified after other instructions/actions
+& ofp_actions|WARN|invalid instruction ordering: apply_actions must appear before write_metadata, for OpenFlow 1.1+ compatibility
 # bad OF1.1 actions: OFPBAC_UNSUPPORTED_ORDER
 ffff 0020 00002320 0016 000000000000 fedcba9876543210 ffffffffffffffff ffff 0010 00002320 0002 0000 12345678
 
@@ -341,7 +341,7 @@ dnl Check that an empty Apply-Actions instruction gets dropped.
 0004 0008 00000000
 
 dnl Duplicate instruction type:
-# bad OF1.1 instructions: OFPIT_BAD_INSTRUCTION
+# bad OF1.1 instructions: OFPBAC_UNSUPPORTED_ORDER
 0004 0008 00000000 0004 0008 00000000
 
 dnl Instructions not multiple of 8 in length.
@@ -379,12 +379,39 @@ dnl Write-Metadata too long.
 0002 0020 00000000 fedcba9876543210 ffffffffffffffff 0000000000000000
 
 dnl Write-Metadata duplicated.
-# bad OF1.1 instructions: OFPIT_BAD_INSTRUCTION
+# bad OF1.1 instructions: OFPBAC_UNSUPPORTED_ORDER
 0002 0018 00000000 fedcba9876543210 ff00ff00ff00ff00 0002 0018 00000000 fedcba9876543210 ff00ff00ff00ff00
 
-dnl Write-Metadata in wrong position.
-& ofp_actions|WARN|write_metadata instruction must be specified after other instructions/actions
-# bad OF1.1 instructions: OFPBAC_UNSUPPORTED_ORDER
+dnl Write-Metadata in wrong position (OpenFlow 1.1+ disregards the order
+dnl and OVS reorders it to the canonical order)
+# actions=write_metadata:0xfedcba9876543210,goto_table:1
+#  1: 01 -> 02
+#  3: 08 -> 18
+#  4: 01 -> 00
+#  8: 00 -> fe
+#  9: 02 -> dc
+# 10: 00 -> ba
+# 11: 18 -> 98
+# 12: 00 -> 76
+# 13: 00 -> 54
+# 14: 00 -> 32
+# 15: 00 -> 10
+# 16: fe -> ff
+# 17: dc -> ff
+# 18: ba -> ff
+# 19: 98 -> ff
+# 20: 76 -> ff
+# 21: 54 -> ff
+# 22: 32 -> ff
+# 23: 10 -> ff
+# 24: ff -> 00
+# 25: ff -> 01
+# 26: ff -> 00
+# 27: ff -> 08
+# 28: ff -> 01
+# 29: ff -> 00
+# 30: ff -> 00
+# 31: ff -> 00
 0001 0008 01 000000 0002 0018 00000000 fedcba9876543210 ffffffffffffffff
 
 dnl Write-Actions not supported yet.
index b45a33a..e99aca9 100644 (file)
@@ -90,11 +90,34 @@ AT_CHECK([ovs-ofctl print-error OFPBIC_BAD_EXPERIMENTER], [0], [dnl
 OpenFlow 1.0: -1,-1
 OpenFlow 1.1: 3,5
 OpenFlow 1.2: 3,5
+OpenFlow 1.3: 3,5
 ])
 AT_CHECK([ovs-ofctl print-error OFPBIC_BAD_EXP_TYPE], [0], [dnl
 OpenFlow 1.0: -1,-1
 OpenFlow 1.1: 3,5
 OpenFlow 1.2: 3,6
+OpenFlow 1.3: 3,6
+])
+AT_CLEANUP
+
+dnl The "bad role" error was a Nicira extension in OpenFlow 1.0 and 1.1.
+dnl It was adopted as an official error code in OpenFlow 1.2.
+AT_SETUP([encoding errors extension that became official])
+AT_KEYWORDS([ofp-print ofp-errors])
+AT_CHECK(
+  [ovs-ofctl encode-error-reply OFPRRFC_BAD_ROLE 0100000812345678], [0], [dnl
+00000000  01 01 00 1c 12 34 56 78-b0 c2 00 00 00 00 23 20 @&t@
+00000010  00 01 02 01 01 00 00 08-12 34 56 78 @&t@
+])
+AT_CHECK(
+  [ovs-ofctl encode-error-reply OFPRRFC_BAD_ROLE 0200000812345678], [0], [dnl
+00000000  02 01 00 1c 12 34 56 78-b0 c2 00 00 00 00 23 20 @&t@
+00000010  00 01 02 01 02 00 00 08-12 34 56 78 @&t@
+])
+AT_CHECK(
+  [ovs-ofctl encode-error-reply OFPRRFC_BAD_ROLE 0300000812345678], [0], [dnl
+00000000  03 01 00 14 12 34 56 78-00 0b 00 02 03 00 00 08 @&t@
+00000010  12 34 56 78 @&t@
 ])
 AT_CLEANUP
 
index 6133fff..4021291 100644 (file)
@@ -45,6 +45,7 @@ AT_SETUP([OFPT_HELLO - ordinary])
 AT_KEYWORDS([ofp-print])
 AT_CHECK([ovs-ofctl ofp-print 0100000800000000], [0], [dnl
 OFPT_HELLO (xid=0x0):
+ version bitmap: 0x01
 ])
 AT_CLEANUP
 
@@ -53,7 +54,92 @@ AT_KEYWORDS([ofp-print])
 AT_CHECK([ovs-ofctl ofp-print 0100001300000000657874726120646174610a], [0],
 [dnl
 OFPT_HELLO (xid=0x0):
-00000000  65 78 74 72 61 20 64 61-74 61 0a                |extra data.     |
+ version bitmap: 0x01
+ unknown data in hello:
+00000000  01 00 00 13 00 00 00 00-65 78 74 72 61 20 64 61 |........extra da|
+00000010  74 61 0a                                        |ta.             |
+])
+AT_CLEANUP
+
+AT_SETUP([OFPT_HELLO with version bitmap])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "01 00 00 10 00 00 00 00 00 01 00 08 00 00 00 f0"], [0],
+[dnl
+OFPT_HELLO (xid=0x0):
+ version bitmap: 0x04, 0x05, 0x06, 0x07
+])
+AT_CLEANUP
+
+AT_SETUP([OFPT_HELLO with version bitmap and extra data])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "\
+01 00 00 1b 00 00 00 00 ff ff 00 06 01 02 00 00 \
+00 01 00 08 00 00 00 f0 61 62 63"], [0],
+[dnl
+OFPT_HELLO (xid=0x0):
+ version bitmap: 0x04, 0x05, 0x06, 0x07
+ unknown data in hello:
+00000000  01 00 00 1b 00 00 00 00-ff ff 00 06 01 02 00 00 |................|
+00000010  00 01 00 08 00 00 00 f0-61 62 63                |........abc     |
+])
+AT_CLEANUP
+
+AT_SETUP([OFPT_HELLO with higher than supported version])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "0f 00 00 08 00 00 00 00"], [0],
+[dnl
+OFPT_HELLO (OF 0x0f) (xid=0x0):
+ version bitmap: 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
+])
+AT_CHECK([ovs-ofctl ofp-print "40 00 00 08 00 00 00 00"], [0],
+[dnl
+OFPT_HELLO (OF 0x40) (xid=0x0):
+ version bitmap: 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f
+])
+AT_CHECK([ovs-ofctl ofp-print "3f 00 00 18 00 00 00 00 00 01 00 0c aa aa aa aa aa aa aa aa 00 00 00 00"], [0],
+[dnl
+OFPT_HELLO (OF 0x3f) (xid=0x0):
+ version bitmap: 0x01, 0x03, 0x05, 0x07, 0x09, 0x0b, 0x0d, 0x0f, 0x11, 0x13, 0x15, 0x17, 0x19, 0x1b, 0x1d, 0x1f
+])
+AT_CLEANUP
+
+AT_SETUP([OFPT_HELLO with contradictory version bitmaps])
+AT_KEYWORDS([ofp-print])
+dnl Bitmap claims support for no versions at all.
+AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' ofp-print "01 00 00 10 00 00 00 00 00 01 00 08 00 00 00 00"], [0],
+[OFPT_HELLO (xid=0x0):
+ version bitmap: 0x01
+ unknown data in hello:
+00000000  01 00 00 10 00 00 00 00-00 01 00 08 00 00 00 00 |................|
+], [dnl
+ofp_util|WARN|peer does not support any OpenFlow version (between 0x01 and 0x1f)
+])
+dnl Bitmap claims support for only versions above 0x1f.
+AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' ofp-print "3f 00 00 18 00 00 00 00 00 01 00 0c 00 00 00 00 aa aa aa aa 00 00 00 00"], [0],
+[OFPT_HELLO (OF 0x3f) (xid=0x0):
+ version bitmap: 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f
+ unknown data in hello:
+00000000  3f 00 00 18 00 00 00 00-00 01 00 0c 00 00 00 00 |?...............|
+00000010  aa aa aa aa 00 00 00 00-                        |........        |
+], [dnl
+ofp_util|WARN|peer does not support any OpenFlow version (between 0x01 and 0x1f)
+])
+dnl Bitmap claims support for nonexistent version 0x00.
+AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' ofp-print "01 00 00 10 00 00 00 00 00 01 00 08 00 00 00 f1"], [0], [dnl
+OFPT_HELLO (xid=0x0):
+ version bitmap: 0x04, 0x05, 0x06, 0x07
+], [dnl
+ofp_util|WARN|peer claims to support invalid OpenFlow version 0x00
+])
+dnl Bitmap claims support for only nonexistent version 0x00.
+AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' ofp-print "01 00 00 10 00 00 00 00 00 01 00 08 00 00 00 01"], [0], [dnl
+OFPT_HELLO (xid=0x0):
+ version bitmap: 0x01
+ unknown data in hello:
+00000000  01 00 00 10 00 00 00 00-00 01 00 08 00 00 00 01 |................|
+], [dnl
+ofp_util|WARN|peer claims to support invalid OpenFlow version 0x00
+ofp_util|WARN|peer does not support any OpenFlow version (between 0x01 and 0x1f)
 ])
 AT_CLEANUP
 
@@ -142,7 +228,7 @@ actions: OUTPUT SET_VLAN_VID SET_VLAN_PCP STRIP_VLAN SET_DL_SRC SET_DL_DST SET_N
  LOCAL(br0): addr:50:54:00:00:00:01
      config:     PORT_DOWN
      state:      LINK_DOWN
-     speed: 100 Mbps now, 100 Mbps max
+     speed: 0 Mbps now, 0 Mbps max
 ])
 AT_CLEANUP
 
@@ -189,7 +275,7 @@ AT_SETUP([OFPT_FEATURES_REPLY - OF1.1])
 AT_KEYWORDS([ofp-print])
 AT_CHECK([ovs-ofctl ofp-print "\
 02 06 00 a0 00 00 00 01 00 00 50 54 00 00 00 01 \
-00 00 01 00 02 00 00 00 00 00 00 87 00 00 ff ff \
+00 00 01 00 02 00 00 00 00 00 00 87 00 00 00 00 \
 ff ff ff fe 00 00 00 00 50 54 00 00 00 01 00 00 \
 62 72 30 00 00 00 00 00 00 00 00 00 00 00 00 00 \
 00 00 00 01 00 00 00 01 00 00 00 00 00 00 00 00 \
@@ -220,7 +306,7 @@ AT_SETUP([OFPT_FEATURES_REPLY cut off mid-port - OF1.1])
 AT_KEYWORDS([ofp-print])
 AT_CHECK([ovs-ofctl ofp-print "\
 02 06 00 90 00 00 00 01 00 00 50 54 00 00 00 01 \
-00 00 01 00 02 00 00 00 00 00 00 87 00 00 ff ff \
+00 00 01 00 02 00 00 00 00 00 00 87 00 00 00 00 \
 ff ff ff fe 00 00 00 00 50 54 00 00 00 01 00 00 \
 62 72 30 00 00 00 00 00 00 00 00 00 00 00 00 00 \
 00 00 00 01 00 00 00 01 00 00 00 00 00 00 00 00 \
@@ -231,7 +317,7 @@ ff ff ff fe 00 00 00 00 50 54 00 00 00 01 00 00 \
 "], [0], [dnl
 ***decode error: OFPBRC_BAD_LEN***
 00000000  02 06 00 90 00 00 00 01-00 00 50 54 00 00 00 01 |..........PT....|
-00000010  00 00 01 00 02 00 00 00-00 00 00 87 00 00 ff ff |................|
+00000010  00 00 01 00 02 00 00 00-00 00 00 87 00 00 00 00 |................|
 00000020  ff ff ff fe 00 00 00 00-50 54 00 00 00 01 00 00 |........PT......|
 00000030  62 72 30 00 00 00 00 00-00 00 00 00 00 00 00 00 |br0.............|
 00000040  00 00 00 01 00 00 00 01-00 00 00 00 00 00 00 00 |................|
@@ -249,7 +335,7 @@ AT_SETUP([OFPT_FEATURES_REPLY - OF1.2])
 AT_KEYWORDS([ofp-print])
 AT_CHECK([ovs-ofctl ofp-print "\
 03 06 00 a0 00 00 00 01 00 00 50 54 00 00 00 01 \
-00 00 01 00 ff 00 00 00 00 00 01 77 00 00 06 ff \
+00 00 01 00 ff 00 00 00 00 00 01 77 00 00 00 00 \
 ff ff ff fe 00 00 00 00 50 54 00 00 00 01 00 00 \
 62 72 30 0a 00 00 00 00 00 00 00 00 00 00 00 00 \
 00 00 00 01 00 00 00 01 00 00 00 00 00 00 00 00 \
@@ -280,7 +366,7 @@ AT_SETUP([OFPT_FEATURES_REPLY cut off mid-port - OF1.2])
 AT_KEYWORDS([ofp-print])
 AT_CHECK([ovs-ofctl ofp-print "\
 03 06 00 a0 00 00 00 01 00 00 50 54 00 00 00 01 \
-00 00 01 00 ff 00 00 00 00 00 01 77 00 00 06 ff \
+00 00 01 00 ff 00 00 00 00 00 01 77 00 00 00 00 \
 ff ff ff fe 00 00 00 00 50 54 00 00 00 01 00 00 \
 62 72 30 0a 00 00 00 00 00 00 00 00 00 00 00 00 \
 00 00 00 01 00 00 00 01 00 00 00 00 00 00 00 00 \
@@ -292,7 +378,7 @@ ff ff ff fe 00 00 00 00 50 54 00 00 00 01 00 00 \
 OFPT_FEATURES_REPLY (OF1.2) (xid=0x1):
 (***truncated to 144 bytes from 160***)
 00000000  03 06 00 a0 00 00 00 01-00 00 50 54 00 00 00 01 |..........PT....|
-00000010  00 00 01 00 ff 00 00 00-00 00 01 77 00 00 06 ff |...........w....|
+00000010  00 00 01 00 ff 00 00 00-00 00 01 77 00 00 00 00 |...........w....|
 00000020  ff ff ff fe 00 00 00 00-50 54 00 00 00 01 00 00 |........PT......|
 00000030  62 72 30 0a 00 00 00 00-00 00 00 00 00 00 00 00 |br0.............|
 00000040  00 00 00 01 00 00 00 01-00 00 00 00 00 00 00 00 |................|
@@ -305,6 +391,30 @@ AT_CHECK([sed 's/.*|//' stderr], [0], [dnl
 ])
 AT_CLEANUP
 
+AT_SETUP([OFPT_FEATURES_REPLY - OF1.3])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "\
+04 06 00 20 00 00 00 01 00 00 50 54 00 00 00 01 \
+00 00 01 00 ff 00 00 00 00 00 01 77 00 00 00 00 \
+"], [0], [dnl
+OFPT_FEATURES_REPLY (OF1.3) (xid=0x1): dpid:0000505400000001
+n_tables:255, n_buffers:256
+capabilities: FLOW_STATS TABLE_STATS PORT_STATS IP_REASM QUEUE_STATS PORT_BLOCKED
+])
+AT_CLEANUP
+
+AT_SETUP([OFPT_FEATURES_REPLY - with auxiliary_id - OF1.3])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "\
+04 06 00 20 00 00 00 01 00 00 50 54 00 00 00 01 \
+00 00 01 00 ff 01 00 00 00 00 01 77 00 00 00 00 \
+"], [0], [dnl
+OFPT_FEATURES_REPLY (OF1.3) (xid=0x1): dpid:0000505400000001
+n_tables:255, n_buffers:256, auxiliary_id:1
+capabilities: FLOW_STATS TABLE_STATS PORT_STATS IP_REASM QUEUE_STATS PORT_BLOCKED
+])
+AT_CLEANUP
+
 AT_SETUP([OFPT_GET_CONFIG_REQUEST])
 AT_KEYWORDS([ofp-print])
 AT_CHECK([ovs-ofctl ofp-print '0107000800000001'], [0], [dnl
@@ -337,7 +447,7 @@ c0 a8 00 02 27 2f 00 00 78 50 cc 5b 57 af 42 1e \
 50 00 02 00 26 e8 00 00 00 00 00 00 00 00 \
 "], [0], [dnl
 OFPT_PACKET_IN (xid=0x0): total_len=60 in_port=3 (via no_match) data_len=60 buffer=0x00000111
-priority=0,tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=10031,tp_dst=0 tcp_csum:26e8
+tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=10031,tp_dst=0 tcp_csum:26e8
 ])
 AT_CLEANUP
 
@@ -351,7 +461,22 @@ AT_CHECK([ovs-ofctl ofp-print "\
 00 00 00 23 20 83 c1 5f 00 00 00 00 \
 "], [0], [dnl
 OFPT_PACKET_IN (OF1.2) (xid=0x0): total_len=42 in_port=LOCAL (via no_match) data_len=42 buffer=0xffffff00
-priority=0,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=00:23:20:83:c1:5f,dl_dst=ff:ff:ff:ff:ff:ff,dl_type=0x8035
+rarp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=00:23:20:83:c1:5f,dl_dst=ff:ff:ff:ff:ff:ff,arp_spa=0.0.0.0,arp_tpa=0.0.0.0,arp_op=3,arp_sha=00:23:20:83:c1:5f,arp_tha=00:23:20:83:c1:5f
+])
+AT_CLEANUP
+
+AT_SETUP([OFPT_PACKET_IN - OF1.3])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "\
+04 0a 00 54 00 00 00 00 ff ff ff 00 00 2a 00 00 \
+01 02 03 04 05 06 07 08 00 01 00 0c 80 00 00 04 \
+ff ff ff fe 00 00 00 00 00 00 ff ff ff ff ff ff \
+00 23 20 83 c1 5f 80 35 00 01 08 00 06 04 00 03 \
+00 23 20 83 c1 5f 00 00 00 00 00 23 20 83 c1 5f \
+00 00 00 00 \
+"], [0], [dnl
+OFPT_PACKET_IN (OF1.3) (xid=0x0): cookie=0x102030405060708 total_len=42 in_port=LOCAL (via no_match) data_len=42 buffer=0xffffff00
+rarp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=00:23:20:83:c1:5f,dl_dst=ff:ff:ff:ff:ff:ff,arp_spa=0.0.0.0,arp_tpa=0.0.0.0,arp_op=3,arp_sha=00:23:20:83:c1:5f,arp_tha=00:23:20:83:c1:5f
 ])
 AT_CLEANUP
 
@@ -380,6 +505,17 @@ OFPT_FLOW_REMOVED (OF1.2) (xid=0x0): dl_vlan=9 reason=hard table_id=5 cookie:0xf
 ])
 AT_CLEANUP
 
+AT_SETUP([OFPT_FLOW_REMOVED - OF1.3])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "\
+04 0b 00 40 00 00 00 00 fe dc ba 98 76 54 32 10 \
+80 00 01 05 00 00 00 01 00 98 96 80 00 3c 00 78 \
+00 00 00 00 00 12 d6 87 00 00 00 00 6f 68 ba 66 \
+00 01 00 0a 80 00 0c 02 10 09 00 00 00 00 00 00"], [0], [dnl
+OFPT_FLOW_REMOVED (OF1.3) (xid=0x0): dl_vlan=9 reason=hard table_id=5 cookie:0xfedcba9876543210 duration1.01s idle60 hard120 pkts1234567 bytes1869134438
+])
+AT_CLEANUP
+
 AT_SETUP([OFPT_PORT_STATUS - OF1.0])
 AT_KEYWORDS([ofp-print])
 AT_CHECK([ovs-ofctl ofp-print "\
@@ -517,6 +653,25 @@ OFPT_FLOW_MOD (OF1.2) (xid=0x2): ADD table:255 priority=65535,arp,in_port=1,vlan
 ])
 AT_CLEANUP
 
+# The flow is formatted with cls_rule_format() for the low-verbosity case.
+AT_SETUP([OFPT_FLOW_MOD - OF1.3 - flags - low verbosity])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' ofp-print "\
+04 0e 00 90 00 00 00 02 00 00 00 00 00 00 00 00 \
+00 00 00 00 00 00 00 00 ff 00 00 00 00 00 ff ff \
+ff ff ff ff ff ff ff ff ff ff ff ff 00 1f 00 00 \
+00 01 00 42 80 00 00 04 00 00 00 01 80 00 08 06 \
+50 54 00 00 00 06 80 00 06 06 50 54 00 00 00 05 \
+80 00 0a 02 08 06 80 00 0c 02 00 00 80 00 2a 02 \
+00 02 80 00 2c 04 c0 a8 00 02 80 00 2e 04 c0 a8 \
+00 01 00 00 00 00 00 00 00 04 00 18 00 00 00 00 \
+00 00 00 10 00 00 00 03 00 00 00 00 00 00 00 00 \
+" 2], [0], [dnl
+OFPT_FLOW_MOD (OF1.3) (xid=0x2): ADD table:255 priority=65535,arp,in_port=1,vlan_tci=0x0000/0x1fff,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,arp_spa=192.168.0.2,arp_tpa=192.168.0.1,arp_op=2 send_flow_rem check_overlap reset_counts no_packet_counts no_byte_counts actions=output:3
+], [dnl
+])
+AT_CLEANUP
+
 AT_SETUP([OFPT_FLOW_MOD - OF1.2 - set-field ip_src])
 AT_KEYWORDS([ofp-print])
 AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' ofp-print "\
@@ -622,6 +777,20 @@ OFPT_PORT_MOD (OF1.2) (xid=0x3):port: 3: addr:50:54:00:00:00:01
 ])
 AT_CLEANUP
 
+AT_SETUP([OFPT_PORT_MOD - OF1.3])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "\
+04 10 00 28 00 00 00 03 00 00 00 03 00 00 00 00 \
+50 54 00 00 00 01 00 00 00 00 00 01 00 00 00 01 \
+00 00 00 00 00 00 00 00 \
+" 3], [0], [dnl
+OFPT_PORT_MOD (OF1.3) (xid=0x3):port: 3: addr:50:54:00:00:00:01
+     config: PORT_DOWN
+     mask:   PORT_DOWN
+     advertise: UNCHANGED
+])
+AT_CLEANUP
+
 AT_SETUP([OFPST_DESC request])
 AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST])
 AT_CHECK([ovs-ofctl ofp-print "0110000c0000000100000000"], [0], [dnl
@@ -733,6 +902,18 @@ OFPST_FLOW request (OF1.2) (xid=0x2): @&t@
 ])
 AT_CLEANUP
 
+AT_SETUP([OFPST_FLOW request - OF1.3])
+AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST])
+AT_CHECK([ovs-ofctl ofp-print "\
+04 12 00 38 00 00 00 02 00 01 00 00 00 00 00 00 \
+ff 00 00 00 ff ff ff ff ff ff ff ff 00 00 00 00 \
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \
+00 01 00 04 00 00 00 00 \
+"], [0], [dnl
+OFPST_FLOW request (OF1.3) (xid=0x2): @&t@
+])
+AT_CLEANUP
+
 AT_SETUP([OFPST_FLOW reply - OF1.0])
 AT_KEYWORDS([ofp-print OFPT_STATS_REPLY])
 AT_CHECK([ovs-ofctl ofp-print "\
@@ -836,6 +1017,18 @@ OFPST_AGGREGATE request (OF1.2) (xid=0x2): @&t@
 ])
 AT_CLEANUP
 
+AT_SETUP([OFPST_AGGREGATE request - OF1.3])
+AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST])
+AT_CHECK([ovs-ofctl ofp-print "\
+04 12 00 38 00 00 00 02 00 02 00 00 00 00 00 00 \
+ff 00 00 00 ff ff ff ff ff ff ff ff 00 00 00 00 \
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \
+00 01 00 04 00 00 00 00 \
+"], [0], [dnl
+OFPST_AGGREGATE request (OF1.3) (xid=0x2): @&t@
+])
+AT_CLEANUP
+
 AT_SETUP([OFPST_AGGREGATE reply - OF1.0])
 AT_KEYWORDS([ofp-print OFPT_STATS_REPLY])
 AT_CHECK([ovs-ofctl ofp-print "\
@@ -858,6 +1051,17 @@ OFPST_AGGREGATE reply (OF1.2) (xid=0x2): packet_count=121 byte_count=19279 flow_
 ])
 AT_CLEANUP
 
+AT_SETUP([OFPST_AGGREGATE reply - OF1.3])
+AT_KEYWORDS([ofp-print OFPT_STATS_REPLY])
+AT_CHECK([ovs-ofctl ofp-print "\
+04 13 00 28 00 00 00 02 00 02 00 00 00 00 00 00 \
+00 00 00 00 00 00 00 79 00 00 00 00 00 00 4b 4f \
+00 00 00 03 00 00 00 00 \
+"], [0], [dnl
+OFPST_AGGREGATE reply (OF1.3) (xid=0x2): packet_count=121 byte_count=19279 flow_count=3
+])
+AT_CLEANUP
+
 AT_SETUP([OFPST_TABLE request - OF1.0])
 AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST])
 AT_CHECK([ovs-ofctl ofp-print "0110000c0000000100030000"], [0], [dnl
@@ -879,6 +1083,13 @@ OFPST_TABLE request (OF1.2) (xid=0x2):
 ])
 AT_CLEANUP
 
+AT_SETUP([OFPST_TABLE request - OF1.3])
+AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST])
+AT_CHECK([ovs-ofctl ofp-print "04120010000000020003000000000000"], [0], [dnl
+OFPST_TABLE request (OF1.3) (xid=0x2):
+])
+AT_CLEANUP
+
 AT_SETUP([OFPST_TABLE reply - OF1.0])
 AT_KEYWORDS([ofp-print OFPT_STATS_REPLY])
 AT_CHECK([ovs-ofctl ofp-print "\
@@ -949,13 +1160,27 @@ AT_KEYWORDS([ofp-print OFPT_STATS_REPLY])
 AT_CHECK([ovs-ofctl ofp-print "$(cat in)"], [0], [expout])
 AT_CLEANUP
 
+AT_SETUP([OFPST_TABLE reply - OF1.3])
+AT_KEYWORDS([ofp-print OFPT_STATS_REPLY])
+AT_CHECK([ovs-ofctl ofp-print "\
+04 13 00 40 00 00 00 01 00 03 00 00 00 00 00 00 \
+00 00 00 00 00 00 00 0b 00 00 00 00 00 00 02 00 \
+00 00 00 00 00 00 01 00 01 00 00 00 00 00 00 0c \
+00 00 00 00 00 00 02 01 00 00 00 00 00 00 01 01 \
+"], [0], [dnl
+OFPST_TABLE reply (OF1.3) (xid=0x1): 2 tables
+  0: active=11, lookup=512, matched=256
+  1: active=12, lookup=513, matched=257
+])
+AT_CLEANUP
+
 AT_SETUP([OFPST_PORT request - 1.0])
 AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST])
 AT_CHECK([ovs-ofctl ofp-print "\
 01 10 00 14 00 00 00 01 00 04 00 00 ff ff 00 00 \
 00 00 00 00 \
 "], [0], [dnl
-OFPST_PORT request (xid=0x1): port_no=65535
+OFPST_PORT request (xid=0x1): port_no=ANY
 ])
 AT_CLEANUP
 
@@ -965,7 +1190,7 @@ AT_CHECK([ovs-ofctl ofp-print "\
 02 12 00 18 00 00 00 02 00 04 00 00 00 00 00 00 \
 ff ff ff ff 00 00 00 00 \
 "], [0], [dnl
-OFPST_PORT request (OF1.1) (xid=0x2): port_no=65535
+OFPST_PORT request (OF1.1) (xid=0x2): port_no=ANY
 ])
 AT_CLEANUP
 
@@ -975,7 +1200,17 @@ AT_CHECK([ovs-ofctl ofp-print "\
 03 12 00 18 00 00 00 02 00 04 00 00 00 00 00 00 \
 ff ff ff ff 00 00 00 00 \
 "], [0], [dnl
-OFPST_PORT request (OF1.2) (xid=0x2): port_no=65535
+OFPST_PORT request (OF1.2) (xid=0x2): port_no=ANY
+])
+AT_CLEANUP
+
+AT_SETUP([OFPST_PORT request - 1.3])
+AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST])
+AT_CHECK([ovs-ofctl ofp-print "\
+04 12 00 18 00 00 00 02 00 04 00 00 00 00 00 00 \
+ff ff ff ff 00 00 00 00 \
+"], [0], [dnl
+OFPST_PORT request (OF1.3) (xid=0x2): port_no=ANY
 ])
 AT_CLEANUP
 
@@ -1013,7 +1248,7 @@ AT_CHECK([ovs-ofctl ofp-print "\
 OFPST_PORT reply (xid=0x1): 4 ports
   port  3: rx pkts=19744, bytes=1007694, drop=0, errs=0, frame=0, over=0, crc=0
            tx pkts=5170, bytes=356796, drop=0, errs=0, coll=0
-  port 65534: rx pkts=684, bytes=68748, drop=0, errs=0, frame=0, over=0, crc=0
+  port LOCAL: rx pkts=684, bytes=68748, drop=0, errs=0, frame=0, over=0, crc=0
            tx pkts=501, bytes=56092, drop=0, errs=0, coll=0
   port  2: rx pkts=1726, bytes=144564, drop=0, errs=0, frame=0, over=0, crc=0
            tx pkts=1412, bytes=140244, drop=0, errs=0, coll=0
@@ -1050,7 +1285,7 @@ AT_CHECK([ovs-ofctl ofp-print "\
 OFPST_PORT reply (OF1.2) (xid=0x2): 3 ports
   port  2: rx pkts=103766, bytes=39651480, drop=0, errs=0, frame=0, over=0, crc=0
            tx pkts=136, bytes=11512, drop=0, errs=0, coll=0
-  port 65534: rx pkts=68, bytes=5756, drop=0, errs=0, frame=0, over=0, crc=0
+  port LOCAL: rx pkts=68, bytes=5756, drop=0, errs=0, frame=0, over=0, crc=0
            tx pkts=40236, bytes=18757188, drop=0, errs=0, coll=0
   port  1: rx pkts=68, bytes=5756, drop=0, errs=0, frame=0, over=0, crc=0
            tx pkts=40236, bytes=18757188, drop=0, errs=0, coll=0
@@ -1063,7 +1298,7 @@ AT_CHECK([ovs-ofctl ofp-print "\
 01 10 00 14 00 00 00 01 00 05 00 00 ff fc 00 00 \
 ff ff ff ff \
 "], [0], [dnl
-OFPST_QUEUE request (xid=0x1):port=ALL queue=ALL
+OFPST_QUEUE request (xid=0x1):port=ANY queue=ALL
 ])
 AT_CLEANUP
 
@@ -1071,9 +1306,9 @@ AT_SETUP([OFPST_QUEUE request - OF1.1])
 AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST])
 AT_CHECK([ovs-ofctl ofp-print "\
 02 12 00 18 00 00 00 02 00 05 00 00 00 00 00 00 \
-ff ff ff fc ff ff ff ff \
+ff ff ff ff ff ff ff ff \
 "], [0], [dnl
-OFPST_QUEUE request (OF1.1) (xid=0x2):port=ALL queue=ALL
+OFPST_QUEUE request (OF1.1) (xid=0x2):port=ANY queue=ALL
 ])
 AT_CLEANUP
 
@@ -1081,9 +1316,19 @@ AT_SETUP([OFPST_QUEUE request - OF1.2])
 AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST])
 AT_CHECK([ovs-ofctl ofp-print "\
 03 12 00 18 00 00 00 02 00 05 00 00 00 00 00 00 \
-ff ff ff fc ff ff ff ff \
+ff ff ff ff ff ff ff ff \
+"], [0], [dnl
+OFPST_QUEUE request (OF1.2) (xid=0x2):port=ANY queue=ALL
+])
+AT_CLEANUP
+
+AT_SETUP([OFPST_QUEUE request - OF1.3])
+AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST])
+AT_CHECK([ovs-ofctl ofp-print "\
+04 12 00 18 00 00 00 02 00 05 00 00 00 00 00 00 \
+ff ff ff ff ff ff ff ff \
 "], [0], [dnl
-OFPST_QUEUE request (OF1.2) (xid=0x2):port=ALL queue=ALL
+OFPST_QUEUE request (OF1.3) (xid=0x2):port=ANY queue=ALL
 ])
 AT_CLEANUP
 
@@ -1215,6 +1460,13 @@ OFPT_BARRIER_REQUEST (OF1.2) (xid=0x1):
 ])
 AT_CLEANUP
 
+AT_SETUP([OFPT_BARRIER_REQUEST - OF1.3])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print '04 14 00 08 00 00 00 01'], [0], [dnl
+OFPT_BARRIER_REQUEST (OF1.3) (xid=0x1):
+])
+AT_CLEANUP
+
 AT_SETUP([OFPT_BARRIER_REPLY - OF1.0])
 AT_KEYWORDS([ofp-print])
 AT_CHECK([ovs-ofctl ofp-print '01 13 00 08 00 00 00 01'], [0], [dnl
@@ -1222,20 +1474,67 @@ OFPT_BARRIER_REPLY (xid=0x1):
 ])
 AT_CLEANUP
 
-AT_SETUP([OFPT_BARRIER_REPLY] - OF1.1)
+AT_SETUP([OFPT_BARRIER_REPLY - OF1.1])
 AT_KEYWORDS([ofp-print])
 AT_CHECK([ovs-ofctl ofp-print '02 15 00 08 00 00 00 01'], [0], [dnl
 OFPT_BARRIER_REPLY (OF1.1) (xid=0x1):
 ])
 AT_CLEANUP
 
-AT_SETUP([OFPT_BARRIER_REPLY] - OF1.2)
+AT_SETUP([OFPT_BARRIER_REPLY - OF1.2])
 AT_KEYWORDS([ofp-print])
 AT_CHECK([ovs-ofctl ofp-print '03 15 00 08 00 00 00 01'], [0], [dnl
 OFPT_BARRIER_REPLY (OF1.2) (xid=0x1):
 ])
 AT_CLEANUP
 
+AT_SETUP([OFPT_BARRIER_REPLY - OF1.3])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print '04 15 00 08 00 00 00 01'], [0], [dnl
+OFPT_BARRIER_REPLY (OF1.3) (xid=0x1):
+])
+AT_CLEANUP
+
+
+AT_SETUP([OFPT_SET_ASYNC - OF1.3])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "\
+04 1c 00 20 00 00 00 00 00 00 10 05 00 00 10 07 \
+00 00 00 03 00 00 00 07 00 00 00 00 00 00 00 03 \
+"], [0], [dnl
+OFPT_SET_ASYNC (OF1.3) (xid=0x0):
+ master:
+       PACKET_IN: no_match invalid_ttl 12
+     PORT_STATUS: add delete
+    FLOW_REMOVED: (off)
+
+ slave:
+       PACKET_IN: no_match action invalid_ttl 12
+     PORT_STATUS: add delete modify
+    FLOW_REMOVED: idle hard
+])
+AT_CLEANUP
+
+AT_SETUP([OFPT_ROLE_REQUEST - OF1.2])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "\
+03 18 00 18 00 00 00 02 00 00 00 02 00 00 00 00 \
+00 00 00 00 00 00 00 03 \
+"], [0], [dnl
+OFPT_ROLE_REQUEST (OF1.2) (xid=0x2): role=master generation_id=3
+])
+AT_CLEANUP
+
+AT_SETUP([OFPT_ROLE_REQUEST - nochange - OF1.2])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "\
+03 18 00 18 00 00 00 02 00 00 00 00 00 00 00 00 \
+00 00 00 00 00 00 00 00 \
+"], [0], [dnl
+OFPT_ROLE_REQUEST (OF1.2) (xid=0x2): role=nochange
+])
+AT_CLEANUP
+
 AT_SETUP([NXT_ROLE_REQUEST])
 AT_KEYWORDS([ofp-print])
 AT_CHECK([ovs-ofctl ofp-print "\
@@ -1246,6 +1545,16 @@ NXT_ROLE_REQUEST (xid=0x2): role=master
 ])
 AT_CLEANUP
 
+AT_SETUP([OFPT_ROLE_REPLY - OF1.2])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "\
+03 19 00 18 00 00 00 02 00 00 00 03 00 00 00 00 \
+00 00 00 00 00 00 00 00 \
+"], [0], [dnl
+OFPT_ROLE_REPLY (OF1.2) (xid=0x2): role=slave
+])
+AT_CLEANUP
+
 AT_SETUP([NXT_ROLE_REPLY])
 AT_KEYWORDS([ofp-print])
 AT_CHECK([ovs-ofctl ofp-print "\
@@ -1283,7 +1592,7 @@ ff ff ff ff ff ff 00 00 00 00 82 82 82 82 82 82 \
 31 6d 00 00 00 00 00 00 00 00 \
 "], [0], [dnl
 NXT_PACKET_IN (xid=0x0): table_id=7 cookie=0x9 total_len=64 in_port=1 tun_id=0x6 metadata=0x5a5a5a5a5a5a5a5a reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x5 (via action) data_len=64 (unbuffered)
-priority=0,tcp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=85,tp_dst=86 tcp_csum:316d
+tcp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=85,tp_dst=86 tcp_csum:316d
 ])
 AT_CLEANUP
 
diff --git a/tests/ofp-util.at b/tests/ofp-util.at
new file mode 100644 (file)
index 0000000..fbb6848
--- /dev/null
@@ -0,0 +1,52 @@
+AT_BANNER([OpenFlow utilities])
+
+AT_SETUP([encoding hellos])
+dnl All versions up to a max version supported:
+AT_CHECK([ovs-ofctl encode-hello 0x2], [0], [dnl
+00000000  01 00 00 08 00 00 00 01-
+OFPT_HELLO (xid=0x1):
+ version bitmap: 0x01
+])
+AT_CHECK([ovs-ofctl encode-hello 0x6], [0], [dnl
+00000000  02 00 00 08 00 00 00 01-
+OFPT_HELLO (OF1.1) (xid=0x1):
+ version bitmap: 0x01, 0x02
+])
+AT_CHECK([ovs-ofctl encode-hello 0xe], [0], [dnl
+00000000  03 00 00 08 00 00 00 01-
+OFPT_HELLO (OF1.2) (xid=0x1):
+ version bitmap: 0x01, 0x02, 0x03
+])
+AT_CHECK([ovs-ofctl encode-hello 0x1e], [0], [dnl
+00000000  04 00 00 08 00 00 00 01-
+OFPT_HELLO (OF1.3) (xid=0x1):
+ version bitmap: 0x01, 0x02, 0x03, 0x04
+])
+AT_CHECK([ovs-ofctl encode-hello 0x3e], [0], [dnl
+00000000  05 00 00 08 00 00 00 01-
+OFPT_HELLO (OF 0x05) (xid=0x1):
+ version bitmap: 0x01, 0x02, 0x03, 0x04, 0x05
+])
+
+dnl Some versions below max version missing.
+AT_CHECK([ovs-ofctl encode-hello 0xc], [0], [dnl
+00000000  03 00 00 10 00 00 00 01-00 01 00 08 00 00 00 0c @&t@
+OFPT_HELLO (OF1.2) (xid=0x1):
+ version bitmap: 0x02, 0x03
+])
+AT_CHECK([ovs-ofctl encode-hello 0xa], [0], [dnl
+00000000  03 00 00 10 00 00 00 01-00 01 00 08 00 00 00 0a @&t@
+OFPT_HELLO (OF1.2) (xid=0x1):
+ version bitmap: 0x01, 0x03
+])
+AT_CHECK([ovs-ofctl encode-hello 0x8], [0], [dnl
+00000000  03 00 00 10 00 00 00 01-00 01 00 08 00 00 00 08 @&t@
+OFPT_HELLO (OF1.2) (xid=0x1):
+ version bitmap: 0x03
+])
+AT_CHECK([ovs-ofctl encode-hello 0x4], [0], [dnl
+00000000  02 00 00 10 00 00 00 01-00 01 00 08 00 00 00 04 @&t@
+OFPT_HELLO (OF1.1) (xid=0x1):
+ version bitmap: 0x02
+])
+AT_CLEANUP
index bc2362d..fd66d24 100644 (file)
@@ -2,6 +2,8 @@ AT_BANNER([ofproto-dpif])
 
 AT_SETUP([ofproto-dpif - resubmit])
 OVS_VSWITCHD_START
+ADD_OF_PORTS([br0], [1], [10], [11], [12], [13], [14], [15],
+                    [16], [17], [18], [19], [20], [21])
 AT_DATA([flows.txt], [dnl
 table=0 in_port=1 priority=1000 icmp actions=output(10),resubmit(2),output(19),resubmit(3),output(21)
 table=0 in_port=2 priority=1500 icmp actions=output(11),resubmit(,1),output(16),resubmit(2,1),output(18)
@@ -20,6 +22,7 @@ AT_CLEANUP
 
 AT_SETUP([ofproto-dpif - registers])
 OVS_VSWITCHD_START
+ADD_OF_PORTS([br0], [20], [21], [22], [33], [90])
 AT_DATA([flows.txt], [dnl
 in_port=90                 actions=resubmit:2,resubmit:3,resubmit:4,resubmit:91
 in_port=91                 actions=resubmit:5,resubmit:6,resubmit:7,resubmit:92
@@ -53,6 +56,7 @@ AT_CLEANUP
 
 AT_SETUP([ofproto-dpif - output])
 OVS_VSWITCHD_START
+ADD_OF_PORTS([br0], [1], [9], [10], [11], [55], [66], [77], [88])
 AT_DATA([flows.txt], [dnl
 in_port=1 actions=resubmit:2,resubmit:3,resubmit:4,resubmit:5,resubmit:6,resubmit:7
 in_port=2 actions=output:9
@@ -72,6 +76,7 @@ AT_CLEANUP
 
 AT_SETUP([ofproto-dpif - dec_ttl])
 OVS_VSWITCHD_START
+ADD_OF_PORTS([br0], [1], [2], [3], [4])
 AT_DATA([flows.txt], [dnl
 table=0 in_port=1 action=dec_ttl,output:2,resubmit(1,1),output:4
 table=1 in_port=1 action=dec_ttl,output:3
@@ -98,16 +103,15 @@ AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(1),eth(src=50:54:00:00:00:05,dst
 OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
 AT_CHECK([cat ofctl_monitor.log], [0], [dnl
 NXT_PACKET_IN (xid=0x0): table_id=1 total_len=42 in_port=1 (via invalid_ttl) data_len=42 (unbuffered)
-priority=0,icmp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=1,icmp_type=0,icmp_code=0
+icmp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=1,icmp_type=0,icmp_code=0
 ])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
 
 AT_SETUP([ofproto-dpif - output, OFPP_NONE ingress port])
-OVS_VSWITCHD_START(
-       [add-port br0 p1 -- set Interface p1 type=dummy --\
-        add-port br0 p2 -- set Interface p2 type=dummy])
+OVS_VSWITCHD_START
+ADD_OF_PORTS([br0], [1], [2])
 
 AT_CHECK([ovs-ofctl add-flow br0 action=normal])
 
@@ -116,7 +120,7 @@ flow="eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src
 AT_CHECK([ovs-appctl ofproto/trace br0 "$flow"], [0], [stdout])
 actual=`tail -1 stdout | sed 's/Datapath actions: //'`
 
-expected="0,1,2"
+expected="1,2,100"
 AT_CHECK([ovs-dpctl normalize-actions "$flow" "$expected"], [0], [stdout])
 mv stdout expout
 AT_CHECK([ovs-dpctl normalize-actions "$flow" "$actual"], [0], [expout])
@@ -138,27 +142,21 @@ AT_CHECK([ovs-vsctl -- \
 AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(9),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=1.1.1.1,dst=2.2.2.2,proto=1,tos=0xff,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout])
 AT_CHECK([tail -1 stdout], [0],
   [Datapath actions: dnl
-0,dnl
-set(ipv4(src=1.1.1.1,dst=2.2.2.2,proto=1,tos=0x7,ttl=128,frag=no)),set(priority(1)),1,dnl
-set(ipv4(src=1.1.1.1,dst=2.2.2.2,proto=1,tos=0xb,ttl=128,frag=no)),set(priority(2)),1,dnl
+100,dnl
+set(ipv4(src=1.1.1.1,dst=2.2.2.2,proto=1,tos=0x7,ttl=128,frag=no)),set(skb_priority(0x1)),1,dnl
+set(ipv4(src=1.1.1.1,dst=2.2.2.2,proto=1,tos=0xb,ttl=128,frag=no)),set(skb_priority(0x2)),1,dnl
 1,dnl
-set(ipv4(src=1.1.1.1,dst=2.2.2.2,proto=1,tos=0x7,ttl=128,frag=no)),set(priority(1)),1,dnl
-set(ipv4(src=1.1.1.1,dst=2.2.2.2,proto=1,tos=0xff,ttl=128,frag=no)),set(priority(0)),1,dnl
+set(ipv4(src=1.1.1.1,dst=2.2.2.2,proto=1,tos=0x7,ttl=128,frag=no)),set(skb_priority(0x1)),1,dnl
+set(ipv4(src=1.1.1.1,dst=2.2.2.2,proto=1,tos=0xff,ttl=128,frag=no)),set(skb_priority(0)),1,dnl
 set(ipv4(src=1.1.1.1,dst=2.2.2.2,proto=1,tos=0x3,ttl=128,frag=no)),1,dnl
-0
+100
 ])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
 AT_SETUP([ofproto-dpif - output/flood flags])
-OVS_VSWITCHD_START([dnl
-        add-port br0 p1 -- set Interface p1 type=dummy --\
-        add-port br0 p2 -- set Interface p2 type=dummy --\
-        add-port br0 p3 -- set Interface p3 type=dummy --\
-        add-port br0 p4 -- set Interface p4 type=dummy --\
-        add-port br0 p5 -- set Interface p5 type=dummy --\
-        add-port br0 p6 -- set Interface p6 type=dummy --\
-        add-port br0 p7 -- set Interface p7 type=dummy ])
+OVS_VSWITCHD_START
+ADD_OF_PORTS([br0], [1], [2], [3], [4], [5], [6], [7])
 
 AT_DATA([flows.txt], [dnl
 in_port=local actions=local,flood
@@ -171,7 +169,7 @@ AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
 AT_CHECK([ovs-ofctl mod-port br0 5 noforward])
 AT_CHECK([ovs-ofctl mod-port br0 6 noflood])
 
-AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(0),eth(src=00:00:00:00:00:01,dst=00:00:00:00:00:02),eth_type(0x0900)'], [0], [stdout])
+AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(100),eth(src=00:00:00:00:00:01,dst=00:00:00:00:00:02),eth_type(0x0900)'], [0], [stdout])
 AT_CHECK([tail -1 stdout \
 | sed -e 's/Datapath actions: //' | tr ',' '\n' | sort], [0], [dnl
 1
@@ -184,7 +182,7 @@ AT_CHECK([tail -1 stdout \
 AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(1),eth(src=00:00:00:00:00:01,dst=00:00:00:00:00:02),eth_type(0x0900)'], [0], [stdout])
 AT_CHECK([tail -1 stdout \
 | sed -e 's/Datapath actions: //' | tr ',' '\n' | sort], [0], [dnl
-0
+100
 2
 3
 4
@@ -194,8 +192,8 @@ AT_CHECK([tail -1 stdout \
 AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(2),eth(src=00:00:00:00:00:01,dst=00:00:00:00:00:02),eth_type(0x0900)'], [0], [stdout])
 AT_CHECK([tail -1 stdout \
 | sed -e 's/Datapath actions: //' | tr ',' '\n' | sort], [0], [dnl
-0
 1
+100
 3
 4
 6
@@ -204,18 +202,19 @@ AT_CHECK([tail -1 stdout \
 
 AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(3),eth(src=00:00:00:00:00:01,dst=00:00:00:00:00:02),eth_type(0x0900)'], [0], [stdout])
 AT_CHECK([tail -1 stdout], [0],
-  [Datapath actions: 0,1,2,4,6,7
+  [Datapath actions: 100,1,2,4,6,7
 ])
 
 AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(4),eth(src=00:00:00:00:00:01,dst=00:00:00:00:00:02),eth_type(0x0900)'], [0], [stdout])
 AT_CHECK([tail -1 stdout], [0],
-  [Datapath actions: set(priority(1)),0,1,2,set(priority(2)),3,set(priority(1)),6,7
+  [Datapath actions: set(skb_priority(0x1)),100,1,2,set(skb_priority(0x2)),3,set(skb_priority(0x1)),6,7
 ])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
 AT_SETUP([ofproto-dpif - set_tunnel])
 OVS_VSWITCHD_START
+ADD_OF_PORTS([br0], [1], [2], [3], [4], [5], [90])
 AT_DATA([flows.txt], [dnl
 in_port=90 actions=resubmit:1,resubmit:2,resubmit:3,resubmit:4,resubmit:5
 in_port=1 actions=set_tunnel:1,output:1
@@ -263,13 +262,13 @@ done
 OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
 AT_CHECK([cat ofctl_monitor.log], [0], [dnl
 OFPT_PACKET_IN (xid=0x0): total_len=60 in_port=1 (via no_match) data_len=60 (unbuffered)
-priority=0,tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=8,tp_dst=9 tcp_csum:0
+tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=9 tcp_csum:0
 dnl
 OFPT_PACKET_IN (xid=0x0): total_len=60 in_port=1 (via no_match) data_len=60 (unbuffered)
-priority=0,tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=8,tp_dst=9 tcp_csum:0
+tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=9 tcp_csum:0
 dnl
 OFPT_PACKET_IN (xid=0x0): total_len=60 in_port=1 (via no_match) data_len=60 (unbuffered)
-priority=0,tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=8,tp_dst=9 tcp_csum:0
+tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=9 tcp_csum:0
 ])
 
 dnl Singleton controller action.
@@ -282,13 +281,13 @@ done
 OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
 AT_CHECK([cat ofctl_monitor.log], [0], [dnl
 OFPT_PACKET_IN (xid=0x0): total_len=60 in_port=1 (via action) data_len=60 (unbuffered)
-priority=0,tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=10:11:11:11:11:11,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=8,tp_dst=10 tcp_csum:0
+tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=10:11:11:11:11:11,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=10 tcp_csum:0
 dnl
 OFPT_PACKET_IN (xid=0x0): total_len=60 in_port=1 (via action) data_len=60 (unbuffered)
-priority=0,tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=10:11:11:11:11:11,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=8,tp_dst=10 tcp_csum:0
+tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=10:11:11:11:11:11,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=10 tcp_csum:0
 dnl
 OFPT_PACKET_IN (xid=0x0): total_len=60 in_port=1 (via action) data_len=60 (unbuffered)
-priority=0,tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=10:11:11:11:11:11,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=8,tp_dst=10 tcp_csum:0
+tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=10:11:11:11:11:11,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=10 tcp_csum:0
 ])
 
 dnl Modified controller action.
@@ -301,13 +300,13 @@ done
 OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
 AT_CHECK([cat ofctl_monitor.log], [0], [dnl
 OFPT_PACKET_IN (xid=0x0): total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
-priority=0,tcp,metadata=0,in_port=0,dl_vlan=15,dl_vlan_pcp=0,dl_src=30:33:33:33:33:33,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=8,tp_dst=10 tcp_csum:0
+tcp,metadata=0,in_port=0,dl_vlan=15,dl_vlan_pcp=0,dl_src=30:33:33:33:33:33,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=10 tcp_csum:0
 dnl
 OFPT_PACKET_IN (xid=0x0): total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
-priority=0,tcp,metadata=0,in_port=0,dl_vlan=15,dl_vlan_pcp=0,dl_src=30:33:33:33:33:33,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=8,tp_dst=10 tcp_csum:0
+tcp,metadata=0,in_port=0,dl_vlan=15,dl_vlan_pcp=0,dl_src=30:33:33:33:33:33,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=10 tcp_csum:0
 dnl
 OFPT_PACKET_IN (xid=0x0): total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
-priority=0,tcp,metadata=0,in_port=0,dl_vlan=15,dl_vlan_pcp=0,dl_src=30:33:33:33:33:33,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=8,tp_dst=10 tcp_csum:0
+tcp,metadata=0,in_port=0,dl_vlan=15,dl_vlan_pcp=0,dl_src=30:33:33:33:33:33,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=10 tcp_csum:0
 ])
 
 dnl Checksum TCP.
@@ -320,31 +319,31 @@ done
 OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
 AT_CHECK([cat ofctl_monitor.log], [0], [dnl
 NXT_PACKET_IN (xid=0x0): cookie=0x1 total_len=60 in_port=1 (via action) data_len=60 (unbuffered)
-priority=0,tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=20:22:22:22:22:22,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=8,tp_dst=11 tcp_csum:0
+tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=20:22:22:22:22:22,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=11 tcp_csum:0
 dnl
 NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x3 total_len=64 in_port=1 reg0=0x1 (via action) data_len=64 (unbuffered)
-priority=0,tcp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=20:22:22:22:22:22,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=8,tp_dst=11 tcp_csum:0
+tcp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=20:22:22:22:22:22,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=11 tcp_csum:0
 dnl
 NXT_PACKET_IN (xid=0x0): table_id=2 cookie=0x4 total_len=64 in_port=1 reg0=0x1 reg1=0x2 (via action) data_len=64 (unbuffered)
-priority=0,tcp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=8,tp_dst=11 tcp_csum:0
+tcp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=11 tcp_csum:0
 dnl
 NXT_PACKET_IN (xid=0x0): table_id=3 cookie=0x5 total_len=64 in_port=1 reg0=0x1 reg1=0x2 reg2=0x3 (via action) data_len=64 (unbuffered)
-priority=0,tcp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=8,tp_dst=11 tcp_csum:0
+tcp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=11 tcp_csum:0
 dnl
 NXT_PACKET_IN (xid=0x0): table_id=4 cookie=0x6 total_len=64 in_port=1 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 (via action) data_len=64 (unbuffered)
-priority=0,tcp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=8,tp_dst=11 tcp_csum:1a03
+tcp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=11 tcp_csum:1a03
 dnl
 NXT_PACKET_IN (xid=0x0): table_id=5 cookie=0x7 total_len=64 in_port=1 tun_id=0x6 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x5 (via action) data_len=64 (unbuffered)
-priority=0,tcp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=8,tp_dst=11 tcp_csum:3205
+tcp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=11 tcp_csum:3205
 dnl
 NXT_PACKET_IN (xid=0x0): table_id=6 cookie=0x8 total_len=64 in_port=1 tun_id=0x6 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x5 (via action) data_len=64 (unbuffered)
-priority=0,tcp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=85,tp_dst=11 tcp_csum:31b8
+tcp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=85,tp_dst=11 tcp_csum:31b8
 dnl
 NXT_PACKET_IN (xid=0x0): table_id=7 cookie=0x9 total_len=64 in_port=1 tun_id=0x6 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x5 (via action) data_len=64 (unbuffered)
-priority=0,tcp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=85,tp_dst=86 tcp_csum:316d
+tcp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=85,tp_dst=86 tcp_csum:316d
 dnl
 NXT_PACKET_IN (xid=0x0): table_id=7 cookie=0x9 total_len=64 in_port=1 tun_id=0x6 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x5 (via action) data_len=64 (unbuffered)
-priority=0,tcp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=85,tp_dst=86 tcp_csum:316d
+tcp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=85,tp_dst=86 tcp_csum:316d
 ])
 
 dnl Checksum UDP.
@@ -357,31 +356,31 @@ done
 OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
 AT_CHECK([cat ofctl_monitor.log], [0], [dnl
 NXT_PACKET_IN (xid=0x0): cookie=0x1 total_len=60 in_port=1 (via action) data_len=60 (unbuffered)
-priority=0,udp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=20:22:22:22:22:22,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=8,tp_dst=11 udp_csum:1234
+udp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=20:22:22:22:22:22,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=8,tp_dst=11 udp_csum:1234
 dnl
 NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x3 total_len=64 in_port=1 reg0=0x1 (via action) data_len=64 (unbuffered)
-priority=0,udp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=20:22:22:22:22:22,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=8,tp_dst=11 udp_csum:1234
+udp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=20:22:22:22:22:22,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=8,tp_dst=11 udp_csum:1234
 dnl
 NXT_PACKET_IN (xid=0x0): table_id=2 cookie=0x4 total_len=64 in_port=1 reg0=0x1 reg1=0x2 (via action) data_len=64 (unbuffered)
-priority=0,udp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=8,tp_dst=11 udp_csum:1234
+udp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=8,tp_dst=11 udp_csum:1234
 dnl
 NXT_PACKET_IN (xid=0x0): table_id=3 cookie=0x5 total_len=64 in_port=1 reg0=0x1 reg1=0x2 reg2=0x3 (via action) data_len=64 (unbuffered)
-priority=0,udp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=8,tp_dst=11 udp_csum:1234
+udp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=8,tp_dst=11 udp_csum:1234
 dnl
 NXT_PACKET_IN (xid=0x0): table_id=4 cookie=0x6 total_len=64 in_port=1 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 (via action) data_len=64 (unbuffered)
-priority=0,udp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=8,tp_dst=11 udp_csum:2c37
+udp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=8,tp_dst=11 udp_csum:2c37
 dnl
 NXT_PACKET_IN (xid=0x0): table_id=5 cookie=0x7 total_len=64 in_port=1 tun_id=0x6 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x5 (via action) data_len=64 (unbuffered)
-priority=0,udp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=8,tp_dst=11 udp_csum:4439
+udp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=8,tp_dst=11 udp_csum:4439
 dnl
 NXT_PACKET_IN (xid=0x0): table_id=6 cookie=0x8 total_len=64 in_port=1 tun_id=0x6 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x5 (via action) data_len=64 (unbuffered)
-priority=0,udp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=85,tp_dst=11 udp_csum:43ec
+udp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=85,tp_dst=11 udp_csum:43ec
 dnl
 NXT_PACKET_IN (xid=0x0): table_id=7 cookie=0x9 total_len=64 in_port=1 tun_id=0x6 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x5 (via action) data_len=64 (unbuffered)
-priority=0,udp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=85,tp_dst=86 udp_csum:43a1
+udp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=85,tp_dst=86 udp_csum:43a1
 dnl
 NXT_PACKET_IN (xid=0x0): table_id=7 cookie=0x9 total_len=64 in_port=1 tun_id=0x6 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x5 (via action) data_len=64 (unbuffered)
-priority=0,udp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=85,tp_dst=86 udp_csum:43a1
+udp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=85,tp_dst=86 udp_csum:43a1
 ])
 
 AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
@@ -427,87 +426,87 @@ dnl Each of these specifies an in_port by number, a VLAN VID (or "none"),
 dnl a VLAN PCP (used if the VID isn't "none") and the expected set of datapath
 dnl actions.
 for tuple in \
-        "0 none 0 drop" \
-        "0 0    0 drop" \
-        "0 0    1 drop" \
-        "0 10   0 1,5,6,7,8,pop_vlan,2" \
-        "0 10   1 1,5,6,7,8,pop_vlan,2" \
-        "0 11   0 5,7" \
-        "0 11   1 5,7" \
-        "0 12   0 1,5,6,pop_vlan,3,4,7,8" \
-        "0 12   1 1,5,6,pop_vlan,4,7,push_vlan(vid=0,pcp=1),3,8" \
+        "100 none 0 drop" \
+        "100 0    0 drop" \
+        "100 0    1 drop" \
+        "100 10   0 1,5,6,7,8,pop_vlan,2" \
+        "100 10   1 1,5,6,7,8,pop_vlan,2" \
+        "100 11   0 5,7" \
+        "100 11   1 5,7" \
+        "100 12   0 1,5,6,pop_vlan,3,4,7,8" \
+        "100 12   1 1,5,6,pop_vlan,4,7,push_vlan(vid=0,pcp=1),3,8" \
         "1  none 0 drop" \
         "1  0    0 drop" \
         "1  0    1 drop" \
-        "1  10   0 0,5,6,7,8,pop_vlan,2" \
-        "1  10   1 0,5,6,7,8,pop_vlan,2" \
+        "1  10   0 5,6,7,8,100,pop_vlan,2" \
+        "1  10   1 5,6,7,8,100,pop_vlan,2" \
         "1  11   0 drop" \
         "1  11   1 drop" \
-        "1  12   0 0,5,6,pop_vlan,3,4,7,8" \
-        "1  12   1 0,5,6,pop_vlan,4,7,push_vlan(vid=0,pcp=1),3,8" \
-        "2  none 0 push_vlan(vid=10,pcp=0),0,1,5,6,7,8" \
-        "2  0    0 pop_vlan,push_vlan(vid=10,pcp=0),0,1,5,6,7,8" \
-        "2  0    1 pop_vlan,push_vlan(vid=10,pcp=1),0,1,5,6,7,8" \
+        "1  12   0 5,6,100,pop_vlan,3,4,7,8" \
+        "1  12   1 5,6,100,pop_vlan,4,7,push_vlan(vid=0,pcp=1),3,8" \
+        "2  none 0 push_vlan(vid=10,pcp=0),1,5,6,7,8,100" \
+        "2  0    0 pop_vlan,push_vlan(vid=10,pcp=0),1,5,6,7,8,100" \
+        "2  0    1 pop_vlan,push_vlan(vid=10,pcp=1),1,5,6,7,8,100" \
         "2  10   0 drop" \
         "2  10   1 drop" \
         "2  11   0 drop" \
         "2  11   1 drop" \
         "2  12   0 drop" \
         "2  12   1 drop" \
-        "3  none 0 4,7,8,push_vlan(vid=12,pcp=0),0,1,5,6" \
-        "3  0    0 pop_vlan,4,7,8,push_vlan(vid=12,pcp=0),0,1,5,6" \
-        "3  0    1 8,pop_vlan,4,7,push_vlan(vid=12,pcp=1),0,1,5,6" \
+        "3  none 0 4,7,8,push_vlan(vid=12,pcp=0),1,5,6,100" \
+        "3  0    0 pop_vlan,4,7,8,push_vlan(vid=12,pcp=0),1,5,6,100" \
+        "3  0    1 8,pop_vlan,4,7,push_vlan(vid=12,pcp=1),1,5,6,100" \
         "3  10   0 drop" \
         "3  10   1 drop" \
         "3  11   0 drop" \
         "3  11   1 drop" \
         "3  12   0 drop" \
         "3  12   1 drop" \
-        "4  none 0 3,7,8,push_vlan(vid=12,pcp=0),0,1,5,6" \
-        "4  0    0 pop_vlan,3,7,8,push_vlan(vid=12,pcp=0),0,1,5,6" \
-        "4  0    1 3,8,pop_vlan,7,push_vlan(vid=12,pcp=1),0,1,5,6" \
+        "4  none 0 3,7,8,push_vlan(vid=12,pcp=0),1,5,6,100" \
+        "4  0    0 pop_vlan,3,7,8,push_vlan(vid=12,pcp=0),1,5,6,100" \
+        "4  0    1 3,8,pop_vlan,7,push_vlan(vid=12,pcp=1),1,5,6,100" \
         "4  10   0 drop" \
         "4  10   1 drop" \
         "4  11   0 drop" \
         "4  11   1 drop" \
         "4  12   0 drop" \
         "4  12   1 drop" \
-        "5  none 0 2,push_vlan(vid=10,pcp=0),0,1,6,7,8" \
-        "5  0    0 pop_vlan,2,push_vlan(vid=10,pcp=0),0,1,6,7,8" \
-        "5  0    1 pop_vlan,2,push_vlan(vid=10,pcp=1),0,1,6,7,8" \
-        "5  10   0 0,1,6,7,8,pop_vlan,2" \
-        "5  10   1 0,1,6,7,8,pop_vlan,2" \
-        "5  11   0 0,7" \
-        "5  11   1 0,7" \
-        "5  12   0 0,1,6,pop_vlan,3,4,7,8" \
-        "5  12   1 0,1,6,pop_vlan,4,7,push_vlan(vid=0,pcp=1),3,8" \
-        "6  none 0 2,push_vlan(vid=10,pcp=0),0,1,5,7,8" \
-        "6  0    0 pop_vlan,2,push_vlan(vid=10,pcp=0),0,1,5,7,8" \
-        "6  0    1 pop_vlan,2,push_vlan(vid=10,pcp=1),0,1,5,7,8" \
-        "6  10   0 0,1,5,7,8,pop_vlan,2" \
-        "6  10   1 0,1,5,7,8,pop_vlan,2" \
+        "5  none 0 2,push_vlan(vid=10,pcp=0),1,6,7,8,100" \
+        "5  0    0 pop_vlan,2,push_vlan(vid=10,pcp=0),1,6,7,8,100" \
+        "5  0    1 pop_vlan,2,push_vlan(vid=10,pcp=1),1,6,7,8,100" \
+        "5  10   0 1,6,7,8,100,pop_vlan,2" \
+        "5  10   1 1,6,7,8,100,pop_vlan,2" \
+        "5  11   0 7,100" \
+        "5  11   1 7,100" \
+        "5  12   0 1,6,100,pop_vlan,3,4,7,8" \
+        "5  12   1 1,6,100,pop_vlan,4,7,push_vlan(vid=0,pcp=1),3,8" \
+        "6  none 0 2,push_vlan(vid=10,pcp=0),1,5,7,8,100" \
+        "6  0    0 pop_vlan,2,push_vlan(vid=10,pcp=0),1,5,7,8,100" \
+        "6  0    1 pop_vlan,2,push_vlan(vid=10,pcp=1),1,5,7,8,100" \
+        "6  10   0 1,5,7,8,100,pop_vlan,2" \
+        "6  10   1 1,5,7,8,100,pop_vlan,2" \
         "6  11   0 drop" \
         "6  11   1 drop" \
-        "6  12   0 0,1,5,pop_vlan,3,4,7,8" \
-        "6  12   1 0,1,5,pop_vlan,4,7,push_vlan(vid=0,pcp=1),3,8" \
-        "7  none 0 3,4,8,push_vlan(vid=12,pcp=0),0,1,5,6" \
-        "7  0    0 pop_vlan,3,4,8,push_vlan(vid=12,pcp=0),0,1,5,6" \
-        "7  0    1 3,8,pop_vlan,4,push_vlan(vid=12,pcp=1),0,1,5,6" \
-        "7  10   0 0,1,5,6,8,pop_vlan,2" \
-        "7  10   1 0,1,5,6,8,pop_vlan,2" \
-        "7  11   0 0,5" \
-        "7  11   1 0,5" \
-        "7  12   0 0,1,5,6,pop_vlan,3,4,8" \
-        "7  12   1 0,1,5,6,pop_vlan,4,push_vlan(vid=0,pcp=1),3,8" \
-        "8  none 0 3,4,7,push_vlan(vid=12,pcp=0),0,1,5,6" \
-        "8  0    0 pop_vlan,3,4,7,push_vlan(vid=12,pcp=0),0,1,5,6" \
-        "8  0    1 3,pop_vlan,4,7,push_vlan(vid=12,pcp=1),0,1,5,6" \
-        "8  10   0 0,1,5,6,7,pop_vlan,2" \
-        "8  10   1 0,1,5,6,7,pop_vlan,2" \
+        "6  12   0 1,5,100,pop_vlan,3,4,7,8" \
+        "6  12   1 1,5,100,pop_vlan,4,7,push_vlan(vid=0,pcp=1),3,8" \
+        "7  none 0 3,4,8,push_vlan(vid=12,pcp=0),1,5,6,100" \
+        "7  0    0 pop_vlan,3,4,8,push_vlan(vid=12,pcp=0),1,5,6,100" \
+        "7  0    1 3,8,pop_vlan,4,push_vlan(vid=12,pcp=1),1,5,6,100" \
+        "7  10   0 1,5,6,8,100,pop_vlan,2" \
+        "7  10   1 1,5,6,8,100,pop_vlan,2" \
+        "7  11   0 5,100" \
+        "7  11   1 5,100" \
+        "7  12   0 1,5,6,100,pop_vlan,3,4,8" \
+        "7  12   1 1,5,6,100,pop_vlan,4,push_vlan(vid=0,pcp=1),3,8" \
+        "8  none 0 3,4,7,push_vlan(vid=12,pcp=0),1,5,6,100" \
+        "8  0    0 pop_vlan,3,4,7,push_vlan(vid=12,pcp=0),1,5,6,100" \
+        "8  0    1 3,pop_vlan,4,7,push_vlan(vid=12,pcp=1),1,5,6,100" \
+        "8  10   0 1,5,6,7,100,pop_vlan,2" \
+        "8  10   1 1,5,6,7,100,pop_vlan,2" \
         "8  11   0 drop" \
         "8  11   1 drop" \
-        "8  12   0 0,1,5,6,pop_vlan,3,4,7" \
-        "8  12   1 0,1,5,6,pop_vlan,4,7,push_vlan(vid=0,pcp=1),3"
+        "8  12   0 1,5,6,100,pop_vlan,3,4,7" \
+        "8  12   1 1,5,6,100,pop_vlan,4,7,push_vlan(vid=0,pcp=1),3"
 do
   set $tuple
   in_port=$1
@@ -537,6 +536,7 @@ AT_CLEANUP
 
 AT_SETUP([ofproto-dpif - fragment handling])
 OVS_VSWITCHD_START
+ADD_OF_PORTS([br0], [1], [2], [3], [4], [5], [6])
 AT_DATA([flows.txt], [dnl
 priority=75 tcp ip_frag=no    tp_dst=80 actions=output:1
 priority=75 tcp ip_frag=first tp_dst=80 actions=output:2
@@ -577,6 +577,7 @@ AT_CLEANUP
 
 AT_SETUP([ofproto-dpif - exit])
 OVS_VSWITCHD_START
+ADD_OF_PORTS([br0], [1], [2], [3], [10], [11], [12], [13], [14])
 AT_DATA([flows.txt], [dnl
 in_port=1 actions=output:10,exit,output:11
 in_port=2 actions=output:12,resubmit:1,output:12
@@ -688,9 +689,9 @@ AT_CLEANUP
 
 AT_SETUP([ofproto-dpif - mirroring, select_dst])
 OVS_VSWITCHD_START(
-       [add-port br0 p1 -- set Interface p1 type=dummy --\
-        add-port br0 p2 -- set Interface p2 type=dummy --\
-        add-port br0 p3 -- set Interface p3 type=dummy --\
+       [add-port br0 p1 -- set Interface p1 type=dummy ofport_request=1 --\
+        add-port br0 p2 -- set Interface p2 type=dummy ofport_request=2 --\
+        add-port br0 p3 -- set Interface p3 type=dummy ofport_request=3 --\
         set Bridge br0 mirrors=@m --\
         --id=@p2 get Port p2 -- --id=@p3 get Port p3 --\
         --id=@m create Mirror name=mymirror \
@@ -808,7 +809,7 @@ flow="in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x080
 AT_CHECK([ovs-appctl ofproto/trace br0 "$flow"], [0], [stdout])
 actual=`tail -1 stdout | sed 's/Datapath actions: //'`
 
-expected="2,push_vlan(vid=12,pcp=0),0,1,2"
+expected="2,push_vlan(vid=12,pcp=0),1,2,100"
 AT_CHECK([ovs-dpctl normalize-actions "$flow" "$expected"], [0], [stdout])
 mv stdout expout
 AT_CHECK([ovs-dpctl normalize-actions "$flow" "$actual"], [0], [expout])
@@ -817,7 +818,7 @@ flow="in_port(2),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x080
 AT_CHECK([ovs-appctl ofproto/trace br0 "$flow"], [0], [stdout])
 actual=`tail -1 stdout | sed 's/Datapath actions: //'`
 
-expected="push_vlan(vid=17,pcp=0),1,pop_vlan,push_vlan(vid=12,pcp=0),0,1,2"
+expected="push_vlan(vid=17,pcp=0),1,pop_vlan,push_vlan(vid=12,pcp=0),1,2,100"
 AT_CHECK([ovs-dpctl normalize-actions "$flow" "$expected"], [0], [stdout])
 mv stdout expout
 AT_CHECK([ovs-dpctl normalize-actions "$flow" "$actual"], [0], [expout])
@@ -850,7 +851,7 @@ OFPROTO_TRACE(
   [br0],
   [in_port(3),eth(src=50:54:00:00:00:05,dst=ff:ff:ff:ff:ff:ff),$arp],
   [-generate],
-  [0,1,2])
+  [1,2,100])
 
 # Check for the MAC learning entry.
 AT_CHECK_UNQUOTED([ovs-appctl fdb/show br0 | sed 's/[[0-9]]\{1,\}$/?/'], [0], [dnl
@@ -878,7 +879,7 @@ OFPROTO_TRACE(
   [br0],
   [in_port(2),eth(src=50:54:00:00:00:05,dst=ff:ff:ff:ff:ff:ff),$arp],
   [-generate],
-  [0,1,3])
+  [1,3,100])
 
 # Check that the MAC learning entry was updated.
 AT_CHECK_UNQUOTED([ovs-appctl fdb/show br0 | sed 's/[[0-9]]\{1,\}$/?/'], [0], [dnl
@@ -900,12 +901,12 @@ OFPROTO_TRACE(
   [br1],
   [in_port(4),eth(src=50:54:00:00:00:06,dst=ff:ff:ff:ff:ff:ff),$arp],
   [-generate],
-  [0,5])
+  [5,101])
 OFPROTO_TRACE(
   [br1],
   [in_port(5),eth(src=50:54:00:00:00:07,dst=ff:ff:ff:ff:ff:ff),$arp],
   [-generate],
-  [0,4])
+  [4,101])
 
 # Check that the MAC learning entries were added.
 AT_CHECK_UNQUOTED([ovs-appctl fdb/show br1 | sed 's/[[0-9]]\{1,\}$/?/'], [0], [dnl
@@ -929,6 +930,68 @@ AT_CHECK_UNQUOTED([ovs-appctl fdb/show br1 | sed 's/[[0-9]]\{1,\}$/?/'], [0], [d
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
+AT_SETUP([ofproto-dpif - MAC table overflow])
+OVS_VSWITCHD_START(
+  [set bridge br0 fail-mode=standalone other-config:mac-table-size=10 -- \
+   add-port br0 p1 -- set Interface p1 type=dummy -- \
+   add-port br0 p2 -- set Interface p2 type=dummy -- \
+   add-port br0 p3 -- set Interface p3 type=dummy])
+
+arp='eth_type(0x0806),arp(sip=192.168.0.1,tip=192.168.0.2,op=1,sha=50:54:00:00:00:05,tha=00:00:00:00:00:00)'
+
+AT_CHECK([ovs-appctl time/stop])
+
+# Trace 10 ARP packets arriving on p3, to create MAC learning entries.
+for i in 0 1 2 3 4 5 6 7 8 9; do
+    OFPROTO_TRACE(
+      [br0],
+      [in_port(3),eth(src=50:54:00:00:00:0$i,dst=ff:ff:ff:ff:ff:ff),$arp],
+      [-generate],
+      [1,2,100])
+    ovs-appctl time/warp 1000
+done
+
+# Check for the MAC learning entries.
+AT_CHECK_UNQUOTED([ovs-appctl fdb/show br0 | sed 's/ *[[0-9]]\{1,\}$//' | sort],
+  [0], [dnl
+    3     0  50:54:00:00:00:00
+    3     0  50:54:00:00:00:01
+    3     0  50:54:00:00:00:02
+    3     0  50:54:00:00:00:03
+    3     0  50:54:00:00:00:04
+    3     0  50:54:00:00:00:05
+    3     0  50:54:00:00:00:06
+    3     0  50:54:00:00:00:07
+    3     0  50:54:00:00:00:08
+    3     0  50:54:00:00:00:09
+ port  VLAN  MAC                Age
+])
+
+# Trace another ARP packet on another MAC.
+OFPROTO_TRACE(
+  [br0],
+  [in_port(3),eth(src=50:54:00:00:00:10,dst=ff:ff:ff:ff:ff:ff),$arp],
+  [-generate],
+  [1,2,100])
+
+# Check that the new one chased the oldest one out of the table.
+AT_CHECK_UNQUOTED([ovs-appctl fdb/show br0 | sed 's/[[0-9]]\{1,\}$/?/' | sort],
+  [0], [dnl
+    3     0  50:54:00:00:00:01    ?
+    3     0  50:54:00:00:00:02    ?
+    3     0  50:54:00:00:00:03    ?
+    3     0  50:54:00:00:00:04    ?
+    3     0  50:54:00:00:00:05    ?
+    3     0  50:54:00:00:00:06    ?
+    3     0  50:54:00:00:00:07    ?
+    3     0  50:54:00:00:00:08    ?
+    3     0  50:54:00:00:00:09    ?
+    3     0  50:54:00:00:00:10    ?
+ port  VLAN  MAC                Age
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
 dnl Test that basic NetFlow reports flow statistics correctly:
 dnl - The initial packet of a flow are correctly accounted.
 dnl - Later packets within a flow are correctly accounted.
@@ -1189,3 +1252,103 @@ AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip], [0],
 ])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
+
+AT_SETUP([ofproto-dpif - ovs-appctl dpif/dump-dps])
+OVS_VSWITCHD_START([add-br br1 -- set bridge br1 datapath-type=dummy])
+ADD_OF_PORTS([br0], [1], [2])
+ADD_OF_PORTS([br1], [3])
+
+AT_CHECK([ovs-appctl dpif/dump-dps], [0], [dnl
+dummy@br0
+dummy@br1
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto-dpif - ovs-appctl dpif/show])
+OVS_VSWITCHD_START([add-br br1 -- set bridge br1 datapath-type=dummy])
+ADD_OF_PORTS([br0], [1], [2])
+ADD_OF_PORTS([br1], [3])
+
+AT_CHECK([ovs-appctl dpif/show], [0], [dnl
+br0 (dummy@ovs-dummy):
+       lookups: hit:0 missed:0 lost:0
+       flows: 0
+       br0 65534/100: (dummy)
+       p1 1/1: (dummy)
+       p2 2/2: (dummy)
+br1 (dummy@ovs-dummy):
+       lookups: hit:0 missed:0 lost:0
+       flows: 0
+       br1 65534/101: (dummy)
+       p3 3/3: (dummy)
+])
+
+AT_CHECK([ovs-appctl dpif/show br0], [0], [dnl
+br0 (dummy@ovs-dummy):
+       lookups: hit:0 missed:0 lost:0
+       flows: 0
+       br0 65534/100: (dummy)
+       p1 1/1: (dummy)
+       p2 2/2: (dummy)
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto-dpif - ovs-appctl dpif/dump-flows])
+OVS_VSWITCHD_START([add-br br1 -- \
+                    set bridge br1 datapath-type=dummy fail-mode=secure])
+ADD_OF_PORTS([br0], [1], [2])
+ADD_OF_PORTS([br1], [3])
+
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'], [0], [success
+])
+AT_CHECK([ovs-appctl netdev-dummy/receive p2 'in_port(2),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=0,code=0)'], [0], [success
+])
+AT_CHECK([ovs-appctl netdev-dummy/receive p3 'in_port(3),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'], [0], [success
+])
+
+AT_CHECK([ovs-appctl dpif/dump-flows br0 | sort | STRIP_USED], [0], [dnl
+in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0), packets:0, bytes:0, used:0.0s, actions:drop
+in_port(2),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=0,code=0), packets:0, bytes:0, used:0.0s, actions:drop
+])
+
+AT_CHECK([ovs-appctl dpif/dump-flows br1 | sort | STRIP_USED], [0], [dnl
+in_port(3),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0), packets:0, bytes:0, used:0.0s, actions:drop
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto-dpif - ovs-appctl dpif/del-flows])
+OVS_VSWITCHD_START([add-br br1 -- \
+                    set bridge br1 datapath-type=dummy fail-mode=secure])
+ADD_OF_PORTS([br0], [1], [2])
+ADD_OF_PORTS([br1], [3])
+
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'], [0], [success
+])
+AT_CHECK([ovs-appctl netdev-dummy/receive p2 'in_port(2),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=0,code=0)'], [0], [success
+])
+AT_CHECK([ovs-appctl netdev-dummy/receive p3 'in_port(3),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'], [0], [success
+])
+
+AT_CHECK([ovs-appctl dpif/dump-flows br0 | sort | STRIP_USED], [0], [dnl
+in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0), packets:0, bytes:0, used:0.0s, actions:drop
+in_port(2),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=0,code=0), packets:0, bytes:0, used:0.0s, actions:drop
+])
+
+AT_CHECK([ovs-appctl dpif/dump-flows br1 | sort | STRIP_USED], [0], [dnl
+in_port(3),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0), packets:0, bytes:0, used:0.0s, actions:drop
+])
+
+AT_CHECK([ovs-appctl dpif/del-flows br0])
+AT_CHECK([ovs-appctl dpif/dump-flows br0 | sort | STRIP_USED], [0], [dnl
+])
+
+AT_CHECK([ovs-appctl dpif/dump-flows br1 | sort | STRIP_USED], [0], [dnl
+in_port(3),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0), packets:0, bytes:0, used:0.0s, actions:drop
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
index 52f19fc..b9356d3 100644 (file)
@@ -18,6 +18,7 @@ m4_divert_pop([PREPARE_TESTS])
 
 m4_define([STRIP_XIDS], [[sed 's/ (xid=0x[0-9a-fA-F]*)//']])
 m4_define([STRIP_DURATION], [[sed 's/\bduration=[0-9.]*s/duration=?s/']])
+m4_define([STRIP_USED], [[sed 's/used:[0-9]\.[0-9]*/used:0.0/']])
 m4_define([TESTABLE_LOG], [-vPATTERN:ANY:'%c|%p|%m'])
 
 # OVS_VSWITCHD_START([vsctl-args], [vsctl-output])
@@ -60,9 +61,20 @@ m4_define([OVS_VSWITCHD_START],
 /ofproto|INFO|datapath ID changed to fedcba9876543210/d']])
 
    dnl Add bridges, ports, etc.
-   AT_CHECK([ovs-vsctl -- add-br br0 -- set bridge br0 datapath-type=dummy other-config:datapath-id=fedcba9876543210 other-config:hwaddr=aa:55:aa:55:00:00 fail-mode=secure -- $1 m4_if([$2], [], [], [| perl $srcdir/uuidfilt.pl])], [0], [$2])
+   AT_CHECK([ovs-vsctl -- add-br br0 -- set bridge br0 datapath-type=dummy other-config:datapath-id=fedcba9876543210 other-config:hwaddr=aa:55:aa:55:00:00 protocols=[[OpenFlow10,OpenFlow12,OpenFlow13]] fail-mode=secure -- $1 m4_if([$2], [], [], [| perl $srcdir/uuidfilt.pl])], [0], [$2])
 ])
 
 m4_define([OVS_VSWITCHD_STOP],
   [AT_CHECK([ovs-appctl -t ovs-vswitchd exit])
    AT_CHECK([ovs-appctl -t ovsdb-server exit])])
+
+# ADD_OF_PORTS(BRIDGE, OF_PORT[, OF_PORT...])
+#
+# Creates a dummy interface with an OpenFlow port number of OF_PORT and
+# name of p{OF_PORT}.  The dummy implementation will treat the OF_PORT
+# as the datapath port number, which as the effect of making the
+# OpenFlow and datapath numbers the same.
+m4_define([ADD_OF_PORTS],
+ [ovs-vsctl m4_foreach([of_port], m4_cdr($@),
+    [ \
+    -- add-port $1 p[]of_port -- set Interface p[]of_port type=dummy ofport_request=of_port])])
index cbf07bc..45aa549 100644 (file)
@@ -11,26 +11,68 @@ OVS_VSWITCHD_START
 AT_CHECK([ovs-ofctl -vwarn show br0], [0], [stdout])
 AT_CHECK([STRIP_XIDS stdout], [0], [dnl
 OFPT_FEATURES_REPLY: dpid:fedcba9876543210
-n_tables:255, n_buffers:256
+n_tables:254, n_buffers:256
 capabilities: FLOW_STATS TABLE_STATS PORT_STATS QUEUE_STATS ARP_MATCH_IP
 actions: OUTPUT SET_VLAN_VID SET_VLAN_PCP STRIP_VLAN SET_DL_SRC SET_DL_DST SET_NW_SRC SET_NW_DST SET_NW_TOS SET_TP_SRC SET_TP_DST ENQUEUE
  LOCAL(br0): addr:aa:55:aa:55:00:00
+     config:     0
+     state:      0
+     speed: 0 Mbps now, 0 Mbps max
+OFPT_GET_CONFIG_REPLY: frags=normal miss_send_len=0
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto - set OpenFlow port number])
+OVS_VSWITCHD_START(
+       [add-port br0 p1 -- set Interface p1 type=dummy --\
+        add-port br0 p2 -- set Interface p2 type=dummy ofport_request=99])
+AT_CHECK([ovs-ofctl -vwarn show br0], [0], [stdout])
+AT_CHECK([[sed '
+s/ (xid=0x[0-9a-fA-F]*)//
+s/00:0.$/00:0x/' < stdout]],
+      [0], [dnl
+OFPT_FEATURES_REPLY: dpid:fedcba9876543210
+n_tables:254, n_buffers:256
+capabilities: FLOW_STATS TABLE_STATS PORT_STATS QUEUE_STATS ARP_MATCH_IP
+actions: OUTPUT SET_VLAN_VID SET_VLAN_PCP STRIP_VLAN SET_DL_SRC SET_DL_DST SET_NW_SRC SET_NW_DST SET_NW_TOS SET_TP_SRC SET_TP_DST ENQUEUE
+ 1(p1): addr:aa:55:aa:55:00:0x
+     config:     PORT_DOWN
+     state:      LINK_DOWN
+     speed: 0 Mbps now, 0 Mbps max
+ 99(p2): addr:aa:55:aa:55:00:0x
      config:     PORT_DOWN
      state:      LINK_DOWN
-     speed: 100 Mbps now, 100 Mbps max
+     speed: 0 Mbps now, 0 Mbps max
+ LOCAL(br0): addr:aa:55:aa:55:00:0x
+     config:     0
+     state:      0
+     speed: 0 Mbps now, 0 Mbps max
 OFPT_GET_CONFIG_REPLY: frags=normal miss_send_len=0
 ])
+
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
 dnl This is really bare-bones.
 dnl It at least checks request and reply serialization and deserialization.
-AT_SETUP([ofproto - port stats])
+AT_SETUP([ofproto - port stats - (OpenFlow 1.0)])
 OVS_VSWITCHD_START
 AT_CHECK([ovs-ofctl -vwarn dump-ports br0], [0], [stdout])
 AT_CHECK([STRIP_XIDS stdout], [0], [dnl
 OFPST_PORT reply: 1 ports
-  port 65534: rx pkts=0, bytes=0, drop=0, errs=0, frame=0, over=0, crc=0
+  port LOCAL: rx pkts=0, bytes=0, drop=0, errs=0, frame=0, over=0, crc=0
+           tx pkts=0, bytes=0, drop=0, errs=0, coll=0
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto - port stats - (OpenFlow 1.2)])
+OVS_VSWITCHD_START
+AT_CHECK([ovs-ofctl -O OpenFlow12 -vwarn dump-ports br0], [0], [stdout])
+AT_CHECK([STRIP_XIDS stdout], [0], [dnl
+OFPST_PORT reply (OF1.2): 1 ports
+  port LOCAL: rx pkts=0, bytes=0, drop=0, errs=0, frame=0, over=0, crc=0
            tx pkts=0, bytes=0, drop=0, errs=0, coll=0
 ])
 OVS_VSWITCHD_STOP
@@ -38,30 +80,45 @@ AT_CLEANUP
 
 dnl This is really bare-bones.
 dnl It at least checks request and reply serialization and deserialization.
-AT_SETUP([ofproto - port-desc stats])
+AT_SETUP([ofproto - port-desc stats (OpenFlow 1.0)])
 OVS_VSWITCHD_START
 AT_CHECK([ovs-ofctl -vwarn dump-ports-desc br0], [0], [stdout])
 AT_CHECK([STRIP_XIDS stdout], [0], [dnl
 OFPST_PORT_DESC reply:
  LOCAL(br0): addr:aa:55:aa:55:00:00
-     config:     PORT_DOWN
-     state:      LINK_DOWN
-     speed: 100 Mbps now, 100 Mbps max
+     config:     0
+     state:      0
+     speed: 0 Mbps now, 0 Mbps max
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+dnl This is really bare-bones.
+dnl It at least checks request and reply serialization and deserialization.
+AT_SETUP([ofproto - port-desc stats (OpenFlow 1.2)])
+OVS_VSWITCHD_START
+AT_CHECK([ovs-ofctl -O OpenFlow12 -vwarn dump-ports-desc br0], [0], [stdout])
+AT_CHECK([STRIP_XIDS stdout], [0], [dnl
+OFPST_PORT_DESC reply (OF1.2):
+ LOCAL(br0): addr:aa:55:aa:55:00:00
+     config:     0
+     state:      0
+     speed: 0 Mbps now, 0 Mbps max
 ])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
 dnl This is really bare-bones.
 dnl It at least checks request and reply serialization and deserialization.
-AT_SETUP([ofproto - queue stats])
+AT_SETUP([ofproto - queue stats - (OpenFlow 1.0)])
 OVS_VSWITCHD_START
 AT_CHECK([ovs-ofctl -vwarn queue-stats br0], [0], [stdout])
 AT_CHECK([STRIP_XIDS stdout], [0], [dnl
 OFPST_QUEUE reply: 0 queues
 ])
-AT_CHECK([ovs-ofctl -vwarn queue-stats br0 ALL 5], [0],
+AT_CHECK([ovs-ofctl -vwarn queue-stats br0 ANY 5], [0],
   [OFPT_ERROR (xid=0x2): OFPQOFC_BAD_QUEUE
-OFPST_QUEUE request (xid=0x2):port=ALL queue=5
+OFPST_QUEUE request (xid=0x2):port=ANY queue=5
 ])
 AT_CHECK([ovs-ofctl -vwarn queue-stats br0 10], [0],
   [OFPT_ERROR (xid=0x2): OFPQOFC_BAD_PORT
@@ -70,7 +127,24 @@ OFPST_QUEUE request (xid=0x2):port=10 queue=ALL
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
-AT_SETUP([ofproto - mod-port])
+AT_SETUP([ofproto - queue stats - (OpenFlow 1.2)])
+OVS_VSWITCHD_START
+AT_CHECK([ovs-ofctl -O OpenFlow12 -vwarn queue-stats br0], [0], [stdout])
+AT_CHECK([STRIP_XIDS stdout], [0], [dnl
+OFPST_QUEUE reply (OF1.2): 0 queues
+])
+AT_CHECK([ovs-ofctl -O OpenFlow12 -vwarn queue-stats br0 ALL 5], [0],
+  [OFPT_ERROR (OF1.2) (xid=0x2): OFPQOFC_BAD_QUEUE
+OFPST_QUEUE request (OF1.2) (xid=0x2):port=ANY queue=5
+])
+AT_CHECK([ovs-ofctl -O OpenFlow12 -vwarn queue-stats br0 10], [0],
+  [OFPT_ERROR (OF1.2) (xid=0x2): OFPQOFC_BAD_PORT
+OFPST_QUEUE request (OF1.2) (xid=0x2):port=10 queue=ALL
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto - mod-port (OpenFlow 1.0)])
 OVS_VSWITCHD_START
 for command_config_state in \
     'up 0 0' \
@@ -91,19 +165,50 @@ do
     AT_CHECK([ovs-ofctl -vwarn show br0], [0], [stdout])
     AT_CHECK_UNQUOTED([STRIP_XIDS stdout], [0], [dnl
 OFPT_FEATURES_REPLY: dpid:fedcba9876543210
-n_tables:255, n_buffers:256
+n_tables:254, n_buffers:256
 capabilities: FLOW_STATS TABLE_STATS PORT_STATS QUEUE_STATS ARP_MATCH_IP
 actions: OUTPUT SET_VLAN_VID SET_VLAN_PCP STRIP_VLAN SET_DL_SRC SET_DL_DST SET_NW_SRC SET_NW_DST SET_NW_TOS SET_TP_SRC SET_TP_DST ENQUEUE
  LOCAL(br0): addr:aa:55:aa:55:00:00
      config:     $config
      state:      $state
-     speed: 100 Mbps now, 100 Mbps max
+     speed: 0 Mbps now, 0 Mbps max
 OFPT_GET_CONFIG_REPLY: frags=normal miss_send_len=0
 ])
 done
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
+AT_SETUP([ofproto - mod-port (OpenFlow 1.2)])
+OVS_VSWITCHD_START
+for command_config_state in \
+    'up 0 0' \
+    'down PORT_DOWN LINK_DOWN' \
+    'no-receive PORT_DOWN,NO_RECV LINK_DOWN' \
+    'no-forward PORT_DOWN,NO_RECV,NO_FWD LINK_DOWN' \
+    'no-packet-in PORT_DOWN,NO_RECV,NO_FWD,NO_PACKET_IN LINK_DOWN' \
+    'forward PORT_DOWN,NO_RECV,NO_PACKET_IN LINK_DOWN' \
+    'packet-in PORT_DOWN,NO_RECV LINK_DOWN' \
+    'up NO_RECV 0' \
+    'receive 0 0'
+do
+    set $command_config_state
+    command=$[1] config=`echo $[2] | sed 's/,/ /g'` state=$[3]
+    AT_CHECK([ovs-ofctl -O OpenFlow12 -vwarn mod-port br0 br0 $command])
+    AT_CHECK([ovs-ofctl -O OpenFlow12 -vwarn show br0], [0], [stdout])
+    AT_CHECK_UNQUOTED([STRIP_XIDS stdout], [0], [dnl
+OFPT_FEATURES_REPLY (OF1.2): dpid:fedcba9876543210
+n_tables:254, n_buffers:256
+capabilities: FLOW_STATS TABLE_STATS PORT_STATS QUEUE_STATS
+ LOCAL(br0): addr:aa:55:aa:55:00:00
+     config:     $config
+     state:      $state
+     speed: 0 Mbps now, 0 Mbps max
+OFPT_GET_CONFIG_REPLY (OF1.2): frags=normal miss_send_len=0
+])
+done
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
 AT_SETUP([ofproto - basic flow_mod commands (NXM)])
 OVS_VSWITCHD_START
 AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip], [0], [NXST_FLOW reply:
@@ -159,31 +264,26 @@ NXST_FLOW reply:
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
-AT_SETUP([ofproto - dump flows with cookie])
+AT_SETUP([ofproto - basic flow_mod commands (OpenFlow 1.2)])
 OVS_VSWITCHD_START
-AT_CHECK([ovs-ofctl add-flow br0 cookie=0x1,in_port=1,actions=1])
-AT_CHECK([ovs-ofctl add-flow br0 cookie=0x2,in_port=2,actions=1])
-AT_CHECK([ovs-ofctl add-flow br0 cookie=0x3,in_port=3,actions=1])
-AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
- cookie=0x1, in_port=1 actions=output:1
- cookie=0x2, in_port=2 actions=output:1
- cookie=0x3, in_port=3 actions=output:1
-NXST_FLOW reply:
+AT_CHECK([ovs-ofctl -O OpenFlow12 dump-flows br0 | ofctl_strip], [0], [OFPST_FLOW reply (OF1.2):
 ])
-AT_CHECK([ovs-ofctl dump-aggregate br0 table=0 | STRIP_XIDS], [0], [dnl
-NXST_AGGREGATE reply: packet_count=0 byte_count=0 flow_count=3
-])
-AT_CHECK([ovs-ofctl dump-flows br0 cookie=0x3/-1 | ofctl_strip | sort], [0], [dnl
- cookie=0x3, in_port=3 actions=output:1
-NXST_FLOW reply:
+AT_CHECK([echo 'in_port=2,actions=1' | ovs-ofctl -O OpenFlow12 add-flows br0 -])
+AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 in_port=1,actions=2])
+AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 table=1,in_port=4,actions=3])
+AT_CHECK([ovs-ofctl -O OpenFlow12 dump-flows br0 | ofctl_strip | sort], [0], [dnl
+ in_port=1 actions=output:2
+ in_port=2 actions=output:1
+ table=1, in_port=4 actions=output:3
+OFPST_FLOW reply (OF1.2):
 ])
-AT_CHECK([ovs-ofctl dump-aggregate br0 cookie=0x3/-1 | STRIP_XIDS], [0], [dnl
-NXST_AGGREGATE reply: packet_count=0 byte_count=0 flow_count=1
+AT_CHECK([ovs-ofctl -O OpenFlow12 del-flows br0])
+AT_CHECK([ovs-ofctl -O OpenFlow12 dump-flows br0 | ofctl_strip], [0], [OFPST_FLOW reply (OF1.2):
 ])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
-AT_SETUP([ofproto - dump flows with cookie mask])
+AT_SETUP([ofproto - dump flows with cookie])
 OVS_VSWITCHD_START
 AT_CHECK([ovs-ofctl add-flow br0 cookie=0x1,in_port=1,actions=1])
 AT_CHECK([ovs-ofctl add-flow br0 cookie=0x2,in_port=2,actions=1])
@@ -197,13 +297,12 @@ NXST_FLOW reply:
 AT_CHECK([ovs-ofctl dump-aggregate br0 table=0 | STRIP_XIDS], [0], [dnl
 NXST_AGGREGATE reply: packet_count=0 byte_count=0 flow_count=3
 ])
-AT_CHECK([ovs-ofctl dump-flows br0 cookie=0x3/0x1 | ofctl_strip | sort], [0], [dnl
- cookie=0x1, in_port=1 actions=output:1
+AT_CHECK([ovs-ofctl dump-flows br0 cookie=0x3/-1 | ofctl_strip | sort], [0], [dnl
  cookie=0x3, in_port=3 actions=output:1
 NXST_FLOW reply:
 ])
-AT_CHECK([ovs-ofctl dump-aggregate br0 cookie=0x3/0x1 | STRIP_XIDS], [0], [dnl
-NXST_AGGREGATE reply: packet_count=0 byte_count=0 flow_count=2
+AT_CHECK([ovs-ofctl dump-aggregate br0 cookie=0x3/-1 | STRIP_XIDS], [0], [dnl
+NXST_AGGREGATE reply: packet_count=0 byte_count=0 flow_count=1
 ])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
@@ -240,7 +339,24 @@ NXST_FLOW reply:
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
-AT_SETUP([ofproto - mod flows based on cookie mask])
+dnl The OpenFlow 1.2 spec states that the cookie may not be modified
+AT_SETUP([ofproto - no mod flow with cookie change (OpenFlow1.2)])
+OVS_VSWITCHD_START
+AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 cookie=0x1,in_port=1,actions=1])
+AT_CHECK([ovs-ofctl -O OpenFlow12 dump-flows br0 | ofctl_strip | sort], [0], [dnl
+ cookie=0x1, in_port=1 actions=output:1
+OFPST_FLOW reply (OF1.2):
+])
+
+AT_CHECK([ovs-ofctl -O OpenFlow12 mod-flows br0 cookie=0x2,in_port=1,actions=1])
+AT_CHECK([ovs-ofctl -O OpenFlow12 dump-flows br0 | ofctl_strip | sort], [0], [dnl
+ cookie=0x1, in_port=1 actions=output:1
+OFPST_FLOW reply (OF1.2):
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto - mod flows based on cookie mask (OpenFlow 1.0)])
 OVS_VSWITCHD_START
 AT_CHECK([ovs-ofctl add-flow br0 cookie=0x1,in_port=1,actions=1])
 AT_CHECK([ovs-ofctl add-flow br0 cookie=0x1,in_port=2,actions=1])
@@ -262,6 +378,29 @@ NXST_FLOW reply:
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
+AT_SETUP([ofproto - mod flows based on cookie mask (OpenFlow 1.2)])
+OVS_VSWITCHD_START
+AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 cookie=0x1,in_port=1,actions=1])
+AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 cookie=0x1,in_port=2,actions=1])
+AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 cookie=0x2,in_port=3,actions=1])
+AT_CHECK([ovs-ofctl -O OpenFlow12 dump-flows br0 | ofctl_strip | sort], [0], [dnl
+ cookie=0x1, in_port=1 actions=output:1
+ cookie=0x1, in_port=2 actions=output:1
+ cookie=0x2, in_port=3 actions=output:1
+OFPST_FLOW reply (OF1.2):
+])
+
+AT_CHECK([ovs-ofctl -O OpenFlow12 mod-flows br0 cookie=0x1/0xff,actions=4])
+AT_CHECK([ovs-ofctl -O OpenFlow12 dump-flows br0 | ofctl_strip | sort], [0], [dnl
+ cookie=0x1, in_port=1 actions=output:4
+ cookie=0x1, in_port=2 actions=output:4
+ cookie=0x2, in_port=3 actions=output:1
+OFPST_FLOW reply (OF1.2):
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+dnl The OpenFlow 1.2 spec states that the cookie may not be modified
 AT_SETUP([ofproto - mod flows based on cookie mask with cookie change])
 OVS_VSWITCHD_START
 AT_CHECK([ovs-ofctl add-flow br0 cookie=0x1,in_port=1,actions=1])
@@ -362,7 +501,7 @@ NXST_FLOW reply:
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
-AT_SETUP([ofproto - del flows based on table id])
+AT_SETUP([ofproto - del flows based on table id (NXM)])
 OVS_VSWITCHD_START
 AT_CHECK([ovs-ofctl add-flow br0 cookie=0x1,in_port=1,actions=1])
 AT_CHECK([ovs-ofctl add-flow br0 cookie=0x2,in_port=2,table=1,actions=1])
@@ -394,10 +533,42 @@ NXST_FLOW reply:
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
-AT_SETUP([ofproto - flow table configuration])
+AT_SETUP([ofproto - del flows based on table id (OpenFlow 1.2)])
+OVS_VSWITCHD_START
+AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 cookie=0x1,in_port=1,actions=1])
+AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 cookie=0x2,in_port=2,table=1,actions=1])
+AT_CHECK([ovs-ofctl -O OpenFlow12 dump-flows br0 | ofctl_strip | sort], [0], [dnl
+ cookie=0x1, in_port=1 actions=output:1
+ cookie=0x2, table=1, in_port=2 actions=output:1
+OFPST_FLOW reply (OF1.2):
+])
+AT_CHECK([ovs-ofctl -O OpenFlow12 del-flows br0 table=0])
+AT_CHECK([ovs-ofctl -O OpenFlow12 dump-flows br0 | ofctl_strip | sort], [0], [dnl
+ cookie=0x2, table=1, in_port=2 actions=output:1
+OFPST_FLOW reply (OF1.2):
+])
+AT_CHECK([ovs-ofctl -O OpenFlow12 del-flows br0 table=1])
+AT_CHECK([ovs-ofctl -O OpenFlow12 dump-flows br0 | ofctl_strip | sort], [0], [dnl
+OFPST_FLOW reply (OF1.2):
+])
+AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 cookie=0x1,in_port=1,actions=1])
+AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 cookie=0x2,in_port=2,table=1,actions=1])
+AT_CHECK([ovs-ofctl -O OpenFlow12 dump-flows br0 | ofctl_strip | sort], [0], [dnl
+ cookie=0x1, in_port=1 actions=output:1
+ cookie=0x2, table=1, in_port=2 actions=output:1
+OFPST_FLOW reply (OF1.2):
+])
+AT_CHECK([ovs-ofctl -O OpenFlow12 del-flows br0])
+AT_CHECK([ovs-ofctl -O OpenFlow12 dump-flows br0 | ofctl_strip | sort], [0], [dnl
+OFPST_FLOW reply (OF1.2):
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto - flow table configuration (OpenFlow 1.0)])
 OVS_VSWITCHD_START
 # Check the default configuration.
-(echo "OFPST_TABLE reply (xid=0x2): 255 tables
+(echo "OFPST_TABLE reply (xid=0x2): 254 tables
   0: classifier: wild=0x3fffff, max=1000000, active=0
                lookup=0, matched=0"
  x=1
@@ -406,9 +577,7 @@ OVS_VSWITCHD_START
                lookup=0, matched=0
 " $x table$x
    x=`expr $x + 1`
- done
- echo "  254: table254: wild=0x3fffff, max=1000000, active=2
-               lookup=0, matched=0") > expout
+ done) > expout
 AT_CHECK([ovs-ofctl dump-tables br0], [0], [expout])
 # Change the configuration.
 AT_CHECK(
@@ -422,7 +591,7 @@ AT_CHECK(
 ])
 # Check that the configuration was updated.
 mv expout orig-expout
-(echo "OFPST_TABLE reply (xid=0x2): 255 tables
+(echo "OFPST_TABLE reply (xid=0x2): 254 tables
   0: main    : wild=0x3fffff, max=1000000, active=0
                lookup=0, matched=0
   1: table1  : wild=0x3fffff, max=  1024, active=0
@@ -432,7 +601,49 @@ AT_CHECK([ovs-ofctl dump-tables br0], [0], [expout])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
-AT_SETUP([ofproto - hard limits on flow table size])
+AT_SETUP([ofproto - flow table configuration (OpenFlow 1.2)])
+OVS_VSWITCHD_START
+# Check the default configuration.
+(mid="wild=0xffffffffff, max=1000000,"
+ tail="
+               lookup=0, matched=0
+               match=0xffffffffff, instructions=0x00000007, config=0x00000003
+               write_actions=0x00000000, apply_actions=0x00000000
+               write_setfields=0x000000ffffffffff
+               apply_setfields=0x000000ffffffffff
+               metadata_match=0xffffffffffffffff
+               metadata_write=0xffffffffffffffff"
+ echo "OFPST_TABLE reply (OF1.2) (xid=0x2): 254 tables
+  0: classifier: $mid active=0$tail"
+ x=1
+ while test $x -lt 254; do
+   printf "  %d: %-8s: $mid active=0$tail
+" $x table$x
+   x=`expr $x + 1`
+ done) > expout
+AT_CHECK([ovs-ofctl -O OpenFlow12 dump-tables br0], [0], [expout])
+# Change the configuration.
+AT_CHECK(
+  [ovs-vsctl \
+     -- --id=@t0 create Flow_Table name=main \
+     -- --id=@t1 create Flow_Table flow-limit=1024 \
+     -- set bridge br0 'flow_tables={1=@t1,0=@t0}' \
+   | perl $srcdir/uuidfilt.pl],
+  [0], [<0>
+<1>
+])
+# Check that the configuration was updated.
+mv expout orig-expout
+(echo "OFPST_TABLE reply (OF1.2) (xid=0x2): 254 tables
+  0: main    : wild=0xffffffffff, max=1000000, active=0"
+ tail -n +3 orig-expout | head -7
+ echo "  1: table1  : wild=0xffffffffff, max=  1024, active=0"
+ tail -n +11 orig-expout) > expout
+AT_CHECK([ovs-ofctl -O OpenFlow12 dump-tables br0], [0], [expout])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto - hard limits on flow table size (OpenFLow 1.0)])
 OVS_VSWITCHD_START
 # Configure a maximum of 4 flows.
 AT_CHECK(
@@ -476,7 +687,46 @@ NXST_FLOW reply:
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
-AT_SETUP([ofproto - eviction upon table overflow])
+AT_SETUP([ofproto - hard limits on flow table size (OpenFLow 1.2)])
+OVS_VSWITCHD_START
+# Configure a maximum of 4 flows.
+AT_CHECK(
+  [ovs-vsctl \
+     -- --id=@t0 create Flow_Table flow-limit=4 \
+     -- set bridge br0 flow_tables:0=@t0 \
+   | perl $srcdir/uuidfilt.pl],
+  [0], [<0>
+])
+# Add 4 flows.
+for in_port in 1 2 3 4; do
+    ovs-ofctl -O OpenFlow12 add-flow br0 in_port=$in_port,actions=drop
+done
+AT_CHECK([ovs-ofctl -O OpenFlow12 dump-flows br0 | ofctl_strip | sort], [0], [dnl
+ in_port=1 actions=drop
+ in_port=2 actions=drop
+ in_port=3 actions=drop
+ in_port=4 actions=drop
+OFPST_FLOW reply (OF1.2):
+])
+# Adding another flow will be refused.
+AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 in_port=5,actions=drop], [1], [], [stderr])
+AT_CHECK([head -n 1 stderr | ofctl_strip], [0],
+  [OFPT_ERROR (OF1.2): OFPFMFC_TABLE_FULL
+])
+# Replacing or modifying an existing flow is allowed.
+AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 in_port=4,actions=normal])
+AT_CHECK([ovs-ofctl -O OpenFlow12 mod-flows br0 in_port=3,actions=output:1])
+AT_CHECK([ovs-ofctl -O OpenFlow12 dump-flows br0 | ofctl_strip | sort], [0], [dnl
+ in_port=1 actions=drop
+ in_port=2 actions=drop
+ in_port=3 actions=output:1
+ in_port=4 actions=NORMAL
+OFPST_FLOW reply (OF1.2):
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto - eviction upon table overflow (OpenFlow 1.0)])
 OVS_VSWITCHD_START
 # Configure a maximum of 4 flows.
 AT_CHECK(
@@ -534,7 +784,66 @@ NXST_FLOW reply:
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
-AT_SETUP([ofproto - eviction upon table overflow, with fairness])
+AT_SETUP([ofproto - eviction upon table overflow (OpenFlow 1.2)])
+OVS_VSWITCHD_START
+# Configure a maximum of 4 flows.
+AT_CHECK(
+  [ovs-vsctl \
+     -- --id=@t0 create Flow_Table flow-limit=4 overflow-policy=evict \
+     -- set bridge br0 flow_tables:0=@t0 \
+   | perl $srcdir/uuidfilt.pl],
+  [0], [<0>
+])
+# Add 4 flows.
+for in_port in 4 3 2 1; do
+    ovs-ofctl -O OpenFlow12 add-flow br0 idle_timeout=${in_port}0,in_port=$in_port,actions=drop
+done
+AT_CHECK([ovs-ofctl -O OpenFlow12 dump-flows br0 | ofctl_strip | sort], [0], [dnl
+ idle_timeout=10, in_port=1 actions=drop
+ idle_timeout=20, in_port=2 actions=drop
+ idle_timeout=30, in_port=3 actions=drop
+ idle_timeout=40, in_port=4 actions=drop
+OFPST_FLOW reply (OF1.2):
+])
+# Adding another flow will cause the one that expires soonest to be evicted.
+AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 in_port=5,actions=drop])
+AT_CHECK([ovs-ofctl -O OpenFlow12 dump-flows br0 | ofctl_strip | sort], [0], [dnl
+ idle_timeout=20, in_port=2 actions=drop
+ idle_timeout=30, in_port=3 actions=drop
+ idle_timeout=40, in_port=4 actions=drop
+ in_port=5 actions=drop
+OFPST_FLOW reply (OF1.2):
+])
+# In Open Flow 1.2 a mod-flow does not ever add a flow and thus
+# has no effect on eviction
+AT_CHECK([ovs-ofctl -O OpenFlow12 mod-flows br0 in_port=6,actions=drop])
+AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 in_port=4,actions=normal])
+AT_CHECK([ovs-ofctl -O OpenFlow12 mod-flows br0 in_port=3,actions=output:1])
+AT_CHECK([ovs-ofctl -O OpenFlow12 dump-flows br0 | ofctl_strip | sort], [0], [dnl
+ idle_timeout=20, in_port=2 actions=drop
+ idle_timeout=30, in_port=3 actions=output:1
+ in_port=4 actions=NORMAL
+ in_port=5 actions=drop
+OFPST_FLOW reply (OF1.2):
+])
+# Flows with no timeouts at all cannot be evicted.
+AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 in_port=6,actions=drop])
+AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 in_port=7,actions=normal])
+AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 in_port=8,actions=drop], [1], [], [stderr])
+AT_CHECK([head -n 1 stderr | ofctl_strip], [0],
+  [OFPT_ERROR (OF1.2): OFPFMFC_TABLE_FULL
+])
+AT_CHECK([ovs-ofctl -O OpenFlow12 dump-flows br0 | ofctl_strip | sort], [0], [dnl
+ in_port=4 actions=NORMAL
+ in_port=5 actions=drop
+ in_port=6 actions=drop
+ in_port=7 actions=NORMAL
+OFPST_FLOW reply (OF1.2):
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto - eviction upon table overflow, with fairness (OpenFlow 1.0)])
 OVS_VSWITCHD_START
 # Configure a maximum of 4 flows.
 AT_CHECK(
@@ -616,7 +925,89 @@ NXST_FLOW reply:
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
-AT_SETUP([ofproto - asynchronous message control])
+AT_SETUP([ofproto - eviction upon table overflow, with fairness (OpenFlow 1.2)])
+OVS_VSWITCHD_START
+# Configure a maximum of 4 flows.
+AT_CHECK(
+  [ovs-vsctl \
+     -- --id=@t0 create Flow_Table name=evict flow-limit=4 \
+                                   overflow-policy=evict \
+                                   groups='"NXM_OF_IN_PORT[[]]"' \
+     -- set bridge br0 flow_tables:0=@t0 \
+   | perl $srcdir/uuidfilt.pl],
+  [0], [<0>
+])
+# Add 4 flows.
+ovs-ofctl -O OpenFlow12 add-flows br0 - <<EOF
+idle_timeout=10 in_port=2 dl_src=00:44:55:66:77:88 actions=drop
+idle_timeout=20 in_port=1 dl_src=00:11:22:33:44:55 actions=drop
+idle_timeout=30 in_port=1 dl_src=00:22:33:44:55:66 actions=drop
+idle_timeout=40 in_port=1 dl_src=00:33:44:55:66:77 actions=drop
+EOF
+AT_CHECK([ovs-ofctl -O OpenFlow12 dump-flows br0 | ofctl_strip | sort], [0], [dnl
+ idle_timeout=10, in_port=2,dl_src=00:44:55:66:77:88 actions=drop
+ idle_timeout=20, in_port=1,dl_src=00:11:22:33:44:55 actions=drop
+ idle_timeout=30, in_port=1,dl_src=00:22:33:44:55:66 actions=drop
+ idle_timeout=40, in_port=1,dl_src=00:33:44:55:66:77 actions=drop
+OFPST_FLOW reply (OF1.2):
+])
+# Adding another flow will cause the one that expires soonest within
+# the largest group (those with in_port=1) to be evicted.  In this
+# case this is not the same as the one that expires soonest overall
+# (which is what makes the test interesting):
+AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 in_port=2,dl_src=00:55:66:77:88:99,actions=drop])
+AT_CHECK([ovs-ofctl -O OpenFlow12 dump-flows br0 | ofctl_strip | sort], [0], [dnl
+ idle_timeout=10, in_port=2,dl_src=00:44:55:66:77:88 actions=drop
+ idle_timeout=30, in_port=1,dl_src=00:22:33:44:55:66 actions=drop
+ idle_timeout=40, in_port=1,dl_src=00:33:44:55:66:77 actions=drop
+ in_port=2,dl_src=00:55:66:77:88:99 actions=drop
+OFPST_FLOW reply (OF1.2):
+])
+# Enlarge the flow limit, change the eviction policy back to strictly
+# based on expiration, and and add some flows.
+AT_CHECK([ovs-vsctl set Flow_Table evict groups='[[]]' flow-limit=7])
+ovs-ofctl -O OpenFlow12 add-flows br0 - <<EOF
+idle_timeout=50 in_port=2 dl_src=00:66:77:88:99:aa actions=drop
+idle_timeout=60 in_port=2 dl_src=00:77:88:99:aa:bb actions=drop
+idle_timeout=70 in_port=2 dl_src=00:88:99:aa:bb:cc actions=drop
+EOF
+AT_CHECK([ovs-ofctl -O OpenFlow12 dump-flows br0 | ofctl_strip | sort], [0], [dnl
+ idle_timeout=10, in_port=2,dl_src=00:44:55:66:77:88 actions=drop
+ idle_timeout=30, in_port=1,dl_src=00:22:33:44:55:66 actions=drop
+ idle_timeout=40, in_port=1,dl_src=00:33:44:55:66:77 actions=drop
+ idle_timeout=50, in_port=2,dl_src=00:66:77:88:99:aa actions=drop
+ idle_timeout=60, in_port=2,dl_src=00:77:88:99:aa:bb actions=drop
+ idle_timeout=70, in_port=2,dl_src=00:88:99:aa:bb:cc actions=drop
+ in_port=2,dl_src=00:55:66:77:88:99 actions=drop
+OFPST_FLOW reply (OF1.2):
+])
+# Adding another flow will cause the one that expires soonest overall
+# to be evicted.
+AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 'idle_timeout=80 in_port=2 dl_src=00:99:aa:bb:cc:dd actions=drop'])
+AT_CHECK([ovs-ofctl -O OpenFlow12 dump-flows br0 | ofctl_strip | sort], [0], [dnl
+ idle_timeout=30, in_port=1,dl_src=00:22:33:44:55:66 actions=drop
+ idle_timeout=40, in_port=1,dl_src=00:33:44:55:66:77 actions=drop
+ idle_timeout=50, in_port=2,dl_src=00:66:77:88:99:aa actions=drop
+ idle_timeout=60, in_port=2,dl_src=00:77:88:99:aa:bb actions=drop
+ idle_timeout=70, in_port=2,dl_src=00:88:99:aa:bb:cc actions=drop
+ idle_timeout=80, in_port=2,dl_src=00:99:aa:bb:cc:dd actions=drop
+ in_port=2,dl_src=00:55:66:77:88:99 actions=drop
+OFPST_FLOW reply (OF1.2):
+])
+# Reducing the flow limit also causes the flows that expire soonest
+# overall to be evicted.
+AT_CHECK([ovs-vsctl set Flow_Table evict flow-limit=4])
+AT_CHECK([ovs-ofctl -O OpenFlow12 dump-flows br0 | ofctl_strip | sort], [0], [dnl
+ idle_timeout=60, in_port=2,dl_src=00:77:88:99:aa:bb actions=drop
+ idle_timeout=70, in_port=2,dl_src=00:88:99:aa:bb:cc actions=drop
+ idle_timeout=80, in_port=2,dl_src=00:99:aa:bb:cc:dd actions=drop
+ in_port=2,dl_src=00:55:66:77:88:99 actions=drop
+OFPST_FLOW reply (OF1.2):
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto - asynchronous message control (OpenFlow 1.0)])
 OVS_VSWITCHD_START
 AT_CHECK([ovs-ofctl -P openflow10 monitor br0 --detach --no-chdir --pidfile])
 check_async () {
@@ -628,33 +1019,33 @@ check_async () {
     : > expout
 
     # OFPT_PACKET_IN, OFPR_ACTION (controller_id=0)
-    ovs-ofctl -v packet-out br0 none controller '0001020304050010203040501234'
+    ovs-ofctl -v packet-out br0 controller controller '0001020304050010203040501234'
     if test X"$1" = X"OFPR_ACTION"; then shift;
-        echo >>expout "OFPT_PACKET_IN: total_len=14 in_port=NONE (via action) data_len=14 (unbuffered)
-priority=0,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=00:10:20:30:40:50,dl_dst=00:01:02:03:04:05,dl_type=0x1234"
+        echo >>expout "OFPT_PACKET_IN: total_len=14 in_port=CONTROLLER (via action) data_len=14 (unbuffered)
+metadata=0,in_port=0,vlan_tci=0x0000,dl_src=00:10:20:30:40:50,dl_dst=00:01:02:03:04:05,dl_type=0x1234"
     fi
 
     # OFPT_PACKET_IN, OFPR_NO_MATCH (controller_id=123)
-    ovs-ofctl -v packet-out br0 none 'controller(reason=no_match,id=123)' '0001020304050010203040501234'
+    ovs-ofctl -v packet-out br0 controller 'controller(reason=no_match,id=123)' '0001020304050010203040501234'
     if test X"$1" = X"OFPR_NO_MATCH"; then shift;
-        echo >>expout "OFPT_PACKET_IN: total_len=14 in_port=NONE (via no_match) data_len=14 (unbuffered)
-priority=0,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=00:10:20:30:40:50,dl_dst=00:01:02:03:04:05,dl_type=0x1234"
+        echo >>expout "OFPT_PACKET_IN: total_len=14 in_port=CONTROLLER (via no_match) data_len=14 (unbuffered)
+metadata=0,in_port=0,vlan_tci=0x0000,dl_src=00:10:20:30:40:50,dl_dst=00:01:02:03:04:05,dl_type=0x1234"
     fi
 
     # OFPT_PACKET_IN, OFPR_INVALID_TTL (controller_id=0)
-    ovs-ofctl packet-out br0 none dec_ttl '002583dfb4000026b98cb0f908004500003fb7e200000011339bac11370dac100002d7730035002b8f6d86fb0100000100000000000006626c702d7873066e696369726103636f6d00000f00'
+    ovs-ofctl packet-out br0 controller dec_ttl '002583dfb4000026b98cb0f908004500003fb7e200000011339bac11370dac100002d7730035002b8f6d86fb0100000100000000000006626c702d7873066e696369726103636f6d00000f00'
     if test X"$1" = X"OFPR_INVALID_TTL"; then shift;
-        echo >>expout "OFPT_PACKET_IN: total_len=76 in_port=NONE (via invalid_ttl) data_len=76 (unbuffered)
-priority=0,udp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=00:26:b9:8c:b0:f9,dl_dst=00:25:83:df:b4:00,nw_src=172.17.55.13,nw_dst=172.16.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=55155,tp_dst=53 udp_csum:8f6d"
+        echo >>expout "OFPT_PACKET_IN: total_len=76 in_port=CONTROLLER (via invalid_ttl) data_len=76 (unbuffered)
+udp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=00:26:b9:8c:b0:f9,dl_dst=00:25:83:df:b4:00,nw_src=172.17.55.13,nw_dst=172.16.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=55155,tp_dst=53 udp_csum:8f6d"
     fi
 
     # OFPT_PORT_STATUS, OFPPR_ADD
-    ovs-vsctl add-port br0 test -- set Interface test type=dummy
+    ovs-vsctl add-port br0 test -- set Interface test type=dummy ofport_request=1
     if test X"$1" = X"OFPPR_ADD"; then shift;
         echo >>expout "OFPT_PORT_STATUS: ADD: 1(test): addr:aa:55:aa:55:00:0x
      config:     PORT_DOWN
      state:      LINK_DOWN
-     speed: 100 Mbps now, 100 Mbps max"
+     speed: 0 Mbps now, 0 Mbps max"
     fi
 
     # OFPT_PORT_STATUS, OFPPR_DELETE
@@ -663,7 +1054,7 @@ priority=0,udp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=00:26:b9:8c:b0:f9,dl_
         echo >>expout "OFPT_PORT_STATUS: DEL: 1(test): addr:aa:55:aa:55:00:0x
      config:     PORT_DOWN
      state:      LINK_DOWN
-     speed: 100 Mbps now, 100 Mbps max"
+     speed: 0 Mbps now, 0 Mbps max"
     fi
 
     # OFPT_FLOW_REMOVED, OFPRR_DELETE
@@ -719,11 +1110,254 @@ ovs-appctl -t ovs-ofctl exit
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
+AT_SETUP([ofproto - asynchronous message control (OpenFlow 1.2)])
+OVS_VSWITCHD_START
+AT_CHECK([ovs-ofctl -O OpenFlow12 monitor br0 --detach --no-chdir --pidfile])
+check_async () {
+    printf '\n\n--- check_async %d ---\n\n\n' $1
+    INDEX=$1
+    shift
+
+    ovs-appctl -t ovs-ofctl ofctl/barrier
+    ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log
+    : > expout
+
+    # OFPT_PACKET_IN, OFPR_ACTION (controller_id=0)
+    ovs-ofctl -O OpenFlow12 -v packet-out br0 none controller '0001020304050010203040501234'
+    if test X"$1" = X"OFPR_ACTION"; then shift;
+        echo >>expout "OFPT_PACKET_IN (OF1.2): total_len=14 in_port=ANY (via action) data_len=14 (unbuffered)
+metadata=0,in_port=0,vlan_tci=0x0000,dl_src=00:10:20:30:40:50,dl_dst=00:01:02:03:04:05,dl_type=0x1234"
+    fi
+
+    # OFPT_PACKET_IN, OFPR_NO_MATCH (controller_id=123)
+    ovs-ofctl -O OpenFlow12 -v packet-out br0 none 'controller(reason=no_match,id=123)' '0001020304050010203040501234'
+    if test X"$1" = X"OFPR_NO_MATCH"; then shift;
+        echo >>expout "OFPT_PACKET_IN (OF1.2): total_len=14 in_port=ANY (via no_match) data_len=14 (unbuffered)
+metadata=0,in_port=0,vlan_tci=0x0000,dl_src=00:10:20:30:40:50,dl_dst=00:01:02:03:04:05,dl_type=0x1234"
+    fi
+
+    # OFPT_PACKET_IN, OFPR_INVALID_TTL (controller_id=0)
+    ovs-ofctl -O OpenFlow12 packet-out br0 none dec_ttl '002583dfb4000026b98cb0f908004500003fb7e200000011339bac11370dac100002d7730035002b8f6d86fb0100000100000000000006626c702d7873066e696369726103636f6d00000f00'
+    if test X"$1" = X"OFPR_INVALID_TTL"; then shift;
+        echo >>expout "OFPT_PACKET_IN (OF1.2): total_len=76 in_port=ANY (via invalid_ttl) data_len=76 (unbuffered)
+udp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=00:26:b9:8c:b0:f9,dl_dst=00:25:83:df:b4:00,nw_src=172.17.55.13,nw_dst=172.16.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=55155,tp_dst=53 udp_csum:8f6d"
+    fi
+
+    # OFPT_PORT_STATUS, OFPPR_ADD
+    ovs-vsctl add-port br0 test -- set Interface test type=dummy
+    if test X"$1" = X"OFPPR_ADD"; then shift;
+        echo >>expout "OFPT_PORT_STATUS (OF1.2): ADD: ${INDEX}(test): addr:aa:55:aa:55:00:0x
+     config:     PORT_DOWN
+     state:      LINK_DOWN
+     speed: 0 Mbps now, 0 Mbps max"
+    fi
+
+    # OFPT_PORT_STATUS, OFPPR_DELETE
+    ovs-vsctl del-port br0 test
+    if test X"$1" = X"OFPPR_DELETE"; then shift;
+        echo >>expout "OFPT_PORT_STATUS (OF1.2): DEL: ${INDEX}(test): addr:aa:55:aa:55:00:0x
+     config:     PORT_DOWN
+     state:      LINK_DOWN
+     speed: 0 Mbps now, 0 Mbps max"
+    fi
+
+    # OFPT_FLOW_REMOVED, OFPRR_DELETE
+    ovs-ofctl -O OpenFlow12 add-flow br0 send_flow_rem,actions=drop
+    ovs-ofctl -O OpenFlow12 --strict del-flows br0 ''
+    if test X"$1" = X"OFPRR_DELETE"; then shift;
+        echo >>expout "OFPT_FLOW_REMOVED (OF1.2):  reason=delete table_id=0"
+    fi
+    AT_FAIL_IF([test X"$1" != X])
+
+    ovs-appctl -t ovs-ofctl ofctl/barrier
+    echo >>expout "OFPT_BARRIER_REPLY (OF1.2):"
+
+    AT_CHECK(
+      [[sed '
+s/ (xid=0x[0-9a-fA-F]*)//
+s/ *duration.*//
+s/00:0.$/00:0x/' < monitor.log]],
+      [0], [expout])
+}
+
+# It's a service connection so initially there should be no async messages.
+check_async 1
+
+# Set miss_send_len to 128, turning on packet-ins for our service connection.
+ovs-appctl -t ovs-ofctl ofctl/send 0309000c0123456700000080
+check_async 2 OFPR_ACTION OFPPR_ADD OFPPR_DELETE OFPRR_DELETE
+
+# Set miss_send_len to 128 and enable invalid_ttl.
+ovs-appctl -t ovs-ofctl ofctl/send 0309000c0123456700040080
+check_async 3 OFPR_ACTION OFPR_INVALID_TTL OFPPR_ADD OFPPR_DELETE OFPRR_DELETE
+
+# Become slave (OF 1.2), which should disable everything except port status.
+ovs-appctl -t ovs-ofctl ofctl/send 031800180000000200000003000000000000000000000001
+check_async 4 OFPPR_ADD OFPPR_DELETE
+
+# Use NXT_SET_ASYNC_CONFIG to enable a patchwork of asynchronous messages.
+ovs-appctl -t ovs-ofctl ofctl/send 03040028000000020000232000000013000000020000000500000005000000020000000200000005
+check_async 5 OFPR_INVALID_TTL OFPPR_DELETE OFPRR_DELETE
+
+# Set controller ID 123.
+ovs-appctl -t ovs-ofctl ofctl/send 03040018000000030000232000000014000000000000007b
+check_async 6 OFPR_NO_MATCH OFPPR_DELETE OFPRR_DELETE
+
+# Restore controller ID 0.
+ovs-appctl -t ovs-ofctl ofctl/send 030400180000000300002320000000140000000000000000
+
+# Become master (OF 1.2).
+ovs-appctl -t ovs-ofctl ofctl/send 031800180000000400000002000000000000000000000002
+check_async 7 OFPR_ACTION OFPPR_ADD
+
+ovs-appctl -t ovs-ofctl exit
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto - asynchronous message control (OpenFlow 1.3)])
+OVS_VSWITCHD_START
+AT_CHECK([ovs-ofctl -O OpenFlow13 monitor br0 --detach --no-chdir --pidfile])
+check_async () {
+    printf '\n\n--- check_async %d ---\n\n\n' $1
+    INDEX=$1
+    shift
+
+    ovs-appctl -t ovs-ofctl ofctl/barrier
+    ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log
+    : > expout
+
+    # OFPT_PACKET_IN, OFPR_ACTION (controller_id=0)
+    ovs-ofctl -O OpenFlow13 -v packet-out br0 none controller '0001020304050010203040501234'
+    if test X"$1" = X"OFPR_ACTION"; then shift;
+        echo >>expout "OFPT_PACKET_IN (OF1.3): total_len=14 in_port=ANY (via action) data_len=14 (unbuffered)
+metadata=0,in_port=0,vlan_tci=0x0000,dl_src=00:10:20:30:40:50,dl_dst=00:01:02:03:04:05,dl_type=0x1234"
+    fi
+
+    # OFPT_PACKET_IN, OFPR_NO_MATCH (controller_id=123)
+    ovs-ofctl -O OpenFlow13 -v packet-out br0 none 'controller(reason=no_match,id=123)' '0001020304050010203040501234'
+    if test X"$1" = X"OFPR_NO_MATCH"; then shift;
+        echo >>expout "OFPT_PACKET_IN (OF1.3): total_len=14 in_port=ANY (via no_match) data_len=14 (unbuffered)
+metadata=0,in_port=0,vlan_tci=0x0000,dl_src=00:10:20:30:40:50,dl_dst=00:01:02:03:04:05,dl_type=0x1234"
+    fi
+
+    # OFPT_PACKET_IN, OFPR_INVALID_TTL (controller_id=0)
+    ovs-ofctl -O OpenFlow13 packet-out br0 none dec_ttl '002583dfb4000026b98cb0f908004500003fb7e200000011339bac11370dac100002d7730035002b8f6d86fb0100000100000000000006626c702d7873066e696369726103636f6d00000f00'
+    if test X"$1" = X"OFPR_INVALID_TTL"; then shift;
+        echo >>expout "OFPT_PACKET_IN (OF1.3): total_len=76 in_port=ANY (via invalid_ttl) data_len=76 (unbuffered)
+udp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=00:26:b9:8c:b0:f9,dl_dst=00:25:83:df:b4:00,nw_src=172.17.55.13,nw_dst=172.16.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=55155,tp_dst=53 udp_csum:8f6d"
+    fi
+
+    # OFPT_PORT_STATUS, OFPPR_ADD
+    ovs-vsctl add-port br0 test -- set Interface test type=dummy
+    if test X"$1" = X"OFPPR_ADD"; then shift;
+        echo >>expout "OFPT_PORT_STATUS (OF1.3): ADD: ${INDEX}(test): addr:aa:55:aa:55:00:0x
+     config:     PORT_DOWN
+     state:      LINK_DOWN
+     speed: 0 Mbps now, 0 Mbps max"
+    fi
+
+    # OFPT_PORT_STATUS, OFPPR_DELETE
+    ovs-vsctl del-port br0 test
+    if test X"$1" = X"OFPPR_DELETE"; then shift;
+        echo >>expout "OFPT_PORT_STATUS (OF1.3): DEL: ${INDEX}(test): addr:aa:55:aa:55:00:0x
+     config:     PORT_DOWN
+     state:      LINK_DOWN
+     speed: 0 Mbps now, 0 Mbps max"
+    fi
+
+    # OFPT_FLOW_REMOVED, OFPRR_DELETE
+    ovs-ofctl -O OpenFlow13 add-flow br0 send_flow_rem,actions=drop
+    ovs-ofctl -O OpenFlow13 --strict del-flows br0 ''
+    if test X"$1" = X"OFPRR_DELETE"; then shift;
+        echo >>expout "OFPT_FLOW_REMOVED (OF1.3):  reason=delete table_id=0"
+    fi
+    AT_FAIL_IF([test X"$1" != X])
+
+    ovs-appctl -t ovs-ofctl ofctl/barrier
+    echo >>expout "OFPT_BARRIER_REPLY (OF1.3):"
+
+    AT_CHECK(
+      [[sed '
+s/ (xid=0x[0-9a-fA-F]*)//
+s/ *duration.*//
+s/00:0.$/00:0x/' < monitor.log]],
+      [0], [expout])
+}
+
+# It's a service connection so initially there should be no async messages.
+check_async 1
+
+# Set miss_send_len to 128, turning on packet-ins for our service connection.
+ovs-appctl -t ovs-ofctl ofctl/send 0409000c0123456700000080
+check_async 2 OFPR_ACTION OFPPR_ADD OFPPR_DELETE OFPRR_DELETE
+
+# Become slave (OF 1.3), which should disable everything except port status.
+ovs-appctl -t ovs-ofctl ofctl/send 041800180000000200000003000000000000000000000001
+check_async 3 OFPPR_ADD OFPPR_DELETE
+
+# Use OF 1.3 OFPT_SET_ASYNC to enable a patchwork of asynchronous messages.
+ovs-appctl -t ovs-ofctl ofctl/send 041c002000000002000000020000000500000005000000020000000200000005
+check_async 4 OFPR_INVALID_TTL OFPPR_DELETE OFPRR_DELETE
+
+# Set controller ID 123.
+ovs-appctl -t ovs-ofctl ofctl/send 04040018000000030000232000000014000000000000007b
+check_async 5 OFPR_NO_MATCH OFPPR_DELETE OFPRR_DELETE
+
+# Restore controller ID 0.
+ovs-appctl -t ovs-ofctl ofctl/send 040400180000000300002320000000140000000000000000
+
+# Become master (OF 1.3).
+ovs-appctl -t ovs-ofctl ofctl/send 041800180000000400000002000000000000000000000002
+check_async 6 OFPR_ACTION OFPPR_ADD
+
+ovs-appctl -t ovs-ofctl exit
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+dnl This test checks that the role request/response messaging works
+dnl and that generation_id is handled properly.
+AT_SETUP([ofproto - controller role (OpenFlow 1.2)])
+OVS_VSWITCHD_START
+AT_CHECK([ovs-ofctl -O OpenFlow12 monitor br0 --detach --no-chdir --pidfile])
+
+ovs-appctl -t ovs-ofctl ofctl/barrier
+ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log
+: > expout
+
+# find out current role
+ovs-appctl -t ovs-ofctl ofctl/send 031800180000000200000000000000000000000000000000
+echo >>expout "send: OFPT_ROLE_REQUEST (OF1.2) (xid=0x2): role=nochange"
+echo >>expout "OFPT_ROLE_REPLY (OF1.2) (xid=0x2): role=equal"
+
+# Become slave (generation_id is initially undefined, so 2^63+2 should not be stale)
+ovs-appctl -t ovs-ofctl ofctl/send 031800180000000300000003000000008000000000000002
+echo >>expout "send: OFPT_ROLE_REQUEST (OF1.2) (xid=0x3): role=slave generation_id=9223372036854775810"
+echo >>expout "OFPT_ROLE_REPLY (OF1.2) (xid=0x3): role=slave"
+
+# Try to become the master using a stale generation ID
+ovs-appctl -t ovs-ofctl ofctl/send 031800180000000400000002000000000000000000000002
+echo >>expout "send: OFPT_ROLE_REQUEST (OF1.2) (xid=0x4): role=master generation_id=2"
+echo >>expout "OFPT_ERROR (OF1.2) (xid=0x4): OFPRRFC_STALE"
+echo >>expout "OFPT_ROLE_REQUEST (OF1.2) (xid=0x4): role=master generation_id=2"
+
+# Become master using a valid generation ID
+ovs-appctl -t ovs-ofctl ofctl/send 031800180000000500000002000000000000000000000001
+echo >>expout "send: OFPT_ROLE_REQUEST (OF1.2) (xid=0x5): role=master generation_id=1"
+echo >>expout "OFPT_ROLE_REPLY (OF1.2) (xid=0x5): role=master"
+ovs-appctl -t ovs-ofctl ofctl/barrier
+echo >>expout "OFPT_BARRIER_REPLY (OF1.2) (xid=0x3):"
+
+AT_CHECK([cat monitor.log], [0], [expout])
+
+ovs-appctl -t ovs-ofctl exit
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
 dnl This test checks that OFPT_PACKET_OUT accepts both OFPP_NONE (as
 dnl specified by OpenFlow 1.0) and OFPP_CONTROLLER (used by some
 dnl controllers despite the spec) as meaning a packet that was generated
 dnl by the controller.
-AT_SETUP([ofproto - packet-out from controller])
+AT_SETUP([ofproto - packet-out from controller (OpenFlow 1.0)])
 OVS_VSWITCHD_START
 
 # Start a monitor listening for packet-ins.
@@ -742,16 +1376,49 @@ ovs-appctl -t ovs-ofctl ofctl/barrier
 ovs-appctl -t ovs-ofctl exit
 
 AT_CHECK([sed 's/ (xid=0x[[0-9a-fA-F]]*)//' monitor.log], [0], [dnl
-OFPT_PACKET_IN: total_len=14 in_port=NONE (via action) data_len=14 (unbuffered)
-priority=0,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=00:10:20:30:40:50,dl_dst=00:01:02:03:04:05,dl_type=0x1234
+OFPT_PACKET_IN: total_len=14 in_port=ANY (via action) data_len=14 (unbuffered)
+metadata=0,in_port=0,vlan_tci=0x0000,dl_src=00:10:20:30:40:50,dl_dst=00:01:02:03:04:05,dl_type=0x1234
 OFPT_PACKET_IN: total_len=14 in_port=CONTROLLER (via action) data_len=14 (unbuffered)
-priority=0,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=00:10:20:30:40:50,dl_dst=00:01:02:03:04:05,dl_type=0x5678
+metadata=0,in_port=0,vlan_tci=0x0000,dl_src=00:10:20:30:40:50,dl_dst=00:01:02:03:04:05,dl_type=0x5678
 OFPT_BARRIER_REPLY:
 ])
 
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
+dnl This test checks that OFPT_PACKET_OUT accepts both OFPP_NONE (as
+dnl specified by OpenFlow 1.2) and OFPP_CONTROLLER (used by some
+dnl controllers despite the spec) as meaning a packet that was generated
+dnl by the controller.
+AT_SETUP([ofproto - packet-out from controller (OpenFlow 1.2)])
+OVS_VSWITCHD_START
+
+# Start a monitor listening for packet-ins.
+AT_CHECK([ovs-ofctl -O OpenFlow12 monitor br0 --detach --no-chdir --pidfile])
+ovs-appctl -t ovs-ofctl ofctl/send 0309000c0123456700000080
+ovs-appctl -t ovs-ofctl ofctl/barrier
+ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log
+AT_CAPTURE_FILE([monitor.log])
+
+# Send some packet-outs with OFPP_NONE and OFPP_CONTROLLER (65533) as in_port.
+AT_CHECK([ovs-ofctl -O OpenFlow12 packet-out br0 none controller '0001020304050010203040501234'])
+AT_CHECK([ovs-ofctl -O OpenFlow12 packet-out br0 4294967293 controller '0001020304050010203040505678'])
+
+# Stop the monitor and check its output.
+ovs-appctl -t ovs-ofctl ofctl/barrier
+ovs-appctl -t ovs-ofctl exit
+
+AT_CHECK([sed 's/ (xid=0x[[0-9a-fA-F]]*)//' monitor.log], [0], [dnl
+OFPT_PACKET_IN (OF1.2): total_len=14 in_port=ANY (via action) data_len=14 (unbuffered)
+metadata=0,in_port=0,vlan_tci=0x0000,dl_src=00:10:20:30:40:50,dl_dst=00:01:02:03:04:05,dl_type=0x1234
+OFPT_PACKET_IN (OF1.2): total_len=14 in_port=CONTROLLER (via action) data_len=14 (unbuffered)
+metadata=0,in_port=0,vlan_tci=0x0000,dl_src=00:10:20:30:40:50,dl_dst=00:01:02:03:04:05,dl_type=0x5678
+OFPT_BARRIER_REPLY (OF1.2):
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
 dnl This test checks that metadata is encoded in packet_in structures,
 dnl supported by NXAST.
 AT_SETUP([ofproto - packet-out with metadata (NXM)])
@@ -765,21 +1432,49 @@ ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log
 AT_CAPTURE_FILE([monitor.log])
 
 # Send a packet-out with a load action to set some metadata, and forward to controller
-AT_CHECK([ovs-ofctl packet-out br0 none 'load(0xfafafafa5a5a5a5a->OXM_OF_METADATA[[0..63]]), controller' '0001020304050010203040501234'])
+AT_CHECK([ovs-ofctl packet-out br0 controller 'load(0xfafafafa5a5a5a5a->OXM_OF_METADATA[[0..63]]), controller' '0001020304050010203040501234'])
 
 # Stop the monitor and check its output.
 ovs-appctl -t ovs-ofctl ofctl/barrier
 ovs-appctl -t ovs-ofctl exit
 
 AT_CHECK([sed 's/ (xid=0x[[0-9a-fA-F]]*)//' monitor.log], [0], [dnl
-NXT_PACKET_IN: total_len=14 in_port=NONE metadata=0xfafafafa5a5a5a5a (via action) data_len=14 (unbuffered)
-priority=0,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=00:10:20:30:40:50,dl_dst=00:01:02:03:04:05,dl_type=0x1234
+NXT_PACKET_IN: total_len=14 in_port=CONTROLLER metadata=0xfafafafa5a5a5a5a (via action) data_len=14 (unbuffered)
+metadata=0,in_port=0,vlan_tci=0x0000,dl_src=00:10:20:30:40:50,dl_dst=00:01:02:03:04:05,dl_type=0x1234
 OFPT_BARRIER_REPLY:
 ])
 
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
+dnl This test checks that metadata is encoded in packet_in structures,
+dnl supported by NXAST.
+AT_SETUP([ofproto - packet-out with metadata (OpenFlow 1.2)])
+OVS_VSWITCHD_START
+
+# Start a monitor listening for packet-ins.
+AT_CHECK([ovs-ofctl -O OpenFlow12 monitor br0 --detach --no-chdir --pidfile])
+ovs-appctl -t ovs-ofctl ofctl/send 0309000c0123456700000080
+ovs-appctl -t ovs-ofctl ofctl/barrier
+ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log
+AT_CAPTURE_FILE([monitor.log])
+
+# Send a packet-out with a load action to set some metadata, and forward to controller
+AT_CHECK([ovs-ofctl -O OpenFlow12 packet-out br0 none 'load(0xfafafafa5a5a5a5a->OXM_OF_METADATA[[0..63]]), controller' '0001020304050010203040501234'])
+
+# Stop the monitor and check its output.
+ovs-appctl -t ovs-ofctl ofctl/barrier
+ovs-appctl -t ovs-ofctl exit
+
+AT_CHECK([sed 's/ (xid=0x[[0-9a-fA-F]]*)//' monitor.log], [0], [dnl
+OFPT_PACKET_IN (OF1.2): total_len=14 in_port=ANY metadata=0xfafafafa5a5a5a5a (via action) data_len=14 (unbuffered)
+metadata=0,in_port=0,vlan_tci=0x0000,dl_src=00:10:20:30:40:50,dl_dst=00:01:02:03:04:05,dl_type=0x1234
+OFPT_BARRIER_REPLY (OF1.2):
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
 AT_SETUP([ofproto - flow monitoring])
 AT_KEYWORDS([monitor])
 OVS_VSWITCHD_START
@@ -907,6 +1602,7 @@ OFPT_BARRIER_REPLY:
 ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log
 ovs-ofctl add-flow br0 in_port=1,actions=output:2
 ovs-ofctl add-flow br0 in_port=2,actions=output:1
+ovs-appctl -t ovs-ofctl ofctl/barrier
 ovs-appctl -t ovs-ofctl ofctl/send 010e004812345678003fffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000ffffffffffff0000
 ovs-appctl -t ovs-ofctl ofctl/barrier
 AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip], [0], [NXST_FLOW reply:
@@ -916,6 +1612,7 @@ AT_CHECK([sed 's/ (xid=0x[[1-9a-fA-F]][[0-9a-fA-F]]*)//' monitor.log], [0],
  event=ADDED table=0 cookie=0 in_port=1 actions=output:2
 NXST_FLOW_MONITOR reply (xid=0x0):
  event=ADDED table=0 cookie=0 in_port=2 actions=output:1
+OFPT_BARRIER_REPLY:
 send: OFPT_FLOW_MOD: DEL priority=0 actions=drop
 NXST_FLOW_MONITOR reply (xid=0x0):
  event=ABBREV xid=0x12345678
index 1fa1a34..ca68226 100644 (file)
@@ -20,7 +20,7 @@ AT_CHECK([[sed 's/ (xid=0x[0-9a-fA-F]*)//' stdout]], [0],
 [[usable protocols: any
 chosen protocol: OpenFlow10-table_id
 OFPT_FLOW_MOD: ADD tcp,tp_src=123 actions=FLOOD
-OFPT_FLOW_MOD: ADD in_port=65534,dl_vlan=9,dl_src=00:0a:e4:25:6b:b0 actions=drop
+OFPT_FLOW_MOD: ADD in_port=LOCAL,dl_vlan=9,dl_src=00:0a:e4:25:6b:b0 actions=drop
 OFPT_FLOW_MOD: ADD udp,dl_vlan_pcp=7 idle:5 actions=strip_vlan,output:0
 OFPT_FLOW_MOD: ADD tcp,nw_src=192.168.0.3,tp_dst=80 actions=set_queue:37,output:1
 OFPT_FLOW_MOD: ADD udp,nw_src=192.168.0.3,tp_dst=53 actions=pop_queue,output:1
@@ -31,6 +31,61 @@ OFPT_FLOW_MOD: ADD in_port=0 actions=resubmit:0
 ]])
 AT_CLEANUP
 
+AT_SETUP([ovs-ofctl parse-flows (OpenFlow 1.2)])
+AT_DATA([flows.txt], [[
+# comment
+tcp,tp_src=123,actions=flood
+in_port=LOCAL dl_vlan=9 dl_src=00:0A:E4:25:6B:B0 actions=drop
+udp dl_vlan_pcp=7 idle_timeout=5 actions=strip_vlan output:0
+tcp,nw_src=192.168.0.3,tp_dst=80 actions=set_queue:37,output:1
+udp,nw_src=192.168.0.3,tp_dst=53 actions=pop_queue,output:1
+cookie=0x123456789abcdef hard_timeout=10 priority=60000 actions=controller
+actions=note:41.42.43,note:00.01.02.03.04.05.06.07,note
+actions=set_field:fe80:0123:4567:890a:a6ba:dbff:fefe:59fa->ipv6_src
+in_port=0 actions=resubmit:0
+]])
+
+AT_CHECK([ovs-ofctl --protocols OpenFlow12 parse-flows flows.txt
+], [0], [stdout])
+AT_CHECK([[sed 's/ (xid=0x[0-9a-fA-F]*)//' stdout]], [0],
+[[usable protocols: any
+chosen protocol: OXM-OpenFlow12
+OFPT_FLOW_MOD (OF1.2): ADD table:255 tcp,tp_src=123 actions=FLOOD
+OFPT_FLOW_MOD (OF1.2): ADD table:255 in_port=LOCAL,dl_vlan=9,dl_src=00:0a:e4:25:6b:b0 actions=drop
+OFPT_FLOW_MOD (OF1.2): ADD table:255 udp,dl_vlan_pcp=7 idle:5 actions=strip_vlan,output:0
+OFPT_FLOW_MOD (OF1.2): ADD table:255 tcp,nw_src=192.168.0.3,tp_dst=80 actions=set_queue:37,output:1
+OFPT_FLOW_MOD (OF1.2): ADD table:255 udp,nw_src=192.168.0.3,tp_dst=53 actions=pop_queue,output:1
+OFPT_FLOW_MOD (OF1.2): ADD table:255 priority=60000 cookie:0x123456789abcdef hard:10 actions=CONTROLLER:65535
+OFPT_FLOW_MOD (OF1.2): ADD table:255 actions=note:41.42.43.00.00.00,note:00.01.02.03.04.05.06.07.00.00.00.00.00.00,note:00.00.00.00.00.00
+OFPT_FLOW_MOD (OF1.2): ADD table:255 actions=set_field:fe80:123:4567:890a:a6ba:dbff:fefe:59fa->ipv6_src
+OFPT_FLOW_MOD (OF1.2): ADD table:255 in_port=0 actions=resubmit:0
+]])
+AT_CLEANUP
+
+AT_SETUP([ovs-ofctl parse-flows (With Tunnel-Parameters)])
+AT_DATA([flows.txt], [[
+tun_id=0x1234000056780000/0xffff0000ffff0000,tun_src=1.1.1.1,tun_dst=2.2.2.2,tun_tos=0x3,tun_ttl=20,tun_flags=key|csum actions=drop
+]])
+
+AT_CHECK([ovs-ofctl parse-flows flows.txt
+], [1], [usable protocols: none
+], [stderr])
+
+AT_CLEANUP
+
+
+AT_SETUP([ovs-ofctl parse-flows (skb_mark and skb_priority)])
+AT_DATA([flows.txt], [[
+skb_mark=0x12345678,skb_priority=0x12341234,tcp,tp_src=123,actions=flood
+]])
+
+AT_CHECK([ovs-ofctl parse-flows flows.txt
+], [1], [usable protocols: none
+], [stderr])
+
+AT_CLEANUP
+
+
 AT_SETUP([ovs-ofctl parse-flows (NXM)])
 AT_DATA([flows.txt], [[
 # comment
@@ -66,10 +121,10 @@ actions=controller(max_len=123,reason=invalid_ttl,id=555)
 AT_CHECK([ovs-ofctl parse-flows flows.txt
 ], [0], [stdout])
 AT_CHECK([[sed 's/ (xid=0x[0-9a-fA-F]*)//' stdout]], [0],
-[[usable protocols: NXM+table_id
+[[usable protocols: OXM,NXM+table_id
 chosen protocol: NXM+table_id
 NXT_FLOW_MOD: ADD table:255 tcp,tp_src=123 actions=FLOOD
-NXT_FLOW_MOD: ADD table:255 in_port=65534,dl_vlan=9,dl_src=00:0a:e4:25:6b:b0 actions=drop
+NXT_FLOW_MOD: ADD table:255 in_port=LOCAL,dl_vlan=9,dl_src=00:0a:e4:25:6b:b0 actions=drop
 NXT_FLOW_MOD: ADD table:255 udp,dl_vlan_pcp=7 idle:5 actions=strip_vlan,output:0
 NXT_FLOW_MOD: ADD table:255 tcp,nw_src=192.168.0.3,tp_dst=80 actions=set_queue:37,output:1
 NXT_FLOW_MOD: ADD table:255 udp,nw_src=192.168.0.3,tp_dst=53 actions=pop_queue,output:1
@@ -131,10 +186,10 @@ dl_dst=aa:bb:cc:dd:ee:ff/00:00:00:00:00:00,actions=drop
 ])
 AT_CHECK([ovs-ofctl -F nxm parse-flows flows.txt], [0], [stdout])
 AT_CHECK([[sed 's/ (xid=0x[0-9a-fA-F]*)//' stdout]], [0], [dnl
-usable protocols: NXM
+usable protocols: NXM,OXM
 chosen protocol: NXM-table_id
 NXT_FLOW_MOD: ADD tcp,tp_src=123 actions=FLOOD
-NXT_FLOW_MOD: ADD in_port=65534,dl_vlan=9,dl_src=00:0a:e4:25:6b:b0 actions=drop
+NXT_FLOW_MOD: ADD in_port=LOCAL,dl_vlan=9,dl_src=00:0a:e4:25:6b:b0 actions=drop
 NXT_FLOW_MOD: ADD arp,dl_src=00:0a:e4:25:6b:b0,arp_sha=00:0a:e4:25:6b:b0 actions=drop
 NXT_FLOW_MOD: ADD ipv6,ipv6_label=0x12345 actions=output:2
 NXT_FLOW_MOD: ADD ipv6,ipv6_src=2001:db8:3c4d:1:2:3:4:5 actions=output:3
@@ -193,7 +248,7 @@ vlan_tci=0x1123/0x1fff,actions=drop
 ]])
 AT_CHECK([ovs-ofctl -F nxm -mmm parse-flows flows.txt], [0], [stdout], [stderr])
 AT_CHECK([[sed 's/ (xid=0x[0-9a-fA-F]*)//' stdout]], [0],
-[[usable protocols: NXM
+[[usable protocols: NXM,OXM
 chosen protocol: NXM-table_id
 NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(06), NXM_OF_TCP_SRC(007b) actions=FLOOD
 NXT_FLOW_MOD: ADD NXM_OF_IN_PORT(fffe), NXM_OF_ETH_SRC(000ae4256bb0), NXM_OF_VLAN_TCI_W(1009/1fff) actions=drop
@@ -377,6 +432,41 @@ NXM_OF_ETH_TYPE(0806) NXM_NX_ARP_THA(0002e30f80a4)
 NXM_OF_ETH_TYPE(0800) NXM_NX_ARP_THA(0002e30f80a4)
 NXM_NX_ARP_THA(0002e30f80a4)
 
+# RARP opcode
+NXM_OF_ETH_TYPE(8035) NXM_OF_ARP_OP(0003)
+NXM_OF_ETH_TYPE(8035) NXM_OF_ARP_OP(1111)
+NXM_OF_ETH_TYPE(0000) NXM_OF_ARP_OP(0003)
+NXM_OF_ARP_OP(0003)
+NXM_OF_ETH_TYPE(8035) NXM_OF_ARP_OP(0003) NXM_OF_ARP_OP(0003)
+
+# RARP source protocol address
+NXM_OF_ETH_TYPE(8035) NXM_OF_ARP_SPA(ac100014)
+NXM_OF_ETH_TYPE(8035) NXM_OF_ARP_SPA_W(C0a81234/FFFFFF00)
+NXM_OF_ETH_TYPE(8035) NXM_OF_ARP_SPA_W(C0a81234/aaaaaa00)
+NXM_OF_ETH_TYPE(8035) NXM_OF_ARP_SPA_W(C0a81234/ffffffff)
+NXM_OF_ETH_TYPE(8035) NXM_OF_ARP_SPA_W(C0a81234/00000000)
+NXM_OF_ETH_TYPE(0800) NXM_OF_ARP_SPA(ac100014)
+NXM_OF_ARP_SPA_W(C0D8fedc/FFFF0000)
+
+# RARP destination protocol address
+NXM_OF_ETH_TYPE(8035) NXM_OF_ARP_TPA(ac100014)
+NXM_OF_ETH_TYPE(8035) NXM_OF_ARP_TPA_W(C0a812fe/FFFFFF00)
+NXM_OF_ETH_TYPE(8035) NXM_OF_ARP_TPA_W(C0a81234/77777777)
+NXM_OF_ETH_TYPE(8035) NXM_OF_ARP_TPA_W(C0a81234/ffffffff)
+NXM_OF_ETH_TYPE(8035) NXM_OF_ARP_TPA_W(C0a81234/00000000)
+NXM_OF_ETH_TYPE(0800) NXM_OF_ARP_TPA(ac100014)
+NXM_OF_ARP_TPA_W(C0D80000/FFFF0000)
+
+# RARP source hardware address
+NXM_OF_ETH_TYPE(8035) NXM_NX_ARP_SHA(0002e30f80a4)
+NXM_OF_ETH_TYPE(0800) NXM_NX_ARP_SHA(0002e30f80a4)
+NXM_NX_ARP_SHA(0002e30f80a4)
+
+# RARP destination hardware address
+NXM_OF_ETH_TYPE(8035) NXM_NX_ARP_THA(0002e30f80a4)
+NXM_OF_ETH_TYPE(0800) NXM_NX_ARP_THA(0002e30f80a4)
+NXM_NX_ARP_THA(0002e30f80a4)
+
 # IPv6 source
 NXM_OF_ETH_TYPE(86dd) NXM_NX_IPV6_SRC(20010db83c4d00010002000300040005)
 NXM_OF_ETH_TYPE(0800) NXM_NX_IPV6_SRC(20010db83c4d00010002000300040005)
@@ -479,7 +569,7 @@ NXM_NX_REG0_W(a0e0d050/00000000)
 00011e04(12345678)
 00011f08(12345678/12345678)
 ])
-AT_CHECK([ovs-ofctl --strict parse-nx-match < nx-match.txt], [0], [dnl
+AT_CHECK([ovs-ofctl -vPATTERN:'console:%c|%p|%m' --strict parse-nx-match < nx-match.txt], [0], [dnl
 <any>
 
 # in port
@@ -629,6 +719,41 @@ NXM_OF_ETH_TYPE(0806), NXM_NX_ARP_THA(0002e30f80a4)
 nx_pull_match() returned error OFPBMC_BAD_PREREQ
 nx_pull_match() returned error OFPBMC_BAD_PREREQ
 
+# RARP opcode
+NXM_OF_ETH_TYPE(8035), NXM_OF_ARP_OP(0003)
+nx_pull_match() returned error OFPBMC_BAD_VALUE
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_DUP_FIELD
+
+# RARP source protocol address
+NXM_OF_ETH_TYPE(8035), NXM_OF_ARP_SPA(ac100014)
+NXM_OF_ETH_TYPE(8035), NXM_OF_ARP_SPA_W(c0a81200/ffffff00)
+NXM_OF_ETH_TYPE(8035), NXM_OF_ARP_SPA_W(80a80200/aaaaaa00)
+NXM_OF_ETH_TYPE(8035), NXM_OF_ARP_SPA(c0a81234)
+NXM_OF_ETH_TYPE(8035)
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+
+# RARP destination protocol address
+NXM_OF_ETH_TYPE(8035), NXM_OF_ARP_TPA(ac100014)
+NXM_OF_ETH_TYPE(8035), NXM_OF_ARP_TPA_W(c0a81200/ffffff00)
+NXM_OF_ETH_TYPE(8035), NXM_OF_ARP_TPA_W(40201234/77777777)
+NXM_OF_ETH_TYPE(8035), NXM_OF_ARP_TPA(c0a81234)
+NXM_OF_ETH_TYPE(8035)
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+
+# RARP source hardware address
+NXM_OF_ETH_TYPE(8035), NXM_NX_ARP_SHA(0002e30f80a4)
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+
+# RARP destination hardware address
+NXM_OF_ETH_TYPE(8035), NXM_NX_ARP_THA(0002e30f80a4)
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+
 # IPv6 source
 NXM_OF_ETH_TYPE(86dd), NXM_NX_IPV6_SRC(20010db83c4d00010002000300040005)
 nx_pull_match() returned error OFPBMC_BAD_PREREQ
@@ -730,13 +855,22 @@ NXM_NX_REG0(12345678)
 NXM_NX_REG0_W(12345678/12345678)
 nx_pull_match() returned error OFPBMC_BAD_FIELD
 nx_pull_match() returned error OFPBMC_BAD_FIELD
+], [stderr])
+
+# Check that at least the first warning made it.  (It's rate-limited
+# so a variable number could show up, especially under valgrind etc.)
+AT_CHECK([grep 'has 1-bits in value' stderr | sed 1q], [0], [dnl
+nx_match|WARN|NXM/OXM entry NXM_OF_ETH_DST_W(ffffffffffff/010000000000) has 1-bits in value for bits wildcarded by the mask.  (Future versions of OVS may report this as an OpenFlow error.)
 ])
+
+# Check that there wasn't any other stderr output.
+AT_CHECK([grep -v 'has 1-bits in value' stderr], [1])
 AT_CLEANUP
 
 AT_SETUP([ovs-ofctl parse-ofp10-match])
 AT_KEYWORDS([OF1.0])
 AT_DATA([test-data], [dnl
-# in_port=65534
+# in_port=LOCAL
 003820fe fffe xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx xxxx xx xx xxxx dnl
 xxxxxxxx xxxxxxxx xxxx xxxx
 
@@ -943,7 +1077,7 @@ AT_CLEANUP
 AT_SETUP([ovs-ofctl parse-ofp11-match])
 AT_KEYWORDS([OF1.1])
 AT_DATA([test-data], [dnl
-# in_port=65534
+# in_port=LOCAL
 0000 0058 fffffffe 000003fe dnl
 000000000000ffffffffffff 000000000000ffffffffffff dnl
 0000 00 00 0000 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl
@@ -1480,7 +1614,8 @@ OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(3a) OXM_OF_ICMPV6_TYPE(88) OXM_OF_IPV6_ND_
 # Invalid field number.
 01020304(1111/2222)
 ])
-AT_CHECK([ovs-ofctl --strict parse-oxm < oxm.txt], [0], [dnl
+AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' --strict parse-oxm < oxm.txt],
+  [0], [dnl
 <any>
 
 # in port
@@ -1674,7 +1809,16 @@ nx_pull_match() returned error OFPBMC_BAD_PREREQ
 
 # Invalid field number.
 nx_pull_match() returned error OFPBMC_BAD_FIELD
+], [stderr])
+
+# Check that at least the first warning made it.  (It's rate-limited
+# so a variable number could show up, especially under valgrind etc.)
+AT_CHECK([grep 'has 1-bits in value' stderr | sed 1q], [0], [dnl
+nx_match|WARN|NXM/OXM entry OXM_OF_METADATA_W(1234567890abcdef/ffff0000ffff0000) has 1-bits in value for bits wildcarded by the mask.  (Future versions of OVS may report this as an OpenFlow error.)
 ])
+
+# Check that there wasn't any other stderr output.
+AT_CHECK([grep -v 'has 1-bits in value' stderr], [1])
 AT_CLEANUP
 
 AT_SETUP([ovs-ofctl parse-oxm loose])
@@ -1793,10 +1937,10 @@ dnl Check that "-F openflow10" rejects a flow_mod with unsupported features,
 dnl such as tunnels and metadata.
 AT_SETUP([ovs-ofctl -F option and NXM features])
 AT_CHECK([ovs-ofctl -F openflow10 add-flow dummy tun_id=123,actions=drop],
-  [1], [], [ovs-ofctl: none of the usable flow formats (NXM) is among the allowed flow formats (OpenFlow10)
+  [1], [], [ovs-ofctl: none of the usable flow formats (NXM,OXM) is among the allowed flow formats (OpenFlow10)
 ])
 AT_CHECK([ovs-ofctl -F openflow10 add-flow dummy metadata=123,actions=drop],
-  [1], [], [ovs-ofctl: none of the usable flow formats (NXM) is among the allowed flow formats (OpenFlow10)
+  [1], [], [ovs-ofctl: none of the usable flow formats (NXM,OXM) is among the allowed flow formats (OpenFlow10)
 ])
 AT_CLEANUP
 
@@ -1831,7 +1975,7 @@ dnl can't be represented in OpenFlow 1.0.
 AT_SETUP([ovs-ofctl dump-flows rejects bad -F option])
 OVS_VSWITCHD_START
 AT_CHECK([ovs-ofctl -F openflow10 dump-flows unix:br0.mgmt reg0=0xabcdef], [1], [],
-  [ovs-ofctl: none of the usable flow formats (NXM) is among the allowed flow formats (OpenFlow10)
+  [ovs-ofctl: none of the usable flow formats (NXM,OXM) is among the allowed flow formats (OpenFlow10)
 ])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
@@ -1986,3 +2130,24 @@ AT_CHECK([ovs-ofctl diff-flows flows.txt br0], [2], [dnl
 ])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
+
+AT_SETUP([ovs-ofctl -F and -O interaction])
+AT_CHECK([ovs-ofctl -F oxm -O openflow10], [1], [],
+  [ovs-ofctl: None of the enabled OpenFlow versions (OpenFlow10) supports any of the enabled flow formats (OXM).  (Use -O to enable additional OpenFlow versions or -F to enable additional flow formats.)
+])
+AT_CHECK([ovs-ofctl -F oxm -O openflow11], [1], [],
+  [ovs-ofctl: None of the enabled OpenFlow versions (OpenFlow11) supports any of the enabled flow formats (OXM).  (Use -O to enable additional OpenFlow versions or -F to enable additional flow formats.)
+])
+AT_CHECK([ovs-ofctl -F oxm -O openflow10,openflow11], [1], [],
+  [ovs-ofctl: None of the enabled OpenFlow versions (OpenFlow10, OpenFlow11) supports any of the enabled flow formats (OXM).  (Use -O to enable additional OpenFlow versions or -F to enable additional flow formats.)
+])
+AT_CHECK([ovs-ofctl -F oxm -O openflow10,openflow12], [1], [],
+ [ovs-ofctl: missing command name; use --help for help
+])
+AT_CHECK([ovs-ofctl -F oxm -O openflow12], [1], [],
+  [ovs-ofctl: missing command name; use --help for help
+])
+AT_CHECK([ovs-ofctl -F oxm -O openflow13], [1], [],
+  [ovs-ofctl: missing command name; use --help for help
+])
+AT_CLEANUP
index e903619..6a1cc35 100644 (file)
@@ -15,7 +15,7 @@ dnl RUN_OVS_VSCTL(COMMAND, ...)
 dnl
 dnl Executes each ovs-vsctl COMMAND.
 m4_define([RUN_OVS_VSCTL],
-  [m4_foreach([command], [$@], [ovs-vsctl --timeout=5 --no-wait -vreconnect:emer --db=unix:socket -- command
+  [m4_foreach([command], [$@], [ovs-vsctl --timeout=5 --no-wait -vreconnect:emer --db=unix:socket command
 ])])
 m4_define([RUN_OVS_VSCTL_ONELINE],
   [m4_foreach([command], [$@], [ovs-vsctl --timeout=5 --no-wait -vreconnect:emer --db=unix:socket --oneline -- command
@@ -378,7 +378,7 @@ AT_SETUP([controllers])
 AT_KEYWORDS([controller ovs-vsctl])
 OVS_VSCTL_SETUP
 AT_CHECK([RUN_OVS_VSCTL_TOGETHER(
-  [add-br br0], 
+  [add-br br0],
 
   [get-controller br0],
   [set-controller br0 tcp:4.5.6.7],
@@ -439,6 +439,22 @@ CHECK_IFACES([xapi1], [eth0.$1])
 OVS_VSCTL_CLEANUP
 AT_CLEANUP
 
+AT_SETUP([list bridges -- real and fake (VLAN $1)])
+AT_KEYWORDS([ovs-vsctl fake-bridge])
+OVS_VSCTL_SETUP
+OVS_VSCTL_SETUP_SIMPLE_FAKE_CONF([$1])
+AT_CHECK([RUN_OVS_VSCTL_ONELINE([-- list-br])], [0],
+  [xapi1\nxenbr0
+], [], [OVS_VSCTL_CLEANUP])
+AT_CHECK([RUN_OVS_VSCTL_ONELINE([-- --real list-br])], [0],
+  [xenbr0
+], [], [OVS_VSCTL_CLEANUP])
+AT_CHECK([RUN_OVS_VSCTL_ONELINE([-- --fake list-br])], [0],
+  [xapi1
+], [], [OVS_VSCTL_CLEANUP])
+OVS_VSCTL_CLEANUP
+AT_CLEANUP
+
 AT_SETUP([simple fake bridge + del-br fake bridge (VLAN $1)])
 AT_KEYWORDS([ovs-vsctl fake-bridge])
 OVS_VSCTL_SETUP
@@ -568,7 +584,7 @@ AT_CHECK(
                           [set o . bridges=@br0])],
   [0], [stdout], [], [OVS_VSCTL_CLEANUP])
 cp stdout out1
-AT_CHECK([RUN_OVS_VSCTL([list b], [get b br0 _uuid])], 
+AT_CHECK([RUN_OVS_VSCTL([list b], [get b br0 _uuid])],
   [0], [stdout], [], [OVS_VSCTL_CLEANUP])
 cp stdout out2
 AT_CHECK([perl $srcdir/uuidfilt.pl out1 out2], [0], 
@@ -587,6 +603,7 @@ name                : "br0"
 netflow             : []
 other_config        : {}
 ports               : []
+protocols           : []
 sflow               : []
 status              : {}
 stp_enable          : false
@@ -624,12 +641,12 @@ AT_CHECK(
       'other_config:datapath_id="0123456789ab"' \
       'other_config:hwaddr="00:11:22:33:44:55"' \
       'external-ids={"uuids"="9c45f225-a7cf-439d-976d-83db6271fda1"}' -- \
-     add bridge br0 external_ids '"roles"="local; remote; cloud"'])], 
+     add bridge br0 external_ids '"roles"="local; remote; cloud"'])],
   [0], [], [], [OVS_VSCTL_CLEANUP])
-AT_CHECK([RUN_OVS_VSCTL_ONELINE([get bridge br0 other_config external-ids])], 
+AT_CHECK([RUN_OVS_VSCTL_ONELINE([get bridge br0 other_config external-ids])],
   [0], [{datapath_id="0123456789ab", hwaddr="00:11:22:33:44:55"}\n{roles="local; remote; cloud", uuids="9c45f225-a7cf-439d-976d-83db6271fda1"}
 ], [], [OVS_VSCTL_CLEANUP])
-AT_CHECK([RUN_OVS_VSCTL([get bridge br0 other_config:hwaddr -- --if-exists get bridge br0 other-config:nonexistent])], 
+AT_CHECK([RUN_OVS_VSCTL([get bridge br0 other_config:hwaddr -- --if-exists get bridge br0 other-config:nonexistent])],
   [0], ["00:11:22:33:44:55"
 
 ], [], [OVS_VSCTL_CLEANUP])
@@ -655,6 +672,17 @@ AT_CLEANUP
 AT_SETUP([database commands -- negative checks])
 AT_KEYWORDS([ovs-vsctl])
 OVS_VSCTL_SETUP
+
+AT_CHECK([ovs-vsctl --may-exist],
+  [1], [ignore], [ovs-vsctl: missing command name (use --help for help)
+], [OVS_VSCTL_CLEANUP])
+AT_CHECK([ovs-vsctl --may-exist --],
+  [1], [ignore], [ovs-vsctl: missing command name (use --help for help)
+], [OVS_VSCTL_CLEANUP])
+AT_CHECK([ovs-vsctl -- --may-exist],
+  [1], [ignore], [ovs-vsctl: missing command name (use --help for help)
+], [OVS_VSCTL_CLEANUP])
+
 AT_CHECK([RUN_OVS_VSCTL([add-br br0])],
   [0], [ignore], [], [OVS_VSCTL_CLEANUP])
 AT_CHECK([RUN_OVS_VSCTL([add-br br1])], 
@@ -1042,6 +1070,7 @@ name                : "br0"
 netflow             : []
 other_config        : {}
 ports               : []
+protocols           : []
 sflow               : []
 status              : {}
 stp_enable          : false
index 79ca29c..b1461ff 100644 (file)
@@ -95,6 +95,15 @@ test_rule_from_cls_rule(const struct cls_rule *rule)
     return rule ? CONTAINER_OF(rule, struct test_rule, cls_rule) : NULL;
 }
 
+static void
+test_rule_destroy(struct test_rule *rule)
+{
+    if (rule) {
+        cls_rule_destroy(&rule->cls_rule);
+        free(rule);
+    }
+}
+
 static struct test_rule *make_rule(int wc_fields, unsigned int priority,
                                    int value_pat);
 static void free_rule(struct test_rule *);
@@ -122,7 +131,7 @@ tcls_destroy(struct tcls *tcls)
         size_t i;
 
         for (i = 0; i < tcls->n_rules; i++) {
-            free(tcls->rules[i]);
+            test_rule_destroy(tcls->rules[i]);
         }
         free(tcls->rules);
     }
@@ -172,9 +181,11 @@ tcls_remove(struct tcls *cls, const struct test_rule *rule)
     for (i = 0; i < cls->n_rules; i++) {
         struct test_rule *pos = cls->rules[i];
         if (pos == rule) {
-            free(pos);
+            test_rule_destroy(pos);
+
             memmove(&cls->rules[i], &cls->rules[i + 1],
                     sizeof *cls->rules * (cls->n_rules - i - 1));
+
             cls->n_rules--;
             return;
         }
@@ -1221,6 +1232,8 @@ test_minimask_has_extra(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
 
         minimask_destroy(&minimask);
     }
+
+    minimask_destroy(&minicatchall);
 }
 
 static void
@@ -1260,6 +1273,8 @@ test_minimask_combine(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
         minimask_destroy(&minimask);
         minimask_destroy(&minimask2);
     }
+
+    minimask_destroy(&minicatchall);
 }
 \f
 static const struct command commands[] = {
index a40709a..b4dedee 100644 (file)
@@ -68,7 +68,7 @@ main(int argc OVS_UNUSED, char *argv[])
             ovs_fatal(retval, "error reading pcap file");
         }
 
-        flow_extract(packet, 0, NULL, 1, &flow);
+        flow_extract(packet, 0, 0, NULL, 1, &flow);
         match_init_exact(&match, &flow);
         ofputil_match_to_ofp10_match(&match, &extracted_match);
 
index c37eeaf..5147a20 100644 (file)
@@ -75,7 +75,7 @@ print_netflow(struct ofpbuf *buf)
         }
 
         printf("seq %"PRIu32": "IP_FMT" > "IP_FMT, ntohl(hdr->flow_seq),
-               IP_ARGS(&rec->src_addr), IP_ARGS(&rec->dst_addr));
+               IP_ARGS(rec->src_addr), IP_ARGS(rec->dst_addr));
 
         printf(", if %"PRIu16" > %"PRIu16,
                ntohs(rec->input), ntohs(rec->output));
@@ -137,7 +137,7 @@ print_netflow(struct ofpbuf *buf)
                ntohl(rec->init_time), ntohl(rec->used_time));
 
         if (rec->nexthop != htonl(0)) {
-            printf(", nexthop "IP_FMT, IP_ARGS(&rec->nexthop));
+            printf(", nexthop "IP_FMT, IP_ARGS(rec->nexthop));
         }
         if (rec->src_as != htons(0) || rec->dst_as != htons(0)) {
             printf(", AS %"PRIu16" > %"PRIu16,
index dd80766..5ed31a9 100644 (file)
@@ -70,7 +70,7 @@ parse_keys(void)
         /* Convert cls_rule back to odp_key. */
         ofpbuf_uninit(&odp_key);
         ofpbuf_init(&odp_key, 0);
-        odp_flow_key_from_flow(&odp_key, &flow);
+        odp_flow_key_from_flow(&odp_key, &flow, flow.in_port);
 
         if (odp_key.size > ODPUTIL_FLOW_KEY_BYTES) {
             printf ("too long: %zu > %d\n",
index 0b2b063..1e4e787 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -148,8 +148,7 @@ test_refuse_connection(int argc OVS_UNUSED, char *argv[])
     int error;
 
     fpv_create(type, &fpv);
-    CHECK_ERRNO(vconn_open(fpv.vconn_name, OFP10_VERSION, &vconn,
-                           DSCP_DEFAULT), 0);
+    CHECK_ERRNO(vconn_open(fpv.vconn_name, 0, DSCP_DEFAULT, &vconn), 0);
     fpv_close(&fpv);
     vconn_run(vconn);
 
@@ -186,8 +185,7 @@ test_accept_then_close(int argc OVS_UNUSED, char *argv[])
     int error;
 
     fpv_create(type, &fpv);
-    CHECK_ERRNO(vconn_open(fpv.vconn_name, OFP10_VERSION, &vconn,
-                           DSCP_DEFAULT), 0);
+    CHECK_ERRNO(vconn_open(fpv.vconn_name, 0, DSCP_DEFAULT, &vconn), 0);
     vconn_run(vconn);
     stream_close(fpv_accept(&fpv));
     fpv_close(&fpv);
@@ -219,8 +217,7 @@ test_read_hello(int argc OVS_UNUSED, char *argv[])
     int error;
 
     fpv_create(type, &fpv);
-    CHECK_ERRNO(vconn_open(fpv.vconn_name, OFP10_VERSION, &vconn,
-                           DSCP_DEFAULT), 0);
+    CHECK_ERRNO(vconn_open(fpv.vconn_name, 0, DSCP_DEFAULT, &vconn), 0);
     vconn_run(vconn);
     stream = fpv_accept(&fpv);
     fpv_destroy(&fpv);
@@ -273,8 +270,7 @@ test_send_hello(const char *type, const void *out, size_t out_size,
     size_t n_sent;
 
     fpv_create(type, &fpv);
-    CHECK_ERRNO(vconn_open(fpv.vconn_name, OFP10_VERSION, &vconn,
-                           DSCP_DEFAULT), 0);
+    CHECK_ERRNO(vconn_open(fpv.vconn_name, 0, DSCP_DEFAULT, &vconn), 0);
     vconn_run(vconn);
     stream = fpv_accept(&fpv);
     fpv_destroy(&fpv);
index 2b4ccdb..c330f2c 100644 (file)
@@ -77,6 +77,7 @@ m4_include([tests/daemon.at])
 m4_include([tests/daemon-py.at])
 m4_include([tests/ofp-actions.at])
 m4_include([tests/ofp-print.at])
+m4_include([tests/ofp-util.at])
 m4_include([tests/ofp-errors.at])
 m4_include([tests/ovs-ofctl.at])
 m4_include([tests/odp.at])
index cd422aa..88f905a 100644 (file)
@@ -1,8 +1,10 @@
 if HAVE_PYTHON
 sbin_SCRIPTS += utilities/bugtool/ovs-bugtool
 CLEANFILES += utilities/bugtool/ovs-bugtool
+
 man_MANS += utilities/bugtool/ovs-bugtool.8
-MAN_ROOTS += utilities/bugtool/ovs-bugtool.8
+MAN_ROOTS += utilities/bugtool/ovs-bugtool.8.in
+DISTCLEANFILES += utilities/bugtool/ovs-bugtool.8
 
 bugtool_plugins = \
        utilities/bugtool/plugins/kernel-info/openvswitch.xml \
@@ -49,5 +51,4 @@ endif
 EXTRA_DIST += \
        $(bugtool_plugins) \
        $(bugtool_scripts) \
-       utilities/bugtool/ovs-bugtool.8 \
        utilities/bugtool/ovs-bugtool.in
similarity index 95%
rename from utilities/bugtool/ovs-bugtool.8
rename to utilities/bugtool/ovs-bugtool.8.in
index 18a8395..a1e653b 100644 (file)
@@ -4,7 +4,7 @@
 .  ns
 .  IP "\\$1"
 ..
-.TH ovs\-bugtool 8 "June 2011" "Open vSwitch" "Open vSwitch Manual"
+.TH ovs\-bugtool 8 "@VERSION@" "Open vSwitch" "Open vSwitch Manual"
 .\" This program's name:
 .ds PN ovs\-bugtool
 .
index 2074e23..f91a3b3 100755 (executable)
@@ -135,7 +135,6 @@ KRB5_CONF = '/etc/krb5.conf'
 
 os.environ['PATH'] = '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:@pkgdatadir@/scripts'
 ARP = 'arp'
-BRCTL = 'brctl'
 CAT = 'cat'
 CHKCONFIG = 'chkconfig'
 DF = 'df'
@@ -584,7 +583,6 @@ exclude those logs from the archive.
     cmd_output(CAP_NETWORK_STATUS, [NETSTAT, '-an'])
     for dir in DHCP_LEASE_DIR:
         tree_output(CAP_NETWORK_STATUS, dir)
-    cmd_output(CAP_NETWORK_STATUS, [BRCTL, 'show'])
     cmd_output(CAP_NETWORK_STATUS, [IPTABLES, '-nL'])
     for p in os.listdir('/sys/class/net/'):
         try:
@@ -634,7 +632,7 @@ exclude those logs from the archive.
              [ 'crit.log', 'kern.log', 'daemon.log', 'user.log',
              'syslog', 'messages', 'secure', 'debug', 'dmesg', 'boot' ]]
             + [ OPENVSWITCH_LOG_DIR + x for x in
-                [ 'ovs-vswitchd.log', 'ovs-brcompatd.log', 'ovsdb-server.log',
+                [ 'ovs-vswitchd.log', 'ovsdb-server.log',
                   'ovs-xapi-sync.log', 'ovs-monitor-ipsec.log' ]])
     file_output(CAP_SYSTEM_LOGS, logs)
     file_output(CAP_SYSTEM_LOGS,
index 7edd182..df56309 100644 (file)
@@ -4,7 +4,7 @@
 .  ns
 .  IP "\\$1"
 ..
-.TH ovs\-appctl 8 "November 2009" "Open vSwitch" "Open vSwitch Manual"
+.TH ovs\-appctl 8 "@VERSION@" "Open vSwitch" "Open vSwitch Manual"
 .ds PN ovs\-appctl
 .
 .SH NAME
index 1183fe8..97e9d32 100644 (file)
@@ -1,6 +1,6 @@
 .\" -*- nroff -*-
 .so lib/ovs.tmac
-.TH ovs\-benchmark 1 "July 2011" "Open vSwitch" "Open vSwitch Manual"
+.TH ovs\-benchmark 1 "@VERSION@" "Open vSwitch" "Open vSwitch Manual"
 .
 .SH NAME
 ovs\-benchmark \- flow setup benchmark utility for Open vSwitch
index cc21c9c..162c585 100644 (file)
@@ -4,7 +4,7 @@
 .  ns
 .  IP "\\$1"
 ..
-.TH ovs\-controller 8 "March 2009" "Open vSwitch" "Open vSwitch Manual"
+.TH ovs\-controller 8 "@VERSION@" "Open vSwitch" "Open vSwitch Manual"
 .ds PN ovs\-controller
 .
 .SH NAME
@@ -140,6 +140,7 @@ Use this option more than once to add flows from multiple files.
 .so lib/vlog.man
 .so lib/unixctl.man
 .so lib/common.man
+.so so lib/ofp-version.man
 .
 .SH EXAMPLES
 .PP
index 07e300b..51765bd 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -29,6 +29,7 @@
 #include "daemon.h"
 #include "learning-switch.h"
 #include "ofp-parse.h"
+#include "ofp-version-opt.h"
 #include "ofpbuf.h"
 #include "openflow/openflow.h"
 #include "poll-loop.h"
@@ -41,6 +42,7 @@
 #include "vconn.h"
 #include "vlog.h"
 #include "socket-util.h"
+#include "ofp-util.h"
 
 VLOG_DEFINE_THIS_MODULE(controller);
 
@@ -114,7 +116,8 @@ main(int argc, char *argv[])
         const char *name = argv[i];
         struct vconn *vconn;
 
-        retval = vconn_open(name, OFP10_VERSION, &vconn, DSCP_DEFAULT);
+        retval = vconn_open(name, get_allowed_ofp_versions(), DSCP_DEFAULT,
+                            &vconn);
         if (!retval) {
             if (n_switches >= MAX_SWITCHES) {
                 ovs_fatal(0, "max %d switch connections", n_switches);
@@ -123,7 +126,8 @@ main(int argc, char *argv[])
             continue;
         } else if (retval == EAFNOSUPPORT) {
             struct pvconn *pvconn;
-            retval = pvconn_open(name, &pvconn, DSCP_DEFAULT);
+            retval = pvconn_open(name, get_allowed_ofp_versions(),
+                                 DSCP_DEFAULT, &pvconn);
             if (!retval) {
                 if (n_listeners >= MAX_LISTENERS) {
                     ovs_fatal(0, "max %d passive connections", n_listeners);
@@ -153,7 +157,7 @@ main(int argc, char *argv[])
         for (i = 0; i < n_listeners && n_switches < MAX_SWITCHES; ) {
             struct vconn *new_vconn;
 
-            retval = pvconn_accept(listeners[i], OFP10_VERSION, &new_vconn);
+            retval = pvconn_accept(listeners[i], &new_vconn);
             if (!retval || retval == EAGAIN) {
                 if (!retval) {
                     new_switch(&switches[n_switches++], new_vconn);
@@ -202,7 +206,7 @@ new_switch(struct switch_ *sw, struct vconn *vconn)
     struct lswitch_config cfg;
     struct rconn *rconn;
 
-    rconn = rconn_create(60, 0, DSCP_DEFAULT);
+    rconn = rconn_create(60, 0, DSCP_DEFAULT, get_allowed_ofp_versions());
     rconn_connect_unreliably(rconn, vconn, NULL);
 
     cfg.mode = (action_normal ? LSW_NORMAL
@@ -248,7 +252,8 @@ parse_options(int argc, char *argv[])
         OPT_WITH_FLOWS,
         OPT_UNIXCTL,
         VLOG_OPTION_ENUMS,
-        DAEMON_OPTION_ENUMS
+        DAEMON_OPTION_ENUMS,
+        OFP_VERSION_OPTION_ENUMS
     };
     static struct option long_options[] = {
         {"hub",         no_argument, NULL, 'H'},
@@ -262,8 +267,8 @@ parse_options(int argc, char *argv[])
         {"with-flows",  required_argument, NULL, OPT_WITH_FLOWS},
         {"unixctl",     required_argument, NULL, OPT_UNIXCTL},
         {"help",        no_argument, NULL, 'h'},
-        {"version",     no_argument, NULL, 'V'},
         DAEMON_LONG_OPTIONS,
+        OFP_VERSION_LONG_OPTIONS,
         VLOG_LONG_OPTIONS,
         STREAM_SSL_LONG_OPTIONS,
         {"peer-ca-cert", required_argument, NULL, OPT_PEER_CA_CERT},
@@ -333,11 +338,8 @@ parse_options(int argc, char *argv[])
         case 'h':
             usage();
 
-        case 'V':
-            ovs_print_version(OFP10_VERSION, OFP10_VERSION);
-            exit(EXIT_SUCCESS);
-
         VLOG_OPTION_HANDLERS
+        OFP_VERSION_OPTION_HANDLERS
         DAEMON_OPTION_HANDLERS
 
         STREAM_SSL_OPTION_HANDLERS
@@ -379,6 +381,7 @@ usage(void)
            program_name, program_name);
     vconn_usage(true, true, false);
     daemon_usage();
+    ofp_version_usage();
     vlog_usage();
     printf("\nOther options:\n"
            "  -H, --hub               act as hub instead of learning switch\n"
index 3092d46..7e1057a 100644 (file)
@@ -69,41 +69,30 @@ bridge module and tries loading the Open vSwitch kernel module again.
 (This is because the Open vSwitch kernel module cannot coexist with
 the Linux bridge module before 2.6.37.)
 .
-.IP 2.
-If \fB\-\-brcompat\fR was specified, loads the Open vSwitch bridge
-compatibility module.
-.
 .PP
 The \fBstart\fR command skips the following steps if
 \fBovsdb\-server\fR is already running:
-.IP 3.
+.IP 2.
 If the Open vSwitch database file does not exist, it creates it.
 If the database does exist, but it has an obsolete version, it
 upgrades it to the latest schema.
 .
-.IP 4.
+.IP 3.
 Starts \fBovsdb-server\fR.
 .
-.IP 5.
+.IP 4.
 Initializes a few values inside the database.
 .
-.IP 6.
+.IP 5.
 If the \fB\-\-delete\-bridges\fR option was used, deletes all of the
 bridges from the database.
 .
 .PP
 The \fBstart\fR command skips the following step if
 \fBovs\-vswitchd\fR is already running:
-.IP 7.
+.IP 6.
 Starts \fBovs\-vswitchd\fR.
 .
-.PP
-The \fBstart\fR command skips the following step if
-\fBovs\-brcompatd\fR is already running or if \fB\-\-brcompat\fR is
-not specified:
-.IP 8.
-Starts \fBovs\-brcompatd\fR.
-.
 .SS "Options"
 .PP
 Several command-line options influence the \fBstart\fR command's
@@ -179,13 +168,11 @@ suppresses that behavior.
 .
 .IP "\fB\-\-ovsdb\-server\-priority=\fIniceness\fR"
 .IQ "\fB\-\-ovs\-vswitchd\-priority=\fIniceness\fR"
-.IQ "\fB\-\-ovs\-brcompatd\-priority=\fIniceness\fR"
 Sets the \fBnice\fR(1) level used for each daemon.  All of them
 default to \fB\-10\fR.
 .
 .IP "\fB\-\-ovsdb\-server\-wrapper=\fIwrapper\fR"
 .IQ "\fB\-\-ovs\-vswitchd\-wrapper=\fIwrapper\fR"
-.IQ "\fB\-\-ovs\-brcompatd\-wrapper=\fIwrapper\fR"
 .
 Configures the specified daemon to run under \fIwrapper\fR, which is
 one of the following:
@@ -238,11 +225,6 @@ taken as relative to \fIdbdir\fR.
 .SH "The ``stop'' command"
 .
 .PP
-The \fBstop\fR command shuts down Open vSwitch.  It kills any running
-\fBovs\-brcompatd\fR, \fBovs\-vswitchd\fR, or \fBovsdb\-server\fR
-daemons and waits for them to terminate.
-.
-.PP
 The \fBstop\fR command does not unload the Open vSwitch kernel
 modules.
 .
@@ -263,16 +245,14 @@ individual bridge.
 .PP
 The \fBstatus\fR command checks whether the OVS daemons
 \fBovs-vswitchd\fR and \fBovsdb\-server\fR are running and prints
-messages with that information.  If \fB\-\-brcompat\fR is specified,
-it also checks for \fBovs\-brcompatd\fR.  It exits with status 0 if
+messages with that information.  It exits with status 0 if
 the daemons are running, 1 otherwise.
 .
 .SH "The ``version'' command"
 .
 .PP
 The \fBversion\fR command runs \fBovsdb\-server \-\-version\fR and
-\fBovs\-vswitchd \-\-version\fR.  If \fB\-\-brcompat\fR is specified,
-it also runs \fBovs\-brcompatd \-\-version\fR.
+\fBovs\-vswitchd \-\-version\fR.
 .
 .SH "The ``force\-reload\-kmod'' command"
 .
@@ -287,8 +267,7 @@ implemented by Open vSwitch.  The most common examples of these are
 bridge ``local ports''.
 .
 .IP 2.
-Saves the Openflow flows of each bridge and the kernel datapath
-configuration for each of the kernel datapaths.
+Saves the Openflow flows of each bridge.
 .
 .IP 3.
 Stops the Open vSwitch daemons, as if by a call to \fBovs\-ctl
@@ -305,9 +284,8 @@ compatibility module if it is loaded).
 .
 .IP 6.
 Starts OVS back up, as if by a call to \fBovs\-ctl start\fR.  This
-reloads the kernel module, restores the saved kernel datapath configuration,
-restarts the OVS daemons (including \fBovs\-brcompatd\fR, if \fB\-\-brcompat\fR
-is specified) and finally restores the saved Openflow flows.
+reloads the kernel module, restarts the OVS daemons and finally
+restores the saved Openflow flows.
 .
 .IP 7.
 Restores the kernel configuration state that was saved in step 4.
@@ -337,8 +315,7 @@ from other errors that may occur when running the \fBstart\fR command.
 .
 .PP
 By default the \fBload\-kmod\fR command attempts to load the
-openvswitch kernel module. If the \fB\-\-brcompat\fR option is
-specified then the brcompat kernel module is also loaded.
+openvswitch kernel module.
 .
 .SH "The ``enable\-protocol'' command"
 .
@@ -391,16 +368,6 @@ Prints a usage message and exits successfully.
 In addition to the options listed for each command above, this option
 controls the behavior of several of \fBovs\-ctl\fR's commands.
 .
-.IP "\fB\-\-brcompat\fR"
-By default, \fBovs\-ctl\fR does not load the Open vSwitch bridge
-compatibility module and does not start or check the status or report
-the version of the \fBovs\-brcompatd\fR daemon.  This option enables
-all of those behaviors.
-.
-.IP
-The \fBstop\fR command always stops \fBovs\-brcompatd\fR, if it is
-running, regardless of this option.
-.
 .SH "EXIT STATUS"
 .
 \fBovs\-ctl\fR exits with status 0 on success and nonzero on failure.
index e8b72ba..bce74a6 100755 (executable)
@@ -30,21 +30,13 @@ done
 ## start ##
 ## ----- ##
 
-restore_datapaths () {
-    [ -n "${script_datapaths}" ] && \
-        action "Restoring datapath configuration" "${script_datapaths}"
-}
-
 insert_openvswitch_mod_if_required () {
     # If openvswitch is already loaded then we're done.
     test -e /sys/module/openvswitch -o -e /sys/module/openvswitch_mod && \
      return 0
 
     # Load openvswitch.  If that's successful then we're done.
-    if action "Inserting openvswitch module" modprobe openvswitch; then
-        restore_datapaths
-        return 0
-    fi
+    action "Inserting openvswitch module" modprobe openvswitch && return 0
 
     # If the bridge module is loaded, then that might be blocking
     # openvswitch.  Try to unload it, if there are no bridges.
@@ -58,28 +50,10 @@ insert_openvswitch_mod_if_required () {
 
     # Try loading openvswitch again.
     action "Inserting openvswitch module" modprobe openvswitch
-    restore_datapaths
-}
-
-insert_brcompat_mod_if_required () {
-    if test -e /sys/module/bridge; then
-        log_warning_msg "bridge module is loaded, not loading brcompat"
-        return 1
-    fi
-    test -e /sys/module/brcompat -o -e /sys/module/brcompat_mod && return 0
-    action "Inserting brcompat module" modprobe brcompat
 }
 
 insert_mod_if_required () {
     insert_openvswitch_mod_if_required || return 1
-    if test X"$BRCOMPAT" = Xyes; then
-        if insert_brcompat_mod_if_required; then
-            :
-        else
-            log_warning_msg "could not load brcompat module, disabling bridge compatibility"
-            BRCOMPAT=no
-        fi
-    fi
 }
 
 ovs_vsctl () {
@@ -254,14 +228,6 @@ start_forwarding () {
            fi
            start_daemon "$OVS_VSWITCHD_PRIORITY" "$OVS_VSWITCHD_WRAPPER" "$@"
     fi
-
-    if daemon_is_running ovs-brcompatd; then
-           log_success_msg "ovs-brcompatd is already running"
-    elif test X"$BRCOMPAT" = Xyes; then
-        set ovs-brcompatd
-           set "$@" -vconsole:emer -vsyslog:err -vfile:info
-           start_daemon "$OVS_BRCOMPATD_PRIORITY" "$OVS_BRCOMPATD_WRAPPER" "$@"
-    fi
 }
 
 ## ---- ##
@@ -273,7 +239,6 @@ stop_ovsdb () {
 }
 
 stop_forwarding () {
-    stop_daemon ovs-brcompatd
     stop_daemon ovs-vswitchd
 }
 
@@ -303,7 +268,7 @@ internal_interfaces () {
 }
 
 save_flows () {
-   if set X `ovs_vsctl list-br`; then
+   if set X `ovs_vsctl -- --real list-br`; then
         shift
         if "$datadir/scripts/ovs-save" save-flows "$@" > "$script_flows"; then
             chmod +x "$script_flows"
@@ -319,11 +284,6 @@ save_interfaces () {
         > "${script_interfaces}"
 }
 
-save_datapaths () {
-    "$datadir/scripts/ovs-save" save-datapaths ${datapaths} \
-        > "${script_datapaths}"
-}
-
 restore_flows () {
     [ -n "${script_flows}" ] && \
         action "Restoring saved flows" "${script_flows}"
@@ -334,10 +294,8 @@ force_reload_kmod () {
     action "Detected internal interfaces: $ifaces" true
 
     script_interfaces=`mktemp`
-    script_datapaths=`mktemp`
     script_flows=`mktemp`
-    trap 'rm -f "${script_interfaces}" "${script_flows}" \
-        "${script_datapaths}"' 0 1 2 13 15
+    trap 'rm -f "${script_interfaces}" "${script_flows}" ' 0
 
     action "Saving flows" save_flows
 
@@ -357,25 +315,11 @@ force_reload_kmod () {
     fi
     chmod +x "$script_interfaces"
 
-    datapaths=`ovs-dpctl dump-dps`
-    if action "Saving datapath configuration" save_datapaths; then
-        chmod +x "${script_datapaths}"
-    else
-        log_warning_msg "Failed to save datapath configuration. The port\
-                         numbers may change after the restart"
-        script_datapaths=""
-    fi
-
-    for dp in ${datapaths}; do
+    for dp in `ovs-dpctl dump-dps`; do
         action "Removing datapath: $dp" ovs-dpctl del-dp "$dp"
     done
 
     # try both old and new names in case this is post upgrade
-    if test -e /sys/module/brcompat_mod; then
-        action "Removing brcompat module" rmmod brcompat_mod
-    elif test -e /sys/module/brcompat; then
-        action "Removing brcompat module" rmmod brcompat
-    fi
     if test -e /sys/module/openvswitch_mod; then
         action "Removing openvswitch module" rmmod openvswitch_mod
     elif test -e /sys/module/openvswitch; then
@@ -407,7 +351,7 @@ force_reload_kmod () {
 restart () {
     if daemon_is_running ovsdb-server && daemon_is_running ovs-vswitchd; then
         script_flows=`mktemp`
-        trap 'rm -f "${script_flows}"' 0 1 2 13 15
+        trap 'rm -f "${script_flows}"' 0
 
         action "Saving flows" save_flows
     fi
@@ -420,7 +364,8 @@ restart () {
     stop_forwarding
     start_forwarding
 
-    restore_flows
+    # Restore the saved flows. Do not return error if restore fails.
+    restore_flows || true
 }
 
 ## --------------- ##
@@ -477,17 +422,14 @@ set_defaults () {
     SYSTEM_ID=
 
     DELETE_BRIDGES=no
-    BRCOMPAT=no
 
     DAEMON_CWD=/
     FORCE_COREFILES=yes
     MLOCKALL=yes
     OVSDB_SERVER_PRIORITY=-10
     OVS_VSWITCHD_PRIORITY=-10
-    OVS_BRCOMPATD_PRIORITY=-10
     OVSDB_SERVER_WRAPPER=
     OVS_VSWITCHD_WRAPPER=
-    OVS_BRCOMPATD_WRAPPER=
 
     DB_FILE=$dbdir/conf.db
     DB_SOCK=$rundir/db.sock
@@ -553,7 +495,6 @@ Less important options for "start", "restart" and "force-reload-kmod":
   --no-mlockall                  do not lock all of ovs-vswitchd into memory
   --ovsdb-server-priority=NICE   set ovsdb-server's niceness (default: $OVSDB_SERVER_PRIORITY)
   --ovs-vswitchd-priority=NICE   set ovs-vswitchd's niceness (default: $OVS_VSWITCHD_PRIORITY)
-  --ovs-brcompatd-priority=NICE  set ovs-brcompatd's niceness (default: $OVS_BRCOMPATD_PRIORITY)
 
 Debugging options for "start", "restart" and "force-reload-kmod":
   --ovsdb-server-wrapper=WRAPPER
@@ -561,9 +502,6 @@ Debugging options for "start", "restart" and "force-reload-kmod":
   --ovs-vswitchd-wrapper=WRAPPER
      run specified daemon under WRAPPER (either 'valgrind' or 'strace')
 
-Options for "start", "restart", "force-reload-kmod", "load-kmod", "status", and "version":
-  --brcompat         enable Linux bridge compatibility module and daemon
-
 File location options:
   --db-file=FILE     database file name (default: $DB_FILE)
   --db-sock=SOCKET   JSON-RPC socket name (default: $DB_SOCK)
@@ -608,9 +546,6 @@ set_option () {
 
 daemons () {
     echo ovsdb-server ovs-vswitchd
-    if test X"$BRCOMPAT" = Xyes; then
-        echo ovs-brcompatd
-    fi
 }
 
 set_defaults
index 042bcd5..b1b2570 100644 (file)
@@ -3,7 +3,7 @@
 .  ns
 .  IP "\\$1"
 ..
-.TH ovs\-dpctl 8 "August 2009" "Open vSwitch" "Open vSwitch Manual"
+.TH ovs\-dpctl 8 "@VERSION@" "Open vSwitch" "Open vSwitch Manual"
 .ds PN ovs\-dpctl
 .
 .SH NAME
@@ -105,16 +105,21 @@ If one or more datapaths are specified, information on only those
 datapaths are displayed.  Otherwise, \fBovs\-dpctl\fR displays information
 about all configured datapaths.
 .
-.IP "\fBdump\-flows \fIdp\fR"
+.IP "\fBdump\-flows\fR [\fIdp\fR]"
 Prints to the console all flow entries in datapath \fIdp\fR's
-flow table.
+flow table.  If \fIdp\fR is not specified and exactly one datapath
+exists, the flows for that datapath will be printed.
 .IP
 This command is primarily useful for debugging Open vSwitch.  The flow
 table entries that it displays are not
 OpenFlow flow entries.  Instead, they are different and considerably
-simpler flows maintained by the Open vSwitch kernel module.
-.IP "\fBdel\-flows \fIdp\fR"
-Deletes all flow entries from datapath \fIdp\fR's flow table.
+simpler flows maintained by the Open vSwitch kernel module.  If you wish
+to see the OpenFlow flow entries, use \fBovs\-ofctl dump\-flows\fR.
+.
+.IP "\fBdel\-flows\fR [\fIdp\fR]"
+Deletes all flow entries from datapath \fIdp\fR's flow table.  If
+\fIdp\fR is not specified and exactly one datapath exists, the flows for
+that datapath will be deleted.
 .IP
 This command is primarily useful for debugging Open vSwitch.  As
 discussed in \fBdump\-flows\fR, these entries are
index 6224237..2e32604 100644 (file)
@@ -161,7 +161,9 @@ usage(void)
            "Interface table in ovs-vswitchd.conf.db(5) for an options list.\n",
            program_name, program_name);
     vlog_usage();
-    printf("\nOther options:\n"
+    printf("\nOptions for show:\n"
+           "  -s,  --statistics           print port statistics\n"
+           "\nOther options:\n"
            "  -t, --timeout=SECS          give up after SECS seconds\n"
            "  -h, --help                  display this help message\n"
            "  -V, --version               display version information\n");
@@ -196,6 +198,42 @@ static int if_up(const char *netdev_name)
     return retval;
 }
 
+/* Retrieve the name of the datapath if exactly one exists.  The caller
+ * is responsible for freeing the returned string.  If there is not one
+ * datapath, aborts with an error message. */
+static char *
+get_one_dp(void)
+{
+    struct sset types;
+    const char *type;
+    char *dp_name = NULL;
+    size_t count = 0;
+
+    sset_init(&types);
+    dp_enumerate_types(&types);
+    SSET_FOR_EACH (type, &types) {
+        struct sset names;
+
+        sset_init(&names);
+        if (!dp_enumerate_names(type, &names)) {
+            count += sset_count(&names);
+            if (!dp_name && count == 1) {
+                dp_name = xasprintf("%s@%s", type, SSET_FIRST(&names));
+            }
+        }
+        sset_destroy(&names);
+    }
+    sset_destroy(&types);
+
+    if (!count) {
+        ovs_fatal(0, "no datapaths exist");
+    } else if (count > 1) {
+        ovs_fatal(0, "multiple datapaths, specify one");
+    }
+
+    return dp_name;
+}
+
 static int
 parsed_dpif_open(const char *arg_, bool create, struct dpif **dpifp)
 {
@@ -248,7 +286,7 @@ dpctl_add_if(int argc OVS_UNUSED, char *argv[])
         char *save_ptr = NULL;
         struct netdev *netdev = NULL;
         struct smap args;
-        uint16_t port_no = UINT16_MAX;
+        uint32_t port_no = UINT32_MAX;
         char *option;
         int error;
 
@@ -416,7 +454,7 @@ next:
 }
 
 static bool
-get_port_number(struct dpif *dpif, const char *name, uint16_t *port)
+get_port_number(struct dpif *dpif, const char *name, uint32_t *port)
 {
     struct dpif_port dpif_port;
 
@@ -440,7 +478,7 @@ dpctl_del_if(int argc OVS_UNUSED, char *argv[])
     run(parsed_dpif_open(argv[1], false, &dpif), "opening datapath");
     for (i = 2; i < argc; i++) {
         const char *name = argv[i];
-        uint16_t port;
+        uint32_t port;
         int error;
 
         if (!name[strspn(name, "0123456789")]) {
@@ -677,7 +715,7 @@ dpctl_dump_dps(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
 }
 
 static void
-dpctl_dump_flows(int argc OVS_UNUSED, char *argv[])
+dpctl_dump_flows(int argc, char *argv[])
 {
     const struct dpif_flow_stats *stats;
     const struct nlattr *actions;
@@ -687,8 +725,11 @@ dpctl_dump_flows(int argc OVS_UNUSED, char *argv[])
     struct dpif *dpif;
     size_t key_len;
     struct ds ds;
+    char *name;
 
-    run(parsed_dpif_open(argv[1], false, &dpif), "opening datapath");
+    name = (argc == 2) ? xstrdup(argv[1]) : get_one_dp();
+    run(parsed_dpif_open(name, false, &dpif), "opening datapath");
+    free(name);
 
     ds_init(&ds);
     dpif_flow_dump_start(&dump, dpif);
@@ -708,11 +749,15 @@ dpctl_dump_flows(int argc OVS_UNUSED, char *argv[])
 }
 
 static void
-dpctl_del_flows(int argc OVS_UNUSED, char *argv[])
+dpctl_del_flows(int argc, char *argv[])
 {
     struct dpif *dpif;
+    char *name;
+
+    name = (argc == 2) ? xstrdup(argv[1]) : get_one_dp();
+    run(parsed_dpif_open(name, false, &dpif), "opening datapath");
+    free(name);
 
-    run(parsed_dpif_open(argv[1], false, &dpif), "opening datapath");
     run(dpif_flow_flush(dpif), "deleting all flows");
     dpif_close(dpif);
 }
@@ -951,8 +996,8 @@ static const struct command all_commands[] = {
     { "set-if", 2, INT_MAX, dpctl_set_if },
     { "dump-dps", 0, 0, dpctl_dump_dps },
     { "show", 0, INT_MAX, dpctl_show },
-    { "dump-flows", 1, 1, dpctl_dump_flows },
-    { "del-flows", 1, 1, dpctl_del_flows },
+    { "dump-flows", 0, 1, dpctl_dump_flows },
+    { "del-flows", 0, 1, dpctl_del_flows },
     { "help", 0, INT_MAX, dpctl_help },
 
     /* Undocumented commands for testing. */
index d3ce3cb..d6a3f94 100644 (file)
@@ -3,7 +3,7 @@
 .  ns
 .  IP "\\$1"
 ..
-.TH ovs\-l3ping 1 "June 2012" "Open vSwitch" "Open vSwitch Manual"
+.TH ovs\-l3ping 1 "@VERSION@" "Open vSwitch" "Open vSwitch Manual"
 .
 .SH NAME
 \fBovs\-l3ping\fR \- check network deployment for L3 tunneling
index 9ea0973..c48645a 100644 (file)
@@ -4,7 +4,7 @@
 .  ns
 .  IP "\\$1"
 ..
-.TH ovs\-ofctl 8 "January 2011" "Open vSwitch" "Open vSwitch Manual"
+.TH ovs\-ofctl 8 "@VERSION@" "Open vSwitch" "Open vSwitch Manual"
 .ds PN ovs\-ofctl
 .
 .SH NAME
@@ -20,6 +20,7 @@ The
 program is a command line tool for monitoring and administering
 OpenFlow switches.  It can also show the current state of an OpenFlow
 switch, including features, configuration, and table entries.
+It should work with any OpenFlow switch, not just Open vSwitch.
 .
 .SS "OpenFlow Switch Management Commands"
 .PP
@@ -392,8 +393,8 @@ flows not in normal form.
 The following field assignments describe how a flow matches a packet.
 If any of these assignments is omitted from the flow syntax, the field
 is treated as a wildcard; thus, if all of them are omitted, the
-resulting flow matches all packets.  The string \fB*\fR or \fBANY\fR
-may be specified to explicitly mark any of these fields as a wildcard.  
+resulting flow matches all packets.  The string \fB*\fR may be specified
+to explicitly mark any of these fields as a wildcard.
 (\fB*\fR should be quoted to protect it from shell expansion.)
 .
 .IP \fBin_port=\fIport\fR
@@ -468,8 +469,12 @@ When \fBdl_type=0x0806\fR or \fBarp\fR is specified, matches the
 \fBar_spa\fR or \fBar_tpa\fR field, respectively, in ARP packets for
 IPv4 and Ethernet.
 .IP
-When \fBdl_type\fR is wildcarded or set to a value other than 0x0800
-or 0x0806, the values of \fBnw_src\fR and \fBnw_dst\fR are ignored
+When \fBdl_type=0x8035\fR or \fBrarp\fR is specified, matches the
+\fBar_spa\fR or \fBar_tpa\fR field, respectively, in RARP packets for
+IPv4 and Ethernet.
+.IP
+When \fBdl_type\fR is wildcarded or set to a value other than 0x0800,
+0x0806, or 0x8035, the values of \fBnw_src\fR and \fBnw_dst\fR are ignored
 (see \fBFlow Syntax\fR above).
 .
 .IP \fBnw_proto=\fIproto\fR
@@ -488,9 +493,13 @@ When \fBarp\fR or \fBdl_type=0x0806\fR is specified, matches the lower
 8 bits of the ARP opcode.  ARP opcodes greater than 255 are treated as
 0.
 .IP
+When \fBrarp\fR or \fBdl_type=0x8035\fR is specified, matches the lower
+8 bits of the ARP opcode.  ARP opcodes greater than 255 are treated as
+0.
+.IP
 When \fBdl_type\fR is wildcarded or set to a value other than 0x0800,
-0x0806, or 0x86dd, the value of \fBnw_proto\fR is ignored (see \fBFlow
-Syntax\fR above).
+0x0806, 0x8035 or 0x86dd, the value of \fBnw_proto\fR is ignored (see
+\fBFlow Syntax\fR above).
 .
 .IP \fBnw_tos=\fItos\fR
 Matches IP ToS/DSCP or IPv6 traffic class field \fItos\fR, which is
@@ -645,6 +654,9 @@ Same as \fBdl_type=0x0800,nw_proto=17\fR.
 .IP \fBarp\fR
 Same as \fBdl_type=0x0806\fR.
 .
+.IP \fBrarp\fR
+Same as \fBdl_type=0x8035\fR.
+.
 .PP
 The following field assignments require support for the NXM (Nicira
 Extended Match) extension to OpenFlow.  When one of these is specified,
@@ -712,9 +724,9 @@ command, above, for more details.
 .
 .IP \fBarp_sha=\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fR
 .IQ \fBarp_tha=\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fR
-When \fBdl_type\fR specifies ARP, \fBarp_sha\fR and \fBarp_tha\fR match
-the source and target hardware address, respectively.  An address is
-specified as 6 pairs of hexadecimal digits delimited by colons.
+When \fBdl_type\fR specifies either ARP or RARP, \fBarp_sha\fR and
+\fBarp_tha\fR match the source and target hardware address, respectively.  An
+address is specified as 6 pairs of hexadecimal digits delimited by colons.
 .
 .IP \fBipv6_src=\fIipv6\fR[\fB/\fInetmask\fR]
 .IQ \fBipv6_dst=\fIipv6\fR[\fB/\fInetmask\fR]
@@ -1332,6 +1344,8 @@ passing through the flow.
 \fB\-\-strict\fR
 Uses strict matching when running flow modification commands.
 .
+.so lib/ofp-version.man
+.
 .IP "\fB\-F \fIformat\fR[\fB,\fIformat\fR...]"
 .IQ "\fB\-\-flow\-format=\fIformat\fR[\fB,\fIformat\fR...]"
 \fBovs\-ofctl\fR supports the following individual flow formats, any
@@ -1356,6 +1370,11 @@ registers.  Open vSwitch 1.1 and later supports this flow format.
 This combines Nicira Extended match with the ability to place a flow
 in a specific table.  Open vSwitch 1.2 and later supports this flow
 format.
+.
+.IP "\fBOXM-OpenFlow12\fR"
+.IQ "\fBOXM-OpenFlow13\fR"
+These are the standard OXM (OpenFlow Extensible Match) flow format in
+OpenFlow 1.2 and 1.3, respectively.
 .RE
 .
 .IP
@@ -1368,6 +1387,8 @@ Any supported flow format.
 \fBOpenFlow10\-table_id\fR or \fBOpenFlow10+table_id\fR.
 .IP "\fBNXM\fR"
 \fBNXM\-table_id\fR or \fBNXM+table_id\fR.
+.IP "\fBOXM\fR"
+\fBOXM-OpenFlow12\fR or \fBOXM-OpenFlow13\fR.
 .RE
 .
 .IP
index a67a554..239f317 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -44,6 +44,7 @@
 #include "ofp-parse.h"
 #include "ofp-print.h"
 #include "ofp-util.h"
+#include "ofp-version-opt.h"
 #include "ofpbuf.h"
 #include "ofproto/ofproto.h"
 #include "openflow/nicira-ext.h"
@@ -52,6 +53,7 @@
 #include "poll-loop.h"
 #include "random.h"
 #include "stream-ssl.h"
+#include "socket-util.h"
 #include "timeval.h"
 #include "unixctl.h"
 #include "util.h"
@@ -146,6 +148,7 @@ parse_options(int argc, char *argv[])
         OPT_SORT,
         OPT_RSORT,
         DAEMON_OPTION_ENUMS,
+        OFP_VERSION_OPTION_ENUMS,
         VLOG_OPTION_ENUMS
     };
     static struct option long_options[] = {
@@ -159,13 +162,15 @@ parse_options(int argc, char *argv[])
         {"sort", optional_argument, NULL, OPT_SORT},
         {"rsort", optional_argument, NULL, OPT_RSORT},
         {"help", no_argument, NULL, 'h'},
-        {"version", no_argument, NULL, 'V'},
         DAEMON_LONG_OPTIONS,
+        OFP_VERSION_LONG_OPTIONS,
         VLOG_LONG_OPTIONS,
         STREAM_SSL_LONG_OPTIONS,
         {NULL, 0, NULL, 0},
     };
     char *short_options = long_options_to_short_options(long_options);
+    uint32_t versions;
+    enum ofputil_protocol version_protocols;
 
     for (;;) {
         unsigned long int timeout;
@@ -209,10 +214,6 @@ parse_options(int argc, char *argv[])
         case 'h':
             usage();
 
-        case 'V':
-            ovs_print_version(OFP10_VERSION, OFP10_VERSION);
-            exit(EXIT_SUCCESS);
-
         case OPT_STRICT:
             strict = true;
             break;
@@ -234,6 +235,7 @@ parse_options(int argc, char *argv[])
             break;
 
         DAEMON_OPTION_HANDLERS
+        OFP_VERSION_OPTION_HANDLERS
         VLOG_OPTION_HANDLERS
         STREAM_SSL_OPTION_HANDLERS
 
@@ -251,6 +253,22 @@ parse_options(int argc, char *argv[])
     }
 
     free(short_options);
+
+    versions = get_allowed_ofp_versions();
+    version_protocols = ofputil_protocols_from_version_bitmap(versions);
+    if (!(allowed_protocols & version_protocols)) {
+        char *protocols = ofputil_protocols_to_string(allowed_protocols);
+        struct ds version_s = DS_EMPTY_INITIALIZER;
+
+        ofputil_format_version_bitmap_names(&version_s, versions);
+        ovs_fatal(0, "None of the enabled OpenFlow versions (%s) supports "
+                  "any of the enabled flow formats (%s).  (Use -O to enable "
+                  "additional OpenFlow versions or -F to enable additional "
+                  "flow formats.)", ds_cstr(&version_s), protocols);
+    }
+    allowed_protocols &= version_protocols;
+    mask_allowed_ofp_versions(ofputil_protocols_to_version_bitmap(
+                                  allowed_protocols));
 }
 
 static void
@@ -291,6 +309,7 @@ usage(void)
            program_name, program_name);
     vconn_usage(true, false, false);
     daemon_usage();
+    ofp_version_usage();
     vlog_usage();
     printf("\nOther options:\n"
            "  --strict                    use strict match for flow commands\n"
@@ -319,7 +338,8 @@ ofctl_exit(struct unixctl_conn *conn, int argc OVS_UNUSED,
 static void run(int retval, const char *message, ...)
     PRINTF_FORMAT(2, 3);
 
-static void run(int retval, const char *message, ...)
+static void
+run(int retval, const char *message, ...)
 {
     if (retval) {
         va_list args;
@@ -331,14 +351,21 @@ static void run(int retval, const char *message, ...)
 \f
 /* Generic commands. */
 
-static void
+static int
 open_vconn_socket(const char *name, struct vconn **vconnp)
 {
     char *vconn_name = xasprintf("unix:%s", name);
-    VLOG_DBG("connecting to %s", vconn_name);
-    run(vconn_open_block(vconn_name, OFP10_VERSION, vconnp),
-        "connecting to %s", vconn_name);
+    int error;
+
+    error = vconn_open(vconn_name, get_allowed_ofp_versions(), DSCP_DEFAULT,
+                       vconnp);
+    if (error && error != ENOENT) {
+        ovs_fatal(0, "%s: failed to open socket (%s)", name,
+                  strerror(error));
+    }
     free(vconn_name);
+
+    return error;
 }
 
 static enum ofputil_protocol
@@ -349,7 +376,7 @@ open_vconn__(const char *name, const char *default_suffix,
     enum ofputil_protocol protocol;
     char *bridge_path;
     int ofp_version;
-    struct stat s;
+    int error;
 
     bridge_path = xasprintf("%s/%s.%s", ovs_rundir(), name, default_suffix);
 
@@ -360,18 +387,15 @@ open_vconn__(const char *name, const char *default_suffix,
     free(datapath_type);
 
     if (strchr(name, ':')) {
-        run(vconn_open_block(name, OFP10_VERSION, vconnp),
+        run(vconn_open_block(name, get_allowed_ofp_versions(), DSCP_DEFAULT,
+                             vconnp),
             "connecting to %s", name);
-    } else if (!stat(name, &s) && S_ISSOCK(s.st_mode)) {
-        open_vconn_socket(name, vconnp);
-    } else if (!stat(bridge_path, &s) && S_ISSOCK(s.st_mode)) {
-        open_vconn_socket(bridge_path, vconnp);
-    } else if (!stat(socket_name, &s)) {
-        if (!S_ISSOCK(s.st_mode)) {
-            ovs_fatal(0, "cannot connect to %s: %s is not a socket",
-                      name, socket_name);
-        }
-        open_vconn_socket(socket_name, vconnp);
+    } else if (!open_vconn_socket(name, vconnp)) {
+        /* Fall Through. */
+    } else if (!open_vconn_socket(bridge_path, vconnp)) {
+        /* Fall Through. */
+    } else if (!open_vconn_socket(socket_name, vconnp)) {
+        /* Fall Through. */
     } else {
         ovs_fatal(0, "%s is not a bridge or a socket", name);
     }
@@ -379,6 +403,13 @@ open_vconn__(const char *name, const char *default_suffix,
     free(bridge_path);
     free(socket_name);
 
+    VLOG_DBG("connecting to %s", vconn_get_name(*vconnp));
+    error = vconn_connect_block(*vconnp);
+    if (error) {
+        ovs_fatal(0, "%s: failed to connect to socket (%s)", name,
+                  strerror(error));
+    }
+
     ofp_version = vconn_get_version(*vconnp);
     protocol = ofputil_protocol_from_ofp_version(ofp_version);
     if (!protocol) {
@@ -402,25 +433,27 @@ send_openflow_buffer(struct vconn *vconn, struct ofpbuf *buffer)
 }
 
 static void
-dump_transaction(const char *vconn_name, struct ofpbuf *request)
+dump_transaction(struct vconn *vconn, struct ofpbuf *request)
 {
-    struct vconn *vconn;
     struct ofpbuf *reply;
 
     ofpmsg_update_length(request);
-    open_vconn(vconn_name, &vconn);
-    run(vconn_transact(vconn, request, &reply), "talking to %s", vconn_name);
+    run(vconn_transact(vconn, request, &reply), "talking to %s",
+        vconn_get_name(vconn));
     ofp_print(stdout, reply->data, reply->size, verbosity + 1);
     ofpbuf_delete(reply);
-    vconn_close(vconn);
 }
 
 static void
 dump_trivial_transaction(const char *vconn_name, enum ofpraw raw)
 {
     struct ofpbuf *request;
-    request = ofpraw_alloc(raw, OFP10_VERSION, 0);
-    dump_transaction(vconn_name, request);
+    struct vconn *vconn;
+
+    open_vconn(vconn_name, &vconn);
+    request = ofpraw_alloc(raw, vconn_get_version(vconn), 0);
+    dump_transaction(vconn, request);
+    vconn_close(vconn);
 }
 
 static void
@@ -524,7 +557,8 @@ fetch_switch_config(struct vconn *vconn, struct ofp_switch_config *config_)
     struct ofpbuf *reply;
     enum ofptype type;
 
-    request = ofpraw_alloc(OFPRAW_OFPT_GET_CONFIG_REQUEST, OFP10_VERSION, 0);
+    request = ofpraw_alloc(OFPRAW_OFPT_GET_CONFIG_REQUEST,
+                           vconn_get_version(vconn), 0);
     run(vconn_transact(vconn, request, &reply),
         "talking to %s", vconn_get_name(vconn));
 
@@ -543,7 +577,7 @@ set_switch_config(struct vconn *vconn, const struct ofp_switch_config *config)
 {
     struct ofpbuf *request;
 
-    request = ofpraw_alloc(OFPRAW_OFPT_SET_CONFIG, OFP10_VERSION, 0);
+    request = ofpraw_alloc(OFPRAW_OFPT_SET_CONFIG, vconn_get_version(vconn), 0);
     ofpbuf_put(request, config, sizeof *config);
 
     transact_noreply(vconn, request);
@@ -558,15 +592,15 @@ ofctl_show(int argc OVS_UNUSED, char *argv[])
     struct ofpbuf *reply;
     bool trunc;
 
-    request = ofpraw_alloc(OFPRAW_OFPT_FEATURES_REQUEST, OFP10_VERSION, 0);
     open_vconn(vconn_name, &vconn);
+    request = ofpraw_alloc(OFPRAW_OFPT_FEATURES_REQUEST,
+                           vconn_get_version(vconn), 0);
     run(vconn_transact(vconn, request, &reply), "talking to %s", vconn_name);
 
     trunc = ofputil_switch_features_ports_trunc(reply);
     ofp_print(stdout, reply->data, reply->size, verbosity + 1);
 
     ofpbuf_delete(reply);
-    vconn_close(vconn);
 
     if (trunc) {
         /* The Features Reply may not contain all the ports, so send a
@@ -576,6 +610,7 @@ ofctl_show(int argc OVS_UNUSED, char *argv[])
                                        OFPRAW_OFPST_PORT_DESC_REQUEST);
     }
     dump_trivial_transaction(vconn_name, OFPRAW_OFPT_GET_CONFIG_REQUEST);
+    vconn_close(vconn);
 }
 
 static void
@@ -605,8 +640,9 @@ fetch_port_by_features(const char *vconn_name,
     bool found = false;
 
     /* Fetch the switch's ofp_switch_features. */
-    request = ofpraw_alloc(OFPRAW_OFPT_FEATURES_REQUEST, OFP10_VERSION, 0);
     open_vconn(vconn_name, &vconn);
+    request = ofpraw_alloc(OFPRAW_OFPT_FEATURES_REQUEST,
+                           vconn_get_version(vconn), 0);
     run(vconn_transact(vconn, request, &reply), "talking to %s", vconn_name);
     vconn_close(vconn);
 
@@ -763,7 +799,7 @@ try_set_protocol(struct vconn *vconn, enum ofputil_protocol want,
 
         request = ofputil_encode_set_protocol(*cur, want, &next);
         if (!request) {
-            return true;
+            return *cur == want;
         }
 
         run(vconn_transact_noreply(vconn, request, &reply),
@@ -958,7 +994,7 @@ ofctl_queue_stats(int argc, char *argv[])
     if (argc > 2 && argv[2][0] && strcasecmp(argv[2], "all")) {
         oqs.port_no = str_to_port_no(argv[1], argv[2]);
     } else {
-        oqs.port_no = OFPP_ALL;
+        oqs.port_no = OFPP_ANY;
     }
     if (argc > 3 && argv[3][0] && strcasecmp(argv[3], "all")) {
         oqs.queue_id = atoi(argv[3]);
@@ -1384,19 +1420,32 @@ ofctl_monitor(int argc, char *argv[])
     if (preferred_packet_in_format >= 0) {
         set_packet_in_format(vconn, preferred_packet_in_format);
     } else {
-        struct ofpbuf *spif, *reply;
-
-        spif = ofputil_make_set_packet_in_format(vconn_get_version(vconn),
-                                                 NXPIF_NXM);
-        run(vconn_transact_noreply(vconn, spif, &reply),
-            "talking to %s", vconn_get_name(vconn));
-        if (reply) {
-            char *s = ofp_to_string(reply->data, reply->size, 2);
-            VLOG_DBG("%s: failed to set packet in format to nxm, controller"
-                     " replied: %s. Falling back to the switch default.",
-                     vconn_get_name(vconn), s);
-            free(s);
-            ofpbuf_delete(reply);
+        enum ofp_version version = vconn_get_version(vconn);
+
+        switch (version) {
+        case OFP10_VERSION: {
+            struct ofpbuf *spif, *reply;
+
+            spif = ofputil_make_set_packet_in_format(vconn_get_version(vconn),
+                                                     NXPIF_NXM);
+            run(vconn_transact_noreply(vconn, spif, &reply),
+                "talking to %s", vconn_get_name(vconn));
+            if (reply) {
+                char *s = ofp_to_string(reply->data, reply->size, 2);
+                VLOG_DBG("%s: failed to set packet in format to nxm, controller"
+                        " replied: %s. Falling back to the switch default.",
+                        vconn_get_name(vconn), s);
+                free(s);
+                ofpbuf_delete(reply);
+            }
+            break;
+        }
+        case OFP11_VERSION:
+        case OFP12_VERSION:
+        case OFP13_VERSION:
+            break;
+        default:
+            NOT_REACHED();
         }
     }
 
@@ -1420,7 +1469,7 @@ ofctl_dump_ports(int argc, char *argv[])
     uint16_t port;
 
     open_vconn(argv[1], &vconn);
-    port = argc > 2 ? str_to_port_no(argv[1], argv[2]) : OFPP_NONE;
+    port = argc > 2 ? str_to_port_no(argv[1], argv[2]) : OFPP_ANY;
     request = ofputil_encode_dump_ports_request(vconn_get_version(vconn), port);
     dump_stats_transaction(vconn, request);
     vconn_close(vconn);
@@ -1611,8 +1660,8 @@ ofctl_ping(int argc, char *argv[])
         const struct ofp_header *rpy_hdr;
         enum ofptype type;
 
-        request = ofpraw_alloc(OFPRAW_OFPT_ECHO_REQUEST, OFP10_VERSION,
-                               payload);
+        request = ofpraw_alloc(OFPRAW_OFPT_ECHO_REQUEST,
+                               vconn_get_version(vconn), payload);
         random_bytes(ofpbuf_put_uninit(request, payload), payload);
 
         xgettimeofday(&start);
@@ -1666,8 +1715,8 @@ ofctl_benchmark(int argc OVS_UNUSED, char *argv[])
     for (i = 0; i < count; i++) {
         struct ofpbuf *request, *reply;
 
-        request = ofpraw_alloc(OFPRAW_OFPT_ECHO_REQUEST, OFP10_VERSION,
-                               payload_size);
+        request = ofpraw_alloc(OFPRAW_OFPT_ECHO_REQUEST,
+                               vconn_get_version(vconn), payload_size);
         ofpbuf_put_zeros(request, payload_size);
         run(vconn_transact(vconn, request, &reply), "transact");
         ofpbuf_delete(reply);
@@ -1930,7 +1979,7 @@ read_flows_from_switch(struct vconn *vconn,
 
     fsr.aggregate = false;
     match_init_catchall(&fsr.match);
-    fsr.out_port = OFPP_NONE;
+    fsr.out_port = OFPP_ANY;
     fsr.table_id = 0xff;
     fsr.cookie = fsr.cookie_mask = htonll(0);
     request = ofputil_encode_flow_stats_request(&fsr, protocol);
@@ -1973,7 +2022,7 @@ fte_make_flow_mod(const struct fte *fte, int index, uint16_t command,
     fm.idle_timeout = version->idle_timeout;
     fm.hard_timeout = version->hard_timeout;
     fm.buffer_id = UINT32_MAX;
-    fm.out_port = OFPP_NONE;
+    fm.out_port = OFPP_ANY;
     fm.flags = version->flags;
     if (command == OFPFC_ADD || command == OFPFC_MODIFY ||
         command == OFPFC_MODIFY_STRICT) {
@@ -2703,6 +2752,40 @@ ofctl_print_error(int argc OVS_UNUSED, char *argv[])
     }
 }
 
+/* "encode-error-reply ENUM REQUEST": Encodes an error reply to REQUEST for the
+ * error named ENUM and prints the error reply in hex. */
+static void
+ofctl_encode_error_reply(int argc OVS_UNUSED, char *argv[])
+{
+    const struct ofp_header *oh;
+    struct ofpbuf request, *reply;
+    enum ofperr error;
+
+    error = ofperr_from_name(argv[1]);
+    if (!error) {
+        ovs_fatal(0, "unknown error \"%s\"", argv[1]);
+    }
+
+    ofpbuf_init(&request, 0);
+    if (ofpbuf_put_hex(&request, argv[2], NULL)[0] != '\0') {
+        ovs_fatal(0, "Trailing garbage in hex data");
+    }
+    if (request.size < sizeof(struct ofp_header)) {
+        ovs_fatal(0, "Request too short");
+    }
+
+    oh = request.data;
+    if (request.size != ntohs(oh->length)) {
+        ovs_fatal(0, "Request size inconsistent");
+    }
+
+    reply = ofperr_encode_reply(error, request.data);
+    ofpbuf_uninit(&request);
+
+    ovs_hex_dump(stdout, reply->data, reply->size, 0, false);
+    ofpbuf_delete(reply);
+}
+
 /* "ofp-print HEXSTRING [VERBOSITY]": Converts the hex digits in HEXSTRING into
  * binary data, interpreting them as an OpenFlow message, and prints the
  * OpenFlow message on stdout, at VERBOSITY (level 2 by default).  */
@@ -2719,6 +2802,20 @@ ofctl_ofp_print(int argc, char *argv[])
     ofpbuf_uninit(&packet);
 }
 
+/* "encode-hello BITMAP...": Encodes each BITMAP as an OpenFlow hello message
+ * and dumps each message in hex.  */
+static void
+ofctl_encode_hello(int argc OVS_UNUSED, char *argv[])
+{
+    uint32_t bitmap = strtol(argv[1], NULL, 0);
+    struct ofpbuf *hello;
+
+    hello = ofputil_encode_hello(bitmap);
+    ovs_hex_dump(stdout, hello->data, hello->size, 0, false);
+    ofp_print(stdout, hello->data, hello->size, verbosity);
+    ofpbuf_delete(hello);
+}
+
 static const struct command all_commands[] = {
     { "show", 1, 1, ofctl_show },
     { "monitor", 1, 3, ofctl_monitor },
@@ -2758,7 +2855,9 @@ static const struct command all_commands[] = {
     { "parse-ofp11-instructions", 0, 0, ofctl_parse_ofp11_instructions },
     { "check-vlan", 2, 2, ofctl_check_vlan },
     { "print-error", 1, 1, ofctl_print_error },
+    { "encode-error-reply", 2, 2, ofctl_encode_error_reply },
     { "ofp-print", 1, 2, ofctl_ofp_print },
+    { "encode-hello", 1, 1, ofctl_encode_hello },
 
     { NULL, 0, 0, NULL },
 };
index b36bbe7..8f794be 100644 (file)
@@ -1,4 +1,4 @@
-.TH ovs\-pcap 1 "December 2010" "Open vSwitch" "Open vSwitch Manual"
+.TH ovs\-pcap 1 "@VERSION@" "Open vSwitch" "Open vSwitch Manual"
 .
 .SH NAME
 ovs\-pcap \- print packets from a pcap file as hex
index e2aa5b4..6d042b4 100644 (file)
@@ -3,7 +3,7 @@
 .  ns
 .  IP "\\$1"
 ..
-.TH ovs\-pki 8 "May 2008" "Open vSwitch" "Open vSwitch Manual"
+.TH ovs\-pki 8 "@VERSION@" "Open vSwitch" "Open vSwitch Manual"
 
 .SH NAME
 ovs\-pki \- OpenFlow public key infrastructure management utility
index 01e5791..2ab9d08 100755 (executable)
@@ -27,9 +27,6 @@ Commands:
                         configuration.
  save-flows             Outputs a shell script on stdout that will restore
                         Openflow flows of each Open vSwitch bridge.
- save-datapaths         Outputs a shell script on stdout that will restore
-                        the datapaths with the same port numbers as before.
-
 This script is meant as a helper for the Open vSwitch init script commands.
 EOF
 }
@@ -177,89 +174,10 @@ save_flows () {
     done
 }
 
-ovs_vsctl () {
-    ovs-vsctl --no-wait --timeout=5 "$@"
-}
-
-save_datapaths () {
-    if missing_program ovs-dpctl; then
-        echo "$0: ovs-dpctl not found in $PATH" >&2
-        exit 1
-    fi
-    if missing_program ovs-vsctl; then
-        echo "$0: ovs-vsctl not found in $PATH" >&2
-        exit 1
-    fi
-
-    for dp in "$@"; do
-        echo "ovs-dpctl add-dp ${dp}"
-        ovs-dpctl show $dp | while read line; do
-            # An example 'ovs-dpctl show' output looks like this:
-            # system@br1:
-            # lookups: hit:0 missed:0 lost:0
-            # flows: 0
-            # port 0: br1 (internal)
-            # port 2: gre2886795521 (ipsec_gre: key=flow, pmtud=false, remote_ip=172.17.1.1, tos=inherit)
-            # port 3: gre1 (ipsec_gre: remote_ip=192.168.113.1)
-            # port 14: gre2 (gre: remote_ip=192.168.115.1)
-            # port 15: gre3 (gre64: remote_ip=192.168.116.1)
-            # port 16: eth0
-            # port 17: br1- (patch: peer=br1+)
-
-            # Skip lines which do not have 'port'
-            if port_no=`expr "${line}" : '.*port \([0-9]\+\):'`; then :; else
-                continue
-            fi
-
-            netdev=`echo ${line} | awk '{print $3}'`
-
-            # Do not add port that has the same name as the datapath. It gets
-            # added by default.
-            [ "${dp#system@}" = "${netdev}" ] && continue
-
-            type=`echo ${line} | awk '{print $4}' | sed 's/[:)(]//g'`
-            [ ! -n "${type}" ] && type="system"
-
-            command="ovs-dpctl add-if ${dp}\
-                        ${netdev},type=${type},port_no=${port_no}"
-
-            options=`echo ${line} | awk -F: '{print $3}' | sed 's/[) ]//g'`
-            [ -n "${options}" ] && command="${command},${options}"
-
-            # For ipsec, ovs-dpctl does not show the key value pairs related
-            # to certificates. Get that information from ovs-vsctl.
-            if [ "${type}" = "ipsec_gre" ] ; then
-                if peer_cert=`ovs_vsctl get interface \
-                                "${netdev}" options:peer_cert 2>/dev/null`; then
-                    # The option peer_cert comes with an accompanying
-                    # "certificate" or "use_ssl_cert"
-                    if certificate=`ovs_vsctl get interface "${netdev}" \
-                            options:certificate 2>/dev/null` ; then
-                        command="${command},peer_cert=${peer_cert},certificate=${certificate}"
-                    else
-                        use_ssl_cert=`ovs_vsctl get interface "${netdev}" \
-                                        options:use_ssl_cert 2>/dev/null`
-                        command="${command},peer_cert=${peer_cert},use_ssl_cert=${use_ssl_cert}"
-                    fi
-                else
-                    psk=`ovs_vsctl get interface "${netdev}" \
-                            options:psk 2>/dev/null`
-                    command="${command},psk=${psk}"
-                fi
-            fi
-            echo ${command}
-        done
-    done
-}
 
 while [ $# -ne 0 ]
 do
     case $1 in
-        "save-datapaths")
-            shift
-            save_datapaths "$@"
-            exit 0
-            ;;
         "save-flows")
             shift
             save_flows "$@"
index ce12e82..133c042 100644 (file)
@@ -1,4 +1,4 @@
-.TH ovs\-tcpundump 1 "December 2010" "Open vSwitch" "Open vSwitch Manual"
+.TH ovs\-tcpundump 1 "@VERSION@" "Open vSwitch" "Open vSwitch Manual"
 .
 .SH NAME
 ovs\-tcpundump \- convert ``tcpdump \-xx'' output to hex strings
index 9704f72..1126b3c 100644 (file)
@@ -3,7 +3,7 @@
 .  ns
 .  IP "\\$1"
 ..
-.TH ovs\-test 1 "April 2012" "Open vSwitch" "Open vSwitch Manual"
+.TH ovs\-test 1 "@VERSION@" "Open vSwitch" "Open vSwitch Manual"
 .
 .SH NAME
 \fBovs\-test\fR \- check Linux drivers for performance, vlan and L3 tunneling
@@ -141,4 +141,4 @@ GRE tests between both nodes:
 .BR ovs\-vsctl (8),
 .BR ovs\-vlan\-test (8),
 .BR ethtool (8),
-.BR uname (1)
\ No newline at end of file
+.BR uname (1)
index d05fe93..51ea4ab 100644 (file)
@@ -4,7 +4,7 @@
 .  ns
 .  IP "\\$1"
 ..
-.TH ovs\-vlan\-bug\-workaround 8 "February 2011" "Open vSwitch" "Open vSwitch Manual"
+.TH ovs\-vlan\-bug\-workaround 8 "@VERSION@" "Open vSwitch" "Open vSwitch Manual"
 .ds PN ovs\-vlan\-bug\-workaround
 .
 .SH NAME
index 0f7c564..2cdeff8 100644 (file)
@@ -3,7 +3,7 @@
 .  ns
 .  IP "\\$1"
 ..
-.TH ovs\-vlan\-test 1 "December 2010" "Open vSwitch" "Open vSwitch Manual"
+.TH ovs\-vlan\-test 1 "@VERSION@" "Open vSwitch" "Open vSwitch Manual"
 .
 .SH NAME
 \fBovs\-vlan\-test\fR \- check Linux drivers for problems with vlan traffic
index 1b80d05..30baafd 100644 (file)
@@ -10,7 +10,7 @@
 .  I "\\$1"
 .  RE
 ..
-.TH ovs\-vsctl 8 "November 2009" "Open vSwitch" "Open vSwitch Manual"
+.TH ovs\-vsctl 8 "@VERSION@" "Open vSwitch" "Open vSwitch Manual"
 .\" This program's name:
 .ds PN ovs\-vsctl
 .\" SSL peer program's name:
@@ -43,9 +43,9 @@ implemented as a single atomic transaction against the database.
 The \fBovs\-vsctl\fR command line begins with global options (see
 \fBOPTIONS\fR below for details).  The global options are followed by
 one or more commands.  Each command should begin with \fB\-\-\fR by
-itself as a command-line argument, to separate it from the global
-options and following commands.  (If the first command does not have
-any options, then the first \fB\-\-\fR may be omitted.)  The command
+itself as a command-line argument, to separate it from the following
+commands.  (The \fB\-\-\fR before the first command is optional.)  The
+command
 itself starts with command-specific options, if any, followed by the
 command name and any arguments.  See \fBEXAMPLES\fR below for syntax
 examples.
@@ -195,9 +195,10 @@ Without \fB\-\-if\-exists\fR, attempting to delete a bridge that does
 not exist is an error.  With \fB\-\-if\-exists\fR, attempting to
 delete a bridge that does not exist has no effect.
 .
-.IP "\fBlist\-br\fR"
+.IP "[\fB\-\-real\fR|\fB\-\-fake\fR] \fBlist\-br\fR"
 Lists all existing real and fake bridges on standard output, one per
-line.
+line.  With \fB\-\-real\fR or \fB\-\-fake\fR, only bridges of that type
+are returned.
 .
 .IP "\fBbr\-exists \fIbridge\fR"
 Tests whether \fIbridge\fR exists as a real or fake bridge.  If so,
@@ -769,10 +770,9 @@ Delete bridge \fBbr0\fR, reporting an error if it does not exist:
 .IP
 .B "ovs\-vsctl del\-br br0"
 .PP
-Delete bridge \fBbr0\fR if it exists (the \fB\-\-\fR is required to
-separate \fBdel\-br\fR's options from the global options):
+Delete bridge \fBbr0\fR if it exists:
 .IP
-.B "ovs\-vsctl \-\- \-\-if\-exists del\-br br0"
+.B "ovs\-vsctl \-\-if\-exists del\-br br0"
 .PP
 Set the \fBqos\fR column of the \fBPort\fR record for \fBeth0\fR to
 point to a new \fBQoS\fR record, which in turn points with its queue 0
@@ -911,6 +911,13 @@ Deconfigure STP from above:
 .IP
 .B "ovs\-vsctl clear Bridge br0 stp_enable"
 .PP
+.SS "OpenFlow Version"
+.PP
+Configure bridge \fBbr0\fR to support OpenFlow versions 1.0, 1.2, and
+1.3:
+.IP
+.B "ovs\-vsctl set bridge br0 protocols=openflow10,openflow12,openflow13"
+.
 .SH "EXIT STATUS"
 .IP "0"
 Successful program execution.
index fda3a89..bccb2c9 100644 (file)
@@ -131,12 +131,14 @@ static void vsctl_exit(int status) NO_RETURN;
 static void vsctl_fatal(const char *, ...) PRINTF_FORMAT(1, 2) NO_RETURN;
 static char *default_db(void);
 static void usage(void) NO_RETURN;
-static void parse_options(int argc, char *argv[]);
+static void parse_options(int argc, char *argv[], struct shash *local_options);
 static bool might_write_to_db(char **argv);
 
 static struct vsctl_command *parse_commands(int argc, char *argv[],
+                                            struct shash *local_options,
                                             size_t *n_commandsp);
-static void parse_command(int argc, char *argv[], struct vsctl_command *);
+static void parse_command(int argc, char *argv[], struct shash *local_options,
+                          struct vsctl_command *);
 static const struct vsctl_command_syntax *find_command(const char *name);
 static void run_prerequisites(struct vsctl_command[], size_t n_commands,
                               struct ovsdb_idl *);
@@ -159,6 +161,7 @@ main(int argc, char *argv[])
     extern struct vlog_module VLM_reconnect;
     struct ovsdb_idl *idl;
     struct vsctl_command *commands;
+    struct shash local_options;
     unsigned int seqno;
     size_t n_commands;
     char *args;
@@ -174,8 +177,10 @@ main(int argc, char *argv[])
     VLOG(might_write_to_db(argv) ? VLL_INFO : VLL_DBG, "Called as %s", args);
 
     /* Parse command line. */
-    parse_options(argc, argv);
-    commands = parse_commands(argc - optind, argv + optind, &n_commands);
+    shash_init(&local_options);
+    parse_options(argc, argv, &local_options);
+    commands = parse_commands(argc - optind, argv + optind, &local_options,
+                              &n_commands);
 
     if (timeout) {
         time_alarm(timeout);
@@ -208,8 +213,32 @@ main(int argc, char *argv[])
     }
 }
 
+static struct option *
+find_option(const char *name, struct option *options, size_t n_options)
+{
+    size_t i;
+
+    for (i = 0; i < n_options; i++) {
+        if (!strcmp(options[i].name, name)) {
+            return &options[i];
+        }
+    }
+    return NULL;
+}
+
+static struct option *
+add_option(struct option **optionsp, size_t *n_optionsp,
+           size_t *allocated_optionsp)
+{
+    if (*n_optionsp >= *allocated_optionsp) {
+        *optionsp = x2nrealloc(*optionsp, allocated_optionsp,
+                               sizeof **optionsp);
+    }
+    return &(*optionsp)[(*n_optionsp)++];
+}
+
 static void
-parse_options(int argc, char *argv[])
+parse_options(int argc, char *argv[], struct shash *local_options)
 {
     enum {
         OPT_DB = UCHAR_MAX + 1,
@@ -218,10 +247,11 @@ parse_options(int argc, char *argv[])
         OPT_NO_WAIT,
         OPT_DRY_RUN,
         OPT_PEER_CA_CERT,
+        OPT_LOCAL,
         VLOG_OPTION_ENUMS,
         TABLE_OPTION_ENUMS
     };
-    static struct option long_options[] = {
+    static const struct option global_long_options[] = {
         {"db", required_argument, NULL, OPT_DB},
         {"no-syslog", no_argument, NULL, OPT_NO_SYSLOG},
         {"no-wait", no_argument, NULL, OPT_NO_WAIT},
@@ -236,18 +266,75 @@ parse_options(int argc, char *argv[])
         {"peer-ca-cert", required_argument, NULL, OPT_PEER_CA_CERT},
         {NULL, 0, NULL, 0},
     };
+    const int n_global_long_options = ARRAY_SIZE(global_long_options) - 1;
     char *tmp, *short_options;
 
-    tmp = long_options_to_short_options(long_options);
+    const struct vsctl_command_syntax *p;
+    struct option *options, *o;
+    size_t allocated_options;
+    size_t n_options;
+    size_t i;
+
+    tmp = long_options_to_short_options(global_long_options);
     short_options = xasprintf("+%s", tmp);
     free(tmp);
 
+    /* We want to parse both global and command-specific options here, but
+     * getopt_long() isn't too convenient for the job.  We copy our global
+     * options into a dynamic array, then append all of the command-specific
+     * options. */
+    options = xmemdup(global_long_options, sizeof global_long_options);
+    allocated_options = ARRAY_SIZE(global_long_options);
+    n_options = n_global_long_options;
+    for (p = all_commands; p->name; p++) {
+        if (p->options[0]) {
+            char *save_ptr = NULL;
+            char *name;
+            char *s;
+
+            s = xstrdup(p->options);
+            for (name = strtok_r(s, ",", &save_ptr); name != NULL;
+                 name = strtok_r(NULL, ",", &save_ptr)) {
+                char *equals;
+                int has_arg;
+
+                assert(name[0] == '-' && name[1] == '-' && name[2]);
+                name += 2;
+
+                equals = strchr(name, '=');
+                if (equals) {
+                    has_arg = required_argument;
+                    *equals = '\0';
+                } else {
+                    has_arg = no_argument;
+                }
+
+                o = find_option(name, options, n_options);
+                if (o) {
+                    assert(o - options >= n_global_long_options);
+                    assert(o->has_arg == has_arg);
+                } else {
+                    o = add_option(&options, &n_options, &allocated_options);
+                    o->name = xstrdup(name);
+                    o->has_arg = has_arg;
+                    o->flag = NULL;
+                    o->val = OPT_LOCAL;
+                }
+            }
+
+            free(s);
+        }
+    }
+    o = add_option(&options, &n_options, &allocated_options);
+    memset(o, 0, sizeof *o);
+
     table_style.format = TF_LIST;
 
     for (;;) {
+        int idx;
         int c;
 
-        c = getopt_long(argc, argv, short_options, long_options, NULL);
+        c = getopt_long(argc, argv, short_options, options, &idx);
         if (c == -1) {
             break;
         }
@@ -273,6 +360,16 @@ parse_options(int argc, char *argv[])
             dry_run = true;
             break;
 
+        case OPT_LOCAL:
+            if (shash_find(local_options, options[idx].name)) {
+                vsctl_fatal("'%s' option specified multiple times",
+                            options[idx].name);
+            }
+            shash_add_nocopy(local_options,
+                             xasprintf("--%s", options[idx].name),
+                             optarg ? xstrdup(optarg) : NULL);
+            break;
+
         case 'h':
             usage();
 
@@ -309,10 +406,16 @@ parse_options(int argc, char *argv[])
     if (!db) {
         db = default_db();
     }
+
+    for (i = n_global_long_options; options[i].name; i++) {
+        free(CONST_CAST(char *, options[i].name));
+    }
+    free(options);
 }
 
 static struct vsctl_command *
-parse_commands(int argc, char *argv[], size_t *n_commandsp)
+parse_commands(int argc, char *argv[], struct shash *local_options,
+               size_t *n_commandsp)
 {
     struct vsctl_command *commands;
     size_t n_commands, allocated_commands;
@@ -333,8 +436,10 @@ parse_commands(int argc, char *argv[], size_t *n_commandsp)
                         shash_moved(&c->options);
                     }
                 }
-                parse_command(i - start, &argv[start],
+                parse_command(i - start, &argv[start], local_options,
                               &commands[n_commands++]);
+            } else if (!shash_is_empty(local_options)) {
+                vsctl_fatal("missing command name (use --help for help)");
             }
             start = i + 1;
         }
@@ -347,7 +452,8 @@ parse_commands(int argc, char *argv[], size_t *n_commandsp)
 }
 
 static void
-parse_command(int argc, char *argv[], struct vsctl_command *command)
+parse_command(int argc, char *argv[], struct shash *local_options,
+              struct vsctl_command *command)
 {
     const struct vsctl_command_syntax *p;
     struct shash_node *node;
@@ -355,6 +461,7 @@ parse_command(int argc, char *argv[], struct vsctl_command *command)
     int i;
 
     shash_init(&command->options);
+    shash_swap(local_options, &command->options);
     for (i = 0; i < argc; i++) {
         const char *option = argv[i];
         const char *equals;
@@ -379,7 +486,7 @@ parse_command(int argc, char *argv[], struct vsctl_command *command)
         shash_add_nocopy(&command->options, key, value);
     }
     if (i == argc) {
-        vsctl_fatal("missing command name");
+        vsctl_fatal("missing command name (use --help for help)");
     }
 
     p = find_command(argv[i]);
@@ -1582,13 +1689,23 @@ cmd_list_br(struct vsctl_context *ctx)
 {
     struct shash_node *node;
     struct svec bridges;
+    bool real = shash_find(&ctx->options, "--real");
+    bool fake = shash_find(&ctx->options, "--fake");
+
+    /* If neither fake nor real were requested, return both. */
+    if (!real && !fake) {
+        real = fake = true;
+    }
 
     vsctl_context_populate_cache(ctx);
 
     svec_init(&bridges);
     SHASH_FOR_EACH (node, &ctx->bridges) {
         struct vsctl_bridge *br = node->data;
-        svec_add(&bridges, br->name);
+
+        if (br->parent ? fake : real) {
+            svec_add(&bridges, br->name);
+        }
     }
     output_sorted(&bridges, &ctx->output);
     svec_destroy(&bridges);
@@ -3961,7 +4078,7 @@ static const struct vsctl_command_syntax all_commands[] = {
     /* Bridge commands. */
     {"add-br", 1, 3, pre_get_info, cmd_add_br, NULL, "--may-exist", RW},
     {"del-br", 1, 1, pre_get_info, cmd_del_br, NULL, "--if-exists", RW},
-    {"list-br", 0, 0, pre_get_info, cmd_list_br, NULL, "", RO},
+    {"list-br", 0, 0, pre_get_info, cmd_list_br, NULL, "--real,--fake", RO},
     {"br-exists", 1, 1, pre_get_info, cmd_br_exists, NULL, "", RO},
     {"br-to-vlan", 1, 1, pre_get_info, cmd_br_to_vlan, NULL, "", RO},
     {"br-to-parent", 1, 1, pre_get_info, cmd_br_to_parent, NULL, "", RO},
index d5c239b..afdff20 100644 (file)
@@ -1,7 +1,5 @@
 /Makefile
 /Makefile.in
-/ovs-brcompatd
-/ovs-brcompatd.8
 /ovs-vswitchd
 /ovs-vswitchd.8
 /ovs-vswitchd.conf.db.5
index fe513ac..62ace69 100644 (file)
@@ -1,11 +1,7 @@
 sbin_PROGRAMS += vswitchd/ovs-vswitchd
 man_MANS += vswitchd/ovs-vswitchd.8
-if BUILD_BRCOMPAT
-  man_MANS += vswitchd/ovs-brcompatd.8
-endif
 DISTCLEANFILES += \
-       vswitchd/ovs-vswitchd.8 \
-       vswitchd/ovs-brcompatd.8
+       vswitchd/ovs-vswitchd.8
 
 vswitchd_ovs_vswitchd_SOURCES = \
        vswitchd/bridge.c \
@@ -23,16 +19,6 @@ vswitchd_ovs_vswitchd_LDADD = \
 EXTRA_DIST += vswitchd/INTERNALS
 MAN_ROOTS += vswitchd/ovs-vswitchd.8.in
 
-if BUILD_BRCOMPAT
-if LINUX_DATAPATH
-sbin_PROGRAMS += vswitchd/ovs-brcompatd
-vswitchd_ovs_brcompatd_SOURCES = \
-       vswitchd/ovs-brcompatd.c
-vswitchd_ovs_brcompatd_LDADD = lib/libopenvswitch.a $(SSL_LIBS)
-endif
-MAN_ROOTS += vswitchd/ovs-brcompatd.8.in
-endif
-
 # vswitch schema and IDL
 EXTRA_DIST += vswitchd/vswitch.ovsschema
 pkgdata_DATA += vswitchd/vswitch.ovsschema
index a481f06..348faef 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
+/* Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -36,6 +36,7 @@
 #include "meta-flow.h"
 #include "netdev.h"
 #include "ofp-print.h"
+#include "ofp-util.h"
 #include "ofpbuf.h"
 #include "ofproto/ofproto.h"
 #include "poll-loop.h"
@@ -66,6 +67,7 @@ struct if_cfg {
     struct hmap_node hmap_node;         /* Node in bridge's if_cfg_todo. */
     const struct ovsrec_interface *cfg; /* Interface record. */
     const struct ovsrec_port *parent;   /* Parent port record. */
+    int64_t ofport;                     /* Requested OpenFlow port number. */
 };
 
 /* OpenFlow port slated for removal from ofproto. */
@@ -180,10 +182,11 @@ static void bridge_configure_datapath_id(struct bridge *);
 static void bridge_configure_flow_eviction_threshold(struct bridge *);
 static void bridge_configure_netflow(struct bridge *);
 static void bridge_configure_forward_bpdu(struct bridge *);
-static void bridge_configure_mac_idle_time(struct bridge *);
+static void bridge_configure_mac_table(struct bridge *);
 static void bridge_configure_sflow(struct bridge *, int *sflow_bridge_number);
 static void bridge_configure_stp(struct bridge *);
 static void bridge_configure_tables(struct bridge *);
+static void bridge_configure_dp_desc(struct bridge *);
 static void bridge_configure_remotes(struct bridge *,
                                      const struct sockaddr_in *managers,
                                      size_t n_managers);
@@ -226,6 +229,8 @@ static void mirror_refresh_stats(struct mirror *);
 
 static void iface_configure_lacp(struct iface *, struct lacp_slave_settings *);
 static bool iface_create(struct bridge *, struct if_cfg *, int ofp_port);
+static bool iface_is_internal(const struct ovsrec_interface *iface,
+                              const struct ovsrec_bridge *br);
 static const char *iface_get_type(const struct ovsrec_interface *,
                                   const struct ovsrec_bridge *);
 static void iface_destroy(struct iface *);
@@ -243,6 +248,7 @@ static void iface_refresh_cfm_stats(struct iface *);
 static void iface_refresh_stats(struct iface *);
 static void iface_refresh_status(struct iface *);
 static bool iface_is_synthetic(const struct iface *);
+static int64_t iface_pick_ofport(const struct ovsrec_interface *);
 
 /* Linux VLAN device support (e.g. "eth0.10" for VLAN 10.)
  *
@@ -261,6 +267,49 @@ static void configure_splinter_port(struct port *);
 static void add_vlan_splinter_ports(struct bridge *,
                                     const unsigned long int *splinter_vlans,
                                     struct shash *ports);
+
+static void
+bridge_init_ofproto(const struct ovsrec_open_vswitch *cfg)
+{
+    struct shash iface_hints;
+    static bool initialized = false;
+    int i;
+
+    if (initialized) {
+        return;
+    }
+
+    shash_init(&iface_hints);
+
+    if (cfg) {
+        for (i = 0; i < cfg->n_bridges; i++) {
+            const struct ovsrec_bridge *br_cfg = cfg->bridges[i];
+            int j;
+
+            for (j = 0; j < br_cfg->n_ports; j++) {
+                struct ovsrec_port *port_cfg = br_cfg->ports[j];
+                int k;
+
+                for (k = 0; k < port_cfg->n_interfaces; k++) {
+                    struct ovsrec_interface *if_cfg = port_cfg->interfaces[k];
+                    struct iface_hint *iface_hint;
+
+                    iface_hint = xmalloc(sizeof *iface_hint);
+                    iface_hint->br_name = br_cfg->name;
+                    iface_hint->br_type = br_cfg->datapath_type;
+                    iface_hint->ofp_port = iface_pick_ofport(if_cfg);
+
+                    shash_add(&iface_hints, if_cfg->name, iface_hint);
+                }
+            }
+        }
+    }
+
+    ofproto_init(&iface_hints);
+
+    shash_destroy_free_data(&iface_hints);
+    initialized = true;
+}
 \f
 /* Public functions. */
 
@@ -543,12 +592,13 @@ bridge_reconfigure_continue(const struct ovsrec_open_vswitch *ovs_cfg)
         bridge_configure_mirrors(br);
         bridge_configure_flow_eviction_threshold(br);
         bridge_configure_forward_bpdu(br);
-        bridge_configure_mac_idle_time(br);
+        bridge_configure_mac_table(br);
         bridge_configure_remotes(br, managers, n_managers);
         bridge_configure_netflow(br);
         bridge_configure_sflow(br, &sflow_bridge_number);
         bridge_configure_stp(br);
         bridge_configure_tables(br);
+        bridge_configure_dp_desc(br);
     }
     free(managers);
 
@@ -558,7 +608,7 @@ bridge_reconfigure_continue(const struct ovsrec_open_vswitch *ovs_cfg)
         daemonize_complete();
         reconfiguring = false;
 
-        VLOG_INFO("%s (Open vSwitch) %s", program_name, VERSION);
+        VLOG_INFO_ONCE("%s (Open vSwitch) %s", program_name, VERSION);
     }
 
     return done;
@@ -767,6 +817,18 @@ bridge_configure_datapath_id(struct bridge *br)
     free(dpid_string);
 }
 
+/* Returns a bitmap of "enum ofputil_protocol"s that are allowed for use with
+ * 'br'. */
+static uint32_t
+bridge_get_allowed_versions(struct bridge *br)
+{
+    if (!br->cfg->n_protocols)
+        return 0;
+
+    return ofputil_versions_from_strings(br->cfg->protocols,
+                                         br->cfg->n_protocols);
+}
+
 /* Set NetFlow configuration on 'br'. */
 static void
 bridge_configure_netflow(struct bridge *br)
@@ -953,16 +1015,11 @@ port_configure_stp(const struct ofproto *ofproto, struct port *port,
         port_s->path_cost = strtoul(config_str, NULL, 10);
     } else {
         enum netdev_features current;
+        unsigned int mbps;
 
-        if (netdev_get_features(iface->netdev, &current, NULL, NULL, NULL)) {
-            /* Couldn't get speed, so assume 100Mb/s. */
-            port_s->path_cost = 19;
-        } else {
-            unsigned int mbps;
-
-            mbps = netdev_features_to_bps(current) / 1000000;
-            port_s->path_cost = stp_convert_speed_to_cost(mbps);
-        }
+        netdev_get_features(iface->netdev, &current, NULL, NULL, NULL);
+        mbps = netdev_features_to_bps(current, 100 * 1000 * 1000) / 1000000;
+        port_s->path_cost = stp_convert_speed_to_cost(mbps);
     }
 
     config_str = smap_get(&port->cfg->other_config, "stp-port-priority");
@@ -1190,7 +1247,7 @@ bridge_refresh_one_ofp_port(struct bridge *br,
              * configured as the user requested, so we must destroy it. */
             return false;
         } else {
-            /* It's the right type and configured correctly.  keep it. */
+            /* It's the right type and configured correctly.  Keep it. */
             iface_set_ofp_port(iface, ofp_port);
             return true;
         }
@@ -1261,7 +1318,7 @@ bridge_refresh_ofp_port(struct bridge *br)
     }
 }
 
-/* Opens a network device for 'iface_cfg' and configures it.  If '*ofp_portp'
+/* Opens a network device for 'if_cfg' and configures it.  If '*ofp_portp'
  * is negative, adds the network device to br->ofproto and stores the OpenFlow
  * port number in '*ofp_portp'; otherwise leaves br->ofproto and '*ofp_portp'
  * untouched.
@@ -1270,10 +1327,11 @@ bridge_refresh_ofp_port(struct bridge *br)
  * failure, returns a positive errno value and stores NULL in '*netdevp'. */
 static int
 iface_do_create(const struct bridge *br,
-                const struct ovsrec_interface *iface_cfg,
-                const struct ovsrec_port *port_cfg,
+                const struct if_cfg *if_cfg,
                 int *ofp_portp, struct netdev **netdevp)
 {
+    const struct ovsrec_interface *iface_cfg = if_cfg->cfg;
+    const struct ovsrec_port *port_cfg = if_cfg->parent;
     struct netdev *netdev;
     int error;
 
@@ -1291,7 +1349,7 @@ iface_do_create(const struct bridge *br,
     }
 
     if (*ofp_portp < 0) {
-        uint16_t ofp_port;
+        uint16_t ofp_port = if_cfg->ofport;
 
         error = ofproto_port_add(br->ofproto, netdev, &ofp_port);
         if (error) {
@@ -1306,7 +1364,8 @@ iface_do_create(const struct bridge *br,
                  br->name, iface_cfg->name, *ofp_portp);
     }
 
-    if (port_cfg->vlan_mode && !strcmp(port_cfg->vlan_mode, "splinter")) {
+    if ((port_cfg->vlan_mode && !strcmp(port_cfg->vlan_mode, "splinter"))
+        || iface_is_internal(iface_cfg, br->cfg)) {
         netdev_turn_flags_on(netdev, NETDEV_UP, true);
     }
 
@@ -1335,11 +1394,7 @@ iface_create(struct bridge *br, struct if_cfg *if_cfg, int ofp_port)
     struct iface *iface;
     struct port *port;
     int error;
-
-    /* Get rid of 'if_cfg' itself.  We already copied out the interesting
-     * bits. */
-    hmap_remove(&br->if_cfg_todo, &if_cfg->hmap_node);
-    free(if_cfg);
+    bool ok = true;
 
     /* Do the bits that can fail up front.
      *
@@ -1348,11 +1403,13 @@ iface_create(struct bridge *br, struct if_cfg *if_cfg, int ofp_port)
      * additions and deletions are cheaper, these calls should be removed. */
     bridge_run_fast();
     assert(!iface_lookup(br, iface_cfg->name));
-    error = iface_do_create(br, iface_cfg, port_cfg, &ofp_port, &netdev);
+    error = iface_do_create(br, if_cfg, &ofp_port, &netdev);
     bridge_run_fast();
     if (error) {
+        iface_set_ofport(iface_cfg, -1);
         iface_clear_db_record(iface_cfg);
-        return false;
+        ok = false;
+        goto done;
     }
 
     /* Get or create the port structure. */
@@ -1390,7 +1447,9 @@ iface_create(struct bridge *br, struct if_cfg *if_cfg, int ofp_port)
 
             error = netdev_open(port->name, "internal", &netdev);
             if (!error) {
-                ofproto_port_add(br->ofproto, netdev, NULL);
+                uint16_t ofp_port = if_cfg->ofport;
+
+                ofproto_port_add(br->ofproto, netdev, &ofp_port);
                 netdev_close(netdev);
             } else {
                 VLOG_WARN("could not open network device %s (%s)",
@@ -1402,7 +1461,11 @@ iface_create(struct bridge *br, struct if_cfg *if_cfg, int ofp_port)
         }
     }
 
-    return true;
+done:
+    hmap_remove(&br->if_cfg_todo, &if_cfg->hmap_node);
+    free(if_cfg);
+
+    return ok;
 }
 
 /* Set Flow eviction threshold */
@@ -1432,18 +1495,27 @@ bridge_configure_forward_bpdu(struct bridge *br)
                                            false));
 }
 
-/* Set MAC aging time for 'br'. */
+/* Set MAC learning table configuration for 'br'. */
 static void
-bridge_configure_mac_idle_time(struct bridge *br)
+bridge_configure_mac_table(struct bridge *br)
 {
     const char *idle_time_str;
     int idle_time;
 
+    const char *mac_table_size_str;
+    int mac_table_size;
+
     idle_time_str = smap_get(&br->cfg->other_config, "mac-aging-time");
     idle_time = (idle_time_str && atoi(idle_time_str)
                  ? atoi(idle_time_str)
                  : MAC_ENTRY_DEFAULT_IDLE_TIME);
-    ofproto_set_mac_idle_time(br->ofproto, idle_time);
+
+    mac_table_size_str = smap_get(&br->cfg->other_config, "mac-table-size");
+    mac_table_size = (mac_table_size_str && atoi(mac_table_size_str)
+                      ? atoi(mac_table_size_str)
+                      : MAC_DEFAULT_MAX);
+
+    ofproto_set_mac_table_config(br->ofproto, idle_time, mac_table_size);
 }
 
 static void
@@ -1546,15 +1618,10 @@ bridge_pick_local_hw_addr(struct bridge *br, uint8_t ea[ETH_ADDR_LEN],
             found_addr = true;
         }
     }
-    if (found_addr) {
-        VLOG_DBG("bridge %s: using bridge Ethernet address "ETH_ADDR_FMT,
-                 br->name, ETH_ADDR_ARGS(ea));
-    } else {
-        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 10);
+
+    if (!found_addr) {
         memcpy(ea, br->default_ea, ETH_ADDR_LEN);
         *hw_addr_iface = NULL;
-        VLOG_WARN_RL(&rl, "bridge %s: using default bridge Ethernet "
-                     "address "ETH_ADDR_FMT, br->name, ETH_ADDR_ARGS(ea));
     }
 
     hmapx_destroy(&mirror_output_ports);
@@ -1645,7 +1712,7 @@ iface_refresh_status(struct iface *iface)
 
     smap_init(&smap);
 
-    if (!netdev_get_drv_info(iface->netdev, &smap)) {
+    if (!netdev_get_status(iface->netdev, &smap)) {
         ovsrec_interface_set_status(iface->cfg, &smap);
     } else {
         ovsrec_interface_set_status(iface->cfg, NULL);
@@ -1654,12 +1721,11 @@ iface_refresh_status(struct iface *iface)
     smap_destroy(&smap);
 
     error = netdev_get_features(iface->netdev, &current, NULL, NULL, NULL);
-    if (!error) {
+    bps = !error ? netdev_features_to_bps(current, 0) : 0;
+    if (bps) {
         ovsrec_interface_set_duplex(iface->cfg,
                                     netdev_features_is_full_duplex(current)
                                     ? "full" : "half");
-        /* warning: uint64_t -> int64_t conversion */
-        bps = netdev_features_to_bps(current);
         ovsrec_interface_set_link_speed(iface->cfg, &bps, 1);
     }
     else {
@@ -2031,8 +2097,17 @@ refresh_instant_stats(void)
 void
 bridge_run_fast(void)
 {
+    struct sset types;
+    const char *type;
     struct bridge *br;
 
+    sset_init(&types);
+    ofproto_enumerate_types(&types);
+    SSET_FOR_EACH (type, &types) {
+        ofproto_type_run_fast(type);
+    }
+    sset_destroy(&types);
+
     HMAP_FOR_EACH (br, node, &all_bridges) {
         ofproto_run_fast(br->ofproto);
     }
@@ -2041,14 +2116,16 @@ bridge_run_fast(void)
 void
 bridge_run(void)
 {
-    static const struct ovsrec_open_vswitch null_cfg;
+    static struct ovsrec_open_vswitch null_cfg;
     const struct ovsrec_open_vswitch *cfg;
     struct ovsdb_idl_txn *reconf_txn = NULL;
+    struct sset types;
+    const char *type;
 
     bool vlan_splinters_changed;
     struct bridge *br;
 
-    ovsrec_open_vswitch_init((struct ovsrec_open_vswitch *) &null_cfg);
+    ovsrec_open_vswitch_init(&null_cfg);
 
     /* (Re)configure if necessary. */
     if (!reconfiguring) {
@@ -2071,6 +2148,20 @@ bridge_run(void)
     }
     cfg = ovsrec_open_vswitch_first(idl);
 
+    /* Initialize the ofproto library.  This only needs to run once, but
+     * it must be done after the configuration is set.  If the
+     * initialization has already occurred, bridge_init_ofproto()
+     * returns immediately. */
+    bridge_init_ofproto(cfg);
+
+    /* Let each datapath type do the work that it needs to do. */
+    sset_init(&types);
+    ofproto_enumerate_types(&types);
+    SSET_FOR_EACH (type, &types) {
+        ofproto_type_run(type);
+    }
+    sset_destroy(&types);
+
     /* Let each bridge do the work that it needs to do. */
     HMAP_FOR_EACH (br, node, &all_bridges) {
         ofproto_run(br->ofproto);
@@ -2173,12 +2264,22 @@ bridge_run(void)
 void
 bridge_wait(void)
 {
+    struct sset types;
+    const char *type;
+
     ovsdb_idl_wait(idl);
 
     if (reconfiguring) {
         poll_immediate_wake();
     }
 
+    sset_init(&types);
+    ofproto_enumerate_types(&types);
+    SSET_FOR_EACH (type, &types) {
+        ofproto_type_wait(type);
+    }
+    sset_destroy(&types);
+
     if (!hmap_is_empty(&all_bridges)) {
         struct bridge *br;
 
@@ -2451,6 +2552,7 @@ bridge_queue_if_cfg(struct bridge *br,
 
     if_cfg->cfg = cfg;
     if_cfg->parent = parent;
+    if_cfg->ofport = iface_pick_ofport(cfg);
     hmap_insert(&br->if_cfg_todo, &if_cfg->hmap_node,
                 hash_string(if_cfg->cfg->name, 0));
 }
@@ -2612,7 +2714,7 @@ bridge_configure_local_iface_netdev(struct bridge *br,
     }
     if (!netdev_set_in4(netdev, ip, mask)) {
         VLOG_INFO("bridge %s: configured IP address "IP_FMT", netmask "IP_FMT,
-                  br->name, IP_ARGS(&ip.s_addr), IP_ARGS(&mask.s_addr));
+                  br->name, IP_ARGS(ip.s_addr), IP_ARGS(mask.s_addr));
     }
 
     /* Configure the default gateway. */
@@ -2621,7 +2723,7 @@ bridge_configure_local_iface_netdev(struct bridge *br,
         && gateway.s_addr) {
         if (!netdev_add_router(netdev, gateway)) {
             VLOG_INFO("bridge %s: configured gateway "IP_FMT,
-                      br->name, IP_ARGS(&gateway.s_addr));
+                      br->name, IP_ARGS(gateway.s_addr));
         }
     }
 }
@@ -2718,7 +2820,8 @@ bridge_configure_remotes(struct bridge *br,
         n_ocs++;
     }
 
-    ofproto_set_controllers(br->ofproto, ocs, n_ocs);
+    ofproto_set_controllers(br->ofproto, ocs, n_ocs,
+                            bridge_get_allowed_versions(br));
     free(ocs[0].target); /* From bridge_ofproto_controller_for_mgmt(). */
     free(ocs);
 
@@ -2800,6 +2903,13 @@ bridge_configure_tables(struct bridge *br)
                      br->cfg->key_flow_tables[j]);
     }
 }
+
+static void
+bridge_configure_dp_desc(struct bridge *br)
+{
+    ofproto_set_dp_desc(br->ofproto,
+                        smap_get(&br->cfg->other_config, "dp-desc"));
+}
 \f
 /* Port functions. */
 
@@ -3056,17 +3166,32 @@ port_is_synthetic(const struct port *port)
 \f
 /* Interface functions. */
 
+static bool
+iface_is_internal(const struct ovsrec_interface *iface,
+                  const struct ovsrec_bridge *br)
+{
+    /* The local port and "internal" ports are always "internal". */
+    return !strcmp(iface->type, "internal") || !strcmp(iface->name, br->name);
+}
+
 /* Returns the correct network device type for interface 'iface' in bridge
  * 'br'. */
 static const char *
 iface_get_type(const struct ovsrec_interface *iface,
                const struct ovsrec_bridge *br)
 {
-    /* The local port always has type "internal".  Other ports take their type
-     * from the database and default to "system" if none is specified. */
-    return (!strcmp(iface->name, br->name) ? "internal"
-            : iface->type[0] ? iface->type
-            : "system");
+    const char *type;
+
+    /* The local port always has type "internal".  Other ports take
+     * their type from the database and default to "system" if none is
+     * specified. */
+    if (iface_is_internal(iface, br)) {
+        type = "internal";
+    } else {
+        type = iface->type[0] ? iface->type : "system";
+    }
+
+    return ofproto_port_open_type(br->datapath_type, type);
 }
 
 static void
@@ -3197,7 +3322,6 @@ static void
 iface_clear_db_record(const struct ovsrec_interface *if_cfg)
 {
     if (!ovsdb_idl_row_is_synthetic(&if_cfg->header_)) {
-        iface_set_ofport(if_cfg, -1);
         ovsrec_interface_set_status(if_cfg, NULL);
         ovsrec_interface_set_admin_state(if_cfg, NULL);
         ovsrec_interface_set_duplex(if_cfg, NULL);
@@ -3367,6 +3491,13 @@ iface_is_synthetic(const struct iface *iface)
     return ovsdb_idl_row_is_synthetic(&iface->cfg->header_);
 }
 
+static int64_t
+iface_pick_ofport(const struct ovsrec_interface *cfg)
+{
+    int64_t ofport = cfg->n_ofport ? *cfg->ofport : OFPP_NONE;
+    return cfg->n_ofport_request ? *cfg->ofport_request : ofport;
+}
+
 \f
 /* Port mirroring. */
 
diff --git a/vswitchd/ovs-brcompatd.8.in b/vswitchd/ovs-brcompatd.8.in
deleted file mode 100644 (file)
index fdce042..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-.TH ovs\-brcompatd 8 "March 2009" "Open vSwitch" "Open vSwitch Manual"
-.ds PN ovs\-brcompatd
-.
-.SH NAME
-ovs\-brcompatd \- Bridge compatibility front-end for ovs\-vswitchd
-.
-.SH SYNOPSIS
-.B ovs\-brcompatd
-[\fIoptions\fR]
-.
-.SH DESCRIPTION
-A daemon that provides a legacy bridge front-end for \fBovs\-vswitchd\fR.  It
-does this by listening for bridge ioctl commands (e.g., those generated by
-the \fBbrctl\fR program) to add or remove datapaths and the interfaces
-that attach to them.
-.PP
-.SH OPTIONS
-.IP "\fB\-\-appctl=\fIprogram\fR"
-Sets the name to the program that \fBovs\-brcompatd\fR runs to
-communicate with \fBovs\-vswitchd\fR.  The default is
-\fBovs\-appctl\fR.  Unless \fIprogram\fR contains \fB/\fR,
-\fBovs\-brcompatd\fR will search the \fBPATH\fR environment variable
-to find it.
-.
-.IP "\fB\-\-vsctl=\fIprogram\fR"
-Sets the name to the program that \fBovs\-brcompatd\fR runs to
-communicate with \fBovsdb\-server\fR.  The default is
-\fBovs\-vsctl\fR.  Unless \fIprogram\fR contains \fB/\fR,
-\fBovs\-brcompatd\fR will search the \fBPATH\fR environment variable
-to find it.
-.
-.ds DD
-.so lib/daemon.man
-.so lib/vlog.man
-.so lib/common.man
-.so lib/leak-checker.man
-.
-.SH NOTES
-\fBovs\-brcompatd\fR requires the \fBbrcompat.ko\fR kernel module to be
-loaded.
-.SH "SEE ALSO"
-.BR ovs\-appctl (8),
-.BR ovs\-vsctl (8),
-.BR ovs\-vswitchd (8),
-.BR ovsdb\-server (1),
-\fBINSTALL.bridge\fR in the Open vSwitch distribution.
diff --git a/vswitchd/ovs-brcompatd.c b/vswitchd/ovs-brcompatd.c
deleted file mode 100644 (file)
index df9332f..0000000
+++ /dev/null
@@ -1,943 +0,0 @@
-/* Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
- *
- * 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 <asm/param.h>
-#include <assert.h>
-#include <errno.h>
-#include <getopt.h>
-#include <inttypes.h>
-#include <limits.h>
-#include <net/if.h>
-#include <linux/genetlink.h>
-#include <linux/rtnetlink.h>
-#include <signal.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <sys/stat.h>
-#include <time.h>
-#include <fcntl.h>
-#include <unistd.h>
-
-#include "command-line.h"
-#include "coverage.h"
-#include "daemon.h"
-#include "dirs.h"
-#include "dynamic-string.h"
-#include "fatal-signal.h"
-#include "json.h"
-#include "leak-checker.h"
-#include "netdev.h"
-#include "netlink.h"
-#include "netlink-notifier.h"
-#include "netlink-socket.h"
-#include "ofpbuf.h"
-#include "openvswitch/brcompat-netlink.h"
-#include "packets.h"
-#include "poll-loop.h"
-#include "process.h"
-#include "rtnetlink-link.h"
-#include "signals.h"
-#include "sset.h"
-#include "svec.h"
-#include "timeval.h"
-#include "unixctl.h"
-#include "util.h"
-#include "vlog.h"
-
-VLOG_DEFINE_THIS_MODULE(brcompatd);
-
-/* xxx Just hangs if datapath is rmmod/insmod.  Learn to reconnect? */
-
-static void parse_options(int argc, char *argv[]);
-static void usage(void) NO_RETURN;
-
-static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 60);
-
-/* --appctl: Absolute path to ovs-appctl. */
-static char *appctl_program;
-
-/* --vsctl: Absolute path to ovs-vsctl. */
-static char *vsctl_program;
-
-/* Options that we should generally pass to ovs-vsctl. */
-#define VSCTL_OPTIONS "--timeout=5", "-vconsole:warn"
-
-/* Netlink socket to bridge compatibility kernel module. */
-static struct nl_sock *brc_sock;
-
-/* The Generic Netlink family number used for bridge compatibility. */
-static int brc_family;
-
-static const struct nl_policy brc_multicast_policy[] = {
-    [BRC_GENL_A_MC_GROUP] = {.type = NL_A_U32 }
-};
-
-static char *
-capture_vsctl_valist(const char *arg0, va_list args)
-{
-    char *stdout_log, *stderr_log;
-    enum vlog_level log_level;
-    struct svec argv;
-    int status;
-    char *msg;
-
-    /* Compose arguments. */
-    svec_init(&argv);
-    svec_add(&argv, arg0);
-    for (;;) {
-        const char *arg = va_arg(args, const char *);
-        if (!arg) {
-            break;
-        }
-        svec_add(&argv, arg);
-    }
-    svec_terminate(&argv);
-
-    /* Run process. */
-    if (process_run_capture(argv.names, &stdout_log, &stderr_log, SIZE_MAX,
-                            &status)) {
-        svec_destroy(&argv);
-        return NULL;
-    }
-
-    /* Log results. */
-    if (WIFEXITED(status)) {
-        int code = WEXITSTATUS(status);
-        log_level = code == 0 ? VLL_DBG : code == 1 ? VLL_WARN : VLL_ERR;
-    } else {
-        log_level = VLL_ERR;
-    }
-    msg = process_status_msg(status);
-    VLOG(log_level, "ovs-vsctl exited (%s)", msg);
-    if (stdout_log && *stdout_log) {
-        VLOG(log_level, "ovs-vsctl wrote to stdout:\n%s\n", stdout_log);
-    }
-    if (stderr_log && *stderr_log) {
-        VLOG(log_level, "ovs-vsctl wrote to stderr:\n%s\n", stderr_log);
-    }
-    free(msg);
-
-    svec_destroy(&argv);
-
-    free(stderr_log);
-    if (WIFEXITED(status) && !WEXITSTATUS(status)) {
-        return stdout_log;
-    } else {
-        free(stdout_log);
-        return NULL;
-    }
-}
-
-static char * SENTINEL(0)
-capture_vsctl(const char *arg0, ...)
-{
-    char *stdout_log;
-    va_list args;
-
-    va_start(args, arg0);
-    stdout_log = capture_vsctl_valist(arg0, args);
-    va_end(args);
-
-    return stdout_log;
-}
-
-static bool SENTINEL(0)
-run_vsctl(const char *arg0, ...)
-{
-    char *stdout_log;
-    va_list args;
-    bool ok;
-
-    va_start(args, arg0);
-    stdout_log = capture_vsctl_valist(arg0, args);
-    va_end(args);
-
-    ok = stdout_log != NULL;
-    free(stdout_log);
-    return ok;
-}
-
-static int
-lookup_brc_multicast_group(int *multicast_group)
-{
-    struct nl_sock *sock;
-    struct ofpbuf request, *reply;
-    struct nlattr *attrs[ARRAY_SIZE(brc_multicast_policy)];
-    int retval;
-
-    retval = nl_sock_create(NETLINK_GENERIC, &sock);
-    if (retval) {
-        return retval;
-    }
-    ofpbuf_init(&request, 0);
-    nl_msg_put_genlmsghdr(&request, 0, brc_family,
-            NLM_F_REQUEST, BRC_GENL_C_QUERY_MC, 1);
-    retval = nl_sock_transact(sock, &request, &reply);
-    ofpbuf_uninit(&request);
-    if (retval) {
-        nl_sock_destroy(sock);
-        return retval;
-    }
-    if (!nl_policy_parse(reply, NLMSG_HDRLEN + GENL_HDRLEN,
-                         brc_multicast_policy, attrs,
-                         ARRAY_SIZE(brc_multicast_policy))) {
-        nl_sock_destroy(sock);
-        ofpbuf_delete(reply);
-        return EPROTO;
-    }
-    *multicast_group = nl_attr_get_u32(attrs[BRC_GENL_A_MC_GROUP]);
-    nl_sock_destroy(sock);
-    ofpbuf_delete(reply);
-
-    return 0;
-}
-
-/* Opens a socket for brcompat notifications.  Returns 0 if successful,
- * otherwise a positive errno value. */
-static int
-brc_open(struct nl_sock **sock)
-{
-    int multicast_group = 0;
-    int retval;
-
-    retval = nl_lookup_genl_family(BRC_GENL_FAMILY_NAME, &brc_family);
-    if (retval) {
-        return retval;
-    }
-
-    retval = lookup_brc_multicast_group(&multicast_group);
-    if (retval) {
-        return retval;
-    }
-
-    retval = nl_sock_create(NETLINK_GENERIC, sock);
-    if (retval) {
-        return retval;
-    }
-
-    retval = nl_sock_join_mcgroup(*sock, multicast_group);
-    if (retval) {
-        nl_sock_destroy(*sock);
-        *sock = NULL;
-    }
-    return retval;
-}
-
-static const struct nl_policy brc_dp_policy[] = {
-    [BRC_GENL_A_DP_NAME] = { .type = NL_A_STRING },
-};
-
-static int
-parse_command(struct ofpbuf *buffer, uint32_t *seq, const char **br_name,
-              const char **port_name, uint64_t *count, uint64_t *skip)
-{
-    static const struct nl_policy policy[] = {
-        [BRC_GENL_A_DP_NAME] = { .type = NL_A_STRING, .optional = true },
-        [BRC_GENL_A_PORT_NAME] = { .type = NL_A_STRING, .optional = true },
-        [BRC_GENL_A_FDB_COUNT] = { .type = NL_A_U64, .optional = true },
-        [BRC_GENL_A_FDB_SKIP] = { .type = NL_A_U64, .optional = true },
-    };
-    struct nlattr *attrs[ARRAY_SIZE(policy)];
-
-    if (!nl_policy_parse(buffer, NLMSG_HDRLEN + GENL_HDRLEN, policy,
-                         attrs, ARRAY_SIZE(policy))
-        || (br_name && !attrs[BRC_GENL_A_DP_NAME])
-        || (port_name && !attrs[BRC_GENL_A_PORT_NAME])
-        || (count && !attrs[BRC_GENL_A_FDB_COUNT])
-        || (skip && !attrs[BRC_GENL_A_FDB_SKIP])) {
-        return EINVAL;
-    }
-
-    *seq = ((struct nlmsghdr *) buffer->data)->nlmsg_seq;
-    if (br_name) {
-        *br_name = nl_attr_get_string(attrs[BRC_GENL_A_DP_NAME]);
-    }
-    if (port_name) {
-        *port_name = nl_attr_get_string(attrs[BRC_GENL_A_PORT_NAME]);
-    }
-    if (count) {
-        *count = nl_attr_get_u64(attrs[BRC_GENL_A_FDB_COUNT]);
-    }
-    if (skip) {
-        *skip = nl_attr_get_u64(attrs[BRC_GENL_A_FDB_SKIP]);
-    }
-    return 0;
-}
-
-/* Composes and returns a reply to a request made by the datapath with error
- * code 'error'.  The caller may add additional attributes to the message, then
- * it may send it with send_reply(). */
-static struct ofpbuf *
-compose_reply(int error)
-{
-    struct ofpbuf *reply = ofpbuf_new(4096);
-    nl_msg_put_genlmsghdr(reply, 32, brc_family, NLM_F_REQUEST,
-                          BRC_GENL_C_DP_RESULT, 1);
-    nl_msg_put_u32(reply, BRC_GENL_A_ERR_CODE, error);
-    return reply;
-}
-
-/* Sends 'reply' to the datapath, using sequence number 'nlmsg_seq', and frees
- * it. */
-static void
-send_reply(struct ofpbuf *reply, uint32_t nlmsg_seq)
-{
-    int retval = nl_sock_send_seq(brc_sock, reply, nlmsg_seq, false);
-    if (retval) {
-        VLOG_WARN_RL(&rl, "replying to brcompat request: %s",
-                     strerror(retval));
-    }
-    ofpbuf_delete(reply);
-}
-
-/* Composes and sends a reply to a request made by the datapath with Netlink
- * sequence number 'seq' and error code 'error'. */
-static void
-send_simple_reply(uint32_t seq, int error)
-{
-    send_reply(compose_reply(error), seq);
-}
-
-static int
-handle_bridge_cmd(struct ofpbuf *buffer, bool add)
-{
-    const char *br_name;
-    uint32_t seq;
-    int error;
-
-    error = parse_command(buffer, &seq, &br_name, NULL, NULL, NULL);
-    if (!error) {
-        const char *vsctl_cmd = add ? "add-br" : "del-br";
-        const char *brctl_cmd = add ? "addbr" : "delbr";
-        if (!run_vsctl(vsctl_program, VSCTL_OPTIONS,
-                       "--", vsctl_cmd, br_name,
-                       "--", "comment", "ovs-brcompatd:", brctl_cmd, br_name,
-                       (char *) NULL)) {
-            error = add ? EEXIST : ENXIO;
-        }
-        send_simple_reply(seq, error);
-    }
-    return error;
-}
-
-static const struct nl_policy brc_port_policy[] = {
-    [BRC_GENL_A_DP_NAME] = { .type = NL_A_STRING },
-    [BRC_GENL_A_PORT_NAME] = { .type = NL_A_STRING },
-};
-
-static int
-handle_port_cmd(struct ofpbuf *buffer, bool add)
-{
-    const char *br_name, *port_name;
-    uint32_t seq;
-    int error;
-
-    error = parse_command(buffer, &seq, &br_name, &port_name, NULL, NULL);
-    if (!error) {
-        const char *vsctl_cmd = add ? "add-port" : "del-port";
-        const char *brctl_cmd = add ? "addif" : "delif";
-        if (!run_vsctl(vsctl_program, VSCTL_OPTIONS,
-                       "--", vsctl_cmd, br_name, port_name,
-                       "--", "comment", "ovs-brcompatd:", brctl_cmd,
-                       br_name, port_name, (char *) NULL)) {
-            error = EINVAL;
-        }
-        send_simple_reply(seq, error);
-    }
-    return error;
-}
-
-static char *
-linux_bridge_to_ovs_bridge(const char *linux_name, int *br_vlanp)
-{
-    char *save_ptr = NULL;
-    const char *br_name, *br_vlan;
-    char *br_name_copy;
-    char *output;
-
-    output = capture_vsctl(vsctl_program, VSCTL_OPTIONS,
-                           "--", "br-to-parent", linux_name,
-                           "--", "br-to-vlan", linux_name,
-                           (char *) NULL);
-    if (!output) {
-        return NULL;
-    }
-
-    br_name = strtok_r(output, " \t\r\n", &save_ptr);
-    br_vlan = strtok_r(NULL, " \t\r\n", &save_ptr);
-    if (!br_name || !br_vlan) {
-        free(output);
-        return NULL;
-    }
-    br_name_copy = xstrdup(br_name);
-    *br_vlanp = atoi(br_vlan);
-
-    free(output);
-
-    return br_name_copy;
-}
-
-static void
-get_bridge_ifaces(const char *br_name, struct sset *ifaces)
-{
-    char *save_ptr = NULL;
-    char *output;
-    char *iface;
-
-    output = capture_vsctl(vsctl_program, VSCTL_OPTIONS, "list-ifaces",
-                           br_name, (char *) NULL);
-    if (!output) {
-        return;
-    }
-
-    for (iface = strtok_r(output, " \t\r\n", &save_ptr); iface;
-         iface = strtok_r(NULL, " \t\r\n", &save_ptr)) {
-        sset_add(ifaces, iface);
-    }
-    free(output);
-}
-
-static int
-handle_fdb_query_cmd(struct ofpbuf *buffer)
-{
-    /* This structure is copied directly from the Linux 2.6.30 header files.
-     * It would be more straightforward to #include <linux/if_bridge.h>, but
-     * the 'port_hi' member was only introduced in Linux 2.6.26 and so systems
-     * with old header files won't have it. */
-    struct __fdb_entry {
-        __u8 mac_addr[6];
-        __u8 port_no;
-        __u8 is_local;
-        __u32 ageing_timer_value;
-        __u8 port_hi;
-        __u8 pad0;
-        __u16 unused;
-    };
-
-    struct mac {
-        uint8_t addr[6];
-    };
-    struct mac *local_macs;
-    int n_local_macs;
-    int i;
-
-    /* Impedance matching between the vswitchd and Linux kernel notions of what
-     * a bridge is.  The kernel only handles a single VLAN per bridge, but
-     * vswitchd can deal with all the VLANs on a single bridge.  We have to
-     * pretend that the former is the case even though the latter is the
-     * implementation. */
-    const char *linux_name;   /* Name used by brctl. */
-    int br_vlan;                /* VLAN tag. */
-    struct sset ifaces;
-
-    struct ofpbuf query_data;
-    const char *iface_name;
-    struct ofpbuf *reply;
-    uint64_t count, skip;
-    char *br_name;
-    char *output;
-    char *save_ptr;
-    uint32_t seq;
-    int error;
-
-    /* Parse the command received from brcompat. */
-    error = parse_command(buffer, &seq, &linux_name, NULL, &count, &skip);
-    if (error) {
-        return error;
-    }
-
-    /* Figure out vswitchd bridge and VLAN. */
-    br_name = linux_bridge_to_ovs_bridge(linux_name, &br_vlan);
-    if (!br_name) {
-        error = EINVAL;
-        send_simple_reply(seq, error);
-        return error;
-    }
-
-    /* Fetch the forwarding database using ovs-appctl. */
-    output = capture_vsctl(appctl_program, "fdb/show", br_name,
-                           (char *) NULL);
-    if (!output) {
-        error = ECHILD;
-        send_simple_reply(seq, error);
-        return error;
-    }
-
-    /* Fetch the MAC address for each interface on the bridge, so that we can
-     * fill in the is_local field in the response. */
-    sset_init(&ifaces);
-    get_bridge_ifaces(linux_name, &ifaces);
-    local_macs = xmalloc(sset_count(&ifaces) * sizeof *local_macs);
-    n_local_macs = 0;
-    SSET_FOR_EACH (iface_name, &ifaces) {
-        struct mac *mac = &local_macs[n_local_macs];
-        struct netdev *netdev;
-
-        error = netdev_open(iface_name, "system", &netdev);
-        if (!error) {
-            if (!netdev_get_etheraddr(netdev, mac->addr)) {
-                n_local_macs++;
-            }
-            netdev_close(netdev);
-        }
-    }
-    sset_destroy(&ifaces);
-
-    /* Parse the response from ovs-appctl and convert it to binary format to
-     * pass back to the kernel. */
-    ofpbuf_init(&query_data, sizeof(struct __fdb_entry) * 8);
-    save_ptr = NULL;
-    strtok_r(output, "\n", &save_ptr); /* Skip header line. */
-    while (count > 0) {
-        struct __fdb_entry *entry;
-        int port, vlan, age;
-        uint8_t mac[ETH_ADDR_LEN];
-        char *line;
-        bool is_local;
-
-        line = strtok_r(NULL, "\n", &save_ptr);
-        if (!line) {
-            break;
-        }
-
-        if (sscanf(line, "%d %d "ETH_ADDR_SCAN_FMT" %d",
-                   &port, &vlan, ETH_ADDR_SCAN_ARGS(mac), &age)
-            != 2 + ETH_ADDR_SCAN_COUNT + 1) {
-            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
-            VLOG_INFO_RL(&rl, "fdb/show output has invalid format: %s", line);
-            continue;
-        }
-
-        if (vlan != br_vlan) {
-            continue;
-        }
-
-        if (skip > 0) {
-            skip--;
-            continue;
-        }
-
-        /* Is this the MAC address of an interface on the bridge? */
-        is_local = false;
-        for (i = 0; i < n_local_macs; i++) {
-            if (eth_addr_equals(local_macs[i].addr, mac)) {
-                is_local = true;
-                break;
-            }
-        }
-
-        entry = ofpbuf_put_uninit(&query_data, sizeof *entry);
-        memcpy(entry->mac_addr, mac, ETH_ADDR_LEN);
-        entry->port_no = port & 0xff;
-        entry->is_local = is_local;
-        entry->ageing_timer_value = age * HZ;
-        entry->port_hi = (port & 0xff00) >> 8;
-        entry->pad0 = 0;
-        entry->unused = 0;
-        count--;
-    }
-    free(output);
-
-    /* Compose and send reply to datapath. */
-    reply = compose_reply(0);
-    nl_msg_put_unspec(reply, BRC_GENL_A_FDB_DATA,
-                      query_data.data, query_data.size);
-    send_reply(reply, seq);
-
-    /* Free memory. */
-    ofpbuf_uninit(&query_data);
-    free(local_macs);
-
-    return 0;
-}
-
-static void
-send_ifindex_reply(uint32_t seq, char *output)
-{
-    size_t allocated_indices;
-    char *save_ptr = NULL;
-    struct ofpbuf *reply;
-    const char *iface;
-    size_t n_indices;
-    int *indices;
-
-    indices = NULL;
-    n_indices = allocated_indices = 0;
-    for (iface = strtok_r(output, " \t\r\n", &save_ptr); iface;
-         iface = strtok_r(NULL, " \t\r\n", &save_ptr)) {
-        int ifindex;
-
-        if (n_indices >= allocated_indices) {
-            indices = x2nrealloc(indices, &allocated_indices, sizeof *indices);
-        }
-
-        ifindex = if_nametoindex(iface);
-        if (ifindex) {
-            indices[n_indices++] = ifindex;
-        }
-    }
-
-    /* Compose and send reply. */
-    reply = compose_reply(0);
-    nl_msg_put_unspec(reply, BRC_GENL_A_IFINDEXES,
-                      indices, n_indices * sizeof *indices);
-    send_reply(reply, seq);
-
-    /* Free memory. */
-    free(indices);
-}
-
-static int
-handle_get_bridges_cmd(struct ofpbuf *buffer)
-{
-    char *output;
-    uint32_t seq;
-    int error;
-
-    /* Parse Netlink command.
-     *
-     * The command doesn't actually have any arguments, but we need the
-     * sequence number to send the reply. */
-    error = parse_command(buffer, &seq, NULL, NULL, NULL, NULL);
-    if (error) {
-        return error;
-    }
-
-    output = capture_vsctl(vsctl_program, VSCTL_OPTIONS, "list-br", (char *) NULL);
-    if (!output) {
-        return ENODEV;
-    }
-
-    send_ifindex_reply(seq, output);
-    free(output);
-    return 0;
-}
-
-static int
-handle_get_ports_cmd(struct ofpbuf *buffer)
-{
-    const char *linux_name;
-    uint32_t seq;
-    char *output;
-    int error;
-
-    /* Parse Netlink command. */
-    error = parse_command(buffer, &seq, &linux_name, NULL, NULL, NULL);
-    if (error) {
-        return error;
-    }
-
-    output = capture_vsctl(vsctl_program, VSCTL_OPTIONS, "list-ports", linux_name,
-                           (char *) NULL);
-    if (!output) {
-        return ENODEV;
-    }
-
-    send_ifindex_reply(seq, output);
-    free(output);
-    return 0;
-}
-
-static bool
-brc_recv_update__(struct ofpbuf *buffer)
-{
-    for (;;) {
-        int retval = nl_sock_recv(brc_sock, buffer, false);
-        switch (retval) {
-        case 0:
-            if (nl_msg_nlmsgerr(buffer, NULL)
-                || nl_msg_nlmsghdr(buffer)->nlmsg_type == NLMSG_DONE) {
-                break;
-            }
-            return true;
-
-        case ENOBUFS:
-            break;
-
-        case EAGAIN:
-            return false;
-
-        default:
-            VLOG_WARN_RL(&rl, "brc_recv_update: %s", strerror(retval));
-            return false;
-        }
-    }
-}
-
-static void
-brc_recv_update(void)
-{
-    struct genlmsghdr *genlmsghdr;
-    uint64_t buffer_stub[1024 / 8];
-    struct ofpbuf buffer;
-
-    ofpbuf_use_stub(&buffer, buffer_stub, sizeof buffer_stub);
-    if (!brc_recv_update__(&buffer)) {
-        goto error;
-    }
-
-    genlmsghdr = nl_msg_genlmsghdr(&buffer);
-    if (!genlmsghdr) {
-        VLOG_WARN_RL(&rl, "received packet too short for generic NetLink");
-        goto error;
-    }
-
-    if (nl_msg_nlmsghdr(&buffer)->nlmsg_type != brc_family) {
-        VLOG_DBG_RL(&rl, "received type (%"PRIu16") != brcompat family (%d)",
-                nl_msg_nlmsghdr(&buffer)->nlmsg_type, brc_family);
-        goto error;
-    }
-
-    /* Service all pending network device notifications before executing the
-     * command.  This is very important to avoid a race in a scenario like the
-     * following, which is what happens with XenServer Tools version 5.0.0
-     * during boot of a Windows VM:
-     *
-     *      1. Create tap1.0 and vif1.0.
-     *      2. Delete tap1.0.
-     *      3. Delete vif1.0.
-     *      4. Re-create vif1.0.
-     *
-     * We must process the network device notification from step 3 before we
-     * process the brctl command from step 4.  If we process them in the
-     * reverse order, then step 4 completes as a no-op but step 3 then deletes
-     * the port that was just added.
-     *
-     * (XenServer Tools 5.5.0 does not exhibit this behavior, and neither does
-     * a VM without Tools installed at all.)
-     */
-    rtnetlink_link_run();
-
-    switch (genlmsghdr->cmd) {
-    case BRC_GENL_C_DP_ADD:
-        handle_bridge_cmd(&buffer, true);
-        break;
-
-    case BRC_GENL_C_DP_DEL:
-        handle_bridge_cmd(&buffer, false);
-        break;
-
-    case BRC_GENL_C_PORT_ADD:
-        handle_port_cmd(&buffer, true);
-        break;
-
-    case BRC_GENL_C_PORT_DEL:
-        handle_port_cmd(&buffer, false);
-        break;
-
-    case BRC_GENL_C_FDB_QUERY:
-        handle_fdb_query_cmd(&buffer);
-        break;
-
-    case BRC_GENL_C_GET_BRIDGES:
-        handle_get_bridges_cmd(&buffer);
-        break;
-
-    case BRC_GENL_C_GET_PORTS:
-        handle_get_ports_cmd(&buffer);
-        break;
-
-    default:
-        VLOG_WARN_RL(&rl, "received unknown brc netlink command: %d\n",
-                     genlmsghdr->cmd);
-        break;
-    }
-
-error:
-    ofpbuf_uninit(&buffer);
-}
-
-static void
-netdev_changed_cb(const struct rtnetlink_link_change *change,
-                  void *aux OVS_UNUSED)
-{
-    char br_name[IFNAMSIZ];
-    const char *port_name;
-
-    if (!change) {
-        VLOG_WARN_RL(&rl, "network monitor socket overflowed");
-        return;
-    }
-
-    if (change->nlmsg_type != RTM_DELLINK || !change->master_ifindex) {
-        return;
-    }
-
-    port_name = change->ifname;
-    if (!if_indextoname(change->master_ifindex, br_name)) {
-        return;
-    }
-
-    VLOG_INFO("network device %s destroyed, removing from bridge %s",
-              port_name, br_name);
-
-    run_vsctl(vsctl_program, VSCTL_OPTIONS,
-              "--", "--if-exists", "del-port", port_name,
-              "--", "comment", "ovs-brcompatd:", port_name, "disappeared",
-              (char *) NULL);
-}
-
-int
-main(int argc, char *argv[])
-{
-    extern struct vlog_module VLM_reconnect;
-    struct nln_notifier *link_notifier;
-    struct unixctl_server *unixctl;
-    int retval;
-
-    proctitle_init(argc, argv);
-    set_program_name(argv[0]);
-    vlog_set_levels(&VLM_reconnect, VLF_ANY_FACILITY, VLL_WARN);
-
-    parse_options(argc, argv);
-    signal(SIGPIPE, SIG_IGN);
-    process_init();
-
-    daemonize_start();
-
-    retval = unixctl_server_create(NULL, &unixctl);
-    if (retval) {
-        exit(EXIT_FAILURE);
-    }
-
-    if (brc_open(&brc_sock)) {
-        VLOG_FATAL("could not open brcompat socket.  Check "
-                   "\"brcompat\" kernel module.");
-    }
-
-    link_notifier = rtnetlink_link_notifier_create(netdev_changed_cb, NULL);
-
-    daemonize_complete();
-
-    for (;;) {
-        unixctl_server_run(unixctl);
-        rtnetlink_link_run();
-        brc_recv_update();
-
-        netdev_run();
-
-        nl_sock_wait(brc_sock, POLLIN);
-        unixctl_server_wait(unixctl);
-        rtnetlink_link_wait();
-        netdev_wait();
-        poll_block();
-    }
-
-    rtnetlink_link_notifier_destroy(link_notifier);
-
-    return 0;
-}
-
-static void
-parse_options(int argc, char *argv[])
-{
-    enum {
-        OPT_APPCTL,
-        OPT_VSCTL,
-        VLOG_OPTION_ENUMS,
-        LEAK_CHECKER_OPTION_ENUMS,
-        DAEMON_OPTION_ENUMS
-    };
-    static struct option long_options[] = {
-        {"help",             no_argument, NULL, 'h'},
-        {"version",          no_argument, NULL, 'V'},
-        {"appctl",           required_argument, NULL, OPT_APPCTL},
-        {"vsctl",            required_argument, NULL, OPT_VSCTL},
-        DAEMON_LONG_OPTIONS,
-        VLOG_LONG_OPTIONS,
-        LEAK_CHECKER_LONG_OPTIONS,
-        {NULL, 0, NULL, 0},
-    };
-    char *short_options = long_options_to_short_options(long_options);
-    const char *appctl = "ovs-appctl";
-    const char *vsctl = "ovs-vsctl";
-
-    for (;;) {
-        int c;
-
-        c = getopt_long(argc, argv, short_options, long_options, NULL);
-        if (c == -1) {
-            break;
-        }
-
-        switch (c) {
-        case 'h':
-            usage();
-
-        case 'V':
-            ovs_print_version(0, 0);
-            exit(EXIT_SUCCESS);
-
-        case OPT_APPCTL:
-            appctl = optarg;
-            break;
-
-        case OPT_VSCTL:
-            vsctl = optarg;
-            break;
-
-        VLOG_OPTION_HANDLERS
-        DAEMON_OPTION_HANDLERS
-        LEAK_CHECKER_OPTION_HANDLERS
-
-        case '?':
-            exit(EXIT_FAILURE);
-
-        default:
-            abort();
-        }
-    }
-    free(short_options);
-
-    appctl_program = process_search_path(appctl);
-    if (!appctl_program) {
-        VLOG_FATAL("%s: not found in $PATH (use --appctl to specify an "
-                   "alternate location)", appctl);
-    }
-
-    vsctl_program = process_search_path(vsctl);
-    if (!vsctl_program) {
-        VLOG_FATAL("%s: not found in $PATH (use --vsctl to specify an "
-                   "alternate location)", vsctl);
-    }
-
-    if (argc != optind) {
-        VLOG_FATAL("no non-option arguments are supported; "
-                   "use --help for usage");
-    }
-}
-
-static void
-usage(void)
-{
-    printf("%s: bridge compatibility front-end for ovs-vswitchd\n"
-           "usage: %s [OPTIONS]\n",
-           program_name, program_name);
-    printf("\nConfiguration options:\n"
-           "  --appctl=PROGRAM        overrides $PATH for finding ovs-appctl\n"
-           "  --vsctl=PROGRAM         overrides $PATH for finding ovs-vsctl\n"
-          );
-    daemon_usage();
-    vlog_usage();
-    printf("\nOther options:\n"
-           "  -h, --help              display this help message\n"
-           "  -V, --version           display version information\n");
-    leak_checker_usage();
-    exit(EXIT_SUCCESS);
-}
index 5a959dd..764ed41 100644 (file)
@@ -4,7 +4,7 @@
 .  ns
 .  IP "\\$1"
 ..
-.TH ovs\-vswitchd 8 "June 2009" "Open vSwitch" "Open vSwitch Manual"
+.TH ovs\-vswitchd 8 "@VERSION@" "Open vSwitch" "Open vSwitch Manual"
 .\" This program's name:
 .ds PN ovs\-vswitchd
 .\" SSL peer program's name:
@@ -204,6 +204,7 @@ information, and partner information.  If \fIport\fR is not specified,
 then displays detailed information about all interfaces with CFM
 enabled.
 .
+.so ofproto/ofproto-dpif-unixctl.man
 .so ofproto/ofproto-unixctl.man
 .so lib/vlog-unixctl.man
 .so lib/memory-unixctl.man
@@ -253,6 +254,5 @@ time linear in the number of flows.
 .
 .SH "SEE ALSO"
 .BR ovs\-appctl (8),
-.BR ovs\-brcompatd (8),
 .BR ovsdb\-server (1),
 \fBINSTALL.Linux\fR in the Open vSwitch distribution.
index bbfb01f..16125a5 100644 (file)
@@ -1,6 +1,6 @@
 {"name": "Open_vSwitch",
- "version": "6.10.0",
- "cksum": "3699312094 16958",
+ "version": "6.11.3",
+ "cksum": "2234602985 17310",
  "tables": {
    "Open_vSwitch": {
      "columns": {
          "type": {"key": {"type": "uuid",
                           "refTable": "Controller"},
                   "min": 0, "max": "unlimited"}},
+       "protocols": {
+         "type": {"key": {"type": "string",
+           "enum": ["set", ["OpenFlow10", "OpenFlow12", "OpenFlow13"]]},
+          "min": 0, "max": "unlimited"}},
        "fail_mode": {
          "type": {"key": {"type": "string",
                           "enum": ["set", ["standalone", "secure"]]},
        "external_ids": {
          "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}},
        "ofport": {
-         "type": {"key": "integer", "min": 0, "max": 1},
-         "ephemeral": true},
+         "type": {"key": "integer", "min": 0, "max": 1}},
+       "ofport_request": {
+         "type": {
+           "key": {"type": "integer",
+                   "minInteger": 1,
+                   "maxInteger": 65279},
+           "min": 0,
+           "max": 1}},
        "cfm_mpid": {
          "type": {
            "key": {"type": "integer"},
index 0bc4ccd..18643c2 100644 (file)
         value.  May not be all-zero.
       </column>
 
+      <column name="other_config" key="dp-desc">
+        Human readable description of datapath.  It it a maximum 256
+        byte-long free-form string to describe the datapath for
+        debugging purposes, e.g. <code>switch3 in room 3120</code>.
+      </column>
+
       <column name="other_config" key="disable-in-band"
               type='{"type": "boolean"}'>
         If set to <code>true</code>, disable in-band control on the bridge
         QoS configured, or if the port does not have a queue with the specified
         ID, the default queue is used instead.
       </column>
+
+      <column name="protocols">
+        List of OpenFlow protocols that may be used when negotiating a
+        connection with a controller.  A default value of
+        <code>OpenFlow10</code> will be used if this column is empty.
+      </column>
     </group>
 
     <group title="Spanning Tree Configuration">
           transmit packets.
         </p>
       </column>
+
+      <column name="other_config" key="mac-table-size"
+              type='{"type": "integer", "minInteger": 1}'>
+        <p>
+          The maximum number of MAC addresses to learn.  The default is
+          currently 2048.  The value, if specified, is forced into a reasonable
+          range, currently 10 to 1,000,000.
+        </p>
+      </column>
     </group>
 
     <group title="Bridge Status">
         port number for the OpenFlow ``local port'').  If the interface
         cannot be added then Open vSwitch sets this column
         to -1.</p>
+        <p>When <ref column="ofport_request"/> is not set, Open vSwitch picks
+        an appropriate value for this column and then tries to keep the value
+        constant across restarts.</p>
+      </column>
+
+      <column name="ofport_request">
+        <p>Requested OpenFlow port number for this interface.  The port
+        number must be between 1 and 65279, inclusive.  Some datapaths
+        cannot satisfy all requests for particular port numbers.  When
+        this column is empty or the request cannot be fulfilled, the
+        system will choose a free port.  The <ref column="ofport"/>
+        column reports the assigned OpenFlow port number.</p>
+        <p>The port number must be requested in the same transaction
+        that creates the port.</p>
       </column>
     </group>
 
           <dt><code>gre</code></dt>
           <dd>
             An Ethernet over RFC 2890 Generic Routing Encapsulation over IPv4
-            tunnel.  See <ref group="Tunnel Options"/> for information on
-            configuring GRE tunnels.
+            tunnel.
           </dd>
 
           <dt><code>ipsec_gre</code></dt>
             implemented.  UDP ports 58881 and 58882 are used as the source and
             destination ports respectively.  CAPWAP is currently supported only
             with the Linux kernel datapath with kernel version 2.6.26 or later.
+
+            CAPWAP support is deprecated and will be removed no earlier than
+            February 2013.
+          </dd>
+
+          <dt><code>vxlan</code></dt>
+          <dd>
+           <p>
+             An Ethernet tunnel over the experimental, UDP-based VXLAN
+             protocol described at
+             <code>http://tools.ietf.org/html/draft-mahalingam-dutt-dcops-vxlan-02</code>.
+             VXLAN is currently supported only with the Linux kernel datapath
+             with kernel version 2.6.26 or later.
+           </p>
+           <p>
+             As an experimental protocol, VXLAN has no officially assigned UDP
+             port.  Open vSwitch currently uses UDP destination port 8472.
+             The source port used for VXLAN traffic varies on a per-flow basis
+             and is in the ephemeral port range.
+           </p>
           </dd>
 
           <dt><code>patch</code></dt>
       <p>
         These options apply to interfaces with <ref column="type"/> of
         <code>gre</code>, <code>ipsec_gre</code>, <code>gre64</code>,
-        <code>ipsec_gre64</code>, and <code>capwap</code>.
+        <code>ipsec_gre64</code>, <code>capwap</code>, and
+        <code>vxlan</code>.
       </p>
 
       <p>
             key="in_key"/> at all.
           </li>
           <li>
-            A positive 32-bit (for GRE) or 64-bit (for CAPWAP) number.  The
-            tunnel receives only packets with the specified key.
+            A positive 24-bit (for VXLAN), 32-bit (for GRE) or 64-bit (for
+            CAPWAP) number.  The tunnel receives only packets with the
+            specified key.
           </li>
           <li>
             The word <code>flow</code>.  The tunnel accepts packets with any
             key="out_key"/> at all.
           </li>
           <li>
-            A positive 32-bit (for GRE) or 64-bit (for CAPWAP) number.  Packets
-            sent through the tunnel will have the specified key.
+            A positive 24-bit (for VXLAN), 32-bit (for GRE) or 64-bit (for
+            CAPWAP) number.  Packets sent through the tunnel will have the
+            specified key.
           </li>
           <li>
             The word <code>flow</code>.  Packets sent through the tunnel will
         system default, typically 64).  Default is the system default TTL.
       </column>
 
-      <column name="options" key="df_inherit" type='{"type": "boolean"}'>
-        Optional.  If enabled, the Don't Fragment bit will be copied from the
-        inner IP headers (those of the encapsulated traffic) to the outer
-        (tunnel) headers.  Default is disabled; set to <code>true</code> to
-        enable.
-      </column>
-
       <column name="options" key="df_default"
               type='{"type": "boolean"}'>
-        Optional.  If enabled, the Don't Fragment bit will be set by default on
-        tunnel headers if the <code>df_inherit</code> option is not set, or if
-        the encapsulated packet is not IP.  Default is enabled; set to
-        <code>false</code> to disable.
-      </column>
-
-      <column name="options" key="pmtud" type='{"type": "boolean"}'>
-        Optional.  Enable tunnel path MTU discovery.  If enabled ``ICMP
-        Destination Unreachable - Fragmentation Needed'' messages will be
-        generated for IPv4 packets with the DF bit set and IPv6 packets above
-        the minimum MTU if the packet size exceeds the path MTU minus the size
-        of the tunnel headers.  Note that this option causes behavior that is
-        typically reserved for routers and therefore is not entirely in
-        compliance with the IEEE 802.1D specification for bridges.  Default is
-        disabled; set to <code>true</code> to enable.
+        Optional.  If enabled, the Don't Fragment bit will be set on tunnel
+        outer headers to allow path MTU discovery. Default is enabled; set
+        to <code>false</code> to disable.
       </column>
 
-      <group title="Tunnel Options: gre only">
-        <p>
-          Only <code>gre</code> interfaces support these options.
-        </p>
-
-        <column name="options" key="header_cache" type='{"type": "boolean"}'>
-          Enable caching of tunnel headers and the output path.  This can lead
-          to a significant performance increase without changing behavior.  In
-          general it should not be necessary to adjust this setting.  However,
-          the caching can bypass certain components of the IP stack (such as
-          <code>iptables</code>) and it may be useful to disable it if these
-          features are required or as a debugging measure.  Default is enabled,
-          set to <code>false</code> to disable.
-        </column>
-      </group>
-
       <group title="Tunnel Options: gre and ipsec_gre only">
         <p>
           Only <code>gre</code> and <code>ipsec_gre</code> interfaces support
index 1f29daf..7427e4c 100644 (file)
@@ -36,7 +36,7 @@ read_host_uuid(void)
     file = fopen(filename, "r");
     if (!file) {
         if (errno == ENOENT) {
-            VLOG_INFO("not running on a XenServer");
+            VLOG_DBG("not running on a XenServer");
         } else {
             VLOG_INFO("%s: open: %s", filename, strerror(errno));
         }
index 18e421b..c41a7e5 100644 (file)
@@ -285,7 +285,7 @@ done
 # provided by OVS. Any time a replacement script is removed from OVS,
 # it should be added here to ensure correct reversion from old versions of
 # OVS that don't clean up dangling symlinks during the uninstall phase.
-for orig in /usr/sbin/brctl /usr/sbin/xen-bugtool $keep_files; do
+for orig in /usr/sbin/xen-bugtool $keep_files; do
     saved=/usr/lib/openvswitch/xs-saved/$(basename "$orig")
     [ -e "$saved" ] && mv -f "$saved" "$orig"
 done
@@ -357,7 +357,6 @@ fi
 # this restore-on-upgrade logic.
 for f in \
     /etc/xensource/scripts/vif \
-    /usr/sbin/brctl \
     /usr/sbin/xen-bugtool \
     /opt/xensource/libexec/interface-reconfigure \
     /opt/xensource/libexec/InterfaceReconfigure.py \
@@ -455,8 +454,6 @@ exit 0
 /usr/share/man/man8/ovs-vswitchd.8.gz
 /var/lib/openvswitch
 %exclude /usr/lib/xsconsole/plugins-base/*.py[co]
-%exclude /usr/sbin/ovs-brcompatd
-%exclude /usr/share/man/man8/ovs-brcompatd.8.gz
 %exclude /usr/share/openvswitch/scripts/*.py[co]
 %exclude /usr/share/openvswitch/python/*.py[co]
 %exclude /usr/share/openvswitch/python/ovs/*.py[co]
@@ -464,4 +461,3 @@ exit 0
 
 %files %{module_package}
 /lib/modules/%{xen_version}/extra/openvswitch/openvswitch.ko
-%exclude /lib/modules/%{xen_version}/extra/openvswitch/brcompat.ko