Merge commit '10a89ef04df5669c5cdd02f786150a7ab8454e01'
authorGiuseppe Lettieri <g.lettieri@iet.unipi.it>
Sat, 6 Jul 2013 16:57:17 +0000 (18:57 +0200)
committerGiuseppe Lettieri <g.lettieri@iet.unipi.it>
Sat, 6 Jul 2013 16:57:17 +0000 (18:57 +0200)
275 files changed:
AUTHORS
FAQ
INSTALL
INSTALL.KVM
INSTALL.userspace
Makefile.am
NEWS
OPENFLOW-1.1+
PORTING
README-OFTest [new file with mode: 0644]
README-lisp
acinclude.m4
build-aux/extract-ofp-errors
configure.ac
datapath/README
datapath/actions.c
datapath/compat.h
datapath/datapath.c
datapath/datapath.h
datapath/flow.c
datapath/flow.h
datapath/linux/.gitignore
datapath/linux/Makefile.main.in
datapath/linux/Modules.mk
datapath/linux/compat/gre.c [new file with mode: 0644]
datapath/linux/compat/gso.c [new file with mode: 0644]
datapath/linux/compat/gso.h [new file with mode: 0644]
datapath/linux/compat/include/linux/if_ether.h
datapath/linux/compat/include/linux/kconfig.h [new file with mode: 0644]
datapath/linux/compat/include/linux/netdevice.h
datapath/linux/compat/include/net/gre.h [new file with mode: 0644]
datapath/linux/compat/include/net/ip_tunnels.h [new file with mode: 0644]
datapath/linux/compat/ip_tunnels_core.c [new file with mode: 0644]
datapath/linux/compat/skbuff-openvswitch.c
datapath/tunnel.c
datapath/tunnel.h
datapath/vlan.h
datapath/vport-gre.c
datapath/vport-internal_dev.c
datapath/vport-lisp.c
datapath/vport-netdev.c
datapath/vport-vxlan.c
datapath/vport.c
datapath/vport.h
debian/openvswitch-common.install
debian/openvswitch-common.manpages
debian/openvswitch-controller.init
include/linux/openvswitch.h
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
include/openflow/openflow-common.h
include/openvswitch/types.h
include/sparse/automake.mk
include/sparse/math.h
include/sparse/netinet/in.h
include/sparse/pthread.h [new file with mode: 0644]
include/sparse/sys/socket.h
lib/automake.mk
lib/backtrace.c
lib/bfd.c
lib/bfd.h
lib/bond.c
lib/bond.h
lib/bundle.c
lib/bundle.h
lib/cfm.c
lib/cfm.h
lib/classifier.c
lib/classifier.h
lib/command-line.c
lib/command-line.h
lib/compiler.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/dummy.c
lib/dummy.h
lib/dynamic-string.c
lib/entropy.c
lib/fatal-signal.c
lib/flow.c
lib/flow.h
lib/hindex.c [new file with mode: 0644]
lib/hindex.h [new file with mode: 0644]
lib/hmap.c
lib/json.c
lib/jsonrpc.c
lib/lacp.c
lib/lacp.h
lib/leak-checker.c [deleted file]
lib/leak-checker.h [deleted file]
lib/leak-checker.man [deleted file]
lib/learn.c
lib/learn.h
lib/learning-switch.c
lib/lockfile.c
lib/mac-learning.c
lib/mac-learning.h
lib/match.c
lib/match.h
lib/meta-flow.c
lib/meta-flow.h
lib/multipath.c
lib/multipath.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/netlink-notifier.c
lib/netlink-socket.c
lib/netlink.c
lib/netlink.h
lib/nx-match.c
lib/nx-match.h
lib/odp-execute.c [new file with mode: 0644]
lib/odp-execute.h [new file with mode: 0644]
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.h
lib/ofp-parse.c
lib/ofp-parse.h
lib/ofp-print.c
lib/ofp-util.c
lib/ofp-util.h
lib/ofp-version-opt.c
lib/ofpbuf.h
lib/ovs-atomic-c11.h [new file with mode: 0644]
lib/ovs-atomic-gcc4+.c [new file with mode: 0644]
lib/ovs-atomic-gcc4+.h [new file with mode: 0644]
lib/ovs-atomic-gcc4.7+.h [new file with mode: 0644]
lib/ovs-atomic-pthreads.c [new file with mode: 0644]
lib/ovs-atomic-pthreads.h [new file with mode: 0644]
lib/ovs-atomic.h [new file with mode: 0644]
lib/ovs-thread.c [new file with mode: 0644]
lib/ovs-thread.h [new file with mode: 0644]
lib/packets.c
lib/packets.h
lib/poll-loop.c
lib/process.c
lib/process.h
lib/rconn.c
lib/reconnect.c
lib/route-table.c
lib/rtbsd.c
lib/rtnetlink-link.c
lib/sflow_agent.c
lib/signals.c
lib/signals.h
lib/socket-util.c
lib/stream-fd.c
lib/stream-ssl.c
lib/stream-tcp.c
lib/stream-unix.c
lib/stress.h
lib/timeval.c
lib/timeval.h
lib/unixctl.c
lib/util.c
lib/util.h
lib/vconn-stream.c
lib/vconn.c
lib/vlandev.c
lib/vlandev.h
lib/vlog.c
lib/worker.c
m4/openvswitch.m4
manpages.mk
ofproto/automake.mk
ofproto/collectors.c
ofproto/connmgr.c
ofproto/connmgr.h
ofproto/in-band.c
ofproto/in-band.h
ofproto/netflow.c
ofproto/netflow.h
ofproto/ofproto-dpif-governor.c
ofproto/ofproto-dpif-governor.h
ofproto/ofproto-dpif-ipfix.c
ofproto/ofproto-dpif-ipfix.h
ofproto/ofproto-dpif-sflow.c
ofproto/ofproto-dpif-sflow.h
ofproto/ofproto-dpif-unixctl.man
ofproto/ofproto-dpif-xlate.c [new file with mode: 0644]
ofproto/ofproto-dpif-xlate.h [new file with mode: 0644]
ofproto/ofproto-dpif.c
ofproto/ofproto-dpif.h [new file with mode: 0644]
ofproto/ofproto-provider.h
ofproto/ofproto-unixctl.man
ofproto/ofproto.c
ofproto/ofproto.h
ofproto/pinsched.c
ofproto/pinsched.h
ofproto/pktbuf.c
ofproto/pktbuf.h
ofproto/tunnel.c
ofproto/tunnel.h
ovsdb/jsonrpc-server.c
ovsdb/jsonrpc-server.h
ovsdb/ovsdb-idlc.in
ovsdb/ovsdb-server.1.in
ovsdb/ovsdb-server.c
ovsdb/server.c
ovsdb/server.h
ovsdb/table.c
python/ovs/db/data.py
python/ovs/db/schema.py
rhel/openvswitch-fedora.spec.in
rhel/openvswitch.spec.in
tests/.gitignore
tests/automake.mk
tests/daemon-py.at
tests/daemon.at
tests/learn.at
tests/library.at
tests/odp.at
tests/ofp-actions.at
tests/ofp-errors.at
tests/ofp-print.at
tests/ofproto-dpif.at
tests/ofproto-macros.at
tests/ofproto.at
tests/ovs-ofctl.at
tests/ovs-vsctl.at
tests/ovsdb-server.at
tests/run-oftest [new file with mode: 0755]
tests/test-atomic.c [new file with mode: 0644]
tests/test-bundle.c
tests/test-classifier.c
tests/test-flows.c
tests/test-hindex.c [new file with mode: 0644]
tests/test-multipath.c
tests/test-netflow.c
tests/test-odp.c
tests/test-sflow.c
tests/test-vconn.c
tests/testsuite.at
tests/tunnel.at
tests/vlan-splinters.at [new file with mode: 0644]
tutorial/Tutorial
tutorial/t-stage0
utilities/.gitignore
utilities/automake.mk
utilities/ovs-controller.c
utilities/ovs-ctl.in
utilities/ovs-dpctl.c
utilities/ovs-ofctl.8.in
utilities/ovs-ofctl.c
utilities/ovs-parse-leaks.8 [deleted file]
utilities/ovs-parse-leaks.in [deleted file]
utilities/ovs-vsctl.c
vswitchd/bridge.c
vswitchd/ovs-vswitchd.8.in
vswitchd/ovs-vswitchd.c
vswitchd/system-stats.c
vswitchd/vswitch.xml
vswitchd/xenserver.c
xenserver/openvswitch-xen.spec.in
xenserver/opt_xensource_libexec_InterfaceReconfigure.py
xenserver/opt_xensource_libexec_interface-reconfigure
xenserver/usr_share_openvswitch_scripts_ovs-xapi-sync

diff --git a/AUTHORS b/AUTHORS
index c113f16..26e1f7f 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -6,6 +6,7 @@ Alexey I. Froloff       raorn@altlinux.org
 Alex Wang               alexw@nicira.com
 Andrew Evans            aevans@nicira.com
 Andrew Lambeth          wal@nicira.com
+Andy Hill               hillad@gmail.com
 Andy Southgate          andy.southgate@citrix.com
 Andy Zhou               azhou@nicira.com
 Ansis Atteka            aatteka@nicira.com
@@ -40,11 +41,13 @@ Henry Mai               hmai@nicira.com
 Hao Zheng               hzheng@nicira.com
 Ian Campbell            Ian.Campbell@citrix.com
 Isaku Yamahata          yamahata@valinux.co.jp
+James P.                roampune@gmail.com
 James Page              james.page@ubuntu.com
 Jarno Rajahalme         jarno.rajahalme@nsn.com
 Jean Tourrilhes         jt@hpl.hp.com
 Jeremy Stribling        strib@nicira.com
 Jesse Gross             jesse@nicira.com
+Jing Ai                 jinga@google.com
 Joe Perches             joe@perches.com
 Joe Stringer            joe@wand.net.nz
 Jun Nakajima            jun.nakajima@intel.com
@@ -57,6 +60,7 @@ Lorand Jakab            lojakab@cisco.com
 Luca Giraudo            lgiraudo@nicira.com
 Martin Casado           casado@nicira.com
 Mehak Mahajan           mmahajan@nicira.com
+Murphy McCauley         murphy.mccauley@gmail.com
 Natasha Gude            natasha@nicira.com
 Neil McKee              neil.mckee@inmon.com
 Paraneetharan Chandrasekaran    paraneetharanc@gmail.com
@@ -121,6 +125,7 @@ Brent Salisbury         brent.salisbury@gmail.com
 Bryan Fulton            bryan@nicira.com
 Bryan Osoro             bosoro@nicira.com
 Cedric Hobbs            cedric@nicira.com
+Christopher Paggen      cpaggen@cisco.com
 Dave Walker             DaveWalker@ubuntu.com
 David Palma             palma@onesource.pt
 Derek Cormier           derek.cormier@lab.ntt.co.jp
@@ -153,7 +158,6 @@ Jari Sundell            sundell.software@gmail.com
 Jed Daniels             openvswitch@jeddaniels.com
 Jeff Merrick            jmerrick@vmware.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
@@ -166,6 +170,7 @@ Krishna Miriyala        krishna@nicira.com
 Logan Rosen             logatronico@gmail.com
 Luca Falavigna          dktrkranz@debian.org
 Luiz Henrique Ozaki     luiz.ozaki@gmail.com
+Maxime Brun             m.brun@alphalink.fr
 Michael A. Collins      mike.a.collins@ark-net.org
 Michael Hu              mhu@nicira.com
 Michael Mao             mmao@nicira.com
@@ -173,9 +178,10 @@ Michael Shigorin        mike@osdn.org.ua
 Mike Bursell            mike.bursell@citrix.com
 Mike Kruze              mkruze@nicira.com
 Min Chen                ustcer.tonychan@gmail.com
-Murphy McCauley         murphy.mccauley@gmail.com
 Mikael Doverhag         mdoverhag@nicira.com
+Nagi Reddy Jonnala      njonnala@Brocade.com
 Niklas Andersson        nandersson@nicira.com
+Padmanabhan Krishnan    kprad1@yahoo.com
 Pankaj Thakkar          thakkar@nicira.com
 Paul Ingram             paul@nicira.com
 Paulo Cravero           pcravero@as2594.net
@@ -206,6 +212,7 @@ Vjekoslav Brajkovic     balkan@cs.washington.edu
 Voravit T.              voravit@kth.se
 YAMAMOTO Takashi        yamamoto@valinux.co.jp
 Yeming Zhao             zhaoyeming@gmail.com
+Ying Chen               yingchen@vmware.com
 Yongqiang Liu           liuyq7809@gmail.com
 kk yap                  yapkke@stanford.edu
 likunyun                kunyunli@hotmail.com
diff --git a/FAQ b/FAQ
index 1f0f841..7df4e90 100644 (file)
--- a/FAQ
+++ b/FAQ
@@ -940,6 +940,11 @@ A: Open vSwitch 1.9 and earlier support only OpenFlow 1.0 (plus
 
        ovs-vsctl set bridge br0 protocols=OpenFlow10,OpenFlow12,OpenFlow13
 
+   Use the -O option to enable support for later versions of OpenFlow
+   in ovs-ofctl.  For example:
+
+       ovs-ofctl -O OpenFlow13 dump-flows br0
+
    Support for OpenFlow 1.1 is incomplete enough that it cannot yet be
    enabled, even experimentally.
 
diff --git a/INSTALL b/INSTALL
index 18e8532..0ff237c 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -1,9 +1,9 @@
-           How to Install Open vSwitch on Linux and FreeBSD
-           ================================================
+      How to Install Open vSwitch on Linux, FreeBSD and NetBSD
+      ========================================================
 
 This document describes how to build and install Open vSwitch on a
-generic Linux or FreeBSD host. For specifics around installation on a
-specific platform, please see one of these files:
+generic Linux, FreeBSD, or NetBSD host. For specifics around installation
+on a specific platform, please see one of these files:
 
     - INSTALL.Debian
     - INSTALL.Fedora
@@ -49,9 +49,9 @@ INSTALL.userspace for more information.
       NET_ACT_POLICE, either built-in or as modules.  (NET_CLS_POLICE is
       obsolete and not needed.)
 
-      If GRE tunneling is being used it is recommended that the kernel
-      be compiled with IPv6 support (CONFIG_IPV6).  This allows for
-      special handling (such as path MTU discovery) of IPv6 packets.
+      To use GRE tunneling on Linux 2.6.37 or newer, kernel support
+      for GRE must be compiled in or available as a module
+      (CONFIG_NET_IPGRE_DEMUX).
 
       To configure HTB or HFSC quality of service with Open vSwitch,
       you must enable the respective configuration options.
@@ -143,8 +143,8 @@ software:
 (On Debian "lenny" the above can be installed with "apt-get install
 python-json python-qt4 python-zopeinterface python-twisted-conch".)
 
-Building and Installing Open vSwitch for Linux or FreeBSD
-=========================================================
+Building and Installing Open vSwitch for Linux, FreeBSD or NetBSD
+=================================================================
 
 Once you have installed all the prerequisites listed above in the Base
 Prerequisites section, follow the procedure below to build.
@@ -209,15 +209,15 @@ Prerequisites section, follow the procedure below to build.
 6. Run "make install" to install the executables and manpages into the
    running system, by default under /usr/local.
 
-7. If you built kernel modules, you may load them with "insmod", e.g.:
+7. If you built kernel modules, you may install and load them, e.g.:
 
-      % insmod datapath/linux/openvswitch.ko
+      % make modules_install
+      % /sbin/modprobe openvswitch 
 
-   You may need to specify a full path to insmod, e.g. /sbin/insmod.
    To verify that the modules have been loaded, run "/sbin/lsmod" and
    check that openvswitch is listed.
 
-   If the "insmod" operation fails, look at the last few kernel log
+   If the "modprobe" operation fails, look at the last few kernel log
    messages (e.g. with "dmesg | tail"):
 
       - The message "openvswitch: exports duplicate symbol
@@ -264,11 +264,6 @@ Prerequisites section, follow the procedure below to build.
    you do not understand what this means or do not know if your driver
    will work, do not set this.
 
-   Once you verify that the kernel modules load properly, you should
-   install them:
-
-      % make modules_install
-
 8. Initialize the configuration database using ovsdb-tool, e.g.:
 
       % mkdir -p /usr/local/etc/openvswitch
@@ -286,10 +281,10 @@ any managers specified in the database itself, and to use the SSL
 configuration in the database:
 
       % ovsdb-server --remote=punix:/usr/local/var/run/openvswitch/db.sock \
-                     --remote=db:Open_vSwitch,manager_options \
-                     --private-key=db:SSL,private_key \
-                     --certificate=db:SSL,certificate \
-                     --bootstrap-ca-cert=db:SSL,ca_cert \
+                     --remote=db:Open_vSwitch,Open_vSwitch,manager_options \
+                     --private-key=db:Open_vSwitch,SSL,private_key \
+                     --certificate=db:Open_vSwitch,SSL,certificate \
+                     --bootstrap-ca-cert=db:Open_vSwitch,SSL,ca_cert \
                      --pidfile --detach
 
 (If you built Open vSwitch without SSL support, then omit
@@ -333,7 +328,7 @@ also upgrade the database schema:
       - If there is no important data in your database, then you may
         delete the database file and recreate it with ovsdb-tool,
         following the instructions under "Building and Installing Open
-        vSwitch for Linux or FreeBSD".
+        vSwitch for Linux, FreeBSD or NetBSD".
 
       - If you want to preserve the contents of your database, back it
         up first, then use "ovsdb-tool convert" to upgrade it, e.g.:
@@ -341,7 +336,49 @@ also upgrade the database schema:
         % ovsdb-tool convert /usr/local/etc/openvswitch/conf.db vswitchd/vswitch.ovsschema
 
 4. Start the Open vSwitch daemons as described under "Building and
-   Installing Open vSwitch for Linux or FreeBSD" above.
+   Installing Open vSwitch for Linux, FreeBSD or NetBSD" above.
+
+Hot Upgrading
+=============
+Upgrading Open vSwitch from one version to the next version with minimum
+disruption of traffic going through the system that is using that Open vSwitch
+needs some considerations:
+
+1. If the upgrade only involves upgrading the userspace utilities and daemons
+of Open vSwitch, make sure that the new userspace version is compatible with
+the previously loaded kernel module.
+
+2. An upgrade of userspace daemons means that they have to be restarted.
+Restarting the daemons means that the Openflow flows in the ovs-vswitchd daemon
+will be lost.  One way to restore the flows is to let the controller
+re-populate it.  Another way is to save the previous flows using a utility
+like ovs-ofctl and then re-add them after the restart. Restoring the old flows
+is accurate only if the new Open vSwitch interfaces retain the old 'ofport'
+values.
+
+3. When the new userspace daemons get restarted, they automatically flush
+the old flows setup in the kernel. This can be expensive if there are hundreds
+of new flows that are entering the kernel but userspace daemons are busy
+setting up new userspace flows from either the controller or an utility like
+ovs-ofctl. Open vSwitch database provides an option to solve this problem
+through the other_config:flow-restore-wait column of the Open_vSwitch table.
+Refer to the ovs-vswitchd.conf.db(5) manpage for details.
+
+4. If the upgrade also involves upgrading the kernel module, the old kernel
+module needs to be unloaded and the new kernel module should be loaded. This
+means that the kernel network devices belonging to Open vSwitch is recreated
+and the kernel flows are lost. The downtime of the traffic can be reduced
+if the userspace daemons are restarted immediately and the userspace flows
+are restored as soon as possible.
+
+The ovs-ctl utility's "restart" function only restarts the userspace daemons,
+makes sure that the 'ofport' values remain consistent across restarts, restores
+userspace flows using the ovs-ofctl utility and also uses the
+other_config:flow-restore-wait column to keep the traffic downtime to the
+minimum. The ovs-ctl utility's "force-reload-kmod" function does all of the
+above, but also replaces the old kernel module with the new one. Open vSwitch
+startup scripts for Debian, XenServer and RHEL use ovs-ctl's functions and it
+is recommended that these functions be used for other software platforms too.
 
 Running the Testsuite
 =====================
@@ -353,7 +390,7 @@ features will ensure your features don't break as developers modify
 other areas of Open vSwitch.
 
 You must configure and build Open vSwitch (steps 1 through 3 in
-"Building and Installing Open vSwitch for Linux or FreeBSD" above)
+"Building and Installing Open vSwitch for Linux, FreeBSD or NetBSD" above)
 before you run the testsuite.  You do not need to install Open vSwitch
 or to build or load the kernel module to run the testsuite.  You do
 not need supervisor privilege to run the testsuite.
index 18a7bd4..aa7ab92 100644 (file)
@@ -69,7 +69,7 @@ guest will be able to communicate over that bridge.
 To get some more information and for debugging you can use Open
 vSwitch utilities such as ovs-dpctl and ovs-ofctl, For example:
 
-      % ovs-dpctl show br0 
+      % ovs-dpctl show
       % ovs-ofctl show br0
 
 You should see tap devices for each KVM guest added as ports to 
index 296f077..f54b93e 100644 (file)
@@ -31,9 +31,9 @@ The tun device must also exist as /dev/net/tun.  If it does not exist,
 then create /dev/net (if necessary) with "mkdir /dev/net", then create
 /dev/net/tun with "mknod /dev/net/tun c 10 200".
 
-On FreeBSD, the userspace switch additionally requires the kernel
-tap(4) driver to be available, either built into the kernel or loaded
-as a module.
+On FreeBSD and NetBSD, the userspace switch additionally requires the
+kernel tap(4) driver to be available, either built into the kernel or
+loaded as a module.
 
 Using the Userspace Datapath with ovs-vswitchd
 ----------------------------------------------
@@ -51,6 +51,12 @@ ovs-vswitchd will create a TAP device as the bridge's local interface,
 named the same as the bridge, as well as for each configured internal
 interface.
 
+Currently, on FreeBSD, the functionality required for in-band control
+support is not implemented.  To avoid related errors, you can disable
+the in-band support with the following command.
+
+    ovs-vsctl set bridge br0 other_config:disable-in-band=true
+
 Firewall Rules
 --------------
 
@@ -64,6 +70,14 @@ eth0:
     iptables -A INPUT -i eth0 -j DROP
     iptables -A FORWARD -i eth0 -j DROP
 
+Other settings
+--------------
+
+On NetBSD, depending on your network topology and applications, the
+following configuration might help.  See sysctl(7).
+
+       sysctl net.inet.ip.checkinterface=1
+
 Bug Reporting
 -------------
 
index b15a9a4..eed5a74 100644 (file)
@@ -55,6 +55,7 @@ EXTRA_DIST = \
        NOTICE \
        OPENFLOW-1.1+ \
        PORTING \
+       README-OFTest \
        README-gcov \
        README-lisp \
        REPORTING-BUGS \
@@ -185,17 +186,17 @@ config-h-check:
        fi
 .PHONY: config-h-check
 
-# Check that "struct vlog_ratelimit" is always declared "static".
-ALL_LOCAL += rate-limit-check
-rate-limit-check:
+# Check that certain data structures are always declared "static".
+ALL_LOCAL += static-check
+static-check:
        @if test -e $(srcdir)/.git && (git --version) >/dev/null 2>&1 && \
-           git --no-pager grep -n -E '^[       ]+struct vlog_rate_limit.*=' $(srcdir); \
+           git --no-pager grep -n -E '^[       ]+(struct vlog_rate_limit|pthread_once_t|struct ovsthread_once).*=' $(srcdir); \
          then \
            echo "See above for list of violations of the rule that "; \
-           echo "'struct vlog_rate_limit' must always be 'static'"; \
+           echo "certain data structures must always be 'static'"; \
            exit 1; \
         fi
-.PHONY: rate-limit-check
+.PHONY: static-check
 
 # Check that assert.h is not used outside a whitelist of files.
 ALL_LOCAL += check-assert-h-usage
diff --git a/NEWS b/NEWS
index 4cb4499..b07a651 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -4,10 +4,20 @@ post-v1.11.0
       * New support for matching outer source and destination IP address
         of tunneled packets, for tunnel ports configured with the newly
        added "remote_ip=flow" and "local_ip=flow" options.
+    - New "check-oftest" Makefile target for running OFTest against Open
+      vSwitch.  See README-OFTest for details.
+    - The flow eviction threshold has been moved to the Open_vSwitch table.
+    - Database names are now mandatory when specifying ovsdb-server options
+      through database paths (e.g. Private key option with the database name
+      should look like "--private-key=db:Open_vSwitch,SSL,private_key").
 
 
 v1.11.0 - xx xxx xxxx
 ---------------------
+    - Support for megaflows, which allows wildcarding in the kernel (and
+      any dpif implementation that supports wildcards).  Depending on
+      the flow table and switch configuration, flow set up rates are
+      close to the Linux bridge.
     - The "tutorial" directory contains a new tutorial for some advanced
       Open vSwitch features.
     - Stable bond mode has been removed.
@@ -25,11 +35,15 @@ v1.11.0 - xx xxx xxxx
         in_port to some unused value, such as OFPP_NONE.)
     - ovs-dpctl:
       * New debugging commands "add-flow", "mod-flow", "del-flow".
+    - In dpif-based bridges, cache action translations, which can improve
+      flow set up performance by 80% with a complicated flow table.
     - New syslog format, prefixed with "ovs|", to be easier to filter.
     - RHEL: Removes the default firewall rule that allowed GRE traffic to
       pass through. Any users that relied on this automatic firewall hole
       will have to manually configure it. The ovs-ctl(8) manpage documents
       the "enable-protocol" command that can be used as an alternative.
+    - New CFM demand mode which uses data traffic to indicate interface
+      liveness.
 
 v1.10.0 - 01 May 2013
 ---------------------
index b6a4222..fb39873 100644 (file)
@@ -70,8 +70,6 @@ probably incomplete.
       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.  This is partially merged.
 
@@ -94,9 +92,6 @@ 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
@@ -113,8 +108,6 @@ end of the OF1.2 spec.  I didn't compare the specs carefully yet.)
 
         * Update DESIGN to describe OF1.2 behavior also.
 
-    * Add ability to turn off packet buffering with OFPCML_NO_BUFFER.
-
 OpenFlow 1.3
 ------------
 
@@ -162,7 +155,8 @@ didn't compare the specs carefully yet.)
     * Rework tag order.  I'm not sure whether we need to do anything
       for this.
 
-    * Duration for stats.
+    * Duration for queue stats.  (Duration for port stats is already
+      implemented.)
 
     * On-demand flow counters.  I think this might be a real
       optimization in some cases for the software switch.
diff --git a/PORTING b/PORTING
index ffde296..53c19d3 100644 (file)
--- a/PORTING
+++ b/PORTING
@@ -148,9 +148,9 @@ depends on a few different factors:
 
     * A dpif provider is usually easier to implement, but most
       appropriate for software switching.  It "explodes" wildcard
-      rules into exact-match entries.  This allows fast hash lookups
-      in software, but makes inefficient use of TCAMs in hardware
-      that support wildcarding.
+      rules into exact-match entries (with an optional wildcard mask).
+      This allows fast hash lookups in software, but makes
+      inefficient use of TCAMs in hardware that support wildcarding.
 
 The following sections describe how to implement each kind of port.
 
@@ -175,18 +175,26 @@ Writing a dpif Provider
 
 Open vSwitch has a built-in ofproto provider named "ofproto-dpif",
 which is built on top of a library for manipulating datapaths, called
-"dpif".  A "datapath" is a simple flow table, one that supports only
-exact-match flows, that is, flows without wildcards.  When a packet
-arrives on a network device, the datapath looks for it in this
-exact-match table.  If there is a match, then it performs the
-associated actions.  If there is no match, the datapath passes the
-packet up to ofproto-dpif, which maintains an OpenFlow flow table
-(that supports wildcards).  If the packet matches in this flow table,
-then ofproto-dpif executes its actions and inserts a new exact-match
-entry into the dpif flow table.  (Otherwise, ofproto-dpif passes the
+"dpif".  A "datapath" is a simple flow table, one that is only required
+to support exact-match flows, that is, flows without wildcards.  When a
+packet arrives on a network device, the datapath looks for it in this
+table.  If there is a match, then it performs the associated actions.
+If there is no match, the datapath passes the packet up to ofproto-dpif,
+which maintains the full OpenFlow flow table.  If the packet matches in
+this flow table, then ofproto-dpif executes its actions and inserts a
+new entry into the dpif flow table.  (Otherwise, ofproto-dpif passes the
 packet up to ofproto to send the packet to the OpenFlow controller, if
 one is configured.)
 
+When calculating the dpif flow, ofproto-dpif generates an exact-match
+flow that describes the missed packet.  It makes an effort to figure out
+what fields can be wildcarded based on the switch's configuration and
+OpenFlow flow table.  The dpif is free to ignore the suggested wildcards
+and only support the exact-match entry.  However, if the dpif supports
+wildcarding, then it can use the masks to match multiple flows with
+fewer entries and potentially significantly reduce the number of flow
+misses handled by ofproto-dpif.
+
 The "dpif" library in turn delegates much of its functionality to a
 "dpif provider".  The following diagram shows how dpif providers fit
 into the Open vSwitch architecture:
diff --git a/README-OFTest b/README-OFTest
new file mode 100644 (file)
index 0000000..1b8516b
--- /dev/null
@@ -0,0 +1,70 @@
+                How to Use OFTest With Open vSwitch
+                ===================================
+
+This document describes how to use the OFTest OpenFlow protocol
+testing suite with Open vSwitch in "dummy mode".  In this mode of
+testing, no packets travel across physical or virtual networks.
+Instead, Unix domain sockets stand in as simulated networks.  This
+simulation is imperfect, but it is much easier to set up, does not
+require extra physical or virtual hardware, and does not require
+supervisor privileges.
+
+Prerequisites
+-------------
+
+First, build Open vSwitch according to the instructions in INSTALL.
+You need not install it.
+
+Second, obtain a copy of OFTest and install its prerequisites.  You
+need a copy of OFTest that includes commit 406614846c5 (make ovs-dummy
+platform work again).  This commit was merged into the OFTest
+repository on Feb 1, 2013, so any copy of OFTest more recent than that
+should work.
+
+Testing OVS in dummy mode does not require root privilege, so you may
+ignore that requirement.
+
+Optionally, add the top-level OFTest directory (containing the "oft"
+program) to your $PATH.  This slightly simplifies running OFTest later.
+
+Running OFTest
+--------------
+
+To run OFTest in dummy mode, run the following command from your Open
+vSwitch build directory:
+
+    make check-oftest OFT=<oft-binary>
+
+where <oft-binary> is the absolute path to the "oft" program in
+OFTest.
+
+If you added "oft" to your $PATH, you may omit the OFT variable
+assignment:
+
+    make check-oftest
+
+By default, "check-oftest" passes "oft" just enough options to enable
+dummy mode.  You can use OFTFLAGS to pass additional options.  For
+example, to run just the basic.Echo test instead of all tests (the
+default) and enable verbose logging:
+
+    make check-oftest OFT=<oft-binary> OFTFLAGS='--verbose -T basic.Echo'
+
+Interpreting OFTest Results
+---------------------------
+
+Please interpret OFTest results cautiously.  Open vSwitch can fail a
+given test in OFTest for many reasons, including bugs in Open vSwitch,
+bugs in OFTest, bugs in the "dummy mode" integration, and differing
+interpretations of the OpenFlow standard and other standards.
+
+Open vSwitch has not been validated against OFTest.  Please do report
+test failures that you believe to represent bugs in Open vSwitch.
+Include the precise versions of Open vSwitch and OFTest in your bug
+report, plus any other information needed to reproduce the problem.
+
+Contact 
+-------
+
+bugs@openvswitch.org
+http://openvswitch.org/
index 7c9071a..6057706 100644 (file)
@@ -6,11 +6,12 @@ not carry Ethernet headers, and ARP requests shouldn't be sent over the
 tunnel.  Because of this, there are some additional steps required for setting
 up LISP tunnels in Open vSwitch, until support for L3 tunnels will improve.
 
-This guide assumes a point-to-point tunnel between two VMs connected to OVS
-bridges on different hypervisors connected via IPv4.  Of course, more than one
-VM may be connected to any of the hypervisors, using the same LISP tunnel, and
-a hypervisor may be connected to several hypervisors over different LISP
-tunnels.
+This guide assumes tunneling between two VMs connected to OVS bridges on
+different hypervisors reachable over IPv4.  Of course, more than one VM may be
+connected to any of the hypervisors, and a hypervisor may communicate with
+several different hypervisors over the same lisp tunneling interface.  A LISP
+"map-cache" can be implemented using flows, see example at the bottom of this
+file.
 
 There are several scenarios:
 
@@ -33,7 +34,7 @@ Unix based VMs), or have the hypervisor do proxy ARP.
 
 On the receiving side, the packet arrives without the original MAC header.
 The LISP tunneling code attaches a header with harcoded source and destination
-MAC addres 02:00:00:00:00:00.  This address has all bits set to 0, except the
+MAC address 02:00:00:00:00:00.  This address has all bits set to 0, except the
 locally administered bit, in order to avoid potential collisions with existing
 allocations.  In order for packets to reach their intended destination, the
 destination MAC address needs to be rewritten.  This can be done using the
@@ -58,11 +59,14 @@ bridge instance, and become numbered 1, 2, and 3 respectively:
     ovs-vsctl add-br br0
     ovs-vsctl add-port br0 tap0
     ovs-vsctl add-port br0 eth0
-    ovs-vsctl add-port br0 lisp0 -- set Interface lisp0 type=lisp options:remote_ip=<OVSx_IP>
+    ovs-vsctl add-port br0 lisp0 -- set Interface lisp0 type=lisp options:remote_ip=flow options:key=flow
 
 Flows on br0 are configured as follows:
 
     priority=3,dl_dst=02:00:00:00:00:00,action=mod_dl_dst:<VMx_MAC>,output:1
     priority=2,in_port=1,dl_type=0x0806,action=NORMAL
-    priority=1,in_port=1,dl_type=0x0800,vlan_tci=0,nw_src=<EID_prefix>,action=output:3
+    priority=1,in_port=1,dl_type=0x0800,vlan_tci=0,nw_src=<EID_prefix>,action=set_field:<OVSx_IP>->tun_dst,output:3
     priority=0,action=NORMAL
+
+Optionally, if you want to use Instance ID in a flow, you can set it with
+"action=set_tunnel:<IID>".
index 34634a2..717c681 100644 (file)
@@ -264,6 +264,8 @@ AC_DEFUN([OVS_CHECK_LINUX_COMPAT], [
                   [OVS_DEFINE([HAVE_BOOL_TYPE])])
   OVS_GREP_IFELSE([$KSRC/include/linux/types.h], [__wsum],
                   [OVS_DEFINE([HAVE_CSUM_TYPES])])
+  OVS_GREP_IFELSE([$KSRC/include/uapi/linux/types.h], [__wsum],
+                  [OVS_DEFINE([HAVE_CSUM_TYPES])])
 
   OVS_GREP_IFELSE([$KSRC/include/net/checksum.h], [csum_replace4])
   OVS_GREP_IFELSE([$KSRC/include/net/checksum.h], [csum_unfold])
@@ -306,7 +308,8 @@ AC_DEFUN([OVS_CHECK_IF_PACKET],
 
 dnl Checks for net/if_dl.h.
 dnl
-dnl (We use this as a proxy for checking whether we're building on FreeBSD.)
+dnl (We use this as a proxy for checking whether we're building on FreeBSD
+dnl or NetBSD.)
 AC_DEFUN([OVS_CHECK_IF_DL],
   [AC_CHECK_HEADER([net/if_dl.h],
                    [HAVE_IF_DL=yes],
@@ -316,7 +319,7 @@ AC_DEFUN([OVS_CHECK_IF_DL],
       AC_DEFINE([HAVE_IF_DL], [1],
                 [Define to 1 if net/if_dl.h is available.])
 
-      # On FreeBSD we use libpcap to access network devices.
+      # On these platforms we use libpcap to access network devices.
       AC_SEARCH_LIBS([pcap_open_live], [pcap])
    fi])
 
@@ -359,15 +362,15 @@ dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
 dnl with or without modifications, as long as this notice is preserved.
 
-dnl OVS_CHECK_CC_OPTION([OPTION], [ACTION-IF-ACCEPTED], [ACTION-IF-REJECTED])
-dnl Check whether the given C compiler OPTION is accepted.
-dnl If so, execute ACTION-IF-ACCEPTED, otherwise ACTION-IF-REJECTED.
-AC_DEFUN([OVS_CHECK_CC_OPTION],
-[
+AC_DEFUN([_OVS_CHECK_CC_OPTION], [dnl
   m4_define([ovs_cv_name], [ovs_cv_[]m4_translit([$1], [-], [_])])dnl
   AC_CACHE_CHECK([whether $CC accepts $1], [ovs_cv_name], 
     [ovs_save_CFLAGS="$CFLAGS"
-     CFLAGS="$CFLAGS $1"
+     dnl Include -Werror in the compiler options, because without -Werror
+     dnl clang's GCC-compatible compiler driver does not return a failure
+     dnl exit status even though it complains about options it does not
+     dnl understand.
+     CFLAGS="$CFLAGS $WERROR $1"
      AC_COMPILE_IFELSE([AC_LANG_PROGRAM(,)], [ovs_cv_name[]=yes], [ovs_cv_name[]=no])
      CFLAGS="$ovs_save_CFLAGS"])
   if test $ovs_cv_name = yes; then
@@ -377,6 +380,21 @@ AC_DEFUN([OVS_CHECK_CC_OPTION],
   fi
 ])
 
+dnl OVS_CHECK_WERROR
+dnl
+dnl Check whether the C compiler accepts -Werror.
+dnl Sets $WERROR to "-Werror", if so, and otherwise to the empty string.
+AC_DEFUN([OVS_CHECK_WERROR],
+  [WERROR=
+   _OVS_CHECK_CC_OPTION([-Werror], [WERROR=-Werror])])
+
+dnl OVS_CHECK_CC_OPTION([OPTION], [ACTION-IF-ACCEPTED], [ACTION-IF-REJECTED])
+dnl Check whether the given C compiler OPTION is accepted.
+dnl If so, execute ACTION-IF-ACCEPTED, otherwise ACTION-IF-REJECTED.
+AC_DEFUN([OVS_CHECK_CC_OPTION],
+  [AC_REQUIRE([OVS_CHECK_WERROR])
+   _OVS_CHECK_CC_OPTION([$1], [$2], [$3])])
+
 dnl OVS_ENABLE_OPTION([OPTION])
 dnl Check whether the given C compiler OPTION is accepted.
 dnl If so, add it to WARNING_FLAGS.
index 965e322..bffead4 100755 (executable)
@@ -6,6 +6,13 @@ import re
 
 macros = {}
 
+# Map from OpenFlow version number to version ID used in ofp_header.
+version_map = {"1.0": 0x01,
+               "1.1": 0x02,
+               "1.2": 0x03,
+               "1.3": 0x04}
+version_reverse_map = dict((v, k) for (k, v) in version_map.iteritems())
+
 token = None
 line = ""
 idRe = "[a-zA-Z_][a-zA-Z_0-9]*"
@@ -13,12 +20,24 @@ tokenRe = "#?" + idRe + "|[0-9]+|."
 inComment = False
 inDirective = False
 
-def getLine():
+def open_file(fn):
+    global fileName
+    global inputFile
+    global lineNumber
+    fileName = fn
+    inputFile = open(fileName)
+    lineNumber = 0
+
+def tryGetLine():
+    global inputFile
     global line
     global lineNumber
     line = inputFile.readline()
     lineNumber += 1
-    if line == "":
+    return line != ""
+
+def getLine():
+    if not tryGetLine():
         fatal("unexpected end of input")
 
 def getToken():
@@ -133,150 +152,185 @@ def usage():
     argv0 = os.path.basename(sys.argv[0])
     print ('''\
 %(argv0)s, for extracting OpenFlow error codes from header files
-usage: %(argv0)s FILE [FILE...]
+usage: %(argv0)s ERROR_HEADER VENDOR_HEADER
 
-This program reads the header files specified on the command line and
-outputs a C source file for translating OpenFlow error codes into
-strings, for use as lib/ofp-errors.c in the Open vSwitch source tree.
+This program reads VENDOR_HEADER to obtain OpenFlow vendor (aka
+experimenter IDs), then ERROR_HEADER to obtain OpenFlow error number.
+It outputs a C source file for translating OpenFlow error codes into
+strings.
 
-This program is specialized for reading lib/ofp-errors.h.  It will not
-work on arbitrary header files without extensions.\
+ERROR_HEADER should point to lib/ofp-errors.h.
+VENDOR_HEADER should point to include/openflow/openflow-common.h.
+The output is suitable for use as lib/ofp-errors.inc.\
 ''' % {"argv0": argv0})
     sys.exit(0)
 
-def extract_ofp_errors(filenames):
+def extract_vendor_ids(fn):
+    global vendor_map
+    vendor_map = {}
+    vendor_loc = {}
+
+    open_file(fn)
+    while tryGetLine():
+        m = re.match(r'#define\s+([A-Z0-9_]+)_VENDOR_ID\s+(0x[0-9a-fA-F]+|[0-9]+)', line)
+        if not m:
+            continue
+
+        name = m.group(1)
+        id_ = int(m.group(2), 0)
+
+        if name in vendor_map:
+            error("%s: duplicate definition of vendor" % name)
+            sys.stderr.write("%s: Here is the location of the previous "
+                             "definition.\n" % vendor_loc[name])
+            sys.exit(1)
+
+        vendor_map[name] = id_
+        vendor_loc[name] = "%s:%d" % (fileName, lineNumber)
+
+    if not vendor_map:
+        fatal("%s: no vendor definitions found" % fn)
+
+    inputFile.close()
+
+    vendor_reverse_map = {}
+    for name, id_ in vendor_map.items():
+        if id_ in vendor_reverse_map:
+            fatal("%s: duplicate vendor id for vendors %s and %s"
+                  % (id_, vendor_reverse_map[id_], name))
+        vendor_reverse_map[id_] = name
+
+def extract_ofp_errors(fn):
     error_types = {}
 
     comments = []
     names = []
     domain = {}
     reverse = {}
-    for domain_name in ("OF1.0", "OF1.1", "OF1.2", "OF1.3",
-                        "NX1.0", "NX1.1", "NX1.2", "NX1.3"):
+    for domain_name in version_map.values():
         domain[domain_name] = {}
         reverse[domain_name] = {}
 
     n_errors = 0
     expected_errors = {}
 
-    global fileName
-    for fileName in filenames:
-        global inputFile
-        global lineNumber
-        inputFile = open(fileName)
-        lineNumber = 0
+    open_file(fn)
 
-        while True:
-            getLine()
-            if re.match('enum ofperr', line):
-                break
+    while True:
+        getLine()
+        if re.match('enum ofperr', line):
+            break
+
+    while True:
+        getLine()
+        if line.startswith('/*') or not line or line.isspace():
+            continue
+        elif re.match('}', line):
+            break
 
-        while True:
+        if not line.lstrip().startswith('/*'):
+            fatal("unexpected syntax between errors")
+
+        comment = line.lstrip()[2:].strip()
+        while not comment.endswith('*/'):
             getLine()
             if line.startswith('/*') or not line or line.isspace():
-                continue
-            elif re.match('}', line):
-                break
-
-            if not line.lstrip().startswith('/*'):
-                fatal("unexpected syntax between errors")
-
-            comment = line.lstrip()[2:].strip()
-            while not comment.endswith('*/'):
-                getLine()
-                if line.startswith('/*') or not line or line.isspace():
-                    fatal("unexpected syntax within error")
-                comment += ' %s' % line.lstrip('* \t').rstrip(' \t\r\n')
-            comment = comment[:-2].rstrip()
-
-            m = re.match('Expected: (.*)\.$', comment)
-            if m:
-                expected_errors[m.group(1)] = (fileName, lineNumber)
-                continue
+                fatal("unexpected syntax within error")
+            comment += ' %s' % line.lstrip('* \t').rstrip(' \t\r\n')
+        comment = comment[:-2].rstrip()
 
-            m = re.match('((?:.(?!\.  ))+.)\.  (.*)$', comment)
-            if not m:
-                fatal("unexpected syntax between errors")
+        m = re.match('Expected: (.*)\.$', comment)
+        if m:
+            expected_errors[m.group(1)] = (fileName, lineNumber)
+            continue
 
-            dsts, comment = m.groups()
+        m = re.match('((?:.(?!\.  ))+.)\.  (.*)$', comment)
+        if not m:
+            fatal("unexpected syntax between errors")
 
-            getLine()
-            m = re.match('\s+(?:OFPERR_((?:OFP|NX)[A-Z0-9_]+))(\s*=\s*OFPERR_OFS)?,',
-                         line)
-            if not m:
-                fatal("syntax error expecting enum value")
+        dsts, comment = m.groups()
 
-            enum = m.group(1)
+        getLine()
+        m = re.match('\s+(?:OFPERR_([A-Z0-9_]+))(\s*=\s*OFPERR_OFS)?,',
+                     line)
+        if not m:
+            fatal("syntax error expecting enum value")
 
-            comments.append(re.sub('\[[^]]*\]', '', comment))
-            names.append(enum)
+        enum = m.group(1)
+        if enum in names:
+            fatal("%s specified twice" % enum)
 
-            for dst in dsts.split(', '):
-                m = re.match(r'([A-Z0-9.+]+)\((\d+|(0x)[0-9a-fA-F]+)(?:,(\d+))?\)$', dst)
-                if not m:
-                    fatal("%s: syntax error in destination" % dst)
-                targets = m.group(1)
-                if m.group(3):
-                    base = 16
-                else:
-                    base = 10
-                type_ = int(m.group(2), base)
-                if m.group(4):
-                    code = int(m.group(4))
-                else:
-                    code = None
-
-                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",),
-                              "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.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:
-                    fatal("%s: NX domain code cannot be less than 0x100" % dst)
-                if targets.startswith('OF') and code >= 0x100:
-                    fatal("%s: OF domain code cannot be greater than 0x100"
-                          % dst)
-                for target in target_map[targets]:
-                    domain[target].setdefault(type_, {})
-                    if code in domain[target][type_]:
-                        msg = "%d,%d in %s means both %s and %s" % (
-                            type_, code, target,
-                            domain[target][type_][code][0], enum)
-                        if msg in expected_errors:
-                            del expected_errors[msg]
-                        else:
-                            error("%s: %s." % (dst, msg))
-                            sys.stderr.write("%s:%d: %s: Here is the location "
-                                             "of the previous definition.\n"
-                                             % (domain[target][type_][code][1],
-                                                domain[target][type_][code][2],
-                                                dst))
+        comments.append(re.sub('\[[^]]*\]', '', comment))
+        names.append(enum)
+
+        for dst in dsts.split(', '):
+            m = re.match(r'([A-Z]+)([0-9.]+)(\+|-[0-9.]+)?\((\d+)(?:,(\d+))?\)$', dst)
+            if not m:
+                fatal("%r: syntax error in destination" % dst)
+            vendor_name = m.group(1)
+            version1_name = m.group(2)
+            version2_name = m.group(3)
+            type_ = int(m.group(4))
+            if m.group(5):
+                code = int(m.group(5))
+            else:
+                code = None
+
+            if vendor_name not in vendor_map:
+                fatal("%s: unknown vendor" % vendor_name)
+            vendor = vendor_map[vendor_name]
+
+            if version1_name not in version_map:
+                fatal("%s: unknown OpenFlow version" % version1_name)
+            v1 = version_map[version1_name]
+
+            if version2_name is None:
+                v2 = v1
+            elif version2_name == "+":
+                v2 = max(version_map.values())
+            elif version2_name[1:] not in version_map:
+                fatal("%s: unknown OpenFlow version" % version2_name[1:])
+            else:
+                v2 = version_map[version2_name[1:]]
+
+            if v2 < v1:
+                fatal("%s%s: %s precedes %s"
+                      % (version1_name, version2_name,
+                         version2_name, version1_name))
+
+            if vendor == vendor_map['NX']:
+                if v1 >= version_map['1.2'] or v2 >= version_map['1.2']:
+                    if code is not None:
+                        fatal("%s: NX1.2+ domains do not have codes" % dst)
+                    code = 0
+            elif vendor != vendor_map['OF']:
+                if code is not None:
+                    fatal("%s: %s domains do not have codes" % vendor_name)
+
+            for version in range(v1, v2 + 1):
+                domain[version].setdefault(vendor, {})
+                domain[version][vendor].setdefault(type_, {})
+                if code in domain[version][vendor][type_]:
+                    msg = "%#x,%d,%d in OF%s means both %s and %s" % (
+                        vendor, type_, code, version_reverse_map[version],
+                        domain[version][vendor][type_][code][0], enum)
+                    if msg in expected_errors:
+                        del expected_errors[msg]
                     else:
-                        domain[target][type_][code] = (enum, fileName,
-                                                       lineNumber)
+                        error("%s: %s." % (dst, msg))
+                        sys.stderr.write("%s:%d: %s: Here is the location "
+                                         "of the previous definition.\n"
+                                         % (domain[version][vendor][type_][code][1],
+                                            domain[version][vendor][type_][code][2],
+                                            dst))
+                else:
+                    domain[version][vendor][type_][code] = (enum, fileName,
+                                                   lineNumber)
 
-                    if enum in reverse[target]:
-                        error("%s: %s in %s means both %d,%d and %d,%d." %
-                              (dst, enum, target,
-                               reverse[target][enum][0],
-                               reverse[target][enum][1],
-                               type_, code))
-                    reverse[target][enum] = (type_, code)
+                assert enum not in reverse[version]
+                reverse[version][enum] = (vendor, type_, code)
 
-        inputFile.close()
+    inputFile.close()
 
     for fn, ln in expected_errors.values():
         sys.stderr.write("%s:%d: expected duplicate not used.\n" % (fn, ln))
@@ -293,8 +347,8 @@ def extract_ofp_errors(filenames):
 struct ofperr_domain {
     const char *name;
     uint8_t version;
-    enum ofperr (*decode)(uint16_t type, uint16_t code);
-    struct pair errors[OFPERR_N_ERRORS];
+    enum ofperr (*decode)(uint32_t vendor, uint16_t type, uint16_t code);
+    struct triplet errors[OFPERR_N_ERRORS];
 };
 
 static const char *error_names[OFPERR_N_ERRORS] = {
@@ -312,21 +366,25 @@ static const char *error_comments[OFPERR_N_ERRORS] = {
     def output_domain(map, name, description, version):
         print ("""
 static enum ofperr
-%s_decode(uint16_t type, uint16_t code)
+%s_decode(uint32_t vendor, uint16_t type, uint16_t code)
 {
-    switch ((type << 16) | code) {""" % name)
+    switch (((uint64_t) vendor << 32) | (type << 16) | code) {""" % name)
         found = set()
         for enum in names:
             if enum not in map:
                 continue
-            type_, code = map[enum]
+            vendor, type_, code = map[enum]
             if code is None:
                 continue
-            value = (type_ << 16) | code
+            value = (vendor << 32) | (type_ << 16) | code
             if value in found:
                 continue
             found.add(value)
-            print ("    case (%d << 16) | %d:" % (type_, code))
+            if vendor:
+                vendor_s = "(%#xULL << 32) | " % vendor
+            else:
+                vendor_s = ""
+            print ("    case %s(%d << 16) | %d:" % (vendor_s, type_, code))
             print ("        return OFPERR_%s;" % enum)
         print ("""\
     }
@@ -342,27 +400,28 @@ static const struct ofperr_domain %s = {
     {""" % (name, description, version, name))
         for enum in names:
             if enum in map:
-                type_, code = map[enum]
+                vendor, type_, code = map[enum]
                 if code == None:
                     code = -1
+                print "        { %#8x, %2d, %3d }, /* %s */" % (vendor, type_, code, enum)
             else:
-                type_ = code = -1
-            print ("        { %2d, %3d }, /* %s */" % (type_, code, enum))
+                print ("        {       -1, -1,  -1 }, /* %s */" % enum)
         print ("""\
     },
 };""")
 
-    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)
+    for version_name, id_ in version_map.items():
+        var = 'ofperr_of' + re.sub('[^A-Za-z0-9_]', '', version_name)
+        description = "OpenFlow %s" % version_name
+        output_domain(reverse[id_], var, description, id_)
 
 if __name__ == '__main__':
     if '--help' in sys.argv:
         usage()
-    elif len(sys.argv) < 2:
-        sys.stderr.write("at least one non-option argument required; "
+    elif len(sys.argv) != 3:
+        sys.stderr.write("exactly two non-options arguments required; "
                          "use --help for help\n")
         sys.exit(1)
     else:
-        extract_ofp_errors(sys.argv[1:])
+        extract_vendor_ids(sys.argv[2])
+        extract_ofp_errors(sys.argv[1])
index fe3f9c7..e4f9991 100644 (file)
@@ -45,6 +45,7 @@ AC_SEARCH_LIBS([pow], [m])
 AC_SEARCH_LIBS([clock_gettime], [rt])
 AC_SEARCH_LIBS([timer_create], [rt])
 AC_SEARCH_LIBS([pthread_sigmask], [pthread])
+AC_FUNC_STRERROR_R
 
 OVS_CHECK_ESX
 OVS_CHECK_COVERAGE
@@ -59,10 +60,14 @@ OVS_CHECK_DOT
 OVS_CHECK_IF_PACKET
 OVS_CHECK_IF_DL
 OVS_CHECK_STRTOK_R
+AC_CHECK_DECLS([sys_siglist], [], [], [[#include <signal.h>]])
 AC_CHECK_MEMBERS([struct stat.st_mtim.tv_nsec, struct stat.st_mtimensec],
   [], [], [[#include <sys/stat.h>]])
-AC_CHECK_FUNCS([mlockall strnlen strsignal getloadavg statvfs getmntent_r])
-AC_CHECK_HEADERS([mntent.h sys/statvfs.h linux/types.h linux/if_ether.h])
+AC_CHECK_MEMBERS([struct ifreq.ifr_flagshigh], [], [], [[#include <net/if.h>]])
+AC_CHECK_FUNCS([mlockall strnlen getloadavg statvfs getmntent_r])
+AC_CHECK_HEADERS([mntent.h sys/statvfs.h linux/types.h linux/if_ether.h stdatomic.h])
+AC_CHECK_HEADERS([net/if_mib.h], [], [], [[#include <sys/types.h>
+#include <net/if.h>]])
 
 OVS_CHECK_PKIDIR
 OVS_CHECK_RUNDIR
@@ -76,6 +81,11 @@ OVS_CHECK_XENSERVER_VERSION
 OVS_CHECK_GROFF
 OVS_CHECK_GNU_MAKE
 OVS_CHECK_CACHE_TIME
+OVS_CHECK_TLS
+OVS_CHECK_ATOMIC_ALWAYS_LOCK_FREE(1)
+OVS_CHECK_ATOMIC_ALWAYS_LOCK_FREE(2)
+OVS_CHECK_ATOMIC_ALWAYS_LOCK_FREE(4)
+OVS_CHECK_ATOMIC_ALWAYS_LOCK_FREE(8)
 
 OVS_ENABLE_OPTION([-Wall])
 OVS_ENABLE_OPTION([-Wno-sign-compare])
index 8fa2dd1..37c20ee 100644 (file)
@@ -91,6 +91,46 @@ Often we ellipsize arguments not important to the discussion, e.g.:
     in_port(1), eth(...), eth_type(0x0800), ipv4(...), tcp(...)
 
 
+Wildcarded flow key format
+--------------------------
+
+A wildcarded flow is described with two sequences of Netlink attributes
+passed over the Netlink socket. A flow key, exactly as described above, and an
+optional corresponding flow mask.
+
+A wildcarded flow can represent a group of exact match flows. Each '1' bit
+in the mask specifies a exact match with the corresponding bit in the flow key.
+A '0' bit specifies a don't care bit, which will match either a '1' or '0' bit
+of a incoming packet. Using wildcarded flow can improve the flow set up rate
+by reduce the number of new flows need to be processed by the user space program.
+
+Support for the mask Netlink attribute is optional for both the kernel and user
+space program. The kernel can ignore the mask attribute, installing an exact
+match flow, or reduce the number of don't care bits in the kernel to less than
+what was specified by the user space program. In this case, variations in bits
+that the kernel does not implement will simply result in additional flow setups.
+The kernel module will also work with user space programs that neither support
+nor supply flow mask attributes.
+
+Since the kernel may ignore or modify wildcard bits, it can be difficult for
+the userspace program to know exactly what matches are installed. There are
+two possible approaches: reactively install flows as they miss the kernel
+flow table (and therefore not attempt to determine wildcard changes at all)
+or use the kernel's response messages to determine the installed wildcards.
+
+When interacting with userspace, the kernel should maintain the match portion
+of the key exactly as originally installed. This will provides a handle to
+identify the flow for all future operations. However, when reporting the
+mask of an installed flow, the mask should include any restrictions imposed
+by the kernel.
+
+The behavior when using overlapping wildcarded flows is undefined. It is the
+responsibility of the user space program to ensure that any incoming packet
+can match at most one flow, wildcarded or not. The current implementation
+performs best-effort detection of overlapping wildcarded flows and may reject
+some but not all of them. However, this behavior may change in future versions.
+
+
 Basic rule for evolving flow keys
 ---------------------------------
 
index 0dac658..0a2def6 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2012 Nicira, Inc.
+ * Copyright (c) 2007-2013 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
@@ -132,9 +132,17 @@ static int set_eth_addr(struct sk_buff *skb,
        if (unlikely(err))
                return err;
 
+       if (get_ip_summed(skb) == OVS_CSUM_COMPLETE)
+               skb->csum = csum_sub(skb->csum, csum_partial(eth_hdr(skb),
+                                                            ETH_ALEN * 2, 0));
+
        memcpy(eth_hdr(skb)->h_source, eth_key->eth_src, ETH_ALEN);
        memcpy(eth_hdr(skb)->h_dest, eth_key->eth_dst, ETH_ALEN);
 
+       if (get_ip_summed(skb) == OVS_CSUM_COMPLETE)
+               skb->csum = csum_add(skb->csum, csum_partial(eth_hdr(skb),
+                                                            ETH_ALEN * 2, 0));
+
        return 0;
 }
 
@@ -376,8 +384,10 @@ static int output_userspace(struct datapath *dp, struct sk_buff *skb,
        const struct nlattr *a;
        int rem;
 
+       BUG_ON(!OVS_CB(skb)->pkt_key);
+
        upcall.cmd = OVS_PACKET_CMD_ACTION;
-       upcall.key = &OVS_CB(skb)->flow->key;
+       upcall.key = OVS_CB(skb)->pkt_key;
        upcall.userdata = NULL;
        upcall.portid = 0;
 
index c7fd225..a6a01d5 100644 (file)
@@ -94,4 +94,10 @@ static inline void skb_set_mark(struct sk_buff *skb, u32 mark)
 }
 #endif /* after 2.6.20 */
 
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)
+#define rt_dst(rt) (rt->dst)
+#else
+#define rt_dst(rt) (rt->u.dst)
+#endif
+
 #endif /* compat.h */
index 42af315..3680391 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2012 Nicira, Inc.
+ * Copyright (c) 2007-2013 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
@@ -173,7 +173,7 @@ static void destroy_dp_rcu(struct rcu_head *rcu)
 {
        struct datapath *dp = container_of(rcu, struct datapath, rcu);
 
-       ovs_flow_tbl_destroy((__force struct flow_table *)dp->table);
+       ovs_flow_tbl_destroy((__force struct flow_table *)dp->table, false);
        free_percpu(dp->stats_percpu);
        release_net(ovs_dp_get_net(dp));
        kfree(dp->ports);
@@ -234,19 +234,18 @@ void ovs_dp_process_received_packet(struct vport *p, struct sk_buff *skb)
        struct sw_flow_key key;
        u64 *stats_counter;
        int error;
-       int key_len;
 
        stats = this_cpu_ptr(dp->stats_percpu);
 
        /* Extract flow from 'skb' into 'key'. */
-       error = ovs_flow_extract(skb, p->port_no, &key, &key_len);
+       error = ovs_flow_extract(skb, p->port_no, &key);
        if (unlikely(error)) {
                kfree_skb(skb);
                return;
        }
 
        /* Look up flow. */
-       flow = ovs_flow_tbl_lookup(rcu_dereference(dp->table), &key, key_len);
+       flow = ovs_flow_lookup(rcu_dereference(dp->table), &key);
        if (unlikely(!flow)) {
                struct dp_upcall_info upcall;
 
@@ -261,6 +260,7 @@ void ovs_dp_process_received_packet(struct vport *p, struct sk_buff *skb)
        }
 
        OVS_CB(skb)->flow = flow;
+       OVS_CB(skb)->pkt_key = &key;
 
        stats_counter = &stats->n_hit;
        ovs_flow_used(OVS_CB(skb)->flow, skb);
@@ -443,7 +443,7 @@ static int queue_userspace_packet(struct net *net, int dp_ifindex,
        upcall->dp_ifindex = dp_ifindex;
 
        nla = nla_nest_start(user_skb, OVS_PACKET_ATTR_KEY);
-       ovs_flow_to_nlattrs(upcall_info->key, user_skb);
+       ovs_flow_to_nlattrs(upcall_info->key, upcall_info->key, user_skb);
        nla_nest_end(user_skb, nla);
 
        if (upcall_info->userdata)
@@ -476,7 +476,7 @@ static int flush_flows(struct datapath *dp)
 
        rcu_assign_pointer(dp->table, new_table);
 
-       ovs_flow_tbl_deferred_destroy(old_table);
+       ovs_flow_tbl_destroy(old_table, true);
        return 0;
 }
 
@@ -619,10 +619,12 @@ static int validate_tp_port(const struct sw_flow_key *flow_key)
 static int validate_and_copy_set_tun(const struct nlattr *attr,
                                     struct sw_flow_actions **sfa)
 {
-       struct ovs_key_ipv4_tunnel tun_key;
+       struct sw_flow_match match;
+       struct sw_flow_key key;
        int err, start;
 
-       err = ipv4_tun_from_nlattr(nla_data(attr), &tun_key);
+       ovs_match_init(&match, &key, NULL);
+       err = ipv4_tun_from_nlattr(nla_data(attr), &match, false);
        if (err)
                return err;
 
@@ -630,7 +632,8 @@ static int validate_and_copy_set_tun(const struct nlattr *attr,
        if (start < 0)
                return start;
 
-       err = add_action(sfa, OVS_KEY_ATTR_IPV4_TUNNEL, &tun_key, sizeof(tun_key));
+       err = add_action(sfa, OVS_KEY_ATTR_IPV4_TUNNEL, &match.key->tun_key,
+                       sizeof(match.key->tun_key));
        add_nested_action_end(*sfa, start);
 
        return err;
@@ -871,7 +874,6 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info)
        struct ethhdr *eth;
        int len;
        int err;
-       int key_len;
 
        err = -EINVAL;
        if (!a[OVS_PACKET_ATTR_PACKET] || !a[OVS_PACKET_ATTR_KEY] ||
@@ -904,11 +906,11 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info)
        if (IS_ERR(flow))
                goto err_kfree_skb;
 
-       err = ovs_flow_extract(packet, -1, &flow->key, &key_len);
+       err = ovs_flow_extract(packet, -1, &flow->key);
        if (err)
                goto err_flow_free;
 
-       err = ovs_flow_metadata_from_nlattrs(flow, key_len, a[OVS_PACKET_ATTR_KEY]);
+       err = ovs_flow_metadata_from_nlattrs(flow, a[OVS_PACKET_ATTR_KEY]);
        if (err)
                goto err_flow_free;
        acts = ovs_flow_actions_alloc(nla_len(a[OVS_PACKET_ATTR_ACTIONS]));
@@ -922,6 +924,7 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info)
                goto err_flow_free;
 
        OVS_CB(packet)->flow = flow;
+       OVS_CB(packet)->pkt_key = &flow->key;
        packet->priority = flow->key.phy.priority;
        skb_set_mark(packet, flow->key.phy.skb_mark);
 
@@ -936,13 +939,13 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info)
        local_bh_enable();
        rcu_read_unlock();
 
-       ovs_flow_free(flow);
+       ovs_flow_free(flow, false);
        return err;
 
 err_unlock:
        rcu_read_unlock();
 err_flow_free:
-       ovs_flow_free(flow);
+       ovs_flow_free(flow, false);
 err_kfree_skb:
        kfree_skb(packet);
 err:
@@ -1061,7 +1064,8 @@ static int set_action_to_attr(const struct nlattr *a, struct sk_buff *skb)
                if (!start)
                        return -EMSGSIZE;
 
-               err = ipv4_tun_to_nlattr(skb, nla_data(ovs_key));
+               err = ipv4_tun_to_nlattr(skb,
+                               nla_data(ovs_key), nla_data(ovs_key));
                if (err)
                        return err;
                nla_nest_end(skb, start);
@@ -1109,6 +1113,7 @@ static size_t ovs_flow_cmd_msg_size(const struct sw_flow_actions *acts)
 {
        return NLMSG_ALIGN(sizeof(struct ovs_header))
                + nla_total_size(key_attr_size()) /* OVS_FLOW_ATTR_KEY */
+               + nla_total_size(key_attr_size()) /* OVS_FLOW_ATTR_MASK */
                + nla_total_size(sizeof(struct ovs_flow_stats)) /* OVS_FLOW_ATTR_STATS */
                + nla_total_size(1) /* OVS_FLOW_ATTR_TCP_FLAGS */
                + nla_total_size(8) /* OVS_FLOW_ATTR_USED */
@@ -1138,14 +1143,28 @@ static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp,
 
        ovs_header->dp_ifindex = get_dpifindex(dp);
 
+       /* Fill flow key. */
        nla = nla_nest_start(skb, OVS_FLOW_ATTR_KEY);
        if (!nla)
                goto nla_put_failure;
-       err = ovs_flow_to_nlattrs(&flow->key, skb);
+
+       err = ovs_flow_to_nlattrs(&flow->unmasked_key,
+                       &flow->unmasked_key, skb);
        if (err)
                goto error;
        nla_nest_end(skb, nla);
 
+       nla = nla_nest_start(skb, OVS_FLOW_ATTR_MASK);
+       if (!nla)
+               goto nla_put_failure;
+
+       err = ovs_flow_to_nlattrs(&flow->key,
+                       &ovsl_dereference(flow->mask)->key, skb);
+       if (err)
+               goto error;
+
+       nla_nest_end(skb, nla);
+
        spin_lock_bh(&flow->lock);
        used = flow->used;
        stats.n_packets = flow->packet_count;
@@ -1229,19 +1248,23 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
        struct nlattr **a = info->attrs;
        struct ovs_header *ovs_header = info->userhdr;
        struct sw_flow_key key;
-       struct sw_flow *flow;
+       struct sw_flow *flow = NULL;
+       struct sw_flow_mask mask;
        struct sk_buff *reply;
        struct datapath *dp;
        struct flow_table *table;
        struct sw_flow_actions *acts = NULL;
+       struct sw_flow_match match;
        int error;
-       int key_len;
 
        /* Extract key. */
        error = -EINVAL;
        if (!a[OVS_FLOW_ATTR_KEY])
                goto error;
-       error = ovs_flow_from_nlattrs(&key, &key_len, a[OVS_FLOW_ATTR_KEY]);
+
+       ovs_match_init(&match, &key, &mask);
+       error = ovs_match_from_nlattrs(&match,
+                       a[OVS_FLOW_ATTR_KEY], a[OVS_FLOW_ATTR_MASK]);
        if (error)
                goto error;
 
@@ -1267,8 +1290,11 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
                goto err_unlock_ovs;
 
        table = ovsl_dereference(dp->table);
-       flow = ovs_flow_tbl_lookup(table, &key, key_len);
+
+       /* Check if this is a duplicate flow */
+       flow = ovs_flow_lookup(table, &key);
        if (!flow) {
+               struct sw_flow_mask *mask_p;
                /* Bail out if we're not allowed to create a new flow. */
                error = -ENOENT;
                if (info->genlhdr->cmd == OVS_FLOW_CMD_SET)
@@ -1281,7 +1307,7 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
                        new_table = ovs_flow_tbl_expand(table);
                        if (!IS_ERR(new_table)) {
                                rcu_assign_pointer(dp->table, new_table);
-                               ovs_flow_tbl_deferred_destroy(table);
+                               ovs_flow_tbl_destroy(table, true);
                                table = ovsl_dereference(dp->table);
                        }
                }
@@ -1294,14 +1320,27 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
                }
                clear_stats(flow);
 
+               /* Make sure mask is unique in the system */
+               mask_p = ovs_sw_flow_mask_find(table, &mask);
+               if (!mask_p) {
+                       /* Allocate a new mask if none exsits. */
+                       mask_p = ovs_sw_flow_mask_alloc();
+                       if (!mask_p)
+                               goto err_flow_free;
+                       mask_p->key = mask.key;
+                       mask_p->range = mask.range;
+                       ovs_sw_flow_mask_insert(table, mask_p);
+               }
+
+               ovs_sw_flow_mask_add_ref(mask_p);
+               rcu_assign_pointer(flow->mask, mask_p);
                rcu_assign_pointer(flow->sf_acts, acts);
 
                /* Put flow in bucket. */
-               ovs_flow_tbl_insert(table, flow, &key, key_len);
+               ovs_flow_insert(table, flow, &key, match.range.end);
 
                reply = ovs_flow_cmd_build_info(flow, dp, info->snd_portid,
-                                               info->snd_seq,
-                                               OVS_FLOW_CMD_NEW);
+                                               info->snd_seq, OVS_FLOW_CMD_NEW);
        } else {
                /* We found a matching flow. */
                struct sw_flow_actions *old_acts;
@@ -1317,6 +1356,11 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
                    info->nlhdr->nlmsg_flags & (NLM_F_CREATE | NLM_F_EXCL))
                        goto err_unlock_ovs;
 
+               /* The unmasked key has to be the same for flow updates. */
+               error = -EINVAL;
+               if (!ovs_flow_cmp_unmasked_key(flow, &key, match.range.end))
+                       goto err_unlock_ovs;
+
                /* Update actions. */
                old_acts = ovsl_dereference(flow->sf_acts);
                rcu_assign_pointer(flow->sf_acts, acts);
@@ -1341,6 +1385,8 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
                                ovs_dp_flow_multicast_group.id, PTR_ERR(reply));
        return 0;
 
+err_flow_free:
+       ovs_flow_free(flow, false);
 err_unlock_ovs:
        ovs_unlock();
 err_kfree:
@@ -1358,12 +1404,14 @@ static int ovs_flow_cmd_get(struct sk_buff *skb, struct genl_info *info)
        struct sw_flow *flow;
        struct datapath *dp;
        struct flow_table *table;
+       struct sw_flow_match match;
        int err;
-       int key_len;
 
        if (!a[OVS_FLOW_ATTR_KEY])
                return -EINVAL;
-       err = ovs_flow_from_nlattrs(&key, &key_len, a[OVS_FLOW_ATTR_KEY]);
+
+       ovs_match_init(&match, &key, NULL);
+       err = ovs_match_from_nlattrs(&match, a[OVS_FLOW_ATTR_KEY], NULL);
        if (err)
                return err;
 
@@ -1375,7 +1423,7 @@ static int ovs_flow_cmd_get(struct sk_buff *skb, struct genl_info *info)
        }
 
        table = ovsl_dereference(dp->table);
-       flow = ovs_flow_tbl_lookup(table, &key, key_len);
+       flow = ovs_flow_lookup_unmasked_key(table, &match);
        if (!flow) {
                err = -ENOENT;
                goto unlock;
@@ -1404,8 +1452,8 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info)
        struct sw_flow *flow;
        struct datapath *dp;
        struct flow_table *table;
+       struct sw_flow_match match;
        int err;
-       int key_len;
 
        ovs_lock();
        dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex);
@@ -1418,12 +1466,14 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info)
                err = flush_flows(dp);
                goto unlock;
        }
-       err = ovs_flow_from_nlattrs(&key, &key_len, a[OVS_FLOW_ATTR_KEY]);
+
+       ovs_match_init(&match, &key, NULL);
+       err = ovs_match_from_nlattrs(&match, a[OVS_FLOW_ATTR_KEY], NULL);
        if (err)
                goto unlock;
 
        table = ovsl_dereference(dp->table);
-       flow = ovs_flow_tbl_lookup(table, &key, key_len);
+       flow = ovs_flow_lookup_unmasked_key(table, &match);
        if (!flow) {
                err = -ENOENT;
                goto unlock;
@@ -1435,13 +1485,13 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info)
                goto unlock;
        }
 
-       ovs_flow_tbl_remove(table, flow);
+       ovs_flow_remove(table, flow);
 
        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);
+       ovs_flow_free(flow, true);
        ovs_unlock();
 
        ovs_notify(reply, info, &ovs_dp_flow_multicast_group);
@@ -1472,7 +1522,7 @@ static int ovs_flow_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb)
 
                bucket = cb->args[0];
                obj = cb->args[1];
-               flow = ovs_flow_tbl_next(table, &bucket, &obj);
+               flow = ovs_flow_dump_next(table, &bucket, &obj);
                if (!flow)
                        break;
 
@@ -1705,7 +1755,7 @@ err_destroy_ports_array:
 err_destroy_percpu:
        free_percpu(dp->stats_percpu);
 err_destroy_table:
-       ovs_flow_tbl_destroy(ovsl_dereference(dp->table));
+       ovs_flow_tbl_destroy(ovsl_dereference(dp->table), false);
 err_free_dp:
        release_net(ovs_dp_get_net(dp));
        kfree(dp);
@@ -2357,7 +2407,7 @@ static void rehash_flow_table(struct work_struct *work)
                        new_table = ovs_flow_tbl_rehash(old_table);
                        if (!IS_ERR(new_table)) {
                                rcu_assign_pointer(dp->table, new_table);
-                               ovs_flow_tbl_deferred_destroy(old_table);
+                               ovs_flow_tbl_destroy(old_table, true);
                        }
                }
        }
index ad59a3a..559df69 100644 (file)
@@ -92,6 +92,7 @@ struct datapath {
 /**
  * struct ovs_skb_cb - OVS data in skb CB
  * @flow: The flow associated with this packet.  May be %NULL if no flow.
+ * @pkt_key: The flow information extracted from the packet.  Must be nonnull.
  * @tun_key: Key for the tunnel that encapsulated this packet. NULL if the
  * packet is not being tunneled.
  * @ip_summed: Consistently stores L4 checksumming status across different
@@ -104,6 +105,7 @@ struct datapath {
  */
 struct ovs_skb_cb {
        struct sw_flow          *flow;
+       struct sw_flow_key      *pkt_key;
        struct ovs_key_ipv4_tunnel  *tun_key;
 #ifdef NEED_CSUM_NORMALIZE
        enum csum_type          ip_summed;
index 3ce926e..2ac36b6 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2011 Nicira, Inc.
+ * Copyright (c) 2007-2013 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
 
 static struct kmem_cache *flow_cache;
 
+static void ovs_sw_flow_mask_set(struct sw_flow_mask *mask,
+               struct sw_flow_key_range *range, u8 val);
+
+static void update_range__(struct sw_flow_match *match,
+                         size_t offset, size_t size, bool is_mask)
+{
+       struct sw_flow_key_range *range = NULL;
+       size_t start = offset;
+       size_t end = offset + size;
+
+       if (!is_mask)
+               range = &match->range;
+       else if (match->mask)
+               range = &match->mask->range;
+
+       if (!range)
+               return;
+
+       if (range->start == range->end) {
+               range->start = start;
+               range->end = end;
+               return;
+       }
+
+       if (range->start > start)
+               range->start = start;
+
+       if (range->end < end)
+               range->end = end;
+}
+
+#define SW_FLOW_KEY_PUT(match, field, value, is_mask) \
+       do { \
+               update_range__(match, offsetof(struct sw_flow_key, field),  \
+                                    sizeof((match)->key->field), is_mask); \
+               if (is_mask && match->mask != NULL) {                       \
+                       (match)->mask->key.field = value;                   \
+               } else {                                                    \
+                       (match)->key->field = value;                        \
+               }                                                           \
+       } while (0)
+
+#define SW_FLOW_KEY_MEMCPY(match, field, value_p, len, is_mask) \
+       do { \
+               update_range__(match, offsetof(struct sw_flow_key, field),  \
+                               len, is_mask);                              \
+               if (is_mask && match->mask != NULL) {                       \
+                       memcpy(&(match)->mask->key.field, value_p, len);    \
+               } else {                                                    \
+                       memcpy(&(match)->key->field, value_p, len);         \
+               }                                                           \
+       } while (0)
+
+void ovs_match_init(struct sw_flow_match *match,
+                   struct sw_flow_key *key,
+                   struct sw_flow_mask *mask)
+{
+       memset(match, 0, sizeof(*match));
+       match->key = key;
+       match->mask = mask;
+
+       memset(key, 0, sizeof(*key));
+
+       if (mask) {
+               memset(&mask->key, 0, sizeof(mask->key));
+               mask->range.start = mask->range.end = 0;
+       }
+}
+
+static bool ovs_match_validate(const struct sw_flow_match *match,
+               u64 key_attrs, u64 mask_attrs)
+{
+       u64 key_expected = 1ULL << OVS_KEY_ATTR_ETHERNET;
+       u64 mask_allowed = key_attrs;  /* At most allow all key attributes */
+
+       /* The following mask attributes allowed only if they
+        * pass the validation tests. */
+       mask_allowed &= ~((1ULL << OVS_KEY_ATTR_IPV4)
+                       | (1ULL << OVS_KEY_ATTR_IPV6)
+                       | (1ULL << OVS_KEY_ATTR_TCP)
+                       | (1ULL << OVS_KEY_ATTR_UDP)
+                       | (1ULL << OVS_KEY_ATTR_ICMP)
+                       | (1ULL << OVS_KEY_ATTR_ICMPV6)
+                       | (1ULL << OVS_KEY_ATTR_ARP)
+                       | (1ULL << OVS_KEY_ATTR_ND));
+
+       if (match->key->eth.type == htons(ETH_P_802_2) &&
+           match->mask && (match->mask->key.eth.type == htons(0xffff)))
+               mask_allowed |= (1ULL << OVS_KEY_ATTR_ETHERTYPE);
+
+       /* Check key attributes. */
+       if (match->key->eth.type == htons(ETH_P_ARP)
+                       || match->key->eth.type == htons(ETH_P_RARP)) {
+               key_expected |= 1ULL << OVS_KEY_ATTR_ARP;
+               if (match->mask && (match->mask->key.eth.type == htons(0xffff)))
+                       mask_allowed |= 1ULL << OVS_KEY_ATTR_ARP;
+       }
+
+       if (match->key->eth.type == htons(ETH_P_IP)) {
+               key_expected |= 1ULL << OVS_KEY_ATTR_IPV4;
+               if (match->mask && (match->mask->key.eth.type == htons(0xffff)))
+                       mask_allowed |= 1ULL << OVS_KEY_ATTR_IPV4;
+
+               if (match->key->ip.frag != OVS_FRAG_TYPE_LATER) {
+                       if (match->key->ip.proto == IPPROTO_UDP) {
+                               key_expected |= 1ULL << OVS_KEY_ATTR_UDP;
+                               if (match->mask && (match->mask->key.ip.proto == 0xff))
+                                       mask_allowed |= 1ULL << OVS_KEY_ATTR_UDP;
+                       }
+
+                       if (match->key->ip.proto == IPPROTO_TCP) {
+                               key_expected |= 1ULL << OVS_KEY_ATTR_TCP;
+                               if (match->mask && (match->mask->key.ip.proto == 0xff))
+                                       mask_allowed |= 1ULL << OVS_KEY_ATTR_TCP;
+                       }
+
+                       if (match->key->ip.proto == IPPROTO_ICMP) {
+                               key_expected |= 1ULL << OVS_KEY_ATTR_ICMP;
+                               if (match->mask && (match->mask->key.ip.proto == 0xff))
+                                       mask_allowed |= 1ULL << OVS_KEY_ATTR_ICMP;
+                       }
+               }
+       }
+
+       if (match->key->eth.type == htons(ETH_P_IPV6)) {
+               key_expected |= 1ULL << OVS_KEY_ATTR_IPV6;
+               if (match->mask && (match->mask->key.eth.type == htons(0xffff)))
+                       mask_allowed |= 1ULL << OVS_KEY_ATTR_IPV6;
+
+               if (match->key->ip.frag != OVS_FRAG_TYPE_LATER) {
+                       if (match->key->ip.proto == IPPROTO_UDP) {
+                               key_expected |= 1ULL << OVS_KEY_ATTR_UDP;
+                               if (match->mask && (match->mask->key.ip.proto == 0xff))
+                                       mask_allowed |= 1ULL << OVS_KEY_ATTR_UDP;
+                       }
+
+                       if (match->key->ip.proto == IPPROTO_TCP) {
+                               key_expected |= 1ULL << OVS_KEY_ATTR_TCP;
+                               if (match->mask && (match->mask->key.ip.proto == 0xff))
+                                       mask_allowed |= 1ULL << OVS_KEY_ATTR_TCP;
+                       }
+
+                       if (match->key->ip.proto == IPPROTO_ICMPV6) {
+                               key_expected |= 1ULL << OVS_KEY_ATTR_ICMPV6;
+                               if (match->mask && (match->mask->key.ip.proto == 0xff))
+                                       mask_allowed |= 1ULL << OVS_KEY_ATTR_ICMPV6;
+
+                               if (match->key->ipv6.tp.src ==
+                                               htons(NDISC_NEIGHBOUR_SOLICITATION) ||
+                                   match->key->ipv6.tp.src == htons(NDISC_NEIGHBOUR_ADVERTISEMENT)) {
+                                       key_expected |= 1ULL << OVS_KEY_ATTR_ND;
+                                       if (match->mask && (match->mask->key.ipv6.tp.src == htons(0xffff)))
+                                               mask_allowed |= 1ULL << OVS_KEY_ATTR_ND;
+                               }
+                       }
+               }
+       }
+
+       if ((key_attrs & key_expected) != key_expected)
+               /* Key attributes check failed. */
+               return false;
+
+       if ((mask_attrs & mask_allowed) != mask_attrs)
+               /* Mask attributes check failed. */
+               return false;
+
+       return true;
+}
+
 static int check_header(struct sk_buff *skb, int len)
 {
        if (unlikely(skb->len < len))
@@ -122,12 +291,7 @@ u64 ovs_flow_used_time(unsigned long flow_jiffies)
        return cur_ms - idle_ms;
 }
 
-#define SW_FLOW_KEY_OFFSET(field)              \
-       (offsetof(struct sw_flow_key, field) +  \
-        FIELD_SIZEOF(struct sw_flow_key, field))
-
-static int parse_ipv6hdr(struct sk_buff *skb, struct sw_flow_key *key,
-                        int *key_lenp)
+static int parse_ipv6hdr(struct sk_buff *skb, struct sw_flow_key *key)
 {
        unsigned int nh_ofs = skb_network_offset(skb);
        unsigned int nh_len;
@@ -137,8 +301,6 @@ static int parse_ipv6hdr(struct sk_buff *skb, struct sw_flow_key *key,
        __be16 frag_off;
        int err;
 
-       *key_lenp = SW_FLOW_KEY_OFFSET(ipv6.label);
-
        err = check_header(skb, nh_ofs + sizeof(*nh));
        if (unlikely(err))
                return err;
@@ -177,6 +339,22 @@ static bool icmp6hdr_ok(struct sk_buff *skb)
                                  sizeof(struct icmp6hdr));
 }
 
+static void flow_key_mask(struct sw_flow_key *dst,
+                         const struct sw_flow_key *src,
+                         const struct sw_flow_mask *mask)
+{
+       u8 *m = (u8 *)&mask->key + mask->range.start;
+       u8 *s = (u8 *)src + mask->range.start;
+       u8 *d = (u8 *)dst + mask->range.start;
+       int i;
+
+       memset(dst, 0, sizeof(*dst));
+       for (i = 0; i < ovs_sw_flow_mask_size_roundup(mask); i++) {
+               *d = *s & *m;
+               d++, s++, m++;
+       }
+}
+
 #define TCP_FLAGS_OFFSET 13
 #define TCP_FLAG_MASK 0x3f
 
@@ -225,6 +403,7 @@ struct sw_flow *ovs_flow_alloc(void)
 
        spin_lock_init(&flow->lock);
        flow->sf_acts = NULL;
+       flow->mask = NULL;
 
        return flow;
 }
@@ -264,7 +443,7 @@ static void free_buckets(struct flex_array *buckets)
        flex_array_free(buckets);
 }
 
-struct flow_table *ovs_flow_tbl_alloc(int new_size)
+static struct flow_table *__flow_tbl_alloc(int new_size)
 {
        struct flow_table *table = kmalloc(sizeof(*table), GFP_KERNEL);
 
@@ -282,17 +461,15 @@ struct flow_table *ovs_flow_tbl_alloc(int new_size)
        table->node_ver = 0;
        table->keep_flows = false;
        get_random_bytes(&table->hash_seed, sizeof(u32));
+       table->mask_list = NULL;
 
        return table;
 }
 
-void ovs_flow_tbl_destroy(struct flow_table *table)
+static void __flow_tbl_destroy(struct flow_table *table)
 {
        int i;
 
-       if (!table)
-               return;
-
        if (table->keep_flows)
                goto skip_flows;
 
@@ -304,31 +481,55 @@ void ovs_flow_tbl_destroy(struct flow_table *table)
 
                hlist_for_each_entry_safe(flow, n, head, hash_node[ver]) {
                        hlist_del_rcu(&flow->hash_node[ver]);
-                       ovs_flow_free(flow);
+                       ovs_flow_free(flow, false);
                }
        }
 
+       BUG_ON(!list_empty(table->mask_list));
+       kfree(table->mask_list);
+
 skip_flows:
        free_buckets(table->buckets);
        kfree(table);
 }
 
+struct flow_table *ovs_flow_tbl_alloc(int new_size)
+{
+       struct flow_table *table = __flow_tbl_alloc(new_size);
+
+       if (!table)
+               return NULL;
+
+       table->mask_list = kmalloc(sizeof(struct list_head), GFP_KERNEL);
+       if (!table->mask_list) {
+               table->keep_flows = true;
+               __flow_tbl_destroy(table);
+               return NULL;
+       }
+       INIT_LIST_HEAD(table->mask_list);
+
+       return table;
+}
+
 static void flow_tbl_destroy_rcu_cb(struct rcu_head *rcu)
 {
        struct flow_table *table = container_of(rcu, struct flow_table, rcu);
 
-       ovs_flow_tbl_destroy(table);
+       __flow_tbl_destroy(table);
 }
 
-void ovs_flow_tbl_deferred_destroy(struct flow_table *table)
+void ovs_flow_tbl_destroy(struct flow_table *table, bool deferred)
 {
        if (!table)
                return;
 
-       call_rcu(&table->rcu, flow_tbl_destroy_rcu_cb);
+       if (deferred)
+               call_rcu(&table->rcu, flow_tbl_destroy_rcu_cb);
+       else
+               __flow_tbl_destroy(table);
 }
 
-struct sw_flow *ovs_flow_tbl_next(struct flow_table *table, u32 *bucket, u32 *last)
+struct sw_flow *ovs_flow_dump_next(struct flow_table *table, u32 *bucket, u32 *last)
 {
        struct sw_flow *flow;
        struct hlist_head *head;
@@ -354,11 +555,13 @@ struct sw_flow *ovs_flow_tbl_next(struct flow_table *table, u32 *bucket, u32 *la
        return NULL;
 }
 
-static void __flow_tbl_insert(struct flow_table *table, struct sw_flow *flow)
+static void __tbl_insert(struct flow_table *table, struct sw_flow *flow)
 {
        struct hlist_head *head;
+
        head = find_bucket(table, flow->hash);
        hlist_add_head_rcu(&flow->hash_node[table->node_ver], head);
+
        table->count++;
 }
 
@@ -378,8 +581,10 @@ static void flow_table_copy_flows(struct flow_table *old, struct flow_table *new
                head = flex_array_get(old->buckets, i);
 
                hlist_for_each_entry(flow, head, hash_node[old_ver])
-                       __flow_tbl_insert(new, flow);
+                       __tbl_insert(new, flow);
        }
+
+       new->mask_list = old->mask_list;
        old->keep_flows = true;
 }
 
@@ -387,7 +592,7 @@ static struct flow_table *__flow_tbl_rehash(struct flow_table *table, int n_buck
 {
        struct flow_table *new_table;
 
-       new_table = ovs_flow_tbl_alloc(n_buckets);
+       new_table = __flow_tbl_alloc(n_buckets);
        if (!new_table)
                return ERR_PTR(-ENOMEM);
 
@@ -406,28 +611,31 @@ 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)
+static void __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);
 
-       ovs_flow_free(flow);
+       __flow_free(flow);
 }
 
-/* Schedules 'flow' to be freed after the next RCU grace period.
- * The caller must hold rcu_read_lock for this to be sensible. */
-void ovs_flow_deferred_free(struct sw_flow *flow)
+void ovs_flow_free(struct sw_flow *flow, bool deferred)
 {
-       call_rcu(&flow->rcu, rcu_free_flow_callback);
+       if (!flow)
+               return;
+
+       ovs_sw_flow_mask_del_ref((struct sw_flow_mask __force *)flow->mask,
+                                deferred);
+
+       if (deferred)
+               call_rcu(&flow->rcu, rcu_free_flow_callback);
+       else
+               __flow_free(flow);
 }
 
 /* RCU callback used by ovs_flow_deferred_free_acts. */
@@ -506,18 +714,15 @@ static __be16 parse_ethertype(struct sk_buff *skb)
 }
 
 static int parse_icmpv6(struct sk_buff *skb, struct sw_flow_key *key,
-                       int *key_lenp, int nh_len)
+                       int nh_len)
 {
        struct icmp6hdr *icmp = icmp6_hdr(skb);
-       int error = 0;
-       int key_len;
 
        /* The ICMPv6 type and code fields use the 16-bit transport port
         * fields, so we need to store them in 16-bit network byte order.
         */
        key->ipv6.tp.src = htons(icmp->icmp6_type);
        key->ipv6.tp.dst = htons(icmp->icmp6_code);
-       key_len = SW_FLOW_KEY_OFFSET(ipv6.tp);
 
        if (icmp->icmp6_code == 0 &&
            (icmp->icmp6_type == NDISC_NEIGHBOUR_SOLICITATION ||
@@ -526,21 +731,17 @@ static int parse_icmpv6(struct sk_buff *skb, struct sw_flow_key *key,
                struct nd_msg *nd;
                int offset;
 
-               key_len = SW_FLOW_KEY_OFFSET(ipv6.nd);
-
                /* In order to process neighbor discovery options, we need the
                 * entire packet.
                 */
                if (unlikely(icmp_len < sizeof(*nd)))
-                       goto out;
-               if (unlikely(skb_linearize(skb))) {
-                       error = -ENOMEM;
-                       goto out;
-               }
+                       return 0;
+
+               if (unlikely(skb_linearize(skb)))
+                       return -ENOMEM;
 
                nd = (struct nd_msg *)skb_transport_header(skb);
                key->ipv6.nd.target = nd->target;
-               key_len = SW_FLOW_KEY_OFFSET(ipv6.nd);
 
                icmp_len -= sizeof(*nd);
                offset = 0;
@@ -550,7 +751,7 @@ static int parse_icmpv6(struct sk_buff *skb, struct sw_flow_key *key,
                        int opt_len = nd_opt->nd_opt_len * 8;
 
                        if (unlikely(!opt_len || opt_len > icmp_len))
-                               goto invalid;
+                               return 0;
 
                        /* Store the link layer address if the appropriate
                         * option is provided.  It is considered an error if
@@ -575,16 +776,14 @@ static int parse_icmpv6(struct sk_buff *skb, struct sw_flow_key *key,
                }
        }
 
-       goto out;
+       return 0;
 
 invalid:
        memset(&key->ipv6.nd.target, 0, sizeof(key->ipv6.nd.target));
        memset(key->ipv6.nd.sll, 0, sizeof(key->ipv6.nd.sll));
        memset(key->ipv6.nd.tll, 0, sizeof(key->ipv6.nd.tll));
 
-out:
-       *key_lenp = key_len;
-       return error;
+       return 0;
 }
 
 /**
@@ -606,16 +805,14 @@ out:
  *    - skb->network_header: just past the Ethernet header, or just past the
  *      VLAN header, to the first byte of the Ethernet payload.
  *
- *    - skb->transport_header: If key->dl_type is ETH_P_IP or ETH_P_IPV6
+ *    - skb->transport_header: If key->eth.type is ETH_P_IP or ETH_P_IPV6
  *      on output, then just past the IP header, if one is present and
  *      of a correct length, otherwise the same as skb->network_header.
- *      For other key->dl_type values it is left untouched.
+ *      For other key->eth.type values it is left untouched.
  */
-int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key,
-                int *key_lenp)
+int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key)
 {
-       int error = 0;
-       int key_len = SW_FLOW_KEY_OFFSET(eth);
+       int error;
        struct ethhdr *eth;
 
        memset(key, 0, sizeof(*key));
@@ -636,6 +833,8 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key,
        memcpy(key->eth.dst, eth->h_dest, ETH_ALEN);
 
        __skb_pull(skb, 2 * ETH_ALEN);
+       /* We are going to push all headers that we pull, so no need to
+        * update skb->csum here. */
 
        if (vlan_tx_tag_present(skb))
                key->eth.tci = htons(vlan_get_tci(skb));
@@ -655,15 +854,13 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key,
                struct iphdr *nh;
                __be16 offset;
 
-               key_len = SW_FLOW_KEY_OFFSET(ipv4.addr);
-
                error = check_iphdr(skb);
                if (unlikely(error)) {
                        if (error == -EINVAL) {
                                skb->transport_header = skb->network_header;
                                error = 0;
                        }
-                       goto out;
+                       return error;
                }
 
                nh = ip_hdr(skb);
@@ -677,7 +874,7 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key,
                offset = nh->frag_off & htons(IP_OFFSET);
                if (offset) {
                        key->ip.frag = OVS_FRAG_TYPE_LATER;
-                       goto out;
+                       return 0;
                }
                if (nh->frag_off & htons(IP_MF) ||
                         skb_shinfo(skb)->gso_type & SKB_GSO_UDP)
@@ -685,21 +882,18 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key,
 
                /* Transport layer. */
                if (key->ip.proto == IPPROTO_TCP) {
-                       key_len = SW_FLOW_KEY_OFFSET(ipv4.tp);
                        if (tcphdr_ok(skb)) {
                                struct tcphdr *tcp = tcp_hdr(skb);
                                key->ipv4.tp.src = tcp->source;
                                key->ipv4.tp.dst = tcp->dest;
                        }
                } else if (key->ip.proto == IPPROTO_UDP) {
-                       key_len = SW_FLOW_KEY_OFFSET(ipv4.tp);
                        if (udphdr_ok(skb)) {
                                struct udphdr *udp = udp_hdr(skb);
                                key->ipv4.tp.src = udp->source;
                                key->ipv4.tp.dst = udp->dest;
                        }
                } else if (key->ip.proto == IPPROTO_ICMP) {
-                       key_len = SW_FLOW_KEY_OFFSET(ipv4.tp);
                        if (icmphdr_ok(skb)) {
                                struct icmphdr *icmp = icmp_hdr(skb);
                                /* The ICMP type and code fields use the 16-bit
@@ -728,53 +922,49 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key,
                        memcpy(&key->ipv4.addr.dst, arp->ar_tip, sizeof(key->ipv4.addr.dst));
                        memcpy(key->ipv4.arp.sha, arp->ar_sha, ETH_ALEN);
                        memcpy(key->ipv4.arp.tha, arp->ar_tha, ETH_ALEN);
-                       key_len = SW_FLOW_KEY_OFFSET(ipv4.arp);
                }
        } else if (key->eth.type == htons(ETH_P_IPV6)) {
                int nh_len;             /* IPv6 Header + Extensions */
 
-               nh_len = parse_ipv6hdr(skb, key, &key_len);
+               nh_len = parse_ipv6hdr(skb, key);
                if (unlikely(nh_len < 0)) {
-                       if (nh_len == -EINVAL)
+                       if (nh_len == -EINVAL) {
                                skb->transport_header = skb->network_header;
-                       else
+                               error = 0;
+                       } else {
                                error = nh_len;
-                       goto out;
+                       }
+                       return error;
                }
 
                if (key->ip.frag == OVS_FRAG_TYPE_LATER)
-                       goto out;
+                       return 0;
                if (skb_shinfo(skb)->gso_type & SKB_GSO_UDP)
                        key->ip.frag = OVS_FRAG_TYPE_FIRST;
 
                /* Transport layer. */
                if (key->ip.proto == NEXTHDR_TCP) {
-                       key_len = SW_FLOW_KEY_OFFSET(ipv6.tp);
                        if (tcphdr_ok(skb)) {
                                struct tcphdr *tcp = tcp_hdr(skb);
                                key->ipv6.tp.src = tcp->source;
                                key->ipv6.tp.dst = tcp->dest;
                        }
                } else if (key->ip.proto == NEXTHDR_UDP) {
-                       key_len = SW_FLOW_KEY_OFFSET(ipv6.tp);
                        if (udphdr_ok(skb)) {
                                struct udphdr *udp = udp_hdr(skb);
                                key->ipv6.tp.src = udp->source;
                                key->ipv6.tp.dst = udp->dest;
                        }
                } else if (key->ip.proto == NEXTHDR_ICMP) {
-                       key_len = SW_FLOW_KEY_OFFSET(ipv6.tp);
                        if (icmp6hdr_ok(skb)) {
-                               error = parse_icmpv6(skb, key, &key_len, nh_len);
-                               if (error < 0)
-                                       goto out;
+                               error = parse_icmpv6(skb, key, nh_len);
+                               if (error)
+                                       return error;
                        }
                }
        }
 
-out:
-       *key_lenp = key_len;
-       return error;
+       return 0;
 }
 
 static u32 ovs_flow_hash(const struct sw_flow_key *key, int key_start, int key_len)
@@ -783,7 +973,7 @@ static u32 ovs_flow_hash(const struct sw_flow_key *key, int key_start, int key_l
                      DIV_ROUND_UP(key_len - key_start, sizeof(u32)), 0);
 }
 
-static int flow_key_start(struct sw_flow_key *key)
+static int flow_key_start(const struct sw_flow_key *key)
 {
        if (key->tun_key.ipv4_dst)
                return 0;
@@ -791,39 +981,98 @@ static int flow_key_start(struct sw_flow_key *key)
                return offsetof(struct sw_flow_key, phy);
 }
 
-struct sw_flow *ovs_flow_tbl_lookup(struct flow_table *table,
-                               struct sw_flow_key *key, int key_len)
+static bool __cmp_key(const struct sw_flow_key *key1,
+               const struct sw_flow_key *key2,  int key_start, int key_len)
+{
+       return !memcmp((u8 *)key1 + key_start,
+                       (u8 *)key2 + key_start, (key_len - key_start));
+}
+
+static bool __flow_cmp_key(const struct sw_flow *flow,
+               const struct sw_flow_key *key, int key_start, int key_len)
+{
+       return __cmp_key(&flow->key, key, key_start, key_len);
+}
+
+static bool __flow_cmp_unmasked_key(const struct sw_flow *flow,
+                 const struct sw_flow_key *key, int key_start, int key_len)
+{
+       return __cmp_key(&flow->unmasked_key, key, key_start, key_len);
+}
+
+bool ovs_flow_cmp_unmasked_key(const struct sw_flow *flow,
+               const struct sw_flow_key *key, int key_len)
+{
+       int key_start;
+       key_start = flow_key_start(key);
+
+       return __flow_cmp_unmasked_key(flow, key, key_start, key_len);
+
+}
+
+struct sw_flow *ovs_flow_lookup_unmasked_key(struct flow_table *table,
+                                      struct sw_flow_match *match)
+{
+       struct sw_flow_key *unmasked = match->key;
+       int key_len = match->range.end;
+       struct sw_flow *flow;
+
+       flow = ovs_flow_lookup(table, unmasked);
+       if (flow && (!ovs_flow_cmp_unmasked_key(flow, unmasked, key_len)))
+               flow = NULL;
+
+       return flow;
+}
+
+static struct sw_flow *ovs_masked_flow_lookup(struct flow_table *table,
+                                   const struct sw_flow_key *flow_key,
+                                   struct sw_flow_mask *mask)
 {
        struct sw_flow *flow;
        struct hlist_head *head;
-       u8 *_key;
-       int key_start;
+       int key_start = mask->range.start;
+       int key_len = mask->range.end;
        u32 hash;
+       struct sw_flow_key masked_key;
 
-       key_start = flow_key_start(key);
-       hash = ovs_flow_hash(key, key_start, key_len);
-
-       _key = (u8 *) key + key_start;
+       flow_key_mask(&masked_key, flow_key, mask);
+       hash = ovs_flow_hash(&masked_key, key_start, key_len);
        head = find_bucket(table, hash);
        hlist_for_each_entry_rcu(flow, head, hash_node[table->node_ver]) {
-
-               if (flow->hash == hash &&
-                   !memcmp((u8 *)&flow->key + key_start, _key, key_len - key_start)) {
+               if (__flow_cmp_key(flow, &masked_key, key_start, key_len))
                        return flow;
-               }
        }
        return NULL;
 }
 
-void ovs_flow_tbl_insert(struct flow_table *table, struct sw_flow *flow,
-                        struct sw_flow_key *key, int key_len)
+struct sw_flow *ovs_flow_lookup(struct flow_table *tbl,
+                               const struct sw_flow_key *key)
+{
+       struct sw_flow *flow = NULL;
+       struct sw_flow_mask *mask;
+
+       list_for_each_entry_rcu(mask, tbl->mask_list, list) {
+               flow = ovs_masked_flow_lookup(tbl, key, mask);
+               if (flow)  /* Found */
+                       break;
+       }
+
+       return flow;
+}
+
+
+void ovs_flow_insert(struct flow_table *table, struct sw_flow *flow,
+                        const struct sw_flow_key *key, int key_len)
 {
-       flow->hash = ovs_flow_hash(key, flow_key_start(key), key_len);
-       memcpy(&flow->key, key, sizeof(flow->key));
-       __flow_tbl_insert(table, flow);
+       flow->unmasked_key = *key;
+       flow_key_mask(&flow->key, &flow->unmasked_key, ovsl_dereference(flow->mask));
+       flow->hash = ovs_flow_hash(&flow->key,
+                       ovsl_dereference(flow->mask)->range.start,
+                       ovsl_dereference(flow->mask)->range.end);
+       __tbl_insert(table, flow);
 }
 
-void ovs_flow_tbl_remove(struct flow_table *table, struct sw_flow *flow)
+void ovs_flow_remove(struct flow_table *table, struct sw_flow *flow)
 {
        BUG_ON(table->count == 0);
        hlist_del_rcu(&flow->hash_node[table->node_ver]);
@@ -850,120 +1099,29 @@ const int ovs_key_lens[OVS_KEY_ATTR_MAX + 1] = {
        [OVS_KEY_ATTR_TUNNEL] = -1,
 };
 
-static int ipv4_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_len,
-                                 const struct nlattr *a[], u64 *attrs)
+static bool is_all_zero(const u8 *fp, size_t size)
 {
-       const struct ovs_key_icmp *icmp_key;
-       const struct ovs_key_tcp *tcp_key;
-       const struct ovs_key_udp *udp_key;
-
-       switch (swkey->ip.proto) {
-       case IPPROTO_TCP:
-               if (!(*attrs & (1 << OVS_KEY_ATTR_TCP)))
-                       return -EINVAL;
-               *attrs &= ~(1 << OVS_KEY_ATTR_TCP);
-
-               *key_len = SW_FLOW_KEY_OFFSET(ipv4.tp);
-               tcp_key = nla_data(a[OVS_KEY_ATTR_TCP]);
-               swkey->ipv4.tp.src = tcp_key->tcp_src;
-               swkey->ipv4.tp.dst = tcp_key->tcp_dst;
-               break;
-
-       case IPPROTO_UDP:
-               if (!(*attrs & (1 << OVS_KEY_ATTR_UDP)))
-                       return -EINVAL;
-               *attrs &= ~(1 << OVS_KEY_ATTR_UDP);
-
-               *key_len = SW_FLOW_KEY_OFFSET(ipv4.tp);
-               udp_key = nla_data(a[OVS_KEY_ATTR_UDP]);
-               swkey->ipv4.tp.src = udp_key->udp_src;
-               swkey->ipv4.tp.dst = udp_key->udp_dst;
-               break;
-
-       case IPPROTO_ICMP:
-               if (!(*attrs & (1 << OVS_KEY_ATTR_ICMP)))
-                       return -EINVAL;
-               *attrs &= ~(1 << OVS_KEY_ATTR_ICMP);
-
-               *key_len = SW_FLOW_KEY_OFFSET(ipv4.tp);
-               icmp_key = nla_data(a[OVS_KEY_ATTR_ICMP]);
-               swkey->ipv4.tp.src = htons(icmp_key->icmp_type);
-               swkey->ipv4.tp.dst = htons(icmp_key->icmp_code);
-               break;
-       }
-
-       return 0;
-}
-
-static int ipv6_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_len,
-                                 const struct nlattr *a[], u64 *attrs)
-{
-       const struct ovs_key_icmpv6 *icmpv6_key;
-       const struct ovs_key_tcp *tcp_key;
-       const struct ovs_key_udp *udp_key;
-
-       switch (swkey->ip.proto) {
-       case IPPROTO_TCP:
-               if (!(*attrs & (1 << OVS_KEY_ATTR_TCP)))
-                       return -EINVAL;
-               *attrs &= ~(1 << OVS_KEY_ATTR_TCP);
-
-               *key_len = SW_FLOW_KEY_OFFSET(ipv6.tp);
-               tcp_key = nla_data(a[OVS_KEY_ATTR_TCP]);
-               swkey->ipv6.tp.src = tcp_key->tcp_src;
-               swkey->ipv6.tp.dst = tcp_key->tcp_dst;
-               break;
-
-       case IPPROTO_UDP:
-               if (!(*attrs & (1 << OVS_KEY_ATTR_UDP)))
-                       return -EINVAL;
-               *attrs &= ~(1 << OVS_KEY_ATTR_UDP);
-
-               *key_len = SW_FLOW_KEY_OFFSET(ipv6.tp);
-               udp_key = nla_data(a[OVS_KEY_ATTR_UDP]);
-               swkey->ipv6.tp.src = udp_key->udp_src;
-               swkey->ipv6.tp.dst = udp_key->udp_dst;
-               break;
-
-       case IPPROTO_ICMPV6:
-               if (!(*attrs & (1 << OVS_KEY_ATTR_ICMPV6)))
-                       return -EINVAL;
-               *attrs &= ~(1 << OVS_KEY_ATTR_ICMPV6);
-
-               *key_len = SW_FLOW_KEY_OFFSET(ipv6.tp);
-               icmpv6_key = nla_data(a[OVS_KEY_ATTR_ICMPV6]);
-               swkey->ipv6.tp.src = htons(icmpv6_key->icmpv6_type);
-               swkey->ipv6.tp.dst = htons(icmpv6_key->icmpv6_code);
+       int i;
 
-               if (swkey->ipv6.tp.src == htons(NDISC_NEIGHBOUR_SOLICITATION) ||
-                   swkey->ipv6.tp.src == htons(NDISC_NEIGHBOUR_ADVERTISEMENT)) {
-                       const struct ovs_key_nd *nd_key;
+       if (!fp)
+               return false;
 
-                       if (!(*attrs & (1 << OVS_KEY_ATTR_ND)))
-                               return -EINVAL;
-                       *attrs &= ~(1 << OVS_KEY_ATTR_ND);
-
-                       *key_len = SW_FLOW_KEY_OFFSET(ipv6.nd);
-                       nd_key = nla_data(a[OVS_KEY_ATTR_ND]);
-                       memcpy(&swkey->ipv6.nd.target, nd_key->nd_target,
-                              sizeof(swkey->ipv6.nd.target));
-                       memcpy(swkey->ipv6.nd.sll, nd_key->nd_sll, ETH_ALEN);
-                       memcpy(swkey->ipv6.nd.tll, nd_key->nd_tll, ETH_ALEN);
-               }
-               break;
-       }
+       for (i = 0; i < size; i++)
+               if (fp[i])
+                       return false;
 
-       return 0;
+       return true;
 }
 
-static int parse_flow_nlattrs(const struct nlattr *attr,
-                             const struct nlattr *a[], u64 *attrsp)
+static int __parse_flow_nlattrs(const struct nlattr *attr,
+                             const struct nlattr *a[],
+                             u64 *attrsp, bool nz)
 {
        const struct nlattr *nla;
        u64 attrs;
        int rem;
 
-       attrs = 0;
+       attrs = *attrsp;
        nla_for_each_nested(nla, attr, rem) {
                u16 type = nla_type(nla);
                int expected_len;
@@ -975,8 +1133,14 @@ static int parse_flow_nlattrs(const struct nlattr *attr,
                if (nla_len(nla) != expected_len && expected_len != -1)
                        return -EINVAL;
 
-               attrs |= 1ULL << type;
-               a[type] = nla;
+               if (attrs & (1ULL << type))
+                       /* Duplicated field. */
+                       return -EINVAL;
+
+               if (!nz || !is_all_zero(nla_data(nla), expected_len)) {
+                       attrs |= 1ULL << type;
+                       a[type] = nla;
+               }
        }
        if (rem)
                return -EINVAL;
@@ -985,14 +1149,25 @@ static int parse_flow_nlattrs(const struct nlattr *attr,
        return 0;
 }
 
+static int parse_flow_mask_nlattrs(const struct nlattr *attr,
+                             const struct nlattr *a[], u64 *attrsp)
+{
+       return __parse_flow_nlattrs(attr, a, attrsp, true);
+}
+
+static int parse_flow_nlattrs(const struct nlattr *attr,
+                             const struct nlattr *a[], u64 *attrsp)
+{
+       return __parse_flow_nlattrs(attr, a, attrsp, false);
+}
+
 int ipv4_tun_from_nlattr(const struct nlattr *attr,
-                        struct ovs_key_ipv4_tunnel *tun_key)
+                        struct sw_flow_match *match, bool is_mask)
 {
        struct nlattr *a;
        int rem;
        bool ttl = false;
-
-       memset(tun_key, 0, sizeof(*tun_key));
+       __be16 tun_flags = 0;
 
        nla_for_each_nested(a, attr, rem) {
                int type = nla_type(a);
@@ -1012,37 +1187,44 @@ int ipv4_tun_from_nlattr(const struct nlattr *attr,
 
                switch (type) {
                case OVS_TUNNEL_KEY_ATTR_ID:
-                       tun_key->tun_id = nla_get_be64(a);
-                       tun_key->tun_flags |= OVS_TNL_F_KEY;
+                       SW_FLOW_KEY_PUT(match, tun_key.tun_id,
+                                       nla_get_be64(a), is_mask);
+                       tun_flags |= TUNNEL_KEY;
                        break;
                case OVS_TUNNEL_KEY_ATTR_IPV4_SRC:
-                       tun_key->ipv4_src = nla_get_be32(a);
+                       SW_FLOW_KEY_PUT(match, tun_key.ipv4_src,
+                                       nla_get_be32(a), is_mask);
                        break;
                case OVS_TUNNEL_KEY_ATTR_IPV4_DST:
-                       tun_key->ipv4_dst = nla_get_be32(a);
+                       SW_FLOW_KEY_PUT(match, tun_key.ipv4_dst,
+                                       nla_get_be32(a), is_mask);
                        break;
                case OVS_TUNNEL_KEY_ATTR_TOS:
-                       tun_key->ipv4_tos = nla_get_u8(a);
+                       SW_FLOW_KEY_PUT(match, tun_key.ipv4_tos,
+                                       nla_get_u8(a), is_mask);
                        break;
                case OVS_TUNNEL_KEY_ATTR_TTL:
-                       tun_key->ipv4_ttl = nla_get_u8(a);
+                       SW_FLOW_KEY_PUT(match, tun_key.ipv4_ttl,
+                                       nla_get_u8(a), is_mask);
                        ttl = true;
                        break;
                case OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT:
-                       tun_key->tun_flags |= OVS_TNL_F_DONT_FRAGMENT;
+                       tun_flags |= TUNNEL_DONT_FRAGMENT;
                        break;
                case OVS_TUNNEL_KEY_ATTR_CSUM:
-                       tun_key->tun_flags |= OVS_TNL_F_CSUM;
+                       tun_flags |= TUNNEL_CSUM;
                        break;
                default:
                        return -EINVAL;
-
                }
        }
+
+       SW_FLOW_KEY_PUT(match, tun_key.tun_flags, tun_flags, is_mask);
+
        if (rem > 0)
                return -EINVAL;
 
-       if (!tun_key->ipv4_dst)
+       if (!match->key->tun_key.ipv4_dst)
                return -EINVAL;
 
        if (!ttl)
@@ -1052,7 +1234,8 @@ int ipv4_tun_from_nlattr(const struct nlattr *attr,
 }
 
 int ipv4_tun_to_nlattr(struct sk_buff *skb,
-                       const struct ovs_key_ipv4_tunnel *tun_key)
+                       const struct ovs_key_ipv4_tunnel *tun_key,
+                       const struct ovs_key_ipv4_tunnel *output)
 {
        struct nlattr *nla;
 
@@ -1060,23 +1243,23 @@ int ipv4_tun_to_nlattr(struct sk_buff *skb,
        if (!nla)
                return -EMSGSIZE;
 
-       if (tun_key->tun_flags & OVS_TNL_F_KEY &&
-           nla_put_be64(skb, OVS_TUNNEL_KEY_ATTR_ID, tun_key->tun_id))
+       if (tun_key->tun_flags & TUNNEL_KEY &&
+           nla_put_be64(skb, OVS_TUNNEL_KEY_ATTR_ID, output->tun_id))
                return -EMSGSIZE;
        if (tun_key->ipv4_src &&
-           nla_put_be32(skb, OVS_TUNNEL_KEY_ATTR_IPV4_SRC, tun_key->ipv4_src))
+           nla_put_be32(skb, OVS_TUNNEL_KEY_ATTR_IPV4_SRC, output->ipv4_src))
                return -EMSGSIZE;
-       if (nla_put_be32(skb, OVS_TUNNEL_KEY_ATTR_IPV4_DST, tun_key->ipv4_dst))
+       if (nla_put_be32(skb, OVS_TUNNEL_KEY_ATTR_IPV4_DST, output->ipv4_dst))
                return -EMSGSIZE;
        if (tun_key->ipv4_tos &&
-           nla_put_u8(skb, OVS_TUNNEL_KEY_ATTR_TOS, tun_key->ipv4_tos))
+           nla_put_u8(skb, OVS_TUNNEL_KEY_ATTR_TOS, output->ipv4_tos))
                return -EMSGSIZE;
-       if (nla_put_u8(skb, OVS_TUNNEL_KEY_ATTR_TTL, tun_key->ipv4_ttl))
+       if (nla_put_u8(skb, OVS_TUNNEL_KEY_ATTR_TTL, output->ipv4_ttl))
                return -EMSGSIZE;
-       if ((tun_key->tun_flags & OVS_TNL_F_DONT_FRAGMENT) &&
+       if ((tun_key->tun_flags & TUNNEL_DONT_FRAGMENT) &&
                nla_put_flag(skb, OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT))
                return -EMSGSIZE;
-       if ((tun_key->tun_flags & OVS_TNL_F_CSUM) &&
+       if ((tun_key->tun_flags & TUNNEL_CSUM) &&
                nla_put_flag(skb, OVS_TUNNEL_KEY_ATTR_CSUM))
                return -EMSGSIZE;
 
@@ -1084,181 +1267,323 @@ int ipv4_tun_to_nlattr(struct sk_buff *skb,
        return 0;
 }
 
-/**
- * ovs_flow_from_nlattrs - parses Netlink attributes into a flow key.
- * @swkey: receives the extracted flow key.
- * @key_lenp: number of bytes used in @swkey.
- * @attr: Netlink attribute holding nested %OVS_KEY_ATTR_* Netlink attribute
- * sequence.
- */
-int ovs_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_lenp,
-                     const struct nlattr *attr)
-{
-       const struct nlattr *a[OVS_KEY_ATTR_MAX + 1];
-       const struct ovs_key_ethernet *eth_key;
-       int key_len;
-       u64 attrs;
-       int err;
-
-       memset(swkey, 0, sizeof(struct sw_flow_key));
-       key_len = SW_FLOW_KEY_OFFSET(eth);
 
-       err = parse_flow_nlattrs(attr, a, &attrs);
-       if (err)
-               return err;
-
-       /* Metadata attributes. */
-       if (attrs & (1 << OVS_KEY_ATTR_PRIORITY)) {
-               swkey->phy.priority = nla_get_u32(a[OVS_KEY_ATTR_PRIORITY]);
-               attrs &= ~(1 << OVS_KEY_ATTR_PRIORITY);
+static int metadata_from_nlattrs(struct sw_flow_match *match,  u64 *attrs,
+               const struct nlattr **a, bool is_mask)
+{
+       if (*attrs & (1ULL << OVS_KEY_ATTR_PRIORITY)) {
+               SW_FLOW_KEY_PUT(match, phy.priority,
+                         nla_get_u32(a[OVS_KEY_ATTR_PRIORITY]), is_mask);
+               *attrs &= ~(1ULL << OVS_KEY_ATTR_PRIORITY);
        }
-       if (attrs & (1 << OVS_KEY_ATTR_IN_PORT)) {
+
+       if (*attrs & (1ULL << OVS_KEY_ATTR_IN_PORT)) {
                u32 in_port = nla_get_u32(a[OVS_KEY_ATTR_IN_PORT]);
-               if (in_port >= DP_MAX_PORTS)
+
+               if (!is_mask && in_port >= DP_MAX_PORTS)
                        return -EINVAL;
-               swkey->phy.in_port = in_port;
-               attrs &= ~(1 << OVS_KEY_ATTR_IN_PORT);
-       } else {
-               swkey->phy.in_port = DP_MAX_PORTS;
+               SW_FLOW_KEY_PUT(match, phy.in_port, in_port, is_mask);
+               *attrs &= ~(1ULL << OVS_KEY_ATTR_IN_PORT);
        }
-       if (attrs & (1 << OVS_KEY_ATTR_SKB_MARK)) {
+
+       if (*attrs & (1ULL << 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)
+               if (!is_mask && mark != 0)
                        return -EINVAL;
 #endif
-               swkey->phy.skb_mark = mark;
-               attrs &= ~(1 << OVS_KEY_ATTR_SKB_MARK);
+               SW_FLOW_KEY_PUT(match, phy.skb_mark, mark, is_mask);
+               *attrs &= ~(1ULL << OVS_KEY_ATTR_SKB_MARK);
+       }
+       if (*attrs & (1ULL << OVS_KEY_ATTR_TUNNEL)) {
+               if (ipv4_tun_from_nlattr(a[OVS_KEY_ATTR_TUNNEL], match,
+                                       is_mask))
+                       return -EINVAL;
+               *attrs &= ~(1ULL << OVS_KEY_ATTR_TUNNEL);
        }
+       return 0;
+}
 
-       if (attrs & (1ULL << OVS_KEY_ATTR_TUNNEL)) {
-               err = ipv4_tun_from_nlattr(a[OVS_KEY_ATTR_TUNNEL], &swkey->tun_key);
-               if (err)
-                       return err;
+static int ovs_key_from_nlattrs(struct sw_flow_match *match,  u64 attrs,
+               const struct nlattr **a, bool is_mask)
+{
+       int err;
+       u64 orig_attrs = attrs;
 
-               attrs &= ~(1ULL << OVS_KEY_ATTR_TUNNEL);
-       }
+       err = metadata_from_nlattrs(match, &attrs, a, is_mask);
+       if (err)
+               return err;
 
-       /* Data attributes. */
-       if (!(attrs & (1 << OVS_KEY_ATTR_ETHERNET)))
-               return -EINVAL;
-       attrs &= ~(1 << OVS_KEY_ATTR_ETHERNET);
+       if (attrs & (1ULL << OVS_KEY_ATTR_ETHERNET)) {
+               const struct ovs_key_ethernet *eth_key;
 
-       eth_key = nla_data(a[OVS_KEY_ATTR_ETHERNET]);
-       memcpy(swkey->eth.src, eth_key->eth_src, ETH_ALEN);
-       memcpy(swkey->eth.dst, eth_key->eth_dst, ETH_ALEN);
+               eth_key = nla_data(a[OVS_KEY_ATTR_ETHERNET]);
+               SW_FLOW_KEY_MEMCPY(match, eth.src,
+                               eth_key->eth_src, ETH_ALEN, is_mask);
+               SW_FLOW_KEY_MEMCPY(match, eth.dst,
+                               eth_key->eth_dst, ETH_ALEN, is_mask);
+               attrs &= ~(1ULL << OVS_KEY_ATTR_ETHERNET);
+       }
 
-       if (attrs & (1u << OVS_KEY_ATTR_ETHERTYPE) &&
-           nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE]) == htons(ETH_P_8021Q)) {
-               const struct nlattr *encap;
+       if (attrs & (1ULL << OVS_KEY_ATTR_VLAN)) {
                __be16 tci;
 
-               if (attrs != ((1 << OVS_KEY_ATTR_VLAN) |
-                             (1 << OVS_KEY_ATTR_ETHERTYPE) |
-                             (1 << OVS_KEY_ATTR_ENCAP)))
-                       return -EINVAL;
-
-               encap = a[OVS_KEY_ATTR_ENCAP];
                tci = nla_get_be16(a[OVS_KEY_ATTR_VLAN]);
-               if (tci & htons(VLAN_TAG_PRESENT)) {
-                       swkey->eth.tci = tci;
-
-                       err = parse_flow_nlattrs(encap, a, &attrs);
-                       if (err)
-                               return err;
-               } else if (!tci) {
-                       /* Corner case for truncated 802.1Q header. */
-                       if (nla_len(encap))
+               if (!is_mask)
+                       if (!(tci & htons(VLAN_TAG_PRESENT)))
                                return -EINVAL;
 
-                       swkey->eth.type = htons(ETH_P_8021Q);
-                       *key_lenp = key_len;
-                       return 0;
-               } else {
-                       return -EINVAL;
-               }
+               SW_FLOW_KEY_PUT(match, eth.tci, tci, is_mask);
+               attrs &= ~(1ULL << OVS_KEY_ATTR_VLAN);
        }
 
-       if (attrs & (1 << OVS_KEY_ATTR_ETHERTYPE)) {
-               swkey->eth.type = nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE]);
-               if (ntohs(swkey->eth.type) < ETH_P_802_3_MIN)
+       if (attrs & (1ULL << OVS_KEY_ATTR_ETHERTYPE)) {
+               __be16 eth_type;
+
+               eth_type = nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE]);
+               if (!is_mask && ntohs(eth_type) < ETH_P_802_3_MIN)
                        return -EINVAL;
-               attrs &= ~(1 << OVS_KEY_ATTR_ETHERTYPE);
-       } else {
-               swkey->eth.type = htons(ETH_P_802_2);
+
+               SW_FLOW_KEY_PUT(match, eth.type, eth_type, is_mask);
+               attrs &= ~(1ULL << OVS_KEY_ATTR_ETHERTYPE);
+       } else if (!is_mask) {
+               SW_FLOW_KEY_PUT(match, eth.type, htons(ETH_P_802_2), is_mask);
        }
 
-       if (swkey->eth.type == htons(ETH_P_IP)) {
+       if (attrs & (1ULL << OVS_KEY_ATTR_IPV4)) {
                const struct ovs_key_ipv4 *ipv4_key;
 
-               if (!(attrs & (1 << OVS_KEY_ATTR_IPV4)))
-                       return -EINVAL;
-               attrs &= ~(1 << OVS_KEY_ATTR_IPV4);
-
-               key_len = SW_FLOW_KEY_OFFSET(ipv4.addr);
                ipv4_key = nla_data(a[OVS_KEY_ATTR_IPV4]);
-               if (ipv4_key->ipv4_frag > OVS_FRAG_TYPE_MAX)
+               if (!is_mask && ipv4_key->ipv4_frag > OVS_FRAG_TYPE_MAX)
                        return -EINVAL;
-               swkey->ip.proto = ipv4_key->ipv4_proto;
-               swkey->ip.tos = ipv4_key->ipv4_tos;
-               swkey->ip.ttl = ipv4_key->ipv4_ttl;
-               swkey->ip.frag = ipv4_key->ipv4_frag;
-               swkey->ipv4.addr.src = ipv4_key->ipv4_src;
-               swkey->ipv4.addr.dst = ipv4_key->ipv4_dst;
-
-               if (swkey->ip.frag != OVS_FRAG_TYPE_LATER) {
-                       err = ipv4_flow_from_nlattrs(swkey, &key_len, a, &attrs);
-                       if (err)
-                               return err;
-               }
-       } else if (swkey->eth.type == htons(ETH_P_IPV6)) {
+               SW_FLOW_KEY_PUT(match, ip.proto,
+                               ipv4_key->ipv4_proto, is_mask);
+               SW_FLOW_KEY_PUT(match, ip.tos,
+                               ipv4_key->ipv4_tos, is_mask);
+               SW_FLOW_KEY_PUT(match, ip.ttl,
+                               ipv4_key->ipv4_ttl, is_mask);
+               SW_FLOW_KEY_PUT(match, ip.frag,
+                               ipv4_key->ipv4_frag, is_mask);
+               SW_FLOW_KEY_PUT(match, ipv4.addr.src,
+                               ipv4_key->ipv4_src, is_mask);
+               SW_FLOW_KEY_PUT(match, ipv4.addr.dst,
+                               ipv4_key->ipv4_dst, is_mask);
+               attrs &= ~(1ULL << OVS_KEY_ATTR_IPV4);
+       }
+
+       if (attrs & (1ULL << OVS_KEY_ATTR_IPV6)) {
                const struct ovs_key_ipv6 *ipv6_key;
 
-               if (!(attrs & (1 << OVS_KEY_ATTR_IPV6)))
+               ipv6_key = nla_data(a[OVS_KEY_ATTR_IPV6]);
+               if (!is_mask && ipv6_key->ipv6_frag > OVS_FRAG_TYPE_MAX)
                        return -EINVAL;
-               attrs &= ~(1 << OVS_KEY_ATTR_IPV6);
+               SW_FLOW_KEY_PUT(match, ipv6.label,
+                               ipv6_key->ipv6_label, is_mask);
+               SW_FLOW_KEY_PUT(match, ip.proto,
+                               ipv6_key->ipv6_proto, is_mask);
+               SW_FLOW_KEY_PUT(match, ip.tos,
+                               ipv6_key->ipv6_tclass, is_mask);
+               SW_FLOW_KEY_PUT(match, ip.ttl,
+                               ipv6_key->ipv6_hlimit, is_mask);
+               SW_FLOW_KEY_PUT(match, ip.frag,
+                               ipv6_key->ipv6_frag, is_mask);
+               SW_FLOW_KEY_MEMCPY(match, ipv6.addr.src,
+                               ipv6_key->ipv6_src,
+                               sizeof(match->key->ipv6.addr.src),
+                               is_mask);
+               SW_FLOW_KEY_MEMCPY(match, ipv6.addr.dst,
+                               ipv6_key->ipv6_dst,
+                               sizeof(match->key->ipv6.addr.dst),
+                               is_mask);
+
+               attrs &= ~(1ULL << OVS_KEY_ATTR_IPV6);
+       }
 
-               key_len = SW_FLOW_KEY_OFFSET(ipv6.label);
-               ipv6_key = nla_data(a[OVS_KEY_ATTR_IPV6]);
-               if (ipv6_key->ipv6_frag > OVS_FRAG_TYPE_MAX)
+       if (attrs & (1ULL << OVS_KEY_ATTR_ARP)) {
+               const struct ovs_key_arp *arp_key;
+
+               arp_key = nla_data(a[OVS_KEY_ATTR_ARP]);
+               if (!is_mask && (arp_key->arp_op & htons(0xff00)))
                        return -EINVAL;
-               swkey->ipv6.label = ipv6_key->ipv6_label;
-               swkey->ip.proto = ipv6_key->ipv6_proto;
-               swkey->ip.tos = ipv6_key->ipv6_tclass;
-               swkey->ip.ttl = ipv6_key->ipv6_hlimit;
-               swkey->ip.frag = ipv6_key->ipv6_frag;
-               memcpy(&swkey->ipv6.addr.src, ipv6_key->ipv6_src,
-                      sizeof(swkey->ipv6.addr.src));
-               memcpy(&swkey->ipv6.addr.dst, ipv6_key->ipv6_dst,
-                      sizeof(swkey->ipv6.addr.dst));
-
-               if (swkey->ip.frag != OVS_FRAG_TYPE_LATER) {
-                       err = ipv6_flow_from_nlattrs(swkey, &key_len, a, &attrs);
+
+               SW_FLOW_KEY_PUT(match, ipv4.addr.src,
+                               arp_key->arp_sip, is_mask);
+               SW_FLOW_KEY_PUT(match, ipv4.addr.dst,
+                       arp_key->arp_tip, is_mask);
+               SW_FLOW_KEY_PUT(match, ip.proto,
+                               ntohs(arp_key->arp_op), is_mask);
+               SW_FLOW_KEY_MEMCPY(match, ipv4.arp.sha,
+                               arp_key->arp_sha, ETH_ALEN, is_mask);
+               SW_FLOW_KEY_MEMCPY(match, ipv4.arp.tha,
+                               arp_key->arp_tha, ETH_ALEN, is_mask);
+
+               attrs &= ~(1ULL << OVS_KEY_ATTR_ARP);
+       }
+
+       if (attrs & (1ULL << OVS_KEY_ATTR_TCP)) {
+               const struct ovs_key_tcp *tcp_key;
+
+               tcp_key = nla_data(a[OVS_KEY_ATTR_TCP]);
+               if (orig_attrs & (1ULL << OVS_KEY_ATTR_IPV4)) {
+                       SW_FLOW_KEY_PUT(match, ipv4.tp.src,
+                                       tcp_key->tcp_src, is_mask);
+                       SW_FLOW_KEY_PUT(match, ipv4.tp.dst,
+                                       tcp_key->tcp_dst, is_mask);
+               } else {
+                       SW_FLOW_KEY_PUT(match, ipv6.tp.src,
+                                       tcp_key->tcp_src, is_mask);
+                       SW_FLOW_KEY_PUT(match, ipv6.tp.dst,
+                                       tcp_key->tcp_dst, is_mask);
+               }
+               attrs &= ~(1ULL << OVS_KEY_ATTR_TCP);
+       }
+
+       if (attrs & (1ULL << OVS_KEY_ATTR_UDP)) {
+               const struct ovs_key_udp *udp_key;
+
+               udp_key = nla_data(a[OVS_KEY_ATTR_UDP]);
+               if (orig_attrs & (1ULL << OVS_KEY_ATTR_IPV4)) {
+                       SW_FLOW_KEY_PUT(match, ipv4.tp.src,
+                                       udp_key->udp_src, is_mask);
+                       SW_FLOW_KEY_PUT(match, ipv4.tp.dst,
+                                       udp_key->udp_dst, is_mask);
+               } else {
+                       SW_FLOW_KEY_PUT(match, ipv6.tp.src,
+                                       udp_key->udp_src, is_mask);
+                       SW_FLOW_KEY_PUT(match, ipv6.tp.dst,
+                                       udp_key->udp_dst, is_mask);
+               }
+               attrs &= ~(1ULL << OVS_KEY_ATTR_UDP);
+       }
+
+       if (attrs & (1ULL << OVS_KEY_ATTR_ICMP)) {
+               const struct ovs_key_icmp *icmp_key;
+
+               icmp_key = nla_data(a[OVS_KEY_ATTR_ICMP]);
+               SW_FLOW_KEY_PUT(match, ipv4.tp.src,
+                               htons(icmp_key->icmp_type), is_mask);
+               SW_FLOW_KEY_PUT(match, ipv4.tp.dst,
+                               htons(icmp_key->icmp_code), is_mask);
+               attrs &= ~(1ULL << OVS_KEY_ATTR_ICMP);
+       }
+
+       if (attrs & (1ULL << OVS_KEY_ATTR_ICMPV6)) {
+               const struct ovs_key_icmpv6 *icmpv6_key;
+
+               icmpv6_key = nla_data(a[OVS_KEY_ATTR_ICMPV6]);
+               SW_FLOW_KEY_PUT(match, ipv6.tp.src,
+                               htons(icmpv6_key->icmpv6_type), is_mask);
+               SW_FLOW_KEY_PUT(match, ipv6.tp.dst,
+                               htons(icmpv6_key->icmpv6_code), is_mask);
+               attrs &= ~(1ULL << OVS_KEY_ATTR_ICMPV6);
+       }
+
+       if (attrs & (1ULL << OVS_KEY_ATTR_ND)) {
+               const struct ovs_key_nd *nd_key;
+
+               nd_key = nla_data(a[OVS_KEY_ATTR_ND]);
+               SW_FLOW_KEY_MEMCPY(match, ipv6.nd.target,
+                       nd_key->nd_target,
+                       sizeof(match->key->ipv6.nd.target),
+                       is_mask);
+               SW_FLOW_KEY_MEMCPY(match, ipv6.nd.sll,
+                       nd_key->nd_sll, ETH_ALEN, is_mask);
+               SW_FLOW_KEY_MEMCPY(match, ipv6.nd.tll,
+                               nd_key->nd_tll, ETH_ALEN, is_mask);
+               attrs &= ~(1ULL << OVS_KEY_ATTR_ND);
+       }
+
+       if (attrs != 0)
+               return -EINVAL;
+
+       return 0;
+}
+
+/**
+ * ovs_match_from_nlattrs - parses Netlink attributes into a flow key and
+ * mask. In case the 'mask' is NULL, the flow is treated as exact match
+ * flow. Otherwise, it is treated as a wildcarded flow, except the mask
+ * does not include any don't care bit.
+ * @match: receives the extracted flow match information.
+ * @key: Netlink attribute holding nested %OVS_KEY_ATTR_* Netlink attribute
+ * sequence. The fields should of the packet that triggered the creation
+ * of this flow.
+ * @mask: Optional. Netlink attribute holding nested %OVS_KEY_ATTR_* Netlink
+ * attribute specifies the mask field of the wildcarded flow.
+ */
+int ovs_match_from_nlattrs(struct sw_flow_match *match,
+                          const struct nlattr *key,
+                          const struct nlattr *mask)
+{
+       const struct nlattr *a[OVS_KEY_ATTR_MAX + 1];
+       const struct nlattr *encap;
+       u64 key_attrs = 0;
+       u64 mask_attrs = 0;
+       bool encap_valid = false;
+       int err;
+
+       err = parse_flow_nlattrs(key, a, &key_attrs);
+       if (err)
+               return err;
+
+       if (key_attrs & 1ULL << OVS_KEY_ATTR_ENCAP) {
+               encap = a[OVS_KEY_ATTR_ENCAP];
+               key_attrs &= ~(1ULL << OVS_KEY_ATTR_ENCAP);
+               if (nla_len(encap)) {
+                       __be16 eth_type = 0; /* ETH_P_8021Q */
+
+                       if (a[OVS_KEY_ATTR_ETHERTYPE])
+                               eth_type = nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE]);
+
+                       if  ((eth_type == htons(ETH_P_8021Q)) && (a[OVS_KEY_ATTR_VLAN])) {
+                               encap_valid = true;
+                               key_attrs &= ~(1ULL << OVS_KEY_ATTR_ETHERTYPE);
+                               err = parse_flow_nlattrs(encap, a, &key_attrs);
+                       } else
+                               err = -EINVAL;
+
                        if (err)
                                return err;
                }
-       } 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)))
-                       return -EINVAL;
-               attrs &= ~(1 << OVS_KEY_ATTR_ARP);
+       err = ovs_key_from_nlattrs(match, key_attrs, a, false);
+       if (err)
+               return err;
 
-               key_len = SW_FLOW_KEY_OFFSET(ipv4.arp);
-               arp_key = nla_data(a[OVS_KEY_ATTR_ARP]);
-               swkey->ipv4.addr.src = arp_key->arp_sip;
-               swkey->ipv4.addr.dst = arp_key->arp_tip;
-               if (arp_key->arp_op & htons(0xff00))
-                       return -EINVAL;
-               swkey->ip.proto = ntohs(arp_key->arp_op);
-               memcpy(swkey->ipv4.arp.sha, arp_key->arp_sha, ETH_ALEN);
-               memcpy(swkey->ipv4.arp.tha, arp_key->arp_tha, ETH_ALEN);
+       if (mask) {
+               err = parse_flow_mask_nlattrs(mask, a, &mask_attrs);
+               if (err)
+                       return err;
+
+               if ((mask_attrs & 1ULL << OVS_KEY_ATTR_ENCAP) && encap_valid) {
+                       __be16 eth_type = 0;
+
+                       mask_attrs &= ~(1ULL << OVS_KEY_ATTR_ENCAP);
+                       if (a[OVS_KEY_ATTR_ETHERTYPE])
+                               eth_type = nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE]);
+                       if (eth_type == htons(0xffff)) {
+                               mask_attrs &= ~(1ULL << OVS_KEY_ATTR_ETHERTYPE);
+                               encap = a[OVS_KEY_ATTR_ENCAP];
+                               err = parse_flow_mask_nlattrs(encap, a, &mask_attrs);
+                       } else
+                               err = -EINVAL;
+
+                       if (err)
+                               return err;
+               }
+
+               err = ovs_key_from_nlattrs(match, mask_attrs, a, true);
+               if (err)
+                       return err;
+       } else {
+               /* Populate exact match flow's key mask. */
+               if (match->mask)
+                       ovs_sw_flow_mask_set(match->mask, &match->range, 0xff);
        }
 
-       if (attrs)
+       if (!ovs_match_validate(match, key_attrs, mask_attrs))
                return -EINVAL;
-       *key_lenp = key_len;
 
        return 0;
 }
@@ -1266,7 +1591,6 @@ int ovs_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_lenp,
 /**
  * ovs_flow_metadata_from_nlattrs - parses Netlink attributes into a flow key.
  * @flow: Receives extracted in_port, priority, tun_key and skb_mark.
- * @key_len: Length of key in @flow.  Used for calculating flow hash.
  * @attr: Netlink attribute holding nested %OVS_KEY_ATTR_* Netlink attribute
  * sequence.
  *
@@ -1276,106 +1600,88 @@ int ovs_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_lenp,
  * extracted from the packet itself.
  */
 
-int ovs_flow_metadata_from_nlattrs(struct sw_flow *flow, int key_len, const struct nlattr *attr)
+int ovs_flow_metadata_from_nlattrs(struct sw_flow *flow,
+               const struct nlattr *attr)
 {
        struct ovs_key_ipv4_tunnel *tun_key = &flow->key.tun_key;
-       const struct nlattr *nla;
-       int rem;
+       const struct nlattr *a[OVS_KEY_ATTR_MAX + 1];
+       u64 attrs = 0;
+       int err;
+       struct sw_flow_match match;
 
        flow->key.phy.in_port = DP_MAX_PORTS;
        flow->key.phy.priority = 0;
        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);
-
-               if (type <= OVS_KEY_ATTR_MAX && ovs_key_lens[type] > 0) {
-                       int err;
-
-                       if (nla_len(nla) != ovs_key_lens[type])
-                               return -EINVAL;
-
-                       switch (type) {
-                       case OVS_KEY_ATTR_PRIORITY:
-                               flow->key.phy.priority = nla_get_u32(nla);
-                               break;
-
-                       case OVS_KEY_ATTR_TUNNEL:
-                               err = ipv4_tun_from_nlattr(nla, tun_key);
-                               if (err)
-                                       return err;
-                               break;
-
-                       case OVS_KEY_ATTR_IN_PORT:
-                               if (nla_get_u32(nla) >= DP_MAX_PORTS)
-                                       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;
-                       }
-               }
-       }
-       if (rem)
+       err = parse_flow_nlattrs(attr, a, &attrs);
+       if (err)
                return -EINVAL;
 
-       flow->hash = ovs_flow_hash(&flow->key,
-                                  flow_key_start(&flow->key), key_len);
+       memset(&match, 0, sizeof(match));
+       match.key = &flow->key;
+
+       err = metadata_from_nlattrs(&match, &attrs, a, false);
+       if (err)
+               return err;
 
        return 0;
 }
 
-int ovs_flow_to_nlattrs(const struct sw_flow_key *swkey, struct sk_buff *skb)
+int ovs_flow_to_nlattrs(const struct sw_flow_key *swkey,
+               const struct sw_flow_key *output, struct sk_buff *skb)
 {
        struct ovs_key_ethernet *eth_key;
        struct nlattr *nla, *encap;
 
        if (swkey->phy.priority &&
-           nla_put_u32(skb, OVS_KEY_ATTR_PRIORITY, swkey->phy.priority))
+           nla_put_u32(skb, OVS_KEY_ATTR_PRIORITY, output->phy.priority))
                goto nla_put_failure;
 
        if (swkey->tun_key.ipv4_dst &&
-           ipv4_tun_to_nlattr(skb, &swkey->tun_key))
+           ipv4_tun_to_nlattr(skb, &swkey->tun_key, &output->tun_key))
                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.in_port != DP_MAX_PORTS) {
+               /* Exact match upper 16 bits. */
+               u16 upper_u16;
+               upper_u16 = (swkey == output) ? 0 : 0xffff;
+
+               if (nla_put_u32(skb, OVS_KEY_ATTR_IN_PORT,
+                                       (upper_u16 << 16) | output->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))
+           nla_put_u32(skb, OVS_KEY_ATTR_SKB_MARK, output->phy.skb_mark))
                goto nla_put_failure;
 
        nla = nla_reserve(skb, OVS_KEY_ATTR_ETHERNET, sizeof(*eth_key));
        if (!nla)
                goto nla_put_failure;
+
        eth_key = nla_data(nla);
-       memcpy(eth_key->eth_src, swkey->eth.src, ETH_ALEN);
-       memcpy(eth_key->eth_dst, swkey->eth.dst, ETH_ALEN);
+       memcpy(eth_key->eth_src, output->eth.src, ETH_ALEN);
+       memcpy(eth_key->eth_dst, output->eth.dst, ETH_ALEN);
 
        if (swkey->eth.tci || swkey->eth.type == htons(ETH_P_8021Q)) {
-               if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE, htons(ETH_P_8021Q)) ||
-                   nla_put_be16(skb, OVS_KEY_ATTR_VLAN, swkey->eth.tci))
+               __be16 eth_type;
+               eth_type = (swkey == output) ? htons(ETH_P_8021Q) : htons(0xffff) ;
+               if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE, eth_type) ||
+                   nla_put_be16(skb, OVS_KEY_ATTR_VLAN, output->eth.tci))
                        goto nla_put_failure;
                encap = nla_nest_start(skb, OVS_KEY_ATTR_ENCAP);
                if (!swkey->eth.tci)
                        goto unencap;
-       } else {
+       } else
                encap = NULL;
-       }
 
-       if (swkey->eth.type == htons(ETH_P_802_2))
+       if ((swkey == output) && (swkey->eth.type == htons(ETH_P_802_2)))
                goto unencap;
 
-       if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE, swkey->eth.type))
-               goto nla_put_failure;
+       if (output->eth.type != 0)
+               if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE, output->eth.type))
+                       goto nla_put_failure;
 
        if (swkey->eth.type == htons(ETH_P_IP)) {
                struct ovs_key_ipv4 *ipv4_key;
@@ -1384,12 +1690,12 @@ int ovs_flow_to_nlattrs(const struct sw_flow_key *swkey, struct sk_buff *skb)
                if (!nla)
                        goto nla_put_failure;
                ipv4_key = nla_data(nla);
-               ipv4_key->ipv4_src = swkey->ipv4.addr.src;
-               ipv4_key->ipv4_dst = swkey->ipv4.addr.dst;
-               ipv4_key->ipv4_proto = swkey->ip.proto;
-               ipv4_key->ipv4_tos = swkey->ip.tos;
-               ipv4_key->ipv4_ttl = swkey->ip.ttl;
-               ipv4_key->ipv4_frag = swkey->ip.frag;
+               ipv4_key->ipv4_src = output->ipv4.addr.src;
+               ipv4_key->ipv4_dst = output->ipv4.addr.dst;
+               ipv4_key->ipv4_proto = output->ip.proto;
+               ipv4_key->ipv4_tos = output->ip.tos;
+               ipv4_key->ipv4_ttl = output->ip.ttl;
+               ipv4_key->ipv4_frag = output->ip.frag;
        } else if (swkey->eth.type == htons(ETH_P_IPV6)) {
                struct ovs_key_ipv6 *ipv6_key;
 
@@ -1397,15 +1703,15 @@ int ovs_flow_to_nlattrs(const struct sw_flow_key *swkey, struct sk_buff *skb)
                if (!nla)
                        goto nla_put_failure;
                ipv6_key = nla_data(nla);
-               memcpy(ipv6_key->ipv6_src, &swkey->ipv6.addr.src,
+               memcpy(ipv6_key->ipv6_src, &output->ipv6.addr.src,
                                sizeof(ipv6_key->ipv6_src));
-               memcpy(ipv6_key->ipv6_dst, &swkey->ipv6.addr.dst,
+               memcpy(ipv6_key->ipv6_dst, &output->ipv6.addr.dst,
                                sizeof(ipv6_key->ipv6_dst));
-               ipv6_key->ipv6_label = swkey->ipv6.label;
-               ipv6_key->ipv6_proto = swkey->ip.proto;
-               ipv6_key->ipv6_tclass = swkey->ip.tos;
-               ipv6_key->ipv6_hlimit = swkey->ip.ttl;
-               ipv6_key->ipv6_frag = swkey->ip.frag;
+               ipv6_key->ipv6_label = output->ipv6.label;
+               ipv6_key->ipv6_proto = output->ip.proto;
+               ipv6_key->ipv6_tclass = output->ip.tos;
+               ipv6_key->ipv6_hlimit = output->ip.ttl;
+               ipv6_key->ipv6_frag = output->ip.frag;
        } else if (swkey->eth.type == htons(ETH_P_ARP) ||
                   swkey->eth.type == htons(ETH_P_RARP)) {
                struct ovs_key_arp *arp_key;
@@ -1415,11 +1721,11 @@ int ovs_flow_to_nlattrs(const struct sw_flow_key *swkey, struct sk_buff *skb)
                        goto nla_put_failure;
                arp_key = nla_data(nla);
                memset(arp_key, 0, sizeof(struct ovs_key_arp));
-               arp_key->arp_sip = swkey->ipv4.addr.src;
-               arp_key->arp_tip = swkey->ipv4.addr.dst;
-               arp_key->arp_op = htons(swkey->ip.proto);
-               memcpy(arp_key->arp_sha, swkey->ipv4.arp.sha, ETH_ALEN);
-               memcpy(arp_key->arp_tha, swkey->ipv4.arp.tha, ETH_ALEN);
+               arp_key->arp_sip = output->ipv4.addr.src;
+               arp_key->arp_tip = output->ipv4.addr.dst;
+               arp_key->arp_op = htons(output->ip.proto);
+               memcpy(arp_key->arp_sha, output->ipv4.arp.sha, ETH_ALEN);
+               memcpy(arp_key->arp_tha, output->ipv4.arp.tha, ETH_ALEN);
        }
 
        if ((swkey->eth.type == htons(ETH_P_IP) ||
@@ -1434,11 +1740,11 @@ int ovs_flow_to_nlattrs(const struct sw_flow_key *swkey, struct sk_buff *skb)
                                goto nla_put_failure;
                        tcp_key = nla_data(nla);
                        if (swkey->eth.type == htons(ETH_P_IP)) {
-                               tcp_key->tcp_src = swkey->ipv4.tp.src;
-                               tcp_key->tcp_dst = swkey->ipv4.tp.dst;
+                               tcp_key->tcp_src = output->ipv4.tp.src;
+                               tcp_key->tcp_dst = output->ipv4.tp.dst;
                        } else if (swkey->eth.type == htons(ETH_P_IPV6)) {
-                               tcp_key->tcp_src = swkey->ipv6.tp.src;
-                               tcp_key->tcp_dst = swkey->ipv6.tp.dst;
+                               tcp_key->tcp_src = output->ipv6.tp.src;
+                               tcp_key->tcp_dst = output->ipv6.tp.dst;
                        }
                } else if (swkey->ip.proto == IPPROTO_UDP) {
                        struct ovs_key_udp *udp_key;
@@ -1448,11 +1754,11 @@ int ovs_flow_to_nlattrs(const struct sw_flow_key *swkey, struct sk_buff *skb)
                                goto nla_put_failure;
                        udp_key = nla_data(nla);
                        if (swkey->eth.type == htons(ETH_P_IP)) {
-                               udp_key->udp_src = swkey->ipv4.tp.src;
-                               udp_key->udp_dst = swkey->ipv4.tp.dst;
+                               udp_key->udp_src = output->ipv4.tp.src;
+                               udp_key->udp_dst = output->ipv4.tp.dst;
                        } else if (swkey->eth.type == htons(ETH_P_IPV6)) {
-                               udp_key->udp_src = swkey->ipv6.tp.src;
-                               udp_key->udp_dst = swkey->ipv6.tp.dst;
+                               udp_key->udp_src = output->ipv6.tp.src;
+                               udp_key->udp_dst = output->ipv6.tp.dst;
                        }
                } else if (swkey->eth.type == htons(ETH_P_IP) &&
                           swkey->ip.proto == IPPROTO_ICMP) {
@@ -1462,8 +1768,8 @@ int ovs_flow_to_nlattrs(const struct sw_flow_key *swkey, struct sk_buff *skb)
                        if (!nla)
                                goto nla_put_failure;
                        icmp_key = nla_data(nla);
-                       icmp_key->icmp_type = ntohs(swkey->ipv4.tp.src);
-                       icmp_key->icmp_code = ntohs(swkey->ipv4.tp.dst);
+                       icmp_key->icmp_type = ntohs(output->ipv4.tp.src);
+                       icmp_key->icmp_code = ntohs(output->ipv4.tp.dst);
                } else if (swkey->eth.type == htons(ETH_P_IPV6) &&
                           swkey->ip.proto == IPPROTO_ICMPV6) {
                        struct ovs_key_icmpv6 *icmpv6_key;
@@ -1473,8 +1779,8 @@ int ovs_flow_to_nlattrs(const struct sw_flow_key *swkey, struct sk_buff *skb)
                        if (!nla)
                                goto nla_put_failure;
                        icmpv6_key = nla_data(nla);
-                       icmpv6_key->icmpv6_type = ntohs(swkey->ipv6.tp.src);
-                       icmpv6_key->icmpv6_code = ntohs(swkey->ipv6.tp.dst);
+                       icmpv6_key->icmpv6_type = ntohs(output->ipv6.tp.src);
+                       icmpv6_key->icmpv6_code = ntohs(output->ipv6.tp.dst);
 
                        if (icmpv6_key->icmpv6_type == NDISC_NEIGHBOUR_SOLICITATION ||
                            icmpv6_key->icmpv6_type == NDISC_NEIGHBOUR_ADVERTISEMENT) {
@@ -1484,10 +1790,10 @@ int ovs_flow_to_nlattrs(const struct sw_flow_key *swkey, struct sk_buff *skb)
                                if (!nla)
                                        goto nla_put_failure;
                                nd_key = nla_data(nla);
-                               memcpy(nd_key->nd_target, &swkey->ipv6.nd.target,
+                               memcpy(nd_key->nd_target, &output->ipv6.nd.target,
                                                        sizeof(nd_key->nd_target));
-                               memcpy(nd_key->nd_sll, swkey->ipv6.nd.sll, ETH_ALEN);
-                               memcpy(nd_key->nd_tll, swkey->ipv6.nd.tll, ETH_ALEN);
+                               memcpy(nd_key->nd_sll, output->ipv6.nd.sll, ETH_ALEN);
+                               memcpy(nd_key->nd_tll, output->ipv6.nd.tll, ETH_ALEN);
                        }
                }
        }
@@ -1519,3 +1825,91 @@ void ovs_flow_exit(void)
 {
        kmem_cache_destroy(flow_cache);
 }
+
+struct sw_flow_mask *ovs_sw_flow_mask_alloc(void)
+{
+       struct sw_flow_mask *mask;
+
+       mask = kmalloc(sizeof(*mask), GFP_KERNEL);
+       if (mask)
+               mask->ref_count = 0;
+
+       return mask;
+}
+
+void ovs_sw_flow_mask_add_ref(struct sw_flow_mask *mask)
+{
+       mask->ref_count++;
+}
+
+static void rcu_free_sw_flow_mask_cb(struct rcu_head *rcu)
+{
+       struct sw_flow_mask *mask = container_of(rcu, struct sw_flow_mask, rcu);
+
+       kfree(mask);
+}
+
+void ovs_sw_flow_mask_del_ref(struct sw_flow_mask *mask, bool deferred)
+{
+       if (!mask)
+               return;
+
+       BUG_ON(!mask->ref_count);
+       mask->ref_count--;
+
+       if (!mask->ref_count) {
+               list_del_rcu(&mask->list);
+               if (deferred)
+                       call_rcu(&mask->rcu, rcu_free_sw_flow_mask_cb);
+               else
+                       kfree(mask);
+       }
+}
+
+static bool ovs_sw_flow_mask_equal(const struct sw_flow_mask *a,
+               const struct sw_flow_mask *b)
+{
+       u8 *a_ = (u8 *)&a->key + a->range.start;
+       u8 *b_ = (u8 *)&b->key + b->range.start;
+
+       return  (a->range.end == b->range.end)
+               && (a->range.start == b->range.start)
+               && (memcmp(a_, b_, ovs_sw_flow_mask_actual_size(a)) == 0);
+}
+
+struct sw_flow_mask *ovs_sw_flow_mask_find(const struct flow_table *tbl,
+                                           const struct sw_flow_mask *mask)
+{
+       struct list_head *ml;
+
+       list_for_each(ml, tbl->mask_list) {
+               struct sw_flow_mask *m;
+               m = container_of(ml, struct sw_flow_mask, list);
+               if (ovs_sw_flow_mask_equal(mask, m))
+                       return m;
+       }
+
+       return NULL;
+}
+
+/**
+ * add a new mask into the mask list.
+ * The caller needs to make sure that 'mask' is not the same
+ * as any masks that are already on the list.
+ */
+void ovs_sw_flow_mask_insert(struct flow_table *tbl, struct sw_flow_mask *mask)
+{
+       list_add_rcu(&mask->list, tbl->mask_list);
+}
+
+/**
+ * Set 'range' fields in the mask to the value of 'val'.
+ */
+static void ovs_sw_flow_mask_set(struct sw_flow_mask *mask,
+               struct sw_flow_key_range *range, u8 val)
+{
+       u8 *m = (u8 *)&mask->key + range->start;
+
+       mask->range = *range;
+       memset(m, val, ovs_sw_flow_mask_size_roundup(mask));
+}
index dba66cf..a31dab0 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2011 Nicira, Inc.
+ * Copyright (c) 2007-2013 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
 #include <linux/jiffies.h>
 #include <linux/time.h>
 #include <linux/flex_array.h>
+
 #include <net/inet_ecn.h>
+#include <net/ip_tunnels.h>
 
 struct sk_buff;
+struct sw_flow_mask;
+struct flow_table;
 
 struct sw_flow_actions {
        struct rcu_head rcu;
@@ -40,11 +44,6 @@ struct sw_flow_actions {
        struct nlattr actions[];
 };
 
-/* Tunnel flow flags. */
-#define OVS_TNL_F_DONT_FRAGMENT                (1 << 0)
-#define OVS_TNL_F_CSUM                 (1 << 1)
-#define OVS_TNL_F_KEY                  (1 << 2)
-
 /* Used to memset ovs_key_ipv4_tunnel padding. */
 #define OVS_TUNNEL_KEY_SIZE                                    \
         (offsetof(struct ovs_key_ipv4_tunnel, ipv4_ttl) +      \
@@ -54,7 +53,7 @@ struct ovs_key_ipv4_tunnel {
        __be64 tun_id;
        __be32 ipv4_src;
        __be32 ipv4_dst;
-       u16  tun_flags;
+       __be16 tun_flags;
        u8   ipv4_tos;
        u8   ipv4_ttl;
 };
@@ -120,6 +119,8 @@ struct sw_flow {
        u32 hash;
 
        struct sw_flow_key key;
+       struct sw_flow_key unmasked_key;
+       struct sw_flow_mask __rcu *mask;
        struct sw_flow_actions __rcu *sf_acts;
 
        spinlock_t lock;        /* Lock for values below. */
@@ -129,6 +130,25 @@ struct sw_flow {
        u8 tcp_flags;           /* Union of seen TCP flags. */
 };
 
+struct sw_flow_key_range {
+       size_t start;
+       size_t end;
+};
+
+static inline u16 ovs_sw_flow_key_range_actual_size(const struct sw_flow_key_range *range)
+{
+       return range->end - range->start;
+}
+
+struct sw_flow_match {
+       struct sw_flow_key *key;
+       struct sw_flow_key_range range;
+       struct sw_flow_mask *mask;
+};
+
+void ovs_match_init(struct sw_flow_match *match,
+               struct sw_flow_key *key, struct sw_flow_mask *mask);
+
 struct arp_eth_header {
        __be16      ar_hrd;     /* format of hardware address   */
        __be16      ar_pro;     /* format of protocol address   */
@@ -148,21 +168,21 @@ 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 *);
+void ovs_flow_free(struct sw_flow *, bool deferred);
 
 struct sw_flow_actions *ovs_flow_actions_alloc(int actions_len);
 void ovs_flow_deferred_free_acts(struct sw_flow_actions *);
 
-int ovs_flow_extract(struct sk_buff *, u16 in_port, struct sw_flow_key *,
-                    int *key_lenp);
+int ovs_flow_extract(struct sk_buff *, u16 in_port, struct sw_flow_key *);
 void ovs_flow_used(struct sw_flow *, struct sk_buff *);
 u64 ovs_flow_used_time(unsigned long flow_jiffies);
-
-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,
+int ovs_flow_to_nlattrs(const struct sw_flow_key *,
+               const struct sw_flow_key *, struct sk_buff *);
+int ovs_match_from_nlattrs(struct sw_flow_match *match,
+                     const struct nlattr *,
                      const struct nlattr *);
-int ovs_flow_metadata_from_nlattrs(struct sw_flow *flow, int key_len,
-                                  const struct nlattr *attr);
+int ovs_flow_metadata_from_nlattrs(struct sw_flow *flow,
+               const struct nlattr *attr);
 
 #define MAX_ACTIONS_BUFSIZE    (32 * 1024)
 #define TBL_MIN_BUCKETS                1024
@@ -171,6 +191,7 @@ struct flow_table {
        struct flex_array *buckets;
        unsigned int count, n_buckets;
        struct rcu_head rcu;
+       struct list_head *mask_list;
        int node_ver;
        u32 hash_seed;
        bool keep_flows;
@@ -186,22 +207,55 @@ static inline int ovs_flow_tbl_need_to_expand(struct flow_table *table)
        return (table->count > table->n_buckets);
 }
 
-struct sw_flow *ovs_flow_tbl_lookup(struct flow_table *table,
-                                   struct sw_flow_key *key, int len);
-void ovs_flow_tbl_destroy(struct flow_table *table);
-void ovs_flow_tbl_deferred_destroy(struct flow_table *table);
+struct sw_flow *ovs_flow_lookup(struct flow_table *,
+                               const struct sw_flow_key *);
+struct sw_flow *ovs_flow_lookup_unmasked_key(struct flow_table *table,
+                                   struct sw_flow_match *match);
+
+void ovs_flow_tbl_destroy(struct flow_table *table, bool deferred);
 struct flow_table *ovs_flow_tbl_alloc(int new_size);
 struct flow_table *ovs_flow_tbl_expand(struct flow_table *table);
 struct flow_table *ovs_flow_tbl_rehash(struct flow_table *table);
-void ovs_flow_tbl_insert(struct flow_table *table, struct sw_flow *flow,
-                        struct sw_flow_key *key, int key_len);
-void ovs_flow_tbl_remove(struct flow_table *table, struct sw_flow *flow);
+void ovs_flow_insert(struct flow_table *table, struct sw_flow *flow,
+               const struct sw_flow_key *key, int key_len);
 
-struct sw_flow *ovs_flow_tbl_next(struct flow_table *table, u32 *bucket, u32 *idx);
+void ovs_flow_remove(struct flow_table *table, struct sw_flow *flow);
+
+struct sw_flow *ovs_flow_dump_next(struct flow_table *table, u32 *bucket, u32 *idx);
 extern const int ovs_key_lens[OVS_KEY_ATTR_MAX + 1];
 int ipv4_tun_from_nlattr(const struct nlattr *attr,
-                        struct ovs_key_ipv4_tunnel *tun_key);
+                        struct sw_flow_match *match, bool is_mask);
 int ipv4_tun_to_nlattr(struct sk_buff *skb,
-                       const struct ovs_key_ipv4_tunnel *tun_key);
+                       const struct ovs_key_ipv4_tunnel *tun_key,
+                       const struct ovs_key_ipv4_tunnel *output);
+
+bool ovs_flow_cmp_unmasked_key(const struct sw_flow *flow,
+               const struct sw_flow_key *key, int key_len);
+
+struct sw_flow_mask {
+       int ref_count;
+       struct rcu_head rcu;
+       struct list_head list;
+       struct sw_flow_key_range range;
+       struct sw_flow_key key;
+};
+
+static inline u16
+ovs_sw_flow_mask_actual_size(const struct sw_flow_mask *mask)
+{
+       return ovs_sw_flow_key_range_actual_size(&mask->range);
+}
+
+static inline u16
+ovs_sw_flow_mask_size_roundup(const struct sw_flow_mask *mask)
+{
+       return roundup(ovs_sw_flow_mask_actual_size(mask), sizeof(u32));
+}
 
+struct sw_flow_mask *ovs_sw_flow_mask_alloc(void);
+void ovs_sw_flow_mask_add_ref(struct sw_flow_mask *);
+void ovs_sw_flow_mask_del_ref(struct sw_flow_mask *, bool deferred);
+void ovs_sw_flow_mask_insert(struct flow_table *, struct sw_flow_mask *);
+struct sw_flow_mask *ovs_sw_flow_mask_find(const struct flow_table *,
+               const struct sw_flow_mask *);
 #endif /* flow.h */
index 16fbc8a..e7ac6c1 100644 (file)
 /flow.c
 /genetlink-openvswitch.c
 /genl_exec.c
+/gre.c
+/gso.c
 /ip_output-openvswitch.c
+/ip_tunnels_core.c
 /kcompat.h
 /kmemdup.c
 /loop_counter.c
index 2e445ac..88f144c 100644 (file)
@@ -71,6 +71,7 @@ default:
 
 modules_install:
        $(MAKE) -C $(KSRC) M=$(builddir) modules_install
+       depmod `sed -n 's/#define UTS_RELEASE "\([^"]*\)"/\1/p' $(KSRC)/include/generated/utsrelease.h`
 endif
 
 # Much of the kernel build system in this file is derived from Intel's
index 1434a2d..dcacc79 100644 (file)
@@ -3,8 +3,11 @@ openvswitch_sources += \
        linux/compat/dev-openvswitch.c \
        linux/compat/exthdrs_core.c \
        linux/compat/flex_array.c \
+       linux/compat/gre.c \
+       linux/compat/gso.c \
        linux/compat/genetlink-openvswitch.c \
        linux/compat/ip_output-openvswitch.c \
+       linux/compat/ip_tunnels_core.c \
        linux/compat/kmemdup.c \
        linux/compat/netdevice.c \
        linux/compat/net_namespace.c \
@@ -13,6 +16,7 @@ openvswitch_sources += \
        linux/compat/time.c     \
        linux/compat/workqueue.c
 openvswitch_headers += \
+       linux/compat/gso.h \
        linux/compat/include/asm/percpu.h \
        linux/compat/include/linux/compiler.h \
        linux/compat/include/linux/compiler-gcc.h \
@@ -33,6 +37,7 @@ openvswitch_headers += \
        linux/compat/include/linux/ip.h \
        linux/compat/include/linux/ipv6.h \
        linux/compat/include/linux/jiffies.h \
+       linux/compat/include/linux/kconfig.h \
        linux/compat/include/linux/kernel.h \
        linux/compat/include/linux/kobject.h \
        linux/compat/include/linux/list.h \
@@ -61,8 +66,10 @@ openvswitch_headers += \
        linux/compat/include/net/checksum.h \
        linux/compat/include/net/dst.h \
        linux/compat/include/net/genetlink.h \
+       linux/compat/include/net/gre.h \
        linux/compat/include/net/inet_frag.h \
        linux/compat/include/net/ip.h \
+       linux/compat/include/net/ip_tunnels.h \
        linux/compat/include/net/ipv6.h \
        linux/compat/include/net/net_namespace.h \
        linux/compat/include/net/netlink.h \
diff --git a/datapath/linux/compat/gre.c b/datapath/linux/compat/gre.c
new file mode 100644 (file)
index 0000000..582bd94
--- /dev/null
@@ -0,0 +1,357 @@
+/*
+ * Copyright (c) 2007-2013 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/kconfig.h>
+#if IS_ENABLED(CONFIG_NET_IPGRE_DEMUX)
+
+#include <linux/module.h>
+#include <linux/if.h>
+#include <linux/if_tunnel.h>
+#include <linux/icmp.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/kernel.h>
+#include <linux/kmod.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+
+#include <net/gre.h>
+#include <net/icmp.h>
+#include <net/protocol.h>
+#include <net/route.h>
+#include <net/xfrm.h>
+
+#include "gso.h"
+
+static struct gre_cisco_protocol __rcu *gre_cisco_proto;
+
+static void gre_csum_fix(struct sk_buff *skb)
+{
+       struct gre_base_hdr *greh;
+       __be32 *options;
+       int gre_offset = skb_transport_offset(skb);
+
+       greh = (struct gre_base_hdr *)skb_transport_header(skb);
+       options = ((__be32 *)greh + 1);
+
+       *options = 0;
+       *(__sum16 *)options = csum_fold(skb_checksum(skb, gre_offset,
+                                                    skb->len - gre_offset, 0));
+}
+
+struct sk_buff *gre_handle_offloads(struct sk_buff *skb, bool gre_csum)
+{
+       int err;
+
+       skb_reset_inner_headers(skb);
+
+       if (skb_is_gso(skb)) {
+               if (gre_csum)
+                       OVS_GSO_CB(skb)->fix_segment = gre_csum_fix;
+       } else {
+               if (skb->ip_summed == CHECKSUM_PARTIAL && gre_csum) {
+                       err = skb_checksum_help(skb);
+                       if (err)
+                               goto error;
+
+               } else if (skb->ip_summed != CHECKSUM_PARTIAL)
+                       skb->ip_summed = CHECKSUM_NONE;
+       }
+       return skb;
+error:
+       kfree_skb(skb);
+       return ERR_PTR(err);
+}
+
+static bool is_gre_gso(struct sk_buff *skb)
+{
+       return skb_is_gso(skb);
+}
+
+void gre_build_header(struct sk_buff *skb, const struct tnl_ptk_info *tpi,
+                     int hdr_len)
+{
+       struct gre_base_hdr *greh;
+
+       __skb_push(skb, hdr_len);
+
+       greh = (struct gre_base_hdr *)skb->data;
+       greh->flags = tnl_flags_to_gre_flags(tpi->flags);
+       greh->protocol = tpi->proto;
+
+       if (tpi->flags & (TUNNEL_KEY | TUNNEL_CSUM | TUNNEL_SEQ)) {
+               __be32 *ptr = (__be32 *)(((u8 *)greh) + hdr_len - 4);
+
+               if (tpi->flags & TUNNEL_SEQ) {
+                       *ptr = tpi->seq;
+                       ptr--;
+               }
+               if (tpi->flags & TUNNEL_KEY) {
+                       *ptr = tpi->key;
+                       ptr--;
+               }
+               if (tpi->flags & TUNNEL_CSUM && !is_gre_gso(skb)) {
+                       *ptr = 0;
+                       *(__sum16 *)ptr = csum_fold(skb_checksum(skb, 0,
+                                               skb->len, 0));
+               }
+       }
+}
+
+static __sum16 check_checksum(struct sk_buff *skb)
+{
+       __sum16 csum = 0;
+
+       switch (skb->ip_summed) {
+       case CHECKSUM_COMPLETE:
+               csum = csum_fold(skb->csum);
+
+               if (!csum)
+                       break;
+               /* Fall through. */
+
+       case CHECKSUM_NONE:
+               skb->csum = 0;
+               csum = __skb_checksum_complete(skb);
+               skb->ip_summed = CHECKSUM_COMPLETE;
+               break;
+       }
+
+       return csum;
+}
+
+static int parse_gre_header(struct sk_buff *skb, struct tnl_ptk_info *tpi,
+                           bool *csum_err)
+{
+       unsigned int ip_hlen = ip_hdrlen(skb);
+       struct gre_base_hdr *greh;
+       __be32 *options;
+       int hdr_len;
+
+       if (unlikely(!pskb_may_pull(skb, sizeof(struct gre_base_hdr))))
+               return -EINVAL;
+
+       greh = (struct gre_base_hdr *)(skb_network_header(skb) + ip_hlen);
+       if (unlikely(greh->flags & (GRE_VERSION | GRE_ROUTING)))
+               return -EINVAL;
+
+       tpi->flags = gre_flags_to_tnl_flags(greh->flags);
+       hdr_len = ip_gre_calc_hlen(tpi->flags);
+
+       if (!pskb_may_pull(skb, hdr_len))
+               return -EINVAL;
+
+       greh = (struct gre_base_hdr *)(skb_network_header(skb) + ip_hlen);
+       tpi->proto = greh->protocol;
+
+       options = (__be32 *)(greh + 1);
+       if (greh->flags & GRE_CSUM) {
+               if (check_checksum(skb)) {
+                       *csum_err = true;
+                       return -EINVAL;
+               }
+               options++;
+       }
+
+       if (greh->flags & GRE_KEY) {
+               tpi->key = *options;
+               options++;
+       } else
+               tpi->key = 0;
+
+       if (unlikely(greh->flags & GRE_SEQ)) {
+               tpi->seq = *options;
+               options++;
+       } else
+               tpi->seq = 0;
+
+       /* WCCP version 1 and 2 protocol decoding.
+        * - Change protocol to IP
+        * - When dealing with WCCPv2, Skip extra 4 bytes in GRE header
+        */
+       if (greh->flags == 0 && tpi->proto == htons(ETH_P_WCCP)) {
+               tpi->proto = htons(ETH_P_IP);
+               if ((*(u8 *)options & 0xF0) != 0x40) {
+                       hdr_len += 4;
+                       if (!pskb_may_pull(skb, hdr_len))
+                               return -EINVAL;
+               }
+       }
+
+       return iptunnel_pull_header(skb, hdr_len, tpi->proto);
+}
+
+static int gre_cisco_rcv(struct sk_buff *skb)
+{
+       struct tnl_ptk_info tpi;
+       bool csum_err = false;
+       struct gre_cisco_protocol *proto;
+
+       rcu_read_lock();
+       proto = rcu_dereference(gre_cisco_proto);
+       if (!proto)
+               goto drop;
+
+       if (parse_gre_header(skb, &tpi, &csum_err) < 0)
+               goto drop;
+       proto->handler(skb, &tpi);
+       rcu_read_unlock();
+       return 0;
+
+drop:
+       rcu_read_unlock();
+       kfree_skb(skb);
+       return 0;
+}
+
+static const struct gre_protocol ipgre_protocol = {
+       .handler        =       gre_cisco_rcv,
+};
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)
+static const struct gre_protocol __rcu *gre_proto[GREPROTO_MAX] __read_mostly;
+
+int gre_add_protocol(const struct gre_protocol *proto, u8 version)
+{
+       if (version >= GREPROTO_MAX)
+               return -EINVAL;
+
+       return (cmpxchg((const struct gre_protocol **)&gre_proto[version], NULL, proto) == NULL) ?
+               0 : -EBUSY;
+}
+
+int gre_del_protocol(const struct gre_protocol *proto, u8 version)
+{
+       int ret;
+
+       if (version >= GREPROTO_MAX)
+               return -EINVAL;
+
+       ret = (cmpxchg((const struct gre_protocol **)&gre_proto[version], proto, NULL) == proto) ?
+               0 : -EBUSY;
+
+       if (ret)
+               return ret;
+
+       synchronize_net();
+       return 0;
+}
+
+static int gre_rcv(struct sk_buff *skb)
+{
+       const struct gre_protocol *proto;
+       u8 ver;
+       int ret;
+
+       if (!pskb_may_pull(skb, 12))
+               goto drop;
+
+       ver = skb->data[1] & 0x7f;
+       if (ver >= GREPROTO_MAX)
+               goto drop;
+
+       rcu_read_lock();
+       proto = rcu_dereference(gre_proto[ver]);
+       if (!proto || !proto->handler)
+               goto drop_unlock;
+       ret = proto->handler(skb);
+       rcu_read_unlock();
+       return ret;
+
+drop_unlock:
+       rcu_read_unlock();
+drop:
+       kfree_skb(skb);
+       return NET_RX_DROP;
+}
+
+static const struct net_protocol net_gre_protocol = {
+       .handler     = gre_rcv,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)
+       .netns_ok    = 1,
+#endif
+};
+#endif
+
+static int gre_compat_init(void)
+{
+       int err;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)
+       if (inet_add_protocol(&net_gre_protocol, IPPROTO_GRE) < 0) {
+               pr_err("%s: cannot register gre protocol handler\n", __func__);
+               return -EAGAIN;
+       }
+#endif
+       err = gre_add_protocol(&ipgre_protocol, GREPROTO_CISCO);
+       if (err) {
+               pr_warn("%s: cannot register gre_cisco protocol handler\n", __func__);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)
+               inet_del_protocol(&net_gre_protocol, IPPROTO_GRE);
+#endif
+       }
+
+       return err;
+}
+
+static int gre_compat_exit(void)
+{
+       int ret;
+
+       ret = gre_del_protocol(&ipgre_protocol, GREPROTO_CISCO);
+       if (ret)
+               return ret;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)
+       ret = inet_del_protocol(&net_gre_protocol, IPPROTO_GRE);
+       if (ret)
+               return ret;
+#endif
+       return 0;
+}
+
+int gre_cisco_register(struct gre_cisco_protocol *newp)
+{
+       int err;
+
+       err = gre_compat_init();
+       if (err)
+               return err;
+
+       return (cmpxchg((struct gre_cisco_protocol **)&gre_cisco_proto, NULL, newp) == NULL) ?
+               0 : -EBUSY;
+}
+
+int gre_cisco_unregister(struct gre_cisco_protocol *proto)
+{
+       int ret;
+
+       ret = (cmpxchg((struct gre_cisco_protocol **)&gre_cisco_proto, proto, NULL) == proto) ?
+               0 : -EINVAL;
+
+       if (ret)
+               return ret;
+
+       synchronize_net();
+       ret = gre_compat_exit();
+       return ret;
+}
+
+#endif /* CONFIG_NET_IPGRE_DEMUX */
diff --git a/datapath/linux/compat/gso.c b/datapath/linux/compat/gso.c
new file mode 100644 (file)
index 0000000..3cadde9
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2007-2013 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/module.h>
+#include <linux/if.h>
+#include <linux/if_tunnel.h>
+#include <linux/icmp.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/kernel.h>
+#include <linux/kmod.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+
+#include <net/gre.h>
+#include <net/icmp.h>
+#include <net/protocol.h>
+#include <net/route.h>
+#include <net/xfrm.h>
+
+#include "gso.h"
+
+static __be16 skb_network_protocol(struct sk_buff *skb)
+{
+       __be16 type = skb->protocol;
+       int vlan_depth = ETH_HLEN;
+
+       while (type == htons(ETH_P_8021Q) || type == htons(ETH_P_8021AD)) {
+               struct vlan_hdr *vh;
+
+               if (unlikely(!pskb_may_pull(skb, vlan_depth + VLAN_HLEN)))
+                       return 0;
+
+               vh = (struct vlan_hdr *)(skb->data + vlan_depth);
+               type = vh->h_vlan_encapsulated_proto;
+               vlan_depth += VLAN_HLEN;
+       }
+
+       return type;
+}
+
+static struct sk_buff *tnl_skb_gso_segment(struct sk_buff *skb,
+                                          netdev_features_t features,
+                                          bool tx_path)
+{
+       struct iphdr *iph = ip_hdr(skb);
+       int pkt_hlen = skb_inner_network_offset(skb); /* inner l2 + tunnel hdr. */
+       int mac_offset = skb_inner_mac_offset(skb);
+       struct sk_buff *skb1 = skb;
+       struct sk_buff *segs;
+       __be16 proto = skb->protocol;
+
+       /* setup whole inner packet to get protocol. */
+       __skb_pull(skb, mac_offset);
+       skb->protocol = skb_network_protocol(skb);
+
+       /* setup l3 packet to gso, to get around segmentation bug on older kernel.*/
+       __skb_pull(skb, (pkt_hlen - mac_offset));
+       skb_reset_mac_header(skb);
+       skb_reset_network_header(skb);
+       skb_reset_transport_header(skb);
+
+       segs = __skb_gso_segment(skb, 0, tx_path);
+       if (!segs || IS_ERR(segs))
+               goto free;
+
+       skb = segs;
+       while (skb) {
+               __skb_push(skb, pkt_hlen);
+               skb_reset_mac_header(skb);
+               skb_reset_network_header(skb);
+               skb_set_transport_header(skb, sizeof(struct iphdr));
+               skb->mac_len = 0;
+
+               memcpy(ip_hdr(skb), iph, pkt_hlen);
+               if (OVS_GSO_CB(skb)->fix_segment)
+                       OVS_GSO_CB(skb)->fix_segment(skb);
+
+               skb->protocol = proto;
+               skb = skb->next;
+       }
+free:
+       consume_skb(skb1);
+       return segs;
+}
+
+int rpl_ip_local_out(struct sk_buff *skb)
+{
+       int ret = NETDEV_TX_OK;
+       int id = -1;
+
+       if (skb_is_gso(skb)) {
+               struct iphdr *iph;
+
+               iph = ip_hdr(skb);
+               id = ntohs(iph->id);
+               skb = tnl_skb_gso_segment(skb, 0, false);
+               if (!skb || IS_ERR(skb))
+                       return 0;
+       }  else if (skb->ip_summed == CHECKSUM_PARTIAL) {
+               int err;
+
+               err = skb_checksum_help(skb);
+               if (unlikely(err))
+                       return 0;
+       }
+
+       while (skb) {
+               struct sk_buff *next_skb = skb->next;
+               struct iphdr *iph;
+               int err;
+
+               skb->next = NULL;
+
+               iph = ip_hdr(skb);
+               if (id >= 0)
+                       iph->id = htons(id++);
+
+               memset(IPCB(skb), 0, sizeof(*IPCB(skb)));
+
+#undef ip_local_out
+               err = ip_local_out(skb);
+               if (unlikely(net_xmit_eval(err)))
+                       ret = err;
+
+               skb = next_skb;
+       }
+       return ret;
+}
diff --git a/datapath/linux/compat/gso.h b/datapath/linux/compat/gso.h
new file mode 100644 (file)
index 0000000..44fd213
--- /dev/null
@@ -0,0 +1,72 @@
+#ifndef __LINUX_GSO_WRAPPER_H
+#define __LINUX_GSO_WRAPPER_H
+
+#include <linux/skbuff.h>
+#include <net/protocol.h>
+
+#include "datapath.h"
+
+struct ovs_gso_cb {
+       struct ovs_skb_cb dp_cb;
+       sk_buff_data_t  inner_network_header;
+       sk_buff_data_t  inner_mac_header;
+       void (*fix_segment)(struct sk_buff *);
+};
+#define OVS_GSO_CB(skb) ((struct ovs_gso_cb *)(skb)->cb)
+
+#define skb_inner_network_header rpl_skb_inner_network_header
+
+#ifdef NET_SKBUFF_DATA_USES_OFFSET
+#define skb_inner_network_header rpl_skb_inner_network_header
+static inline unsigned char *skb_inner_network_header(const struct sk_buff *skb)
+{
+       return skb->head + OVS_GSO_CB(skb)->inner_network_header;
+}
+
+#define skb_inner_mac_header rpl_skb_inner_mac_header
+static inline unsigned char *skb_inner_mac_header(const struct sk_buff *skb)
+{
+       return skb->head + OVS_GSO_CB(skb)->inner_mac_header;
+}
+
+#else
+
+#define skb_inner_network_header rpl_skb_inner_network_header
+static inline unsigned char *skb_inner_network_header(const struct sk_buff *skb)
+{
+       return OVS_GSO_CB(skb)->inner_network_header;
+}
+
+#define skb_inner_mac_header rpl_skb_inner_mac_header
+static inline unsigned char *skb_inner_mac_header(const struct sk_buff *skb)
+{
+       return OVS_GSO_CB(skb)->inner_mac_header;
+}
+
+#endif
+
+#define skb_inner_network_offset rpl_skb_inner_network_offset
+static inline int skb_inner_network_offset(const struct sk_buff *skb)
+{
+       return skb_inner_network_header(skb) - skb->data;
+}
+
+#define skb_inner_mac_offset rpl_skb_inner_mac_offset
+static inline int skb_inner_mac_offset(const struct sk_buff *skb)
+{
+       return skb_inner_mac_header(skb) - skb->data;
+}
+
+#define skb_reset_inner_headers rpl_skb_reset_inner_headers
+static inline void skb_reset_inner_headers(struct sk_buff *skb)
+{
+       BUILD_BUG_ON(sizeof(struct ovs_gso_cb) > FIELD_SIZEOF(struct sk_buff, cb));
+       OVS_GSO_CB(skb)->inner_network_header = skb->network_header;
+       OVS_GSO_CB(skb)->inner_mac_header = skb->mac_header;
+
+       OVS_GSO_CB(skb)->fix_segment = NULL;
+}
+
+#define ip_local_out rpl_ip_local_out
+int ip_local_out(struct sk_buff *skb);
+#endif
index 85b0d22..e22ea96 100644 (file)
@@ -20,4 +20,8 @@
 #define ETH_P_802_3_MIN        0x0600
 #endif
 
+#ifndef ETH_P_8021AD
+#define ETH_P_8021AD    0x88A8          /* 802.1ad Service VLAN         */
+#endif
+
 #endif
diff --git a/datapath/linux/compat/include/linux/kconfig.h b/datapath/linux/compat/include/linux/kconfig.h
new file mode 100644 (file)
index 0000000..5717a26
--- /dev/null
@@ -0,0 +1,57 @@
+#ifndef __LINUX_KCONFIG_WRAPPER_H
+#define __LINUX_KCONFIG_WRAPPER_H
+
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)
+#define CONFIG_NET_IPGRE_DEMUX 1
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,1,0)
+#include_next <linux/kconfig.h>
+#endif
+
+#ifndef IS_ENABLED
+
+/*
+ * Helper macros to use CONFIG_ options in C/CPP expressions. Note that
+ * these only work with boolean and tristate options.
+ */
+
+/*
+ * Getting something that works in C and CPP for an arg that may or may
+ * not be defined is tricky.  Here, if we have "#define CONFIG_BOOGER 1"
+ * we match on the placeholder define, insert the "0," for arg1 and generate
+ * the triplet (0, 1, 0).  Then the last step cherry picks the 2nd arg (a one).
+ * When CONFIG_BOOGER is not defined, we generate a (... 1, 0) pair, and when
+ * the last step cherry picks the 2nd arg, we get a zero.
+ */
+#define __ARG_PLACEHOLDER_1 0,
+#define config_enabled(cfg) _config_enabled(cfg)
+#define _config_enabled(value) __config_enabled(__ARG_PLACEHOLDER_##value)
+#define __config_enabled(arg1_or_junk) ___config_enabled(arg1_or_junk 1, 0)
+#define ___config_enabled(__ignored, val, ...) val
+
+/*
+ * IS_ENABLED(CONFIG_FOO) evaluates to 1 if CONFIG_FOO is set to 'y' or 'm',
+ * 0 otherwise.
+ *
+ */
+#define IS_ENABLED(option) \
+       (config_enabled(option) || config_enabled(option##_MODULE))
+
+/*
+ * IS_BUILTIN(CONFIG_FOO) evaluates to 1 if CONFIG_FOO is set to 'y', 0
+ * otherwise. For boolean options, this is equivalent to
+ * IS_ENABLED(CONFIG_FOO).
+ */
+#define IS_BUILTIN(option) config_enabled(option)
+
+/*
+ * IS_MODULE(CONFIG_FOO) evaluates to 1 if CONFIG_FOO is set to 'm', 0
+ * otherwise.
+ */
+#define IS_MODULE(option) config_enabled(option##_MODULE)
+
+#endif /* IS_ENABLED */
+#endif /* __LINUX_KCONFIG_WRAPER_H */
index f8240df..f2ced69 100644 (file)
@@ -77,8 +77,11 @@ extern void unregister_netdevice_many(struct list_head *head);
 extern void dev_disable_lro(struct net_device *dev);
 #endif
 
+#define skb_checksum_help rpl_skb_checksum_help
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
-#define skb_checksum_help(skb) skb_checksum_help((skb), 0)
+extern int skb_checksum_help(struct sk_buff *skb, int);
+#else
+extern int skb_checksum_help(struct sk_buff *skb);
 #endif
 
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36)
@@ -166,5 +169,4 @@ static inline struct sk_buff *__skb_gso_segment(struct sk_buff *skb,
        return skb_gso_segment(skb, features);
 }
 #endif
-
 #endif
diff --git a/datapath/linux/compat/include/net/gre.h b/datapath/linux/compat/include/net/gre.h
new file mode 100644 (file)
index 0000000..139e4ab
--- /dev/null
@@ -0,0 +1,102 @@
+#ifndef __LINUX_GRE_WRAPPER_H
+#define __LINUX_GRE_WRAPPER_H
+
+#include <linux/skbuff.h>
+#include <net/ip_tunnels.h>
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,37)
+#include_next <net/gre.h>
+
+#else /* LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,37) */
+
+#define GREPROTO_CISCO         0
+#define GREPROTO_MAX           2
+
+struct gre_protocol {
+       int  (*handler)(struct sk_buff *skb);
+};
+
+int gre_add_protocol(const struct gre_protocol *proto, u8 version);
+int gre_del_protocol(const struct gre_protocol *proto, u8 version);
+
+#endif
+
+struct gre_base_hdr {
+       __be16 flags;
+       __be16 protocol;
+};
+#define GRE_HEADER_SECTION 4
+
+#define MAX_GRE_PROTO_PRIORITY 255
+struct gre_cisco_protocol {
+       int (*handler)(struct sk_buff *skb, const struct tnl_ptk_info *tpi);
+       u8 priority;
+};
+
+#define gre_build_header rpl_gre_build_header
+void gre_build_header(struct sk_buff *skb, const struct tnl_ptk_info *tpi,
+                     int hdr_len);
+
+#define gre_handle_offloads rpl_gre_handle_offloads
+struct sk_buff *gre_handle_offloads(struct sk_buff *skb, bool gre_csum);
+
+int gre_cisco_register(struct gre_cisco_protocol *proto);
+int gre_cisco_unregister(struct gre_cisco_protocol *proto);
+
+static inline int ip_gre_calc_hlen(__be16 o_flags)
+{
+       int addend = 4;
+
+       if (o_flags & TUNNEL_CSUM)
+               addend += 4;
+       if (o_flags & TUNNEL_KEY)
+               addend += 4;
+       if (o_flags & TUNNEL_SEQ)
+               addend += 4;
+       return addend;
+}
+
+static inline __be16 gre_flags_to_tnl_flags(__be16 flags)
+{
+       __be16 tflags = 0;
+
+       if (flags & GRE_CSUM)
+               tflags |= TUNNEL_CSUM;
+       if (flags & GRE_ROUTING)
+               tflags |= TUNNEL_ROUTING;
+       if (flags & GRE_KEY)
+               tflags |= TUNNEL_KEY;
+       if (flags & GRE_SEQ)
+               tflags |= TUNNEL_SEQ;
+       if (flags & GRE_STRICT)
+               tflags |= TUNNEL_STRICT;
+       if (flags & GRE_REC)
+               tflags |= TUNNEL_REC;
+       if (flags & GRE_VERSION)
+               tflags |= TUNNEL_VERSION;
+
+       return tflags;
+}
+
+static inline __be16 tnl_flags_to_gre_flags(__be16 tflags)
+{
+       __be16 flags = 0;
+
+       if (tflags & TUNNEL_CSUM)
+               flags |= GRE_CSUM;
+       if (tflags & TUNNEL_ROUTING)
+               flags |= GRE_ROUTING;
+       if (tflags & TUNNEL_KEY)
+               flags |= GRE_KEY;
+       if (tflags & TUNNEL_SEQ)
+               flags |= GRE_SEQ;
+       if (tflags & TUNNEL_STRICT)
+               flags |= GRE_STRICT;
+       if (tflags & TUNNEL_REC)
+               flags |= GRE_REC;
+       if (tflags & TUNNEL_VERSION)
+               flags |= GRE_VERSION;
+
+       return flags;
+}
+#endif
diff --git a/datapath/linux/compat/include/net/ip_tunnels.h b/datapath/linux/compat/include/net/ip_tunnels.h
new file mode 100644 (file)
index 0000000..ad17c9d
--- /dev/null
@@ -0,0 +1,54 @@
+#ifndef __NET_IP_TUNNELS_WRAPPER_H
+#define __NET_IP_TUNNELS_WRAPPER_H 1
+
+#include <linux/if_tunnel.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/types.h>
+#include <net/dsfield.h>
+#include <net/flow.h>
+#include <net/inet_ecn.h>
+#include <net/ip.h>
+#include <net/rtnetlink.h>
+
+#define TUNNEL_CSUM    __cpu_to_be16(0x01)
+#define TUNNEL_ROUTING __cpu_to_be16(0x02)
+#define TUNNEL_KEY     __cpu_to_be16(0x04)
+#define TUNNEL_SEQ     __cpu_to_be16(0x08)
+#define TUNNEL_STRICT  __cpu_to_be16(0x10)
+#define TUNNEL_REC     __cpu_to_be16(0x20)
+#define TUNNEL_VERSION __cpu_to_be16(0x40)
+#define TUNNEL_NO_KEY  __cpu_to_be16(0x80)
+#define TUNNEL_DONT_FRAGMENT   __cpu_to_be16(0x0100)
+
+struct tnl_ptk_info {
+       __be16 flags;
+       __be16 proto;
+       __be32 key;
+       __be32 seq;
+};
+
+#define PACKET_RCVD    0
+#define PACKET_REJECT  1
+
+static inline void tunnel_ip_select_ident(struct sk_buff *skb,
+                                         const struct iphdr  *old_iph,
+                                         struct dst_entry *dst)
+{
+       struct iphdr *iph = ip_hdr(skb);
+
+       /* Use inner packet iph-id if possible. */
+       if (skb->protocol == htons(ETH_P_IP) && old_iph->id)
+               iph->id = old_iph->id;
+       else
+               __ip_select_ident(iph, dst,
+                               (skb_shinfo(skb)->gso_segs ?: 1) - 1);
+}
+
+int iptunnel_xmit(struct net *net, struct rtable *rt,
+                 struct sk_buff *skb,
+                 __be32 src, __be32 dst, __u8 proto,
+                 __u8 tos, __u8 ttl, __be16 df);
+
+int iptunnel_pull_header(struct sk_buff *skb, int hdr_len, __be16 inner_proto);
+#endif /* __NET_IP_TUNNELS_H */
diff --git a/datapath/linux/compat/ip_tunnels_core.c b/datapath/linux/compat/ip_tunnels_core.c
new file mode 100644 (file)
index 0000000..03c47a2
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2007-2013 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/in.h>
+#include <linux/in_route.h>
+#include <linux/inetdevice.h>
+#include <linux/jhash.h>
+#include <linux/list.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/workqueue.h>
+#include <linux/rculist.h>
+#include <net/ip_tunnels.h>
+#include <net/route.h>
+#include <net/xfrm.h>
+
+#include "checksum.h"
+#include "compat.h"
+#include "gso.h"
+
+int iptunnel_xmit(struct net *net, struct rtable *rt,
+                 struct sk_buff *skb,
+                 __be32 src, __be32 dst, __u8 proto,
+                 __u8 tos, __u8 ttl, __be16 df)
+{
+       int pkt_len = skb->len;
+       struct iphdr *iph;
+       int err;
+
+       nf_reset(skb);
+       secpath_reset(skb);
+       skb_clear_rxhash(skb);
+       skb_dst_drop(skb);
+       skb_dst_set(skb, &rt_dst(rt));
+#if 0
+       /* Do not clear ovs_skb_cb.  It will be done in gso code. */
+       memset(IPCB(skb), 0, sizeof(*IPCB(skb)));
+#endif
+
+       /* Push down and install the IP header. */
+       __skb_push(skb, sizeof(struct iphdr));
+       skb_reset_network_header(skb);
+
+       iph = ip_hdr(skb);
+
+       iph->version    =       4;
+       iph->ihl        =       sizeof(struct iphdr) >> 2;
+       iph->frag_off   =       df;
+       iph->protocol   =       proto;
+       iph->tos        =       tos;
+       iph->daddr      =       dst;
+       iph->saddr      =       src;
+       iph->ttl        =       ttl;
+       tunnel_ip_select_ident(skb,
+                              (const struct iphdr *)skb_inner_network_header(skb),
+                              &rt_dst(rt));
+
+       err = ip_local_out(skb);
+       if (unlikely(net_xmit_eval(err)))
+               pkt_len = 0;
+       return pkt_len;
+}
+
+int iptunnel_pull_header(struct sk_buff *skb, int hdr_len, __be16 inner_proto)
+{
+       if (unlikely(!pskb_may_pull(skb, hdr_len)))
+               return -ENOMEM;
+
+       skb_pull_rcsum(skb, hdr_len);
+
+       if (inner_proto == htons(ETH_P_TEB)) {
+               struct ethhdr *eh;
+
+               if (unlikely(!pskb_may_pull(skb, ETH_HLEN)))
+                       return -ENOMEM;
+
+               eh = (struct ethhdr *)skb->data;
+
+               if (likely(ntohs(eh->h_proto) >= ETH_P_802_3_MIN))
+                       skb->protocol = eh->h_proto;
+               else
+                       skb->protocol = htons(ETH_P_802_2);
+
+       } else {
+               skb->protocol = inner_proto;
+       }
+
+       if (unlikely(compute_ip_summed(skb, false)))
+               return -EPROTO;
+
+       nf_reset(skb);
+       secpath_reset(skb);
+       skb_clear_rxhash(skb);
+       skb_dst_drop(skb);
+       vlan_set_tci(skb, 0);
+       skb_set_queue_mapping(skb, 0);
+       skb->pkt_type = PACKET_HOST;
+       return 0;
+}
index ef43ba9..2707ef0 100644 (file)
@@ -1,9 +1,11 @@
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+
 #if !defined(HAVE_SKB_WARN_LRO) && defined(NETIF_F_LRO)
 
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
-#include <linux/netdevice.h>
-
 void __skb_warn_lro_forwarding(const struct sk_buff *skb)
 {
        if (net_ratelimit())
@@ -12,3 +14,25 @@ void __skb_warn_lro_forwarding(const struct sk_buff *skb)
 }
 
 #endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
+int skb_checksum_help(struct sk_buff *skb, int inward)
+#else
+int skb_checksum_help(struct sk_buff *skb)
+#endif
+{
+       if (unlikely(skb_is_nonlinear(skb))) {
+               int err;
+
+               err = __skb_linearize(skb);
+               if (unlikely(err))
+                       return err;
+       }
+
+#undef skb_checksum_help
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
+       return skb_checksum_help(skb, 0);
+#else
+       return skb_checksum_help(skb);
+#endif
+}
index 8c93e18..9102786 100644 (file)
 #include "vlan.h"
 #include "vport.h"
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)
-#define rt_dst(rt) (rt->dst)
-#else
-#define rt_dst(rt) (rt->u.dst)
-#endif
-
 /**
  *     ovs_tnl_rcv - ingress point for generic tunnel code
  *
@@ -85,9 +79,9 @@ void ovs_tnl_rcv(struct vport *vport, struct sk_buff *skb,
        ovs_vport_receive(vport, skb, tun_key);
 }
 
-static struct rtable *find_route(struct net *net,
-               __be32 *saddr, __be32 daddr, u8 ipproto,
-               u8 tos, u32 skb_mark)
+struct rtable *find_route(struct net *net,
+                         __be32 *saddr, __be32 daddr, u8 ipproto,
+                         u8 tos, u32 skb_mark)
 {
        struct rtable *rt;
        /* Tunnel configuration keeps DSCP part of TOS bits, But Linux
@@ -289,7 +283,7 @@ int ovs_tnl_send(struct vport *vport, struct sk_buff *skb,
                iph->tos        = OVS_CB(skb)->tun_key->ipv4_tos;
                iph->ttl        = OVS_CB(skb)->tun_key->ipv4_ttl;
                iph->frag_off   = OVS_CB(skb)->tun_key->tun_flags &
-                                 OVS_TNL_F_DONT_FRAGMENT ?  htons(IP_DF) : 0;
+                                 TUNNEL_DONT_FRAGMENT ?  htons(IP_DF) : 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
index 89c4e16..17de7c4 100644 (file)
 #include "flow.h"
 #include "vport.h"
 
+struct rtable *find_route(struct net *net,
+                         __be32 *saddr, __be32 daddr, u8 ipproto,
+                         u8 tos, u32 skb_mark);
+
+u16 ovs_tnl_get_src_port(struct sk_buff *skb);
 
 int ovs_tnl_send(struct vport *vport, struct sk_buff *skb,
                 u8 ipproto, int tunnel_hlen,
@@ -35,10 +40,10 @@ int ovs_tnl_send(struct vport *vport, struct sk_buff *skb,
 
 void ovs_tnl_rcv(struct vport *vport, struct sk_buff *skb,
                 struct ovs_key_ipv4_tunnel *tun_key);
-u16 ovs_tnl_get_src_port(struct sk_buff *skb);
 
 static inline void tnl_tun_key_init(struct ovs_key_ipv4_tunnel *tun_key,
-                                   const struct iphdr *iph, __be64 tun_id, u32 tun_flags)
+                                        const struct iphdr *iph, __be64 tun_id,
+                                        __be16 tun_flags)
 {
        tun_key->tun_id = tun_id;
        tun_key->ipv4_src = iph->saddr;
@@ -52,4 +57,4 @@ static inline void tnl_tun_key_init(struct ovs_key_ipv4_tunnel *tun_key,
               sizeof(*tun_key) - OVS_TUNNEL_KEY_SIZE);
 }
 
-#endif /* tunnel.h */
+#endif /* TUNNEL_H */
index 5d3573b..46d0db3 100644 (file)
@@ -93,6 +93,11 @@ static inline int vlan_deaccel_tag(struct sk_buff *skb)
        if (unlikely(!skb))
                return -ENOMEM;
 
+       if (get_ip_summed(skb) == OVS_CSUM_COMPLETE)
+               skb->csum = csum_add(skb->csum,
+                                    csum_partial(skb->data + (2 * ETH_ALEN),
+                                                 VLAN_HLEN, 0));
+
        vlan_set_tci(skb, 0);
        return 0;
 }
index add17d9..c74f5fc 100644 (file)
@@ -16,6 +16,8 @@
  * 02110-1301, USA
  */
 
+#include <linux/kconfig.h>
+#if IS_ENABLED(CONFIG_NET_IPGRE_DEMUX)
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
 #include <linux/if.h>
 #include <linux/if_tunnel.h>
 #include <linux/if_vlan.h>
 #include <linux/in.h>
+#include <linux/if_vlan.h>
+#include <linux/in.h>
+#include <linux/in_route.h>
+#include <linux/inetdevice.h>
+#include <linux/jhash.h>
+#include <linux/list.h>
+#include <linux/kernel.h>
+#include <linux/workqueue.h>
+#include <linux/rculist.h>
+#include <net/route.h>
+#include <net/xfrm.h>
+
 
 #include <net/icmp.h>
 #include <net/ip.h>
+#include <net/ip_tunnels.h>
+#include <net/gre.h>
 #include <net/protocol.h>
 
 #include "datapath.h"
 #include "tunnel.h"
 #include "vport.h"
 
-/*
- * The GRE header is composed of a series of sections: a base and then a variable
- * number of options.
- */
-#define GRE_HEADER_SECTION 4
-
-struct gre_base_hdr {
-       __be16 flags;
-       __be16 protocol;
-};
-
-static int gre_hdr_len(const struct ovs_key_ipv4_tunnel *tun_key)
-{
-       int len = GRE_HEADER_SECTION;
-
-       if (tun_key->tun_flags & OVS_TNL_F_KEY)
-               len += GRE_HEADER_SECTION;
-       if (tun_key->tun_flags & OVS_TNL_F_CSUM)
-               len += GRE_HEADER_SECTION;
-       return len;
-}
-
-static int gre64_hdr_len(const struct ovs_key_ipv4_tunnel *tun_key)
-{
-       /* Set key for GRE64 tunnels, even when key if is zero. */
-       int len = GRE_HEADER_SECTION +          /* GRE Hdr */
-                 GRE_HEADER_SECTION +          /* GRE Key */
-                 GRE_HEADER_SECTION;           /* GRE SEQ */
-
-       if (tun_key->tun_flags & OVS_TNL_F_CSUM)
-               len += GRE_HEADER_SECTION;
-
-       return len;
-}
-
 /* Returns the least-significant 32 bits of a __be64. */
 static __be32 be64_get_low32(__be64 x)
 {
@@ -78,61 +59,30 @@ static __be32 be64_get_low32(__be64 x)
 #endif
 }
 
-static __be32 be64_get_high32(__be64 x)
+static __be16 filter_tnl_flags(__be16 flags)
 {
-#ifdef __BIG_ENDIAN
-       return (__force __be32)((__force u64)x >> 32);
-#else
-       return (__force __be32)x;
-#endif
+       return flags & (TUNNEL_CSUM | TUNNEL_KEY);
 }
 
-static void __gre_build_header(struct sk_buff *skb,
-                              int tunnel_hlen,
-                              bool is_gre64)
+static struct sk_buff *__build_header(struct sk_buff *skb,
+                                     int tunnel_hlen,
+                                     __be32 seq, __be16 gre64_flag)
 {
        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);
-       greh->protocol = htons(ETH_P_TEB);
-       greh->flags = 0;
-
-       /* Work backwards over the options so the checksum is last. */
-       if (tun_key->tun_flags & OVS_TNL_F_KEY || is_gre64) {
-               greh->flags |= GRE_KEY;
-               if (is_gre64) {
-                       /* Set higher 32 bits to seq. */
-                       *options = be64_get_high32(tun_key->tun_id);
-                       options--;
-                       greh->flags |= GRE_SEQ;
-               }
-               *options = be64_get_low32(tun_key->tun_id);
-               options--;
-       }
+       struct tnl_ptk_info tpi;
 
-       if (tun_key->tun_flags & OVS_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));
-       }
-}
+       skb = gre_handle_offloads(skb, !!(tun_key->tun_flags & TUNNEL_CSUM));
+       if (IS_ERR(skb))
+               return NULL;
 
-static void gre_build_header(const struct vport *vport,
-                            struct sk_buff *skb,
-                            int tunnel_hlen)
-{
-       __gre_build_header(skb, tunnel_hlen, false);
-}
+       tpi.flags = filter_tnl_flags(tun_key->tun_flags) | gre64_flag;
 
-static void gre64_build_header(const struct vport *vport,
-                              struct sk_buff *skb,
-                              int tunnel_hlen)
-{
-       __gre_build_header(skb, tunnel_hlen, true);
+       tpi.proto = htons(ETH_P_TEB);
+       tpi.key = be64_get_low32(tun_key->tun_id);
+       tpi.seq = seq;
+       gre_build_header(skb, &tpi, tunnel_hlen);
+
+       return skb;
 }
 
 static __be64 key_to_tunnel_id(__be32 key, __be32 seq)
@@ -144,149 +94,100 @@ static __be64 key_to_tunnel_id(__be32 key, __be32 seq)
 #endif
 }
 
-static int parse_header(struct iphdr *iph, __be16 *flags, __be64 *tun_id,
-                       bool *is_gre64)
-{
-       /* IP and ICMP protocol handlers check that the IHL is valid. */
-       struct gre_base_hdr *greh = (struct gre_base_hdr *)((u8 *)iph + (iph->ihl << 2));
-       __be32 *options = (__be32 *)(greh + 1);
-       int hdr_len;
-
-       *flags = greh->flags;
-
-       if (unlikely(greh->flags & (GRE_VERSION | GRE_ROUTING)))
-               return -EINVAL;
-
-       if (unlikely(greh->protocol != htons(ETH_P_TEB)))
-               return -EINVAL;
-
-       hdr_len = GRE_HEADER_SECTION;
-
-       if (greh->flags & GRE_CSUM) {
-               hdr_len += GRE_HEADER_SECTION;
-               options++;
-       }
-
-       if (greh->flags & GRE_KEY) {
-               __be32 seq;
-               __be32 gre_key;
-
-               gre_key = *options;
-               hdr_len += GRE_HEADER_SECTION;
-               options++;
-
-               if (greh->flags & GRE_SEQ) {
-                       seq = *options;
-                       *is_gre64 = true;
-               } else {
-                       seq = 0;
-                       *is_gre64 = false;
-               }
-               *tun_id = key_to_tunnel_id(gre_key, seq);
-       } else {
-               *tun_id = 0;
-               /* Ignore GRE seq if there is no key present. */
-               *is_gre64 = false;
-       }
-
-       if (greh->flags & GRE_SEQ)
-               hdr_len += GRE_HEADER_SECTION;
-
-       return hdr_len;
-}
-
-static bool check_checksum(struct sk_buff *skb)
-{
-       struct iphdr *iph = ip_hdr(skb);
-       struct gre_base_hdr *greh = (struct gre_base_hdr *)(iph + 1);
-       __sum16 csum = 0;
-
-       if (greh->flags & GRE_CSUM) {
-               switch (skb->ip_summed) {
-               case CHECKSUM_COMPLETE:
-                       csum = csum_fold(skb->csum);
-
-                       if (!csum)
-                               break;
-                       /* Fall through. */
-
-               case CHECKSUM_NONE:
-                       skb->csum = 0;
-                       csum = __skb_checksum_complete(skb);
-                       skb->ip_summed = CHECKSUM_COMPLETE;
-                       break;
-               }
-       }
-
-       return (csum == 0);
-}
-
-static u32 gre_flags_to_tunnel_flags(__be16 gre_flags, bool is_gre64)
-{
-       u32 tunnel_flags = 0;
-
-       if (gre_flags & GRE_KEY || is_gre64)
-               tunnel_flags = OVS_TNL_F_KEY;
-
-       if (gre_flags & GRE_CSUM)
-               tunnel_flags |= OVS_TNL_F_CSUM;
-
-       return tunnel_flags;
-}
-
 /* Called with rcu_read_lock and BH disabled. */
-static int gre_rcv(struct sk_buff *skb)
+static int gre_rcv(struct sk_buff *skb,
+                  const struct tnl_ptk_info *tpi)
 {
+       struct ovs_key_ipv4_tunnel tun_key;
        struct ovs_net *ovs_net;
        struct vport *vport;
-       int hdr_len;
-       struct iphdr *iph;
-       struct ovs_key_ipv4_tunnel tun_key;
-       __be16 gre_flags;
-       u32 tnl_flags;
        __be64 key;
-       bool is_gre64;
-
-       if (unlikely(!pskb_may_pull(skb, sizeof(struct gre_base_hdr) + ETH_HLEN)))
-               goto error;
-       if (unlikely(!check_checksum(skb)))
-               goto error;
-
-       hdr_len = parse_header(ip_hdr(skb), &gre_flags, &key, &is_gre64);
-       if (unlikely(hdr_len < 0))
-               goto error;
 
        ovs_net = net_generic(dev_net(skb->dev), ovs_net_id);
-       if (is_gre64)
+       if ((tpi->flags & TUNNEL_KEY) && (tpi->flags & TUNNEL_SEQ))
                vport = rcu_dereference(ovs_net->vport_net.gre64_vport);
        else
                vport = rcu_dereference(ovs_net->vport_net.gre_vport);
        if (unlikely(!vport))
-               goto error;
+               return PACKET_REJECT;
 
-       if (unlikely(!pskb_may_pull(skb, hdr_len + ETH_HLEN)))
+       key = key_to_tunnel_id(tpi->key, tpi->seq);
+       tnl_tun_key_init(&tun_key, ip_hdr(skb), key, filter_tnl_flags(tpi->flags));
+
+       ovs_vport_receive(vport, skb, &tun_key);
+       return PACKET_RCVD;
+}
+
+static int __send(struct vport *vport, struct sk_buff *skb,
+                 int tunnel_hlen,
+                 __be32 seq, __be16 gre64_flag)
+{
+       struct net *net = ovs_dp_get_net(vport->dp);
+       struct rtable *rt;
+       int min_headroom;
+       __be16 df;
+       __be32 saddr;
+       int err;
+
+       forward_ip_summed(skb, true);
+
+       /* Route lookup */
+       saddr = OVS_CB(skb)->tun_key->ipv4_src;
+       rt = find_route(ovs_dp_get_net(vport->dp),
+                       &saddr,
+                       OVS_CB(skb)->tun_key->ipv4_dst,
+                       IPPROTO_GRE,
+                       OVS_CB(skb)->tun_key->ipv4_tos,
+                       skb_get_mark(skb));
+       if (IS_ERR(rt)) {
+               err = PTR_ERR(rt);
                goto error;
+       }
 
-       iph = ip_hdr(skb);
-       tnl_flags = gre_flags_to_tunnel_flags(gre_flags, is_gre64);
-       tnl_tun_key_init(&tun_key, iph, key, tnl_flags);
+       min_headroom = LL_RESERVED_SPACE(rt_dst(rt).dev) + rt_dst(rt).header_len
+                       + tunnel_hlen + sizeof(struct iphdr)
+                       + (vlan_tx_tag_present(skb) ? VLAN_HLEN : 0);
+
+       if (skb_headroom(skb) < min_headroom || skb_header_cloned(skb)) {
+               int head_delta = SKB_DATA_ALIGN(min_headroom -
+                                               skb_headroom(skb) +
+                                               16);
+               err = pskb_expand_head(skb, max_t(int, head_delta, 0),
+                                       0, GFP_ATOMIC);
+               if (unlikely(err))
+                       goto err_free_rt;
+       }
 
-       __skb_pull(skb, hdr_len);
-       skb_postpull_rcsum(skb, skb_transport_header(skb), hdr_len + ETH_HLEN);
+       if (unlikely(vlan_deaccel_tag(skb))) {
+               err = -ENOMEM;
+               goto err_free_rt;
+       }
 
-       ovs_tnl_rcv(vport, skb, &tun_key);
-       return 0;
+       /* Push Tunnel header. */
+       skb = __build_header(skb, tunnel_hlen, seq, gre64_flag);
+       if (unlikely(!skb)) {
+               err = 0;
+               goto err_free_rt;
+       }
 
+       df = OVS_CB(skb)->tun_key->tun_flags & TUNNEL_DONT_FRAGMENT ?
+               htons(IP_DF) : 0;
+
+       skb->local_df = 1;
+
+       return iptunnel_xmit(net, rt, skb, saddr,
+                            OVS_CB(skb)->tun_key->ipv4_dst, IPPROTO_GRE,
+                            OVS_CB(skb)->tun_key->ipv4_tos,
+                            OVS_CB(skb)->tun_key->ipv4_ttl, df);
+err_free_rt:
+       ip_rt_put(rt);
 error:
-       kfree_skb(skb);
-       return 0;
+       return err;
 }
 
-static const struct net_protocol gre_protocol_handlers = {
-       .handler        =       gre_rcv,
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)
-       .netns_ok       =       1,
-#endif
+static struct gre_cisco_protocol gre_protocol = {
+       .handler        = gre_rcv,
+       .priority       = 1,
 };
 
 static int gre_ports;
@@ -298,7 +199,7 @@ static int gre_init(void)
        if (gre_ports > 1)
                return 0;
 
-       err = inet_add_protocol(&gre_protocol_handlers, IPPROTO_GRE);
+       err = gre_cisco_register(&gre_protocol);
        if (err)
                pr_warn("cannot register gre protocol handler\n");
 
@@ -311,7 +212,7 @@ static void gre_exit(void)
        if (gre_ports > 0)
                return;
 
-       inet_del_protocol(&gre_protocol_handlers, IPPROTO_GRE);
+       gre_cisco_unregister(&gre_protocol);
 }
 
 static const char *gre_get_name(const struct vport *vport)
@@ -361,15 +262,16 @@ static void gre_tnl_destroy(struct vport *vport)
        gre_exit();
 }
 
-static int gre_tnl_send(struct vport *vport, struct sk_buff *skb)
+static int gre_send(struct vport *vport, struct sk_buff *skb)
 {
        int hlen;
 
        if (unlikely(!OVS_CB(skb)->tun_key))
                return -EINVAL;
 
-       hlen = gre_hdr_len(OVS_CB(skb)->tun_key);
-       return ovs_tnl_send(vport, skb, IPPROTO_GRE, hlen, gre_build_header);
+       hlen = ip_gre_calc_hlen(OVS_CB(skb)->tun_key->tun_flags);
+
+       return __send(vport, skb, hlen, 0, 0);
 }
 
 const struct vport_ops ovs_gre_vport_ops = {
@@ -377,7 +279,7 @@ const struct vport_ops ovs_gre_vport_ops = {
        .create         = gre_create,
        .destroy        = gre_tnl_destroy,
        .get_name       = gre_get_name,
-       .send           = gre_tnl_send,
+       .send           = gre_send,
 };
 
 /* GRE64 vport. */
@@ -422,15 +324,28 @@ static void gre64_tnl_destroy(struct vport *vport)
        gre_exit();
 }
 
-static int gre64_tnl_send(struct vport *vport, struct sk_buff *skb)
+static __be32 be64_get_high32(__be64 x)
+{
+#ifdef __BIG_ENDIAN
+       return (__force __be32)((__force u64)x >> 32);
+#else
+       return (__force __be32)x;
+#endif
+}
+
+static int gre64_send(struct vport *vport, struct sk_buff *skb)
 {
        int hlen;
+       __be32 seq;
 
        if (unlikely(!OVS_CB(skb)->tun_key))
                return -EINVAL;
 
-       hlen = gre64_hdr_len(OVS_CB(skb)->tun_key);
-       return ovs_tnl_send(vport, skb, IPPROTO_GRE, hlen, gre64_build_header);
+       hlen = ip_gre_calc_hlen(OVS_CB(skb)->tun_key->tun_flags)
+              + GRE_HEADER_SECTION;
+
+       seq = be64_get_high32(OVS_CB(skb)->tun_key->tun_id);
+       return __send(vport, skb, hlen, seq, TUNNEL_SEQ);
 }
 
 const struct vport_ops ovs_gre64_vport_ops = {
@@ -438,5 +353,6 @@ const struct vport_ops ovs_gre64_vport_ops = {
        .create         = gre64_create,
        .destroy        = gre64_tnl_destroy,
        .get_name       = gre_get_name,
-       .send           = gre64_tnl_send,
+       .send           = gre64_send,
 };
+#endif
index 9a159cd..7f0b3c1 100644 (file)
@@ -281,6 +281,7 @@ static int internal_dev_recv(struct vport *vport, struct sk_buff *skb)
        skb->dev = netdev;
        skb->pkt_type = PACKET_HOST;
        skb->protocol = eth_type_trans(skb, netdev);
+       skb_postpull_rcsum(skb, eth_hdr(skb), ETH_HLEN);
        forward_ip_summed(skb, false);
 
        netif_rx(skb);
index 3d0a315..54c10ae 100644 (file)
@@ -218,7 +218,7 @@ static int lisp_rcv(struct sock *sk, struct sk_buff *skb)
 
        /* Save outer tunnel values */
        iph = ip_hdr(skb);
-       tnl_tun_key_init(&tun_key, iph, key, OVS_TNL_F_KEY);
+       tnl_tun_key_init(&tun_key, iph, key, TUNNEL_KEY);
 
        /* Drop non-IP inner packets */
        inner_iph = (struct iphdr *)(lisph + 1);
@@ -240,6 +240,8 @@ static int lisp_rcv(struct sock *sk, struct sk_buff *skb)
        ethh->h_source[0] = 0x02;
        ethh->h_proto = protocol;
 
+       ovs_skb_postpush_rcsum(skb, skb->data, ETH_HLEN);
+
        ovs_tnl_rcv(vport_from_priv(lisp_port), skb, &tun_key);
        goto out;
 
index 4e7342c..fe7e359 100644 (file)
@@ -256,11 +256,12 @@ static void netdev_port_receive(struct vport *vport, struct sk_buff *skb)
        if (unlikely(!skb))
                return;
 
-       skb_push(skb, ETH_HLEN);
-
        if (unlikely(compute_ip_summed(skb, false)))
                goto error;
 
+       skb_push(skb, ETH_HLEN);
+       ovs_skb_postpush_rcsum(skb, skb->data, ETH_HLEN);
+
        vlan_copy_skb_tci(skb);
 
        ovs_vport_receive(vport, skb, NULL);
index d140c3b..7ff51fd 100644 (file)
@@ -129,14 +129,13 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)
                     vxh->vx_vni & htonl(0xff)))
                goto error;
 
-       __skb_pull(skb, VXLAN_HLEN);
-       skb_postpull_rcsum(skb, skb_transport_header(skb), VXLAN_HLEN + ETH_HLEN);
+       skb_pull_rcsum(skb, VXLAN_HLEN);
 
        key = cpu_to_be64(ntohl(vxh->vx_vni) >> 8);
 
        /* Save outer tunnel values */
        iph = ip_hdr(skb);
-       tnl_tun_key_init(&tun_key, iph, key, OVS_TNL_F_KEY);
+       tnl_tun_key_init(&tun_key, iph, key, TUNNEL_KEY);
 
        ovs_tnl_rcv(vport_from_priv(vxlan_vport), skb, &tun_key);
        goto out;
index 93ab2b5..03b775d 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/if.h>
 #include <linux/if_vlan.h>
 #include <linux/jhash.h>
+#include <linux/kconfig.h>
 #include <linux/kernel.h>
 #include <linux/list.h>
 #include <linux/mutex.h>
 static const struct vport_ops *vport_ops_list[] = {
        &ovs_netdev_vport_ops,
        &ovs_internal_vport_ops,
+#if IS_ENABLED(CONFIG_NET_IPGRE_DEMUX)
        &ovs_gre_vport_ops,
        &ovs_gre64_vport_ops,
+#endif
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
        &ovs_vxlan_vport_ops,
        &ovs_lisp_vport_ops,
@@ -382,7 +385,7 @@ int ovs_vport_send(struct vport *vport, struct sk_buff *skb)
 {
        int sent = vport->ops->send(vport, skb);
 
-       if (likely(sent)) {
+       if (likely(sent > 0)) {
                struct pcpu_tstats *stats;
 
                stats = this_cpu_ptr(vport->percpu_stats);
@@ -407,7 +410,7 @@ int ovs_vport_send(struct vport *vport, struct sk_buff *skb)
  * @err_type: one of enum vport_err_type types to indicate the error type
  *
  * If using the vport generic stats layer indicate that an error of the given
- * type has occured.
+ * type has occurred.
  */
 void ovs_vport_record_error(struct vport *vport, enum vport_err_type err_type)
 {
index 8d262a3..995889c 100644 (file)
@@ -204,4 +204,10 @@ extern const struct vport_ops ovs_gre64_vport_ops;
 extern const struct vport_ops ovs_vxlan_vport_ops;
 extern const struct vport_ops ovs_lisp_vport_ops;
 
+static inline void ovs_skb_postpush_rcsum(struct sk_buff *skb,
+                                     const void *start, unsigned int len)
+{
+       if (skb->ip_summed == CHECKSUM_COMPLETE)
+               skb->csum = csum_add(skb->csum, csum_partial(start, len, 0));
+}
 #endif /* vport.h */
index e2c0454..f2a0d48 100644 (file)
@@ -2,7 +2,6 @@ usr/bin/ovs-appctl
 usr/bin/ovs-benchmark
 usr/bin/ovs-ofctl
 usr/bin/ovs-parse-backtrace
-usr/bin/ovs-parse-leaks
 usr/bin/ovs-pki
 usr/bin/ovsdb-client
 usr/sbin/ovs-bugtool
index 5969677..a219722 100644 (file)
@@ -6,4 +6,3 @@ _debian/utilities/ovs-ofctl.8
 _debian/utilities/ovs-pki.8
 _debian/utilities/bugtool/ovs-bugtool.8
 utilities/ovs-parse-backtrace.8
-utilities/ovs-parse-leaks.8
index b073dde..44d2c87 100755 (executable)
@@ -242,9 +242,11 @@ case "$1" in
         ;;
   restart|force-reload)
         log_daemon_msg "Restarting $DESC" "$NAME"
-        stop_server
-        # Wait some sensible amount, some server need this
-        [ -n "$DODTIME" ] && sleep $DODTIME
+        if running; then
+            stop_server
+            # Wait some sensible amount, some server need this.
+            [ -n "$DODTIME" ] && sleep $DODTIME
+        fi
         start_server
         running
         log_end_msg $?
index e890fd8..a119b14 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2011 Nicira, Inc.
+ * Copyright (c) 2007-2013 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
@@ -410,6 +410,12 @@ struct ovs_key_nd {
  * @OVS_FLOW_ATTR_CLEAR: If present in a %OVS_FLOW_CMD_SET request, clears the
  * last-used time, accumulated TCP flags, and statistics for this flow.
  * Otherwise ignored in requests.  Never present in notifications.
+ * @OVS_FLOW_ATTR_MASK: Nested %OVS_KEY_ATTR_* attributes specifying the
+ * mask bits for wildcarded flow match. Mask bit value '1' specifies exact
+ * match with corresponding flow key bit, while mask bit value '0' specifies
+ * a wildcarded match. Omitting attribute is treated as wildcarding all
+ * corresponding fields. Optional for all requests. If not present,
+ * all flow key bits are exact match bits.
  *
  * These attributes follow the &struct ovs_header within the Generic Netlink
  * payload for %OVS_FLOW_* commands.
@@ -422,6 +428,7 @@ enum ovs_flow_attr {
        OVS_FLOW_ATTR_TCP_FLAGS, /* 8-bit OR'd TCP flags. */
        OVS_FLOW_ATTR_USED,      /* u64 msecs last used in monotonic time. */
        OVS_FLOW_ATTR_CLEAR,     /* Flag to clear stats, tcp_flags, used. */
+       OVS_FLOW_ATTR_MASK,      /* Sequence of OVS_KEY_ATTR_* attributes. */
        __OVS_FLOW_ATTR_MAX
 };
 
index 5ca343e..6319f4e 100644 (file)
@@ -24,7 +24,6 @@
  * standardized, so they are not included in openflow.h.  Some of them may be
  * suitable for standardization; others we never expect to standardize. */
 
-#define NX_VENDOR_ID 0x00002320
 \f
 /* Nicira vendor-specific error messages extension.
  *
index c30fa92..f2b90db 100644 (file)
  * 0xff00...0xfff7  "reserved" but not assigned a meaning by OpenFlow 1.0
  * 0xfff8...0xffff  "reserved" OFPP_* ports with assigned meanings
  */
-enum ofp_port {
-    /* Ranges. */
-    OFPP_MAX        = 0xff00,   /* Maximum number of physical switch ports. */
-    OFPP_FIRST_RESV = 0xfff8,   /* First assigned reserved port number. */
-    OFPP_LAST_RESV  = 0xffff,   /* Last assigned reserved port number. */
-
-    /* Reserved output "ports". */
-    OFPP_IN_PORT    = 0xfff8,  /* Send the packet out the input port.  This
-                                  virtual port must be explicitly used
-                                  in order to send back out of the input
-                                  port. */
-    OFPP_TABLE      = 0xfff9,  /* Perform actions in flow table.
-                                  NB: This can only be the destination
-                                  port for packet-out messages. */
-    OFPP_NORMAL     = 0xfffa,  /* Process with normal L2/L3 switching. */
-    OFPP_FLOOD      = 0xfffb,  /* All physical ports except input port and
-                                  those disabled by STP. */
-    OFPP_ALL        = 0xfffc,  /* All physical ports except input port. */
-    OFPP_CONTROLLER = 0xfffd,  /* Send to controller. */
-    OFPP_LOCAL      = 0xfffe,  /* Local openflow "port". */
-    OFPP_NONE       = 0xffff   /* Not associated with a physical port. */
-};
+
+/* Ranges. */
+#define OFPP_MAX        OFP_PORT_C(0xff00) /* Max # of switch ports. */
+#define OFPP_FIRST_RESV OFP_PORT_C(0xfff8) /* First assigned reserved port. */
+#define OFPP_LAST_RESV  OFP_PORT_C(0xffff) /* Last assigned reserved port. */
+
+/* Reserved output "ports". */
+#define OFPP_IN_PORT    OFP_PORT_C(0xfff8) /* Where the packet came in. */
+#define OFPP_TABLE      OFP_PORT_C(0xfff9) /* Perform actions in flow table. */
+#define OFPP_NORMAL     OFP_PORT_C(0xfffa) /* Process with normal L2/L3. */
+#define OFPP_FLOOD      OFP_PORT_C(0xfffb) /* All ports except input port and
+                                            * ports disabled by STP. */
+#define OFPP_ALL        OFP_PORT_C(0xfffc) /* All ports except input port. */
+#define OFPP_CONTROLLER OFP_PORT_C(0xfffd) /* Send to controller. */
+#define OFPP_LOCAL      OFP_PORT_C(0xfffe) /* Local openflow "port". */
+#define OFPP_NONE       OFP_PORT_C(0xffff) /* Not associated with any port. */
 
 /* OpenFlow 1.0 specific capabilities supported by the datapath (struct
  * ofp_switch_features, member capabilities). */
index ec94cee..f7c2e78 100644 (file)
@@ -67,8 +67,8 @@
  * an OpenFlow 1.0 reserved port number to or from, respectively, the
  * corresponding OpenFlow 1.1 reserved port number.
  */
-#define OFPP11_MAX    0xffffff00
-#define OFPP11_OFFSET (OFPP11_MAX - OFPP_MAX)
+#define OFPP11_MAX    OFP11_PORT_C(0xffffff00)
+#define OFPP11_OFFSET 0xffff0000    /* OFPP11_MAX - OFPP_MAX */
 
 /* Reserved wildcard port used only for flow mod (delete) and flow stats
  * requests. Selects all flows regardless of output port
index 5546313..2977047 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008, 2011, 2012 The Board of Trustees of The Leland Stanford
+/* Copyright (c) 2008, 2011, 2012, 2013 The Board of Trustees of The Leland Stanford
  * Junior University
  *
  * We are making the OpenFlow specification and associated documentation
@@ -55,6 +55,9 @@
 
 #include "openflow/openflow-1.1.h"
 
+/* Error type for experimenter error messages. */
+#define OFPET12_EXPERIMENTER 0xffff
+
 /*
  * OXM Class IDs.
  * The high order bit differentiate reserved classes from member classes.
index 231bba9..1071d3d 100644 (file)
@@ -402,7 +402,7 @@ struct ofp13_meter_stats {
     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
+    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);
index 3cc22c9..a9e5a76 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008, 2011, 2012 The Board of Trustees of The Leland Stanford
+/* Copyright (c) 2008, 2011, 2012, 2013 The Board of Trustees of The Leland Stanford
  * Junior University
  *
  * We are making the OpenFlow specification and associated documentation
@@ -78,6 +78,29 @@ enum ofp_version {
     OFP13_VERSION = 0x04
 };
 
+/* Vendor (aka experimenter) IDs.
+ *
+ * These are used in various places in OpenFlow to identify an extension
+ * defined by some vendor, as opposed to a standardized part of the core
+ * OpenFlow protocol.
+ *
+ * Vendor IDs whose top 8 bits are 0 hold an Ethernet OUI in their low 24 bits.
+ * The Open Networking Foundation assigns vendor IDs whose top 8 bits are
+ * nonzero.
+ *
+ * A few vendor IDs are special:
+ *
+ *    - OF_VENDOR_ID is not a real vendor ID and does not appear in the
+ *      OpenFlow protocol itself.  It can occasionally be useful within Open
+ *      vSwitch to identify a standardized part of OpenFlow.
+ *
+ *    - ONF_VENDOR_ID is being used within the ONF "extensibility" working
+ *      group to identify extensions being proposed for standardization.
+ */
+#define OF_VENDOR_ID    0
+#define NX_VENDOR_ID    0x00002320 /* Nicira. */
+#define ONF_VENDOR_ID   0x4f4e4600 /* Open Networking Foundation. */
+
 #define OFP_MAX_TABLE_NAME_LEN 32
 #define OFP_MAX_PORT_NAME_LEN  16
 
@@ -351,6 +374,7 @@ enum ofp_flow_removed_reason {
     OFPRR_HARD_TIMEOUT,         /* Time exceeded hard_timeout. */
     OFPRR_DELETE,               /* Evicted by a DELETE flow mod. */
     OFPRR_GROUP_DELETE,         /* Group was removed. */
+    OFPRR_METER_DELETE,         /* Meter was removed. */
     OFPRR_EVICTION,             /* Switch eviction to free resources. */
 };
 
index 72caa5c..d07c1e8 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2011 Nicira, Inc.
+ * Copyright (c) 2010, 2011, 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.
@@ -60,4 +60,16 @@ typedef struct {
         ovs_be32 hi, lo;
 } ovs_32aligned_be64;
 
+/* ofp_port_t represents the port number of a OpenFlow switch.
+ * odp_port_t represents the port number on the datapath.
+ * ofp11_port_t represents the OpenFlow-1.1 port number. */
+typedef uint16_t OVS_BITWISE ofp_port_t;
+typedef uint32_t OVS_BITWISE odp_port_t;
+typedef uint32_t OVS_BITWISE ofp11_port_t;
+
+/* Macro functions that cast int types to ofp/odp/ofp11 types. */
+#define OFP_PORT_C(X) ((OVS_FORCE ofp_port_t) (X))
+#define ODP_PORT_C(X) ((OVS_FORCE odp_port_t) (X))
+#define OFP11_PORT_C(X) ((OVS_FORCE ofp11_port_t) (X))
+
 #endif /* openvswitch/types.h */
index 1a77500..45ae1f5 100644 (file)
@@ -4,5 +4,6 @@ noinst_HEADERS += \
         include/sparse/math.h \
         include/sparse/netinet/in.h \
         include/sparse/netinet/ip6.h \
+        include/sparse/pthread.h \
         include/sparse/sys/socket.h \
         include/sparse/sys/wait.h
index f94c527..c2d6156 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Nicira, Inc.
+ * Copyright (c) 2011, 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.
@@ -108,9 +108,6 @@ double jn(int, double);
 double ldexp(double, int);
 float ldexpf(float, int);
 long double ldexpl(long double, int);
-double lgamma(double);
-float lgammaf(float);
-long double lgammal(long double);
 long long llrint(double);
 long long llrintf(float);
 long long llrintl(long double);
index b3924c3..87d48d6 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Nicira, Inc.
+ * Copyright (c) 2011, 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.
@@ -109,7 +109,6 @@ static inline uint16_t ntohs(ovs_be16 x)
 
 in_addr_t inet_addr(const char *);
 int inet_aton (const char *, struct in_addr *);
-char *inet_ntoa(struct in_addr);
 const char *inet_ntop(int, const void *, char *, socklen_t);
 int inet_pton(int, const char *, void *);
 
diff --git a/include/sparse/pthread.h b/include/sparse/pthread.h
new file mode 100644 (file)
index 0000000..7ba6a05
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 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.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CHECKER__
+#error "Use this header only with sparse.  It is not a correct implementation."
+#endif
+
+/* Get actual <pthread.h> definitions for us to annotate and build on. */
+#include_next <pthread.h>
+
+#include "compiler.h"
+
+int pthread_mutex_lock(pthread_mutex_t *mutex) OVS_ACQUIRES(mutex);
+int pthread_mutex_unlock(pthread_mutex_t *mutex) OVS_RELEASES(mutex);
+
+int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock) OVS_ACQUIRES(rwlock);
+int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock) OVS_ACQUIRES(rwlock);
+int pthread_rwlock_unlock(pthread_rwlock_t *rwlock) OVS_RELEASES(rwlock);
+
+int pthread_cond_wait(pthread_cond_t *, pthread_mutex_t *mutex)
+    OVS_MUST_HOLD(mutex);
+
+#define pthread_mutex_trylock(MUTEX)                    \
+    ({                                                  \
+        int retval = pthread_mutex_trylock(mutex);      \
+        if (!retval) {                                  \
+            OVS_ACQUIRE(MUTEX);                         \
+        }                                               \
+        retval;                                         \
+    })
+
+#define pthread_rwlock_tryrdlock(RWLOCK)                \
+    ({                                                  \
+        int retval = pthread_rwlock_tryrdlock(rwlock);  \
+        if (!retval) {                                  \
+            OVS_ACQUIRE(RWLOCK);                        \
+        }                                               \
+        retval;                                         \
+    })
+#define pthread_rwlock_trywrlock(RWLOCK)                \
+    ({                                                  \
+        int retval = pthread_rwlock_trywrlock(rwlock);  \
+        if (!retval) {                                  \
+            OVS_ACQUIRE(RWLOCK);                        \
+        }                                               \
+        retval;                                         \
+    })
index 13f61e5..64b905e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2012 Nicira, Inc.
+ * Copyright (c) 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.
@@ -106,7 +106,8 @@ enum {
     SO_SNDLOWAT,
     SO_SNDTIMEO,
     SO_TYPE,
-    SO_RCVBUFFORCE
+    SO_RCVBUFFORCE,
+    SO_ATTACH_FILTER
 };
 
 enum {
index 9f85de7..0804956 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (C) 2009, 2010, 2011, 2012 Nicira, Inc.
+# Copyright (C) 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -57,6 +57,8 @@ lib_libopenvswitch_a_SOURCES = \
        lib/flow.h \
        lib/hash.c \
        lib/hash.h \
+       lib/hindex.c \
+       lib/hindex.h \
        lib/hmap.c \
        lib/hmap.h \
        lib/hmapx.c \
@@ -69,8 +71,6 @@ lib_libopenvswitch_a_SOURCES = \
        lib/jsonrpc.h \
        lib/lacp.c \
        lib/lacp.h \
-       lib/leak-checker.c \
-       lib/leak-checker.h \
        lib/learn.c \
        lib/learn.h \
        lib/learning-switch.c \
@@ -102,6 +102,8 @@ lib_libopenvswitch_a_SOURCES = \
        lib/netlink.h \
        lib/nx-match.c \
        lib/nx-match.h \
+       lib/odp-execute.c \
+       lib/odp-execute.h \
        lib/odp-util.c \
        lib/odp-util.h \
        lib/ofp-actions.c \
@@ -121,6 +123,15 @@ lib_libopenvswitch_a_SOURCES = \
        lib/ofp-version-opt.c \
        lib/ofpbuf.c \
        lib/ofpbuf.h \
+       lib/ovs-atomic-c11.h \
+       lib/ovs-atomic-gcc4+.c \
+       lib/ovs-atomic-gcc4+.h \
+       lib/ovs-atomic-gcc4.7+.h \
+       lib/ovs-atomic-pthreads.c \
+       lib/ovs-atomic-pthreads.h \
+       lib/ovs-atomic.h \
+       lib/ovs-thread.c \
+       lib/ovs-thread.h \
        lib/ovsdb-data.c \
        lib/ovsdb-data.h \
        lib/ovsdb-error.c \
@@ -293,7 +304,6 @@ MAN_FRAGMENTS += \
        lib/coverage-unixctl.man \
        lib/daemon.man \
        lib/daemon-syn.man \
-       lib/leak-checker.man \
        lib/memory-unixctl.man \
        lib/ofp-version.man \
        lib/ovs.tmac \
@@ -339,9 +349,12 @@ lib/dirs.c: lib/dirs.c.in Makefile
        mv lib/dirs.c.tmp lib/dirs.c
 
 $(srcdir)/lib/ofp-errors.inc: \
-       lib/ofp-errors.h $(srcdir)/build-aux/extract-ofp-errors
+       lib/ofp-errors.h include/openflow/openflow-common.h \
+       $(srcdir)/build-aux/extract-ofp-errors
        $(run_python) $(srcdir)/build-aux/extract-ofp-errors \
-               $(srcdir)/lib/ofp-errors.h > $@.tmp && mv $@.tmp $@
+               $(srcdir)/lib/ofp-errors.h \
+               $(srcdir)/include/openflow/openflow-common.h > $@.tmp
+       mv $@.tmp $@
 $(srcdir)/lib/ofp-errors.c: $(srcdir)/lib/ofp-errors.inc
 EXTRA_DIST += build-aux/extract-ofp-errors lib/ofp-errors.inc
 
index c6829d7..861a109 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 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.
 
 #include "backtrace.h"
 
-#include <errno.h>
-#include <inttypes.h>
-#include <stdbool.h>
-#include <stdio.h>
-
-#include "compiler.h"
-#include "vlog.h"
-
-VLOG_DEFINE_THIS_MODULE(backtrace);
-
 #ifdef HAVE_BACKTRACE
 #include <execinfo.h>
 void
@@ -41,79 +31,7 @@ backtrace_capture(struct backtrace *b)
         b->frames[i] = (uintptr_t) frames[i];
     }
 }
-#elif __GNUC__
-static uintptr_t
-get_max_stack(void)
-{
-    static const char file_name[] = "/proc/self/maps";
-    char line[1024];
-    int line_number;
-    FILE *f;
-
-    f = fopen(file_name, "r");
-    if (f == NULL) {
-        VLOG_WARN("opening %s failed: %s", file_name, strerror(errno));
-        return -1;
-    }
-
-    for (line_number = 1; fgets(line, sizeof line, f); line_number++) {
-        if (strstr(line, "[stack]")) {
-            uintptr_t end;
-            if (sscanf(line, "%*x-%"SCNxPTR, &end) != 1) {
-                VLOG_WARN("%s:%d: parse error", file_name, line_number);
-                continue;
-            }
-            fclose(f);
-            return end;
-        }
-    }
-    fclose(f);
-
-    VLOG_WARN("%s: no stack found", file_name);
-    return -1;
-}
-
-static uintptr_t
-stack_high(void)
-{
-    static uintptr_t high;
-    if (!high) {
-        high = get_max_stack();
-    }
-    return high;
-}
-
-static uintptr_t
-stack_low(void)
-{
-    uintptr_t low = (uintptr_t) &low;
-    return low;
-}
-
-static bool
-in_stack(void *p)
-{
-    uintptr_t address = (uintptr_t) p;
-    return address >= stack_low() && address < stack_high();
-}
-
-void
-backtrace_capture(struct backtrace *backtrace)
-{
-    void **frame;
-    size_t n;
-
-    n = 0;
-    for (frame = __builtin_frame_address(1);
-         frame != NULL && in_stack(frame) && frame[0] != NULL
-             && n < BACKTRACE_MAX_FRAMES;
-         frame = frame[0])
-    {
-        backtrace->frames[n++] = (uintptr_t) frame[1];
-    }
-    backtrace->n_frames = n;
-}
-#else  /* !HAVE_BACKTRACE && !__GNUC__ */
+#else
 void
 backtrace_capture(struct backtrace *backtrace)
 {
index 95dad2d..8039c09 100644 (file)
--- a/lib/bfd.c
+++ b/lib/bfd.c
@@ -179,6 +179,8 @@ struct bfd {
     long long int last_tx;        /* Last TX time. */
     long long int next_tx;        /* Next TX time. */
     long long int detect_time;    /* RFC 5880 6.8.4 Detection time. */
+
+    int ref_cnt;
 };
 
 static bool bfd_in_poll(const struct bfd *);
@@ -229,7 +231,8 @@ bfd_get_status(const struct bfd *bfd, struct smap *smap)
 /* Initializes, destroys, or reconfigures the BFD session 'bfd' (named 'name'),
  * according to the database configuration contained in 'cfg'.  Takes ownership
  * of 'bfd', which may be NULL.  Returns a BFD object which may be used as a
- * handle for the session, or NULL if BFD is not enabled according to 'cfg'. */
+ * handle for the session, or NULL if BFD is not enabled according to 'cfg'.
+ * Also returns NULL if cfg is NULL. */
 struct bfd *
 bfd_configure(struct bfd *bfd, const char *name,
               const struct smap *cfg)
@@ -246,12 +249,8 @@ bfd_configure(struct bfd *bfd, const char *name,
         init = true;
     }
 
-    if (!smap_get_bool(cfg, "enable", false)) {
-        if (bfd) {
-            hmap_remove(&all_bfds, &bfd->node);
-            free(bfd->name);
-            free(bfd);
-        }
+    if (!cfg || !smap_get_bool(cfg, "enable", false)) {
+        bfd_unref(bfd);
         return NULL;
     }
 
@@ -264,6 +263,7 @@ bfd_configure(struct bfd *bfd, const char *name,
         bfd->diag = DIAG_NONE;
         bfd->min_tx = 1000;
         bfd->mult = 3;
+        bfd->ref_cnt = 1;
 
         /* RFC 5881 section 4
          * The source port MUST be in the range 49152 through 65535.  The same
@@ -308,6 +308,30 @@ bfd_configure(struct bfd *bfd, const char *name,
     return bfd;
 }
 
+struct bfd *
+bfd_ref(const struct bfd *bfd_)
+{
+    struct bfd *bfd = CONST_CAST(struct bfd *, bfd_);
+    if (bfd) {
+        ovs_assert(bfd->ref_cnt > 0);
+        bfd->ref_cnt++;
+    }
+    return bfd;
+}
+
+void
+bfd_unref(struct bfd *bfd)
+{
+    if (bfd) {
+        ovs_assert(bfd->ref_cnt > 0);
+        if (!--bfd->ref_cnt) {
+            hmap_remove(&all_bfds, &bfd->node);
+            free(bfd->name);
+            free(bfd);
+        }
+    }
+}
+
 void
 bfd_wait(const struct bfd *bfd)
 {
@@ -413,8 +437,10 @@ bfd_put_packet(struct bfd *bfd, struct ofpbuf *p,
 }
 
 bool
-bfd_should_process_flow(const struct flow *flow)
+bfd_should_process_flow(const struct flow *flow, struct flow_wildcards *wc)
 {
+    memset(&wc->masks.nw_proto, 0xff, sizeof wc->masks.nw_proto);
+    memset(&wc->masks.tp_dst, 0xff, sizeof wc->masks.tp_dst);
     return (flow->dl_type == htons(ETH_TYPE_IP)
             && flow->nw_proto == IPPROTO_UDP
             && flow->tp_dst == htons(3784));
@@ -779,7 +805,7 @@ generate_discriminator(void)
     while (!disc) {
         struct bfd *bfd;
 
-        /* 'disc' is by defnition random, so there's no reason to waste time
+        /* 'disc' is by definition random, so there's no reason to waste time
          * hashing it. */
         disc = random_uint32();
         HMAP_FOR_EACH_IN_BUCKET (bfd, node, disc, &all_bfds) {
index 21203b3..ab854d8 100644 (file)
--- a/lib/bfd.h
+++ b/lib/bfd.h
@@ -23,6 +23,7 @@
 
 struct bfd;
 struct flow;
+struct flow_wildcards;
 struct ofpbuf;
 struct smap;
 
@@ -33,12 +34,14 @@ bool bfd_should_send_packet(const struct bfd *);
 void bfd_put_packet(struct bfd *bfd, struct ofpbuf *packet,
                     uint8_t eth_src[6]);
 
-bool bfd_should_process_flow(const struct flow *);
+bool bfd_should_process_flow(const struct flow *, struct flow_wildcards *);
 void bfd_process_packet(struct bfd *, const struct flow *,
                         const struct ofpbuf *);
 
 struct bfd *bfd_configure(struct bfd *, const char *name,
                           const struct smap *smap);
+struct bfd *bfd_ref(const struct bfd *);
+void bfd_unref(struct bfd *);
 
 bool bfd_forwarding(const struct bfd *);
 void bfd_get_status(const struct bfd *, struct smap *);
index aca18a2..183c37b 100644 (file)
@@ -108,6 +108,8 @@ struct bond {
      * where we can't otherwise provide revalidation feedback to the client.
      * That's only unixctl commands now; I hope no other cases will arise. */
     struct tag_set unixctl_tags;
+
+    int ref_cnt;
 };
 
 static struct hmap all_bonds = HMAP_INITIALIZER(&all_bonds);
@@ -128,6 +130,7 @@ static struct bond_entry *lookup_bond_entry(const struct bond *,
 static tag_type bond_get_active_slave_tag(const struct bond *);
 static struct bond_slave *choose_output_slave(const struct bond *,
                                               const struct flow *,
+                                              struct flow_wildcards *,
                                               uint16_t vlan, tag_type *tags);
 static void bond_update_fake_slave_stats(struct bond *);
 
@@ -178,6 +181,7 @@ bond_create(const struct bond_settings *s)
     hmap_init(&bond->slaves);
     bond->no_slaves_tag = tag_create_random();
     bond->next_fake_iface_update = LLONG_MAX;
+    bond->ref_cnt = 1;
 
     bond_reconfigure(bond, s);
 
@@ -186,9 +190,19 @@ bond_create(const struct bond_settings *s)
     return bond;
 }
 
+struct bond *
+bond_ref(const struct bond *bond_)
+{
+    struct bond *bond = CONST_CAST(struct bond *, bond_);
+
+    ovs_assert(bond->ref_cnt > 0);
+    bond->ref_cnt++;
+    return bond;
+}
+
 /* Frees 'bond'. */
 void
-bond_destroy(struct bond *bond)
+bond_unref(struct bond *bond)
 {
     struct bond_slave *slave, *next_slave;
 
@@ -196,6 +210,11 @@ bond_destroy(struct bond *bond)
         return;
     }
 
+    ovs_assert(bond->ref_cnt > 0);
+    if (--bond->ref_cnt) {
+        return;
+    }
+
     hmap_remove(&all_bonds, &bond->hmap_node);
 
     HMAP_FOR_EACH_SAFE (slave, next_slave, hmap_node, &bond->slaves) {
@@ -505,7 +524,7 @@ bond_compose_learning_packet(struct bond *bond,
 
     memset(&flow, 0, sizeof flow);
     memcpy(flow.dl_src, eth_src, ETH_ADDR_LEN);
-    slave = choose_output_slave(bond, &flow, vlan, &tags);
+    slave = choose_output_slave(bond, &flow, NULL, vlan, &tags);
 
     packet = ofpbuf_new(0);
     compose_rarp(packet, eth_src);
@@ -538,6 +557,10 @@ bond_check_admissibility(struct bond *bond, const void *slave_,
 {
     struct bond_slave *slave = bond_slave_lookup(bond, slave_);
 
+    if (!slave) {
+        return BV_DROP;
+    }
+
     /* LACP bonds have very loose admissibility restrictions because we can
      * assume the remote switch is aware of the bond and will "do the right
      * thing".  However, as a precaution we drop packets on disabled slaves
@@ -555,7 +578,7 @@ bond_check_admissibility(struct bond *bond, const void *slave_,
     /* Drop all multicast packets on inactive slaves. */
     if (eth_addr_is_multicast(eth_dst)) {
         *tags |= bond_get_active_slave_tag(bond);
-        if (bond->active_slave != bond_slave_lookup(bond, slave_)) {
+        if (bond->active_slave != slave) {
             return BV_DROP;
         }
     }
@@ -605,12 +628,17 @@ bond_check_admissibility(struct bond *bond, const void *slave_,
  * packet belongs to (so for an access port it will be the access port's VLAN).
  *
  * Adds a tag to '*tags' that associates the flow with the returned slave.
+ *
+ * If 'wc' is non-NULL, bitwise-OR's 'wc' with the set of bits that were
+ * significant in the selection.  At some point earlier, 'wc' should
+ * have been initialized (e.g., by flow_wildcards_init_catchall()).
  */
 void *
 bond_choose_output_slave(struct bond *bond, const struct flow *flow,
-                         uint16_t vlan, tag_type *tags)
+                         struct flow_wildcards *wc, uint16_t vlan,
+                         tag_type *tags)
 {
-    struct bond_slave *slave = choose_output_slave(bond, flow, vlan, tags);
+    struct bond_slave *slave = choose_output_slave(bond, flow, wc, vlan, tags);
     if (slave) {
         *tags |= slave->tag;
         return slave->aux;
@@ -1349,7 +1377,7 @@ lookup_bond_entry(const struct bond *bond, const struct flow *flow,
 
 static struct bond_slave *
 choose_output_slave(const struct bond *bond, const struct flow *flow,
-                    uint16_t vlan, tag_type *tags)
+                    struct flow_wildcards *wc, uint16_t vlan, tag_type *tags)
 {
     struct bond_entry *e;
 
@@ -1368,8 +1396,14 @@ choose_output_slave(const struct bond *bond, const struct flow *flow,
             /* Must have LACP negotiations for TCP balanced bonds. */
             return NULL;
         }
+        if (wc) {
+            flow_mask_hash_fields(flow, wc, NX_HASH_FIELDS_SYMMETRIC_L4);
+        }
         /* Fall Through. */
     case BM_SLB:
+        if (wc) {
+            flow_mask_hash_fields(flow, wc, NX_HASH_FIELDS_ETH_SRC);
+        }
         e = lookup_bond_entry(bond, flow, vlan);
         if (!e->slave || !e->slave->enabled) {
             e->slave = CONTAINER_OF(hmap_random_node(&bond->slaves),
index 6190081..7190935 100644 (file)
@@ -61,7 +61,8 @@ void bond_init(void);
 
 /* Basics. */
 struct bond *bond_create(const struct bond_settings *);
-void bond_destroy(struct bond *);
+void bond_unref(struct bond *);
+struct bond *bond_ref(const struct bond *);
 
 bool bond_reconfigure(struct bond *, const struct bond_settings *);
 void bond_slave_register(struct bond *, void *slave_, struct netdev *);
@@ -88,8 +89,9 @@ enum bond_verdict {
 enum bond_verdict bond_check_admissibility(struct bond *, const void *slave_,
                                            const uint8_t eth_dst[ETH_ADDR_LEN],
                                            tag_type *);
-void *bond_choose_output_slave(struct bond *,
-                               const struct flow *, uint16_t vlan, tag_type *);
+void *bond_choose_output_slave(struct bond *, const struct flow *,
+                               struct flow_wildcards *, uint16_t vlan,
+                               tag_type *);
 
 /* Rebalancing. */
 void bond_account(struct bond *, const struct flow *, uint16_t vlan,
index 92ac1e1..78a297a 100644 (file)
 
 VLOG_DEFINE_THIS_MODULE(bundle);
 
-static uint16_t
+static ofp_port_t
 execute_ab(const struct ofpact_bundle *bundle,
-           bool (*slave_enabled)(uint16_t ofp_port, void *aux), void *aux)
+           bool (*slave_enabled)(ofp_port_t ofp_port, void *aux), void *aux)
 {
     size_t i;
 
     for (i = 0; i < bundle->n_slaves; i++) {
-        uint16_t slave = bundle->slaves[i];
+        ofp_port_t slave = bundle->slaves[i];
         if (slave_enabled(slave, aux)) {
             return slave;
         }
@@ -51,13 +51,18 @@ execute_ab(const struct ofpact_bundle *bundle,
     return OFPP_NONE;
 }
 
-static uint16_t
-execute_hrw(const struct ofpact_bundle *bundle, const struct flow *flow,
-            bool (*slave_enabled)(uint16_t ofp_port, void *aux), void *aux)
+static ofp_port_t
+execute_hrw(const struct ofpact_bundle *bundle,
+            const struct flow *flow, struct flow_wildcards *wc,
+            bool (*slave_enabled)(ofp_port_t ofp_port, void *aux), void *aux)
 {
     uint32_t flow_hash, best_hash;
     int best, i;
 
+    if (bundle->n_slaves > 1) {
+        flow_mask_hash_fields(flow, wc, bundle->fields);
+    }
+
     flow_hash = flow_hash_fields(flow, bundle->fields, bundle->basis);
     best = -1;
     best_hash = 0;
@@ -76,16 +81,19 @@ execute_hrw(const struct ofpact_bundle *bundle, const struct flow *flow,
     return best >= 0 ? bundle->slaves[best] : OFPP_NONE;
 }
 
-/* Executes 'bundle' on 'flow'.  Uses 'slave_enabled' to determine if the slave
- * designated by 'ofp_port' is up.  Returns the chosen slave, or OFPP_NONE if
- * none of the slaves are acceptable. */
-uint16_t
-bundle_execute(const struct ofpact_bundle *bundle, const struct flow *flow,
-               bool (*slave_enabled)(uint16_t ofp_port, void *aux), void *aux)
+/* Executes 'bundle' on 'flow'.  Sets fields in 'wc' that were used to
+ * calculate the result.  Uses 'slave_enabled' to determine if the slave
+ * designated by 'ofp_port' is up.  Returns the chosen slave, or
+ * OFPP_NONE if none of the slaves are acceptable. */
+ofp_port_t
+bundle_execute(const struct ofpact_bundle *bundle,
+               const struct flow *flow, struct flow_wildcards *wc,
+               bool (*slave_enabled)(ofp_port_t ofp_port, void *aux),
+               void *aux)
 {
     switch (bundle->algorithm) {
     case NX_BD_ALG_HRW:
-        return execute_hrw(bundle, flow, slave_enabled, aux);
+        return execute_hrw(bundle, flow, wc, slave_enabled, aux);
 
     case NX_BD_ALG_ACTIVE_BACKUP:
         return execute_ab(bundle, slave_enabled, aux);
@@ -179,7 +187,7 @@ bundle_from_openflow(const struct nx_action_bundle *nab,
 }
 
 enum ofperr
-bundle_check(const struct ofpact_bundle *bundle, int max_ports,
+bundle_check(const struct ofpact_bundle *bundle, ofp_port_t max_ports,
              const struct flow *flow)
 {
     static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
@@ -193,7 +201,7 @@ bundle_check(const struct ofpact_bundle *bundle, int max_ports,
     }
 
     for (i = 0; i < bundle->n_slaves; i++) {
-        uint16_t ofp_port = bundle->slaves[i];
+        ofp_port_t ofp_port = bundle->slaves[i];
         enum ofperr error;
 
         error = ofputil_check_output_port(ofp_port, max_ports);
@@ -239,7 +247,7 @@ bundle_to_nxast(const struct ofpact_bundle *bundle, struct ofpbuf *openflow)
 
     slaves = ofpbuf_put_zeros(openflow, slaves_len);
     for (i = 0; i < bundle->n_slaves; i++) {
-        slaves[i] = htons(bundle->slaves[i]);
+        slaves[i] = htons(ofp_to_u16(bundle->slaves[i]));
     }
 }
 
@@ -264,7 +272,7 @@ bundle_parse__(const char *s, char **save_ptr,
     bundle = ofpact_put_BUNDLE(ofpacts);
 
     for (;;) {
-        uint16_t slave_port;
+        ofp_port_t slave_port;
         char *slave;
 
         slave = strtok_r(NULL, ", []", save_ptr);
index 5b6bb67..3e92374 100644 (file)
@@ -27,6 +27,7 @@
 
 struct ds;
 struct flow;
+struct flow_wildcards;
 struct ofpact_bundle;
 struct ofpbuf;
 
@@ -34,12 +35,13 @@ struct ofpbuf;
  *
  * See include/openflow/nicira-ext.h for NXAST_BUNDLE specification. */
 
-uint16_t bundle_execute(const struct ofpact_bundle *, const struct flow *,
-                        bool (*slave_enabled)(uint16_t ofp_port, void *aux),
+ofp_port_t bundle_execute(const struct ofpact_bundle *, const struct flow *,
+                        struct flow_wildcards *wc,
+                        bool (*slave_enabled)(ofp_port_t ofp_port, void *aux),
                         void *aux);
 enum ofperr bundle_from_openflow(const struct nx_action_bundle *,
                                  struct ofpbuf *ofpact);
-enum ofperr bundle_check(const struct ofpact_bundle *, int max_ports,
+enum ofperr bundle_check(const struct ofpact_bundle *, ofp_port_t max_ports,
                          const struct flow *);
 void bundle_to_nxast(const struct ofpact_bundle *, struct ofpbuf *of10);
 void bundle_parse(const char *, struct ofpbuf *ofpacts);
index d232b34..06415fc 100644 (file)
--- a/lib/cfm.c
+++ b/lib/cfm.c
@@ -26,6 +26,7 @@
 #include "flow.h"
 #include "hash.h"
 #include "hmap.h"
+#include "netdev.h"
 #include "ofpbuf.h"
 #include "packets.h"
 #include "poll-loop.h"
@@ -81,15 +82,19 @@ struct ccm {
 BUILD_ASSERT_DECL(CCM_LEN == sizeof(struct ccm));
 
 struct cfm {
-    char *name;                 /* Name of this CFM object. */
+    const char *name;           /* Name of this CFM object. */
     struct hmap_node hmap_node; /* Node in all_cfms list. */
 
+    struct netdev *netdev;
+    uint64_t rx_packets;        /* Packets received by 'netdev'. */
+
     uint64_t mpid;
     bool check_tnl_key;    /* Verify the tunnel key of inbound packets? */
     bool extended;         /* Extended mode. */
-    bool booted;           /* A full fault interval has occured. */
+    bool demand;           /* Demand mode. */
+    bool booted;           /* A full fault interval has occurred. */
     enum cfm_fault_reason fault;  /* Connectivity fault status. */
-    enum cfm_fault_reason recv_fault;  /* Bit mask of faults occuring on
+    enum cfm_fault_reason recv_fault;  /* Bit mask of faults occurring on
                                           receive. */
     bool opup;             /* Operational State. */
     bool remote_opup;      /* Remote Operational State. */
@@ -120,6 +125,8 @@ struct cfm {
     int health_interval;      /* Number of fault_intervals since health was
                                  recomputed. */
     long long int last_tx;    /* Last CCM transmission time. */
+
+    int ref_cnt;
 };
 
 /* Remote MPs represent foreign network entities that are configured to have
@@ -143,6 +150,18 @@ static struct hmap all_cfms = HMAP_INITIALIZER(&all_cfms);
 static unixctl_cb_func cfm_unixctl_show;
 static unixctl_cb_func cfm_unixctl_set_fault;
 
+static uint64_t
+cfm_rx_packets(const struct cfm *cfm)
+{
+    struct netdev_stats stats;
+
+    if (!netdev_get_stats(cfm->netdev, &stats)) {
+        return stats.rx_packets;
+    } else {
+        return 0;
+    }
+}
+
 static const uint8_t *
 cfm_ccm_addr(const struct cfm *cfm)
 {
@@ -287,12 +306,13 @@ cfm_init(void)
 /* Allocates a 'cfm' object called 'name'.  'cfm' should be initialized by
  * cfm_configure() before use. */
 struct cfm *
-cfm_create(const char *name)
+cfm_create(const struct netdev *netdev)
 {
     struct cfm *cfm;
 
     cfm = xzalloc(sizeof *cfm);
-    cfm->name = xstrdup(name);
+    cfm->netdev = netdev_ref(netdev);
+    cfm->name = netdev_get_name(cfm->netdev);
     hmap_init(&cfm->remote_mps);
     cfm_generate_maid(cfm);
     hmap_insert(&all_cfms, &cfm->hmap_node, hash_string(cfm->name, 0));
@@ -300,11 +320,12 @@ cfm_create(const char *name)
     cfm->fault_override = -1;
     cfm->health = -1;
     cfm->last_tx = 0;
+    cfm->ref_cnt = 1;
     return cfm;
 }
 
 void
-cfm_destroy(struct cfm *cfm)
+cfm_unref(struct cfm *cfm)
 {
     struct remote_mp *rmp, *rmp_next;
 
@@ -312,6 +333,11 @@ cfm_destroy(struct cfm *cfm)
         return;
     }
 
+    ovs_assert(cfm->ref_cnt);
+    if (--cfm->ref_cnt) {
+        return;
+    }
+
     HMAP_FOR_EACH_SAFE (rmp, rmp_next, node, &cfm->remote_mps) {
         hmap_remove(&cfm->remote_mps, &rmp->node);
         free(rmp);
@@ -319,11 +345,22 @@ cfm_destroy(struct cfm *cfm)
 
     hmap_destroy(&cfm->remote_mps);
     hmap_remove(&all_cfms, &cfm->hmap_node);
+    netdev_close(cfm->netdev);
     free(cfm->rmps_array);
-    free(cfm->name);
     free(cfm);
 }
 
+struct cfm *
+cfm_ref(const struct cfm *cfm_)
+{
+    struct cfm *cfm = CONST_CAST(struct cfm *, cfm_);
+    if (cfm) {
+        ovs_assert(cfm->ref_cnt > 0);
+        cfm->ref_cnt++;
+    }
+    return cfm;
+}
+
 /* Should be run periodically to update fault statistics messages. */
 void
 cfm_run(struct cfm *cfm)
@@ -332,6 +369,7 @@ cfm_run(struct cfm *cfm)
         long long int interval = cfm_fault_interval(cfm);
         struct remote_mp *rmp, *rmp_next;
         bool old_cfm_fault = cfm->fault;
+        bool demand_override;
 
         cfm->fault = cfm->recv_fault;
         cfm->recv_fault = 0;
@@ -373,14 +411,23 @@ cfm_run(struct cfm *cfm)
         }
         cfm->health_interval++;
 
-        HMAP_FOR_EACH_SAFE (rmp, rmp_next, node, &cfm->remote_mps) {
+        demand_override = false;
+        if (cfm->demand) {
+            uint64_t rx_packets = cfm_rx_packets(cfm);
+            demand_override = hmap_count(&cfm->remote_mps) == 1
+                && rx_packets > cfm->rx_packets;
+            cfm->rx_packets = rx_packets;
+        }
 
+        HMAP_FOR_EACH_SAFE (rmp, rmp_next, node, &cfm->remote_mps) {
             if (!rmp->recv) {
                 VLOG_INFO("%s: Received no CCM from RMP %"PRIu64" in the last"
                           " %lldms", cfm->name, rmp->mpid,
                           time_msec() - rmp->last_rx);
-                hmap_remove(&cfm->remote_mps, &rmp->node);
-                free(rmp);
+                if (!demand_override) {
+                    hmap_remove(&cfm->remote_mps, &rmp->node);
+                    free(rmp);
+                }
             } else {
                 rmp->recv = false;
 
@@ -518,6 +565,16 @@ cfm_configure(struct cfm *cfm, const struct cfm_settings *s)
         interval_ms = MIN(s->interval, UINT16_MAX);
     }
 
+    if (cfm->extended && s->demand) {
+        interval_ms = MAX(interval_ms, 500);
+        if (!cfm->demand) {
+            cfm->demand = true;
+            cfm->rx_packets = cfm_rx_packets(cfm);
+        }
+    } else {
+        cfm->demand = false;
+    }
+
     if (interval != cfm->ccm_interval || interval_ms != cfm->ccm_interval_ms) {
         cfm->ccm_interval = interval;
         cfm->ccm_interval_ms = interval_ms;
@@ -529,10 +586,26 @@ cfm_configure(struct cfm *cfm, const struct cfm_settings *s)
     return true;
 }
 
-/* Returns true if 'cfm' should process packets from 'flow'. */
+/* Must be called when the netdev owned by 'cfm' should change. */
+void
+cfm_set_netdev(struct cfm *cfm, const struct netdev *netdev)
+{
+    if (cfm->netdev != netdev) {
+        netdev_close(cfm->netdev);
+        cfm->netdev = netdev_ref(netdev);
+    }
+}
+
+/* Returns true if 'cfm' should process packets from 'flow'.  Sets
+ * fields in 'wc' that were used to make the determination. */
 bool
-cfm_should_process_flow(const struct cfm *cfm, const struct flow *flow)
+cfm_should_process_flow(const struct cfm *cfm, const struct flow *flow,
+                        struct flow_wildcards *wc)
 {
+    memset(&wc->masks.dl_dst, 0xff, sizeof wc->masks.dl_dst);
+    if (cfm->check_tnl_key) {
+        memset(&wc->masks.tunnel.tun_id, 0xff, sizeof wc->masks.tunnel.tun_id);
+    }
     return (ntohs(flow->dl_type) == ETH_TYPE_CFM
             && eth_addr_equals(flow->dl_dst, cfm_ccm_addr(cfm))
             && (!cfm->check_tnl_key || flow->tunnel.tun_id == htonll(0)));
index 8bb6778..8002f3e 100644 (file)
--- a/lib/cfm.h
+++ b/lib/cfm.h
@@ -23,6 +23,8 @@
 
 struct flow;
 struct ofpbuf;
+struct netdev;
+struct flow_wildcards;
 
 #define CFM_RANDOM_VLAN UINT16_MAX
 
@@ -53,6 +55,7 @@ struct cfm_settings {
     uint64_t mpid;              /* The MPID of this CFM. */
     int interval;               /* The requested transmission interval. */
     bool extended;              /* Run in extended mode. */
+    bool demand;                /* Run in demand mode. */
     bool opup;                  /* Operational State. */
     uint16_t ccm_vlan;          /* CCM Vlan tag. Zero if none.
                                    CFM_RANDOM_VLAN if random. */
@@ -62,14 +65,17 @@ struct cfm_settings {
 };
 
 void cfm_init(void);
-struct cfm *cfm_create(const char *name);
-void cfm_destroy(struct cfm *);
+struct cfm *cfm_create(const struct netdev *);
+struct cfm *cfm_ref(const struct cfm *);
+void cfm_unref(struct cfm *);
 void cfm_run(struct cfm *);
 bool cfm_should_send_ccm(struct cfm *);
 void cfm_compose_ccm(struct cfm *, struct ofpbuf *packet, uint8_t eth_src[6]);
 void cfm_wait(struct cfm *);
 bool cfm_configure(struct cfm *, const struct cfm_settings *);
-bool cfm_should_process_flow(const struct cfm *cfm, const struct flow *);
+void cfm_set_netdev(struct cfm *, const struct netdev *);
+bool cfm_should_process_flow(const struct cfm *cfm, const struct flow *,
+                             struct flow_wildcards *);
 void cfm_process_heartbeat(struct cfm *, const struct ofpbuf *packet);
 int cfm_get_fault(const struct cfm *);
 int cfm_get_health(const struct cfm *);
index 2d1e50b..a717bd3 100644 (file)
@@ -252,9 +252,15 @@ classifier_remove(struct classifier *cls, struct cls_rule *rule)
 
 /* Finds and returns the highest-priority rule in 'cls' that matches 'flow'.
  * Returns a null pointer if no rules in 'cls' match 'flow'.  If multiple rules
- * of equal priority match 'flow', returns one arbitrarily. */
+ * of equal priority match 'flow', returns one arbitrarily.
+ *
+ * If a rule is found and 'wc' is non-null, bitwise-OR's 'wc' with the
+ * set of bits that were significant in the lookup.  At some point
+ * earlier, 'wc' should have been initialized (e.g., by
+ * flow_wildcards_init_catchall()). */
 struct cls_rule *
-classifier_lookup(const struct classifier *cls, const struct flow *flow)
+classifier_lookup(const struct classifier *cls, const struct flow *flow,
+                  struct flow_wildcards *wc)
 {
     struct cls_table *table;
     struct cls_rule *best;
@@ -262,6 +268,10 @@ classifier_lookup(const struct classifier *cls, const struct flow *flow)
     best = NULL;
     LIST_FOR_EACH (table, list_node, &cls->tables_priority) {
         struct cls_rule *rule = find_match(table, flow);
+
+        if (wc) {
+            flow_wildcards_fold_minimask(wc, &table->mask);
+        }
         if (rule) {
             best = rule;
             LIST_FOR_EACH_CONTINUE (table, list_node, &cls->tables_priority) {
@@ -271,6 +281,9 @@ classifier_lookup(const struct classifier *cls, const struct flow *flow)
                     return best;
                 }
                 rule = find_match(table, flow);
+                if (wc) {
+                    flow_wildcards_fold_minimask(wc, &table->mask);
+                }
                 if (rule && rule->priority > best->priority) {
                     best = rule;
                 }
index d318864..fdc3af7 100644 (file)
@@ -96,7 +96,8 @@ void classifier_insert(struct classifier *, struct cls_rule *);
 struct cls_rule *classifier_replace(struct classifier *, struct cls_rule *);
 void classifier_remove(struct classifier *, struct cls_rule *);
 struct cls_rule *classifier_lookup(const struct classifier *,
-                                   const struct flow *);
+                                   const struct flow *,
+                                   struct flow_wildcards *);
 bool classifier_rule_overlaps(const struct classifier *,
                               const struct cls_rule *);
 
index b881c04..7800c0b 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 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.
@@ -19,6 +19,7 @@
 #include <getopt.h>
 #include <limits.h>
 #include <stdlib.h>
+#include "ovs-thread.h"
 #include "util.h"
 #include "vlog.h"
 
@@ -110,6 +111,7 @@ proctitle_init(int argc, char **argv)
 {
     int i;
 
+    assert_single_threaded();
     if (!argc || !argv[0]) {
         /* This situation should never occur, but... */
         return;
@@ -190,8 +192,8 @@ proctitle_init(int argc OVS_UNUSED, char **argv OVS_UNUSED)
 {
 }
 
-#ifndef __FreeBSD__
-/* On FreeBSD we #define this to setproctitle. */
+#if !(defined(__FreeBSD__) || defined(__NetBSD__))
+/* On these platforms we #define this to setproctitle. */
 void
 proctitle_set(const char *format OVS_UNUSED, ...)
 {
index 2592b79..bb12f72 100644 (file)
@@ -34,7 +34,7 @@ char *long_options_to_short_options(const struct option *options);
 void run_command(int argc, char *argv[], const struct command[]);
 
 void proctitle_init(int argc, char **argv);
-#ifdef __FreeBSD__
+#if defined(__FreeBSD__) || defined(__NetBSD__)
 #define proctitle_set setproctitle
 #else
 void proctitle_set(const char *, ...)
index 760389d..f3cbe96 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.
 #define OVS_UNLIKELY(CONDITION) (!!(CONDITION))
 #endif
 
+#ifdef __CHECKER__
+/* "sparse" annotations for mutexes and mutex-like constructs.
+ *
+ * In a function prototype, OVS_ACQUIRES(MUTEX) indicates that the function
+ * must be called without MUTEX acquired and that it returns with MUTEX
+ * acquired.  OVS_RELEASES(MUTEX) indicates the reverse.  OVS_MUST_HOLD
+ * indicates that the function must be called with MUTEX acquired by the
+ * caller and that the function does not release MUTEX.
+ *
+ * In practice, sparse ignores the MUTEX argument.  It need not even be a
+ * valid expression.  It is meant to indicate to human readers what mutex is
+ * being acquired.
+ *
+ * Since sparse ignores MUTEX, it need not be an actual mutex.  It can be
+ * any construct on which paired acquire and release semantics make sense:
+ * read/write locks, temporary memory allocations, whatever.
+ *
+ * OVS_ACQUIRE, OVS_RELEASE, and OVS_HOLDS are suitable for use within macros,
+ * where there is no function prototype to annotate. */
+#define OVS_ACQUIRES(MUTEX) __attribute__((context(MUTEX, 0, 1)))
+#define OVS_RELEASES(MUTEX) __attribute__((context(MUTEX, 1, 0)))
+#define OVS_MUST_HOLD(MUTEX) __attribute__((context(MUTEX, 1, 1)))
+#define OVS_ACQUIRE(MUTEX) __context__(MUTEX, 0, 1)
+#define OVS_RELEASE(MUTEX) __context__(MUTEX, 1, 0)
+#define OVS_HOLDS(MUTEX) __context__(MUTEX, 1, 1)
+#else
+#define OVS_ACQUIRES(MUTEX)
+#define OVS_RELEASES(MUTEX)
+#define OVS_MUST_HOLD(MUTEX)
+#define OVS_ACQUIRE(MUTEX)
+#define OVS_RELEASE(MUTEX)
+#define OVS_HOLDS(MUTEX)
+#endif
+
 /* ISO C says that a C implementation may choose any integer type for an enum
  * that is sufficient to hold all of its values.  Common ABIs (such as the
  * System V ABI used on i386 GNU/Linux) always use a full-sized "int", even
index e12bc14..3c1e5c3 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 "fatal-signal.h"
 #include "dirs.h"
 #include "lockfile.h"
+#include "ovs-thread.h"
 #include "process.h"
 #include "socket-util.h"
 #include "timeval.h"
@@ -88,6 +89,7 @@ make_pidfile_name(const char *name)
 void
 set_pidfile(const char *name)
 {
+    assert_single_threaded();
     free(pidfile);
     pidfile = make_pidfile_name(name);
 }
@@ -208,7 +210,7 @@ make_pidfile(void)
 
     file = fopen(tmpfile, "a+");
     if (!file) {
-        VLOG_FATAL("%s: create failed (%s)", tmpfile, strerror(errno));
+        VLOG_FATAL("%s: create failed (%s)", tmpfile, ovs_strerror(errno));
     }
 
     error = lock_pidfile(file, F_SETLK);
@@ -216,7 +218,8 @@ make_pidfile(void)
         /* 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));
+        VLOG_FATAL("%s: fcntl(F_SETLK) failed (%s)", tmpfile,
+                   ovs_strerror(error));
     }
 
     if (!overwrite_pidfile) {
@@ -227,16 +230,16 @@ make_pidfile(void)
     }
 
     if (fstat(fileno(file), &s) == -1) {
-        VLOG_FATAL("%s: fstat failed (%s)", tmpfile, strerror(errno));
+        VLOG_FATAL("%s: fstat failed (%s)", tmpfile, ovs_strerror(errno));
     }
 
     if (ftruncate(fileno(file), 0) == -1) {
-        VLOG_FATAL("%s: truncate failed (%s)", tmpfile, strerror(errno));
+        VLOG_FATAL("%s: truncate failed (%s)", tmpfile, ovs_strerror(errno));
     }
 
     fprintf(file, "%ld\n", pid);
     if (fflush(file) == EOF) {
-        VLOG_FATAL("%s: write failed (%s)", tmpfile, strerror(errno));
+        VLOG_FATAL("%s: write failed (%s)", tmpfile, ovs_strerror(errno));
     }
 
     error = rename(tmpfile, pidfile);
@@ -247,7 +250,7 @@ make_pidfile(void)
 
     if (error < 0) {
         VLOG_FATAL("failed to rename \"%s\" to \"%s\" (%s)",
-                   tmpfile, pidfile, strerror(errno));
+                   tmpfile, pidfile, ovs_strerror(errno));
     }
 
     /* Ensure that the pidfile will get deleted on exit. */
@@ -280,9 +283,7 @@ daemonize(void)
 pid_t
 fork_and_clean_up(void)
 {
-    pid_t pid;
-
-    pid = fork();
+    pid_t pid = xfork();
     if (pid > 0) {
         /* Running in parent process. */
         fatal_signal_fork();
@@ -290,10 +291,7 @@ fork_and_clean_up(void)
         /* Running in child process. */
         time_postfork();
         lockfile_postfork();
-    } else {
-        VLOG_FATAL("fork failed (%s)", strerror(errno));
     }
-
     return pid;
 }
 
@@ -342,7 +340,7 @@ fork_and_wait_for_startup(int *fdp)
                                status_msg);
                 }
             } else if (retval < 0) {
-                VLOG_FATAL("waitpid failed (%s)", strerror(errno));
+                VLOG_FATAL("waitpid failed (%s)", ovs_strerror(errno));
             } else {
                 NOT_REACHED();
             }
@@ -367,7 +365,7 @@ fork_notify_startup(int fd)
 
         error = write_fully(fd, "", 1, &bytes_written);
         if (error) {
-            VLOG_FATAL("pipe write failed (%s)", strerror(error));
+            VLOG_FATAL("pipe write failed (%s)", ovs_strerror(error));
         }
 
         close(fd);
@@ -418,7 +416,7 @@ monitor_daemon(pid_t daemon_pid)
         } while (retval == -1 && errno == EINTR);
 
         if (retval == -1) {
-            VLOG_FATAL("waitpid failed (%s)", strerror(errno));
+            VLOG_FATAL("waitpid failed (%s)", ovs_strerror(errno));
         } else if (retval == daemon_pid) {
             char *s = process_status_msg(status);
             if (should_restart(status)) {
@@ -436,7 +434,7 @@ monitor_daemon(pid_t daemon_pid)
                     r.rlim_max = 0;
                     if (setrlimit(RLIMIT_CORE, &r) == -1) {
                         VLOG_WARN("failed to disable core dumps: %s",
-                                  strerror(errno));
+                                  ovs_strerror(errno));
                     }
                 }
 
@@ -504,6 +502,7 @@ close_standard_fds(void)
 void
 daemonize_start(void)
 {
+    assert_single_threaded();
     daemonize_fd = -1;
 
     if (detach) {
@@ -643,13 +642,13 @@ read_pidfile__(const char *pidfile, bool delete_if_stale)
             return 0;
         }
         error = errno;
-        VLOG_WARN("%s: open: %s", pidfile, strerror(error));
+        VLOG_WARN("%s: open: %s", pidfile, ovs_strerror(error));
         goto error;
     }
 
     error = lock_pidfile__(file, F_GETLK, &lck);
     if (error) {
-        VLOG_WARN("%s: fcntl: %s", pidfile, strerror(error));
+        VLOG_WARN("%s: fcntl: %s", pidfile, ovs_strerror(error));
         goto error;
     }
     if (lck.l_type == F_UNLCK) {
@@ -688,7 +687,7 @@ read_pidfile__(const char *pidfile, bool delete_if_stale)
         if (unlink(pidfile)) {
             error = errno;
             VLOG_WARN("%s: failed to delete stale pidfile (%s)",
-                      pidfile, strerror(error));
+                      pidfile, ovs_strerror(error));
             goto error;
         }
         VLOG_DBG("%s: deleted stale pidfile", pidfile);
@@ -699,7 +698,7 @@ read_pidfile__(const char *pidfile, bool delete_if_stale)
     if (!fgets(line, sizeof line, file)) {
         if (ferror(file)) {
             error = errno;
-            VLOG_WARN("%s: read: %s", pidfile, strerror(error));
+            VLOG_WARN("%s: read: %s", pidfile, ovs_strerror(error));
         } else {
             error = ESRCH;
             VLOG_WARN("%s: read: unexpected end of file", pidfile);
@@ -745,6 +744,6 @@ check_already_running(void)
         VLOG_FATAL("%s: already running as pid %ld, aborting", pidfile, pid);
     } else if (pid < 0) {
         VLOG_FATAL("%s: pidfile check failed (%s), aborting",
-                   pidfile, strerror(-pid));
+                   pidfile, ovs_strerror(-pid));
     }
 }
index ac20ae7..804a90f 100644 (file)
@@ -105,6 +105,8 @@ struct dpif_linux_flow {
      * the Netlink version of the command, even if actions_len is zero. */
     const struct nlattr *key;           /* OVS_FLOW_ATTR_KEY. */
     size_t key_len;
+    const struct nlattr *mask;          /* OVS_FLOW_ATTR_MASK. */
+    size_t mask_len;
     const struct nlattr *actions;       /* OVS_FLOW_ATTR_ACTIONS. */
     size_t actions_len;
     const struct ovs_flow_stats *stats; /* OVS_FLOW_ATTR_STATS. */
@@ -167,7 +169,8 @@ 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 *, uint32_t port_no);
+static uint32_t dpif_linux_port_get_pid(const struct dpif *,
+                                        odp_port_t port_no);
 
 static void dpif_linux_vport_to_ofpbuf(const struct dpif_linux_vport *,
                                        struct ofpbuf *);
@@ -259,7 +262,7 @@ open_dpif(const struct dpif_linux_dp *dp, struct dpif **dpifp)
 static void
 destroy_channels(struct dpif_linux *dpif)
 {
-    int i;
+    unsigned int i;
 
     if (dpif->epoll_fd < 0) {
         return;
@@ -278,7 +281,7 @@ destroy_channels(struct dpif_linux *dpif)
         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.port_no = u32_to_odp(i);
         vport_request.upcall_pid = &upcall_pid;
         dpif_linux_vport_transact(&vport_request, NULL, NULL);
 
@@ -298,9 +301,10 @@ destroy_channels(struct dpif_linux *dpif)
 }
 
 static int
-add_channel(struct dpif_linux *dpif, uint32_t port_no, struct nl_sock *sock)
+add_channel(struct dpif_linux *dpif, odp_port_t port_no, struct nl_sock *sock)
 {
     struct epoll_event event;
+    uint32_t port_idx = odp_to_u32(port_no);
 
     if (dpif->epoll_fd < 0) {
         return 0;
@@ -308,9 +312,9 @@ add_channel(struct dpif_linux *dpif, uint32_t port_no, struct nl_sock *sock)
 
     /* 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 (port_idx >= dpif->uc_array_size) {
+        uint32_t new_size = port_idx + 1;
+        uint32_t i;
 
         if (new_size > MAX_PORTS) {
             VLOG_WARN_RL(&error_rl, "%s: datapath port %"PRIu32" too big",
@@ -331,29 +335,30 @@ add_channel(struct dpif_linux *dpif, uint32_t port_no, struct nl_sock *sock)
 
     memset(&event, 0, sizeof event);
     event.events = EPOLLIN;
-    event.data.u32 = port_no;
+    event.data.u32 = port_idx;
     if (epoll_ctl(dpif->epoll_fd, EPOLL_CTL_ADD, nl_sock_fd(sock),
                   &event) < 0) {
         return errno;
     }
 
-    nl_sock_destroy(dpif->channels[port_no].sock);
-    dpif->channels[port_no].sock = sock;
-    dpif->channels[port_no].last_poll = LLONG_MIN;
+    nl_sock_destroy(dpif->channels[port_idx].sock);
+    dpif->channels[port_idx].sock = sock;
+    dpif->channels[port_idx].last_poll = LLONG_MIN;
 
     return 0;
 }
 
 static void
-del_channel(struct dpif_linux *dpif, uint32_t port_no)
+del_channel(struct dpif_linux *dpif, odp_port_t port_no)
 {
     struct dpif_channel *ch;
+    uint32_t port_idx = odp_to_u32(port_no);
 
-    if (dpif->epoll_fd < 0 || port_no >= dpif->uc_array_size) {
+    if (dpif->epoll_fd < 0 || port_idx >= dpif->uc_array_size) {
         return;
     }
 
-    ch = &dpif->channels[port_no];
+    ch = &dpif->channels[port_idx];
     if (!ch->sock) {
         return;
     }
@@ -480,11 +485,13 @@ netdev_to_ovs_vport_type(const struct netdev *netdev)
 
 static int
 dpif_linux_port_add(struct dpif *dpif_, struct netdev *netdev,
-                    uint32_t *port_nop)
+                    odp_port_t *port_nop)
 {
     struct dpif_linux *dpif = dpif_linux_cast(dpif_);
     const struct netdev_tunnel_config *tnl_cfg;
-    const char *name = netdev_vport_get_dpif_port(netdev);
+    char namebuf[NETDEV_VPORT_NAME_BUFSIZE];
+    const char *name = netdev_vport_get_dpif_port(netdev,
+                                                  namebuf, sizeof namebuf);
     const char *type = netdev_get_type(netdev);
     struct dpif_linux_vport request, reply;
     struct nl_sock *sock = NULL;
@@ -537,7 +544,7 @@ dpif_linux_port_add(struct dpif *dpif_, struct netdev *netdev,
         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) {
+        if (error == EBUSY && *port_nop != ODPP_NONE) {
             VLOG_INFO("%s: requested port %"PRIu32" is in use",
                       dpif_name(dpif_), *port_nop);
         }
@@ -569,7 +576,7 @@ dpif_linux_port_add(struct dpif *dpif_, struct netdev *netdev,
 }
 
 static int
-dpif_linux_port_del(struct dpif *dpif_, uint32_t port_no)
+dpif_linux_port_del(struct dpif *dpif_, odp_port_t port_no)
 {
     struct dpif_linux *dpif = dpif_linux_cast(dpif_);
     struct dpif_linux_vport vport;
@@ -587,7 +594,7 @@ dpif_linux_port_del(struct dpif *dpif_, uint32_t port_no)
 }
 
 static int
-dpif_linux_port_query__(const struct dpif *dpif, uint32_t port_no,
+dpif_linux_port_query__(const struct dpif *dpif, odp_port_t port_no,
                         const char *port_name, struct dpif_port *dpif_port)
 {
     struct dpif_linux_vport request;
@@ -618,7 +625,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, uint32_t port_no,
+dpif_linux_port_query_by_number(const struct dpif *dpif, odp_port_t port_no,
                                 struct dpif_port *dpif_port)
 {
     return dpif_linux_port_query__(dpif, port_no, NULL, dpif_port);
@@ -631,23 +638,24 @@ dpif_linux_port_query_by_name(const struct dpif *dpif, const char *devname,
     return dpif_linux_port_query__(dpif, 0, devname, dpif_port);
 }
 
-static int
+static odp_port_t
 dpif_linux_get_max_ports(const struct dpif *dpif OVS_UNUSED)
 {
-    return MAX_PORTS;
+    return u32_to_odp(MAX_PORTS);
 }
 
 static uint32_t
-dpif_linux_port_get_pid(const struct dpif *dpif_, uint32_t port_no)
+dpif_linux_port_get_pid(const struct dpif *dpif_, odp_port_t port_no)
 {
     struct dpif_linux *dpif = dpif_linux_cast(dpif_);
+    uint32_t port_idx = odp_to_u32(port_no);
 
     if (dpif->epoll_fd < 0) {
         return 0;
     } else {
-        /* The UINT32_MAX "reserved" port number uses the "ovs-system"'s
+        /* The ODPP_NONE "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;
+        uint32_t idx = port_idx >= dpif->uc_array_size ? 0 : port_idx;
         return nl_sock_pid(dpif->channels[idx].sock);
     }
 }
@@ -805,6 +813,8 @@ dpif_linux_init_flow_put(struct dpif *dpif_, const struct dpif_flow_put *put,
     request->dp_ifindex = dpif->dp_ifindex;
     request->key = put->key;
     request->key_len = put->key_len;
+    request->mask = put->mask;
+    request->mask_len = put->mask_len;
     /* Ensure that OVS_FLOW_ATTR_ACTIONS will always be included. */
     request->actions = (put->actions
                         ? put->actions
@@ -899,6 +909,7 @@ dpif_linux_flow_dump_start(const struct dpif *dpif_, void **statep)
 static int
 dpif_linux_flow_dump_next(const struct dpif *dpif_ OVS_UNUSED, void *state_,
                           const struct nlattr **key, size_t *key_len,
+                          const struct nlattr **mask, size_t *mask_len,
                           const struct nlattr **actions, size_t *actions_len,
                           const struct dpif_flow_stats **stats)
 {
@@ -926,7 +937,8 @@ dpif_linux_flow_dump_next(const struct dpif *dpif_ OVS_UNUSED, void *state_,
             if (error == ENOENT) {
                 VLOG_DBG("dumped flow disappeared on get");
             } else if (error) {
-                VLOG_WARN("error fetching dumped flow: %s", strerror(error));
+                VLOG_WARN("error fetching dumped flow: %s",
+                          ovs_strerror(error));
             }
         }
     } while (error);
@@ -939,6 +951,10 @@ dpif_linux_flow_dump_next(const struct dpif *dpif_ OVS_UNUSED, void *state_,
         *key = state->flow.key;
         *key_len = state->flow.key_len;
     }
+    if (mask) {
+        *mask = state->flow.mask;
+        *mask_len = state->flow.mask ? state->flow.mask_len : 0;
+    }
     if (stats) {
         dpif_linux_flow_get_stats(&state->flow, &state->stats);
         *stats = &state->stats;
@@ -1193,7 +1209,7 @@ dpif_linux_recv_set(struct dpif *dpif_, bool enable)
             } else {
                 VLOG_WARN_RL(&error_rl,
                              "%s: failed to set upcall pid on port: %s",
-                             dpif_name(&dpif->dpif), strerror(error));
+                             dpif_name(&dpif->dpif), ovs_strerror(error));
                 nl_sock_destroy(sock);
 
                 if (error == ENODEV || error == ENOENT) {
@@ -1306,7 +1322,7 @@ dpif_linux_recv(struct dpif *dpif_, struct dpif_upcall *upcall,
         } 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));
+            VLOG_WARN_RL(&rl, "epoll_wait failed (%s)", ovs_strerror(errno));
         } else if (retval > 0) {
             dpif->n_events = retval;
         }
@@ -1477,7 +1493,7 @@ dpif_linux_is_internal_device(const char *name)
         ofpbuf_delete(buf);
     } else if (error != ENODEV && error != ENOENT) {
         VLOG_WARN_RL(&error_rl, "%s: vport query failed (%s)",
-                     name, strerror(error));
+                     name, ovs_strerror(error));
     }
 
     return reply.type == OVS_VPORT_TYPE_INTERNAL;
@@ -1551,7 +1567,7 @@ dpif_linux_vport_from_ofpbuf(struct dpif_linux_vport *vport,
 
     vport->cmd = genl->cmd;
     vport->dp_ifindex = ovs_header->dp_ifindex;
-    vport->port_no = nl_attr_get_u32(a[OVS_VPORT_ATTR_PORT_NO]);
+    vport->port_no = nl_attr_get_odp_port(a[OVS_VPORT_ATTR_PORT_NO]);
     vport->type = nl_attr_get_u32(a[OVS_VPORT_ATTR_TYPE]);
     vport->name = nl_attr_get_string(a[OVS_VPORT_ATTR_NAME]);
     if (a[OVS_VPORT_ATTR_UPCALL_PID]) {
@@ -1581,8 +1597,8 @@ dpif_linux_vport_to_ofpbuf(const struct dpif_linux_vport *vport,
     ovs_header = ofpbuf_put_uninit(buf, sizeof *ovs_header);
     ovs_header->dp_ifindex = vport->dp_ifindex;
 
-    if (vport->port_no != UINT32_MAX) {
-        nl_msg_put_u32(buf, OVS_VPORT_ATTR_PORT_NO, vport->port_no);
+    if (vport->port_no != ODPP_NONE) {
+        nl_msg_put_odp_port(buf, OVS_VPORT_ATTR_PORT_NO, vport->port_no);
     }
 
     if (vport->type != OVS_VPORT_TYPE_UNSPEC) {
@@ -1613,7 +1629,7 @@ void
 dpif_linux_vport_init(struct dpif_linux_vport *vport)
 {
     memset(vport, 0, sizeof *vport);
-    vport->port_no = UINT32_MAX;
+    vport->port_no = ODPP_NONE;
 }
 
 /* Executes 'request' in the kernel datapath.  If the command fails, returns a
@@ -1830,6 +1846,7 @@ dpif_linux_flow_from_ofpbuf(struct dpif_linux_flow *flow,
 {
     static const struct nl_policy ovs_flow_policy[] = {
         [OVS_FLOW_ATTR_KEY] = { .type = NL_A_NESTED },
+        [OVS_FLOW_ATTR_MASK] = { .type = NL_A_NESTED, .optional = true },
         [OVS_FLOW_ATTR_ACTIONS] = { .type = NL_A_NESTED, .optional = true },
         [OVS_FLOW_ATTR_STATS] = { NL_POLICY_FOR(struct ovs_flow_stats),
                                   .optional = true },
@@ -1861,6 +1878,11 @@ dpif_linux_flow_from_ofpbuf(struct dpif_linux_flow *flow,
     flow->dp_ifindex = ovs_header->dp_ifindex;
     flow->key = nl_attr_get(a[OVS_FLOW_ATTR_KEY]);
     flow->key_len = nl_attr_get_size(a[OVS_FLOW_ATTR_KEY]);
+
+    if (a[OVS_FLOW_ATTR_MASK]) {
+        flow->mask = nl_attr_get(a[OVS_FLOW_ATTR_MASK]);
+        flow->mask_len = nl_attr_get_size(a[OVS_FLOW_ATTR_MASK]);
+    }
     if (a[OVS_FLOW_ATTR_ACTIONS]) {
         flow->actions = nl_attr_get(a[OVS_FLOW_ATTR_ACTIONS]);
         flow->actions_len = nl_attr_get_size(a[OVS_FLOW_ATTR_ACTIONS]);
@@ -1896,6 +1918,10 @@ dpif_linux_flow_to_ofpbuf(const struct dpif_linux_flow *flow,
         nl_msg_put_unspec(buf, OVS_FLOW_ATTR_KEY, flow->key, flow->key_len);
     }
 
+    if (flow->mask_len) {
+        nl_msg_put_unspec(buf, OVS_FLOW_ATTR_MASK, flow->mask, flow->mask_len);
+    }
+
     if (flow->actions || flow->actions_len) {
         nl_msg_put_unspec(buf, OVS_FLOW_ATTR_ACTIONS,
                           flow->actions, flow->actions_len);
index 81062aa..ec94ccf 100644 (file)
@@ -22,6 +22,8 @@
 #include <stdint.h>
 #include <linux/openvswitch.h>
 
+#include "flow.h"
+
 struct ofpbuf;
 
 struct dpif_linux_vport {
@@ -30,7 +32,7 @@ struct dpif_linux_vport {
 
     /* ovs_vport header. */
     int dp_ifindex;
-    uint32_t port_no;                      /* UINT32_MAX if unknown. */
+    odp_port_t port_no;                    /* ODPP_NONE if unknown. */
     enum ovs_vport_type type;
 
     /* Attributes.
index d5060f1..48d8e27 100644 (file)
@@ -42,6 +42,7 @@
 #include "netdev.h"
 #include "netdev-vport.h"
 #include "netlink.h"
+#include "odp-execute.h"
 #include "odp-util.h"
 #include "ofp-print.h"
 #include "ofpbuf.h"
@@ -103,7 +104,7 @@ struct dp_netdev {
 
 /* A port in a netdev-based datapath. */
 struct dp_netdev_port {
-    int port_no;                /* Index into dp_netdev's 'ports'. */
+    odp_port_t port_no;         /* Index into dp_netdev's 'ports'. */
     struct list node;           /* Element in dp_netdev's 'port_list'. */
     struct netdev *netdev;
     struct netdev_saved_flags *sf;
@@ -140,15 +141,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 *, uint32_t port_no,
+static int get_port_by_number(struct dp_netdev *, odp_port_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, uint32_t port_no);
-static int do_del_port(struct dp_netdev *, uint32_t port_no);
+                       const char *type, odp_port_t port_no);
+static int do_del_port(struct dp_netdev *, odp_port_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 *,
@@ -158,6 +159,10 @@ static void dp_netdev_execute_actions(struct dp_netdev *,
                                       struct ofpbuf *, struct flow *,
                                       const struct nlattr *actions,
                                       size_t actions_len);
+static void dp_netdev_port_input(struct dp_netdev *dp,
+                                 struct dp_netdev_port *port,
+                                 struct ofpbuf *packet, uint32_t skb_priority,
+                                 uint32_t skb_mark, const struct flow_tnl *tnl);
 
 static struct dpif_netdev *
 dpif_netdev_cast(const struct dpif *dpif)
@@ -220,10 +225,12 @@ create_dpif_netdev(struct dp_netdev *dp)
     return &dpif->dpif;
 }
 
-static int
+/* Choose an unused, non-zero port number and return it on success.
+ * Return ODPP_NONE on failure. */
+static odp_port_t
 choose_port(struct dp_netdev *dp, const char *name)
 {
-    int port_no;
+    uint32_t port_no;
 
     if (dp->class != &dpif_netdev_class && 
         dp->class != &dpif_planetlab_class) {
@@ -244,7 +251,7 @@ choose_port(struct dp_netdev *dp, const char *name)
                 port_no = start_no + strtol(p, NULL, 10);
                 if (port_no > 0 && port_no < MAX_PORTS
                     && !dp->ports[port_no]) {
-                    return port_no;
+                    return u32_to_odp(port_no);
                 }
                 break;
             }
@@ -253,11 +260,11 @@ choose_port(struct dp_netdev *dp, const char *name)
 
     for (port_no = 1; port_no < MAX_PORTS; port_no++) {
         if (!dp->ports[port_no]) {
-            return port_no;
+            return u32_to_odp(port_no);
         }
     }
 
-    return -1;
+    return ODPP_NONE;
 }
 
 static int
@@ -278,7 +285,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);
+    error = do_add_port(dp, name, "internal", ODPP_LOCAL);
     if (error) {
         dp_netdev_free(dp);
         return error;
@@ -382,7 +389,7 @@ dpif_netdev_get_stats(const struct dpif *dpif, struct dpif_dp_stats *stats)
 
 static int
 do_add_port(struct dp_netdev *dp, const char *devname, const char *type,
-            uint32_t port_no)
+            odp_port_t port_no)
 {
     struct netdev_saved_flags *sf;
     struct dp_netdev_port *port;
@@ -407,7 +414,7 @@ do_add_port(struct dp_netdev *dp, const char *devname, const char *type,
     if (error
         && !(error == EOPNOTSUPP && dpif_netdev_class_is_dummy(dp->class))) {
         VLOG_ERR("%s: cannot receive packets on this network device (%s)",
-                 devname, strerror(errno));
+                 devname, ovs_strerror(errno));
         netdev_close(netdev);
         return error;
     }
@@ -432,7 +439,7 @@ do_add_port(struct dp_netdev *dp, const char *devname, const char *type,
     }
 
     list_push_back(&dp->port_list, &port->node);
-    dp->ports[port_no] = port;
+    dp->ports[odp_to_u32(port_no)] = port;
     dp->serial++;
 
     return 0;
@@ -440,51 +447,55 @@ do_add_port(struct dp_netdev *dp, const char *devname, const char *type,
 
 static int
 dpif_netdev_port_add(struct dpif *dpif, struct netdev *netdev,
-                     uint32_t *port_nop)
+                     odp_port_t *port_nop)
 {
     struct dp_netdev *dp = get_dp_netdev(dpif);
-    int port_no;
-
-    if (*port_nop != UINT32_MAX) {
-        if (*port_nop >= MAX_PORTS) {
+    char namebuf[NETDEV_VPORT_NAME_BUFSIZE];
+    const char *dpif_port;
+    odp_port_t port_no;
+
+    dpif_port = netdev_vport_get_dpif_port(netdev, namebuf, sizeof namebuf);
+    if (*port_nop != ODPP_NONE) {
+        uint32_t port_idx = odp_to_u32(*port_nop);
+        if (port_idx >= MAX_PORTS) {
             return EFBIG;
-        } else if (dp->ports[*port_nop]) {
+        } else if (dp->ports[port_idx]) {
             return EBUSY;
         }
         port_no = *port_nop;
     } else {
-        port_no = choose_port(dp, netdev_vport_get_dpif_port(netdev));
+        port_no = choose_port(dp, dpif_port);
     }
-    if (port_no >= 0) {
+    if (port_no != ODPP_NONE) {
         *port_nop = port_no;
-        return do_add_port(dp, netdev_vport_get_dpif_port(netdev),
-                           netdev_get_type(netdev), port_no);
+        return do_add_port(dp, dpif_port, netdev_get_type(netdev), port_no);
     }
     return EFBIG;
 }
 
 static int
-dpif_netdev_port_del(struct dpif *dpif, uint32_t port_no)
+dpif_netdev_port_del(struct dpif *dpif, odp_port_t port_no)
 {
     struct dp_netdev *dp = get_dp_netdev(dpif);
-    return port_no == OVSP_LOCAL ? EINVAL : do_del_port(dp, port_no);
+    return (port_no == ODPP_LOCAL ?
+                           EINVAL : do_del_port(dp, port_no));
 }
 
 static bool
-is_valid_port_number(uint32_t port_no)
+is_valid_port_number(odp_port_t port_no)
 {
-    return port_no < MAX_PORTS;
+    return odp_to_u32(port_no) < MAX_PORTS;
 }
 
 static int
 get_port_by_number(struct dp_netdev *dp,
-                   uint32_t port_no, struct dp_netdev_port **portp)
+                   odp_port_t port_no, struct dp_netdev_port **portp)
 {
     if (!is_valid_port_number(port_no)) {
         *portp = NULL;
         return EINVAL;
     } else {
-        *portp = dp->ports[port_no];
+        *portp = dp->ports[odp_to_u32(port_no)];
         return *portp ? 0 : ENOENT;
     }
 }
@@ -496,7 +507,7 @@ get_port_by_name(struct dp_netdev *dp,
     struct dp_netdev_port *port;
 
     LIST_FOR_EACH (port, node, &dp->port_list) {
-        if (!strcmp(netdev_vport_get_dpif_port(port->netdev), devname)) {
+        if (!strcmp(netdev_get_name(port->netdev), devname)) {
             *portp = port;
             return 0;
         }
@@ -505,7 +516,7 @@ get_port_by_name(struct dp_netdev *dp,
 }
 
 static int
-do_del_port(struct dp_netdev *dp, uint32_t port_no)
+do_del_port(struct dp_netdev *dp, odp_port_t port_no)
 {
     struct dp_netdev_port *port;
     int error;
@@ -516,7 +527,7 @@ do_del_port(struct dp_netdev *dp, uint32_t port_no)
     }
 
     list_remove(&port->node);
-    dp->ports[port->port_no] = NULL;
+    dp->ports[odp_to_u32(port_no)] = NULL;
     dp->serial++;
 
     netdev_close(port->netdev);
@@ -532,13 +543,13 @@ static void
 answer_port_query(const struct dp_netdev_port *port,
                   struct dpif_port *dpif_port)
 {
-    dpif_port->name = xstrdup(netdev_vport_get_dpif_port(port->netdev));
+    dpif_port->name = xstrdup(netdev_get_name(port->netdev));
     dpif_port->type = xstrdup(port->type);
     dpif_port->port_no = port->port_no;
 }
 
 static int
-dpif_netdev_port_query_by_number(const struct dpif *dpif, uint32_t port_no,
+dpif_netdev_port_query_by_number(const struct dpif *dpif, odp_port_t port_no,
                                  struct dpif_port *dpif_port)
 {
     struct dp_netdev *dp = get_dp_netdev(dpif);
@@ -567,10 +578,10 @@ dpif_netdev_port_query_by_name(const struct dpif *dpif, const char *devname,
     return error;
 }
 
-static int
+static odp_port_t
 dpif_netdev_get_max_ports(const struct dpif *dpif OVS_UNUSED)
 {
-    return MAX_PORTS;
+    return u32_to_odp(MAX_PORTS);
 }
 
 static void
@@ -600,7 +611,7 @@ dpif_netdev_flow_flush(struct dpif *dpif)
 }
 
 struct dp_netdev_port_state {
-    uint32_t port_no;
+    odp_port_t port_no;
     char *name;
 };
 
@@ -617,17 +628,18 @@ dpif_netdev_port_dump_next(const struct dpif *dpif, void *state_,
 {
     struct dp_netdev_port_state *state = state_;
     struct dp_netdev *dp = get_dp_netdev(dpif);
-    uint32_t port_no;
+    uint32_t port_idx;
 
-    for (port_no = state->port_no; port_no < MAX_PORTS; port_no++) {
-        struct dp_netdev_port *port = dp->ports[port_no];
+    for (port_idx = odp_to_u32(state->port_no);
+         port_idx < MAX_PORTS; port_idx++) {
+        struct dp_netdev_port *port = dp->ports[port_idx];
         if (port) {
             free(state->name);
-            state->name = xstrdup(netdev_vport_get_dpif_port(port->netdev));
+            state->name = xstrdup(netdev_get_name(port->netdev));
             dpif_port->name = state->name;
             dpif_port->type = port->type;
             dpif_port->port_no = port->port_no;
-            state->port_no = port_no + 1;
+            state->port_no = u32_to_odp(port_idx + 1);
             return 0;
         }
     }
@@ -709,9 +721,7 @@ dpif_netdev_flow_from_nlattrs(const struct nlattr *key, uint32_t key_len,
         return EINVAL;
     }
 
-    if (flow->in_port < OFPP_MAX
-        ? flow->in_port >= MAX_PORTS
-        : flow->in_port != OFPP_LOCAL && flow->in_port != OFPP_NONE) {
+    if (!is_valid_port_number(flow->in_port.odp_port)) {
         return EINVAL;
     }
 
@@ -880,6 +890,7 @@ dpif_netdev_flow_dump_start(const struct dpif *dpif OVS_UNUSED, void **statep)
 static int
 dpif_netdev_flow_dump_next(const struct dpif *dpif, void *state_,
                            const struct nlattr **key, size_t *key_len,
+                           const struct nlattr **mask, size_t *mask_len,
                            const struct nlattr **actions, size_t *actions_len,
                            const struct dpif_flow_stats **stats)
 {
@@ -899,12 +910,17 @@ 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, flow->key.in_port);
+        odp_flow_key_from_flow(&buf, &flow->key, flow->key.in_port.odp_port);
 
         *key = buf.data;
         *key_len = buf.size;
     }
 
+    if (mask) {
+        *mask = NULL;
+        *mask_len = 0;
+    }
+
     if (actions) {
         free(state->actions);
         state->actions = xmemdup(flow->actions, flow->actions_len);
@@ -949,7 +965,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, 0, NULL, -1, &key);
+    flow_extract(&copy, 0, 0, NULL, NULL, &key);
     error = dpif_netdev_flow_from_nlattrs(execute->key, execute->key_len,
                                           &key);
     if (!error) {
@@ -1039,15 +1055,18 @@ dp_netdev_flow_used(struct dp_netdev_flow *flow, const struct ofpbuf *packet)
 
 static void
 dp_netdev_port_input(struct dp_netdev *dp, struct dp_netdev_port *port,
-                     struct ofpbuf *packet)
+                     struct ofpbuf *packet, uint32_t skb_priority,
+                     uint32_t skb_mark, const struct flow_tnl *tnl)
 {
     struct dp_netdev_flow *flow;
     struct flow key;
+    union flow_in_port in_port_;
 
     if (packet->size < ETH_HEADER_LEN) {
         return;
     }
-    flow_extract(packet, 0, 0, NULL, port->port_no, &key);
+    in_port_.odp_port = port->port_no;
+    flow_extract(packet, skb_priority, skb_mark, tnl, &in_port_, &key);
     flow = dp_netdev_lookup_flow(dp, &key);
     if (flow) {
         dp_netdev_flow_used(flow, packet);
@@ -1078,12 +1097,12 @@ dpif_netdev_run(struct dpif *dpif)
 
         error = port->rx ? netdev_rx_recv(port->rx, &packet) : EOPNOTSUPP;
         if (!error) {
-            dp_netdev_port_input(dp, port, &packet);
+            dp_netdev_port_input(dp, port, &packet, 0, 0, NULL);
         } else if (error != EAGAIN && error != EOPNOTSUPP) {
             static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+
             VLOG_ERR_RL(&rl, "error receiving data from %s: %s",
-                        netdev_vport_get_dpif_port(port->netdev),
-                        strerror(error));
+                        netdev_get_name(port->netdev), ovs_strerror(error));
         }
     }
     ofpbuf_uninit(&packet);
@@ -1103,18 +1122,9 @@ dpif_netdev_wait(struct dpif *dpif)
 }
 
 static void
-dp_netdev_set_dl(struct ofpbuf *packet, const struct ovs_key_ethernet *eth_key)
-{
-    struct eth_header *eh = packet->l2;
-
-    memcpy(eh->eth_src, eth_key->eth_src, sizeof eh->eth_src);
-    memcpy(eh->eth_dst, eth_key->eth_dst, sizeof eh->eth_dst);
-}
-
-static void
-dp_netdev_output_port(struct dp_netdev *dp, struct ofpbuf *packet,
-                      uint32_t out_port)
+dp_netdev_output_port(void *dp_, struct ofpbuf *packet, uint32_t out_port)
 {
+    struct dp_netdev *dp = dp_;
     struct dp_netdev_port *p = dp->ports[out_port];
     if (p) {
         netdev_send(p->netdev, packet);
@@ -1143,7 +1153,7 @@ dp_netdev_output_userspace(struct dp_netdev *dp, const struct ofpbuf *packet,
         ofpbuf_init(buf, buf_size);
 
         /* Put ODP flow. */
-        odp_flow_key_from_flow(buf, flow, flow->in_port);
+        odp_flow_key_from_flow(buf, flow, flow->in_port.odp_port);
         upcall->key = buf->data;
         upcall->key_len = buf->size;
 
@@ -1171,167 +1181,21 @@ dp_netdev_output_userspace(struct dp_netdev *dp, const struct ofpbuf *packet,
 }
 
 static void
-dp_netdev_sample(struct dp_netdev *dp,
-                 struct ofpbuf *packet, struct flow *key,
-                 const struct nlattr *action)
-{
-    const struct nlattr *subactions = NULL;
-    const struct nlattr *a;
-    size_t left;
-
-    NL_NESTED_FOR_EACH_UNSAFE (a, left, action) {
-        int type = nl_attr_type(a);
-
-        switch ((enum ovs_sample_attr) type) {
-        case OVS_SAMPLE_ATTR_PROBABILITY:
-            if (random_uint32() >= nl_attr_get_u32(a)) {
-                return;
-            }
-            break;
-
-        case OVS_SAMPLE_ATTR_ACTIONS:
-            subactions = a;
-            break;
-
-        case OVS_SAMPLE_ATTR_UNSPEC:
-        case __OVS_SAMPLE_ATTR_MAX:
-        default:
-            NOT_REACHED();
-        }
-    }
-
-    dp_netdev_execute_actions(dp, packet, key, nl_attr_get(subactions),
-                              nl_attr_get_size(subactions));
-}
-
-static void
-dp_netdev_action_userspace(struct dp_netdev *dp,
-                          struct ofpbuf *packet, struct flow *key,
-                          const struct nlattr *a)
+dp_netdev_action_userspace(void *dp, struct ofpbuf *packet,
+                           const struct flow *key,
+                           const struct nlattr *userdata)
 {
-    const struct nlattr *userdata;
-
-    userdata = nl_attr_find_nested(a, OVS_USERSPACE_ATTR_USERDATA);
     dp_netdev_output_userspace(dp, packet, DPIF_UC_ACTION, key, userdata);
 }
 
-static void
-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_PRIORITY:
-    case OVS_KEY_ATTR_SKB_MARK:
-    case OVS_KEY_ATTR_TUNNEL:
-        /* not implemented */
-        break;
-
-    case OVS_KEY_ATTR_ETHERNET:
-        dp_netdev_set_dl(packet,
-                   nl_attr_get_unspec(a, sizeof(struct ovs_key_ethernet)));
-        break;
-
-    case OVS_KEY_ATTR_IPV4:
-        ipv4_key = nl_attr_get_unspec(a, sizeof(struct ovs_key_ipv4));
-        packet_set_ipv4(packet, ipv4_key->ipv4_src, ipv4_key->ipv4_dst,
-                        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);
-        break;
-
-     case OVS_KEY_ATTR_UDP:
-        udp_key = nl_attr_get_unspec(a, sizeof(struct ovs_key_udp));
-        packet_set_udp_port(packet, udp_key->udp_src, udp_key->udp_dst);
-        break;
-
-     case OVS_KEY_ATTR_MPLS:
-         set_mpls_lse(packet, nl_attr_get_be32(a));
-         break;
-
-     case OVS_KEY_ATTR_UNSPEC:
-     case OVS_KEY_ATTR_ENCAP:
-     case OVS_KEY_ATTR_ETHERTYPE:
-     case OVS_KEY_ATTR_IN_PORT:
-     case OVS_KEY_ATTR_VLAN:
-     case OVS_KEY_ATTR_ICMP:
-     case OVS_KEY_ATTR_ICMPV6:
-     case OVS_KEY_ATTR_ARP:
-     case OVS_KEY_ATTR_ND:
-     case __OVS_KEY_ATTR_MAX:
-     default:
-        NOT_REACHED();
-    }
-}
-
 static void
 dp_netdev_execute_actions(struct dp_netdev *dp,
                           struct ofpbuf *packet, struct flow *key,
                           const struct nlattr *actions,
                           size_t actions_len)
 {
-    const struct nlattr *a;
-    unsigned int left;
-
-    NL_ATTR_FOR_EACH_UNSAFE (a, left, actions, actions_len) {
-        int type = nl_attr_type(a);
-
-        switch ((enum ovs_action_attr) type) {
-        case OVS_ACTION_ATTR_OUTPUT:
-            dp_netdev_output_port(dp, packet, nl_attr_get_u32(a));
-            break;
-
-        case OVS_ACTION_ATTR_USERSPACE:
-            dp_netdev_action_userspace(dp, packet, key, a);
-            break;
-
-        case OVS_ACTION_ATTR_PUSH_VLAN: {
-            const struct ovs_action_push_vlan *vlan = nl_attr_get(a);
-            eth_push_vlan(packet, vlan->vlan_tci);
-            break;
-        }
-
-        case OVS_ACTION_ATTR_POP_VLAN:
-            eth_pop_vlan(packet);
-            break;
-
-        case OVS_ACTION_ATTR_PUSH_MPLS: {
-            const struct ovs_action_push_mpls *mpls = nl_attr_get(a);
-            push_mpls(packet, mpls->mpls_ethertype, mpls->mpls_lse);
-            break;
-         }
-
-        case OVS_ACTION_ATTR_POP_MPLS:
-            pop_mpls(packet, nl_attr_get_be16(a));
-            break;
-
-        case OVS_ACTION_ATTR_SET:
-            execute_set_action(packet, nl_attr_get(a));
-            break;
-
-        case OVS_ACTION_ATTR_SAMPLE:
-            dp_netdev_sample(dp, packet, key, a);
-            break;
-
-        case OVS_ACTION_ATTR_UNSPEC:
-        case __OVS_ACTION_ATTR_MAX:
-            NOT_REACHED();
-        }
-    }
+    odp_execute_actions(dp, packet, key, actions, actions_len,
+                        dp_netdev_output_port, dp_netdev_action_userspace);
 }
 
 #define DPIF_NETDEV_CLASS_FUNCTIONS                    \
index 4f04c95..1609c12 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.
@@ -127,10 +127,10 @@ struct dpif_class {
      * 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,
-                    uint32_t *port_no);
+                    odp_port_t *port_no);
 
     /* Removes port numbered 'port_no' from 'dpif'. */
-    int (*port_del)(struct dpif *dpif, uint32_t port_no);
+    int (*port_del)(struct dpif *dpif, odp_port_t port_no);
 
     /* Queries 'dpif' for a port with the given 'port_no' or 'devname'.
      * If 'port' is not null, stores information about the port into
@@ -139,14 +139,14 @@ struct dpif_class {
      * 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,
+    int (*port_query_by_number)(const struct dpif *dpif, odp_port_t port_no,
                                 struct dpif_port *port);
     int (*port_query_by_name)(const struct dpif *dpif, const char *devname,
                               struct dpif_port *port);
 
     /* Returns one greater than the largest port number accepted in flow
      * actions. */
-    int (*get_max_ports)(const struct dpif *dpif);
+    odp_port_t (*get_max_ports)(const struct dpif *dpif);
 
     /* Returns the Netlink PID value to supply in OVS_ACTION_ATTR_USERSPACE
      * actions as the OVS_USERSPACE_ATTR_PID attribute's value, for use in
@@ -162,7 +162,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, uint32_t port_no);
+    uint32_t (*port_get_pid)(const struct dpif *dpif, odp_port_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
@@ -279,12 +279,22 @@ struct dpif_class {
      * called again once it returns nonzero within a given iteration (but the
      * 'flow_dump_done' function will be called afterward).
      *
-     * On success, if 'key' and 'key_len' are nonnull then '*key' and
-     * '*key_len' must be set to Netlink attributes with types OVS_KEY_ATTR_*
-     * representing the dumped flow's key.  If 'actions' and 'actions_len' are
-     * nonnull then they should be set to Netlink attributes with types
-     * OVS_ACTION_ATTR_* representing the dumped flow's actions.  If 'stats'
-     * is nonnull then it should be set to the dumped flow's statistics.
+     * On success:
+     *
+     *     - If 'key' and 'key_len' are nonnull, then '*key' and '*key_len'
+     *       must be set to Netlink attributes with types OVS_KEY_ATTR_*
+     *       representing the dumped flow's key.
+     *
+     *     - If 'mask' and 'mask_len' are nonnull then '*mask' and '*mask_len'
+     *       must be set to Netlink attributes with types of OVS_KEY_ATTR_*
+     *       representing the dumped flow's mask.
+     *
+     *     - If 'actions' and 'actions_len' are nonnull then they should be set
+     *       to Netlink attributes with types OVS_ACTION_ATTR_* representing
+     *       the dumped flow's actions.
+     *
+     *     - If 'stats' is nonnull then it should be set to the dumped flow's
+     *       statistics.
      *
      * All of the returned data is owned by 'dpif', not by the caller, and the
      * caller must not modify or free it.  'dpif' must guarantee that it
@@ -292,6 +302,7 @@ struct dpif_class {
      * 'flow_dump_next' or 'flow_dump_done' for 'state'. */
     int (*flow_dump_next)(const struct dpif *dpif, void *state,
                           const struct nlattr **key, size_t *key_len,
+                          const struct nlattr **mask, size_t *mask_len,
                           const struct nlattr **actions, size_t *actions_len,
                           const struct dpif_flow_stats **stats);
 
index 69d9c34..f07d3c0 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.
@@ -82,6 +82,7 @@ static struct vlog_rate_limit error_rl = VLOG_RATE_LIMIT_INIT(60, 5);
 static void log_flow_message(const struct dpif *dpif, int error,
                              const char *operation,
                              const struct nlattr *key, size_t key_len,
+                             const struct nlattr *mask, size_t mask_len,
                              const struct dpif_flow_stats *stats,
                              const struct nlattr *actions, size_t actions_len);
 static void log_operation(const struct dpif *, const char *operation,
@@ -216,7 +217,7 @@ dp_enumerate_names(const char *type, struct sset *names)
 
     if (error) {
         VLOG_WARN("failed to enumerate %s datapaths: %s", dpif_class->type,
-                   strerror(error));
+                   ovs_strerror(error));
     }
 
     return error;
@@ -311,10 +312,11 @@ dpif_create_and_open(const char *name, const char *type, struct dpif **dpifp)
         error = dpif_open(name, type, dpifp);
         if (error) {
             VLOG_WARN("datapath %s already exists but cannot be opened: %s",
-                      name, strerror(error));
+                      name, ovs_strerror(error));
         }
     } else if (error) {
-        VLOG_WARN("failed to create datapath %s: %s", name, strerror(error));
+        VLOG_WARN("failed to create datapath %s: %s",
+                  name, ovs_strerror(error));
     }
     return error;
 }
@@ -435,18 +437,18 @@ dpif_port_open_type(const char *datapath_type, const char *port_type)
 }
 
 /* Attempts to add 'netdev' as a port on 'dpif'.  If 'port_nop' is
- * non-null and its value is not UINT32_MAX, then attempts to use the
+ * non-null and its value is not ODPP_NONE, 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 UINT32_MAX (if 'port_nop' is
+ * errno value and sets '*port_nop' to ODPP_NONE (if 'port_nop' is
  * non-null). */
 int
-dpif_port_add(struct dpif *dpif, struct netdev *netdev, uint32_t *port_nop)
+dpif_port_add(struct dpif *dpif, struct netdev *netdev, odp_port_t *port_nop)
 {
     const char *netdev_name = netdev_get_name(netdev);
-    uint32_t port_no = UINT32_MAX;
+    odp_port_t port_no = ODPP_NONE;
     int error;
 
     COVERAGE_INC(dpif_port_add);
@@ -461,8 +463,8 @@ dpif_port_add(struct dpif *dpif, struct netdev *netdev, uint32_t *port_nop)
                     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 = UINT32_MAX;
+                     dpif_name(dpif), netdev_name, ovs_strerror(error));
+        port_no = ODPP_NONE;
     }
     if (port_nop) {
         *port_nop = port_no;
@@ -473,7 +475,7 @@ dpif_port_add(struct dpif *dpif, struct netdev *netdev, uint32_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, uint32_t port_no)
+dpif_port_del(struct dpif *dpif, odp_port_t port_no)
 {
     int error;
 
@@ -518,7 +520,7 @@ 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));
+                     dpif_name(dpif), devname, ovs_strerror(error));
     }
 
     return !error;
@@ -531,7 +533,7 @@ dpif_port_exists(const struct dpif *dpif, const char *devname)
  * 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, uint32_t port_no,
+dpif_port_query_by_number(const struct dpif *dpif, odp_port_t port_no,
                           struct dpif_port *port)
 {
     int error = dpif->dpif_class->port_query_by_number(dpif, port_no, port);
@@ -541,7 +543,7 @@ dpif_port_query_by_number(const struct dpif *dpif, uint32_t port_no,
     } else {
         memset(port, 0, sizeof *port);
         VLOG_WARN_RL(&error_rl, "%s: failed to query port %"PRIu32": %s",
-                     dpif_name(dpif), port_no, strerror(error));
+                     dpif_name(dpif), port_no, ovs_strerror(error));
     }
     return error;
 }
@@ -570,14 +572,14 @@ dpif_port_query_by_name(const struct dpif *dpif, const char *devname,
         VLOG_RL(&error_rl,
                 error == ENOENT || error == ENODEV ? VLL_DBG : VLL_WARN,
                 "%s: failed to query port %s: %s",
-                dpif_name(dpif), devname, strerror(error));
+                dpif_name(dpif), devname, ovs_strerror(error));
     }
     return error;
 }
 
 /* Returns one greater than the maximum port number accepted in flow
  * actions. */
-int
+odp_port_t
 dpif_get_max_ports(const struct dpif *dpif)
 {
     return dpif->dpif_class->get_max_ports(dpif);
@@ -587,7 +589,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 UINT32_MAX is a special case: it returns a reserved PID, not
+ * A 'port_no' of ODPP_NONE 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
@@ -596,7 +598,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, uint32_t port_no)
+dpif_port_get_pid(const struct dpif *dpif, odp_port_t port_no)
 {
     return (dpif->dpif_class->port_get_pid
             ? (dpif->dpif_class->port_get_pid)(dpif, port_no)
@@ -608,7 +610,7 @@ dpif_port_get_pid(const struct dpif *dpif, uint32_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, uint32_t port_no,
+dpif_port_get_name(struct dpif *dpif, odp_port_t port_no,
                    char *name, size_t name_size)
 {
     struct dpif_port port;
@@ -809,8 +811,8 @@ dpif_flow_get(const struct dpif *dpif,
             actions = NULL;
             actions_len = 0;
         }
-        log_flow_message(dpif, error, "flow_get", key, key_len, stats,
-                         actions, actions_len);
+        log_flow_message(dpif, error, "flow_get", key, key_len,
+                         NULL, 0, stats, actions, actions_len);
     }
     return error;
 }
@@ -833,9 +835,11 @@ dpif_flow_put__(struct dpif *dpif, const struct dpif_flow_put *put)
 }
 
 /* Adds or modifies a flow in 'dpif'.  The flow is specified by the Netlink
- * attributes with types OVS_KEY_ATTR_* in the 'key_len' bytes starting at
- * 'key'.  The associated actions are specified by the Netlink attributes with
- * types OVS_ACTION_ATTR_* in the 'actions_len' bytes starting at 'actions'.
+ * attribute OVS_FLOW_ATTR_KEY with types OVS_KEY_ATTR_* in the 'key_len' bytes
+ * starting at 'key', and OVS_FLOW_ATTR_MASK with types of OVS_KEY_ATTR_* in the
+ * 'mask_len' bytes starting at 'mask'. The associated actions are specified by
+ * the Netlink attributes with types OVS_ACTION_ATTR_* in the 'actions_len'
+ * bytes starting at 'actions'.
  *
  * - If the flow's key does not exist in 'dpif', then the flow will be added if
  *   'flags' includes DPIF_FP_CREATE.  Otherwise the operation will fail with
@@ -855,6 +859,7 @@ dpif_flow_put__(struct dpif *dpif, const struct dpif_flow_put *put)
 int
 dpif_flow_put(struct dpif *dpif, enum dpif_flow_put_flags flags,
               const struct nlattr *key, size_t key_len,
+              const struct nlattr *mask, size_t mask_len,
               const struct nlattr *actions, size_t actions_len,
               struct dpif_flow_stats *stats)
 {
@@ -863,6 +868,8 @@ dpif_flow_put(struct dpif *dpif, enum dpif_flow_put_flags flags,
     put.flags = flags;
     put.key = key;
     put.key_len = key_len;
+    put.mask = mask;
+    put.mask_len = mask_len;
     put.actions = actions;
     put.actions_len = actions_len;
     put.stats = stats;
@@ -938,6 +945,7 @@ dpif_flow_dump_start(struct dpif_flow_dump *dump, const struct dpif *dpif)
 bool
 dpif_flow_dump_next(struct dpif_flow_dump *dump,
                     const struct nlattr **key, size_t *key_len,
+                    const struct nlattr **mask, size_t *mask_len,
                     const struct nlattr **actions, size_t *actions_len,
                     const struct dpif_flow_stats **stats)
 {
@@ -947,6 +955,7 @@ dpif_flow_dump_next(struct dpif_flow_dump *dump,
     if (!error) {
         error = dpif->dpif_class->flow_dump_next(dpif, dump->state,
                                                  key, key_len,
+                                                 mask, mask_len,
                                                  actions, actions_len,
                                                  stats);
         if (error) {
@@ -958,6 +967,10 @@ dpif_flow_dump_next(struct dpif_flow_dump *dump,
             *key = NULL;
             *key_len = 0;
         }
+        if (mask) {
+            *mask = NULL;
+            *mask_len = 0;
+        }
         if (actions) {
             *actions = NULL;
             *actions_len = 0;
@@ -972,6 +985,7 @@ dpif_flow_dump_next(struct dpif_flow_dump *dump,
         } else if (should_log_flow_message(error)) {
             log_flow_message(dpif, error, "flow_dump",
                              key ? *key : NULL, key ? *key_len : 0,
+                             mask ? *mask : NULL, mask ? *mask_len : 0,
                              stats ? *stats : NULL, actions ? *actions : NULL,
                              actions ? *actions_len : 0);
         }
@@ -1244,14 +1258,19 @@ log_operation(const struct dpif *dpif, const char *operation, int error)
                      dpif_name(dpif), operation, ofperr_get_name(error));
     } else {
         VLOG_WARN_RL(&error_rl, "%s: %s failed (%s)",
-                     dpif_name(dpif), operation, strerror(error));
+                     dpif_name(dpif), operation, ovs_strerror(error));
     }
 }
 
 static enum vlog_level
 flow_message_log_level(int error)
 {
-    return error ? VLL_WARN : VLL_DBG;
+    /* If flows arrive in a batch, userspace may push down multiple
+     * unique flow definitions that overlap when wildcards are applied.
+     * Kernels that support flow wildcarding will reject these flows as
+     * duplicates (EEXIST), so lower the log level to debug for these
+     * types of messages. */
+    return (error && error != EEXIST) ? VLL_WARN : VLL_DBG;
 }
 
 static bool
@@ -1264,6 +1283,7 @@ should_log_flow_message(int error)
 static void
 log_flow_message(const struct dpif *dpif, int error, const char *operation,
                  const struct nlattr *key, size_t key_len,
+                 const struct nlattr *mask, size_t mask_len,
                  const struct dpif_flow_stats *stats,
                  const struct nlattr *actions, size_t actions_len)
 {
@@ -1274,9 +1294,9 @@ log_flow_message(const struct dpif *dpif, int error, const char *operation,
     }
     ds_put_format(&ds, "%s ", operation);
     if (error) {
-        ds_put_format(&ds, "(%s) ", strerror(error));
+        ds_put_format(&ds, "(%s) ", ovs_strerror(error));
     }
-    odp_flow_key_format(key, key_len, &ds);
+    odp_flow_format(key, key_len, mask, mask_len, &ds);
     if (stats) {
         ds_put_cstr(&ds, ", ");
         dpif_flow_stats_format(stats, &ds);
@@ -1308,8 +1328,8 @@ log_flow_put_message(struct dpif *dpif, const struct dpif_flow_put *put,
             ds_put_cstr(&s, "[zero]");
         }
         log_flow_message(dpif, error, ds_cstr(&s),
-                         put->key, put->key_len, put->stats,
-                         put->actions, put->actions_len);
+                         put->key, put->key_len, put->mask, put->mask_len,
+                         put->stats, put->actions, put->actions_len);
         ds_destroy(&s);
     }
 }
@@ -1320,7 +1340,7 @@ log_flow_del_message(struct dpif *dpif, const struct dpif_flow_del *del,
 {
     if (should_log_flow_message(error)) {
         log_flow_message(dpif, error, "flow_del", del->key, del->key_len,
-                         !error ? del->stats : NULL, NULL, 0);
+                         NULL, 0, !error ? del->stats : NULL, NULL, 0);
     }
 }
 
@@ -1337,7 +1357,7 @@ log_execute_message(struct dpif *dpif, const struct dpif_execute *execute,
         ds_put_format(&ds, "%s: execute ", dpif_name(dpif));
         format_odp_actions(&ds, execute->actions, execute->actions_len);
         if (error) {
-            ds_put_format(&ds, " failed (%s)", strerror(error));
+            ds_put_format(&ds, " failed (%s)", ovs_strerror(error));
         }
         ds_put_format(&ds, " on packet %s", packet);
         vlog(THIS_MODULE, error ? VLL_WARN : VLL_DBG, "%s", ds_cstr(&ds));
index fd05b2f..7f1b6c9 100644 (file)
@@ -377,8 +377,8 @@ int dpif_get_dp_stats(const struct dpif *, struct dpif_dp_stats *);
 
 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);
+int dpif_port_add(struct dpif *, struct netdev *, odp_port_t *port_nop);
+int dpif_port_del(struct dpif *, odp_port_t port_no);
 
 /* A port within a datapath.
  *
@@ -386,19 +386,19 @@ int dpif_port_del(struct dpif *, uint32_t port_no);
 struct dpif_port {
     char *name;                 /* Network device name, e.g. "eth0". */
     char *type;                 /* Network device type, e.g. "system". */
-    uint32_t port_no;           /* Port number within datapath. */
+    odp_port_t port_no;         /* Port number within datapath. */
 };
 void dpif_port_clone(struct dpif_port *, const struct dpif_port *);
 void dpif_port_destroy(struct dpif_port *);
 bool dpif_port_exists(const struct dpif *dpif, const char *devname);
-int dpif_port_query_by_number(const struct dpif *, uint32_t port_no,
+int dpif_port_query_by_number(const struct dpif *, odp_port_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 *, uint32_t port_no,
+int dpif_port_get_name(struct dpif *, odp_port_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 *, uint32_t port_no);
+odp_port_t dpif_get_max_ports(const struct dpif *);
+uint32_t dpif_port_get_pid(const struct dpif *, odp_port_t port_no);
 
 struct dpif_port_dump {
     const struct dpif *dpif;
@@ -447,6 +447,7 @@ enum dpif_flow_put_flags {
 int dpif_flow_flush(struct dpif *);
 int dpif_flow_put(struct dpif *, enum dpif_flow_put_flags,
                   const struct nlattr *key, size_t key_len,
+                  const struct nlattr *mask, size_t mask_len,
                   const struct nlattr *actions, size_t actions_len,
                   struct dpif_flow_stats *);
 int dpif_flow_del(struct dpif *,
@@ -464,6 +465,7 @@ struct dpif_flow_dump {
 void dpif_flow_dump_start(struct dpif_flow_dump *, const struct dpif *);
 bool dpif_flow_dump_next(struct dpif_flow_dump *,
                          const struct nlattr **key, size_t *key_len,
+                         const struct nlattr **mask, size_t *mask_len,
                          const struct nlattr **actions, size_t *actions_len,
                          const struct dpif_flow_stats **);
 int dpif_flow_dump_done(struct dpif_flow_dump *);
@@ -492,6 +494,8 @@ struct dpif_flow_put {
     enum dpif_flow_put_flags flags; /* DPIF_FP_*. */
     const struct nlattr *key;       /* Flow to put. */
     size_t key_len;                 /* Length of 'key' in bytes. */
+    const struct nlattr *mask;      /* Mask to put. */
+    size_t mask_len;                /* Length of 'mask' in bytes. */
     const struct nlattr *actions;   /* Actions to perform on flow. */
     size_t actions_len;             /* Length of 'actions' in bytes. */
 
index 1354de6..0918037 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.
@@ -33,5 +33,6 @@ dummy_enable(bool override)
     netdev_dummy_register(override);
     dpif_dummy_register(override);
     timeval_dummy_register();
+    vlandev_dummy_enable();
 }
 
index 2204170..b20519a 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.
@@ -26,5 +26,6 @@ void dummy_enable(bool override);
 void dpif_dummy_register(bool override);
 void netdev_dummy_register(bool override);
 void timeval_dummy_register(void);
+void vlandev_dummy_enable(void);
 
 #endif /* dummy.h */
index ba9aa6d..3cccb5c 100644 (file)
@@ -358,48 +358,50 @@ void
 ds_put_hex_dump(struct ds *ds, const void *buf_, size_t size,
                 uintptr_t ofs, bool ascii)
 {
-  const uint8_t *buf = buf_;
-  const size_t per_line = 16; /* Maximum bytes per line. */
-
-  while (size > 0)
-    {
-      size_t start, end, n;
-      size_t i;
-
-      /* Number of bytes on this line. */
-      start = ofs % per_line;
-      end = per_line;
-      if (end - start > size)
-        end = start + size;
-      n = end - start;
-
-      /* Print line. */
-      ds_put_format(ds, "%08jx  ", (uintmax_t) ROUND_DOWN(ofs, per_line));
-      for (i = 0; i < start; i++)
-        ds_put_format(ds, "   ");
-      for (; i < end; i++)
-        ds_put_format(ds, "%02hhx%c",
-                buf[i - start], i == per_line / 2 - 1? '-' : ' ');
-      if (ascii)
-        {
-          for (; i < per_line; i++)
+    const uint8_t *buf = buf_;
+    const size_t per_line = 16; /* Maximum bytes per line. */
+
+    while (size > 0) {
+        size_t start, end, n;
+        size_t i;
+
+        /* Number of bytes on this line. */
+        start = ofs % per_line;
+        end = per_line;
+        if (end - start > size)
+            end = start + size;
+        n = end - start;
+
+        /* Print line. */
+        ds_put_format(ds, "%08jx  ", (uintmax_t) ROUND_DOWN(ofs, per_line));
+        for (i = 0; i < start; i++) {
             ds_put_format(ds, "   ");
-          ds_put_format(ds, "|");
-          for (i = 0; i < start; i++)
-            ds_put_format(ds, " ");
-          for (; i < end; i++) {
-              int c = buf[i - start];
-              ds_put_char(ds, c >= 32 && c < 127 ? c : '.');
-          }
-          for (; i < per_line; i++)
-            ds_put_format(ds, " ");
-          ds_put_format(ds, "|");
         }
-      ds_put_format(ds, "\n");
+        for (; i < end; i++) {
+            ds_put_format(ds, "%02hhx%c",
+                          buf[i - start], i == per_line / 2 - 1? '-' : ' ');
+        }
+        if (ascii) {
+            for (; i < per_line; i++)
+                ds_put_format(ds, "   ");
+            ds_put_format(ds, "|");
+            for (i = 0; i < start; i++)
+                ds_put_format(ds, " ");
+            for (; i < end; i++) {
+                int c = buf[i - start];
+                ds_put_char(ds, c >= 32 && c < 127 ? c : '.');
+            }
+            for (; i < per_line; i++)
+                ds_put_format(ds, " ");
+            ds_put_format(ds, "|");
+        } else {
+            ds_chomp(ds, ' ');
+        }
+        ds_put_format(ds, "\n");
 
-      ofs += n;
-      buf += n;
-      size -= n;
+        ofs += n;
+        buf += n;
+        size -= n;
     }
 }
 
index c1f0886..02f56e0 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008, 2009, 2010, 2011 Nicira, Inc.
+/* Copyright (c) 2008, 2009, 2010, 2011, 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.
@@ -39,7 +39,7 @@ get_entropy(void *buffer, size_t n)
 
     fd = open(urandom, O_RDONLY);
     if (fd < 0) {
-        VLOG_ERR("%s: open failed (%s)", urandom, strerror(errno));
+        VLOG_ERR("%s: open failed (%s)", urandom, ovs_strerror(errno));
         return errno ? errno : EINVAL;
     }
 
index 66c0445..1770457 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.
@@ -84,7 +84,7 @@ fatal_signal_init(void)
             xsigaction(sig_nr, NULL, &old_sa);
             if (old_sa.sa_handler == SIG_DFL
                 && signal(sig_nr, fatal_signal_handler) == SIG_ERR) {
-                VLOG_FATAL("signal failed (%s)", strerror(errno));
+                VLOG_FATAL("signal failed (%s)", ovs_strerror(errno));
             }
         }
         atexit(atexit_handler);
@@ -155,8 +155,10 @@ fatal_signal_run(void)
 
     sig_nr = stored_sig_nr;
     if (sig_nr != SIG_ATOMIC_MAX) {
+        char namebuf[SIGNAL_NAME_BUFSIZE];
+
         VLOG_WARN("terminating with signal %d (%s)",
-                  (int)sig_nr, signal_name(sig_nr));
+                  (int)sig_nr, signal_name(sig_nr, namebuf, sizeof namebuf));
         call_hooks(sig_nr);
 
         /* Re-raise the signal with the default handling so that the program
@@ -236,7 +238,7 @@ fatal_signal_unlink_file_now(const char *file)
 {
     int error = unlink(file) ? errno : 0;
     if (error) {
-        VLOG_WARN("could not unlink \"%s\" (%s)", file, strerror(error));
+        VLOG_WARN("could not unlink \"%s\" (%s)", file, ovs_strerror(error));
     }
 
     fatal_signal_remove_file_to_unlink(file);
index 3c12984..1a5084b 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.
@@ -337,7 +337,7 @@ invalid:
 }
 
 /* Initializes 'flow' members from 'packet', 'skb_priority', 'tnl', and
- * 'ofp_in_port'.
+ * 'in_port'.
  *
  * Initializes 'packet' header pointers as follows:
  *
@@ -357,7 +357,7 @@ invalid:
  */
 void
 flow_extract(struct ofpbuf *packet, uint32_t skb_priority, uint32_t skb_mark,
-             const struct flow_tnl *tnl, uint16_t ofp_in_port,
+             const struct flow_tnl *tnl, const union flow_in_port *in_port,
              struct flow *flow)
 {
     struct ofpbuf b = *packet;
@@ -371,7 +371,9 @@ flow_extract(struct ofpbuf *packet, uint32_t skb_priority, uint32_t skb_mark,
         ovs_assert(tnl != &flow->tunnel);
         flow->tunnel = *tnl;
     }
-    flow->in_port = ofp_in_port;
+    if (in_port) {
+        flow->in_port = *in_port;
+    }
     flow->skb_priority = skb_priority;
     flow->skb_mark = skb_mark;
 
@@ -498,7 +500,7 @@ flow_get_metadata(const struct flow *flow, struct flow_metadata *fmd)
     fmd->tun_dst = flow->tunnel.ip_dst;
     fmd->metadata = flow->metadata;
     memcpy(fmd->regs, flow->regs, sizeof fmd->regs);
-    fmd->in_port = flow->in_port;
+    fmd->in_port = flow->in_port.ofp_port;
 }
 
 char *
@@ -604,13 +606,13 @@ flow_wildcards_is_catchall(const struct flow_wildcards *wc)
     return true;
 }
 
-/* Initializes 'dst' as the combination of wildcards in 'src1' and 'src2'.
- * That is, a bit or a field is wildcarded in 'dst' if it is wildcarded in
- * 'src1' or 'src2' or both.  */
+/* Sets 'dst' as the bitwise AND of wildcards in 'src1' and 'src2'.
+ * That is, a bit or a field is wildcarded in 'dst' if it is wildcarded
+ * in 'src1' or 'src2' or both.  */
 void
-flow_wildcards_combine(struct flow_wildcards *dst,
-                       const struct flow_wildcards *src1,
-                       const struct flow_wildcards *src2)
+flow_wildcards_and(struct flow_wildcards *dst,
+                   const struct flow_wildcards *src1,
+                   const struct flow_wildcards *src2)
 {
     uint32_t *dst_u32 = (uint32_t *) &dst->masks;
     const uint32_t *src1_u32 = (const uint32_t *) &src1->masks;
@@ -622,6 +624,51 @@ flow_wildcards_combine(struct flow_wildcards *dst,
     }
 }
 
+/* Sets 'dst' as the bitwise OR of wildcards in 'src1' and 'src2'.  That
+ * is, a bit or a field is wildcarded in 'dst' if it is neither
+ * wildcarded in 'src1' nor 'src2'. */
+void
+flow_wildcards_or(struct flow_wildcards *dst,
+                  const struct flow_wildcards *src1,
+                  const struct flow_wildcards *src2)
+{
+    uint32_t *dst_u32 = (uint32_t *) &dst->masks;
+    const uint32_t *src1_u32 = (const uint32_t *) &src1->masks;
+    const uint32_t *src2_u32 = (const uint32_t *) &src2->masks;
+    size_t i;
+
+    for (i = 0; i < FLOW_U32S; i++) {
+        dst_u32[i] = src1_u32[i] | src2_u32[i];
+    }
+}
+
+/* Perform a bitwise OR of miniflow 'src' flow data with the equivalent
+ * fields in 'dst', storing the result in 'dst'. */
+static void
+flow_union_with_miniflow(struct flow *dst, const struct miniflow *src)
+{
+    uint32_t *dst_u32 = (uint32_t *) dst;
+    int ofs;
+    int i;
+
+    ofs = 0;
+    for (i = 0; i < MINI_N_MAPS; i++) {
+        uint32_t map;
+
+        for (map = src->map[i]; map; map = zero_rightmost_1bit(map)) {
+            dst_u32[raw_ctz(map) + i * 32] |= src->values[ofs++];
+        }
+    }
+}
+
+/* Fold minimask 'mask''s wildcard mask into 'wc's wildcard mask. */
+void
+flow_wildcards_fold_minimask(struct flow_wildcards *wc,
+                             const struct minimask *mask)
+{
+    flow_union_with_miniflow(&wc->masks, &mask->masks);
+}
+
 /* Returns a hash of the wildcards in 'wc'. */
 uint32_t
 flow_wildcards_hash(const struct flow_wildcards *wc, uint32_t basis)
@@ -732,6 +779,39 @@ flow_hash_symmetric_l4(const struct flow *flow, uint32_t basis)
     return jhash_bytes(&fields, sizeof fields, basis);
 }
 
+/* Masks the fields in 'wc' that are used by the flow hash 'fields'. */
+void
+flow_mask_hash_fields(const struct flow *flow, struct flow_wildcards *wc,
+                      enum nx_hash_fields fields)
+{
+    switch (fields) {
+    case NX_HASH_FIELDS_ETH_SRC:
+        memset(&wc->masks.dl_src, 0xff, sizeof wc->masks.dl_src);
+        break;
+
+    case NX_HASH_FIELDS_SYMMETRIC_L4:
+        memset(&wc->masks.dl_src, 0xff, sizeof wc->masks.dl_src);
+        memset(&wc->masks.dl_dst, 0xff, sizeof wc->masks.dl_dst);
+        if (flow->dl_type == htons(ETH_TYPE_IP)) {
+            memset(&wc->masks.nw_src, 0xff, sizeof wc->masks.nw_src);
+            memset(&wc->masks.nw_dst, 0xff, sizeof wc->masks.nw_dst);
+        } else {
+            memset(&wc->masks.ipv6_src, 0xff, sizeof wc->masks.ipv6_src);
+            memset(&wc->masks.ipv6_dst, 0xff, sizeof wc->masks.ipv6_dst);
+        }
+        if (is_ip_any(flow)) {
+            memset(&wc->masks.nw_proto, 0xff, sizeof wc->masks.nw_proto);
+            memset(&wc->masks.tp_src, 0xff, sizeof wc->masks.tp_src);
+            memset(&wc->masks.tp_dst, 0xff, sizeof wc->masks.tp_dst);
+        }
+        wc->masks.vlan_tci |= htons(VLAN_VID_MASK | VLAN_CFI);
+        break;
+
+    default:
+        NOT_REACHED();
+    }
+}
+
 /* Hashes the portions of 'flow' designated by 'fields'. */
 uint32_t
 flow_hash_fields(const struct flow *flow, enum nx_hash_fields fields,
@@ -768,6 +848,24 @@ flow_hash_fields_valid(enum nx_hash_fields fields)
         || fields == NX_HASH_FIELDS_SYMMETRIC_L4;
 }
 
+/* Returns a hash value for the bits of 'flow' that are active based on
+ * 'wc', given 'basis'. */
+uint32_t
+flow_hash_in_wildcards(const struct flow *flow,
+                       const struct flow_wildcards *wc, uint32_t basis)
+{
+    const uint32_t *wc_u32 = (const uint32_t *) &wc->masks;
+    const uint32_t *flow_u32 = (const uint32_t *) flow;
+    uint32_t hash;
+    size_t i;
+
+    hash = basis;
+    for (i = 0; i < FLOW_U32S; i++) {
+        hash = mhash_add(hash, flow_u32[i] & wc_u32[i]);
+    }
+    return mhash_finish(hash, 4 * FLOW_U32S);
+}
+
 /* Sets the VLAN VID that 'flow' matches to 'vid', which is interpreted as an
  * OpenFlow 1.0 "dl_vlan" value:
  *
@@ -1024,20 +1122,8 @@ miniflow_destroy(struct miniflow *flow)
 void
 miniflow_expand(const struct miniflow *src, struct flow *dst)
 {
-    uint32_t *dst_u32 = (uint32_t *) dst;
-    int ofs;
-    int i;
-
-    memset(dst_u32, 0, sizeof *dst);
-
-    ofs = 0;
-    for (i = 0; i < MINI_N_MAPS; i++) {
-        uint32_t map;
-
-        for (map = src->map[i]; map; map = zero_rightmost_1bit(map)) {
-            dst_u32[raw_ctz(map) + i * 32] = src->values[ofs++];
-        }
-    }
+    memset(dst, 0, sizeof *dst);
+    flow_union_with_miniflow(dst, src);
 }
 
 static const uint32_t *
index 0fdf4ef..7c3654b 100644 (file)
@@ -68,6 +68,14 @@ struct flow_tnl {
     uint8_t ip_ttl;
 };
 
+/* Unfortunately, a "struct flow" sometimes has to handle OpenFlow port
+ * numbers and other times datapath (dpif) port numbers.  This union allows
+ * access to both. */
+union flow_in_port {
+    ofp_port_t ofp_port;
+    odp_port_t odp_port;
+};
+
 /*
 * A flow in the network.
 *
@@ -87,9 +95,7 @@ struct flow {
     ovs_be32 nw_src;            /* IPv4 source address. */
     ovs_be32 nw_dst;            /* IPv4 destination address. */
     ovs_be32 ipv6_label;        /* IPv6 flow label. */
-    uint32_t in_port;           /* Input port. OpenFlow port number
-                                   unless in DPIF code, in which case it
-                                   is the datapath port number. */
+    union flow_in_port in_port; /* Input port.*/
     uint32_t skb_mark;          /* Packet mark. */
     ovs_be32 mpls_lse;          /* MPLS label stack entry. */
     uint16_t mpls_depth;        /* Depth of MPLS stack. */
@@ -122,11 +128,12 @@ struct flow_metadata {
     ovs_be32 tun_dst;                /* Tunnel outer IPv4 dst addr */
     ovs_be64 metadata;               /* OpenFlow 1.1+ metadata field. */
     uint32_t regs[FLOW_N_REGS];      /* Registers. */
-    uint16_t in_port;                /* OpenFlow port or zero. */
+    ofp_port_t in_port;              /* OpenFlow port or zero. */
 };
 
 void flow_extract(struct ofpbuf *, uint32_t priority, uint32_t mark,
-                  const struct flow_tnl *, uint16_t in_port, struct flow *);
+                  const struct flow_tnl *, const union flow_in_port *in_port,
+                  struct flow *);
 
 void flow_zero_wildcards(struct flow *, const struct flow_wildcards *);
 void flow_get_metadata(const struct flow *, struct flow_metadata *);
@@ -170,6 +177,54 @@ flow_hash(const struct flow *flow, uint32_t basis)
     return hash_words((const uint32_t *) flow, sizeof *flow / 4, basis);
 }
 
+static inline uint16_t
+ofp_to_u16(ofp_port_t ofp_port)
+{
+    return (OVS_FORCE uint16_t) ofp_port;
+}
+
+static inline uint32_t
+odp_to_u32(odp_port_t odp_port)
+{
+    return (OVS_FORCE uint32_t) odp_port;
+}
+
+static inline uint32_t
+ofp11_to_u32(ofp11_port_t ofp11_port)
+{
+    return (OVS_FORCE uint32_t) ofp11_port;
+}
+
+static inline ofp_port_t
+u16_to_ofp(uint16_t port)
+{
+    return OFP_PORT_C(port);
+}
+
+static inline odp_port_t
+u32_to_odp(uint32_t port)
+{
+    return ODP_PORT_C(port);
+}
+
+static inline ofp11_port_t
+u32_to_ofp11(uint32_t port)
+{
+    return OFP11_PORT_C(port);
+}
+
+static inline uint32_t
+hash_ofp_port(ofp_port_t ofp_port)
+{
+    return hash_int(ofp_to_u16(ofp_port), 0);
+}
+
+static inline uint32_t
+hash_odp_port(odp_port_t odp_port)
+{
+    return hash_int(odp_to_u32(odp_port), 0);
+}
+
 uint32_t flow_hash_in_minimask(const struct flow *, const struct minimask *,
                                uint32_t basis);
 \f
@@ -190,22 +245,34 @@ bool flow_wildcards_is_catchall(const struct flow_wildcards *);
 void flow_wildcards_set_reg_mask(struct flow_wildcards *,
                                  int idx, uint32_t mask);
 
-void flow_wildcards_combine(struct flow_wildcards *dst,
-                            const struct flow_wildcards *src1,
-                            const struct flow_wildcards *src2);
+void flow_wildcards_and(struct flow_wildcards *dst,
+                        const struct flow_wildcards *src1,
+                        const struct flow_wildcards *src2);
+void flow_wildcards_or(struct flow_wildcards *dst,
+                       const struct flow_wildcards *src1,
+                       const struct flow_wildcards *src2);
 bool flow_wildcards_has_extra(const struct flow_wildcards *,
                               const struct flow_wildcards *);
 
+void flow_wildcards_fold_minimask(struct flow_wildcards *,
+                                  const struct minimask *);
+
 uint32_t flow_wildcards_hash(const struct flow_wildcards *, uint32_t basis);
 bool flow_wildcards_equal(const struct flow_wildcards *,
                           const struct flow_wildcards *);
 uint32_t flow_hash_symmetric_l4(const struct flow *flow, uint32_t basis);
 
+void flow_mask_hash_fields(const struct flow *, struct flow_wildcards *,
+                           enum nx_hash_fields);
 uint32_t flow_hash_fields(const struct flow *, enum nx_hash_fields,
                           uint16_t basis);
 const char *flow_hash_fields_to_str(enum nx_hash_fields);
 bool flow_hash_fields_valid(enum nx_hash_fields);
 
+uint32_t flow_hash_in_wildcards(const struct flow *,
+                                const struct flow_wildcards *,
+                                uint32_t basis);
+
 bool flow_equal_except(const struct flow *a, const struct flow *b,
                        const struct flow_wildcards *);
 \f
diff --git a/lib/hindex.c b/lib/hindex.c
new file mode 100644 (file)
index 0000000..dc0f1b7
--- /dev/null
@@ -0,0 +1,324 @@
+/*
+ * Copyright (c) 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.
+ * 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 "hindex.h"
+#include "coverage.h"
+
+static bool hindex_node_is_body(const struct hindex_node *);
+static bool hindex_node_is_head(const struct hindex_node *);
+static void hindex_resize(struct hindex *, size_t new_mask);
+static size_t hindex_calc_mask(size_t capacity);
+
+COVERAGE_DEFINE(hindex_pathological);
+COVERAGE_DEFINE(hindex_expand);
+COVERAGE_DEFINE(hindex_shrink);
+COVERAGE_DEFINE(hindex_reserve);
+
+/* Initializes 'hindex' as an empty hash index. */
+void
+hindex_init(struct hindex *hindex)
+{
+    hindex->buckets = &hindex->one;
+    hindex->one = NULL;
+    hindex->mask = 0;
+    hindex->n_unique = 0;
+}
+
+/* Frees memory reserved by 'hindex'.  It is the client's responsibility to
+ * free the nodes themselves, if necessary. */
+void
+hindex_destroy(struct hindex *hindex)
+{
+    if (hindex && hindex->buckets != &hindex->one) {
+        free(hindex->buckets);
+    }
+}
+
+/* Removes all node from 'hindex', leaving it ready to accept more nodes.  Does
+ * not free memory allocated for 'hindex'.
+ *
+ * This function is appropriate when 'hindex' will soon have about as many
+ * elements as it before.  If 'hindex' will likely have fewer elements than
+ * before, use hindex_destroy() followed by hindex_clear() to save memory and
+ * iteration time. */
+void
+hindex_clear(struct hindex *hindex)
+{
+    if (hindex->n_unique > 0) {
+        hindex->n_unique = 0;
+        memset(hindex->buckets, 0,
+               (hindex->mask + 1) * sizeof *hindex->buckets);
+    }
+}
+
+/* Exchanges hash indexes 'a' and 'b'. */
+void
+hindex_swap(struct hindex *a, struct hindex *b)
+{
+    struct hindex tmp = *a;
+    *a = *b;
+    *b = tmp;
+    hindex_moved(a);
+    hindex_moved(b);
+}
+
+/* Adjusts 'hindex' to compensate for having moved position in memory (e.g. due
+ * to realloc()). */
+void
+hindex_moved(struct hindex *hindex)
+{
+    if (!hindex->mask) {
+        hindex->buckets = &hindex->one;
+    }
+}
+
+/* Expands 'hindex', if necessary, to optimize the performance of searches. */
+void
+hindex_expand(struct hindex *hindex)
+{
+    size_t new_mask = hindex_calc_mask(hindex->n_unique);
+    if (new_mask > hindex->mask) {
+        COVERAGE_INC(hindex_expand);
+        hindex_resize(hindex, new_mask);
+    }
+}
+
+/* Shrinks 'hindex', if necessary, to optimize the performance of iteration. */
+void
+hindex_shrink(struct hindex *hindex)
+{
+    size_t new_mask = hindex_calc_mask(hindex->n_unique);
+    if (new_mask < hindex->mask) {
+        COVERAGE_INC(hindex_shrink);
+        hindex_resize(hindex, new_mask);
+    }
+}
+
+/* Expands 'hindex', if necessary, to optimize the performance of searches when
+ * it has up to 'n' unique hashes.  (But iteration will be slow in a hash index
+ * whose allocated capacity is much higher than its current number of
+ * nodes.)  */
+void
+hindex_reserve(struct hindex *hindex, size_t n)
+{
+    size_t new_mask = hindex_calc_mask(n);
+    if (new_mask > hindex->mask) {
+        COVERAGE_INC(hindex_reserve);
+        hindex_resize(hindex, new_mask);
+    }
+}
+
+/* Inserts 'node', with the given 'hash', into 'hindex'.  Never automatically
+ * expands 'hindex' (use hindex_insert() instead if you want that). */
+void
+hindex_insert_fast(struct hindex *hindex,
+                   struct hindex_node *node, size_t hash)
+{
+    struct hindex_node *head = hindex_node_with_hash(hindex, hash);
+    if (head) {
+        /* 'head' is an existing head with hash == 'hash'.
+         * Insert 'node' as a body node just below 'head'. */
+        node->s = head->s;
+        node->d = head;
+        if (node->s) {
+            node->s->d = node;
+        }
+        head->s = node;
+    } else {
+        /* No existing node has hash 'hash'.  Insert 'node' as a new head in
+         * its bucket. */
+        struct hindex_node **bucket = &hindex->buckets[hash & hindex->mask];
+        node->s = NULL;
+        node->d = *bucket;
+        *bucket = node;
+        hindex->n_unique++;
+    }
+    node->hash = hash;
+}
+
+/* Inserts 'node', with the given 'hash', into 'hindex', and expands 'hindex'
+ * if necessary to optimize search performance. */
+void
+hindex_insert(struct hindex *hindex, struct hindex_node *node, size_t hash)
+{
+    hindex_insert_fast(hindex, node, hash);
+    if (hindex->n_unique / 2 > hindex->mask) {
+        hindex_expand(hindex);
+    }
+}
+
+/* Removes 'node' from 'hindex'.  Does not shrink the hash index; call
+ * hindex_shrink() directly if desired. */
+void
+hindex_remove(struct hindex *hindex, struct hindex_node *node)
+{
+    if (!hindex_node_is_head(node)) {
+        node->d->s = node->s;
+        if (node->s) {
+            node->s->d = node->d;
+        }
+    } else {
+        struct hindex_node **head;
+
+        for (head = &hindex->buckets[node->hash & hindex->mask];
+             (*head)->hash != node->hash;
+             head = &(*head)->d)
+        {
+            continue;
+        }
+
+        if (node->s) {
+            *head = node->s;
+            node->s->d = node->d;
+        } else {
+            *head = node->d;
+            hindex->n_unique--;
+        }
+    }
+}
+\f
+/* Helper functions. */
+
+/* Returns true if 'node', which must be inserted into an hindex, is a "body"
+ * node, that is, it is not reachable from a bucket by following zero or more
+ * 'd' pointers.  Returns false otherwise. */
+static bool
+hindex_node_is_body(const struct hindex_node *node)
+{
+    return node->d && node->d->hash == node->hash;
+}
+
+/* Returns true if 'node', which must be inserted into an hindex, is a "head"
+ * node, that is, if it is reachable from a bucket by following zero or more
+ * 'd' pointers.  Returns false if 'node' is a body node (and therefore one
+ * must follow at least one 's' pointer to reach it). */
+static bool
+hindex_node_is_head(const struct hindex_node *node)
+{
+    return !hindex_node_is_body(node);
+}
+
+/* Reallocates 'hindex''s array of buckets to use bitwise mask 'new_mask'. */
+static void
+hindex_resize(struct hindex *hindex, size_t new_mask)
+{
+    struct hindex tmp;
+    size_t i;
+
+    ovs_assert(is_pow2(new_mask + 1));
+    ovs_assert(new_mask != SIZE_MAX);
+
+    hindex_init(&tmp);
+    if (new_mask) {
+        tmp.buckets = xmalloc(sizeof *tmp.buckets * (new_mask + 1));
+        tmp.mask = new_mask;
+        for (i = 0; i <= tmp.mask; i++) {
+            tmp.buckets[i] = NULL;
+        }
+    }
+    for (i = 0; i <= hindex->mask; i++) {
+        struct hindex_node *node, *next;
+        int count;
+
+        count = 0;
+        for (node = hindex->buckets[i]; node; node = next) {
+            struct hindex_node **head = &tmp.buckets[node->hash & tmp.mask];
+
+            next = node->d;
+            node->d = *head;
+            *head = node;
+            count++;
+        }
+        if (count > 5) {
+            COVERAGE_INC(hindex_pathological);
+        }
+    }
+    tmp.n_unique = hindex->n_unique;
+    hindex_swap(hindex, &tmp);
+    hindex_destroy(&tmp);
+}
+
+/* Returns the bitwise mask to use in struct hindex to support 'capacity'
+ * hindex_nodes with unique hashes. */
+static size_t
+hindex_calc_mask(size_t capacity)
+{
+    size_t mask = capacity / 2;
+    mask |= mask >> 1;
+    mask |= mask >> 2;
+    mask |= mask >> 4;
+    mask |= mask >> 8;
+    mask |= mask >> 16;
+#if SIZE_MAX > UINT32_MAX
+    mask |= mask >> 32;
+#endif
+
+    /* If we need to dynamically allocate buckets we might as well allocate at
+     * least 4 of them. */
+    mask |= (mask & 1) << 1;
+
+    return mask;
+}
+
+/* Returns the head node in 'hindex' with the given 'hash', or a null pointer
+ * if no nodes have that hash value. */
+struct hindex_node *
+hindex_node_with_hash(const struct hindex *hindex, size_t hash)
+{
+    struct hindex_node *node = hindex->buckets[hash & hindex->mask];
+
+    while (node && node->hash != hash) {
+        node = node->d;
+    }
+    return node;
+}
+
+static struct hindex_node *
+hindex_next__(const struct hindex *hindex, size_t start)
+{
+    size_t i;
+    for (i = start; i <= hindex->mask; i++) {
+        struct hindex_node *node = hindex->buckets[i];
+        if (node) {
+            return node;
+        }
+    }
+    return NULL;
+}
+
+/* Returns the first node in 'hindex', in arbitrary order, or a null pointer if
+ * 'hindex' is empty. */
+struct hindex_node *
+hindex_first(const struct hindex *hindex)
+{
+    return hindex_next__(hindex, 0);
+}
+
+/* Returns the next node in 'hindex' following 'node', in arbitrary order, or a
+ * null pointer if 'node' is the last node in 'hindex'.
+ *
+ * If the hash index has been reallocated since 'node' was visited, some nodes
+ * may be skipped or visited twice.  (Removing 'node' from the hash index does
+ * not prevent calling this function, since node->next is preserved, although
+ * freeing 'node' of course does.) */
+struct hindex_node *
+hindex_next(const struct hindex *hindex, const struct hindex_node *node)
+{
+    return (node->s ? node->s
+            : node->d && node->d->hash != node->hash ? node->d
+            : hindex_next__(hindex, (node->hash & hindex->mask) + 1));
+}
diff --git a/lib/hindex.h b/lib/hindex.h
new file mode 100644 (file)
index 0000000..10bf024
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 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.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef HINDEX_H
+#define HINDEX_H 1
+
+/* Hashed multimap.
+ *
+ * hindex is a hash table data structure that gracefully handles duplicates.
+ * With a high-quality hash function, insertion, deletion, and search are O(1)
+ * expected time, regardless of the number of duplicates for a given key.  */
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include "util.h"
+
+/* A hash index node, to embed inside the data structure being indexed.
+ *
+ * Nodes are linked together like this (the boxes are labeled with hash
+ * values):
+ *
+ *             +--------+ d   +--------+ d   +--------+ d
+ *  bucket---> |    6   |---->|   20   |---->|   15   |---->null
+ *             +-|------+     +-|------+     +-|------+
+ *               |    ^         |              |    ^
+ *              s|    |d        |s            s|    |d
+ *               V    |         V              V    |
+ *             +------|-+      null          +------|-+
+ *             |    6   |                    |   15   |
+ *             +-|------+                    +-|------+
+ *               |    ^                        |
+ *              s|    |d                      s|
+ *               V    |                        V
+ *             +------|-+                     null
+ *             |    6   |
+ *             +-|------+
+ *               |
+ *              s|
+ *               V
+ *              null
+ *
+ * The basic usage is:
+ *
+ *     - To visit the unique hash values in the hindex, follow the 'd'
+ *       ("different") pointers starting from each bucket.  The nodes visited
+ *       this way are called "head" nodes, because they are at the head of the
+ *       vertical chains.
+ *
+ *     - To visit the nodes with hash value H, follow the 'd' pointers in the
+ *       appropriate bucket until you find one with hash H, then follow the 's'
+ *       ("same") pointers until you hit a null pointer.  The non-head nodes
+ *       visited this way are called "body" nodes.
+ *
+ *     - The 'd' pointers in body nodes point back to the previous body node
+ *       or, for the first body node, to the head node.  (This makes it
+ *       possible to remove a body node without traversing all the way downward
+ *       from the head).
+ */
+struct hindex_node {
+    /* Hash value. */
+    size_t hash;
+
+    /* In a head node, the next head node (with a hash different from this
+     * node), or NULL if this is the last node in this bucket.
+     *
+     * In a body node, the previous head or body node (with the same hash as
+     * this node).  Never null. */
+    struct hindex_node *d;
+
+    /* In a head or a body node, the next body node with the same hash as this
+     * node.  NULL if this is the last node with this hash. */
+    struct hindex_node *s;
+};
+
+/* A hash index. */
+struct hindex {
+    struct hindex_node **buckets; /* Must point to 'one' iff 'mask' == 0. */
+    struct hindex_node *one;
+    size_t mask;      /* 0 or more lowest-order bits set, others cleared. */
+    size_t n_unique;  /* Number of unique hashes (the number of head nodes). */
+};
+
+/* Initializer for an empty hash index. */
+#define HINDEX_INITIALIZER(HINDEX) \
+    { (struct hindex_node **const) &(HINDEX)->one, NULL, 0, 0 }
+
+/* Initialization. */
+void hindex_init(struct hindex *);
+void hindex_destroy(struct hindex *);
+void hindex_clear(struct hindex *);
+void hindex_swap(struct hindex *a, struct hindex *b);
+void hindex_moved(struct hindex *hindex);
+static inline bool hindex_is_empty(const struct hindex *);
+
+/* Adjusting capacity. */
+void hindex_expand(struct hindex *);
+void hindex_shrink(struct hindex *);
+void hindex_reserve(struct hindex *, size_t capacity);
+
+/* Insertion and deletion. */
+void hindex_insert_fast(struct hindex *, struct hindex_node *, size_t hash);
+void hindex_insert(struct hindex *, struct hindex_node *, size_t hash);
+void hindex_remove(struct hindex *, struct hindex_node *);
+
+/* Search.
+ *
+ * HINDEX_FOR_EACH_WITH_HASH iterates NODE over all of the nodes in HINDEX that
+ * have hash value equal to HASH.  MEMBER must be the name of the 'struct
+ * hindex_node' member within NODE.
+ *
+ * The loop should not change NODE to point to a different node or insert or
+ * delete nodes in HINDEX (unless it "break"s out of the loop to terminate
+ * iteration).
+ *
+ * Evaluates HASH only once.
+ */
+#define HINDEX_FOR_EACH_WITH_HASH(NODE, MEMBER, HASH, HINDEX)               \
+    for (ASSIGN_CONTAINER(NODE, hindex_node_with_hash(HINDEX, HASH), MEMBER); \
+         &(NODE)->MEMBER != NULL;                                       \
+         ASSIGN_CONTAINER(NODE, (NODE)->MEMBER.s, MEMBER))
+
+struct hindex_node *hindex_node_with_hash(const struct hindex *, size_t hash);
+
+/* Iteration. */
+
+/* Iterates through every node in HINDEX. */
+#define HINDEX_FOR_EACH(NODE, MEMBER, HINDEX)                           \
+    for (ASSIGN_CONTAINER(NODE, hindex_first(HINDEX), MEMBER);          \
+         &(NODE)->MEMBER != NULL;                                       \
+         ASSIGN_CONTAINER(NODE, hindex_next(HINDEX, &(NODE)->MEMBER), MEMBER))
+
+/* Safe when NODE may be freed (not needed when NODE may be removed from the
+ * hash index but its members remain accessible and intact). */
+#define HINDEX_FOR_EACH_SAFE(NODE, NEXT, MEMBER, HINDEX)                \
+    for (ASSIGN_CONTAINER(NODE, hindex_first(HINDEX), MEMBER);          \
+         (&(NODE)->MEMBER != NULL                                       \
+          ? ASSIGN_CONTAINER(NEXT, hindex_next(HINDEX, &(NODE)->MEMBER), MEMBER) \
+          : 0);                                                         \
+         (NODE) = (NEXT))
+
+struct hindex_node *hindex_first(const struct hindex *);
+struct hindex_node *hindex_next(const struct hindex *,
+                                const struct hindex_node *);
+
+/* Returns true if 'hindex' currently contains no nodes, false otherwise. */
+static inline bool
+hindex_is_empty(const struct hindex *hindex)
+{
+    return hindex->n_unique == 0;
+}
+
+#endif /* hindex.h */
index 97c6959..f15e72c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2012 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 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.
@@ -90,8 +90,7 @@ resize(struct hmap *hmap, size_t new_mask)
     struct hmap tmp;
     size_t i;
 
-    ovs_assert(!(new_mask & (new_mask + 1)));
-    ovs_assert(new_mask != SIZE_MAX);
+    ovs_assert(is_pow2(new_mask + 1));
 
     hmap_init(&tmp);
     if (new_mask) {
index af385c6..56dc5ef 100644 (file)
@@ -1026,7 +1026,8 @@ json_from_file(const char *file_name)
     stream = fopen(file_name, "r");
     if (!stream) {
         return json_string_create_nocopy(
-            xasprintf("error opening \"%s\": %s", file_name, strerror(errno)));
+            xasprintf("error opening \"%s\": %s", file_name,
+                      ovs_strerror(errno)));
     }
     json = json_from_stream(stream);
     fclose(stream);
@@ -1063,7 +1064,7 @@ json_from_stream(FILE *stream)
     if (ferror(stream)) {
         json_destroy(json);
         json = json_string_create_nocopy(
-            xasprintf("error reading JSON stream: %s", strerror(errno)));
+            xasprintf("error reading JSON stream: %s", ovs_strerror(errno)));
     }
 
     return json;
index 1ea5398..56b4cce 100644 (file)
@@ -129,7 +129,7 @@ jsonrpc_run(struct jsonrpc *rpc)
         } else {
             if (retval != -EAGAIN) {
                 VLOG_WARN_RL(&rl, "%s: send error: %s",
-                             rpc->name, strerror(-retval));
+                             rpc->name, ovs_strerror(-retval));
                 jsonrpc_error(rpc, -retval);
             }
             break;
@@ -307,7 +307,7 @@ jsonrpc_recv(struct jsonrpc *rpc, struct jsonrpc_msg **msgp)
                     return EAGAIN;
                 } else {
                     VLOG_WARN_RL(&rl, "%s: receive error: %s",
-                                 rpc->name, strerror(-retval));
+                                 rpc->name, ovs_strerror(-retval));
                     jsonrpc_error(rpc, -retval);
                     return rpc->status;
                 }
@@ -1116,7 +1116,8 @@ jsonrpc_session_set_dscp(struct jsonrpc_session *s,
             error = pstream_set_dscp(s->pstream, dscp);
             if (error) {
                 VLOG_ERR("%s: failed set_dscp %s",
-                         reconnect_get_name(s->reconnect), strerror(error));
+                         reconnect_get_name(s->reconnect),
+                         ovs_strerror(error));
             }
             /*
              * XXX race window between setting dscp to listening socket
index 8fd9d89..9daca3b 100644 (file)
@@ -100,6 +100,8 @@ struct lacp {
     bool fast;               /* True if using fast probe interval. */
     bool negotiated;         /* True if LACP negotiations were successful. */
     bool update;             /* True if lacp_update() needs to be called. */
+
+    int ref_cnt;
 };
 
 struct slave {
@@ -197,14 +199,31 @@ lacp_create(void)
     lacp = xzalloc(sizeof *lacp);
     hmap_init(&lacp->slaves);
     list_push_back(&all_lacps, &lacp->node);
+    lacp->ref_cnt = 1;
+    return lacp;
+}
+
+struct lacp *
+lacp_ref(const struct lacp *lacp_)
+{
+    struct lacp *lacp = CONST_CAST(struct lacp *, lacp_);
+    if (lacp) {
+        ovs_assert(lacp->ref_cnt > 0);
+        lacp->ref_cnt++;
+    }
     return lacp;
 }
 
 /* Destroys 'lacp' and its slaves. Does nothing if 'lacp' is NULL. */
 void
-lacp_destroy(struct lacp *lacp)
+lacp_unref(struct lacp *lacp)
 {
-    if (lacp) {
+    if (!lacp) {
+        return;
+    }
+
+    ovs_assert(lacp->ref_cnt > 0);
+    if (!--lacp->ref_cnt) {
         struct slave *slave, *next;
 
         HMAP_FOR_EACH_SAFE (slave, next, node, &lacp->slaves) {
@@ -382,14 +401,6 @@ lacp_slave_may_enable(const struct lacp *lacp, const void *slave_)
     }
 }
 
-/* Returns the port ID used for 'slave_' in LACP communications. */
-uint16_t
-lacp_slave_get_port_id(const struct lacp *lacp, const void *slave_)
-{
-    struct slave *slave = slave_lookup(lacp, slave_);
-    return slave->port_id;
-}
-
 /* Returns true if partner information on 'slave_' is up to date.  'slave_'
  * not being current, generally indicates a connectivity problem, or a
  * misconfigured (or broken) partner. */
index 624e368..89b0e0a 100644 (file)
@@ -39,7 +39,8 @@ struct lacp_settings {
 
 void lacp_init(void);
 struct lacp *lacp_create(void);
-void lacp_destroy(struct lacp *);
+void lacp_unref(struct lacp *);
+struct lacp *lacp_ref(const struct lacp *);
 
 void lacp_configure(struct lacp *, const struct lacp_settings *);
 bool lacp_is_active(const struct lacp *);
@@ -60,7 +61,6 @@ void lacp_slave_register(struct lacp *, void *slave_,
 void lacp_slave_unregister(struct lacp *, const void *slave);
 void lacp_slave_carrier_changed(const struct lacp *, const void *slave);
 bool lacp_slave_may_enable(const struct lacp *, const void *slave);
-uint16_t lacp_slave_get_port_id(const struct lacp *, const void *slave);
 bool lacp_slave_is_current(const struct lacp *, const void *slave_);
 
 /* Callback function for lacp_run() for sending a LACP PDU. */
diff --git a/lib/leak-checker.c b/lib/leak-checker.c
deleted file mode 100644 (file)
index 1fd3d5b..0000000
+++ /dev/null
@@ -1,243 +0,0 @@
-/*
- * Copyright (c) 2008, 2009, 2010 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 "leak-checker.h"
-#include <inttypes.h>
-#include "backtrace.h"
-#include "vlog.h"
-
-VLOG_DEFINE_THIS_MODULE(leak_checker);
-
-#ifndef HAVE_MALLOC_HOOKS
-void
-leak_checker_start(const char *file_name OVS_UNUSED)
-{
-    VLOG_WARN("not enabling leak checker because the libc in use does not "
-              "have the required hooks");
-}
-
-void
-leak_checker_set_limit(off_t max_size OVS_UNUSED)
-{
-}
-
-void
-leak_checker_claim(const void *p OVS_UNUSED)
-{
-}
-
-void
-leak_checker_usage(void)
-{
-    printf("  --check-leaks=FILE      (accepted but ignored in this build)\n");
-}
-#else /* HAVE_MALLOC_HOOKS */
-#include <errno.h>
-#include <fcntl.h>
-#include <malloc.h>
-#include <sys/stat.h>
-
-typedef void *malloc_hook_type(size_t, const void *);
-typedef void *realloc_hook_type(void *, size_t, const void *);
-typedef void free_hook_type(void *, const void *);
-
-struct hooks {
-    malloc_hook_type *malloc_hook_func;
-    realloc_hook_type *realloc_hook_func;
-    free_hook_type *free_hook_func;
-};
-
-static malloc_hook_type hook_malloc;
-static realloc_hook_type hook_realloc;
-static free_hook_type hook_free;
-
-static struct hooks libc_hooks;
-static const struct hooks our_hooks = { hook_malloc, hook_realloc, hook_free };
-
-static FILE *file;
-static off_t limit = 10 * 1000 * 1000;
-
-static void
-get_hooks(struct hooks *hooks)
-{
-    hooks->malloc_hook_func = __malloc_hook;
-    hooks->realloc_hook_func = __realloc_hook;
-    hooks->free_hook_func = __free_hook;
-}
-
-static void
-set_hooks(const struct hooks *hooks)
-{
-    __malloc_hook = hooks->malloc_hook_func;
-    __realloc_hook = hooks->realloc_hook_func;
-    __free_hook = hooks->free_hook_func;
-}
-
-void
-leak_checker_start(const char *file_name)
-{
-    if (!file) {
-        file = fopen(file_name, "w");
-        if (!file) {
-            VLOG_WARN("failed to create \"%s\": %s",
-                      file_name, strerror(errno));
-            return;
-        }
-        setvbuf(file, NULL, _IOLBF, 0);
-        VLOG_WARN("enabled memory leak logging to \"%s\"", file_name);
-        get_hooks(&libc_hooks);
-        set_hooks(&our_hooks);
-    }
-}
-
-void
-leak_checker_stop(void)
-{
-    if (file) {
-        fclose(file);
-        file = NULL;
-        set_hooks(&libc_hooks);
-        VLOG_WARN("disabled memory leak logging");
-    }
-}
-
-void
-leak_checker_set_limit(off_t limit_)
-{
-    limit = limit_;
-}
-
-void
-leak_checker_usage(void)
-{
-    printf("  --check-leaks=FILE      log malloc and free calls to FILE\n");
-}
-
-static void PRINTF_FORMAT(1, 2)
-log_callers(const char *format, ...)
-{
-    struct backtrace backtrace;
-    va_list args;
-    int i;
-
-    va_start(args, format);
-    vfprintf(file, format, args);
-    va_end(args);
-
-    putc(':', file);
-    backtrace_capture(&backtrace);
-    for (i = 0; i < backtrace.n_frames; i++) {
-        fprintf(file, " 0x%"PRIxPTR, backtrace.frames[i]);
-    }
-    putc('\n', file);
-}
-
-static void
-reset_hooks(void)
-{
-    static int count;
-
-    if (file) {
-        if (ferror(file)) {
-            VLOG_WARN("error writing leak checker log file");
-            leak_checker_stop();
-            return;
-        }
-
-        if (count++ >= 100 && limit) {
-            struct stat s;
-            count = 0;
-            if (fstat(fileno(file), &s) < 0) {
-                VLOG_WARN("cannot fstat leak checker log file: %s",
-                          strerror(errno));
-                leak_checker_stop();
-                return;
-            }
-            if (s.st_size > limit) {
-                VLOG_WARN("leak checker log file size exceeded limit");
-                leak_checker_stop();
-                return;
-            }
-        }
-    }
-    if (file) {
-        set_hooks(&our_hooks);
-    }
-}
-
-static void *
-hook_malloc(size_t size, const void *caller OVS_UNUSED)
-{
-    void *p;
-
-    set_hooks(&libc_hooks);
-    p = malloc(size);
-    get_hooks(&libc_hooks);
-
-    log_callers("malloc(%zu) -> %p", size, p);
-
-    reset_hooks();
-    return p;
-}
-
-void
-leak_checker_claim(const void *p)
-{
-    if (!file) {
-        return;
-    }
-
-    if (p) {
-        set_hooks(&libc_hooks);
-        log_callers("claim(%p)", p);
-        reset_hooks();
-    }
-}
-
-static void
-hook_free(void *p, const void *caller OVS_UNUSED)
-{
-    if (!p) {
-        return;
-    }
-
-    set_hooks(&libc_hooks);
-    log_callers("free(%p)", p);
-    free(p);
-    get_hooks(&libc_hooks);
-
-    reset_hooks();
-}
-
-static void *
-hook_realloc(void *p, size_t size, const void *caller OVS_UNUSED)
-{
-    void *q;
-
-    set_hooks(&libc_hooks);
-    q = realloc(p, size);
-    get_hooks(&libc_hooks);
-
-    if (p != q) {
-        log_callers("realloc(%p, %zu) -> %p", p, size, q);
-    }
-
-    reset_hooks();
-
-    return q;
-}
-#endif /* HAVE_MALLOC_HOOKS */
diff --git a/lib/leak-checker.h b/lib/leak-checker.h
deleted file mode 100644 (file)
index e74cd9d..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (c) 2008, 2009, 2011 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.
- */
-
-#ifndef LEAK_CHECKER_H
-#define LEAK_CHECKER_H 1
-
-#include <sys/types.h>
-
-#define LEAK_CHECKER_OPTION_ENUMS               \
-    OPT_CHECK_LEAKS,                            \
-    OPT_LEAK_LIMIT
-#define LEAK_CHECKER_LONG_OPTIONS                           \
-    {"check-leaks", required_argument, NULL, OPT_CHECK_LEAKS}, \
-    {"leak-limit", required_argument, NULL, OPT_LEAK_LIMIT}
-#define LEAK_CHECKER_OPTION_HANDLERS                \
-        case OPT_CHECK_LEAKS:                       \
-            leak_checker_start(optarg);             \
-            break;                                  \
-        case OPT_LEAK_LIMIT:                        \
-            leak_checker_set_limit(atol(optarg));   \
-            break;
-void leak_checker_start(const char *file_name);
-void leak_checker_set_limit(off_t limit);
-void leak_checker_stop(void);
-void leak_checker_claim(const void *);
-void leak_checker_usage(void);
-
-#endif /* leak-checker.h */
diff --git a/lib/leak-checker.man b/lib/leak-checker.man
deleted file mode 100644 (file)
index beba9e1..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-.TP
-\fB\-\-check\-leaks=\fIfile\fR
-.
-Logs information about memory allocation and deallocation to
-\fIfile\fR, to allow for debugging memory leaks in \fB\*(PN\fR.  This
-option slows down \fB\*(PN\fR considerably, so it should only be used
-when a memory leak is suspected.  Use the \fBovs\-parse\-leaks\fR script
-to interpret the leak file.
-.TP
-\fB\-\-leak\-limit=\fIsize\fR
-.
-Limits size of the leak file as specified by \fB\-\-check\-leaks\fR to 
-\fIsize\fR bytes.  Finding leaks sometimes requires allowing the leak 
-file to grow very large, up to 1GB.  By default, files are limited
-to 10MB.
index ab403be..0cc562d 100644 (file)
@@ -355,9 +355,9 @@ learn_execute(const struct ofpact_learn *learn, const struct flow *flow,
         case NX_LEARN_DST_OUTPUT:
             if (spec->n_bits <= 16
                 || is_all_zeros(value.u8, sizeof value - 2)) {
-                uint16_t port = ntohs(value.be16[7]);
+                ofp_port_t port = u16_to_ofp(ntohs(value.be16[7]));
 
-                if (port < OFPP_MAX
+                if (ofp_to_u16(port) < ofp_to_u16(OFPP_MAX)
                     || port == OFPP_IN_PORT
                     || port == OFPP_FLOOD
                     || port == OFPP_LOCAL
@@ -374,6 +374,22 @@ learn_execute(const struct ofpact_learn *learn, const struct flow *flow,
     fm->ofpacts_len = ofpacts->size;
 }
 
+/* Perform a bitwise-OR on 'wc''s fields that are relevant as sources in
+ * the learn action 'learn'. */
+void
+learn_mask(const struct ofpact_learn *learn, struct flow_wildcards *wc)
+{
+    const struct ofpact_learn_spec *spec;
+    union mf_subvalue value;
+
+    memset(&value, 0xff, sizeof value);
+    for (spec = learn->specs; spec < &learn->specs[learn->n_specs]; spec++) {
+        if (spec->src_type == NX_LEARN_SRC_FIELD) {
+            mf_write_subfield_flow(&spec->src, &value, &wc->masks);
+        }
+    }
+}
+
 static void
 learn_parse_load_immediate(const char *s, struct ofpact_learn_spec *spec)
 {
index 8ea8dcc..5bceb6e 100644 (file)
@@ -21,6 +21,7 @@
 
 struct ds;
 struct flow;
+struct flow_wildcards;
 struct ofpbuf;
 struct ofpact_learn;
 struct ofputil_flow_mod;
@@ -38,6 +39,7 @@ void learn_to_nxast(const struct ofpact_learn *, struct ofpbuf *openflow);
 
 void learn_execute(const struct ofpact_learn *, const struct flow *,
                    struct ofputil_flow_mod *, struct ofpbuf *ofpacts);
+void learn_mask(const struct ofpact_learn *, struct flow_wildcards *);
 
 void learn_parse(char *, struct ofpbuf *ofpacts);
 void learn_format(const struct ofpact_learn *, struct ds *);
index 4a95dc1..9e02860 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.
@@ -48,7 +48,7 @@ VLOG_DEFINE_THIS_MODULE(learning_switch);
 
 struct lswitch_port {
     struct hmap_node hmap_node; /* Hash node for port number. */
-    uint16_t port_no;           /* OpenFlow port number, in host byte order. */
+    ofp_port_t port_no;         /* OpenFlow port number. */
     uint32_t queue_id;          /* OpenFlow queue number. */
 };
 
@@ -208,7 +208,7 @@ lswitch_handshake(struct lswitch *sw)
 
             if (error) {
                 VLOG_INFO_RL(&rl, "%s: failed to queue default flows (%s)",
-                             rconn_get_name(sw->rconn), strerror(error));
+                             rconn_get_name(sw->rconn), ovs_strerror(error));
             }
         } else {
             VLOG_INFO_RL(&rl, "%s: failed to set usable protocol",
@@ -237,7 +237,7 @@ lswitch_destroy(struct lswitch *sw)
             free(node);
         }
         shash_destroy(&sw->queue_names);
-        mac_learning_destroy(sw->ml);
+        mac_learning_unref(sw->ml);
         rconn_packet_counter_destroy(sw->queued);
         free(sw);
     }
@@ -433,7 +433,7 @@ queue_tx(struct lswitch *sw, struct ofpbuf *b)
         } else {
             VLOG_WARN_RL(&rl, "%016llx: %s: send: %s",
                          sw->datapath_id, rconn_get_name(sw->rconn),
-                         strerror(retval));
+                         ovs_strerror(retval));
         }
     }
 }
@@ -460,26 +460,27 @@ process_switch_features(struct lswitch *sw, struct ofp_header *oh)
         if (lp && hmap_node_is_null(&lp->hmap_node)) {
             lp->port_no = port.port_no;
             hmap_insert(&sw->queue_numbers, &lp->hmap_node,
-                        hash_int(lp->port_no, 0));
+                        hash_ofp_port(lp->port_no));
         }
     }
     return 0;
 }
 
-static uint16_t
+static ofp_port_t
 lswitch_choose_destination(struct lswitch *sw, const struct flow *flow)
 {
-    uint16_t out_port;
+    ofp_port_t out_port;
 
     /* Learn the source MAC. */
     if (mac_learning_may_learn(sw->ml, flow->dl_src, 0)) {
         struct mac_entry *mac = mac_learning_insert(sw->ml, flow->dl_src, 0);
-        if (mac_entry_is_new(mac) || mac->port.i != flow->in_port) {
+        if (mac_entry_is_new(mac)
+            || mac->port.ofp_port != flow->in_port.ofp_port) {
             VLOG_DBG_RL(&rl, "%016llx: learned that "ETH_ADDR_FMT" is on "
                         "port %"PRIu16, sw->datapath_id,
-                        ETH_ADDR_ARGS(flow->dl_src), flow->in_port);
+                        ETH_ADDR_ARGS(flow->dl_src), flow->in_port.ofp_port);
 
-            mac->port.i = flow->in_port;
+            mac->port.ofp_port = flow->in_port.ofp_port;
             mac_learning_changed(sw->ml, mac);
         }
     }
@@ -495,8 +496,8 @@ lswitch_choose_destination(struct lswitch *sw, const struct flow *flow)
 
         mac = mac_learning_lookup(sw->ml, flow->dl_dst, 0, NULL);
         if (mac) {
-            out_port = mac->port.i;
-            if (out_port == flow->in_port) {
+            out_port = mac->port.ofp_port;
+            if (out_port == flow->in_port.ofp_port) {
                 /* Don't send a packet back out its input port. */
                 return OFPP_NONE;
             }
@@ -512,11 +513,11 @@ lswitch_choose_destination(struct lswitch *sw, const struct flow *flow)
 }
 
 static uint32_t
-get_queue_id(const struct lswitch *sw, uint16_t in_port)
+get_queue_id(const struct lswitch *sw, ofp_port_t in_port)
 {
     const struct lswitch_port *port;
 
-    HMAP_FOR_EACH_WITH_HASH (port, hmap_node, hash_int(in_port, 0),
+    HMAP_FOR_EACH_WITH_HASH (port, hmap_node, hash_ofp_port(in_port),
                              &sw->queue_numbers) {
         if (port->port_no == in_port) {
             return port->queue_id;
@@ -531,7 +532,7 @@ process_packet_in(struct lswitch *sw, const struct ofp_header *oh)
 {
     struct ofputil_packet_in pi;
     uint32_t queue_id;
-    uint16_t out_port;
+    ofp_port_t out_port;
 
     uint64_t ofpacts_stub[64 / 8];
     struct ofpbuf ofpacts;
@@ -541,6 +542,7 @@ process_packet_in(struct lswitch *sw, const struct ofp_header *oh)
 
     struct ofpbuf pkt;
     struct flow flow;
+    union flow_in_port in_port_;
 
     error = ofputil_decode_packet_in(&pi, oh);
     if (error) {
@@ -558,7 +560,8 @@ 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, 0, NULL, pi.fmd.in_port, &flow);
+    in_port_.ofp_port = pi.fmd.in_port;
+    flow_extract(&pkt, 0, 0, NULL, &in_port_, &flow);
     flow.tunnel.tun_id = pi.fmd.tun_id;
 
     /* Choose output port. */
@@ -569,7 +572,8 @@ process_packet_in(struct lswitch *sw, const struct ofp_header *oh)
     ofpbuf_use_stack(&ofpacts, ofpacts_stub, sizeof ofpacts_stub);
     if (out_port == OFPP_NONE) {
         /* No actions. */
-    } else if (queue_id == UINT32_MAX || out_port >= OFPP_MAX) {
+    } else if (queue_id == UINT32_MAX
+               || ofp_to_u16(out_port) >= ofp_to_u16(OFPP_MAX)) {
         ofpact_put_OUTPUT(&ofpacts)->port = out_port;
     } else {
         struct ofpact_enqueue *enqueue = ofpact_put_ENQUEUE(&ofpacts);
index 3708aec..14e553d 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.
@@ -118,7 +118,7 @@ lockfile_lock(const char *file, struct lockfile **lockfilep)
                       "pid %ld", lock_name, (long int) pid);
         } else {
             VLOG_WARN("%s: failed to lock file: %s",
-                      lock_name, strerror(error));
+                      lock_name, ovs_strerror(error));
         }
     }
 
@@ -225,7 +225,7 @@ lockfile_try_lock(const char *name, pid_t *pidp, struct lockfile **lockfilep)
         }
     } else if (errno != ENOENT) {
         VLOG_WARN("%s: failed to stat lock file: %s",
-                  name, strerror(errno));
+                  name, ovs_strerror(errno));
         return errno;
     }
 
@@ -233,13 +233,14 @@ lockfile_try_lock(const char *name, pid_t *pidp, struct lockfile **lockfilep)
     fd = open(name, O_RDWR | O_CREAT, 0600);
     if (fd < 0) {
         VLOG_WARN("%s: failed to open lock file: %s",
-                  name, strerror(errno));
+                  name, ovs_strerror(errno));
         return errno;
     }
 
     /* Get the inode and device number for the lock table. */
     if (fstat(fd, &s)) {
-        VLOG_ERR("%s: failed to fstat lock file: %s", name, strerror(errno));
+        VLOG_ERR("%s: failed to fstat lock file: %s",
+                 name, ovs_strerror(errno));
         close(fd);
         return errno;
     }
@@ -251,9 +252,7 @@ lockfile_try_lock(const char *name, pid_t *pidp, struct lockfile **lockfilep)
     l.l_start = 0;
     l.l_len = 0;
 
-    time_disable_restart();
     error = fcntl(fd, F_SETLK, &l) == -1 ? errno : 0;
-    time_enable_restart();
 
     if (!error) {
         *lockfilep = lockfile_register(name, s.st_dev, s.st_ino, fd);
index 052ac48..ca0ccb8 100644 (file)
@@ -123,14 +123,32 @@ mac_learning_create(unsigned int idle_time)
     ml->flood_vlans = NULL;
     ml->idle_time = normalize_idle_time(idle_time);
     ml->max_entries = MAC_DEFAULT_MAX;
+    tag_set_init(&ml->tags);
+    ml->ref_cnt = 1;
     return ml;
 }
 
-/* Destroys MAC learning table 'ml'. */
-void
-mac_learning_destroy(struct mac_learning *ml)
+struct mac_learning *
+mac_learning_ref(const struct mac_learning *ml_)
 {
+    struct mac_learning *ml = CONST_CAST(struct mac_learning *, ml_);
     if (ml) {
+        ovs_assert(ml->ref_cnt > 0);
+        ml->ref_cnt++;
+    }
+    return ml;
+}
+
+/* Unreferences (and possibly destroys) MAC learning table 'ml'. */
+void
+mac_learning_unref(struct mac_learning *ml)
+{
+    if (!ml) {
+        return;
+    }
+
+    ovs_assert(ml->ref_cnt > 0);
+    if (!--ml->ref_cnt) {
         struct mac_entry *e, *next;
 
         HMAP_FOR_EACH_SAFE (e, next, hmap_node, &ml->table) {
@@ -245,22 +263,23 @@ mac_learning_insert(struct mac_learning *ml,
     return e;
 }
 
-/* Changes 'e''s tag to a new, randomly selected one, and returns the tag that
- * would have been previously used for this entry's MAC and VLAN (either before
- * 'e' was inserted, if it is new, or otherwise before its port was updated.)
+/* Changes 'e''s tag to a new, randomly selected one.  Causes
+ * mac_learning_run() to flag for revalidation the tag that would have been
+ * previously used for this entry's MAC and VLAN (either before 'e' was
+ * inserted, if it is new, or otherwise before its port was updated.)
  *
  * The client should call this function after obtaining a MAC learning entry
  * from mac_learning_insert(), if the entry is either new or if its learned
  * port has changed. */
-tag_type
+void
 mac_learning_changed(struct mac_learning *ml, struct mac_entry *e)
 {
-    tag_type old_tag = e->tag;
+    tag_type tag = e->tag ? e->tag : make_unknown_mac_tag(ml, e->mac, e->vlan);
 
     COVERAGE_INC(mac_learning_learned);
 
     e->tag = tag_create_random();
-    return old_tag ? old_tag : make_unknown_mac_tag(ml, e->mac, e->vlan);
+    tag_set_add(&ml->tags, tag);
 }
 
 /* Looks up MAC 'dst' for VLAN 'vlan' in 'ml' and returns the associated MAC
@@ -322,6 +341,12 @@ void
 mac_learning_run(struct mac_learning *ml, struct tag_set *set)
 {
     struct mac_entry *e;
+
+    if (set) {
+        tag_set_union(set, &ml->tags);
+    }
+    tag_set_init(&ml->tags);
+
     while (get_lru(ml, &e)
            && (hmap_count(&ml->table) > ml->max_entries
                || time_now() >= e->expires)) {
@@ -336,7 +361,8 @@ mac_learning_run(struct mac_learning *ml, struct tag_set *set)
 void
 mac_learning_wait(struct mac_learning *ml)
 {
-    if (hmap_count(&ml->table) > ml->max_entries) {
+    if (hmap_count(&ml->table) > ml->max_entries
+        || !tag_set_is_empty(&ml->tags)) {
         poll_immediate_wake();
     } else if (!list_is_empty(&ml->lrus)) {
         struct mac_entry *e = mac_entry_from_lru_node(ml->lrus.next);
index 284e7f6..06e99f3 100644 (file)
@@ -49,7 +49,7 @@ struct mac_entry {
     /* Learned port. */
     union {
         void *p;
-        int i;
+        ofp_port_t ofp_port;
     } port;
 };
 
@@ -85,11 +85,14 @@ struct mac_learning {
     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. */
+    struct tag_set tags;        /* Tags which have changed. */
+    int ref_cnt;
 };
 
 /* Basics. */
 struct mac_learning *mac_learning_create(unsigned int idle_time);
-void mac_learning_destroy(struct mac_learning *);
+struct mac_learning *mac_learning_ref(const struct mac_learning *);
+void mac_learning_unref(struct mac_learning *);
 
 void mac_learning_run(struct mac_learning *, struct tag_set *);
 void mac_learning_wait(struct mac_learning *);
@@ -107,7 +110,7 @@ bool mac_learning_may_learn(const struct mac_learning *,
 struct mac_entry *mac_learning_insert(struct mac_learning *,
                                       const uint8_t src[ETH_ADDR_LEN],
                                       uint16_t vlan);
-tag_type mac_learning_changed(struct mac_learning *, struct mac_entry *);
+void mac_learning_changed(struct mac_learning *, struct mac_entry *);
 
 /* Lookup. */
 struct mac_entry *mac_learning_lookup(const struct mac_learning *,
index 512253e..6d66eba 100644 (file)
@@ -271,10 +271,10 @@ match_set_tun_flags_masked(struct match *match, uint16_t flags, uint16_t mask)
 }
 
 void
-match_set_in_port(struct match *match, uint16_t ofp_port)
+match_set_in_port(struct match *match, ofp_port_t ofp_port)
 {
-    match->wc.masks.in_port = UINT16_MAX;
-    match->flow.in_port = ofp_port;
+    match->wc.masks.in_port.ofp_port = u16_to_ofp(UINT16_MAX);
+    match->flow.in_port.ofp_port = ofp_port;
 }
 
 void
@@ -916,9 +916,9 @@ match_format(const struct match *match, struct ds *s, unsigned int priority)
                       ntohll(f->metadata), ntohll(wc->masks.metadata));
         break;
     }
-    if (wc->masks.in_port) {
+    if (wc->masks.in_port.ofp_port) {
         ds_put_cstr(s, "in_port=");
-        ofputil_format_port(f->in_port, s);
+        ofputil_format_port(f->in_port.ofp_port, s);
         ds_put_char(s, ',');
     }
     if (wc->masks.vlan_tci) {
index d435aa4..0ea1f2d 100644 (file)
@@ -60,7 +60,7 @@ 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_in_port(struct match *, ofp_port_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);
index 16850ec..0677202 100644 (file)
@@ -116,6 +116,15 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         MFP_NONE,
         true,
         NXM_OF_IN_PORT, "NXM_OF_IN_PORT",
+        NXM_OF_IN_PORT, "NXM_OF_IN_PORT",
+    }, {
+        MFF_IN_PORT_OXM, "in_port_oxm", NULL,
+        MF_FIELD_SIZES(be32),
+        MFM_NONE,
+        MFS_OFP_PORT_OXM,
+        MFP_NONE,
+        true,
+        OXM_OF_IN_PORT, "OXM_OF_IN_PORT",
         OXM_OF_IN_PORT, "OXM_OF_IN_PORT",
     }, {
         MFF_SKB_PRIORITY, "skb_priority", NULL,
@@ -688,7 +697,8 @@ mf_is_all_wild(const struct mf_field *mf, const struct flow_wildcards *wc)
     case MFF_METADATA:
         return !wc->masks.metadata;
     case MFF_IN_PORT:
-        return !wc->masks.in_port;
+    case MFF_IN_PORT_OXM:
+        return !wc->masks.in_port.ofp_port;
     case MFF_SKB_PRIORITY:
         return !wc->masks.skb_priority;
     case MFF_SKB_MARK:
@@ -926,6 +936,11 @@ mf_is_value_valid(const struct mf_field *mf, const union mf_value *value)
     case MFF_ND_TLL:
         return true;
 
+    case MFF_IN_PORT_OXM: {
+        ofp_port_t port;
+        return !ofputil_port_from_ofp11(value->be32, &port);
+    }
+
     case MFF_IP_DSCP:
         return !(value->u8 & ~IP_DSCP_MASK);
     case MFF_IP_DSCP_SHIFTED:
@@ -996,7 +1011,10 @@ mf_get_value(const struct mf_field *mf, const struct flow *flow,
         break;
 
     case MFF_IN_PORT:
-        value->be16 = htons(flow->in_port);
+        value->be16 = htons(ofp_to_u16(flow->in_port.ofp_port));
+        break;
+    case MFF_IN_PORT_OXM:
+        value->be32 = ofputil_port_to_ofp11(flow->in_port.ofp_port);
         break;
 
     case MFF_SKB_PRIORITY:
@@ -1179,9 +1197,16 @@ mf_set_value(const struct mf_field *mf,
         break;
 
     case MFF_IN_PORT:
-        match_set_in_port(match, ntohs(value->be16));
+        match_set_in_port(match, u16_to_ofp(ntohs(value->be16)));
         break;
 
+    case MFF_IN_PORT_OXM: {
+        ofp_port_t port;
+        ofputil_port_from_ofp11(value->be32, &port);
+        match_set_in_port(match, port);
+        break;
+    }
+
     case MFF_SKB_PRIORITY:
         match_set_skb_priority(match, ntohl(value->be32));
         break;
@@ -1330,9 +1355,8 @@ mf_set_value(const struct mf_field *mf,
     }
 }
 
-/* Makes 'match' match field 'mf' exactly, with the value matched taken from
- * 'value'.  The caller is responsible for ensuring that 'match' meets 'mf''s
- * prerequisites. */
+/* Sets 'flow' member field described by 'mf' to 'value'.  The caller is
+ * responsible for ensuring that 'flow' meets 'mf''s prerequisites.*/
 void
 mf_set_flow_value(const struct mf_field *mf,
                   const union mf_value *value, struct flow *flow)
@@ -1362,9 +1386,16 @@ mf_set_flow_value(const struct mf_field *mf,
         break;
 
     case MFF_IN_PORT:
-        flow->in_port = ntohs(value->be16);
+        flow->in_port.ofp_port = u16_to_ofp(ntohs(value->be16));
         break;
 
+    case MFF_IN_PORT_OXM: {
+        ofp_port_t port;
+        ofputil_port_from_ofp11(value->be32, &port);
+        flow->in_port.ofp_port = port;
+        break;
+    }
+
     case MFF_SKB_PRIORITY:
         flow->skb_priority = ntohl(value->be32);
         break;
@@ -1561,8 +1592,9 @@ mf_set_wild(const struct mf_field *mf, struct match *match)
         break;
 
     case MFF_IN_PORT:
-        match->flow.in_port = 0;
-        match->wc.masks.in_port = 0;
+    case MFF_IN_PORT_OXM:
+        match->flow.in_port.ofp_port = 0;
+        match->wc.masks.in_port.ofp_port = 0;
         break;
 
     case MFF_SKB_PRIORITY:
@@ -1742,6 +1774,7 @@ mf_set(const struct mf_field *mf,
 
     switch (mf->id) {
     case MFF_IN_PORT:
+    case MFF_IN_PORT_OXM:
     case MFF_SKB_MARK:
     case MFF_SKB_PRIORITY:
     case MFF_ETH_TYPE:
@@ -1977,6 +2010,10 @@ mf_random_value(const struct mf_field *mf, union mf_value *value)
     case MFF_ND_TLL:
         break;
 
+    case MFF_IN_PORT_OXM:
+        value->be32 = ofputil_port_to_ofp11(u16_to_ofp(ntohs(value->be16)));
+        break;
+
     case MFF_IPV6_LABEL:
         value->be32 &= ~htonl(IPV6_LABEL_MASK);
         break;
@@ -2165,18 +2202,33 @@ static char *
 mf_from_ofp_port_string(const struct mf_field *mf, const char *s,
                         ovs_be16 *valuep, ovs_be16 *maskp)
 {
-    uint16_t port;
+    ofp_port_t port;
 
     ovs_assert(mf->n_bytes == sizeof(ovs_be16));
 
     if (ofputil_port_from_string(s, &port)) {
-        *valuep = htons(port);
+        *valuep = htons(ofp_to_u16(port));
         *maskp = htons(UINT16_MAX);
         return NULL;
     }
     return xasprintf("%s: port value out of range for %s", s, mf->name);
 }
 
+static char *
+mf_from_ofp_port_string32(const struct mf_field *mf, const char *s,
+                          ovs_be32 *valuep, ovs_be32 *maskp)
+{
+    ofp_port_t port;
+
+    ovs_assert(mf->n_bytes == sizeof(ovs_be32));
+    if (ofputil_port_from_string(s, &port)) {
+        *valuep = ofputil_port_to_ofp11(port);
+        *maskp = htonl(UINT32_MAX);
+        return NULL;
+    }
+    return xasprintf("%s: port value out of range for %s", s, mf->name);
+}
+
 struct frag_handling {
     const char *name;
     uint8_t mask;
@@ -2314,6 +2366,9 @@ mf_parse(const struct mf_field *mf, const char *s,
     case MFS_OFP_PORT:
         return mf_from_ofp_port_string(mf, s, &value->be16, &mask->be16);
 
+    case MFS_OFP_PORT_OXM:
+        return mf_from_ofp_port_string32(mf, s, &value->be32, &mask->be32);
+
     case MFS_FRAG:
         return mf_from_frag_string(s, &value->u8, &mask->u8);
 
@@ -2417,9 +2472,17 @@ mf_format(const struct mf_field *mf,
     }
 
     switch (mf->string) {
+    case MFS_OFP_PORT_OXM:
+        if (!mask) {
+            ofp_port_t port;
+            ofputil_port_from_ofp11(value->be32, &port);
+            ofputil_format_port(port, s);
+            break;
+        }
+        /* fall through */
     case MFS_OFP_PORT:
         if (!mask) {
-            ofputil_format_port(ntohs(value->be16), s);
+            ofputil_format_port(u16_to_ofp(ntohs(value->be16)), s);
             break;
         }
         /* fall through */
index 9577a10..a85a193 100644 (file)
@@ -39,6 +39,7 @@ enum mf_field_id {
     MFF_TUN_TOS,                /* u8 */
     MFF_METADATA,               /* be64 */
     MFF_IN_PORT,                /* be16 */
+    MFF_IN_PORT_OXM,            /* be32 */
     MFF_SKB_PRIORITY,           /* be32 */
     MFF_SKB_MARK,               /* be32 */
 
@@ -220,6 +221,7 @@ enum mf_string {
     MFS_IPV4,
     MFS_IPV6,
     MFS_OFP_PORT,               /* An OpenFlow port number or name. */
+    MFS_OFP_PORT_OXM,           /* An OpenFlow port number or name (32-bit). */
     MFS_FRAG,                   /* no, yes, first, later, not_later */
     MFS_TNL_FLAGS,              /* FLOW_TNL_F_* flags */
 };
index f6a1a0a..1be6964 100644 (file)
@@ -102,15 +102,18 @@ static uint16_t multipath_algorithm(uint32_t hash, enum nx_mp_algorithm,
                                     unsigned int n_links, unsigned int arg);
 
 /* Executes 'mp' based on the current contents of 'flow', writing the results
- * back into 'flow'. */
+ * back into 'flow'.  Sets fields in 'wc' that were used to calculate
+ * the result. */
 void
-multipath_execute(const struct ofpact_multipath *mp, struct flow *flow)
+multipath_execute(const struct ofpact_multipath *mp, struct flow *flow,
+                  struct flow_wildcards *wc)
 {
     /* Calculate value to store. */
     uint32_t hash = flow_hash_fields(flow, mp->fields, mp->basis);
     uint16_t link = multipath_algorithm(hash, mp->algorithm,
                                         mp->max_link + 1, mp->arg);
 
+    flow_mask_hash_fields(flow, wc, mp->fields);
     nxm_reg_load(&mp->dst, link, flow);
 }
 
index 1b5160d..97b5161 100644 (file)
@@ -22,6 +22,7 @@
 
 struct ds;
 struct flow;
+struct flow_wildcards;
 struct nx_action_multipath;
 struct ofpact_multipath;
 struct ofpbuf;
@@ -38,7 +39,8 @@ enum ofperr multipath_check(const struct ofpact_multipath *,
 void multipath_to_nxast(const struct ofpact_multipath *,
                         struct ofpbuf *openflow);
 
-void multipath_execute(const struct ofpact_multipath *, struct flow *);
+void multipath_execute(const struct ofpact_multipath *, struct flow *,
+                       struct flow_wildcards *);
 
 void multipath_parse(struct ofpact_multipath *, const char *);
 void multipath_format(const struct ofpact_multipath *, struct ds *);
index ca43d07..f2cf852 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2011 Gaetano Catalli.
+ * Copyright (c) 2013 YAMAMOTO Takashi.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 #include <net/if_media.h>
 #include <net/if_tap.h>
 #include <netinet/in.h>
+#ifdef HAVE_NET_IF_MIB_H
 #include <net/if_mib.h>
+#endif
 #include <poll.h>
 #include <string.h>
 #include <unistd.h>
 #include <sys/sysctl.h>
+#if defined(__NetBSD__)
+#include <net/route.h>
+#endif
 
 #include "rtbsd.h"
 #include "coverage.h"
@@ -86,6 +92,8 @@ struct netdev_bsd {
     /* Used for sending packets on non-tap devices. */
     pcap_t *pcap;
     int fd;
+
+    char *kernel_name;
 };
 
 
@@ -101,6 +109,11 @@ enum {
 /* An AF_INET socket (used for ioctl operations). */
 static int af_inet_sock = -1;
 
+#if defined(__NetBSD__)
+/* AF_LINK socket used for netdev_bsd_get_stats and set_etheraddr */
+static int af_link_sock = -1;
+#endif /* defined(__NetBSD__) */
+
 #define PCAP_SNAPLEN 2048
 
 
@@ -132,6 +145,9 @@ static int set_etheraddr(const char *netdev_name, int hwaddr_family,
                          int hwaddr_len, const uint8_t[ETH_ADDR_LEN]);
 static int get_ifindex(const struct netdev *, int *ifindexp);
 
+static int ifr_get_flags(const struct ifreq *);
+static void ifr_set_flags(struct ifreq *, int flags);
+
 static int netdev_bsd_init(void);
 
 static bool
@@ -154,6 +170,12 @@ netdev_rx_bsd_cast(const struct netdev_rx *rx)
     return CONTAINER_OF(rx, struct netdev_rx_bsd, up);
 }
 
+static const char *
+netdev_get_kernel_name(const struct netdev *netdev)
+{
+    return netdev_bsd_cast(netdev)->kernel_name;
+}
+
 /* Initialize the AF_INET socket used for ioctl operations */
 static int
 netdev_bsd_init(void)
@@ -166,10 +188,20 @@ netdev_bsd_init(void)
 
     af_inet_sock = socket(AF_INET, SOCK_DGRAM, 0);
     status = af_inet_sock >= 0 ? 0 : errno;
+    if (status) {
+        VLOG_ERR("failed to create inet socket: %s", ovs_strerror(status));
+        return status;
+    }
 
+#if defined(__NetBSD__)
+    af_link_sock = socket(AF_LINK, SOCK_DGRAM, 0);
+    status = af_link_sock >= 0 ? 0 : errno;
     if (status) {
-        VLOG_ERR("failed to create inet socket: %s", strerror(status));
+        VLOG_ERR("failed to create link socket: %s", Ovs_strerror(status));
+        close(af_inet_sock);
+        af_inet_sock = -1;
     }
+#endif /* defined(__NetBSD__) */
 
     return status;
 }
@@ -286,6 +318,7 @@ netdev_bsd_create_system(const struct netdev_class *class, const char *name,
     netdev->change_seq = 1;
     netdev_init(&netdev->up, name, class);
     netdev->tap_fd = -1;
+    netdev->kernel_name = xstrdup(name);
 
     /* Verify that the netdev really exists by attempting to read its flags */
     error = netdev_get_flags(&netdev->up, &flags);
@@ -310,6 +343,7 @@ netdev_bsd_create_tap(const struct netdev_class *class, const char *name,
     struct netdev_bsd *netdev = NULL;
     int error = 0;
     struct ifreq ifr;
+    char *kernel_name = NULL;
 
     error = cache_notifier_ref();
     if (error) {
@@ -327,7 +361,7 @@ netdev_bsd_create_tap(const struct netdev_class *class, const char *name,
     netdev->change_seq = 1;
     if (netdev->tap_fd < 0) {
         error = errno;
-        VLOG_WARN("opening \"/dev/tap\" failed: %s", strerror(error));
+        VLOG_WARN("opening \"/dev/tap\" failed: %s", ovs_strerror(error));
         goto error_undef_notifier;
     }
 
@@ -339,33 +373,42 @@ netdev_bsd_create_tap(const struct netdev_class *class, const char *name,
     }
 
     /* Change the name of the tap device */
+#if defined(SIOCSIFNAME)
     ifr.ifr_data = (void *)name;
     if (ioctl(af_inet_sock, SIOCSIFNAME, &ifr) == -1) {
         error = errno;
         destroy_tap(netdev->tap_fd, ifr.ifr_name);
         goto error_undef_notifier;
     }
+    kernel_name = xstrdup(name);
+#else
+    /*
+     * NetBSD doesn't support inteface renaming.
+     */
+    VLOG_INFO("tap %s is created for bridge %s", ifr.ifr_name, name);
+    kernel_name = xstrdup(ifr.ifr_name);
+#endif
 
     /* set non-blocking. */
     error = set_nonblocking(netdev->tap_fd);
     if (error) {
-        destroy_tap(netdev->tap_fd, name);
+        destroy_tap(netdev->tap_fd, kernel_name);
         goto error_undef_notifier;
     }
 
     /* Turn device UP */
-    ifr.ifr_flags = (uint16_t)IFF_UP;
-    ifr.ifr_flagshigh = 0;
-    strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name);
+    ifr_set_flags(&ifr, IFF_UP);
+    strncpy(ifr.ifr_name, kernel_name, sizeof ifr.ifr_name);
     if (ioctl(af_inet_sock, SIOCSIFFLAGS, &ifr) == -1) {
         error = errno;
-        destroy_tap(netdev->tap_fd, name);
+        destroy_tap(netdev->tap_fd, kernel_name);
         goto error_undef_notifier;
     }
 
     /* initialize the device structure and
      * link the structure to its netdev */
     netdev_init(&netdev->up, name, class);
+    netdev->kernel_name = kernel_name;
     *netdevp = &netdev->up;
 
     return 0;
@@ -374,6 +417,7 @@ error_undef_notifier:
     cache_notifier_unref();
 error:
     free(netdev);
+    free(kernel_name);
     return error;
 }
 
@@ -385,11 +429,12 @@ netdev_bsd_destroy(struct netdev *netdev_)
     cache_notifier_unref();
 
     if (netdev->tap_fd >= 0) {
-        destroy_tap(netdev->tap_fd, netdev_get_name(netdev_));
+        destroy_tap(netdev->tap_fd, netdev_get_kernel_name(netdev_));
     }
     if (netdev->pcap) {
         pcap_close(netdev->pcap);
     }
+    free(netdev->kernel_name);
     free(netdev);
 }
 
@@ -437,7 +482,7 @@ netdev_bsd_open_pcap(const char *name, pcap_t **pcapp, int *fdp)
      * buffer becomes full or a timeout occurs. */
     if (ioctl(fd, BIOCIMMEDIATE, &one) < 0 ) {
         VLOG_ERR_RL(&rl, "ioctl(BIOCIMMEDIATE) on %s device failed: %s",
-                    name, strerror(errno));
+                    name, ovs_strerror(errno));
         error = errno;
         goto error;
     }
@@ -475,7 +520,8 @@ netdev_bsd_rx_open(struct netdev *netdev_, struct netdev_rx **rxp)
         pcap = NULL;
         fd = netdev->tap_fd;
     } else {
-        int error = netdev_bsd_open_pcap(netdev_get_name(netdev_), &pcap, &fd);
+        int error = netdev_bsd_open_pcap(netdev_get_kernel_name(netdev_),
+                                         &pcap, &fd);
         if (error) {
             return error;
         }
@@ -591,7 +637,7 @@ netdev_rx_bsd_recv_tap(struct netdev_rx_bsd *rx, void *data, size_t size)
         } else if (errno != EINTR) {
             if (errno != EAGAIN) {
                 VLOG_WARN_RL(&rl, "error receiving Ethernet packet on %s: %s",
-                             strerror(errno), netdev_rx_get_name(&rx->up));
+                             ovs_strerror(errno), netdev_rx_get_name(&rx->up));
             }
             return -errno;
         }
@@ -628,10 +674,10 @@ netdev_rx_bsd_drain(struct netdev_rx *rx_)
     struct ifreq ifr;
     struct netdev_rx_bsd *rx = netdev_rx_bsd_cast(rx_);
 
-    strcpy(ifr.ifr_name, netdev_rx_get_name(rx_));
+    strcpy(ifr.ifr_name, netdev_get_kernel_name(netdev_rx_get_netdev(rx_)));
     if (ioctl(rx->fd, BIOCFLUSH, &ifr) == -1) {
         VLOG_DBG_RL(&rl, "%s: ioctl(BIOCFLUSH) failed: %s",
-                    netdev_rx_get_name(rx_), strerror(errno));
+                    netdev_rx_get_name(rx_), ovs_strerror(errno));
         return errno;
     }
     return 0;
@@ -666,7 +712,7 @@ netdev_bsd_send(struct netdev *netdev_, const void *data, size_t size)
                 continue;
             } else if (errno != EAGAIN) {
                 VLOG_WARN_RL(&rl, "error sending Ethernet packet on %s: %s",
-                             name, strerror(errno));
+                             name, ovs_strerror(errno));
             }
             return errno;
         } else if (retval != size) {
@@ -713,8 +759,8 @@ netdev_bsd_set_etheraddr(struct netdev *netdev_,
 
     if (!(netdev->cache_valid & VALID_ETHERADDR)
         || !eth_addr_equals(netdev->etheraddr, mac)) {
-        error = set_etheraddr(netdev_get_name(netdev_), AF_LINK, ETH_ADDR_LEN,
-                              mac);
+        error = set_etheraddr(netdev_get_kernel_name(netdev_), AF_LINK,
+                              ETH_ADDR_LEN, mac);
         if (!error) {
             netdev->cache_valid |= VALID_ETHERADDR;
             memcpy(netdev->etheraddr, mac, ETH_ADDR_LEN);
@@ -737,7 +783,7 @@ netdev_bsd_get_etheraddr(const struct netdev *netdev_,
     struct netdev_bsd *netdev = netdev_bsd_cast(netdev_);
 
     if (!(netdev->cache_valid & VALID_ETHERADDR)) {
-        int error = get_etheraddr(netdev_get_name(netdev_),
+        int error = get_etheraddr(netdev_get_kernel_name(netdev_),
                                   netdev->etheraddr);
         if (error) {
             return error;
@@ -763,8 +809,8 @@ netdev_bsd_get_mtu(const struct netdev *netdev_, int *mtup)
         struct ifreq ifr;
         int error;
 
-        error = netdev_bsd_do_ioctl(netdev_get_name(netdev_), &ifr, SIOCGIFMTU,
-                                    "SIOCGIFMTU");
+        error = netdev_bsd_do_ioctl(netdev_get_kernel_name(netdev_), &ifr,
+                                    SIOCGIFMTU, "SIOCGIFMTU");
         if (error) {
             return error;
         }
@@ -794,11 +840,12 @@ netdev_bsd_get_carrier(const struct netdev *netdev_, bool *carrier)
         struct ifmediareq ifmr;
 
         memset(&ifmr, 0, sizeof(ifmr));
-        strncpy(ifmr.ifm_name, netdev_get_name(netdev_), sizeof ifmr.ifm_name);
+        strncpy(ifmr.ifm_name, netdev_get_kernel_name(netdev_),
+                sizeof ifmr.ifm_name);
 
         if (ioctl(af_inet_sock, SIOCGIFMEDIA, &ifmr) == -1) {
             VLOG_DBG_RL(&rl, "%s: ioctl(SIOCGIFMEDIA) failed: %s",
-                        netdev_get_name(netdev_), strerror(errno));
+                        netdev_get_name(netdev_), ovs_strerror(errno));
             return errno;
         }
 
@@ -816,10 +863,40 @@ netdev_bsd_get_carrier(const struct netdev *netdev_, bool *carrier)
     return 0;
 }
 
+static void
+convert_stats(struct netdev_stats *stats, const struct if_data *ifd)
+{
+    /*
+     * note: UINT64_MAX means unsupported
+     */
+    stats->rx_packets = ifd->ifi_ipackets;
+    stats->tx_packets = ifd->ifi_opackets;
+    stats->rx_bytes = ifd->ifi_obytes;
+    stats->tx_bytes = ifd->ifi_ibytes;
+    stats->rx_errors = ifd->ifi_ierrors;
+    stats->tx_errors = ifd->ifi_oerrors;
+    stats->rx_dropped = ifd->ifi_iqdrops;
+    stats->tx_dropped = UINT64_MAX;
+    stats->multicast = ifd->ifi_imcasts;
+    stats->collisions = ifd->ifi_collisions;
+    stats->rx_length_errors = UINT64_MAX;
+    stats->rx_over_errors = UINT64_MAX;
+    stats->rx_crc_errors = UINT64_MAX;
+    stats->rx_frame_errors = UINT64_MAX;
+    stats->rx_fifo_errors = UINT64_MAX;
+    stats->rx_missed_errors = UINT64_MAX;
+    stats->tx_aborted_errors = UINT64_MAX;
+    stats->tx_carrier_errors = UINT64_MAX;
+    stats->tx_fifo_errors = UINT64_MAX;
+    stats->tx_heartbeat_errors = UINT64_MAX;
+    stats->tx_window_errors = UINT64_MAX;
+}
+
 /* Retrieves current device stats for 'netdev'. */
 static int
 netdev_bsd_get_stats(const struct netdev *netdev_, struct netdev_stats *stats)
 {
+#if defined(__FreeBSD__)
     int if_count, i;
     int mib[6];
     size_t len;
@@ -836,7 +913,7 @@ netdev_bsd_get_stats(const struct netdev *netdev_, struct netdev_stats *stats)
 
     if (sysctl(mib, 5, &if_count, &len, (void *)0, 0) == -1) {
         VLOG_DBG_RL(&rl, "%s: sysctl failed: %s",
-                    netdev_get_name(netdev_), strerror(errno));
+                    netdev_get_name(netdev_), ovs_strerror(errno));
         return errno;
     }
 
@@ -847,37 +924,33 @@ netdev_bsd_get_stats(const struct netdev *netdev_, struct netdev_stats *stats)
         mib[4] = i; //row
         if (sysctl(mib, 6, &ifmd, &len, (void *)0, 0) == -1) {
             VLOG_DBG_RL(&rl, "%s: sysctl failed: %s",
-                        netdev_get_name(netdev_), strerror(errno));
+                        netdev_get_name(netdev_), ovs_strerror(errno));
             return errno;
         } else if (!strcmp(ifmd.ifmd_name, netdev_get_name(netdev_))) {
-            stats->rx_packets = ifmd.ifmd_data.ifi_ipackets;
-            stats->tx_packets = ifmd.ifmd_data.ifi_opackets;
-            stats->rx_bytes = ifmd.ifmd_data.ifi_ibytes;
-            stats->tx_bytes = ifmd.ifmd_data.ifi_obytes;
-            stats->rx_errors = ifmd.ifmd_data.ifi_ierrors;
-            stats->tx_errors = ifmd.ifmd_data.ifi_oerrors;
-            stats->rx_dropped = ifmd.ifmd_data.ifi_iqdrops;
-            stats->tx_dropped = UINT64_MAX;
-            stats->multicast = ifmd.ifmd_data.ifi_imcasts;
-            stats->collisions = ifmd.ifmd_data.ifi_collisions;
-
-            stats->rx_length_errors = UINT64_MAX;
-            stats->rx_over_errors = UINT64_MAX;
-            stats->rx_crc_errors = UINT64_MAX;
-            stats->rx_frame_errors = UINT64_MAX;
-            stats->rx_fifo_errors = UINT64_MAX;
-            stats->rx_missed_errors = UINT64_MAX;
-
-            stats->tx_aborted_errors = UINT64_MAX;
-            stats->tx_carrier_errors = UINT64_MAX;
-            stats->tx_fifo_errors = UINT64_MAX;
-            stats->tx_heartbeat_errors = UINT64_MAX;
-            stats->tx_window_errors = UINT64_MAX;
+            convert_stats(stats, &ifmd.ifmd_data);
             break;
         }
     }
 
     return 0;
+#elif defined(__NetBSD__)
+    struct ifdatareq ifdr;
+    int saved_errno;
+    int ret;
+
+    memset(&ifdr, 0, sizeof(ifdr));
+    strncpy(ifdr.ifdr_name, netdev_get_kernel_name(netdev_),
+            sizeof(ifdr.ifdr_name));
+    ret = ioctl(af_link_sock, SIOCGIFDATA, &ifdr);
+    saved_errno = errno;
+    if (ret == -1) {
+        return saved_errno;
+    }
+    convert_stats(stats, &ifdr.ifdr_data);
+    return 0;
+#else
+#error not implemented
+#endif
 }
 
 static uint32_t
@@ -980,7 +1053,7 @@ netdev_bsd_get_features(const struct netdev *netdev,
      * them. */
     if (ioctl(af_inet_sock, SIOCGIFMEDIA, &ifmr) == -1) {
         VLOG_DBG_RL(&rl, "%s: ioctl(SIOCGIFMEDIA) failed: %s",
-                    netdev_get_name(netdev), strerror(errno));
+                    netdev_get_name(netdev), ovs_strerror(errno));
         return errno;
     }
 
@@ -996,7 +1069,7 @@ netdev_bsd_get_features(const struct netdev *netdev,
 
     if (ioctl(af_inet_sock, SIOCGIFMEDIA, &ifmr) == -1) {
         VLOG_DBG_RL(&rl, "%s: ioctl(SIOCGIFMEDIA) failed: %s",
-                    netdev_get_name(netdev), strerror(errno));
+                    netdev_get_name(netdev), ovs_strerror(errno));
         error = errno;
         goto cleanup;
     }
@@ -1038,7 +1111,7 @@ netdev_bsd_get_in4(const struct netdev *netdev_, struct in_addr *in4,
         int error;
 
         ifr.ifr_addr.sa_family = AF_INET;
-        error = netdev_bsd_do_ioctl(netdev_get_name(netdev_), &ifr,
+        error = netdev_bsd_do_ioctl(netdev_get_kernel_name(netdev_), &ifr,
                                     SIOCGIFADDR, "SIOCGIFADDR");
         if (error) {
             return error;
@@ -1047,7 +1120,7 @@ netdev_bsd_get_in4(const struct netdev *netdev_, struct in_addr *in4,
         sin = (struct sockaddr_in *) &ifr.ifr_addr;
         netdev->in4 = sin->sin_addr;
         netdev->cache_valid |= VALID_IN4;
-        error = netdev_bsd_do_ioctl(netdev_get_name(netdev_), &ifr,
+        error = netdev_bsd_do_ioctl(netdev_get_kernel_name(netdev_), &ifr,
                                     SIOCGIFNETMASK, "SIOCGIFNETMASK");
         if (error) {
             return error;
@@ -1095,7 +1168,7 @@ netdev_bsd_get_in6(const struct netdev *netdev_, struct in6_addr *in6)
 
         if (getifaddrs(&head) != 0) {
             VLOG_ERR("getifaddrs on %s device failed: %s", netdev_name,
-                    strerror(errno));
+                    ovs_strerror(errno));
             return errno;
         }
 
@@ -1118,6 +1191,150 @@ netdev_bsd_get_in6(const struct netdev *netdev_, struct in6_addr *in6)
     return 0;
 }
 
+#if defined(__NetBSD__)
+static struct netdev *
+find_netdev_by_kernel_name(const char *kernel_name)
+{
+    struct shash device_shash;
+    struct shash_node *node;
+
+    shash_init(&device_shash);
+    netdev_get_devices(&netdev_tap_class, &device_shash);
+    SHASH_FOR_EACH(node, &device_shash) {
+        struct netdev_bsd * const dev = node->data;
+
+        if (!strcmp(dev->kernel_name, kernel_name)) {
+            shash_destroy(&device_shash);
+            return &dev->up;
+        }
+    }
+    shash_destroy(&device_shash);
+    return NULL;
+}
+
+static const char *
+netdev_bsd_convert_kernel_name_to_ovs_name(const char *kernel_name)
+{
+    const struct netdev * const netdev =
+      find_netdev_by_kernel_name(kernel_name);
+
+    if (netdev == NULL) {
+        return NULL;
+    }
+    return netdev_get_name(netdev);
+}
+#endif
+
+static int
+netdev_bsd_get_next_hop(const struct in_addr *host OVS_UNUSED,
+                        struct in_addr *next_hop OVS_UNUSED,
+                        char **netdev_name OVS_UNUSED)
+{
+#if defined(__NetBSD__)
+    static int seq = 0;
+    struct sockaddr_in sin;
+    struct sockaddr_dl sdl;
+    int s;
+    int i;
+    struct {
+        struct rt_msghdr h;
+        char space[512];
+    } buf;
+    struct rt_msghdr *rtm = &buf.h;
+    const pid_t pid = getpid();
+    char *cp;
+    ssize_t ssz;
+    bool gateway = false;
+    char *ifname = NULL;
+    int saved_errno;
+
+    memset(next_hop, 0, sizeof(*next_hop));
+    *netdev_name = NULL;
+
+    memset(&sin, 0, sizeof(sin));
+    sin.sin_len = sizeof(sin);
+    sin.sin_family = AF_INET;
+    sin.sin_port = 0;
+    sin.sin_addr = *host;
+
+    memset(&sdl, 0, sizeof(sdl));
+    sdl.sdl_len = sizeof(sdl);
+    sdl.sdl_family = AF_LINK;
+
+    s = socket(PF_ROUTE, SOCK_RAW, 0);
+    memset(&buf, 0, sizeof(buf));
+    rtm->rtm_flags = RTF_HOST|RTF_UP;
+    rtm->rtm_version = RTM_VERSION;
+    rtm->rtm_addrs = RTA_DST|RTA_IFP;
+    cp = (void *)&buf.space;
+    memcpy(cp, &sin, sizeof(sin));
+    RT_ADVANCE(cp, (struct sockaddr *)(void *)&sin);
+    memcpy(cp, &sdl, sizeof(sdl));
+    RT_ADVANCE(cp, (struct sockaddr *)(void *)&sdl);
+    rtm->rtm_msglen = cp - (char *)(void *)rtm;
+    rtm->rtm_seq = ++seq;
+    rtm->rtm_type = RTM_GET;
+    rtm->rtm_pid = pid;
+    write(s, rtm, rtm->rtm_msglen);
+    memset(&buf, 0, sizeof(buf));
+    do {
+        ssz = read(s, &buf, sizeof(buf));
+    } while (ssz > 0 && (rtm->rtm_seq != seq || rtm->rtm_pid != pid));
+    saved_errno = errno;
+    close(s);
+    if (ssz <= 0) {
+        if (ssz < 0) {
+            return saved_errno;
+        }
+        return EPIPE; /* XXX */
+    }
+    cp = (void *)&buf.space;
+    for (i = 1; i; i <<= 1) {
+        if ((rtm->rtm_addrs & i) != 0) {
+            const struct sockaddr *sa = (const void *)cp;
+
+            if ((i == RTA_GATEWAY) && sa->sa_family == AF_INET) {
+                const struct sockaddr_in * const sin =
+                  (const struct sockaddr_in *)sa;
+
+                *next_hop = sin->sin_addr;
+                gateway = true;
+            }
+            if ((i == RTA_IFP) && sa->sa_family == AF_LINK) {
+                const struct sockaddr_dl * const sdl =
+                  (const struct sockaddr_dl *)sa;
+                const size_t nlen = sdl->sdl_nlen;
+                char * const kernel_name = xmalloc(nlen + 1);
+                const char *name;
+
+                memcpy(kernel_name, sdl->sdl_data, nlen);
+                kernel_name[nlen] = 0;
+                name = netdev_bsd_convert_kernel_name_to_ovs_name(kernel_name);
+                if (name == NULL) {
+                    ifname = xstrdup(kernel_name);
+                } else {
+                    ifname = xstrdup(name);
+                }
+                free(kernel_name);
+            }
+            RT_ADVANCE(cp, sa);
+        }
+    }
+    if (ifname == NULL) {
+        return ENXIO;
+    }
+    if (!gateway) {
+        *next_hop = *host;
+    }
+    *netdev_name = ifname;
+    VLOG_DBG("host " IP_FMT " next-hop " IP_FMT " if %s",
+      IP_ARGS(host->s_addr), IP_ARGS(next_hop->s_addr), *netdev_name);
+    return 0;
+#else
+    return EOPNOTSUPP;
+#endif
+}
+
 static void
 make_in4_sockaddr(struct sockaddr *sa, struct in_addr addr)
 {
@@ -1137,7 +1354,8 @@ do_set_addr(struct netdev *netdev,
 {
     struct ifreq ifr;
     make_in4_sockaddr(&ifr.ifr_addr, addr);
-    return netdev_bsd_do_ioctl(netdev, &ifr, ioctl_nr, ioctl_name);
+    return netdev_bsd_do_ioctl(netdev_get_kernel_name(netdev), &ifr, ioctl_nr,
+                               ioctl_name);
 }
 
 static int
@@ -1149,7 +1367,9 @@ nd_to_iff_flags(enum netdev_flags nd)
     }
     if (nd & NETDEV_PROMISC) {
         iff |= IFF_PROMISC;
+#if defined(IFF_PPROMISC)
         iff |= IFF_PPROMISC;
+#endif
     }
     return iff;
 }
@@ -1180,7 +1400,7 @@ netdev_bsd_update_flags(struct netdev *netdev_, enum netdev_flags off,
         *old_flagsp = iff_to_nd_flags(old_flags);
         new_flags = (old_flags & ~nd_to_iff_flags(off)) | nd_to_iff_flags(on);
         if (new_flags != old_flags) {
-            error = set_flags(netdev_get_name(netdev_), new_flags);
+            error = set_flags(netdev_get_kernel_name(netdev_), new_flags);
             netdev_bsd_changed(netdev);
         }
     }
@@ -1240,7 +1460,7 @@ const struct netdev_class netdev_bsd_class = {
     netdev_bsd_set_in4,
     netdev_bsd_get_in6,
     NULL, /* add_router */
-    NULL, /* get_next_hop */
+    netdev_bsd_get_next_hop,
     NULL, /* get_status */
     NULL, /* arp_lookup */
 
@@ -1295,7 +1515,7 @@ const struct netdev_class netdev_tap_class = {
     netdev_bsd_set_in4,
     netdev_bsd_get_in6,
     NULL, /* add_router */
-    NULL, /* get_next_hop */
+    netdev_bsd_get_next_hop,
     NULL, /* get_status */
     NULL, /* arp_lookup */
 
@@ -1329,11 +1549,10 @@ get_flags(const struct netdev *netdev, int *flags)
     struct ifreq ifr;
     int error;
 
-    error = netdev_bsd_do_ioctl(netdev->name, &ifr,
+    error = netdev_bsd_do_ioctl(netdev_get_kernel_name(netdev), &ifr,
                                 SIOCGIFFLAGS, "SIOCGIFFLAGS");
 
-    *flags = 0xFFFF0000 & (ifr.ifr_flagshigh << 16);
-    *flags |= 0x0000FFFF & ifr.ifr_flags;
+    *flags = ifr_get_flags(&ifr);
 
     return error;
 }
@@ -1343,8 +1562,7 @@ set_flags(const char *name, int flags)
 {
     struct ifreq ifr;
 
-    ifr.ifr_flags = 0x0000FFFF & flags;
-    ifr.ifr_flagshigh = (0xFFFF0000 & flags) >> 16;
+    ifr_set_flags(&ifr, flags);
 
     return netdev_bsd_do_ioctl(name, &ifr, SIOCSIFFLAGS, "SIOCSIFFLAGS");
 }
@@ -1375,7 +1593,7 @@ get_etheraddr(const char *netdev_name, uint8_t ea[ETH_ADDR_LEN])
 
     if (getifaddrs(&head) != 0) {
         VLOG_ERR("getifaddrs on %s device failed: %s", netdev_name,
-                strerror(errno));
+                ovs_strerror(errno));
         return errno;
     }
 
@@ -1398,9 +1616,11 @@ get_etheraddr(const char *netdev_name, uint8_t ea[ETH_ADDR_LEN])
 }
 
 static int
-set_etheraddr(const char *netdev_name, int hwaddr_family,
-              int hwaddr_len, const uint8_t mac[ETH_ADDR_LEN])
+set_etheraddr(const char *netdev_name OVS_UNUSED, int hwaddr_family OVS_UNUSED,
+              int hwaddr_len OVS_UNUSED,
+              const uint8_t mac[ETH_ADDR_LEN] OVS_UNUSED)
 {
+#if defined(__FreeBSD__)
     struct ifreq ifr;
 
     memset(&ifr, 0, sizeof ifr);
@@ -1410,10 +1630,63 @@ set_etheraddr(const char *netdev_name, int hwaddr_family,
     memcpy(ifr.ifr_addr.sa_data, mac, hwaddr_len);
     if (ioctl(af_inet_sock, SIOCSIFLLADDR, &ifr) < 0) {
         VLOG_ERR("ioctl(SIOCSIFLLADDR) on %s device failed: %s",
-                 netdev_name, strerror(errno));
+                 netdev_name, ovs_strerror(errno));
         return errno;
     }
     return 0;
+#elif defined(__NetBSD__)
+    struct if_laddrreq req;
+    struct sockaddr_dl *sdl;
+    struct sockaddr_storage oldaddr;
+    int ret;
+
+    /*
+     * get the old address, add new one, and then remove old one.
+     */
+
+    if (hwaddr_len != ETH_ADDR_LEN) {
+        /* just to be safe about sockaddr storage size */
+        return EOPNOTSUPP;
+    }
+    memset(&req, 0, sizeof(req));
+    strncpy(req.iflr_name, netdev_name, sizeof(req.iflr_name));
+    req.addr.ss_len = sizeof(req.addr);
+    req.addr.ss_family = hwaddr_family;
+    sdl = (struct sockaddr_dl *)&req.addr;
+    sdl->sdl_alen = hwaddr_len;
+    ret = ioctl(af_link_sock, SIOCGLIFADDR, &req);
+    if (ret == -1) {
+        return errno;
+    }
+    if (!memcmp(&sdl->sdl_data[sdl->sdl_nlen], mac, hwaddr_len)) {
+        return 0;
+    }
+    oldaddr = req.addr;
+
+    memset(&req, 0, sizeof(req));
+    strncpy(req.iflr_name, netdev_name, sizeof(req.iflr_name));
+    req.flags = IFLR_ACTIVE;
+    sdl = (struct sockaddr_dl *)&req.addr;
+    sdl->sdl_len = offsetof(struct sockaddr_dl, sdl_data) + hwaddr_len;
+    sdl->sdl_alen = hwaddr_len;
+    sdl->sdl_family = hwaddr_family;
+    memcpy(sdl->sdl_data, mac, hwaddr_len);
+    ret = ioctl(af_link_sock, SIOCALIFADDR, &req);
+    if (ret == -1) {
+        return errno;
+    }
+
+    memset(&req, 0, sizeof(req));
+    strncpy(req.iflr_name, netdev_name, sizeof(req.iflr_name));
+    req.addr = oldaddr;
+    ret = ioctl(af_link_sock, SIOCDLIFADDR, &req);
+    if (ret == -1) {
+        return errno;
+    }
+    return 0;
+#else
+#error not implemented
+#endif
 }
 
 static int
@@ -1423,8 +1696,27 @@ netdev_bsd_do_ioctl(const char *name, struct ifreq *ifr, unsigned long cmd,
     strncpy(ifr->ifr_name, name, sizeof ifr->ifr_name);
     if (ioctl(af_inet_sock, cmd, ifr) == -1) {
         VLOG_DBG_RL(&rl, "%s: ioctl(%s) failed: %s", name, cmd_name,
-                    strerror(errno));
+                    ovs_strerror(errno));
         return errno;
     }
     return 0;
 }
+
+static int
+ifr_get_flags(const struct ifreq *ifr)
+{
+#ifdef HAVE_STRUCT_IFREQ_IFR_FLAGSHIGH
+    return (ifr->ifr_flagshigh << 16) | ifr->ifr_flags;
+#else
+    return ifr->ifr_flags;
+#endif
+}
+
+static void
+ifr_set_flags(struct ifreq *ifr, int flags)
+{
+    ifr->ifr_flags = flags;
+#ifdef HAVE_STRUCT_IFREQ_IFR_FLAGSHIGH
+    ifr->ifr_flagshigh = flags >> 16;
+#endif
+}
index 908fef2..a940df8 100644 (file)
 #include "poll-loop.h"
 #include "shash.h"
 #include "sset.h"
+#include "stream.h"
+#include "unaligned.h"
 #include "unixctl.h"
 #include "vlog.h"
 
 VLOG_DEFINE_THIS_MODULE(netdev_dummy);
 
-#ifdef __FreeBSD__
-#define FREE_BSD 1
-#else
-#define FREE_BSD 0
-#endif
+struct dummy_stream {
+    struct stream *stream;
+    struct ofpbuf rxbuf;
+    struct list txq;
+};
 
 struct netdev_dummy {
     struct netdev up;
@@ -51,13 +53,22 @@ struct netdev_dummy {
     unsigned int change_seq;
     int ifindex;
 
+    struct pstream *pstream;
+    struct dummy_stream *streams;
+    size_t n_streams;
+
     struct list rxes;           /* List of child "netdev_rx_dummy"s. */
 };
 
+/* Max 'recv_queue_len' in struct netdev_dummy. */
+#define NETDEV_DUMMY_MAX_QUEUE 100
+
 struct netdev_rx_dummy {
     struct netdev_rx up;
     struct list node;           /* In netdev_dummy's "rxes" list. */
     struct list recv_queue;
+    int recv_queue_len;         /* list_size(&recv_queue). */
+    bool listening;
 };
 
 static struct shash dummy_netdevs = SHASH_INITIALIZER(&dummy_netdevs);
@@ -68,6 +79,9 @@ static unixctl_cb_func netdev_dummy_set_admin_state;
 static int netdev_dummy_create(const struct netdev_class *, const char *,
                                struct netdev **);
 static void netdev_dummy_poll_notify(struct netdev_dummy *);
+static void netdev_dummy_queue_packet(struct netdev_dummy *, struct ofpbuf *);
+
+static void dummy_stream_close(struct dummy_stream *);
 
 static bool
 is_dummy_class(const struct netdev_class *class)
@@ -89,6 +103,140 @@ netdev_rx_dummy_cast(const struct netdev_rx *rx)
     return CONTAINER_OF(rx, struct netdev_rx_dummy, up);
 }
 
+static void
+netdev_dummy_run(void)
+{
+    struct shash_node *node;
+
+    SHASH_FOR_EACH (node, &dummy_netdevs) {
+        struct netdev_dummy *dev = node->data;
+        size_t i;
+
+        if (dev->pstream) {
+            struct stream *new_stream;
+            int error;
+
+            error = pstream_accept(dev->pstream, &new_stream);
+            if (!error) {
+                struct dummy_stream *s;
+
+                dev->streams = xrealloc(dev->streams,
+                                        ((dev->n_streams + 1)
+                                         * sizeof *dev->streams));
+                s = &dev->streams[dev->n_streams++];
+                s->stream = new_stream;
+                ofpbuf_init(&s->rxbuf, 2048);
+                list_init(&s->txq);
+            } else if (error != EAGAIN) {
+                VLOG_WARN("%s: accept failed (%s)",
+                          pstream_get_name(dev->pstream), ovs_strerror(error));
+                pstream_close(dev->pstream);
+                dev->pstream = NULL;
+            }
+        }
+
+        for (i = 0; i < dev->n_streams; i++) {
+            struct dummy_stream *s = &dev->streams[i];
+            int error = 0;
+            size_t n;
+
+            stream_run(s->stream);
+
+            if (!list_is_empty(&s->txq)) {
+                struct ofpbuf *txbuf;
+                int retval;
+
+                txbuf = ofpbuf_from_list(list_front(&s->txq));
+                retval = stream_send(s->stream, txbuf->data, txbuf->size);
+                if (retval > 0) {
+                    ofpbuf_pull(txbuf, retval);
+                    if (!txbuf->size) {
+                        list_remove(&txbuf->list_node);
+                        ofpbuf_delete(txbuf);
+                    }
+                } else if (retval != -EAGAIN) {
+                    error = -retval;
+                }
+            }
+
+            if (!error) {
+                if (s->rxbuf.size < 2) {
+                    n = 2 - s->rxbuf.size;
+                } else {
+                    uint16_t frame_len;
+
+                    frame_len = ntohs(get_unaligned_be16(s->rxbuf.data));
+                    if (frame_len < ETH_HEADER_LEN) {
+                        error = EPROTO;
+                        n = 0;
+                    } else {
+                        n = (2 + frame_len) - s->rxbuf.size;
+                    }
+                }
+            }
+            if (!error) {
+                int retval;
+
+                ofpbuf_prealloc_tailroom(&s->rxbuf, n);
+                retval = stream_recv(s->stream, ofpbuf_tail(&s->rxbuf), n);
+                if (retval > 0) {
+                    s->rxbuf.size += retval;
+                    if (retval == n && s->rxbuf.size > 2) {
+                        ofpbuf_pull(&s->rxbuf, 2);
+                        netdev_dummy_queue_packet(dev,
+                                                  ofpbuf_clone(&s->rxbuf));
+                        ofpbuf_clear(&s->rxbuf);
+                    }
+                } else if (retval != -EAGAIN) {
+                    error = (retval < 0 ? -retval
+                             : s->rxbuf.size ? EPROTO
+                             : EOF);
+                }
+            }
+
+            if (error) {
+                VLOG_DBG("%s: closing connection (%s)",
+                         stream_get_name(s->stream),
+                         ovs_retval_to_string(error));
+                dummy_stream_close(&dev->streams[i]);
+                dev->streams[i] = dev->streams[--dev->n_streams];
+            }
+        }
+    }
+}
+
+static void
+dummy_stream_close(struct dummy_stream *s)
+{
+    stream_close(s->stream);
+    ofpbuf_uninit(&s->rxbuf);
+    ofpbuf_list_delete(&s->txq);
+}
+
+static void
+netdev_dummy_wait(void)
+{
+    struct shash_node *node;
+
+    SHASH_FOR_EACH (node, &dummy_netdevs) {
+        struct netdev_dummy *dev = node->data;
+        size_t i;
+
+        if (dev->pstream) {
+            pstream_wait(dev->pstream);
+        }
+        for (i = 0; i < dev->n_streams; i++) {
+            struct dummy_stream *s = &dev->streams[i];
+
+            stream_run_wait(s->stream);
+            if (!list_is_empty(&s->txq)) {
+                stream_send_wait(s->stream);
+            }
+            stream_recv_wait(s->stream);
+        }
+    }
+}
+
 static int
 netdev_dummy_create(const struct netdev_class *class, const char *name,
                     struct netdev **netdevp)
@@ -108,6 +256,11 @@ netdev_dummy_create(const struct netdev_class *class, const char *name,
     netdev->flags = 0;
     netdev->change_seq = 1;
     netdev->ifindex = -EOPNOTSUPP;
+
+    netdev->pstream = NULL;
+    netdev->streams = NULL;
+    netdev->n_streams = 0;
+
     list_init(&netdev->rxes);
 
     shash_add(&dummy_netdevs, name, netdev);
@@ -123,9 +276,15 @@ static void
 netdev_dummy_destroy(struct netdev *netdev_)
 {
     struct netdev_dummy *netdev = netdev_dummy_cast(netdev_);
+    size_t i;
 
     shash_find_and_delete(&dummy_netdevs,
                           netdev_get_name(netdev_));
+    pstream_close(netdev->pstream);
+    for (i = 0; i < netdev->n_streams; i++) {
+        dummy_stream_close(&netdev->streams[i]);
+    }
+    free(netdev->streams);
     free(netdev);
 }
 
@@ -137,6 +296,9 @@ netdev_dummy_get_config(const struct netdev *netdev_, struct smap *args)
     if (netdev->ifindex >= 0) {
         smap_add_format(args, "ifindex", "%d", netdev->ifindex);
     }
+    if (netdev->pstream) {
+        smap_add(args, "pstream", pstream_get_name(netdev->pstream));
+    }
     return 0;
 }
 
@@ -144,8 +306,27 @@ static int
 netdev_dummy_set_config(struct netdev *netdev_, const struct smap *args)
 {
     struct netdev_dummy *netdev = netdev_dummy_cast(netdev_);
+    const char *pstream;
 
     netdev->ifindex = smap_get_int(args, "ifindex", -EOPNOTSUPP);
+
+    pstream = smap_get(args, "pstream");
+    if (!pstream
+        || !netdev->pstream
+        || strcmp(pstream_get_name(netdev->pstream), pstream)) {
+        pstream_close(netdev->pstream);
+        netdev->pstream = NULL;
+
+        if (pstream) {
+            int error;
+
+            error = pstream_open(pstream, &netdev->pstream, DSCP_DEFAULT);
+            if (error) {
+                VLOG_WARN("%s: open failed (%s)",
+                          pstream, ovs_strerror(error));
+            }
+        }
+    }
     return 0;
 }
 
@@ -159,6 +340,7 @@ netdev_dummy_rx_open(struct netdev *netdev_, struct netdev_rx **rxp)
     netdev_rx_init(&rx->up, &netdev->up, &netdev_rx_dummy_class);
     list_push_back(&netdev->rxes, &rx->node);
     list_init(&rx->recv_queue);
+    rx->recv_queue_len = 0;
 
     *rxp = &rx->up;
     return 0;
@@ -176,6 +358,7 @@ netdev_rx_dummy_recv(struct netdev_rx *rx_, void *buffer, size_t size)
     }
 
     packet = ofpbuf_from_list(list_pop_front(&rx->recv_queue));
+    rx->recv_queue_len--;
     if (packet->size > size) {
         return -EMSGSIZE;
     }
@@ -210,18 +393,46 @@ netdev_rx_dummy_drain(struct netdev_rx *rx_)
 {
     struct netdev_rx_dummy *rx = netdev_rx_dummy_cast(rx_);
     ofpbuf_list_delete(&rx->recv_queue);
+    rx->recv_queue_len = 0;
     return 0;
 }
 
 static int
-netdev_dummy_send(struct netdev *netdev, const void *buffer OVS_UNUSED,
-                  size_t size)
+netdev_dummy_send(struct netdev *netdev, const void *buffer, size_t size)
 {
     struct netdev_dummy *dev = netdev_dummy_cast(netdev);
+    size_t i;
+
+    if (size < ETH_HEADER_LEN) {
+        return EMSGSIZE;
+    } else {
+        const struct eth_header *eth = buffer;
+        int max_size;
+
+        max_size = dev->mtu + ETH_HEADER_LEN;
+        if (eth->eth_type == htons(ETH_TYPE_VLAN)) {
+            max_size += VLAN_HEADER_LEN;
+        }
+        if (size > max_size) {
+            return EMSGSIZE;
+        }
+    }
 
     dev->stats.tx_packets++;
     dev->stats.tx_bytes += size;
 
+    for (i = 0; i < dev->n_streams; i++) {
+        struct dummy_stream *s = &dev->streams[i];
+
+        if (list_size(&s->txq) < NETDEV_DUMMY_MAX_QUEUE) {
+            struct ofpbuf *b;
+
+            b = ofpbuf_clone_data_with_headroom(buffer, size, 2);
+            put_unaligned_be16(ofpbuf_push_uninit(b, 2), htons(size));
+            list_push_back(&s->txq, &b->list_node);
+        }
+    }
+
     return 0;
 }
 
@@ -333,8 +544,8 @@ netdev_dummy_poll_notify(struct netdev_dummy *dev)
 static const struct netdev_class dummy_class = {
     "dummy",
     NULL,                       /* init */
-    NULL,                       /* run */
-    NULL,                       /* wait */
+    netdev_dummy_run,
+    netdev_dummy_wait,
 
     netdev_dummy_create,
     netdev_dummy_destroy,
@@ -413,7 +624,7 @@ eth_from_packet_or_flow(const char *s)
      * settle for parsing a datapath key for now.
      */
     ofpbuf_init(&odp_key, 0);
-    error = odp_flow_key_from_string(s, NULL, &odp_key);
+    error = odp_flow_from_string(s, NULL, &odp_key, NULL);
     if (error) {
         ofpbuf_uninit(&odp_key);
         return NULL;
@@ -433,12 +644,39 @@ eth_from_packet_or_flow(const char *s)
     return packet;
 }
 
+static void
+netdev_dummy_queue_packet__(struct netdev_rx_dummy *rx, struct ofpbuf *packet)
+{
+    list_push_back(&rx->recv_queue, &packet->list_node);
+    rx->recv_queue_len++;
+}
+
+static void
+netdev_dummy_queue_packet(struct netdev_dummy *dummy, struct ofpbuf *packet)
+{
+    struct netdev_rx_dummy *rx, *prev;
+
+    prev = NULL;
+    LIST_FOR_EACH (rx, node, &dummy->rxes) {
+        if (rx->recv_queue_len < NETDEV_DUMMY_MAX_QUEUE) {
+            if (prev) {
+                netdev_dummy_queue_packet__(prev, ofpbuf_clone(packet));
+            }
+            prev = rx;
+        }
+    }
+    if (prev) {
+        netdev_dummy_queue_packet__(prev, packet);
+    } else {
+        ofpbuf_delete(packet);
+    }
+}
+
 static void
 netdev_dummy_receive(struct unixctl_conn *conn,
                      int argc, const char *argv[], void *aux OVS_UNUSED)
 {
     struct netdev_dummy *dummy_dev;
-    int n_listeners;
     int i;
 
     dummy_dev = shash_find_data(&dummy_netdevs, argv[1]);
@@ -447,9 +685,7 @@ netdev_dummy_receive(struct unixctl_conn *conn,
         return;
     }
 
-    n_listeners = 0;
     for (i = 2; i < argc; i++) {
-        struct netdev_rx_dummy *rx;
         struct ofpbuf *packet;
 
         packet = eth_from_packet_or_flow(argv[i]);
@@ -461,20 +697,10 @@ netdev_dummy_receive(struct unixctl_conn *conn,
         dummy_dev->stats.rx_packets++;
         dummy_dev->stats.rx_bytes += packet->size;
 
-        n_listeners = 0;
-        LIST_FOR_EACH (rx, node, &dummy_dev->rxes) {
-            struct ofpbuf *copy = ofpbuf_clone(packet);
-            list_push_back(&rx->recv_queue, &copy->list_node);
-            n_listeners++;
-        }
-        ofpbuf_delete(packet);
+        netdev_dummy_queue_packet(dummy_dev, packet);
     }
 
-    if (!n_listeners) {
-        unixctl_command_reply(conn, "packets queued but nobody listened");
-    } else {
-        unixctl_command_reply(conn, "success");
-    }
+    unixctl_command_reply(conn, NULL);
 }
 
 static void
@@ -553,7 +779,5 @@ netdev_dummy_register(bool override)
     }
     netdev_register_provider(&dummy_class);
 
-    if (FREE_BSD) {
-        netdev_vport_tunnel_register();
-    }
+    netdev_vport_tunnel_register();
 }
index b548996..8790f14 100644 (file)
@@ -22,6 +22,7 @@
 #include <fcntl.h>
 #include <arpa/inet.h>
 #include <inttypes.h>
+#include <linux/filter.h>
 #include <linux/gen_stats.h>
 #include <linux/if_ether.h>
 #include <linux/if_tun.h>
@@ -474,7 +475,7 @@ netdev_linux_init(void)
         af_inet_sock = socket(AF_INET, SOCK_DGRAM, 0);
         status = af_inet_sock >= 0 ? 0 : errno;
         if (status) {
-            VLOG_ERR("failed to create inet socket: %s", strerror(status));
+            VLOG_ERR("failed to create inet socket: %s", ovs_strerror(status));
         }
 
         /* Create rtnetlink socket. */
@@ -482,7 +483,7 @@ netdev_linux_init(void)
             status = nl_sock_create(NETLINK_ROUTE, &rtnl_sock);
             if (status) {
                 VLOG_ERR_RL(&rl, "failed to create rtnetlink socket: %s",
-                            strerror(status));
+                            ovs_strerror(status));
             }
         }
     }
@@ -671,7 +672,7 @@ netdev_linux_create_tap(const struct netdev_class *class OVS_UNUSED,
     state->fd = open(tap_dev, O_RDWR);
     if (state->fd < 0) {
         error = errno;
-        VLOG_WARN("opening \"%s\" failed: %s", tap_dev, strerror(error));
+        VLOG_WARN("opening \"%s\" failed: %s", tap_dev, ovs_strerror(error));
         goto error_unref_notifier;
     }
 
@@ -680,7 +681,7 @@ netdev_linux_create_tap(const struct netdev_class *class OVS_UNUSED,
     ovs_strzcpy(ifr.ifr_name, name, sizeof ifr.ifr_name);
     if (ioctl(state->fd, TUNSETIFF, &ifr) == -1) {
         VLOG_WARN("%s: creating tap device failed: %s", name,
-                  strerror(errno));
+                  ovs_strerror(errno));
         error = errno;
         goto error_unref_notifier;
     }
@@ -744,12 +745,20 @@ netdev_linux_rx_open(struct netdev *netdev_, struct netdev_rx **rxp)
     } else {
         struct sockaddr_ll sll;
         int ifindex;
+        /* Result of tcpdump -dd inbound */
+        static struct sock_filter filt[] = {
+            { 0x28, 0, 0, 0xfffff004 }, /* ldh [0] */
+            { 0x15, 0, 1, 0x00000004 }, /* jeq #4     jt 2  jf 3 */
+            { 0x6, 0, 0, 0x00000000 },  /* ret #0 */
+            { 0x6, 0, 0, 0x0000ffff }   /* ret #65535 */
+        };
+        static struct sock_fprog fprog = { ARRAY_SIZE(filt), filt };
 
         /* Create file descriptor. */
         fd = socket(PF_PACKET, SOCK_RAW, 0);
         if (fd < 0) {
             error = errno;
-            VLOG_ERR("failed to create raw socket (%s)", strerror(error));
+            VLOG_ERR("failed to create raw socket (%s)", ovs_strerror(error));
             goto error;
         }
 
@@ -773,7 +782,17 @@ netdev_linux_rx_open(struct netdev *netdev_, struct netdev_rx **rxp)
         if (bind(fd, (struct sockaddr *) &sll, sizeof sll) < 0) {
             error = errno;
             VLOG_ERR("%s: failed to bind raw socket (%s)",
-                     netdev_get_name(netdev_), strerror(error));
+                     netdev_get_name(netdev_), ovs_strerror(error));
+            goto error;
+        }
+
+        /* Filter for only inbound packets. */
+        error = setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog,
+                           sizeof fprog);
+        if (error) {
+            error = errno;
+            VLOG_ERR("%s: failed attach filter (%s)",
+                     netdev_get_name(netdev_), ovs_strerror(error));
             goto error;
         }
     }
@@ -823,7 +842,7 @@ netdev_rx_linux_recv(struct netdev_rx *rx_, void *data, size_t size)
     } else {
         if (errno != EAGAIN) {
             VLOG_WARN_RL(&rl, "error receiving Ethernet packet on %s: %s",
-                         strerror(errno), netdev_rx_get_name(rx_));
+                         ovs_strerror(errno), netdev_rx_get_name(rx_));
         }
         return -errno;
     }
@@ -910,7 +929,8 @@ netdev_linux_send(struct netdev *netdev_, const void *data, size_t size)
             /* Use the tap fd to send to this device.  This is essential for
              * tap devices, because packets sent to a tap device with an
              * AF_PACKET socket will loop back to be *received* again on the
-             * tap device. */
+             * tap device.  This doesn't occur on other interface types
+             * because we attach a socket filter to the rx socket. */
             struct netdev_linux *netdev = netdev_linux_cast(netdev_);
 
             retval = write(netdev->state.tap.fd, data, size);
@@ -926,7 +946,7 @@ netdev_linux_send(struct netdev *netdev_, const void *data, size_t size)
                 continue;
             } else if (errno != EAGAIN) {
                 VLOG_WARN_RL(&rl, "error sending Ethernet packet on %s: %s",
-                             netdev_get_name(netdev_), strerror(errno));
+                             netdev_get_name(netdev_), ovs_strerror(errno));
             }
             return errno;
         } else if (retval != size) {
@@ -977,11 +997,7 @@ netdev_linux_set_etheraddr(struct netdev *netdev_,
 
     /* Tap devices must be brought down before setting the address. */
     if (is_tap_netdev(netdev_)) {
-        enum netdev_flags flags;
-
-        if (!netdev_get_flags(netdev_, &flags) && (flags & NETDEV_UP)) {
-            netdev_turn_flags_off(netdev_, NETDEV_UP, &sf);
-        }
+        netdev_turn_flags_off(netdev_, NETDEV_UP, &sf);
     }
     error = set_etheraddr(netdev_get_name(netdev_), mac);
     if (!error || error == ENODEV) {
@@ -1247,7 +1263,7 @@ check_for_working_netlink_stats(void)
         } else {
             VLOG_INFO("RTM_GETLINK failed (%s), obtaining netdev stats "
                       "via proc (you are probably running a pre-2.6.19 "
-                      "kernel)", strerror(error));
+                      "kernel)", ovs_strerror(error));
             return false;
         }
     }
@@ -1326,7 +1342,8 @@ get_stats_via_vport(const struct netdev *netdev_,
         error = get_stats_via_vport__(netdev_, stats);
         if (error && error != ENOENT) {
             VLOG_WARN_RL(&rl, "%s: obtaining netdev stats via vport failed "
-                         "(%s)", netdev_get_name(netdev_), strerror(error));
+                         "(%s)",
+                         netdev_get_name(netdev_), ovs_strerror(error));
         }
         netdev->vport_stats_error = error;
         netdev->cache_valid |= VALID_VPORT_STAT_ERROR;
@@ -1761,7 +1778,7 @@ netdev_linux_set_policing(struct netdev *netdev_,
     error = tc_add_del_ingress_qdisc(netdev_, false);
     if (error) {
         VLOG_WARN_RL(&rl, "%s: removing policing failed: %s",
-                     netdev_name, strerror(error));
+                     netdev_name, ovs_strerror(error));
         goto out;
     }
 
@@ -1769,14 +1786,14 @@ netdev_linux_set_policing(struct netdev *netdev_,
         error = tc_add_del_ingress_qdisc(netdev_, true);
         if (error) {
             VLOG_WARN_RL(&rl, "%s: adding policing qdisc failed: %s",
-                         netdev_name, strerror(error));
+                         netdev_name, ovs_strerror(error));
             goto out;
         }
 
         error = tc_add_policer(netdev_, kbits_rate, kbits_burst);
         if (error){
             VLOG_WARN_RL(&rl, "%s: adding policing action failed: %s",
-                    netdev_name, strerror(error));
+                    netdev_name, ovs_strerror(error));
             goto out;
         }
     }
@@ -2219,7 +2236,7 @@ netdev_linux_add_router(struct netdev *netdev OVS_UNUSED, struct in_addr router)
     rt.rt_flags = RTF_UP | RTF_GATEWAY;
     error = ioctl(af_inet_sock, SIOCADDRT, &rt) < 0 ? errno : 0;
     if (error) {
-        VLOG_WARN("ioctl(SIOCADDRT): %s", strerror(error));
+        VLOG_WARN("ioctl(SIOCADDRT): %s", ovs_strerror(error));
     }
     return error;
 }
@@ -2236,7 +2253,7 @@ netdev_linux_get_next_hop(const struct in_addr *host, struct in_addr *next_hop,
     *netdev_name = NULL;
     stream = fopen(fn, "r");
     if (stream == NULL) {
-        VLOG_WARN_RL(&rl, "%s: open failed: %s", fn, strerror(errno));
+        VLOG_WARN_RL(&rl, "%s: open failed: %s", fn, ovs_strerror(errno));
         return errno;
     }
 
@@ -2348,7 +2365,8 @@ 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),
+                     ovs_strerror(retval));
     }
     return retval;
 }
@@ -2617,7 +2635,7 @@ htb_setup_class__(struct netdev *netdev, unsigned int handle,
                      tc_get_major(handle), tc_get_minor(handle),
                      tc_get_major(parent), tc_get_minor(parent),
                      class->min_rate, class->max_rate,
-                     class->burst, class->priority, strerror(error));
+                     class->burst, class->priority, ovs_strerror(error));
     }
     return error;
 }
@@ -3277,7 +3295,7 @@ hfsc_setup_class__(struct netdev *netdev, unsigned int handle,
                      netdev_get_name(netdev),
                      tc_get_major(handle), tc_get_minor(handle),
                      tc_get_major(parent), tc_get_minor(parent),
-                     class->min_rate, class->max_rate, strerror(error));
+                     class->min_rate, class->max_rate, ovs_strerror(error));
     }
 
     return error;
@@ -3782,7 +3800,7 @@ read_psched(void)
 
     stream = fopen(fn, "r");
     if (!stream) {
-        VLOG_WARN("%s: open failed: %s", fn, strerror(errno));
+        VLOG_WARN("%s: open failed: %s", fn, ovs_strerror(errno));
         return;
     }
 
@@ -3986,7 +4004,7 @@ tc_query_class(const struct netdev *netdev,
                      netdev_get_name(netdev),
                      tc_get_major(handle), tc_get_minor(handle),
                      tc_get_major(parent), tc_get_minor(parent),
-                     strerror(error));
+                     ovs_strerror(error));
     }
     return error;
 }
@@ -4011,7 +4029,7 @@ tc_delete_class(const struct netdev *netdev, unsigned int handle)
         VLOG_WARN_RL(&rl, "delete %s class %u:%u failed (%s)",
                      netdev_get_name(netdev),
                      tc_get_major(handle), tc_get_minor(handle),
-                     strerror(error));
+                     ovs_strerror(error));
     }
     return error;
 }
@@ -4111,7 +4129,7 @@ tc_query_qdisc(const struct netdev *netdev_)
     } else {
         /* Who knows?  Maybe the device got deleted. */
         VLOG_WARN_RL(&rl, "query %s qdisc failed (%s)",
-                     netdev_get_name(netdev_), strerror(error));
+                     netdev_get_name(netdev_), ovs_strerror(error));
         ops = &tc_ops_other;
     }
 
@@ -4340,7 +4358,7 @@ get_stats_via_proc(const char *netdev_name, struct netdev_stats *stats)
 
     stream = fopen(fn, "r");
     if (!stream) {
-        VLOG_WARN_RL(&rl, "%s: open failed: %s", fn, strerror(errno));
+        VLOG_WARN_RL(&rl, "%s: open failed: %s", fn, ovs_strerror(errno));
         return errno;
     }
 
@@ -4420,7 +4438,7 @@ do_get_ifindex(const char *netdev_name)
     COVERAGE_INC(netdev_get_ifindex);
     if (ioctl(af_inet_sock, SIOCGIFINDEX, &ifr) < 0) {
         VLOG_WARN_RL(&rl, "ioctl(SIOCGIFINDEX) on %s device failed: %s",
-                     netdev_name, strerror(errno));
+                     netdev_name, ovs_strerror(errno));
         return -errno;
     }
     return ifr.ifr_ifindex;
@@ -4463,7 +4481,7 @@ get_etheraddr(const char *netdev_name, uint8_t ea[ETH_ADDR_LEN])
          * to INFO for that case. */
         VLOG(errno == ENODEV ? VLL_INFO : VLL_ERR,
              "ioctl(SIOCGIFHWADDR) on %s device failed: %s",
-             netdev_name, strerror(errno));
+             netdev_name, ovs_strerror(errno));
         return errno;
     }
     hwaddr_family = ifr.ifr_hwaddr.sa_family;
@@ -4488,7 +4506,7 @@ set_etheraddr(const char *netdev_name,
     COVERAGE_INC(netdev_set_hwaddr);
     if (ioctl(af_inet_sock, SIOCSIFHWADDR, &ifr) < 0) {
         VLOG_ERR("ioctl(SIOCSIFHWADDR) on %s device failed: %s",
-                 netdev_name, strerror(errno));
+                 netdev_name, ovs_strerror(errno));
         return errno;
     }
     return 0;
@@ -4510,7 +4528,7 @@ netdev_linux_do_ethtool(const char *name, struct ethtool_cmd *ecmd,
     } else {
         if (errno != EOPNOTSUPP) {
             VLOG_WARN_RL(&rl, "ethtool command %s on network device %s "
-                         "failed: %s", cmd_name, name, strerror(errno));
+                         "failed: %s", cmd_name, name, ovs_strerror(errno));
         } else {
             /* The device doesn't support this operation.  That's pretty
              * common, so there's no point in logging anything. */
@@ -4526,7 +4544,7 @@ netdev_linux_do_ioctl(const char *name, struct ifreq *ifr, int cmd,
     ovs_strzcpy(ifr->ifr_name, name, sizeof ifr->ifr_name);
     if (ioctl(af_inet_sock, cmd, ifr) == -1) {
         VLOG_DBG_RL(&rl, "%s: ioctl(%s) failed: %s", name, cmd_name,
-                     strerror(errno));
+                     ovs_strerror(errno));
         return errno;
     }
     return 0;
@@ -4564,7 +4582,8 @@ af_packet_sock(void)
             }
         } else {
             sock = -errno;
-            VLOG_ERR("failed to create packet socket: %s", strerror(errno));
+            VLOG_ERR("failed to create packet socket: %s",
+                     ovs_strerror(errno));
         }
     }
 
index 3382a39..9af0398 100644 (file)
@@ -470,7 +470,7 @@ struct netdev_class {
      * anyhow. */
     int (*add_router)(struct netdev *netdev, struct in_addr router);
 
-    /* Looks up the next hop for 'host'.  If succesful, stores the next hop
+    /* Looks up the next hop for 'host'.  If successful, stores the next hop
      * gateway's address (0 if 'host' is on a directly connected network) in
      * '*next_hop' and a copy of the name of the device to reach 'host' in
      * '*netdev_name', and returns 0.  The caller is responsible for freeing
@@ -575,7 +575,7 @@ const struct netdev_class *netdev_lookup_provider(const char *type);
 extern const struct netdev_class netdev_linux_class;
 extern const struct netdev_class netdev_internal_class;
 extern const struct netdev_class netdev_tap_class;
-#ifdef __FreeBSD__
+#if defined(__FreeBSD__) || defined(__NetBSD__)
 extern const struct netdev_class netdev_bsd_class;
 #endif
 
index 699ed71..885bf5e 100644 (file)
@@ -115,14 +115,18 @@ netdev_vport_needs_dst_port(const struct netdev *dev)
 }
 
 const char *
-netdev_vport_get_dpif_port(const struct netdev *netdev)
+netdev_vport_class_get_dpif_port(const struct netdev_class *class)
 {
-    const char *dpif_port;
+    return is_vport_class(class) ? vport_class_cast(class)->dpif_port : NULL;
+}
 
+const char *
+netdev_vport_get_dpif_port(const struct netdev *netdev,
+                           char namebuf[], size_t bufsize)
+{
     if (netdev_vport_needs_dst_port(netdev)) {
         const struct netdev_vport *vport = netdev_vport_cast(netdev);
         const char *type = netdev_get_type(netdev);
-        static char dpif_port_combined[IFNAMSIZ];
 
         /*
          * Note: IFNAMSIZ is 16 bytes long. The maximum length of a VXLAN
@@ -130,18 +134,25 @@ netdev_vport_get_dpif_port(const struct netdev *netdev)
          * assert here on the size of strlen(type) in case that changes
          * in the future.
          */
+        BUILD_ASSERT(NETDEV_VPORT_NAME_BUFSIZE >= IFNAMSIZ);
         ovs_assert(strlen(type) + 10 < IFNAMSIZ);
-        snprintf(dpif_port_combined, IFNAMSIZ, "%s_sys_%d", type,
+        snprintf(namebuf, bufsize, "%s_sys_%d", type,
                  ntohs(vport->tnl_cfg.dst_port));
-        return dpif_port_combined;
+        return namebuf;
     } else {
         const struct netdev_class *class = netdev_get_class(netdev);
-        dpif_port = (is_vport_class(class)
-                     ? vport_class_cast(class)->dpif_port
-                     : NULL);
+        const char *dpif_port = netdev_vport_class_get_dpif_port(class);
+        return dpif_port ? dpif_port : netdev_get_name(netdev);
     }
+}
 
-    return dpif_port ? dpif_port : netdev_get_name(netdev);
+char *
+netdev_vport_get_dpif_port_strdup(const struct netdev *netdev)
+{
+    char namebuf[NETDEV_VPORT_NAME_BUFSIZE];
+
+    return xstrdup(netdev_vport_get_dpif_port(netdev, namebuf,
+                                              sizeof namebuf));
 }
 
 static int
@@ -192,7 +203,7 @@ netdev_vport_get_etheraddr(const struct netdev *netdev,
 static int
 tunnel_get_status(const struct netdev *netdev, struct smap *smap)
 {
-    static char iface[IFNAMSIZ];
+    char iface[IFNAMSIZ];
     ovs_be32 route;
 
     route = netdev_vport_cast(netdev)->tnl_cfg.ip_dst;
@@ -596,7 +607,7 @@ set_patch_config(struct netdev *dev_, const struct smap *args)
 
     free(dev->peer);
     dev->peer = xstrdup(peer);
-
+    netdev_vport_poll_notify(dev);
     return 0;
 }
 
@@ -681,11 +692,15 @@ netdev_vport_tunnel_register(void)
         TUNNEL_CLASS("vxlan", "vxlan_system"),
         TUNNEL_CLASS("lisp", "lisp_system")
     };
+    static bool inited;
 
     int i;
 
-    for (i = 0; i < ARRAY_SIZE(vport_classes); i++) {
-        netdev_register_provider(&vport_classes[i].netdev_class);
+    if (!inited) {
+        inited = true;
+        for (i = 0; i < ARRAY_SIZE(vport_classes); i++) {
+            netdev_register_provider(&vport_classes[i].netdev_class);
+        }
     }
 }
 
index c907b0c..5394966 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2011 Nicira, Inc.
+ * Copyright (c) 2010, 2011, 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.
 #define NETDEV_VPORT_H 1
 
 #include <stdbool.h>
+#include <stddef.h>
 
 struct dpif_linux_vport;
 struct dpif_flow_stats;
 struct netdev;
+struct netdev_class;
 struct netdev_stats;
 
 void netdev_vport_tunnel_register(void);
@@ -36,6 +38,11 @@ void netdev_vport_inc_rx(const struct netdev *,
 void netdev_vport_inc_tx(const struct netdev *,
                          const struct dpif_flow_stats *);
 
-const char *netdev_vport_get_dpif_port(const struct netdev *);
+const char *netdev_vport_class_get_dpif_port(const struct netdev_class *);
+
+enum { NETDEV_VPORT_NAME_BUFSIZE = 16 };
+const char *netdev_vport_get_dpif_port(const struct netdev *,
+                                       char namebuf[], size_t bufsize);
+char *netdev_vport_get_dpif_port_strdup(const struct netdev *);
 
 #endif /* netdev-vport.h */
index 6e8af3b..76c8728 100644 (file)
@@ -25,6 +25,7 @@
 #include <unistd.h>
 
 #include "coverage.h"
+#include "dpif.h"
 #include "dynamic-string.h"
 #include "fatal-signal.h"
 #include "hash.h"
@@ -84,7 +85,7 @@ netdev_initialize(void)
         netdev_register_provider(&netdev_tap_class);
         netdev_vport_tunnel_register();
 #endif
-#ifdef __FreeBSD__
+#if defined(__FreeBSD__) || defined(__NetBSD__)
         netdev_register_provider(&netdev_tap_class);
         netdev_register_provider(&netdev_bsd_class);
 #endif
@@ -140,7 +141,7 @@ netdev_register_provider(const struct netdev_class *new_class)
         int error = new_class->init();
         if (error) {
             VLOG_ERR("failed to initialize %s network device class: %s",
-                     new_class->type, strerror(error));
+                     new_class->type, ovs_strerror(error));
             return error;
         }
     }
@@ -202,6 +203,43 @@ netdev_enumerate_types(struct sset *types)
     }
 }
 
+/* Check that the network device name is not the same as any of the registered
+ * vport providers' dpif_port name (dpif_port is NULL if the vport provider
+ * does not define it) or the datapath internal port name (e.g. ovs-system).
+ *
+ * Returns true if there is a name conflict, false otherwise. */
+bool
+netdev_is_reserved_name(const char *name)
+{
+    struct shash_node *node;
+
+    netdev_initialize();
+    SHASH_FOR_EACH (node, &netdev_classes) {
+        const char *dpif_port;
+        dpif_port = netdev_vport_class_get_dpif_port(node->data);
+        if (dpif_port && !strcmp(dpif_port, name)) {
+            return true;
+        }
+    }
+
+    if (!strncmp(name, "ovs-", 4)) {
+        struct sset types;
+        const char *type;
+
+        sset_init(&types);
+        dp_enumerate_types(&types);
+        SSET_FOR_EACH (type, &types) {
+            if (!strcmp(name+4, type)) {
+                sset_destroy(&types);
+                return true;
+            }
+        }
+        sset_destroy(&types);
+    }
+
+    return false;
+}
+
 /* Opens the network device named 'name' (e.g. "eth0") of the specified 'type'
  * (e.g. "system") and returns zero if successful, otherwise a positive errno
  * value.  On success, sets '*netdevp' to the new network device, otherwise to
@@ -241,6 +279,20 @@ netdev_open(const char *name, const char *type, struct netdev **netdevp)
     return 0;
 }
 
+/* Returns a reference to 'netdev_' for the caller to own. Returns null if
+ * 'netdev_' is null. */
+struct netdev *
+netdev_ref(const struct netdev *netdev_)
+{
+    struct netdev *netdev = CONST_CAST(struct netdev *, netdev_);
+
+    if (netdev) {
+        ovs_assert(netdev->ref_cnt > 0);
+        netdev->ref_cnt++;
+    }
+    return netdev;
+}
+
 /* Reconfigures the device 'netdev' with 'args'.  'args' may be empty
  * or NULL if none are needed. */
 int
@@ -476,7 +528,7 @@ netdev_get_mtu(const struct netdev *netdev, int *mtup)
         *mtup = 0;
         if (error != EOPNOTSUPP) {
             VLOG_DBG_RL(&rl, "failed to retrieve MTU for network device %s: "
-                         "%s", netdev_get_name(netdev), strerror(error));
+                         "%s", netdev_get_name(netdev), ovs_strerror(error));
         }
     }
     return error;
@@ -497,7 +549,7 @@ netdev_set_mtu(const struct netdev *netdev, int mtu)
     error = class->set_mtu ? class->set_mtu(netdev, mtu) : EOPNOTSUPP;
     if (error && error != EOPNOTSUPP) {
         VLOG_DBG_RL(&rl, "failed to set MTU for network device %s: %s",
-                     netdev_get_name(netdev), strerror(error));
+                     netdev_get_name(netdev), ovs_strerror(error));
     }
 
     return error;
@@ -775,7 +827,7 @@ do_update_flags(struct netdev *netdev, enum netdev_flags off,
     if (error) {
         VLOG_WARN_RL(&rl, "failed to %s flags for network device %s: %s",
                      off || on ? "set" : "get", netdev_get_name(netdev),
-                     strerror(error));
+                     ovs_strerror(error));
         old_flags = 0;
     } else if ((off || on) && sfp) {
         enum netdev_flags new_flags = (old_flags & ~off) | on;
@@ -906,7 +958,7 @@ netdev_get_carrier(const struct netdev *netdev)
                                                               &carrier);
     if (error) {
         VLOG_DBG("%s: failed to get network device carrier status, assuming "
-                 "down: %s", netdev_get_name(netdev), strerror(error));
+                 "down: %s", netdev_get_name(netdev), ovs_strerror(error));
         carrier = false;
     }
 
index 852b75d..b1cc319 100644 (file)
@@ -106,9 +106,11 @@ void netdev_run(void);
 void netdev_wait(void);
 
 void netdev_enumerate_types(struct sset *types);
+bool netdev_is_reserved_name(const char *name);
 
 /* Open and close. */
 int netdev_open(const char *name, const char *type, struct netdev **);
+struct netdev *netdev_ref(const struct netdev *);
 void netdev_close(struct netdev *);
 
 void netdev_parse_name(const char *netdev_name, char **name, char **type);
index 6739ece..9aa185d 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.
@@ -41,7 +41,7 @@ struct nln {
 
     /* Passed in by nln_create(). */
     int multicast_group;         /* Multicast group we listen on. */
-    int protocol;                /* Protocal passed to nl_sock_create(). */
+    int protocol;                /* Protocol passed to nl_sock_create(). */
     nln_parse_func *parse;       /* Message parsing function. */
     void *change;                /* Change passed to parse. */
 };
@@ -115,7 +115,8 @@ nln_notifier_create(struct nln *nln, nln_notify_func *cb, void *aux)
         }
         if (error) {
             nl_sock_destroy(sock);
-            VLOG_WARN("could not create netlink socket: %s", strerror(error));
+            VLOG_WARN("could not create netlink socket: %s",
+                      ovs_strerror(error));
             return NULL;
         }
         nln->notify_sock = sock;
@@ -184,7 +185,7 @@ nln_run(struct nln *nln)
                 VLOG_WARN_RL(&rl, "netlink receive buffer overflowed");
             } else {
                 VLOG_WARN_RL(&rl, "error reading netlink socket: %s",
-                             strerror(error));
+                             ovs_strerror(error));
             }
             nln_report(nln, NULL);
         }
index fbb1724..dfe39ac 100644 (file)
@@ -99,7 +99,7 @@ nl_sock_create(int protocol, struct nl_sock **sockp)
         max_iovs = sysconf(_SC_UIO_MAXIOV);
         if (max_iovs < _XOPEN_IOV_MAX) {
             if (max_iovs == -1 && errno) {
-                VLOG_WARN("sysconf(_SC_UIO_MAXIOV): %s", strerror(errno));
+                VLOG_WARN("sysconf(_SC_UIO_MAXIOV): %s", ovs_strerror(errno));
             }
             max_iovs = _XOPEN_IOV_MAX;
         } else if (max_iovs > MAX_IOVS) {
@@ -114,7 +114,7 @@ nl_sock_create(int protocol, struct nl_sock **sockp)
 
     sock->fd = socket(AF_NETLINK, SOCK_RAW, protocol);
     if (sock->fd < 0) {
-        VLOG_ERR("fcntl: %s", strerror(errno));
+        VLOG_ERR("fcntl: %s", ovs_strerror(errno));
         goto error;
     }
     sock->protocol = protocol;
@@ -128,7 +128,7 @@ nl_sock_create(int protocol, struct nl_sock **sockp)
          * Warn only if the failure is therefore unexpected. */
         if (errno != EPERM) {
             VLOG_WARN_RL(&rl, "setting %d-byte socket receive buffer failed "
-                         "(%s)", rcvbuf, strerror(errno));
+                         "(%s)", rcvbuf, ovs_strerror(errno));
         }
     }
 
@@ -144,14 +144,14 @@ nl_sock_create(int protocol, struct nl_sock **sockp)
     remote.nl_family = AF_NETLINK;
     remote.nl_pid = 0;
     if (connect(sock->fd, (struct sockaddr *) &remote, sizeof remote) < 0) {
-        VLOG_ERR("connect(0): %s", strerror(errno));
+        VLOG_ERR("connect(0): %s", ovs_strerror(errno));
         goto error;
     }
 
     /* Obtain pid assigned by kernel. */
     local_size = sizeof local;
     if (getsockname(sock->fd, (struct sockaddr *) &local, &local_size) < 0) {
-        VLOG_ERR("getsockname: %s", strerror(errno));
+        VLOG_ERR("getsockname: %s", ovs_strerror(errno));
         goto error;
     }
     if (local_size < sizeof local || local.nl_family != AF_NETLINK) {
@@ -222,7 +222,7 @@ nl_sock_join_mcgroup(struct nl_sock *sock, unsigned int multicast_group)
     if (setsockopt(sock->fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP,
                    &multicast_group, sizeof multicast_group) < 0) {
         VLOG_WARN("could not join multicast group %u (%s)",
-                  multicast_group, strerror(errno));
+                  multicast_group, ovs_strerror(errno));
         return errno;
     }
     return 0;
@@ -245,7 +245,7 @@ nl_sock_leave_mcgroup(struct nl_sock *sock, unsigned int multicast_group)
     if (setsockopt(sock->fd, SOL_NETLINK, NETLINK_DROP_MEMBERSHIP,
                    &multicast_group, sizeof multicast_group) < 0) {
         VLOG_WARN("could not leave multicast group %u (%s)",
-                  multicast_group, strerror(errno));
+                  multicast_group, ovs_strerror(errno));
         return errno;
     }
     return 0;
@@ -527,7 +527,7 @@ nl_sock_transact_multiple__(struct nl_sock *sock,
             }
             if (txn->error) {
                 VLOG_DBG_RL(&rl, "received NAK error=%d (%s)",
-                            error, strerror(txn->error));
+                            error, ovs_strerror(txn->error));
             }
         } else {
             txn->error = 0;
@@ -629,7 +629,7 @@ nl_sock_transact_multiple(struct nl_sock *sock,
         if (error == ENOBUFS) {
             VLOG_DBG_RL(&rl, "receive buffer overflow, resending request");
         } else if (error) {
-            VLOG_ERR_RL(&rl, "transaction error (%s)", strerror(error));
+            VLOG_ERR_RL(&rl, "transaction error (%s)", ovs_strerror(error));
             nl_sock_record_errors__(transactions, n, error);
         }
     }
@@ -815,7 +815,7 @@ nl_dump_recv(struct nl_dump *dump)
 
     if (nl_msg_nlmsgerr(&dump->buffer, &retval)) {
         VLOG_INFO_RL(&rl, "netlink dump request error (%s)",
-                     strerror(retval));
+                     ovs_strerror(retval));
         return retval && retval != EAGAIN ? retval : EPROTO;
     }
 
@@ -1187,7 +1187,7 @@ nlmsg_to_string(const struct ofpbuf *buffer, int protocol)
             if (e) {
                 ds_put_format(&ds, " error(%d", e->error);
                 if (e->error < 0) {
-                    ds_put_format(&ds, "(%s)", strerror(-e->error));
+                    ds_put_format(&ds, "(%s)", ovs_strerror(-e->error));
                 }
                 ds_put_cstr(&ds, ", in-reply-to(");
                 nlmsghdr_to_string(&e->msg, protocol, &ds);
@@ -1200,7 +1200,7 @@ nlmsg_to_string(const struct ofpbuf *buffer, int protocol)
             if (error) {
                 ds_put_format(&ds, " done(%d", *error);
                 if (*error < 0) {
-                    ds_put_format(&ds, "(%s)", strerror(-*error));
+                    ds_put_format(&ds, "(%s)", ovs_strerror(-*error));
                 }
                 ds_put_cstr(&ds, ")");
             } else {
@@ -1232,6 +1232,6 @@ log_nlmsg(const char *function, int error,
 
     ofpbuf_use_const(&buffer, message, size);
     nlmsg = nlmsg_to_string(&buffer, protocol);
-    VLOG_DBG_RL(&rl, "%s (%s): %s", function, strerror(error), nlmsg);
+    VLOG_DBG_RL(&rl, "%s (%s): %s", function, ovs_strerror(error), nlmsg);
     free(nlmsg);
 }
index c8e5905..7e7884e 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 <sys/types.h>
 #include <unistd.h>
 #include "coverage.h"
+#include "flow.h"
 #include "netlink-protocol.h"
 #include "ofpbuf.h"
 #include "timeval.h"
@@ -295,6 +296,15 @@ nl_msg_put_be64(struct ofpbuf *msg, uint16_t type, ovs_be64 value)
     nl_msg_put_unspec(msg, type, &value, sizeof value);
 }
 
+/* Appends a Netlink attribute of the given 'type' and the given odp_port_t
+ * 'value' to 'msg'. */
+void
+nl_msg_put_odp_port(struct ofpbuf *msg, uint16_t type, odp_port_t value)
+{
+    nl_msg_put_u32(msg, type, odp_to_u32(value));
+}
+
+
 /* Appends a Netlink attribute of the given 'type' and the given
  * null-terminated string 'value' to 'msg'. */
 void
@@ -570,6 +580,15 @@ nl_attr_get_be64(const struct nlattr *nla)
     return get_32aligned_be64(x);
 }
 
+/* Returns the 32-bit odp_port_t value in 'nla''s payload.
+ *
+ * Asserts that 'nla''s payload is at least 4 bytes long. */
+odp_port_t
+nl_attr_get_odp_port(const struct nlattr *nla)
+{
+    return u32_to_odp(nl_attr_get_u32(nla));
+}
+
 /* Returns the null-terminated string value in 'nla''s payload.
  *
  * Asserts that 'nla''s payload contains a null-terminated string. */
index a5c129a..afe2277 100644 (file)
@@ -68,6 +68,7 @@ void nl_msg_put_u64(struct ofpbuf *, uint16_t type, uint64_t value);
 void nl_msg_put_be16(struct ofpbuf *, uint16_t type, ovs_be16 value);
 void nl_msg_put_be32(struct ofpbuf *, uint16_t type, ovs_be32 value);
 void nl_msg_put_be64(struct ofpbuf *, uint16_t type, ovs_be64 value);
+void nl_msg_put_odp_port(struct ofpbuf *, uint16_t type, odp_port_t value);
 void nl_msg_put_string(struct ofpbuf *, uint16_t type, const char *value);
 
 size_t nl_msg_start_nested(struct ofpbuf *, uint16_t type);
@@ -170,6 +171,7 @@ uint64_t nl_attr_get_u64(const struct nlattr *);
 ovs_be16 nl_attr_get_be16(const struct nlattr *);
 ovs_be32 nl_attr_get_be32(const struct nlattr *);
 ovs_be64 nl_attr_get_be64(const struct nlattr *);
+odp_port_t nl_attr_get_odp_port(const struct nlattr *);
 const char *nl_attr_get_string(const struct nlattr *);
 void nl_attr_get_nested(const struct nlattr *, struct ofpbuf *);
 
index 51b6dc6..8bdd8ec 100644 (file)
@@ -148,7 +148,7 @@ nx_pull_raw(const uint8_t *p, unsigned int match_len, bool strict,
             error = OFPERR_OFPBMC_BAD_PREREQ;
         } else if (!mf_is_all_wild(mf, &match->wc)) {
             error = OFPERR_OFPBMC_DUP_FIELD;
-        } else if (header != OXM_OF_IN_PORT) {
+        } else {
             unsigned int width = mf->n_bytes;
             union mf_value value;
 
@@ -170,17 +170,6 @@ nx_pull_raw(const uint8_t *p, unsigned int match_len, bool strict,
                     mf_set(mf, &value, &mask, match);
                 }
             }
-        } else {
-            /* Special case for 32bit ports when using OXM,
-             * ports are 16 bits wide otherwise. */
-            ovs_be32 port_of11;
-            uint16_t port;
-
-            memcpy(&port_of11, p + 4, sizeof port_of11);
-            error = ofputil_port_from_ofp11(port_of11, &port);
-            if (!error) {
-                match_set_in_port(match, port);
-            }
         }
 
         /* Check if the match is for a cookie rather than a classifier rule. */
@@ -291,7 +280,7 @@ oxm_pull_match__(struct ofpbuf *b, bool strict, struct match *match)
                        strict, match, NULL, NULL);
 }
 
-/* Parses the oxm formatted match description preceeded by a struct ofp11_match
+/* Parses the oxm formatted match description preceded by a struct ofp11_match
  * in 'b' with length 'match_len'.  Stores the result in 'match'.
  *
  * Fails with an error when encountering unknown OXM headers.
@@ -579,12 +568,12 @@ nx_put_raw(struct ofpbuf *b, bool oxm, const struct match *match,
     BUILD_ASSERT_DECL(FLOW_WC_SEQ == 20);
 
     /* Metadata. */
-    if (match->wc.masks.in_port) {
-        uint16_t in_port = flow->in_port;
+    if (match->wc.masks.in_port.ofp_port) {
+        ofp_port_t in_port = flow->in_port.ofp_port;
         if (oxm) {
             nxm_put_32(b, OXM_OF_IN_PORT, ofputil_port_to_ofp11(in_port));
         } else {
-            nxm_put_16(b, NXM_OF_IN_PORT, htons(in_port));
+            nxm_put_16(b, NXM_OF_IN_PORT, htons(ofp_to_u16(in_port)));
         }
     }
 
@@ -1290,11 +1279,15 @@ nxm_reg_load_to_nxast(const struct ofpact_reg_load *load,
 
 void
 nxm_execute_reg_move(const struct ofpact_reg_move *move,
-                     struct flow *flow)
+                     struct flow *flow, struct flow_wildcards *wc)
 {
+    union mf_subvalue mask_value;
     union mf_value src_value;
     union mf_value dst_value;
 
+    memset(&mask_value, 0xff, sizeof mask_value);
+    mf_write_subfield_flow(&move->src, &mask_value, &wc->masks);
+
     mf_get_value(move->dst.field, flow, &dst_value);
     mf_get_value(move->src.field, flow, &src_value);
     bitwise_copy(&src_value, move->src.field->n_bytes, move->src.ofs,
@@ -1439,10 +1432,15 @@ nx_stack_pop(struct ofpbuf *stack)
 
 void
 nxm_execute_stack_push(const struct ofpact_stack *push,
-                       const struct flow *flow, struct ofpbuf *stack)
+                       const struct flow *flow, struct flow_wildcards *wc,
+                       struct ofpbuf *stack)
 {
+    union mf_subvalue mask_value;
     union mf_subvalue dst_value;
 
+    memset(&mask_value, 0xff, sizeof mask_value);
+    mf_write_subfield_flow(&push->subfield, &mask_value, &wc->masks);
+
     mf_read_subfield(&push->subfield, flow, &dst_value);
     nx_stack_push(stack, &dst_value);
 }
index 7d316d8..bb63b90 100644 (file)
@@ -80,7 +80,8 @@ void nxm_reg_move_to_nxast(const struct ofpact_reg_move *,
 void nxm_reg_load_to_nxast(const struct ofpact_reg_load *,
                            struct ofpbuf *openflow);
 
-void nxm_execute_reg_move(const struct ofpact_reg_move *, struct flow *);
+void nxm_execute_reg_move(const struct ofpact_reg_move *, struct flow *,
+                          struct flow_wildcards *);
 void nxm_execute_reg_load(const struct ofpact_reg_load *, struct flow *);
 void nxm_reg_load(const struct mf_subfield *, uint64_t src_data,
                   struct flow *);
@@ -105,7 +106,8 @@ void nxm_stack_pop_to_nxast(const struct ofpact_stack *,
                            struct ofpbuf *openflow);
 
 void nxm_execute_stack_push(const struct ofpact_stack *,
-                            const struct flow *, struct ofpbuf *);
+                            const struct flow *, struct flow_wildcards *,
+                            struct ofpbuf *);
 void nxm_execute_stack_pop(const struct ofpact_stack *,
                             struct flow *, struct ofpbuf *);
 
diff --git a/lib/odp-execute.c b/lib/odp-execute.c
new file mode 100644 (file)
index 0000000..e6e8c91
--- /dev/null
@@ -0,0 +1,219 @@
+/*
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
+ * Copyright (c) 2013 Simon Horman
+ *
+ * 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 "odp-execute.h"
+#include <linux/openvswitch.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "netlink.h"
+#include "ofpbuf.h"
+#include "odp-util.h"
+#include "packets.h"
+#include "util.h"
+
+static void
+odp_eth_set_addrs(struct ofpbuf *packet, const struct ovs_key_ethernet *eth_key)
+{
+    struct eth_header *eh = packet->l2;
+
+    memcpy(eh->eth_src, eth_key->eth_src, sizeof eh->eth_src);
+    memcpy(eh->eth_dst, eth_key->eth_dst, sizeof eh->eth_dst);
+}
+
+static void
+odp_set_tunnel_action(const struct nlattr *a, struct flow_tnl *tun_key)
+{
+    enum odp_key_fitness fitness;
+
+    fitness = odp_tun_key_from_attr(a, tun_key);
+    ovs_assert(fitness != ODP_FIT_ERROR);
+}
+
+static void
+odp_execute_set_action(struct ofpbuf *packet, const struct nlattr *a,
+                       struct flow *flow)
+{
+    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_PRIORITY:
+        flow->skb_priority = nl_attr_get_u32(a);
+        break;
+
+    case OVS_KEY_ATTR_TUNNEL:
+        odp_set_tunnel_action(a, &flow->tunnel);
+        break;
+
+    case OVS_KEY_ATTR_SKB_MARK:
+        flow->skb_mark = nl_attr_get_u32(a);
+        break;
+
+    case OVS_KEY_ATTR_ETHERNET:
+        odp_eth_set_addrs(packet,
+                          nl_attr_get_unspec(a, sizeof(struct ovs_key_ethernet)));
+        break;
+
+    case OVS_KEY_ATTR_IPV4:
+        ipv4_key = nl_attr_get_unspec(a, sizeof(struct ovs_key_ipv4));
+        packet_set_ipv4(packet, ipv4_key->ipv4_src, ipv4_key->ipv4_dst,
+                        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);
+        break;
+
+    case OVS_KEY_ATTR_UDP:
+        udp_key = nl_attr_get_unspec(a, sizeof(struct ovs_key_udp));
+        packet_set_udp_port(packet, udp_key->udp_src, udp_key->udp_dst);
+        break;
+
+    case OVS_KEY_ATTR_MPLS:
+         set_mpls_lse(packet, nl_attr_get_be32(a));
+         break;
+
+    case OVS_KEY_ATTR_UNSPEC:
+    case OVS_KEY_ATTR_ENCAP:
+    case OVS_KEY_ATTR_ETHERTYPE:
+    case OVS_KEY_ATTR_IN_PORT:
+    case OVS_KEY_ATTR_VLAN:
+    case OVS_KEY_ATTR_ICMP:
+    case OVS_KEY_ATTR_ICMPV6:
+    case OVS_KEY_ATTR_ARP:
+    case OVS_KEY_ATTR_ND:
+    case __OVS_KEY_ATTR_MAX:
+    default:
+        NOT_REACHED();
+    }
+}
+
+static void
+odp_execute_sample(void *dp, struct ofpbuf *packet, struct flow *key,
+                   const struct nlattr *action,
+                   void (*output)(void *dp, struct ofpbuf *packet,
+                                  uint32_t out_port),
+                   void (*userspace)(void *dp, struct ofpbuf *packet,
+                                     const struct flow *key,
+                                     const struct nlattr *a))
+{
+    const struct nlattr *subactions = NULL;
+    const struct nlattr *a;
+    size_t left;
+
+    NL_NESTED_FOR_EACH_UNSAFE (a, left, action) {
+        int type = nl_attr_type(a);
+
+        switch ((enum ovs_sample_attr) type) {
+        case OVS_SAMPLE_ATTR_PROBABILITY:
+            if (random_uint32() >= nl_attr_get_u32(a)) {
+                return;
+            }
+            break;
+
+        case OVS_SAMPLE_ATTR_ACTIONS:
+            subactions = a;
+            break;
+
+        case OVS_SAMPLE_ATTR_UNSPEC:
+        case __OVS_SAMPLE_ATTR_MAX:
+        default:
+            NOT_REACHED();
+        }
+    }
+
+    odp_execute_actions(dp, packet, key, nl_attr_get(subactions),
+                        nl_attr_get_size(subactions), output, userspace);
+}
+
+void
+odp_execute_actions(void *dp, struct ofpbuf *packet, struct flow *key,
+                    const struct nlattr *actions, size_t actions_len,
+                    void (*output)(void *dp, struct ofpbuf *packet,
+                                   uint32_t out_port),
+                    void (*userspace)(void *dp, struct ofpbuf *packet,
+                                      const struct flow *key,
+                                      const struct nlattr *a))
+{
+    const struct nlattr *a;
+    unsigned int left;
+
+    NL_ATTR_FOR_EACH_UNSAFE (a, left, actions, actions_len) {
+        int type = nl_attr_type(a);
+
+        switch ((enum ovs_action_attr) type) {
+        case OVS_ACTION_ATTR_OUTPUT:
+            if (output) {
+                output(dp, packet, nl_attr_get_u32(a));
+            }
+            break;
+
+        case OVS_ACTION_ATTR_USERSPACE: {
+            const struct nlattr *userdata;
+
+            userdata = nl_attr_find_nested(a, OVS_USERSPACE_ATTR_USERDATA);
+            userspace(dp, packet, key, userdata);
+            break;
+        }
+
+        case OVS_ACTION_ATTR_PUSH_VLAN: {
+            const struct ovs_action_push_vlan *vlan = nl_attr_get(a);
+            eth_push_vlan(packet, vlan->vlan_tci);
+            break;
+        }
+
+        case OVS_ACTION_ATTR_POP_VLAN:
+            eth_pop_vlan(packet);
+            break;
+
+        case OVS_ACTION_ATTR_PUSH_MPLS: {
+            const struct ovs_action_push_mpls *mpls = nl_attr_get(a);
+            push_mpls(packet, mpls->mpls_ethertype, mpls->mpls_lse);
+            break;
+         }
+
+        case OVS_ACTION_ATTR_POP_MPLS:
+            pop_mpls(packet, nl_attr_get_be16(a));
+            break;
+
+        case OVS_ACTION_ATTR_SET:
+            odp_execute_set_action(packet, nl_attr_get(a), key);
+            break;
+
+        case OVS_ACTION_ATTR_SAMPLE:
+            odp_execute_sample(dp, packet, key, a, output, userspace);
+            break;
+
+        case OVS_ACTION_ATTR_UNSPEC:
+        case __OVS_ACTION_ATTR_MAX:
+            NOT_REACHED();
+        }
+    }
+}
diff --git a/lib/odp-execute.h b/lib/odp-execute.h
new file mode 100644 (file)
index 0000000..89dd66b
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2013 Nicira, Inc.
+ * Copyright (c) 2013 Simon Horman
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef EXECUTE_ACTIONS_H
+#define EXECUTE_ACTIONS_H 1
+
+#include <stddef.h>
+#include <stdint.h>
+
+struct flow;
+struct nlattr;
+struct ofpbuf;
+
+void
+odp_execute_actions(void *dp, struct ofpbuf *packet, struct flow *key,
+                    const struct nlattr *actions, size_t actions_len,
+                    void (*output)(void *dp, struct ofpbuf *packet,
+                                   uint32_t out_port),
+                    void (*userspace)(void *dp, struct ofpbuf *packet,
+                                      const struct flow *key,
+                                      const struct nlattr *a));
+#endif
index 5bf94fe..14994a9 100644 (file)
@@ -48,9 +48,10 @@ VLOG_DEFINE_THIS_MODULE(odp_util);
  * from another. */
 static const char *delimiters = ", \t\r\n";
 
-static int parse_odp_key_attr(const char *, const struct simap *port_names,
-                              struct ofpbuf *);
-static void format_odp_key_attr(const struct nlattr *a, struct ds *ds);
+static int parse_odp_key_mask_attr(const char *, const struct simap *port_names,
+                              struct ofpbuf *, struct ofpbuf *);
+static void format_odp_key_attr(const struct nlattr *a,
+                                const struct nlattr *ma, struct ds *ds);
 
 /* Returns one the following for the action with the given OVS_ACTION_ATTR_*
  * 'type':
@@ -86,11 +87,13 @@ odp_action_len(uint16_t type)
     return -1;
 }
 
+/* Returns a string form of 'attr'.  The return value is either a statically
+ * allocated constant string or the 'bufsize'-byte buffer 'namebuf'.  'bufsize'
+ * should be at least OVS_KEY_ATTR_BUFSIZE. */
+enum { OVS_KEY_ATTR_BUFSIZE = 3 + INT_STRLEN(unsigned int) + 1 };
 static const char *
-ovs_key_attr_to_string(enum ovs_key_attr attr)
+ovs_key_attr_to_string(enum ovs_key_attr attr, char *namebuf, size_t bufsize)
 {
-    static char unknown_attr[3 + INT_STRLEN(unsigned int) + 1];
-
     switch (attr) {
     case OVS_KEY_ATTR_UNSPEC: return "unspec";
     case OVS_KEY_ATTR_ENCAP: return "encap";
@@ -113,9 +116,8 @@ ovs_key_attr_to_string(enum ovs_key_attr attr)
 
     case __OVS_KEY_ATTR_MAX:
     default:
-        snprintf(unknown_attr, sizeof unknown_attr, "key%u",
-                 (unsigned int) attr);
-        return unknown_attr;
+        snprintf(namebuf, bufsize, "key%u", (unsigned int) attr);
+        return namebuf;
     }
 }
 
@@ -170,30 +172,39 @@ format_odp_sample_action(struct ds *ds, const struct nlattr *attr)
 }
 
 static const char *
-slow_path_reason_to_string(uint32_t data)
+slow_path_reason_to_string(enum slow_path_reason reason)
 {
-    enum slow_path_reason bit = (enum slow_path_reason) data;
-
-    switch (bit) {
+    switch (reason) {
     case SLOW_CFM:
         return "cfm";
     case SLOW_LACP:
         return "lacp";
     case SLOW_STP:
         return "stp";
-    case SLOW_IN_BAND:
-        return "in_band";
     case SLOW_BFD:
         return "bfd";
     case SLOW_CONTROLLER:
         return "controller";
-    case SLOW_MATCH:
-        return "match";
+    case __SLOW_MAX:
     default:
         return NULL;
     }
 }
 
+static enum slow_path_reason
+string_to_slow_path_reason(const char *string)
+{
+    enum slow_path_reason i;
+
+    for (i = 1; i < __SLOW_MAX; i++) {
+        if (!strcmp(string, slow_path_reason_to_string(i))) {
+            return i;
+        }
+    }
+
+    return 0;
+}
+
 static int
 parse_flags(const char *s, const char *(*bit_to_string)(uint32_t),
             uint32_t *res)
@@ -288,10 +299,10 @@ format_odp_userspace_action(struct ds *ds, const struct nlattr *attr)
                               cookie.sflow.output);
             } else if (userdata_len == sizeof cookie.slow_path
                        && cookie.type == USER_ACTION_COOKIE_SLOW_PATH) {
-                ds_put_cstr(ds, ",slow_path(");
-                format_flags(ds, slow_path_reason_to_string,
-                             cookie.slow_path.reason, ',');
-                ds_put_format(ds, ")");
+                const char *reason;
+                reason = slow_path_reason_to_string(cookie.slow_path.reason);
+                reason = reason ? reason : "";
+                ds_put_format(ds, ",slow_path(%s)", reason);
             } else if (userdata_len == sizeof cookie.flow_sample
                        && cookie.type == USER_ACTION_COOKIE_FLOW_SAMPLE) {
                 ds_put_format(ds, ",flow_sample(probability=%"PRIu16
@@ -344,6 +355,25 @@ format_mpls_lse(struct ds *ds, ovs_be32 mpls_lse)
                   mpls_lse_to_bos(mpls_lse));
 }
 
+static void
+format_mpls(struct ds *ds, const struct ovs_key_mpls *mpls_key,
+            const struct ovs_key_mpls *mpls_mask)
+{
+    ovs_be32 key = mpls_key->mpls_lse;
+
+    if (mpls_mask == NULL) {
+        format_mpls_lse(ds, key);
+    } else {
+        ovs_be32 mask = mpls_mask->mpls_lse;
+
+        ds_put_format(ds, "label=%"PRIu32"/0x%x,tc=%d/%x,ttl=%d/0x%x,bos=%d/%x",
+                  mpls_lse_to_label(key), mpls_lse_to_label(mask),
+                  mpls_lse_to_tc(key), mpls_lse_to_tc(mask),
+                  mpls_lse_to_ttl(key), mpls_lse_to_ttl(mask),
+                  mpls_lse_to_bos(key), mpls_lse_to_bos(mask));
+    }
+}
+
 static void
 format_odp_action(struct ds *ds, const struct nlattr *a)
 {
@@ -361,14 +391,14 @@ format_odp_action(struct ds *ds, const struct nlattr *a)
 
     switch (type) {
     case OVS_ACTION_ATTR_OUTPUT:
-        ds_put_format(ds, "%"PRIu16, nl_attr_get_u32(a));
+        ds_put_format(ds, "%"PRIu32, nl_attr_get_u32(a));
         break;
     case OVS_ACTION_ATTR_USERSPACE:
         format_odp_userspace_action(ds, a);
         break;
     case OVS_ACTION_ATTR_SET:
         ds_put_cstr(ds, "set(");
-        format_odp_key_attr(nl_attr_get(a), ds);
+        format_odp_key_attr(nl_attr_get(a), NULL, ds);
         ds_put_cstr(ds, ")");
         break;
     case OVS_ACTION_ATTR_PUSH_VLAN:
@@ -502,25 +532,27 @@ parse_odp_action(const char *s, const struct simap *port_names,
             odp_put_userspace_action(pid, &cookie, sizeof cookie.sflow,
                                      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;
+            char reason[32];
+
+            if (s[n] == ')' && s[n + 1] == ')') {
+                reason[0] = '\0';
+                n += 2;
+            } else if (sscanf(s + n, "%31[^)]))", reason) > 0) {
+                n += strlen(reason) + 2;
+            } else {
+                return -EINVAL;
+            }
 
             cookie.type = USER_ACTION_COOKIE_SLOW_PATH;
             cookie.slow_path.unused = 0;
-            cookie.slow_path.reason = 0;
+            cookie.slow_path.reason = string_to_slow_path_reason(reason);
 
-            res = parse_flags(&s[n], slow_path_reason_to_string,
-                              &cookie.slow_path.reason);
-            if (res < 0) {
-                return res;
-            }
-            n += res;
-            if (s[n] != ')') {
+            if (reason[0] && !cookie.slow_path.reason) {
                 return -EINVAL;
             }
-            n++;
 
             odp_put_userspace_action(pid, &cookie, sizeof cookie.slow_path,
                                      actions);
@@ -568,7 +600,7 @@ parse_odp_action(const char *s, const struct simap *port_names,
         int retval;
 
         start_ofs = nl_msg_start_nested(actions, OVS_ACTION_ATTR_SET);
-        retval = parse_odp_key_attr(s + 4, port_names, actions);
+        retval = parse_odp_key_mask_attr(s + 4, port_names, actions, NULL);
         if (retval < 0) {
             return retval;
         }
@@ -735,10 +767,11 @@ format_generic_odp_key(const struct nlattr *a, struct ds *ds)
 
         unspec = nl_attr_get(a);
         for (i = 0; i < len; i++) {
-            ds_put_char(ds, i ? ' ': '(');
+            if (i) {
+                ds_put_char(ds, ' ');
+            }
             ds_put_format(ds, "%02x", unspec[i]);
         }
-        ds_put_char(ds, ')');
     }
 }
 
@@ -775,8 +808,8 @@ tunnel_key_attr_len(int type)
     return -1;
 }
 
-static enum odp_key_fitness
-tun_key_from_attr(const struct nlattr *attr, struct flow_tnl *tun)
+enum odp_key_fitness
+odp_tun_key_from_attr(const struct nlattr *attr, struct flow_tnl *tun)
 {
     unsigned int left;
     const struct nlattr *a;
@@ -863,174 +896,401 @@ tun_key_to_attr(struct ofpbuf *a, const struct flow_tnl *tun_key)
     nl_msg_end_nested(a, tun_key_ofs);
 }
 
+static bool
+odp_mask_attr_is_exact(const struct nlattr *ma)
+{
+    bool is_exact = false;
+    enum ovs_key_attr attr = nl_attr_type(ma);
+
+    if (attr == OVS_KEY_ATTR_TUNNEL) {
+        /* XXX this is a hack for now. Should change
+         * the exact match dection to per field
+         * instead of per attribute.
+         */
+        struct flow_tnl tun_mask;
+        memset(&tun_mask, 0, sizeof tun_mask);
+        odp_tun_key_from_attr(ma, &tun_mask);
+        if (tun_mask.flags == (FLOW_TNL_F_KEY
+                               | FLOW_TNL_F_DONT_FRAGMENT
+                               | FLOW_TNL_F_CSUM)) {
+            /* The flags are exact match, check the remaining fields. */
+            tun_mask.flags = 0xffff;
+            is_exact = is_all_ones((uint8_t *)&tun_mask,
+                                   offsetof(struct flow_tnl, ip_ttl));
+        }
+    } else {
+        is_exact = is_all_ones(nl_attr_get(ma), nl_attr_get_size(ma));
+    }
+
+    return is_exact;
+}
+
+
 static void
-format_odp_key_attr(const struct nlattr *a, struct ds *ds)
+format_odp_key_attr(const struct nlattr *a, const struct nlattr *ma,
+                    struct ds *ds)
 {
-    const struct ovs_key_ethernet *eth_key;
-    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;
-    const struct ovs_key_icmp *icmp_key;
-    const struct ovs_key_icmpv6 *icmpv6_key;
-    const struct ovs_key_arp *arp_key;
-    const struct ovs_key_nd *nd_key;
     struct flow_tnl tun_key;
     enum ovs_key_attr attr = nl_attr_type(a);
+    char namebuf[OVS_KEY_ATTR_BUFSIZE];
     int expected_len;
+    bool is_exact;
 
-    ds_put_cstr(ds, ovs_key_attr_to_string(attr));
-    expected_len = odp_flow_key_attr_len(nl_attr_type(a));
-    if (expected_len != -2 && nl_attr_get_size(a) != expected_len) {
-        ds_put_format(ds, "(bad length %zu, expected %d)",
-                      nl_attr_get_size(a),
-                      odp_flow_key_attr_len(nl_attr_type(a)));
-        format_generic_odp_key(a, ds);
-        return;
+    is_exact = ma ? odp_mask_attr_is_exact(ma) : true;
+
+    ds_put_cstr(ds, ovs_key_attr_to_string(attr, namebuf, sizeof namebuf));
+
+    {
+        expected_len = odp_flow_key_attr_len(nl_attr_type(a));
+        if (expected_len != -2) {
+            bool bad_key_len = nl_attr_get_size(a) != expected_len;
+            bool bad_mask_len = ma && nl_attr_get_size(a) != expected_len;
+
+            if (bad_key_len || bad_mask_len) {
+                if (bad_key_len) {
+                    ds_put_format(ds, "(bad key length %zu, expected %d)(",
+                                  nl_attr_get_size(a),
+                                  odp_flow_key_attr_len(nl_attr_type(a)));
+                }
+                format_generic_odp_key(a, ds);
+                if (bad_mask_len) {
+                    ds_put_char(ds, '/');
+                    ds_put_format(ds, "(bad mask length %zu, expected %d)(",
+                                  nl_attr_get_size(ma),
+                                  odp_flow_key_attr_len(nl_attr_type(ma)));
+                }
+                format_generic_odp_key(ma, ds);
+                ds_put_char(ds, ')');
+                return;
+            }
+        }
     }
 
+    ds_put_char(ds, '(');
     switch (attr) {
     case OVS_KEY_ATTR_ENCAP:
-        ds_put_cstr(ds, "(");
-        if (nl_attr_get_size(a)) {
-            odp_flow_key_format(nl_attr_get(a), nl_attr_get_size(a), ds);
+        if (ma && nl_attr_get_size(ma) && nl_attr_get_size(a)) {
+            odp_flow_format(nl_attr_get(a), nl_attr_get_size(a),
+                            nl_attr_get(ma), nl_attr_get_size(ma), ds);
+        } else if (nl_attr_get_size(a)) {
+            odp_flow_format(nl_attr_get(a), nl_attr_get_size(a), NULL, 0, ds);
         }
-        ds_put_char(ds, ')');
         break;
 
     case OVS_KEY_ATTR_PRIORITY:
-        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));
+        ds_put_format(ds, "%#"PRIx32, nl_attr_get_u32(a));
+        if (!is_exact) {
+            ds_put_format(ds, "/%#"PRIx32, nl_attr_get_u32(ma));
+        }
         break;
 
     case OVS_KEY_ATTR_TUNNEL:
         memset(&tun_key, 0, sizeof tun_key);
-        if (tun_key_from_attr(a, &tun_key) == ODP_FIT_ERROR) {
-            ds_put_format(ds, "(error)");
+        if (odp_tun_key_from_attr(a, &tun_key) == ODP_FIT_ERROR) {
+            ds_put_format(ds, "error");
+        } else if (!is_exact) {
+            struct flow_tnl tun_mask;
+
+            memset(&tun_mask, 0, sizeof tun_mask);
+            odp_tun_key_from_attr(ma, &tun_mask);
+            ds_put_format(ds, "tun_id=%#"PRIx64"/%#"PRIx64
+                          ",src="IP_FMT"/"IP_FMT",dst="IP_FMT"/"IP_FMT
+                          ",tos=%#"PRIx8"/%#"PRIx8",ttl=%"PRIu8"/%#"PRIx8
+                          ",flags(",
+                          ntohll(tun_key.tun_id), ntohll(tun_mask.tun_id),
+                          IP_ARGS(tun_key.ip_src), IP_ARGS(tun_mask.ip_src),
+                          IP_ARGS(tun_key.ip_dst), IP_ARGS(tun_mask.ip_dst),
+                          tun_key.ip_tos, tun_mask.ip_tos,
+                          tun_key.ip_ttl, tun_mask.ip_ttl);
+
+            format_flags(ds, flow_tun_flag_to_string, tun_key.flags, ',');
+
+            /* XXX This code is correct, but enabling it would break the unit
+               test. Disable it for now until the input parser is fixed.
+
+                ds_put_char(ds, '/');
+                format_flags(ds, flow_tun_flag_to_string, tun_mask.flags, ',');
+            */
+            ds_put_char(ds, ')');
         } else {
-            ds_put_format(ds, "(tun_id=0x%"PRIx64",src="IP_FMT",dst="IP_FMT","
+            ds_put_format(ds, "tun_id=0x%"PRIx64",src="IP_FMT",dst="IP_FMT","
                           "tos=0x%"PRIx8",ttl=%"PRIu8",flags(",
                           ntohll(tun_key.tun_id),
                           IP_ARGS(tun_key.ip_src),
                           IP_ARGS(tun_key.ip_dst),
                           tun_key.ip_tos, tun_key.ip_ttl);
 
-            format_flags(ds, flow_tun_flag_to_string,
-                         (uint32_t) tun_key.flags, ',');
-            ds_put_format(ds, "))");
+            format_flags(ds, flow_tun_flag_to_string, tun_key.flags, ',');
+            ds_put_char(ds, ')');
         }
         break;
 
     case OVS_KEY_ATTR_IN_PORT:
-        ds_put_format(ds, "(%"PRIu32")", nl_attr_get_u32(a));
+        ds_put_format(ds, "%"PRIu32, nl_attr_get_u32(a));
+        if (!is_exact) {
+            ds_put_format(ds, "/%#"PRIx32, nl_attr_get_u32(ma));
+        }
         break;
 
     case OVS_KEY_ATTR_ETHERNET:
-        eth_key = nl_attr_get(a);
-        ds_put_format(ds, "(src="ETH_ADDR_FMT",dst="ETH_ADDR_FMT")",
-                      ETH_ADDR_ARGS(eth_key->eth_src),
-                      ETH_ADDR_ARGS(eth_key->eth_dst));
+        if (!is_exact) {
+            const struct ovs_key_ethernet *eth_mask = nl_attr_get(ma);
+            const struct ovs_key_ethernet *eth_key = nl_attr_get(a);
+
+            ds_put_format(ds, "src="ETH_ADDR_FMT"/"ETH_ADDR_FMT
+                          ",dst="ETH_ADDR_FMT"/"ETH_ADDR_FMT,
+                          ETH_ADDR_ARGS(eth_key->eth_src),
+                          ETH_ADDR_ARGS(eth_mask->eth_src),
+                          ETH_ADDR_ARGS(eth_key->eth_dst),
+                          ETH_ADDR_ARGS(eth_mask->eth_dst));
+        } else {
+            const struct ovs_key_ethernet *eth_key = nl_attr_get(a);
+
+            ds_put_format(ds, "src="ETH_ADDR_FMT",dst="ETH_ADDR_FMT,
+                          ETH_ADDR_ARGS(eth_key->eth_src),
+                          ETH_ADDR_ARGS(eth_key->eth_dst));
+        }
         break;
 
     case OVS_KEY_ATTR_VLAN:
-        ds_put_char(ds, '(');
-        format_vlan_tci(ds, nl_attr_get_be16(a));
-        ds_put_char(ds, ')');
+        {
+            ovs_be16 vlan_tci = nl_attr_get_be16(a);
+            if (!is_exact) {
+                ovs_be16 mask = nl_attr_get_be16(ma);
+                ds_put_format(ds, "vid=%"PRIu16"/%"PRIx16",pcp=%d/0x%x,cfi=%d/%d",
+                              vlan_tci_to_vid(vlan_tci),
+                              vlan_tci_to_vid(mask),
+                              vlan_tci_to_pcp(vlan_tci),
+                              vlan_tci_to_pcp(mask),
+                              vlan_tci_to_cfi(vlan_tci),
+                              vlan_tci_to_cfi(mask));
+            } else {
+                format_vlan_tci(ds, vlan_tci);
+            }
+        }
         break;
 
     case OVS_KEY_ATTR_MPLS: {
         const struct ovs_key_mpls *mpls_key = nl_attr_get(a);
-        ds_put_char(ds, '(');
-        format_mpls_lse(ds, mpls_key->mpls_lse);
-        ds_put_char(ds, ')');
+        const struct ovs_key_mpls *mpls_mask = NULL;
+        if (!is_exact) {
+            mpls_mask = nl_attr_get(ma);
+        }
+        format_mpls(ds, mpls_key, mpls_mask);
         break;
     }
 
     case OVS_KEY_ATTR_ETHERTYPE:
-        ds_put_format(ds, "(0x%04"PRIx16")",
-                      ntohs(nl_attr_get_be16(a)));
+        ds_put_format(ds, "0x%04"PRIx16, ntohs(nl_attr_get_be16(a)));
+        if (!is_exact) {
+            ds_put_format(ds, "/0x%04"PRIx16, ntohs(nl_attr_get_be16(ma)));
+        }
         break;
 
     case OVS_KEY_ATTR_IPV4:
-        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),
-                      ipv4_key->ipv4_proto, ipv4_key->ipv4_tos,
-                      ipv4_key->ipv4_ttl,
-                      ovs_frag_type_to_string(ipv4_key->ipv4_frag));
+        if (!is_exact) {
+            const struct ovs_key_ipv4 *ipv4_key = nl_attr_get(a);
+            const struct ovs_key_ipv4 *ipv4_mask = nl_attr_get(ma);
+
+            ds_put_format(ds, "src="IP_FMT"/"IP_FMT",dst="IP_FMT"/"IP_FMT
+                          ",proto=%"PRIu8"/%#"PRIx8",tos=%#"PRIx8"/%#"PRIx8
+                          ",ttl=%"PRIu8"/%#"PRIx8",frag=%s/%#"PRIx8,
+                          IP_ARGS(ipv4_key->ipv4_src),
+                          IP_ARGS(ipv4_mask->ipv4_src),
+                          IP_ARGS(ipv4_key->ipv4_dst),
+                          IP_ARGS(ipv4_mask->ipv4_dst),
+                          ipv4_key->ipv4_proto, ipv4_mask->ipv4_proto,
+                          ipv4_key->ipv4_tos, ipv4_mask->ipv4_tos,
+                          ipv4_key->ipv4_ttl, ipv4_mask->ipv4_ttl,
+                          ovs_frag_type_to_string(ipv4_key->ipv4_frag),
+                          ipv4_mask->ipv4_frag);
+        } else {
+            const struct ovs_key_ipv4 *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),
+                          ipv4_key->ipv4_proto, ipv4_key->ipv4_tos,
+                          ipv4_key->ipv4_ttl,
+                          ovs_frag_type_to_string(ipv4_key->ipv4_frag));
+        }
         break;
 
-    case OVS_KEY_ATTR_IPV6: {
-        char src_str[INET6_ADDRSTRLEN];
-        char dst_str[INET6_ADDRSTRLEN];
+    case OVS_KEY_ATTR_IPV6:
+        if (!is_exact) {
+            const struct ovs_key_ipv6 *ipv6_key, *ipv6_mask;
+            char src_str[INET6_ADDRSTRLEN];
+            char dst_str[INET6_ADDRSTRLEN];
+            char src_mask[INET6_ADDRSTRLEN];
+            char dst_mask[INET6_ADDRSTRLEN];
+
+            ipv6_key = nl_attr_get(a);
+            inet_ntop(AF_INET6, ipv6_key->ipv6_src, src_str, sizeof src_str);
+            inet_ntop(AF_INET6, ipv6_key->ipv6_dst, dst_str, sizeof dst_str);
+
+            ipv6_mask = nl_attr_get(ma);
+            inet_ntop(AF_INET6, ipv6_mask->ipv6_src, src_mask, sizeof src_mask);
+            inet_ntop(AF_INET6, ipv6_mask->ipv6_dst, dst_mask, sizeof dst_mask);
+
+            ds_put_format(ds, "src=%s/%s,dst=%s/%s,label=%#"PRIx32"/%#"PRIx32
+                          ",proto=%"PRIu8"/%#"PRIx8",tclass=%#"PRIx8"/%#"PRIx8
+                          ",hlimit=%"PRIu8"/%#"PRIx8",frag=%s/%#"PRIx8,
+                          src_str, src_mask, dst_str, dst_mask,
+                          ntohl(ipv6_key->ipv6_label),
+                          ntohl(ipv6_mask->ipv6_label),
+                          ipv6_key->ipv6_proto, ipv6_mask->ipv6_proto,
+                          ipv6_key->ipv6_tclass, ipv6_mask->ipv6_tclass,
+                          ipv6_key->ipv6_hlimit, ipv6_mask->ipv6_hlimit,
+                          ovs_frag_type_to_string(ipv6_key->ipv6_frag),
+                          ipv6_mask->ipv6_frag);
+        } else {
+            const struct ovs_key_ipv6 *ipv6_key;
+            char src_str[INET6_ADDRSTRLEN];
+            char dst_str[INET6_ADDRSTRLEN];
 
-        ipv6_key = nl_attr_get(a);
-        inet_ntop(AF_INET6, ipv6_key->ipv6_src, src_str, sizeof src_str);
-        inet_ntop(AF_INET6, ipv6_key->ipv6_dst, dst_str, sizeof dst_str);
+            ipv6_key = nl_attr_get(a);
+            inet_ntop(AF_INET6, ipv6_key->ipv6_src, src_str, sizeof src_str);
+            inet_ntop(AF_INET6, ipv6_key->ipv6_dst, dst_str, sizeof dst_str);
 
-        ds_put_format(ds, "(src=%s,dst=%s,label=%#"PRIx32",proto=%"PRIu8
-                      ",tclass=%#"PRIx8",hlimit=%"PRIu8",frag=%s)",
-                      src_str, dst_str, ntohl(ipv6_key->ipv6_label),
-                      ipv6_key->ipv6_proto, ipv6_key->ipv6_tclass,
-                      ipv6_key->ipv6_hlimit,
-                      ovs_frag_type_to_string(ipv6_key->ipv6_frag));
+            ds_put_format(ds, "src=%s,dst=%s,label=%#"PRIx32",proto=%"PRIu8
+                          ",tclass=%#"PRIx8",hlimit=%"PRIu8",frag=%s",
+                          src_str, dst_str, ntohl(ipv6_key->ipv6_label),
+                          ipv6_key->ipv6_proto, ipv6_key->ipv6_tclass,
+                          ipv6_key->ipv6_hlimit,
+                          ovs_frag_type_to_string(ipv6_key->ipv6_frag));
+        }
         break;
-    }
 
     case OVS_KEY_ATTR_TCP:
-        tcp_key = nl_attr_get(a);
-        ds_put_format(ds, "(src=%"PRIu16",dst=%"PRIu16")",
-                      ntohs(tcp_key->tcp_src), ntohs(tcp_key->tcp_dst));
+        if (!is_exact) {
+            const struct ovs_key_tcp *tcp_mask = nl_attr_get(ma);
+            const struct ovs_key_tcp *tcp_key = nl_attr_get(a);
+
+            ds_put_format(ds, "src=%"PRIu16"/%#"PRIx16
+                          ",dst=%"PRIu16"/%#"PRIx16,
+                          ntohs(tcp_key->tcp_src), ntohs(tcp_mask->tcp_src),
+                          ntohs(tcp_key->tcp_dst), ntohs(tcp_mask->tcp_dst));
+        } else {
+            const struct ovs_key_tcp *tcp_key = nl_attr_get(a);
+
+            ds_put_format(ds, "src=%"PRIu16",dst=%"PRIu16,
+                          ntohs(tcp_key->tcp_src), ntohs(tcp_key->tcp_dst));
+        }
         break;
 
     case OVS_KEY_ATTR_UDP:
-        udp_key = nl_attr_get(a);
-        ds_put_format(ds, "(src=%"PRIu16",dst=%"PRIu16")",
-                      ntohs(udp_key->udp_src), ntohs(udp_key->udp_dst));
+        if (!is_exact) {
+            const struct ovs_key_udp *udp_mask = nl_attr_get(ma);
+            const struct ovs_key_udp *udp_key = nl_attr_get(a);
+
+            ds_put_format(ds, "src=%"PRIu16"/%#"PRIx16
+                          ",dst=%"PRIu16"/%#"PRIx16,
+                          ntohs(udp_key->udp_src), ntohs(udp_mask->udp_src),
+                          ntohs(udp_key->udp_dst), ntohs(udp_mask->udp_dst));
+        } else {
+            const struct ovs_key_udp *udp_key = nl_attr_get(a);
+
+            ds_put_format(ds, "src=%"PRIu16",dst=%"PRIu16,
+                          ntohs(udp_key->udp_src), ntohs(udp_key->udp_dst));
+        }
         break;
 
     case OVS_KEY_ATTR_ICMP:
-        icmp_key = nl_attr_get(a);
-        ds_put_format(ds, "(type=%"PRIu8",code=%"PRIu8")",
-                      icmp_key->icmp_type, icmp_key->icmp_code);
+        if (!is_exact) {
+            const struct ovs_key_icmp *icmp_mask = nl_attr_get(ma);
+            const struct ovs_key_icmp *icmp_key = nl_attr_get(a);
+
+            ds_put_format(ds, "type=%"PRIu8"/%#"PRIx8",code=%"PRIu8"/%#"PRIx8,
+                          icmp_key->icmp_type, icmp_mask->icmp_type,
+                          icmp_key->icmp_code, icmp_mask->icmp_code);
+        } else {
+            const struct ovs_key_icmp *icmp_key = nl_attr_get(a);
+
+            ds_put_format(ds, "type=%"PRIu8",code=%"PRIu8,
+                          icmp_key->icmp_type, icmp_key->icmp_code);
+        }
         break;
 
     case OVS_KEY_ATTR_ICMPV6:
-        icmpv6_key = nl_attr_get(a);
-        ds_put_format(ds, "(type=%"PRIu8",code=%"PRIu8")",
-                      icmpv6_key->icmpv6_type, icmpv6_key->icmpv6_code);
+        if (!is_exact) {
+            const struct ovs_key_icmpv6 *icmpv6_mask = nl_attr_get(ma);
+            const struct ovs_key_icmpv6 *icmpv6_key = nl_attr_get(a);
+
+            ds_put_format(ds, "type=%"PRIu8"/%#"PRIx8",code=%"PRIu8"/%#"PRIx8,
+                          icmpv6_key->icmpv6_type, icmpv6_mask->icmpv6_type,
+                          icmpv6_key->icmpv6_code, icmpv6_mask->icmpv6_code);
+        } else {
+            const struct ovs_key_icmpv6 *icmpv6_key = nl_attr_get(a);
+
+            ds_put_format(ds, "type=%"PRIu8",code=%"PRIu8,
+                          icmpv6_key->icmpv6_type, icmpv6_key->icmpv6_code);
+        }
         break;
 
     case OVS_KEY_ATTR_ARP:
-        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),
-                      ntohs(arp_key->arp_op), ETH_ADDR_ARGS(arp_key->arp_sha),
-                      ETH_ADDR_ARGS(arp_key->arp_tha));
+        if (!is_exact) {
+            const struct ovs_key_arp *arp_mask = nl_attr_get(ma);
+            const struct ovs_key_arp *arp_key = nl_attr_get(a);
+
+            ds_put_format(ds, "sip="IP_FMT"/"IP_FMT",tip="IP_FMT"/"IP_FMT
+                          ",op=%"PRIu16"/%#"PRIx16
+                          ",sha="ETH_ADDR_FMT"/"ETH_ADDR_FMT
+                          ",tha="ETH_ADDR_FMT"/"ETH_ADDR_FMT,
+                          IP_ARGS(arp_key->arp_sip),
+                          IP_ARGS(arp_mask->arp_sip),
+                          IP_ARGS(arp_key->arp_tip),
+                          IP_ARGS(arp_mask->arp_tip),
+                          ntohs(arp_key->arp_op), ntohs(arp_mask->arp_op),
+                          ETH_ADDR_ARGS(arp_key->arp_sha),
+                          ETH_ADDR_ARGS(arp_mask->arp_sha),
+                          ETH_ADDR_ARGS(arp_key->arp_tha),
+                          ETH_ADDR_ARGS(arp_mask->arp_tha));
+        } else {
+            const struct ovs_key_arp *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),
+                          ntohs(arp_key->arp_op),
+                          ETH_ADDR_ARGS(arp_key->arp_sha),
+                          ETH_ADDR_ARGS(arp_key->arp_tha));
+        }
         break;
 
     case OVS_KEY_ATTR_ND: {
+        const struct ovs_key_nd *nd_key, *nd_mask = NULL;
         char target[INET6_ADDRSTRLEN];
 
         nd_key = nl_attr_get(a);
+        if (!is_exact) {
+            nd_mask = nl_attr_get(ma);
+        }
+
         inet_ntop(AF_INET6, nd_key->nd_target, target, sizeof target);
+        ds_put_format(ds, "target=%s", target);
+        if (!is_exact) {
+            inet_ntop(AF_INET6, nd_mask->nd_target, target, sizeof target);
+            ds_put_format(ds, "/%s", target);
+        }
 
-        ds_put_format(ds, "(target=%s", target);
         if (!eth_addr_is_zero(nd_key->nd_sll)) {
             ds_put_format(ds, ",sll="ETH_ADDR_FMT,
                           ETH_ADDR_ARGS(nd_key->nd_sll));
+            if (!is_exact) {
+                ds_put_format(ds, "/"ETH_ADDR_FMT,
+                              ETH_ADDR_ARGS(nd_mask->nd_sll));
+            }
         }
         if (!eth_addr_is_zero(nd_key->nd_tll)) {
             ds_put_format(ds, ",tll="ETH_ADDR_FMT,
                           ETH_ADDR_ARGS(nd_key->nd_tll));
+            if (!is_exact) {
+                ds_put_format(ds, "/"ETH_ADDR_FMT,
+                              ETH_ADDR_ARGS(nd_mask->nd_tll));
+            }
         }
-        ds_put_char(ds, ')');
         break;
     }
 
@@ -1038,25 +1298,72 @@ format_odp_key_attr(const struct nlattr *a, struct ds *ds)
     case __OVS_KEY_ATTR_MAX:
     default:
         format_generic_odp_key(a, ds);
+        if (!is_exact) {
+            ds_put_char(ds, '/');
+            format_generic_odp_key(ma, ds);
+        }
         break;
     }
+    ds_put_char(ds, ')');
+}
+
+static struct nlattr *
+generate_all_wildcard_mask(struct ofpbuf *ofp, const struct nlattr *key)
+{
+    const struct nlattr *a;
+    unsigned int left;
+    int type = nl_attr_type(key);
+    int size = nl_attr_get_size(key);
+
+    if (odp_flow_key_attr_len(type) >=0) {
+        memset(nl_msg_put_unspec_uninit(ofp, type, size), 0, size);
+    } else {
+        size_t nested_mask;
+
+        nested_mask = nl_msg_start_nested(ofp, type);
+        NL_ATTR_FOR_EACH(a, left, key, nl_attr_get_size(key)) {
+            generate_all_wildcard_mask(ofp, nl_attr_get(a));
+        }
+        nl_msg_end_nested(ofp, nested_mask);
+    }
+
+    return ofp->base;
 }
 
 /* Appends to 'ds' a string representation of the 'key_len' bytes of
- * OVS_KEY_ATTR_* attributes in 'key'. */
+ * OVS_KEY_ATTR_* attributes in 'key'. If non-null, additionally formats the
+ * 'mask_len' bytes of 'mask' which apply to 'key'. */
 void
-odp_flow_key_format(const struct nlattr *key, size_t key_len, struct ds *ds)
+odp_flow_format(const struct nlattr *key, size_t key_len,
+                const struct nlattr *mask, size_t mask_len,
+                struct ds *ds)
 {
     if (key_len) {
         const struct nlattr *a;
         unsigned int left;
+        bool has_ethtype_key = false;
+        const struct nlattr *ma = NULL;
+        struct ofpbuf ofp;
 
+        ofpbuf_init(&ofp, 100);
         NL_ATTR_FOR_EACH (a, left, key, key_len) {
             if (a != key) {
                 ds_put_char(ds, ',');
             }
-            format_odp_key_attr(a, ds);
+            if (nl_attr_type(a) == OVS_KEY_ATTR_ETHERTYPE) {
+                has_ethtype_key = true;
+            }
+            if (mask && mask_len) {
+                ma = nl_attr_find__(mask, mask_len, nl_attr_type(a));
+                if (!ma) {
+                    ma = generate_all_wildcard_mask(&ofp, a);
+                }
+            }
+            format_odp_key_attr(a, ma, ds);
+            ofpbuf_clear(&ofp);
         }
+        ofpbuf_uninit(&ofp);
+
         if (left) {
             int i;
             
@@ -1069,28 +1376,72 @@ odp_flow_key_format(const struct nlattr *key, size_t key_len, struct ds *ds)
             }
             ds_put_char(ds, ')');
         }
+        if (!has_ethtype_key) {
+            ma = nl_attr_find__(mask, mask_len, OVS_KEY_ATTR_ETHERTYPE);
+            if (ma) {
+                ds_put_format(ds, ",eth_type(0/0x%04"PRIx16")",
+                              ntohs(nl_attr_get_be16(ma)));
+            }
+        }
     } else {
         ds_put_cstr(ds, "<empty>");
     }
 }
 
+/* Appends to 'ds' a string representation of the 'key_len' bytes of
+ * OVS_KEY_ATTR_* attributes in 'key'. */
+void
+odp_flow_key_format(const struct nlattr *key,
+                    size_t key_len, struct ds *ds)
+{
+    odp_flow_format(key, key_len, NULL, 0, ds);
+}
+
+static void
+put_nd(struct ovs_key_nd* nd_key, const uint8_t *nd_sll,
+       const uint8_t *nd_tll, struct ofpbuf *key)
+{
+    if (nd_sll) {
+        memcpy(nd_key->nd_sll, nd_sll, ETH_ADDR_LEN);
+    }
+
+    if (nd_tll) {
+        memcpy(nd_key->nd_tll, nd_tll, ETH_ADDR_LEN);
+    }
+
+    nl_msg_put_unspec(key, OVS_KEY_ATTR_ND, nd_key, sizeof *nd_key);
+}
+
 static int
-put_nd_key(int n, const char *nd_target_s,
-           const uint8_t *nd_sll, const uint8_t *nd_tll, struct ofpbuf *key)
+put_nd_key(int n, const char *nd_target_s, const uint8_t *nd_sll,
+           const uint8_t *nd_tll, struct ofpbuf *key)
 {
     struct ovs_key_nd nd_key;
 
     memset(&nd_key, 0, sizeof nd_key);
+
     if (inet_pton(AF_INET6, nd_target_s, nd_key.nd_target) != 1) {
         return -EINVAL;
     }
-    if (nd_sll) {
-        memcpy(nd_key.nd_sll, nd_sll, ETH_ADDR_LEN);
-    }
-    if (nd_tll) {
-        memcpy(nd_key.nd_tll, nd_tll, ETH_ADDR_LEN);
+
+    put_nd(&nd_key, nd_sll, nd_tll, key);
+    return n;
+}
+
+static int
+put_nd_mask(int n, const char *nd_target_s,
+           const uint8_t *nd_sll, const uint8_t *nd_tll, struct ofpbuf *mask)
+{
+    struct ovs_key_nd nd_mask;
+
+    memset(&nd_mask, 0xff, sizeof nd_mask);
+
+    if (strlen(nd_target_s) != 0 &&
+            inet_pton(AF_INET6, nd_target_s, nd_mask.nd_target) != 1) {
+        return -EINVAL;
     }
-    nl_msg_put_unspec(key, OVS_KEY_ATTR_ND, &nd_key, sizeof nd_key);
+
+    put_nd(&nd_mask, nd_sll, nd_tll, mask);
     return n;
 }
 
@@ -1119,8 +1470,8 @@ mpls_lse_from_components(int mpls_label, int mpls_tc, int mpls_ttl, int mpls_bos
 }
 
 static int
-parse_odp_key_attr(const char *s, const struct simap *port_names,
-                   struct ofpbuf *key)
+parse_odp_key_mask_attr(const char *s, const struct simap *port_names,
+                        struct ofpbuf *key, struct ofpbuf *mask)
 {
     /* Many of the sscanf calls in this function use oversized destination
      * fields because some sscanf() implementations truncate the range of %i
@@ -1134,31 +1485,87 @@ parse_odp_key_attr(const char *s, const struct simap *port_names,
 
     {
         unsigned long long int priority;
+        unsigned long long int priority_mask;
         int n = -1;
 
-        if (sscanf(s, "skb_priority(%llx)%n", &priority, &n) > 0 && n > 0) {
+        if (mask && sscanf(s, "skb_priority(%lli/%lli)%n", &priority,
+                   &priority_mask, &n) > 0 && n > 0) {
             nl_msg_put_u32(key, OVS_KEY_ATTR_PRIORITY, priority);
+            nl_msg_put_u32(mask, OVS_KEY_ATTR_PRIORITY, priority_mask);
+            return n;
+        } else if (sscanf(s, "skb_priority(%lli)%n",
+                          &priority, &n) > 0 && n > 0) {
+            nl_msg_put_u32(key, OVS_KEY_ATTR_PRIORITY, priority);
+            if (mask) {
+                nl_msg_put_u32(mask, OVS_KEY_ATTR_PRIORITY, UINT32_MAX);
+            }
             return n;
         }
     }
 
     {
         unsigned long long int mark;
+        unsigned long long int mark_mask;
         int n = -1;
 
-        if (sscanf(s, "skb_mark(%llx)%n", &mark, &n) > 0 && n > 0) {
+        if (mask && sscanf(s, "skb_mark(%lli/%lli)%n", &mark,
+                   &mark_mask, &n) > 0 && n > 0) {
             nl_msg_put_u32(key, OVS_KEY_ATTR_SKB_MARK, mark);
+            nl_msg_put_u32(mask, OVS_KEY_ATTR_SKB_MARK, mark_mask);
+            return n;
+        } else if (sscanf(s, "skb_mark(%lli)%n", &mark, &n) > 0 && n > 0) {
+            nl_msg_put_u32(key, OVS_KEY_ATTR_SKB_MARK, mark);
+            if (mask) {
+                nl_msg_put_u32(mask, OVS_KEY_ATTR_SKB_MARK, UINT32_MAX);
+            }
             return n;
         }
     }
 
     {
         char tun_id_s[32];
-        int tos, ttl;
-        struct flow_tnl tun_key;
+        int tos, tos_mask, ttl, ttl_mask;
+        struct flow_tnl tun_key, tun_key_mask;
+        unsigned long long tun_id_mask;
         int n = -1;
 
-        if (sscanf(s, "tunnel(tun_id=%31[x0123456789abcdefABCDEF],"
+        if (mask && sscanf(s, "tunnel(tun_id=%31[x0123456789abcdefABCDEF]/%llx,"
+                   "src="IP_SCAN_FMT"/"IP_SCAN_FMT",dst="IP_SCAN_FMT
+                   "/"IP_SCAN_FMT",tos=%i/%i,ttl=%i/%i,flags%n",
+                   tun_id_s, &tun_id_mask,
+                   IP_SCAN_ARGS(&tun_key.ip_src),
+                   IP_SCAN_ARGS(&tun_key_mask.ip_src),
+                   IP_SCAN_ARGS(&tun_key.ip_dst),
+                   IP_SCAN_ARGS(&tun_key_mask.ip_dst),
+                   &tos, &tos_mask, &ttl, &ttl_mask,
+                   &n) > 0 && n > 0) {
+            int res;
+            uint32_t flags;
+
+            tun_key.tun_id = htonll(strtoull(tun_id_s, NULL, 0));
+            tun_key_mask.tun_id = htonll(tun_id_mask);
+            tun_key.ip_tos = tos;
+            tun_key_mask.ip_tos = tos_mask;
+            tun_key.ip_ttl = ttl;
+            tun_key_mask.ip_ttl = ttl_mask;
+            res = parse_flags(&s[n], flow_tun_flag_to_string, &flags);
+            tun_key.flags = flags;
+            tun_key_mask.flags = UINT16_MAX;
+
+            if (res < 0) {
+                return res;
+            }
+            n += res;
+            if (s[n] != ')') {
+                return -EINVAL;
+            }
+            n++;
+            tun_key_to_attr(key, &tun_key);
+            if (mask) {
+                tun_key_to_attr(mask, &tun_key_mask);
+            }
+            return n;
+        } else if (sscanf(s, "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.ip_src),
@@ -1171,7 +1578,7 @@ parse_odp_key_attr(const char *s, const struct simap *port_names,
             tun_key.ip_tos = tos;
             tun_key.ip_ttl = ttl;
             res = parse_flags(&s[n], flow_tun_flag_to_string, &flags);
-            tun_key.flags = (uint16_t) flags;
+            tun_key.flags = flags;
 
             if (res < 0) {
                 return res;
@@ -1182,20 +1589,35 @@ parse_odp_key_attr(const char *s, const struct simap *port_names,
             }
             n++;
             tun_key_to_attr(key, &tun_key);
+
+            if (mask) {
+                memset(&tun_key, 0xff, sizeof tun_key);
+                tun_key_to_attr(mask, &tun_key);
+            }
             return n;
         }
     }
 
     {
         unsigned long long int in_port;
+        unsigned long long int in_port_mask;
         int n = -1;
 
-        if (sscanf(s, "in_port(%lli)%n", &in_port, &n) > 0 && n > 0) {
+        if (mask && sscanf(s, "in_port(%lli/%lli)%n", &in_port,
+                   &in_port_mask, &n) > 0 && n > 0) {
             nl_msg_put_u32(key, OVS_KEY_ATTR_IN_PORT, in_port);
+            nl_msg_put_u32(mask, OVS_KEY_ATTR_IN_PORT, in_port_mask);
+            return n;
+        } else if (sscanf(s, "in_port(%lli)%n", &in_port, &n) > 0 && n > 0) {
+            nl_msg_put_u32(key, OVS_KEY_ATTR_IN_PORT, in_port);
+            if (mask) {
+                nl_msg_put_u32(mask, OVS_KEY_ATTR_IN_PORT, UINT32_MAX);
+            }
             return n;
         }
     }
 
+
     if (port_names && !strncmp(s, "in_port(", 8)) {
         const char *name;
         const struct simap_node *node;
@@ -1206,63 +1628,140 @@ parse_odp_key_attr(const char *s, const struct simap *port_names,
         node = simap_find_len(port_names, name, name_len);
         if (node) {
             nl_msg_put_u32(key, OVS_KEY_ATTR_IN_PORT, node->data);
+
+            if (mask) {
+                nl_msg_put_u32(mask, OVS_KEY_ATTR_IN_PORT, UINT32_MAX);
+            }
             return 8 + name_len + 1;
         }
     }
 
     {
         struct ovs_key_ethernet eth_key;
+        struct ovs_key_ethernet eth_key_mask;
         int n = -1;
 
-        if (sscanf(s,
+        if (mask && sscanf(s,
+                   "eth(src="ETH_ADDR_SCAN_FMT"/"ETH_ADDR_SCAN_FMT","
+                        "dst="ETH_ADDR_SCAN_FMT"/"ETH_ADDR_SCAN_FMT")%n",
+                ETH_ADDR_SCAN_ARGS(eth_key.eth_src),
+                ETH_ADDR_SCAN_ARGS(eth_key_mask.eth_src),
+                ETH_ADDR_SCAN_ARGS(eth_key.eth_dst),
+                ETH_ADDR_SCAN_ARGS(eth_key_mask.eth_dst), &n) > 0 && n > 0) {
+
+            nl_msg_put_unspec(key, OVS_KEY_ATTR_ETHERNET,
+                              &eth_key, sizeof eth_key);
+            nl_msg_put_unspec(mask, OVS_KEY_ATTR_ETHERNET,
+                              &eth_key_mask, sizeof eth_key_mask);
+            return n;
+        } else if (sscanf(s,
                    "eth(src="ETH_ADDR_SCAN_FMT",dst="ETH_ADDR_SCAN_FMT")%n",
                    ETH_ADDR_SCAN_ARGS(eth_key.eth_src),
                    ETH_ADDR_SCAN_ARGS(eth_key.eth_dst), &n) > 0 && n > 0) {
             nl_msg_put_unspec(key, OVS_KEY_ATTR_ETHERNET,
                               &eth_key, sizeof eth_key);
+
+            if (mask) {
+                memset(&eth_key, 0xff, sizeof eth_key);
+                nl_msg_put_unspec(mask, OVS_KEY_ATTR_ETHERNET,
+                              &eth_key, sizeof eth_key);
+            }
             return n;
         }
     }
 
     {
-        uint16_t vid;
-        int pcp;
-        int cfi;
+        uint16_t vid, vid_mask;
+        int pcp, pcp_mask;
+        int cfi, cfi_mask;
         int n = -1;
 
-        if ((sscanf(s, "vlan(vid=%"SCNi16",pcp=%i)%n", &vid, &pcp, &n) > 0
-             && n > 0)) {
+        if (mask && (sscanf(s, "vlan(vid=%"SCNi16"/%"SCNi16",pcp=%i/%i)%n",
+                            &vid, &vid_mask, &pcp, &pcp_mask, &n) > 0 && n > 0)) {
             nl_msg_put_be16(key, OVS_KEY_ATTR_VLAN,
                             htons((vid << VLAN_VID_SHIFT) |
                                   (pcp << VLAN_PCP_SHIFT) |
                                   VLAN_CFI));
+            nl_msg_put_be16(mask, OVS_KEY_ATTR_VLAN,
+                            htons((vid_mask << VLAN_VID_SHIFT) |
+                                  (pcp_mask << VLAN_PCP_SHIFT) |
+                                  (1 << VLAN_CFI_SHIFT)));
+            return n;
+        } else if ((sscanf(s, "vlan(vid=%"SCNi16",pcp=%i)%n",
+                           &vid, &pcp, &n) > 0 && n > 0)) {
+            nl_msg_put_be16(key, OVS_KEY_ATTR_VLAN,
+                            htons((vid << VLAN_VID_SHIFT) |
+                                  (pcp << VLAN_PCP_SHIFT) |
+                                  VLAN_CFI));
+            if (mask) {
+                nl_msg_put_be16(mask, OVS_KEY_ATTR_VLAN, htons(UINT16_MAX));
+            }
+            return n;
+        } else if (mask && (sscanf(s, "vlan(vid=%"SCNi16"/%"SCNi16",pcp=%i/%i,cfi=%i/%i)%n",
+                                   &vid, &vid_mask, &pcp, &pcp_mask, &cfi, &cfi_mask, &n) > 0 && n > 0)) {
+            nl_msg_put_be16(key, OVS_KEY_ATTR_VLAN,
+                            htons((vid << VLAN_VID_SHIFT) |
+                                  (pcp << VLAN_PCP_SHIFT) |
+                                  (cfi ? VLAN_CFI : 0)));
+            nl_msg_put_be16(mask, OVS_KEY_ATTR_VLAN,
+                            htons((vid_mask << VLAN_VID_SHIFT) |
+                                  (pcp_mask << VLAN_PCP_SHIFT) |
+                                  (cfi_mask << VLAN_CFI_SHIFT)));
             return n;
         } else if ((sscanf(s, "vlan(vid=%"SCNi16",pcp=%i,cfi=%i)%n",
-                           &vid, &pcp, &cfi, &n) > 0
-             && n > 0)) {
+                           &vid, &pcp, &cfi, &n) > 0 && n > 0)) {
             nl_msg_put_be16(key, OVS_KEY_ATTR_VLAN,
                             htons((vid << VLAN_VID_SHIFT) |
                                   (pcp << VLAN_PCP_SHIFT) |
                                   (cfi ? VLAN_CFI : 0)));
+            if (mask) {
+                nl_msg_put_be16(mask, OVS_KEY_ATTR_VLAN, htons(UINT16_MAX));
+            }
             return n;
         }
     }
 
     {
         int eth_type;
+        int eth_type_mask;
         int n = -1;
 
-        if (sscanf(s, "eth_type(%i)%n", &eth_type, &n) > 0 && n > 0) {
+        if (mask && sscanf(s, "eth_type(%i/%i)%n",
+                   &eth_type, &eth_type_mask, &n) > 0 && n > 0) {
+            if (eth_type != 0) {
+                nl_msg_put_be16(key, OVS_KEY_ATTR_ETHERTYPE, htons(eth_type));
+            }
+            nl_msg_put_be16(mask, OVS_KEY_ATTR_ETHERTYPE, htons(eth_type_mask));
+            return n;
+        } else if (sscanf(s, "eth_type(%i)%n", &eth_type, &n) > 0 && n > 0) {
             nl_msg_put_be16(key, OVS_KEY_ATTR_ETHERTYPE, htons(eth_type));
+            if (mask) {
+                nl_msg_put_be16(mask, OVS_KEY_ATTR_ETHERTYPE,
+                                htons(UINT16_MAX));
+            }
             return n;
         }
     }
 
     {
         int label, tc, ttl, bos;
+        int label_mask, tc_mask, ttl_mask, bos_mask;
         int n = -1;
 
-        if (sscanf(s, "mpls(label=%"SCNi32",tc=%i,ttl=%i,bos=%i)%n",
+        if (mask && sscanf(s, "mpls(label=%"SCNi32"/%"SCNi32",tc=%i/%i,ttl=%i/%i,bos=%i/%i)%n",
+                    &label, &label_mask, &tc, &tc_mask, &ttl, &ttl_mask, &bos, &bos_mask, &n) > 0 && n > 0) {
+            struct ovs_key_mpls *mpls, *mpls_mask;
+
+            mpls = nl_msg_put_unspec_uninit(key, OVS_KEY_ATTR_MPLS,
+                                            sizeof *mpls);
+            mpls->mpls_lse = mpls_lse_from_components(label, tc, ttl, bos);
+
+            mpls_mask = nl_msg_put_unspec_uninit(mask, OVS_KEY_ATTR_MPLS,
+                                            sizeof *mpls_mask);
+            mpls_mask->mpls_lse = mpls_lse_from_components(
+                                  label_mask, tc_mask, ttl_mask, bos_mask);
+            return n;
+        } else if (sscanf(s, "mpls(label=%"SCNi32",tc=%i,ttl=%i,bos=%i)%n",
                     &label, &tc, &ttl, &bos, &n) > 0 &&
                     n > 0) {
             struct ovs_key_mpls *mpls;
@@ -1270,21 +1769,60 @@ parse_odp_key_attr(const char *s, const struct simap *port_names,
             mpls = nl_msg_put_unspec_uninit(key, OVS_KEY_ATTR_MPLS,
                                             sizeof *mpls);
             mpls->mpls_lse = mpls_lse_from_components(label, tc, ttl, bos);
+            if (mask) {
+                mpls = nl_msg_put_unspec_uninit(mask, OVS_KEY_ATTR_MPLS,
+                                            sizeof *mpls);
+                mpls->mpls_lse = htonl(UINT32_MAX);
+            }
             return n;
         }
     }
 
+
     {
-        ovs_be32 ipv4_src;
-        ovs_be32 ipv4_dst;
-        int ipv4_proto;
-        int ipv4_tos;
-        int ipv4_ttl;
+        ovs_be32 ipv4_src, ipv4_src_mask;
+        ovs_be32 ipv4_dst, ipv4_dst_mask;
+        int ipv4_proto, ipv4_proto_mask;
+        int ipv4_tos, ipv4_tos_mask;
+        int ipv4_ttl, ipv4_ttl_mask;
         char frag[8];
+        int  ipv4_frag_mask;
         enum ovs_frag_type ipv4_frag;
         int n = -1;
 
-        if (sscanf(s, "ipv4(src="IP_SCAN_FMT",dst="IP_SCAN_FMT","
+        if (mask && sscanf(s, "ipv4(src="IP_SCAN_FMT"/"IP_SCAN_FMT","
+                      "dst="IP_SCAN_FMT"/"IP_SCAN_FMT","
+                      "proto=%i/%i,tos=%i/%i,ttl=%i/%i,"
+                      "frag=%7[a-z]/%i)%n",
+                      IP_SCAN_ARGS(&ipv4_src), IP_SCAN_ARGS(&ipv4_src_mask),
+                      IP_SCAN_ARGS(&ipv4_dst), IP_SCAN_ARGS(&ipv4_dst_mask),
+                      &ipv4_proto, &ipv4_proto_mask,
+                      &ipv4_tos, &ipv4_tos_mask, &ipv4_ttl, &ipv4_ttl_mask,
+                      frag, &ipv4_frag_mask, &n) > 0
+            && n > 0
+            && ovs_frag_type_from_string(frag, &ipv4_frag)) {
+            struct ovs_key_ipv4 ipv4_key;
+            struct ovs_key_ipv4 ipv4_mask;
+
+            ipv4_key.ipv4_src = ipv4_src;
+            ipv4_key.ipv4_dst = ipv4_dst;
+            ipv4_key.ipv4_proto = ipv4_proto;
+            ipv4_key.ipv4_tos = ipv4_tos;
+            ipv4_key.ipv4_ttl = ipv4_ttl;
+            ipv4_key.ipv4_frag = ipv4_frag;
+            nl_msg_put_unspec(key, OVS_KEY_ATTR_IPV4,
+                              &ipv4_key, sizeof ipv4_key);
+
+            ipv4_mask.ipv4_src = ipv4_src_mask;
+            ipv4_mask.ipv4_dst = ipv4_dst_mask;
+            ipv4_mask.ipv4_proto = ipv4_proto_mask;
+            ipv4_mask.ipv4_tos = ipv4_tos_mask;
+            ipv4_mask.ipv4_ttl = ipv4_ttl_mask;
+            ipv4_mask.ipv4_frag = ipv4_frag_mask;
+            nl_msg_put_unspec(mask, OVS_KEY_ATTR_IPV4,
+                              &ipv4_mask, sizeof ipv4_mask);
+            return n;
+        } else if (sscanf(s, "ipv4(src="IP_SCAN_FMT",dst="IP_SCAN_FMT","
                    "proto=%i,tos=%i,ttl=%i,frag=%7[a-z])%n",
                    IP_SCAN_ARGS(&ipv4_src), IP_SCAN_ARGS(&ipv4_dst),
                    &ipv4_proto, &ipv4_tos, &ipv4_ttl, frag, &n) > 0
@@ -1300,22 +1838,68 @@ parse_odp_key_attr(const char *s, const struct simap *port_names,
             ipv4_key.ipv4_frag = ipv4_frag;
             nl_msg_put_unspec(key, OVS_KEY_ATTR_IPV4,
                               &ipv4_key, sizeof ipv4_key);
+
+            if (mask) {
+                memset(&ipv4_key, 0xff, sizeof ipv4_key);
+                nl_msg_put_unspec(mask, OVS_KEY_ATTR_IPV4,
+                              &ipv4_key, sizeof ipv4_key);
+            }
             return n;
         }
     }
 
     {
         char ipv6_src_s[IPV6_SCAN_LEN + 1];
+        char ipv6_src_mask_s[IPV6_SCAN_LEN + 1];
         char ipv6_dst_s[IPV6_SCAN_LEN + 1];
-        int ipv6_label;
-        int ipv6_proto;
-        int ipv6_tclass;
-        int ipv6_hlimit;
+        char ipv6_dst_mask_s[IPV6_SCAN_LEN + 1];
+        int ipv6_label, ipv6_label_mask;
+        int ipv6_proto, ipv6_proto_mask;
+        int ipv6_tclass, ipv6_tclass_mask;
+        int ipv6_hlimit, ipv6_hlimit_mask;
         char frag[8];
         enum ovs_frag_type ipv6_frag;
+        int ipv6_frag_mask;
         int n = -1;
 
-        if (sscanf(s, "ipv6(src="IPV6_SCAN_FMT",dst="IPV6_SCAN_FMT","
+        if (mask && sscanf(s, "ipv6(src="IPV6_SCAN_FMT"/"IPV6_SCAN_FMT",dst="
+                   IPV6_SCAN_FMT"/"IPV6_SCAN_FMT","
+                   "label=%i/%i,proto=%i/%i,tclass=%i/%i,"
+                   "hlimit=%i/%i,frag=%7[a-z]/%i)%n",
+                   ipv6_src_s, ipv6_src_mask_s, ipv6_dst_s, ipv6_dst_mask_s,
+                   &ipv6_label, &ipv6_label_mask, &ipv6_proto,
+                   &ipv6_proto_mask, &ipv6_tclass, &ipv6_tclass_mask,
+                   &ipv6_hlimit, &ipv6_hlimit_mask, frag,
+                   &ipv6_frag_mask, &n) > 0
+            && n > 0
+            && ovs_frag_type_from_string(frag, &ipv6_frag)) {
+            struct ovs_key_ipv6 ipv6_key;
+            struct ovs_key_ipv6 ipv6_mask;
+
+            if (inet_pton(AF_INET6, ipv6_src_s, &ipv6_key.ipv6_src) != 1 ||
+                inet_pton(AF_INET6, ipv6_dst_s, &ipv6_key.ipv6_dst) != 1 ||
+                inet_pton(AF_INET6, ipv6_src_mask_s, &ipv6_mask.ipv6_src) != 1 ||
+                inet_pton(AF_INET6, ipv6_dst_mask_s, &ipv6_mask.ipv6_dst) != 1) {
+                return -EINVAL;
+            }
+
+            ipv6_key.ipv6_label = htonl(ipv6_label);
+            ipv6_key.ipv6_proto = ipv6_proto;
+            ipv6_key.ipv6_tclass = ipv6_tclass;
+            ipv6_key.ipv6_hlimit = ipv6_hlimit;
+            ipv6_key.ipv6_frag = ipv6_frag;
+            nl_msg_put_unspec(key, OVS_KEY_ATTR_IPV6,
+                              &ipv6_key, sizeof ipv6_key);
+
+            ipv6_mask.ipv6_label = htonl(ipv6_label_mask);
+            ipv6_mask.ipv6_proto = ipv6_proto_mask;
+            ipv6_mask.ipv6_tclass = ipv6_tclass_mask;
+            ipv6_mask.ipv6_hlimit = ipv6_hlimit_mask;
+            ipv6_mask.ipv6_frag = ipv6_frag_mask;
+            nl_msg_put_unspec(mask, OVS_KEY_ATTR_IPV6,
+                              &ipv6_mask, sizeof ipv6_mask);
+            return n;
+        } else if (sscanf(s, "ipv6(src="IPV6_SCAN_FMT",dst="IPV6_SCAN_FMT","
                    "label=%i,proto=%i,tclass=%i,hlimit=%i,frag=%7[a-z])%n",
                    ipv6_src_s, ipv6_dst_s, &ipv6_label,
                    &ipv6_proto, &ipv6_tclass, &ipv6_hlimit, frag, &n) > 0
@@ -1334,6 +1918,12 @@ parse_odp_key_attr(const char *s, const struct simap *port_names,
             ipv6_key.ipv6_frag = ipv6_frag;
             nl_msg_put_unspec(key, OVS_KEY_ATTR_IPV6,
                               &ipv6_key, sizeof ipv6_key);
+
+            if (mask) {
+                memset(&ipv6_key, 0xff, sizeof ipv6_key);
+                nl_msg_put_unspec(mask, OVS_KEY_ATTR_IPV6,
+                              &ipv6_key, sizeof ipv6_key);
+            }
             return n;
         }
     }
@@ -1341,15 +1931,38 @@ parse_odp_key_attr(const char *s, const struct simap *port_names,
     {
         int tcp_src;
         int tcp_dst;
+        int tcp_src_mask;
+        int tcp_dst_mask;
         int n = -1;
 
-        if (sscanf(s, "tcp(src=%i,dst=%i)%n",&tcp_src, &tcp_dst, &n) > 0
+        if (mask && sscanf(s, "tcp(src=%i/%i,dst=%i/%i)%n",
+                   &tcp_src, &tcp_src_mask, &tcp_dst, &tcp_dst_mask, &n) > 0
+            && n > 0) {
+            struct ovs_key_tcp tcp_key;
+            struct ovs_key_tcp tcp_mask;
+
+            tcp_key.tcp_src = htons(tcp_src);
+            tcp_key.tcp_dst = htons(tcp_dst);
+            nl_msg_put_unspec(key, OVS_KEY_ATTR_TCP, &tcp_key, sizeof tcp_key);
+
+            tcp_mask.tcp_src = htons(tcp_src_mask);
+            tcp_mask.tcp_dst = htons(tcp_dst_mask);
+            nl_msg_put_unspec(mask, OVS_KEY_ATTR_TCP,
+                              &tcp_mask, sizeof tcp_mask);
+            return n;
+        } else if (sscanf(s, "tcp(src=%i,dst=%i)%n",&tcp_src, &tcp_dst, &n) > 0
             && n > 0) {
             struct ovs_key_tcp tcp_key;
 
             tcp_key.tcp_src = htons(tcp_src);
             tcp_key.tcp_dst = htons(tcp_dst);
             nl_msg_put_unspec(key, OVS_KEY_ATTR_TCP, &tcp_key, sizeof tcp_key);
+
+            if (mask) {
+                memset(&tcp_key, 0xff, sizeof tcp_key);
+                nl_msg_put_unspec(mask, OVS_KEY_ATTR_TCP,
+                              &tcp_key, sizeof tcp_key);
+            }
             return n;
         }
     }
@@ -1357,8 +1970,26 @@ parse_odp_key_attr(const char *s, const struct simap *port_names,
     {
         int udp_src;
         int udp_dst;
+        int udp_src_mask;
+        int udp_dst_mask;
         int n = -1;
 
+        if (mask && sscanf(s, "udp(src=%i/%i,dst=%i/%i)%n",
+                   &udp_src, &udp_src_mask,
+                   &udp_dst, &udp_dst_mask, &n) > 0 && n > 0) {
+            struct ovs_key_udp udp_key;
+            struct ovs_key_udp udp_mask;
+
+            udp_key.udp_src = htons(udp_src);
+            udp_key.udp_dst = htons(udp_dst);
+            nl_msg_put_unspec(key, OVS_KEY_ATTR_UDP, &udp_key, sizeof udp_key);
+
+            udp_mask.udp_src = htons(udp_src_mask);
+            udp_mask.udp_dst = htons(udp_dst_mask);
+            nl_msg_put_unspec(mask, OVS_KEY_ATTR_UDP,
+                              &udp_mask, sizeof udp_mask);
+            return n;
+        }
         if (sscanf(s, "udp(src=%i,dst=%i)%n", &udp_src, &udp_dst, &n) > 0
             && n > 0) {
             struct ovs_key_udp udp_key;
@@ -1366,6 +1997,11 @@ parse_odp_key_attr(const char *s, const struct simap *port_names,
             udp_key.udp_src = htons(udp_src);
             udp_key.udp_dst = htons(udp_dst);
             nl_msg_put_unspec(key, OVS_KEY_ATTR_UDP, &udp_key, sizeof udp_key);
+
+            if (mask) {
+                memset(&udp_key, 0xff, sizeof udp_key);
+                nl_msg_put_unspec(mask, OVS_KEY_ATTR_UDP, &udp_key, sizeof udp_key);
+            }
             return n;
         }
     }
@@ -1373,9 +2009,27 @@ parse_odp_key_attr(const char *s, const struct simap *port_names,
     {
         int icmp_type;
         int icmp_code;
+        int icmp_type_mask;
+        int icmp_code_mask;
         int n = -1;
 
-        if (sscanf(s, "icmp(type=%i,code=%i)%n",
+        if (mask && sscanf(s, "icmp(type=%i/%i,code=%i/%i)%n",
+                   &icmp_type, &icmp_type_mask,
+                   &icmp_code, &icmp_code_mask, &n) > 0 && n > 0) {
+            struct ovs_key_icmp icmp_key;
+            struct ovs_key_icmp icmp_mask;
+
+            icmp_key.icmp_type = icmp_type;
+            icmp_key.icmp_code = icmp_code;
+            nl_msg_put_unspec(key, OVS_KEY_ATTR_ICMP,
+                              &icmp_key, sizeof icmp_key);
+
+            icmp_mask.icmp_type = icmp_type_mask;
+            icmp_mask.icmp_code = icmp_code_mask;
+            nl_msg_put_unspec(mask, OVS_KEY_ATTR_ICMP,
+                              &icmp_mask, sizeof icmp_mask);
+            return n;
+        } else if (sscanf(s, "icmp(type=%i,code=%i)%n",
                    &icmp_type, &icmp_code, &n) > 0
             && n > 0) {
             struct ovs_key_icmp icmp_key;
@@ -1384,32 +2038,90 @@ parse_odp_key_attr(const char *s, const struct simap *port_names,
             icmp_key.icmp_code = icmp_code;
             nl_msg_put_unspec(key, OVS_KEY_ATTR_ICMP,
                               &icmp_key, sizeof icmp_key);
+            if (mask) {
+                memset(&icmp_key, 0xff, sizeof icmp_key);
+                nl_msg_put_unspec(mask, OVS_KEY_ATTR_ICMP, &icmp_key,
+                              sizeof icmp_key);
+            }
             return n;
         }
     }
 
     {
         struct ovs_key_icmpv6 icmpv6_key;
+        struct ovs_key_icmpv6 icmpv6_mask;
+        int icmpv6_type_mask;
+        int icmpv6_code_mask;
         int n = -1;
 
-        if (sscanf(s, "icmpv6(type=%"SCNi8",code=%"SCNi8")%n",
+        if (mask && sscanf(s, "icmpv6(type=%"SCNi8"/%i,code=%"SCNi8"/%i)%n",
+                   &icmpv6_key.icmpv6_type, &icmpv6_type_mask,
+                   &icmpv6_key.icmpv6_code, &icmpv6_code_mask, &n) > 0
+            && n > 0) {
+            nl_msg_put_unspec(key, OVS_KEY_ATTR_ICMPV6,
+                              &icmpv6_key, sizeof icmpv6_key);
+
+            icmpv6_mask.icmpv6_type = icmpv6_type_mask;
+            icmpv6_mask.icmpv6_code = icmpv6_code_mask;
+            nl_msg_put_unspec(mask, OVS_KEY_ATTR_ICMPV6, &icmpv6_mask,
+                              sizeof icmpv6_mask);
+            return n;
+        } else if (sscanf(s, "icmpv6(type=%"SCNi8",code=%"SCNi8")%n",
                    &icmpv6_key.icmpv6_type, &icmpv6_key.icmpv6_code,&n) > 0
             && n > 0) {
             nl_msg_put_unspec(key, OVS_KEY_ATTR_ICMPV6,
                               &icmpv6_key, sizeof icmpv6_key);
+
+            if (mask) {
+                memset(&icmpv6_key, 0xff, sizeof icmpv6_key);
+                nl_msg_put_unspec(mask, OVS_KEY_ATTR_ICMPV6, &icmpv6_key,
+                              sizeof icmpv6_key);
+            }
             return n;
         }
     }
 
     {
-        ovs_be32 arp_sip;
-        ovs_be32 arp_tip;
-        int arp_op;
+        ovs_be32 arp_sip, arp_sip_mask;
+        ovs_be32 arp_tip, arp_tip_mask;
+        int arp_op, arp_op_mask;
         uint8_t arp_sha[ETH_ADDR_LEN];
+        uint8_t arp_sha_mask[ETH_ADDR_LEN];
         uint8_t arp_tha[ETH_ADDR_LEN];
+        uint8_t arp_tha_mask[ETH_ADDR_LEN];
         int n = -1;
 
-        if (sscanf(s, "arp(sip="IP_SCAN_FMT",tip="IP_SCAN_FMT","
+        if (mask && sscanf(s, "arp(sip="IP_SCAN_FMT"/"IP_SCAN_FMT","
+                   "tip="IP_SCAN_FMT"/"IP_SCAN_FMT","
+                   "op=%i/%i,sha="ETH_ADDR_SCAN_FMT"/"ETH_ADDR_SCAN_FMT","
+                   "tha="ETH_ADDR_SCAN_FMT"/"ETH_ADDR_SCAN_FMT")%n",
+                   IP_SCAN_ARGS(&arp_sip), IP_SCAN_ARGS(&arp_sip_mask),
+                   IP_SCAN_ARGS(&arp_tip), IP_SCAN_ARGS(&arp_tip_mask),
+                   &arp_op, &arp_op_mask,
+                   ETH_ADDR_SCAN_ARGS(arp_sha),
+                   ETH_ADDR_SCAN_ARGS(arp_sha_mask),
+                   ETH_ADDR_SCAN_ARGS(arp_tha),
+                   ETH_ADDR_SCAN_ARGS(arp_tha_mask), &n) > 0 && n > 0) {
+            struct ovs_key_arp arp_key;
+            struct ovs_key_arp arp_mask;
+
+            memset(&arp_key, 0, sizeof arp_key);
+            arp_key.arp_sip = arp_sip;
+            arp_key.arp_tip = arp_tip;
+            arp_key.arp_op = htons(arp_op);
+            memcpy(arp_key.arp_sha, arp_sha, ETH_ADDR_LEN);
+            memcpy(arp_key.arp_tha, arp_tha, ETH_ADDR_LEN);
+            nl_msg_put_unspec(key, OVS_KEY_ATTR_ARP, &arp_key, sizeof arp_key);
+
+            arp_mask.arp_sip = arp_sip_mask;
+            arp_mask.arp_tip = arp_tip_mask;
+            arp_mask.arp_op = htons(arp_op_mask);
+            memcpy(arp_mask.arp_sha, arp_sha_mask, ETH_ADDR_LEN);
+            memcpy(arp_mask.arp_tha, arp_tha_mask, ETH_ADDR_LEN);
+            nl_msg_put_unspec(mask, OVS_KEY_ATTR_ARP,
+                              &arp_mask, sizeof arp_mask);
+            return n;
+        } else if (sscanf(s, "arp(sip="IP_SCAN_FMT",tip="IP_SCAN_FMT","
                    "op=%i,sha="ETH_ADDR_SCAN_FMT",tha="ETH_ADDR_SCAN_FMT")%n",
                    IP_SCAN_ARGS(&arp_sip),
                    IP_SCAN_ARGS(&arp_tip),
@@ -1425,44 +2137,102 @@ parse_odp_key_attr(const char *s, const struct simap *port_names,
             memcpy(arp_key.arp_sha, arp_sha, ETH_ADDR_LEN);
             memcpy(arp_key.arp_tha, arp_tha, ETH_ADDR_LEN);
             nl_msg_put_unspec(key, OVS_KEY_ATTR_ARP, &arp_key, sizeof arp_key);
+
+            if (mask) {
+                memset(&arp_key, 0xff, sizeof arp_key);
+                nl_msg_put_unspec(mask, OVS_KEY_ATTR_ARP,
+                                  &arp_key, sizeof arp_key);
+            }
             return n;
         }
     }
 
     {
         char nd_target_s[IPV6_SCAN_LEN + 1];
+        char nd_target_mask_s[IPV6_SCAN_LEN + 1];
         uint8_t nd_sll[ETH_ADDR_LEN];
+        uint8_t nd_sll_mask[ETH_ADDR_LEN];
         uint8_t nd_tll[ETH_ADDR_LEN];
+        uint8_t nd_tll_mask[ETH_ADDR_LEN];
         int n = -1;
 
-        if (sscanf(s, "nd(target="IPV6_SCAN_FMT")%n",
+        nd_target_mask_s[0] = 0;
+        memset(nd_sll_mask, 0xff, sizeof nd_sll_mask);
+        memset(nd_tll_mask, 0xff, sizeof nd_tll_mask);
+
+        if (mask && sscanf(s, "nd(target="IPV6_SCAN_FMT"/"IPV6_SCAN_FMT")%n",
+                   nd_target_s, nd_target_mask_s, &n) > 0 && n > 0) {
+                put_nd_key(n, nd_target_s, NULL, NULL, key);
+                put_nd_mask(n, nd_target_mask_s, NULL, NULL, mask);
+        } else if (sscanf(s, "nd(target="IPV6_SCAN_FMT")%n",
                    nd_target_s, &n) > 0 && n > 0) {
-            return put_nd_key(n, nd_target_s, NULL, NULL, key);
-        }
-        if (sscanf(s, "nd(target="IPV6_SCAN_FMT",sll="ETH_ADDR_SCAN_FMT")%n",
+                put_nd_key(n, nd_target_s, NULL, NULL, key);
+                if (mask) {
+                    put_nd_mask(n, nd_target_mask_s, NULL, NULL, mask);
+                }
+        } else if (mask && sscanf(s, "nd(target="IPV6_SCAN_FMT"/"IPV6_SCAN_FMT
+                         ",sll="ETH_ADDR_SCAN_FMT"/"ETH_ADDR_SCAN_FMT")%n",
+                   nd_target_s, nd_target_mask_s,
+                   ETH_ADDR_SCAN_ARGS(nd_sll),
+                   ETH_ADDR_SCAN_ARGS(nd_sll_mask), &n) > 0 && n > 0) {
+            put_nd_key(n, nd_target_s, nd_sll, NULL, key);
+            put_nd_mask(n, nd_target_mask_s, nd_sll_mask, NULL, mask);
+        } else if (sscanf(s, "nd(target="IPV6_SCAN_FMT",sll="ETH_ADDR_SCAN_FMT")%n",
                    nd_target_s, ETH_ADDR_SCAN_ARGS(nd_sll), &n) > 0
             && n > 0) {
-            return put_nd_key(n, nd_target_s, nd_sll, NULL, key);
-        }
-        if (sscanf(s, "nd(target="IPV6_SCAN_FMT",tll="ETH_ADDR_SCAN_FMT")%n",
+            put_nd_key(n, nd_target_s, nd_sll, NULL, key);
+            if (mask) {
+                put_nd_mask(n, nd_target_mask_s, nd_sll_mask, NULL, mask);
+            }
+        } else if (mask && sscanf(s, "nd(target="IPV6_SCAN_FMT"/"IPV6_SCAN_FMT
+                         ",tll="ETH_ADDR_SCAN_FMT"/"ETH_ADDR_SCAN_FMT")%n",
+                   nd_target_s, nd_target_mask_s,
+                   ETH_ADDR_SCAN_ARGS(nd_tll),
+                   ETH_ADDR_SCAN_ARGS(nd_tll_mask), &n) > 0 && n > 0) {
+            put_nd_key(n, nd_target_s, NULL, nd_tll, key);
+            put_nd_mask(n, nd_target_mask_s, NULL, nd_tll_mask, mask);
+        } else if (sscanf(s, "nd(target="IPV6_SCAN_FMT",tll="ETH_ADDR_SCAN_FMT")%n",
                    nd_target_s, ETH_ADDR_SCAN_ARGS(nd_tll), &n) > 0
             && n > 0) {
-            return put_nd_key(n, nd_target_s, NULL, nd_tll, key);
-        }
-        if (sscanf(s, "nd(target="IPV6_SCAN_FMT",sll="ETH_ADDR_SCAN_FMT","
+            put_nd_key(n, nd_target_s, NULL, nd_tll, key);
+            if (mask) {
+                put_nd_mask(n, nd_target_mask_s, NULL, nd_tll_mask, mask);
+            }
+        } else if (mask && sscanf(s, "nd(target="IPV6_SCAN_FMT"/"IPV6_SCAN_FMT
+                   ",sll="ETH_ADDR_SCAN_FMT"/"ETH_ADDR_SCAN_FMT","
+                   "tll="ETH_ADDR_SCAN_FMT"/"ETH_ADDR_SCAN_FMT")%n",
+                   nd_target_s, nd_target_mask_s,
+                   ETH_ADDR_SCAN_ARGS(nd_sll), ETH_ADDR_SCAN_ARGS(nd_sll_mask),
+                   ETH_ADDR_SCAN_ARGS(nd_tll), ETH_ADDR_SCAN_ARGS(nd_tll_mask),
+                   &n) > 0
+            && n > 0) {
+            put_nd_key(n, nd_target_s, nd_sll, nd_tll, key);
+            put_nd_mask(n, nd_target_mask_s, nd_sll_mask, nd_tll_mask, mask);
+        } else if (sscanf(s, "nd(target="IPV6_SCAN_FMT",sll="ETH_ADDR_SCAN_FMT","
                    "tll="ETH_ADDR_SCAN_FMT")%n",
                    nd_target_s, ETH_ADDR_SCAN_ARGS(nd_sll),
                    ETH_ADDR_SCAN_ARGS(nd_tll), &n) > 0
             && n > 0) {
-            return put_nd_key(n, nd_target_s, nd_sll, nd_tll, key);
+            put_nd_key(n, nd_target_s, nd_sll, nd_tll, key);
+            if (mask) {
+                put_nd_mask(n, nd_target_mask_s,
+                            nd_sll_mask, nd_tll_mask, mask);
+            }
         }
+
+        if (n != -1)
+            return n;
+
     }
 
     if (!strncmp(s, "encap(", 6)) {
         const char *start = s;
-        size_t encap;
+        size_t encap, encap_mask = 0;
 
         encap = nl_msg_start_nested(key, OVS_KEY_ATTR_ENCAP);
+        if (mask) {
+            encap_mask = nl_msg_start_nested(mask, OVS_KEY_ATTR_ENCAP);
+        }
 
         s += 6;
         for (;;) {
@@ -1475,7 +2245,7 @@ parse_odp_key_attr(const char *s, const struct simap *port_names,
                 break;
             }
 
-            retval = parse_odp_key_attr(s, port_names, key);
+            retval = parse_odp_key_mask_attr(s, port_names, key, mask);
             if (retval < 0) {
                 return retval;
             }
@@ -1484,6 +2254,9 @@ parse_odp_key_attr(const char *s, const struct simap *port_names,
         s++;
 
         nl_msg_end_nested(key, encap);
+        if (mask) {
+            nl_msg_end_nested(mask, encap_mask);
+        }
 
         return s - start;
     }
@@ -1506,8 +2279,8 @@ parse_odp_key_attr(const char *s, const struct simap *port_names,
  * valid, but they may not be valid as a sequence.  'key' might, for example,
  * have duplicated keys.  odp_flow_key_to_flow() will detect those errors. */
 int
-odp_flow_key_from_string(const char *s, const struct simap *port_names,
-                         struct ofpbuf *key)
+odp_flow_from_string(const char *s, const struct simap *port_names,
+                     struct ofpbuf *key, struct ofpbuf *mask)
 {
     const size_t old_size = key->size;
     for (;;) {
@@ -1518,7 +2291,7 @@ odp_flow_key_from_string(const char *s, const struct simap *port_names,
             return 0;
         }
 
-        retval = parse_odp_key_attr(s, port_names, key);
+        retval = parse_odp_key_mask_attr(s, port_names, key, mask);
         if (retval < 0) {
             key->size = old_size;
             return -retval;
@@ -1537,45 +2310,48 @@ ovs_to_odp_frag(uint8_t nw_frag)
           : OVS_FRAG_TYPE_LATER);
 }
 
-/* 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,
-                       uint32_t odp_in_port)
+static void
+odp_flow_key_from_flow__(struct ofpbuf *buf, const struct flow *data,
+                         const struct flow *flow, odp_port_t odp_in_port)
 {
+    bool is_mask;
     struct ovs_key_ethernet *eth_key;
     size_t encap;
 
+    /* We assume that if 'data' and 'flow' are not the same, we should
+     * treat 'data' as a mask. */
+    is_mask = (data != flow);
+
     if (flow->skb_priority) {
-        nl_msg_put_u32(buf, OVS_KEY_ATTR_PRIORITY, flow->skb_priority);
+        nl_msg_put_u32(buf, OVS_KEY_ATTR_PRIORITY, data->skb_priority);
     }
 
     if (flow->tunnel.ip_dst) {
-        tun_key_to_attr(buf, &flow->tunnel);
+        tun_key_to_attr(buf, &data->tunnel);
     }
 
     if (flow->skb_mark) {
-        nl_msg_put_u32(buf, OVS_KEY_ATTR_SKB_MARK, flow->skb_mark);
+        nl_msg_put_u32(buf, OVS_KEY_ATTR_SKB_MARK, data->skb_mark);
     }
 
-    if (odp_in_port != OVSP_NONE) {
-        nl_msg_put_u32(buf, OVS_KEY_ATTR_IN_PORT, odp_in_port);
+    /* Add an ingress port attribute if this is a mask or 'odp_in_port'
+     * is not the magical value "ODPP_NONE". */
+    if (is_mask || odp_in_port != ODPP_NONE) {
+        nl_msg_put_odp_port(buf, OVS_KEY_ATTR_IN_PORT, odp_in_port);
     }
 
     eth_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_ETHERNET,
                                        sizeof *eth_key);
-    memcpy(eth_key->eth_src, flow->dl_src, ETH_ADDR_LEN);
-    memcpy(eth_key->eth_dst, flow->dl_dst, ETH_ADDR_LEN);
+    memcpy(eth_key->eth_src, data->dl_src, ETH_ADDR_LEN);
+    memcpy(eth_key->eth_dst, data->dl_dst, ETH_ADDR_LEN);
 
     if (flow->vlan_tci != htons(0) || flow->dl_type == htons(ETH_TYPE_VLAN)) {
-        nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, htons(ETH_TYPE_VLAN));
-        nl_msg_put_be16(buf, OVS_KEY_ATTR_VLAN, flow->vlan_tci);
+        if (is_mask) {
+            nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, htons(UINT16_MAX));
+        } else {
+            nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, htons(ETH_TYPE_VLAN));
+        }
+        nl_msg_put_be16(buf, OVS_KEY_ATTR_VLAN, data->vlan_tci);
         encap = nl_msg_start_nested(buf, OVS_KEY_ATTR_ENCAP);
         if (flow->vlan_tci == htons(0)) {
             goto unencap;
@@ -1585,33 +2361,47 @@ odp_flow_key_from_flow(struct ofpbuf *buf, const struct flow *flow,
     }
 
     if (ntohs(flow->dl_type) < ETH_TYPE_MIN) {
+        /* For backwards compatibility with kernels that don't support
+         * wildcarding, the following convention is used to encode the
+         * OVS_KEY_ATTR_ETHERTYPE for key and mask:
+         *
+         *   key      mask    matches
+         * -------- --------  -------
+         *  >0x5ff   0xffff   Specified Ethernet II Ethertype.
+         *  >0x5ff      0     Any Ethernet II or non-Ethernet II frame.
+         *  <none>   0xffff   Any non-Ethernet II frame (except valid
+         *                    802.3 SNAP packet with valid eth_type).
+         */
+        if (is_mask) {
+            nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, data->dl_type);
+        }
         goto unencap;
     }
 
-    nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, flow->dl_type);
+    nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, data->dl_type);
 
     if (flow->dl_type == htons(ETH_TYPE_IP)) {
         struct ovs_key_ipv4 *ipv4_key;
 
         ipv4_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_IPV4,
                                             sizeof *ipv4_key);
-        ipv4_key->ipv4_src = flow->nw_src;
-        ipv4_key->ipv4_dst = flow->nw_dst;
-        ipv4_key->ipv4_proto = flow->nw_proto;
-        ipv4_key->ipv4_tos = flow->nw_tos;
-        ipv4_key->ipv4_ttl = flow->nw_ttl;
-        ipv4_key->ipv4_frag = ovs_to_odp_frag(flow->nw_frag);
+        ipv4_key->ipv4_src = data->nw_src;
+        ipv4_key->ipv4_dst = data->nw_dst;
+        ipv4_key->ipv4_proto = data->nw_proto;
+        ipv4_key->ipv4_tos = data->nw_tos;
+        ipv4_key->ipv4_ttl = data->nw_ttl;
+        ipv4_key->ipv4_frag = ovs_to_odp_frag(data->nw_frag);
     } else if (flow->dl_type == htons(ETH_TYPE_IPV6)) {
         struct ovs_key_ipv6 *ipv6_key;
 
         ipv6_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_IPV6,
                                             sizeof *ipv6_key);
-        memcpy(ipv6_key->ipv6_src, &flow->ipv6_src, sizeof ipv6_key->ipv6_src);
-        memcpy(ipv6_key->ipv6_dst, &flow->ipv6_dst, sizeof ipv6_key->ipv6_dst);
-        ipv6_key->ipv6_label = flow->ipv6_label;
-        ipv6_key->ipv6_proto = flow->nw_proto;
-        ipv6_key->ipv6_tclass = flow->nw_tos;
-        ipv6_key->ipv6_hlimit = flow->nw_ttl;
+        memcpy(ipv6_key->ipv6_src, &data->ipv6_src, sizeof ipv6_key->ipv6_src);
+        memcpy(ipv6_key->ipv6_dst, &data->ipv6_dst, sizeof ipv6_key->ipv6_dst);
+        ipv6_key->ipv6_label = data->ipv6_label;
+        ipv6_key->ipv6_proto = data->nw_proto;
+        ipv6_key->ipv6_tclass = data->nw_tos;
+        ipv6_key->ipv6_hlimit = data->nw_ttl;
         ipv6_key->ipv6_frag = ovs_to_odp_frag(flow->nw_frag);
     } else if (flow->dl_type == htons(ETH_TYPE_ARP) ||
                flow->dl_type == htons(ETH_TYPE_RARP)) {
@@ -1620,11 +2410,11 @@ odp_flow_key_from_flow(struct ofpbuf *buf, const struct flow *flow,
         arp_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_ARP,
                                            sizeof *arp_key);
         memset(arp_key, 0, sizeof *arp_key);
-        arp_key->arp_sip = flow->nw_src;
-        arp_key->arp_tip = flow->nw_dst;
-        arp_key->arp_op = htons(flow->nw_proto);
-        memcpy(arp_key->arp_sha, flow->arp_sha, ETH_ADDR_LEN);
-        memcpy(arp_key->arp_tha, flow->arp_tha, ETH_ADDR_LEN);
+        arp_key->arp_sip = data->nw_src;
+        arp_key->arp_tip = data->nw_dst;
+        arp_key->arp_op = htons(data->nw_proto);
+        memcpy(arp_key->arp_sha, data->arp_sha, ETH_ADDR_LEN);
+        memcpy(arp_key->arp_tha, data->arp_tha, ETH_ADDR_LEN);
     }
 
     if (flow->mpls_depth) {
@@ -1632,7 +2422,7 @@ odp_flow_key_from_flow(struct ofpbuf *buf, const struct flow *flow,
 
         mpls_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_MPLS,
                                             sizeof *mpls_key);
-        mpls_key->mpls_lse = flow->mpls_lse;
+        mpls_key->mpls_lse = data->mpls_lse;
     }
 
     if (is_ip_any(flow) && !(flow->nw_frag & FLOW_NW_FRAG_LATER)) {
@@ -1641,31 +2431,31 @@ odp_flow_key_from_flow(struct ofpbuf *buf, const struct flow *flow,
 
             tcp_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_TCP,
                                                sizeof *tcp_key);
-            tcp_key->tcp_src = flow->tp_src;
-            tcp_key->tcp_dst = flow->tp_dst;
+            tcp_key->tcp_src = data->tp_src;
+            tcp_key->tcp_dst = data->tp_dst;
         } else if (flow->nw_proto == IPPROTO_UDP) {
             struct ovs_key_udp *udp_key;
 
             udp_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_UDP,
                                                sizeof *udp_key);
-            udp_key->udp_src = flow->tp_src;
-            udp_key->udp_dst = flow->tp_dst;
+            udp_key->udp_src = data->tp_src;
+            udp_key->udp_dst = data->tp_dst;
         } else if (flow->dl_type == htons(ETH_TYPE_IP)
                 && flow->nw_proto == IPPROTO_ICMP) {
             struct ovs_key_icmp *icmp_key;
 
             icmp_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_ICMP,
                                                 sizeof *icmp_key);
-            icmp_key->icmp_type = ntohs(flow->tp_src);
-            icmp_key->icmp_code = ntohs(flow->tp_dst);
+            icmp_key->icmp_type = ntohs(data->tp_src);
+            icmp_key->icmp_code = ntohs(data->tp_dst);
         } else if (flow->dl_type == htons(ETH_TYPE_IPV6)
                 && flow->nw_proto == IPPROTO_ICMPV6) {
             struct ovs_key_icmpv6 *icmpv6_key;
 
             icmpv6_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_ICMPV6,
                                                   sizeof *icmpv6_key);
-            icmpv6_key->icmpv6_type = ntohs(flow->tp_src);
-            icmpv6_key->icmpv6_code = ntohs(flow->tp_dst);
+            icmpv6_key->icmpv6_type = ntohs(data->tp_src);
+            icmpv6_key->icmpv6_code = ntohs(data->tp_dst);
 
             if (icmpv6_key->icmpv6_type == ND_NEIGHBOR_SOLICIT
                     || icmpv6_key->icmpv6_type == ND_NEIGHBOR_ADVERT) {
@@ -1673,10 +2463,10 @@ odp_flow_key_from_flow(struct ofpbuf *buf, const struct flow *flow,
 
                 nd_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_ND,
                                                     sizeof *nd_key);
-                memcpy(nd_key->nd_target, &flow->nd_target,
+                memcpy(nd_key->nd_target, &data->nd_target,
                         sizeof nd_key->nd_target);
-                memcpy(nd_key->nd_sll, flow->arp_sha, ETH_ADDR_LEN);
-                memcpy(nd_key->nd_tll, flow->arp_tha, ETH_ADDR_LEN);
+                memcpy(nd_key->nd_sll, data->arp_sha, ETH_ADDR_LEN);
+                memcpy(nd_key->nd_tll, data->arp_tha, ETH_ADDR_LEN);
             }
         }
     }
@@ -1687,6 +2477,36 @@ unencap:
     }
 }
 
+/* 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 ODPP_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_port_t odp_in_port)
+{
+    odp_flow_key_from_flow__(buf, flow, flow, odp_in_port);
+}
+
+/* Appends a representation of 'mask' as OVS_KEY_ATTR_* attributes to
+ * 'buf'.  'flow' is used as a template to determine how to interpret
+ * 'mask'.  For example, the 'dl_type' of 'mask' describes the mask, but
+ * it doesn't indicate whether the other fields should be interpreted as
+ * ARP, IPv4, IPv6, etc.
+ *
+ * '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_mask(struct ofpbuf *buf, const struct flow *mask,
+                       const struct flow *flow, uint32_t odp_in_port_mask)
+{
+    odp_flow_key_from_flow__(buf, mask, flow, u32_to_odp(odp_in_port_mask));
+}
+
 uint32_t
 odp_flow_key_hash(const struct nlattr *key, size_t key_len)
 {
@@ -1709,7 +2529,10 @@ log_odp_key_attributes(struct vlog_rate_limit *rl, const char *title,
     ds_init(&s);
     for (i = 0; i < 64; i++) {
         if (attrs & (UINT64_C(1) << i)) {
-            ds_put_format(&s, " %s", ovs_key_attr_to_string(i));
+            char namebuf[OVS_KEY_ATTR_BUFSIZE];
+
+            ds_put_format(&s, " %s",
+                          ovs_key_attr_to_string(i, namebuf, sizeof namebuf));
         }
     }
     if (out_of_range_attr) {
@@ -1761,8 +2584,11 @@ parse_flow_nlattrs(const struct nlattr *key, size_t key_len,
         int expected_len = odp_flow_key_attr_len(type);
 
         if (len != expected_len && expected_len >= 0) {
+            char namebuf[OVS_KEY_ATTR_BUFSIZE];
+
             VLOG_ERR_RL(&rl, "attribute %s has length %zu but should have "
-                        "length %d", ovs_key_attr_to_string(type),
+                        "length %d", ovs_key_attr_to_string(type, namebuf,
+                                                            sizeof namebuf),
                         len, expected_len);
             return false;
         }
@@ -1771,8 +2597,11 @@ parse_flow_nlattrs(const struct nlattr *key, size_t key_len,
             *out_of_range_attrp = type;
         } else {
             if (present_attrs & (UINT64_C(1) << type)) {
+                char namebuf[OVS_KEY_ATTR_BUFSIZE];
+
                 VLOG_ERR_RL(&rl, "duplicate %s attribute in flow key",
-                            ovs_key_attr_to_string(type));
+                            ovs_key_attr_to_string(type,
+                                                   namebuf, sizeof namebuf));
                 return false;
             }
 
@@ -1986,7 +2815,7 @@ parse_8021q_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
     enum odp_key_fitness fitness;
     ovs_be16 tci;
 
-    /* Calulate fitness of outer attributes. */
+    /* Calculate fitness of outer attributes. */
     expected_attrs |= ((UINT64_C(1) << OVS_KEY_ATTR_VLAN) |
                        (UINT64_C(1) << OVS_KEY_ATTR_ENCAP));
     fitness = check_expectations(present_attrs, out_of_range_attr,
@@ -2078,7 +2907,7 @@ odp_flow_key_to_flow(const struct nlattr *key, size_t key_len,
     if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_TUNNEL)) {
         enum odp_key_fitness res;
 
-        res = tun_key_from_attr(attrs[OVS_KEY_ATTR_TUNNEL], &flow->tunnel);
+        res = odp_tun_key_from_attr(attrs[OVS_KEY_ATTR_TUNNEL], &flow->tunnel);
         if (res == ODP_FIT_ERROR) {
             return ODP_FIT_ERROR;
         } else if (res == ODP_FIT_PERFECT) {
@@ -2087,10 +2916,11 @@ odp_flow_key_to_flow(const struct nlattr *key, size_t key_len,
     }
 
     if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_IN_PORT)) {
-        flow->in_port = nl_attr_get_u32(attrs[OVS_KEY_ATTR_IN_PORT]);
+        flow->in_port.odp_port
+            = nl_attr_get_odp_port(attrs[OVS_KEY_ATTR_IN_PORT]);
         expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_IN_PORT;
     } else {
-        flow->in_port = OVSP_NONE;
+        flow->in_port.odp_port = ODPP_NONE;
     }
 
     /* Ethernet header. */
@@ -2211,7 +3041,8 @@ commit_odp_tunnel_action(const struct flow *flow, struct flow *base,
 
 static void
 commit_set_ether_addr_action(const struct flow *flow, struct flow *base,
-                             struct ofpbuf *odp_actions)
+                             struct ofpbuf *odp_actions,
+                             struct flow_wildcards *wc)
 {
     struct ovs_key_ethernet eth_key;
 
@@ -2220,6 +3051,9 @@ commit_set_ether_addr_action(const struct flow *flow, struct flow *base,
         return;
     }
 
+    memset(&wc->masks.dl_src, 0xff, sizeof wc->masks.dl_src);
+    memset(&wc->masks.dl_dst, 0xff, sizeof wc->masks.dl_dst);
+
     memcpy(base->dl_src, flow->dl_src, ETH_ADDR_LEN);
     memcpy(base->dl_dst, flow->dl_dst, ETH_ADDR_LEN);
 
@@ -2232,12 +3066,14 @@ commit_set_ether_addr_action(const struct flow *flow, struct flow *base,
 
 static void
 commit_vlan_action(const struct flow *flow, struct flow *base,
-                   struct ofpbuf *odp_actions)
+                   struct ofpbuf *odp_actions, struct flow_wildcards *wc)
 {
     if (base->vlan_tci == flow->vlan_tci) {
         return;
     }
 
+    memset(&wc->masks.vlan_tci, 0xff, sizeof wc->masks.vlan_tci);
+
     if (base->vlan_tci & htons(VLAN_CFI)) {
         nl_msg_put_flag(odp_actions, OVS_ACTION_ATTR_POP_VLAN);
     }
@@ -2255,13 +3091,15 @@ commit_vlan_action(const struct flow *flow, struct flow *base,
 
 static void
 commit_mpls_action(const struct flow *flow, struct flow *base,
-                   struct ofpbuf *odp_actions)
+                   struct ofpbuf *odp_actions, struct flow_wildcards *wc)
 {
     if (flow->mpls_lse == base->mpls_lse &&
         flow->mpls_depth == base->mpls_depth) {
         return;
     }
 
+    memset(&wc->masks.mpls_lse, 0xff, sizeof wc->masks.mpls_lse);
+
     if (flow->mpls_depth < base->mpls_depth) {
         if (base->mpls_depth - flow->mpls_depth > 1) {
             static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(10, 10);
@@ -2299,7 +3137,7 @@ commit_mpls_action(const struct flow *flow, struct flow *base,
 
 static void
 commit_set_ipv4_action(const struct flow *flow, struct flow *base,
-                     struct ofpbuf *odp_actions)
+                     struct ofpbuf *odp_actions, struct flow_wildcards *wc)
 {
     struct ovs_key_ipv4 ipv4_key;
 
@@ -2311,6 +3149,13 @@ commit_set_ipv4_action(const struct flow *flow, struct flow *base,
         return;
     }
 
+    memset(&wc->masks.nw_src, 0xff, sizeof wc->masks.nw_src);
+    memset(&wc->masks.nw_dst, 0xff, sizeof wc->masks.nw_dst);
+    memset(&wc->masks.nw_tos, 0xff, sizeof wc->masks.nw_tos);
+    memset(&wc->masks.nw_ttl, 0xff, sizeof wc->masks.nw_ttl);
+    memset(&wc->masks.nw_proto, 0xff, sizeof wc->masks.nw_proto);
+    memset(&wc->masks.nw_frag, 0xff, sizeof wc->masks.nw_frag);
+
     ipv4_key.ipv4_src = base->nw_src = flow->nw_src;
     ipv4_key.ipv4_dst = base->nw_dst = flow->nw_dst;
     ipv4_key.ipv4_tos = base->nw_tos = flow->nw_tos;
@@ -2324,7 +3169,7 @@ commit_set_ipv4_action(const struct flow *flow, struct flow *base,
 
 static void
 commit_set_ipv6_action(const struct flow *flow, struct flow *base,
-                       struct ofpbuf *odp_actions)
+                       struct ofpbuf *odp_actions, struct flow_wildcards *wc)
 {
     struct ovs_key_ipv6 ipv6_key;
 
@@ -2337,6 +3182,14 @@ commit_set_ipv6_action(const struct flow *flow, struct flow *base,
         return;
     }
 
+    memset(&wc->masks.ipv6_src, 0xff, sizeof wc->masks.ipv6_src);
+    memset(&wc->masks.ipv6_dst, 0xff, sizeof wc->masks.ipv6_dst);
+    memset(&wc->masks.ipv6_label, 0xff, sizeof wc->masks.ipv6_label);
+    memset(&wc->masks.nw_tos, 0xff, sizeof wc->masks.nw_tos);
+    memset(&wc->masks.nw_ttl, 0xff, sizeof wc->masks.nw_ttl);
+    memset(&wc->masks.nw_proto, 0xff, sizeof wc->masks.nw_proto);
+    memset(&wc->masks.nw_frag, 0xff, sizeof wc->masks.nw_frag);
+
     base->ipv6_src = flow->ipv6_src;
     memcpy(&ipv6_key.ipv6_src, &base->ipv6_src, sizeof(ipv6_key.ipv6_src));
     base->ipv6_dst = flow->ipv6_dst;
@@ -2354,7 +3207,7 @@ commit_set_ipv6_action(const struct flow *flow, struct flow *base,
 
 static void
 commit_set_nw_action(const struct flow *flow, struct flow *base,
-                     struct ofpbuf *odp_actions)
+                     struct ofpbuf *odp_actions, struct flow_wildcards *wc)
 {
     /* Check if flow really have an IP header. */
     if (!flow->nw_proto) {
@@ -2362,15 +3215,15 @@ commit_set_nw_action(const struct flow *flow, struct flow *base,
     }
 
     if (base->dl_type == htons(ETH_TYPE_IP)) {
-        commit_set_ipv4_action(flow, base, odp_actions);
+        commit_set_ipv4_action(flow, base, odp_actions, wc);
     } else if (base->dl_type == htons(ETH_TYPE_IPV6)) {
-        commit_set_ipv6_action(flow, base, odp_actions);
+        commit_set_ipv6_action(flow, base, odp_actions, wc);
     }
 }
 
 static void
 commit_set_port_action(const struct flow *flow, struct flow *base,
-                       struct ofpbuf *odp_actions)
+                       struct ofpbuf *odp_actions, struct flow_wildcards *wc)
 {
     if (!is_ip_any(base) || (!base->tp_src && !base->tp_dst)) {
         return;
@@ -2381,6 +3234,9 @@ commit_set_port_action(const struct flow *flow, struct flow *base,
         return;
     }
 
+    memset(&wc->masks.tp_src, 0xff, sizeof wc->masks.tp_src);
+    memset(&wc->masks.tp_dst, 0xff, sizeof wc->masks.tp_dst);
+
     if (flow->nw_proto == IPPROTO_TCP) {
         struct ovs_key_tcp port_key;
 
@@ -2403,11 +3259,14 @@ commit_set_port_action(const struct flow *flow, struct flow *base,
 
 static void
 commit_set_priority_action(const struct flow *flow, struct flow *base,
-                           struct ofpbuf *odp_actions)
+                           struct ofpbuf *odp_actions,
+                           struct flow_wildcards *wc)
 {
     if (base->skb_priority == flow->skb_priority) {
         return;
     }
+
+    memset(&wc->masks.skb_priority, 0xff, sizeof wc->masks.skb_priority);
     base->skb_priority = flow->skb_priority;
 
     commit_set_action(odp_actions, OVS_KEY_ATTR_PRIORITY,
@@ -2416,11 +3275,14 @@ commit_set_priority_action(const struct flow *flow, struct flow *base,
 
 static void
 commit_set_skb_mark_action(const struct flow *flow, struct flow *base,
-                           struct ofpbuf *odp_actions)
+                           struct ofpbuf *odp_actions,
+                           struct flow_wildcards *wc)
 {
     if (base->skb_mark == flow->skb_mark) {
         return;
     }
+
+    memset(&wc->masks.skb_mark, 0xff, sizeof wc->masks.skb_mark);
     base->skb_mark = flow->skb_mark;
 
     odp_put_skb_mark_action(base->skb_mark, odp_actions);
@@ -2429,20 +3291,21 @@ commit_set_skb_mark_action(const struct flow *flow, struct flow *base,
  * '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.  Does not
  * commit set_tunnel actions.  Users should call commit_odp_tunnel_action()
- * in addition to this function if needed. */
+ * in addition to this function if needed.  Sets fields in 'wc' that are
+ * used as part of the action. */
 void
 commit_odp_actions(const struct flow *flow, struct flow *base,
-                   struct ofpbuf *odp_actions)
+                   struct ofpbuf *odp_actions, struct flow_wildcards *wc)
 {
-    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);
-    /* Commiting MPLS actions should occur after committing nw and port
+    commit_set_ether_addr_action(flow, base, odp_actions, wc);
+    commit_vlan_action(flow, base, odp_actions, wc);
+    commit_set_nw_action(flow, base, odp_actions, wc);
+    commit_set_port_action(flow, base, odp_actions, wc);
+    /* Committing MPLS actions should occur after committing nw and port
      * actions. This is because committing MPLS actions may alter a packet so
      * that it is no longer IP and thus nw and port actions are no longer valid.
      */
-    commit_mpls_action(flow, base, odp_actions);
-    commit_set_priority_action(flow, base, odp_actions);
-    commit_set_skb_mark_action(flow, base, odp_actions);
+    commit_mpls_action(flow, base, odp_actions, wc);
+    commit_set_priority_action(flow, base, odp_actions, wc);
+    commit_set_skb_mark_action(flow, base, odp_actions, wc);
 }
index a981d17..0c10cfa 100644 (file)
 struct ds;
 struct flow;
 struct flow_tnl;
+struct flow_wildcards;
 struct nlattr;
 struct ofpbuf;
 struct simap;
 
-#define OVSP_NONE UINT32_MAX
+#define ODPP_LOCAL ODP_PORT_C(OVSP_LOCAL)
+#define ODPP_NONE  ODP_PORT_C(UINT32_MAX)
 
 void format_odp_actions(struct ds *, const struct nlattr *odp_actions,
                         size_t actions_len);
@@ -87,12 +89,21 @@ struct odputil_keybuf {
     uint32_t keybuf[DIV_ROUND_UP(ODPUTIL_FLOW_KEY_BYTES, 4)];
 };
 
+enum odp_key_fitness odp_tun_key_from_attr(const struct nlattr *,
+                                           struct flow_tnl *);
+
+void odp_flow_format(const struct nlattr *key, size_t key_len,
+                     const struct nlattr *mask, size_t mask_len,
+                     struct ds *);
 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 *);
+int odp_flow_from_string(const char *s,
+                         const struct simap *port_names,
+                         struct ofpbuf *, struct ofpbuf *);
 
 void odp_flow_key_from_flow(struct ofpbuf *, const struct flow *,
-                            uint32_t odp_in_port);
+                            odp_port_t odp_in_port);
+void odp_flow_key_from_mask(struct ofpbuf *, const struct flow *mask,
+                            const struct flow *flow, uint32_t odp_in_port);
 
 uint32_t odp_flow_key_hash(const struct nlattr *, size_t);
 
@@ -116,7 +127,8 @@ const char *odp_key_fitness_to_string(enum odp_key_fitness);
 void commit_odp_tunnel_action(const struct flow *, struct flow *base,
                               struct ofpbuf *odp_actions);
 void commit_odp_actions(const struct flow *, struct flow *base,
-                        struct ofpbuf *odp_actions);
+                        struct ofpbuf *odp_actions,
+                        struct flow_wildcards *wc);
 \f
 /* ofproto-dpif interface.
  *
@@ -174,20 +186,12 @@ void odp_put_skb_mark_action(const uint32_t skb_mark,
 
 /* Reasons why a subfacet might not be fast-pathable. */
 enum slow_path_reason {
-    /* These reasons are mutually exclusive. */
-    SLOW_CFM = 1 << 0,          /* CFM packets need per-packet processing. */
-    SLOW_LACP = 1 << 1,         /* LACP packets need per-packet processing. */
-    SLOW_STP = 1 << 2,          /* STP packets need per-packet processing. */
-    SLOW_IN_BAND = 1 << 3,      /* In-band control needs every packet. */
-    SLOW_BFD = 1 << 4,          /* BFD packets need per-packet processing. */
-
-    /* Mutually exclusive with SLOW_BFD, SLOW_CFM, SLOW_LACP, SLOW_STP.
-     * Could possibly appear with SLOW_IN_BAND. */
-    SLOW_CONTROLLER = 1 << 5,   /* Packets must go to OpenFlow controller. */
-
-    /* This can appear on its own, or, theoretically at least, along with any
-     * other combination of reasons. */
-    SLOW_MATCH = 1 << 6,        /* Datapath can't match specifically enough. */
+    SLOW_CFM = 1,               /* CFM packets need per-packet processing. */
+    SLOW_LACP,                  /* LACP packets need per-packet processing. */
+    SLOW_STP,                   /* STP packets need per-packet processing. */
+    SLOW_BFD,                   /* BFD packets need per-packet processing. */
+    SLOW_CONTROLLER,            /* Packets must go to OpenFlow controller. */
+    __SLOW_MAX
 };
 
 #endif /* odp-util.h */
index 026a376..899928a 100644 (file)
@@ -42,7 +42,7 @@ output_from_openflow10(const struct ofp10_action_output *oao,
     struct ofpact_output *output;
 
     output = ofpact_put_OUTPUT(out);
-    output->port = ntohs(oao->port);
+    output->port = u16_to_ofp(ntohs(oao->port));
     output->max_len = ntohs(oao->max_len);
 
     return ofputil_check_output_port(output->port, OFPP_MAX);
@@ -55,9 +55,10 @@ enqueue_from_openflow10(const struct ofp10_action_enqueue *oae,
     struct ofpact_enqueue *enqueue;
 
     enqueue = ofpact_put_ENQUEUE(out);
-    enqueue->port = ntohs(oae->port);
+    enqueue->port = u16_to_ofp(ntohs(oae->port));
     enqueue->queue = ntohl(oae->queue_id);
-    if (enqueue->port >= OFPP_MAX && enqueue->port != OFPP_IN_PORT
+    if (ofp_to_u16(enqueue->port) >= ofp_to_u16(OFPP_MAX)
+        && enqueue->port != OFPP_IN_PORT
         && enqueue->port != OFPP_LOCAL) {
         return OFPERR_OFPBAC_BAD_OUT_PORT;
     }
@@ -72,7 +73,7 @@ resubmit_from_openflow(const struct nx_action_resubmit *nar,
 
     resubmit = ofpact_put_RESUBMIT(out);
     resubmit->ofpact.compat = OFPUTIL_NXAST_RESUBMIT;
-    resubmit->in_port = ntohs(nar->in_port);
+    resubmit->in_port = u16_to_ofp(ntohs(nar->in_port));
     resubmit->table_id = 0xff;
 }
 
@@ -88,7 +89,7 @@ resubmit_table_from_openflow(const struct nx_action_resubmit *nar,
 
     resubmit = ofpact_put_RESUBMIT(out);
     resubmit->ofpact.compat = OFPUTIL_NXAST_RESUBMIT_TABLE;
-    resubmit->in_port = ntohs(nar->in_port);
+    resubmit->in_port = u16_to_ofp(ntohs(nar->in_port));
     resubmit->table_id = nar->table;
     return 0;
 }
@@ -209,9 +210,9 @@ dec_ttl_cnt_ids_from_openflow(const struct nx_action_cnt_ids *nac_ids,
     for (i = 0; i < ids->n_controllers; i++) {
         uint16_t id = ntohs(((ovs_be16 *)(nac_ids + 1))[i]);
         ofpbuf_put(out, &id, sizeof id);
+        ids = out->l2;
     }
 
-    ids = out->l2;
     ofpact_update_len(out, &ids->ofpact);
 
     return 0;
@@ -913,13 +914,13 @@ OVS_INSTRUCTIONS
 };
 
 const char *
-ofpact_instruction_name_from_type(enum ovs_instruction_type type)
+ovs_instruction_name_from_type(enum ovs_instruction_type type)
 {
     return inst_info[type].name;
 }
 
 int
-ofpact_instruction_type_from_name(const char *name)
+ovs_instruction_type_from_name(const char *name)
 {
     const struct instruction_type_info *p;
     for (p = inst_info; p < &inst_info[ARRAY_SIZE(inst_info)]; p++) {
@@ -930,6 +931,58 @@ ofpact_instruction_type_from_name(const char *name)
     return -1;
 }
 
+enum ovs_instruction_type
+ovs_instruction_type_from_ofpact_type(enum ofpact_type type)
+{
+    switch (type) {
+    case OFPACT_METER:
+        return OVSINST_OFPIT13_METER;
+    case OFPACT_CLEAR_ACTIONS:
+        return OVSINST_OFPIT11_CLEAR_ACTIONS;
+    case OFPACT_WRITE_METADATA:
+        return OVSINST_OFPIT11_WRITE_METADATA;
+    case OFPACT_GOTO_TABLE:
+        return OVSINST_OFPIT11_GOTO_TABLE;
+    case OFPACT_OUTPUT:
+    case OFPACT_CONTROLLER:
+    case OFPACT_ENQUEUE:
+    case OFPACT_OUTPUT_REG:
+    case OFPACT_BUNDLE:
+    case OFPACT_SET_VLAN_VID:
+    case OFPACT_SET_VLAN_PCP:
+    case OFPACT_STRIP_VLAN:
+    case OFPACT_PUSH_VLAN:
+    case OFPACT_SET_ETH_SRC:
+    case OFPACT_SET_ETH_DST:
+    case OFPACT_SET_IPV4_SRC:
+    case OFPACT_SET_IPV4_DST:
+    case OFPACT_SET_IPV4_DSCP:
+    case OFPACT_SET_L4_SRC_PORT:
+    case OFPACT_SET_L4_DST_PORT:
+    case OFPACT_REG_MOVE:
+    case OFPACT_REG_LOAD:
+    case OFPACT_STACK_PUSH:
+    case OFPACT_STACK_POP:
+    case OFPACT_DEC_TTL:
+    case OFPACT_SET_MPLS_TTL:
+    case OFPACT_DEC_MPLS_TTL:
+    case OFPACT_PUSH_MPLS:
+    case OFPACT_POP_MPLS:
+    case OFPACT_SET_TUNNEL:
+    case OFPACT_SET_QUEUE:
+    case OFPACT_POP_QUEUE:
+    case OFPACT_FIN_TIMEOUT:
+    case OFPACT_RESUBMIT:
+    case OFPACT_LEARN:
+    case OFPACT_MULTIPATH:
+    case OFPACT_NOTE:
+    case OFPACT_EXIT:
+    case OFPACT_SAMPLE:
+    default:
+        return OVSINST_OFPIT11_APPLY_ACTIONS;
+    }
+}
+
 static inline struct ofp11_instruction *
 instruction_next(const struct ofp11_instruction *inst)
 {
@@ -1002,9 +1055,7 @@ decode_openflow11_instructions(const struct ofp11_instruction insts[],
         }
 
         if (out[type]) {
-            return OFPERR_OFPBAC_UNSUPPORTED_ORDER; /* No specific code for
-                                                     * a duplicate instruction
-                                                     * exist */
+            return OFPERR_ONFBIC_DUP_INSTRUCTION;
         }
         out[type] = inst;
     }
@@ -1085,6 +1136,16 @@ ofpacts_pull_openflow11_instructions(struct ofpbuf *openflow,
         goto exit;
     }
 
+    if (insts[OVSINST_OFPIT13_METER]) {
+        const struct ofp13_instruction_meter *oim;
+        struct ofpact_meter *om;
+
+        oim = (const struct ofp13_instruction_meter *)
+            insts[OVSINST_OFPIT13_METER];
+
+        om = ofpact_put_METER(ofpacts);
+        om->meter_id = ntohl(oim->meter_id);
+    }
     if (insts[OVSINST_OFPIT11_APPLY_ACTIONS]) {
         const union ofp_action *actions;
         size_t n_actions;
@@ -1136,9 +1197,10 @@ exit:
     return error;
 }
 \f
+/* May modify flow->dl_type, caller must restore it. */
 static enum ofperr
-ofpact_check__(const struct ofpact *a, const struct flow *flow, int max_ports,
-               ovs_be16 *dl_type)
+ofpact_check__(const struct ofpact *a, struct flow *flow, ofp_port_t max_ports,
+               uint8_t table_id)
 {
     const struct ofpact_enqueue *enqueue;
 
@@ -1152,7 +1214,8 @@ ofpact_check__(const struct ofpact *a, const struct flow *flow, int max_ports,
 
     case OFPACT_ENQUEUE:
         enqueue = ofpact_get_ENQUEUE(a);
-        if (enqueue->port >= max_ports && enqueue->port != OFPP_IN_PORT
+        if (ofp_to_u16(enqueue->port) >= ofp_to_u16(max_ports)
+            && enqueue->port != OFPP_IN_PORT
             && enqueue->port != OFPP_LOCAL) {
             return OFPERR_OFPBAC_BAD_OUT_PORT;
         }
@@ -1210,11 +1273,11 @@ ofpact_check__(const struct ofpact *a, const struct flow *flow, int max_ports,
         return 0;
 
     case OFPACT_PUSH_MPLS:
-        *dl_type = ofpact_get_PUSH_MPLS(a)->ethertype;
+        flow->dl_type = ofpact_get_PUSH_MPLS(a)->ethertype;
         return 0;
 
     case OFPACT_POP_MPLS:
-        *dl_type = ofpact_get_POP_MPLS(a)->ethertype;
+        flow->dl_type = ofpact_get_POP_MPLS(a)->ethertype;
         return 0;
 
     case OFPACT_SAMPLE:
@@ -1222,7 +1285,20 @@ ofpact_check__(const struct ofpact *a, const struct flow *flow, int max_ports,
 
     case OFPACT_CLEAR_ACTIONS:
     case OFPACT_WRITE_METADATA:
+        return 0;
+
+    case OFPACT_METER: {
+        uint32_t mid = ofpact_get_METER(a)->meter_id;
+        if (mid == 0 || mid > OFPM13_MAX) {
+            return OFPERR_OFPMMFC_INVALID_METER;
+        }
+        return 0;
+    }
+
     case OFPACT_GOTO_TABLE:
+        if (ofpact_get_GOTO_TABLE(a)->table_id <= table_id) {
+            return OFPERR_OFPBRC_BAD_TABLE_ID;
+        }
         return 0;
 
     default:
@@ -1232,36 +1308,25 @@ ofpact_check__(const struct ofpact *a, const struct flow *flow, int max_ports,
 
 /* Checks that the 'ofpacts_len' bytes of actions in 'ofpacts' are
  * appropriate for a packet with the prerequisites satisfied by 'flow' in a
- * switch with no more than 'max_ports' ports. */
+ * switch with no more than 'max_ports' ports.
+ *
+ * May temporarily modify 'flow', but restores the changes before returning. */
 enum ofperr
 ofpacts_check(const struct ofpact ofpacts[], size_t ofpacts_len,
-              const struct flow *flow, int max_ports)
+              struct flow *flow, ofp_port_t max_ports, uint8_t table_id)
 {
     const struct ofpact *a;
     ovs_be16 dl_type = flow->dl_type;
-    struct flow updated_flow;
+    enum ofperr error = 0;
 
     OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
-        enum ofperr error;
-
-        /* If the dl_type was changed by an action then its new value
-         * should be present in the flow passed to ofpact_check__(). */
-        if (flow->dl_type != dl_type) {
-            /* Only copy flow at most once */
-            if (flow != &updated_flow) {
-                updated_flow = *flow;
-                flow = &updated_flow;
-            }
-            updated_flow.dl_type = dl_type;
-        }
-
-        error = ofpact_check__(a, flow, max_ports, &dl_type);
+        error = ofpact_check__(a, flow, max_ports, table_id);
         if (error) {
-            return error;
+            break;
         }
     }
-
-    return 0;
+    flow->dl_type = dl_type; /* Restore. */
+    return error;
 }
 
 /* Verifies that the 'ofpacts_len' bytes of actions in 'ofpacts' are
@@ -1276,19 +1341,10 @@ ofpacts_verify(const struct ofpact ofpacts[], size_t ofpacts_len)
     OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
         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;
-        }
-
+        next = ovs_instruction_type_from_ofpact_type(a->type);
         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);
+            const char *name = ovs_instruction_name_from_type(inst);
+            const char *next_name = ovs_instruction_name_from_type(next);
 
             if (next == inst) {
                 VLOG_WARN("duplicate %s instruction not allowed, for OpenFlow "
@@ -1334,7 +1390,7 @@ ofpact_resubmit_to_nxast(const struct ofpact_resubmit *resubmit,
         nar = ofputil_put_NXAST_RESUBMIT_TABLE(out);
         nar->table = resubmit->table_id;
     }
-    nar->in_port = htons(resubmit->in_port);
+    nar->in_port = htons(ofp_to_u16(resubmit->in_port));
 }
 
 static void
@@ -1556,6 +1612,7 @@ ofpact_to_nxast(const struct ofpact *a, struct ofpbuf *out)
     case OFPACT_SET_L4_DST_PORT:
     case OFPACT_CLEAR_ACTIONS:
     case OFPACT_GOTO_TABLE:
+    case OFPACT_METER:
         NOT_REACHED();
     }
 }
@@ -1569,7 +1626,7 @@ ofpact_output_to_openflow10(const struct ofpact_output *output,
     struct ofp10_action_output *oao;
 
     oao = ofputil_put_OFPAT10_OUTPUT(out);
-    oao->port = htons(output->port);
+    oao->port = htons(ofp_to_u16(output->port));
     oao->max_len = htons(output->max_len);
 }
 
@@ -1580,7 +1637,7 @@ ofpact_enqueue_to_openflow10(const struct ofpact_enqueue *enqueue,
     struct ofp10_action_enqueue *oae;
 
     oae = ofputil_put_OFPAT10_ENQUEUE(out);
-    oae->port = htons(enqueue->port);
+    oae->port = htons(ofp_to_u16(enqueue->port));
     oae->queue_id = htonl(enqueue->queue);
 }
 
@@ -1648,6 +1705,7 @@ ofpact_to_openflow10(const struct ofpact *a, struct ofpbuf *out)
     case OFPACT_PUSH_VLAN:
     case OFPACT_CLEAR_ACTIONS:
     case OFPACT_GOTO_TABLE:
+    case OFPACT_METER:
         /* XXX */
         break;
 
@@ -1820,6 +1878,7 @@ ofpact_to_openflow11(const struct ofpact *a, struct ofpbuf *out)
 
     case OFPACT_CLEAR_ACTIONS:
     case OFPACT_GOTO_TABLE:
+    case OFPACT_METER:
         NOT_REACHED();
 
     case OFPACT_CONTROLLER:
@@ -1882,17 +1941,20 @@ ofpacts_put_openflow11_instructions(const struct ofpact ofpacts[],
     const struct ofpact *a;
 
     OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
-        /* XXX Write-Actions */
-
-        if (a->type == OFPACT_CLEAR_ACTIONS) {
+        switch (ovs_instruction_type_from_ofpact_type(a->type)) {
+        case OVSINST_OFPIT11_CLEAR_ACTIONS:
             instruction_put_OFPIT11_CLEAR_ACTIONS(openflow);
-        } else if (a->type == OFPACT_GOTO_TABLE) {
-            struct ofp11_instruction_goto_table *oigt;
+            break;
 
+        case OVSINST_OFPIT11_GOTO_TABLE: {
+            struct ofp11_instruction_goto_table *oigt;
             oigt = instruction_put_OFPIT11_GOTO_TABLE(openflow);
             oigt->table_id = ofpact_get_GOTO_TABLE(a)->table_id;
             memset(oigt->pad, 0, sizeof oigt->pad);
-        } else if (a->type == OFPACT_WRITE_METADATA) {
+            break;
+        }
+
+        case OVSINST_OFPIT11_WRITE_METADATA: {
             const struct ofpact_metadata *om;
             struct ofp11_instruction_write_metadata *oiwm;
 
@@ -1900,8 +1962,20 @@ ofpacts_put_openflow11_instructions(const struct ofpact ofpacts[],
             oiwm = instruction_put_OFPIT11_WRITE_METADATA(openflow);
             oiwm->metadata = om->metadata;
             oiwm->metadata_mask = om->mask;
-        } else if (!ofpact_is_instruction(a)) {
-            /* Apply-actions */
+            break;
+        }
+
+        case OVSINST_OFPIT13_METER: {
+            const struct ofpact_meter *om;
+            struct ofp13_instruction_meter *oim;
+
+            om = ofpact_get_METER(a);
+            oim = instruction_put_OFPIT13_METER(openflow);
+            oim->meter_id = htonl(om->meter_id);
+            break;
+        }
+
+        case OVSINST_OFPIT11_APPLY_ACTIONS: {
             const size_t ofs = openflow->size;
             const size_t ofpacts_len_left =
                 (uint8_t*)ofpact_end(ofpacts, ofpacts_len) - (uint8_t*)a;
@@ -1910,7 +1984,8 @@ ofpacts_put_openflow11_instructions(const struct ofpact ofpacts[],
 
             instruction_put_OFPIT11_APPLY_ACTIONS(openflow);
             OFPACT_FOR_EACH(action, a, ofpacts_len_left) {
-                if (ofpact_is_instruction(action)) {
+                if (ovs_instruction_type_from_ofpact_type(action->type)
+                    != OVSINST_OFPIT11_APPLY_ACTIONS) {
                     break;
                 }
                 ofpact_to_openflow11(action, openflow);
@@ -1918,13 +1993,18 @@ ofpacts_put_openflow11_instructions(const struct ofpact ofpacts[],
             }
             ofpacts_update_instruction_actions(openflow, ofs);
             a = processed;
+            break;
+        }
+
+        case OVSINST_OFPIT11_WRITE_ACTIONS:
+            NOT_REACHED();
         }
     }
 }
 \f
 /* Returns true if 'action' outputs to 'port', false otherwise. */
 static bool
-ofpact_outputs_to_port(const struct ofpact *ofpact, uint16_t port)
+ofpact_outputs_to_port(const struct ofpact *ofpact, ofp_port_t port)
 {
     switch (ofpact->type) {
     case OFPACT_OUTPUT:
@@ -1969,6 +2049,7 @@ ofpact_outputs_to_port(const struct ofpact *ofpact, uint16_t port)
     case OFPACT_SAMPLE:
     case OFPACT_CLEAR_ACTIONS:
     case OFPACT_GOTO_TABLE:
+    case OFPACT_METER:
     default:
         return false;
     }
@@ -1978,7 +2059,7 @@ ofpact_outputs_to_port(const struct ofpact *ofpact, uint16_t port)
  * to 'port', false otherwise. */
 bool
 ofpacts_output_to_port(const struct ofpact *ofpacts, size_t ofpacts_len,
-                       uint16_t port)
+                       ofp_port_t port)
 {
     const struct ofpact *a;
 
@@ -2059,12 +2140,12 @@ ofpact_format(const struct ofpact *a, struct ds *s)
     const struct ofpact_metadata *metadata;
     const struct ofpact_tunnel *tunnel;
     const struct ofpact_sample *sample;
-    uint16_t port;
+    ofp_port_t port;
 
     switch (a->type) {
     case OFPACT_OUTPUT:
         port = ofpact_get_OUTPUT(a)->port;
-        if (port < OFPP_MAX) {
+        if (ofp_to_u16(port) < ofp_to_u16(OFPP_MAX)) {
             ds_put_format(s, "output:%"PRIu16, port);
         } else {
             ofputil_format_port(port, s);
@@ -2085,8 +2166,11 @@ ofpact_format(const struct ofpact *a, struct ds *s)
 
             ds_put_cstr(s, "controller(");
             if (reason != OFPR_ACTION) {
+                char reasonbuf[OFPUTIL_PACKET_IN_REASON_BUFSIZE];
+
                 ds_put_format(s, "reason=%s,",
-                              ofputil_packet_in_reason_to_string(reason));
+                              ofputil_packet_in_reason_to_string(
+                                  reason, reasonbuf, sizeof reasonbuf));
             }
             if (controller->max_len != UINT16_MAX) {
                 ds_put_format(s, "max_len=%"PRIu16",", controller->max_len);
@@ -2271,14 +2355,14 @@ ofpact_format(const struct ofpact *a, struct ds *s)
 
     case OFPACT_CLEAR_ACTIONS:
         ds_put_format(s, "%s",
-                      ofpact_instruction_name_from_type(
+                      ovs_instruction_name_from_type(
                           OVSINST_OFPIT11_CLEAR_ACTIONS));
         break;
 
     case OFPACT_WRITE_METADATA:
         metadata = ofpact_get_WRITE_METADATA(a);
         ds_put_format(s, "%s:%#"PRIx64,
-                      ofpact_instruction_name_from_type(
+                      ovs_instruction_name_from_type(
                           OVSINST_OFPIT11_WRITE_METADATA),
                       ntohll(metadata->metadata));
         if (metadata->mask != htonll(UINT64_MAX)) {
@@ -2288,10 +2372,16 @@ ofpact_format(const struct ofpact *a, struct ds *s)
 
     case OFPACT_GOTO_TABLE:
         ds_put_format(s, "%s:%"PRIu8,
-                      ofpact_instruction_name_from_type(
+                      ovs_instruction_name_from_type(
                           OVSINST_OFPIT11_GOTO_TABLE),
                       ofpact_get_GOTO_TABLE(a)->table_id);
         break;
+
+    case OFPACT_METER:
+        ds_put_format(s, "%s:%"PRIu32,
+                      ovs_instruction_name_from_type(OVSINST_OFPIT13_METER),
+                      ofpact_get_METER(a)->meter_id);
+        break;
     }
 }
 
index ffceb05..b97afd0 100644 (file)
     DEFINE_OFPACT(SAMPLE,          ofpact_sample,        ofpact)    \
                                                                     \
     /* Instructions */                                              \
+    DEFINE_OFPACT(METER,           ofpact_meter,         ofpact)    \
     /* XXX Write-Actions */                                         \
-    DEFINE_OFPACT(WRITE_METADATA,  ofpact_metadata,      ofpact)    \
     DEFINE_OFPACT(CLEAR_ACTIONS,   ofpact_null,          ofpact)    \
+    DEFINE_OFPACT(WRITE_METADATA,  ofpact_metadata,      ofpact)    \
     DEFINE_OFPACT(GOTO_TABLE,      ofpact_goto_table,    ofpact)
 
 /* enum ofpact_type, with a member OFPACT_<ENUM> for each action. */
@@ -198,7 +199,7 @@ struct ofpact_null {
  * Used for OFPAT10_OUTPUT. */
 struct ofpact_output {
     struct ofpact ofpact;
-    uint16_t port;              /* Output port. */
+    ofp_port_t port;            /* Output port. */
     uint16_t max_len;           /* Max send len, for port OFPP_CONTROLLER. */
 };
 
@@ -217,7 +218,7 @@ struct ofpact_controller {
  * Used for OFPAT10_ENQUEUE. */
 struct ofpact_enqueue {
     struct ofpact ofpact;
-    uint16_t port;
+    ofp_port_t port;
     uint32_t queue;
 };
 
@@ -247,7 +248,7 @@ struct ofpact_bundle {
 
     /* Slaves for output. */
     unsigned int n_slaves;
-    uint16_t slaves[];
+    ofp_port_t slaves[];
 };
 
 /* OFPACT_SET_VLAN_VID.
@@ -374,25 +375,33 @@ struct ofpact_metadata {
     ovs_be64 mask;
 };
 
+/* OFPACT_METER.
+ *
+ * Used for OFPIT13_METER. */
+struct ofpact_meter {
+    struct ofpact ofpact;
+    uint32_t meter_id;
+};
+
 /* OFPACT_RESUBMIT.
  *
  * Used for NXAST_RESUBMIT, NXAST_RESUBMIT_TABLE. */
 struct ofpact_resubmit {
     struct ofpact ofpact;
-    uint16_t in_port;
+    ofp_port_t in_port;
     uint8_t table_id;
 };
 
 /* Part of struct ofpact_learn, below. */
 struct ofpact_learn_spec {
-    int n_bits;
+    int n_bits;                 /* Number of bits in source and dest. */
 
-    int src_type;
-    struct mf_subfield src;
-    union mf_subvalue src_imm;
+    int src_type;               /* One of NX_LEARN_SRC_*. */
+    struct mf_subfield src;     /* NX_LEARN_SRC_FIELD only. */
+    union mf_subvalue src_imm;  /* NX_LEARN_SRC_IMMEDIATE only. */
 
-    int dst_type;
-    struct mf_subfield dst;
+    int dst_type;             /* One of NX_LEARN_DST_*. */
+    struct mf_subfield dst;   /* NX_LEARN_DST_MATCH, NX_LEARN_DST_LOAD only. */
 };
 
 /* OFPACT_LEARN.
@@ -492,7 +501,8 @@ enum ofperr ofpacts_pull_openflow11_instructions(struct ofpbuf *openflow,
                                                  unsigned int instructions_len,
                                                  struct ofpbuf *ofpacts);
 enum ofperr ofpacts_check(const struct ofpact[], size_t ofpacts_len,
-                          const struct flow *, int max_ports);
+                          struct flow *, ofp_port_t max_ports,
+                          uint8_t table_id);
 enum ofperr ofpacts_verify(const struct ofpact ofpacts[], size_t ofpacts_len);
 
 /* Converting ofpacts to OpenFlow. */
@@ -506,7 +516,7 @@ void ofpacts_put_openflow11_instructions(const struct ofpact[],
 
 /* Working with ofpacts. */
 bool ofpacts_output_to_port(const struct ofpact[], size_t ofpacts_len,
-                            uint16_t port);
+                            ofp_port_t port);
 bool ofpacts_equal(const struct ofpact a[], size_t a_len,
                    const struct ofpact b[], size_t b_len);
 
@@ -600,6 +610,10 @@ void ofpact_pad(struct ofpbuf *);
  * It is enforced on parser from text string.
  */
 #define OVS_INSTRUCTIONS                                    \
+    DEFINE_INST(OFPIT13_METER,                              \
+                ofp13_instruction_meter,          false,    \
+                "meter")                                    \
+                                                            \
     DEFINE_INST(OFPIT11_APPLY_ACTIONS,                      \
                 ofp11_instruction_actions,        true,     \
                 "apply_actions")                            \
@@ -632,18 +646,10 @@ enum {
 #undef DEFINE_INST
 };
 
-
-static inline bool
-ofpact_is_instruction(const struct ofpact *a)
-{
-    /* XXX Write-Actions */
-    return a->type == OFPACT_CLEAR_ACTIONS
-        || a->type == OFPACT_WRITE_METADATA
-        || a->type == OFPACT_GOTO_TABLE;
-}
-
-const char *ofpact_instruction_name_from_type(enum ovs_instruction_type type);
-int ofpact_instruction_type_from_name(const char *name);
+const char *ovs_instruction_name_from_type(enum ovs_instruction_type type);
+int ovs_instruction_type_from_name(const char *name);
+enum ovs_instruction_type ovs_instruction_type_from_ofpact_type(
+    enum ofpact_type);
 
 void ofpact_set_field_init(struct ofpact_reg_load *load,
                            const struct mf_field *mf, const void *src);
index f2a9e8c..30a021b 100644 (file)
@@ -1,3 +1,19 @@
+/*
+ * 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.
+ * 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 "ofp-errors.h"
 #include <errno.h>
@@ -11,7 +27,8 @@
 
 VLOG_DEFINE_THIS_MODULE(ofp_errors);
 
-struct pair {
+struct triplet {
+    uint32_t vendor;
     int type, code;
 };
 
@@ -56,11 +73,12 @@ ofperr_is_valid(enum ofperr error)
 /* 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. */
-enum ofperr
-ofperr_decode(enum ofp_version version, uint16_t type, uint16_t code)
+static enum ofperr
+ofperr_decode(enum ofp_version version,
+              uint32_t vendor, uint16_t type, uint16_t code)
 {
     const struct ofperr_domain *domain = ofperr_domain_from_version(version);
-    return domain ? domain->decode(type, code) : 0;
+    return domain ? domain->decode(vendor, type, code) : 0;
 }
 
 /* Returns the name of 'error', e.g. "OFPBRC_BAD_TYPE" if 'error' is
@@ -105,8 +123,8 @@ ofperr_get_description(enum ofperr error)
             : "<invalid>");
 }
 
-static const struct pair *
-ofperr_get_pair__(enum ofperr error, const struct ofperr_domain *domain)
+static const struct triplet *
+ofperr_get_triplet__(enum ofperr error, const struct ofperr_domain *domain)
 {
     size_t ofs = error - OFPERR_OFS;
 
@@ -120,8 +138,8 @@ ofperr_encode_msg__(enum ofperr error, enum ofp_version ofp_version,
 {
     static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
     const struct ofperr_domain *domain;
+    const struct triplet *triplet;
     struct ofp_error_msg *oem;
-    const struct pair *pair;
     struct ofpbuf *buf;
 
     /* Get the error domain for 'ofp_version', or fall back to OF1.0. */
@@ -136,7 +154,7 @@ ofperr_encode_msg__(enum ofperr error, enum ofp_version ofp_version,
     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, ovs_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",
@@ -144,15 +162,15 @@ ofperr_encode_msg__(enum ofperr error, enum ofp_version ofp_version,
         error = OFPERR_NXBRC_UNENCODABLE_ERROR;
     }
 
-    pair = ofperr_get_pair__(error, domain);
-    if (pair->code < 0x100) {
+    triplet = ofperr_get_triplet__(error, domain);
+    if (!triplet->vendor) {
         buf = ofpraw_alloc_xid(OFPRAW_OFPT_ERROR, domain->version, xid,
                                sizeof *oem + data_len);
 
         oem = ofpbuf_put_uninit(buf, sizeof *oem);
-        oem->type = htons(pair->type);
-        oem->code = htons(pair->code);
-    } else {
+        oem->type = htons(triplet->type);
+        oem->code = htons(triplet->code);
+    } else if (ofp_version <= OFP11_VERSION) {
         struct nx_vendor_error *nve;
 
         buf = ofpraw_alloc_xid(OFPRAW_OFPT_ERROR, domain->version, xid,
@@ -163,9 +181,19 @@ ofperr_encode_msg__(enum ofperr error, enum ofp_version ofp_version,
         oem->code = htons(NXVC_VENDOR_ERROR);
 
         nve = ofpbuf_put_uninit(buf, sizeof *nve);
-        nve->vendor = htonl(NX_VENDOR_ID);
-        nve->type = htons(pair->type);
-        nve->code = htons(pair->code);
+        nve->vendor = htonl(triplet->vendor);
+        nve->type = htons(triplet->type);
+        nve->code = htons(triplet->code);
+    } else {
+        ovs_be32 vendor = htonl(triplet->vendor);
+
+        buf = ofpraw_alloc_xid(OFPRAW_OFPT_ERROR, domain->version, xid,
+                               sizeof *oem + sizeof(uint32_t) + data_len);
+
+        oem = ofpbuf_put_uninit(buf, sizeof *oem);
+        oem->type = htons(OFPET12_EXPERIMENTER);
+        oem->code = htons(triplet->type);
+        ofpbuf_put(buf, &vendor, sizeof vendor);
     }
 
     ofpbuf_put(buf, data, data_len);
@@ -206,6 +234,13 @@ ofperr_encode_hello(enum ofperr error, enum ofp_version ofp_version,
     return ofperr_encode_msg__(error, ofp_version, htonl(0), s, strlen(s));
 }
 
+int
+ofperr_get_vendor(enum ofperr error, enum ofp_version version)
+{
+    const struct ofperr_domain *domain = ofperr_domain_from_version(version);
+    return domain ? ofperr_get_triplet__(error, domain)->vendor : -1;
+}
+
 /* Returns the value that would go into an OFPT_ERROR message's 'type' for
  * encoding 'error' in 'domain'.  Returns -1 if 'error' is not encodable in
  * 'version' or 'version' is unknown.
@@ -215,7 +250,7 @@ int
 ofperr_get_type(enum ofperr error, enum ofp_version version)
 {
     const struct ofperr_domain *domain = ofperr_domain_from_version(version);
-    return domain ? ofperr_get_pair__(error, domain)->type : -1;
+    return domain ? ofperr_get_triplet__(error, domain)->type : -1;
 }
 
 /* Returns the value that would go into an OFPT_ERROR message's 'code' for
@@ -229,7 +264,7 @@ int
 ofperr_get_code(enum ofperr error, enum ofp_version version)
 {
     const struct ofperr_domain *domain = ofperr_domain_from_version(version);
-    return domain ? ofperr_get_pair__(error, domain)->code : -1;
+    return domain ? ofperr_get_triplet__(error, domain)->code : -1;
 }
 
 /* Tries to decode 'oh', which should be an OpenFlow OFPT_ERROR message.
@@ -240,12 +275,11 @@ ofperr_get_code(enum ofperr error, enum ofp_version version)
 enum ofperr
 ofperr_decode_msg(const struct ofp_header *oh, struct ofpbuf *payload)
 {
-    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
-
     const struct ofp_error_msg *oem;
     enum ofpraw raw;
     uint16_t type, code;
     enum ofperr error;
+    uint32_t vendor;
     struct ofpbuf b;
 
     if (payload) {
@@ -261,6 +295,7 @@ ofperr_decode_msg(const struct ofp_header *oh, struct ofpbuf *payload)
     oem = ofpbuf_pull(&b, sizeof *oem);
 
     /* Get the error type and code. */
+    vendor = 0;
     type = ntohs(oem->type);
     code = ntohs(oem->code);
     if (type == NXET_VENDOR && code == NXVC_VENDOR_ERROR) {
@@ -269,17 +304,22 @@ ofperr_decode_msg(const struct ofp_header *oh, struct ofpbuf *payload)
             return 0;
         }
 
-        if (nve->vendor != htonl(NX_VENDOR_ID)) {
-            VLOG_WARN_RL(&rl, "error contains unknown vendor ID %#"PRIx32,
-                         ntohl(nve->vendor));
-            return 0;
-        }
+        vendor = ntohl(nve->vendor);
         type = ntohs(nve->type);
         code = ntohs(nve->code);
+    } else if (type == OFPET12_EXPERIMENTER) {
+        const ovs_be32 *vendorp = ofpbuf_try_pull(&b, sizeof *vendorp);
+        if (!vendorp) {
+            return 0;
+        }
+
+        vendor = ntohl(*vendorp);
+        type = code;
+        code = 0;
     }
 
     /* Translate the error type and code into an ofperr. */
-    error = ofperr_decode(oh->version, type, code);
+    error = ofperr_decode(oh->version, vendor, type, code);
     if (error && payload) {
         ofpbuf_use_const(payload, b.data, b.size);
     }
@@ -288,10 +328,12 @@ ofperr_decode_msg(const struct ofp_header *oh, struct ofpbuf *payload)
 
 /* If 'error' is a valid OFPERR_* value, returns its name
  * (e.g. "OFPBRC_BAD_TYPE" for OFPBRC_BAD_TYPE).  Otherwise, assumes that
- * 'error' is a positive errno value and returns what strerror() produces for
- * 'error'.  */
+ * 'error' is a positive errno value and returns what ovs_strerror() produces
+ * for 'error'.  */
 const char *
 ofperr_to_string(enum ofperr error)
 {
-    return ofperr_is_valid(error) ? ofperr_get_name(error) : strerror(error);
+    return (ofperr_is_valid(error)
+            ? ofperr_get_name(error)
+            : ovs_strerror(error));
 }
index 593241d..5bf5826 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.
@@ -51,8 +51,22 @@ struct ofpbuf;
  * time and used to determine the mapping between "enum ofperr" constants and
  * error type/code values used in the OpenFlow protocol:
  *
- *   - The first part of each comment specifies OpenFlow type/code for each
- *     protocol that supports the error.
+ *   - The first part of each comment specifies the vendor, OpenFlow versions,
+ *     type, and sometimes a code for each protocol that supports the error:
+ *
+ *         # The vendor is OF for standard OpenFlow error codes.  Otherwise it
+ *           is one of the *_VENDOR_ID codes defined in openflow-common.h.
+ *
+ *         # The version can specify a specific OpenFlow version, a version
+ *           range delimited by "-", or an open-ended range with "+".
+ *
+ *         # Standard OpenFlow errors have both a type and a code.  Extension
+ *           errors generally have only a type, no code.  There is one
+ *           exception: Nicira extension (NX) errors for OpenFlow 1.0 and 1.1
+ *           have both a type and a code.  (This means that the version
+ *           specification for NX errors may not include version 1.0 or 1.1 (or
+ *           both) along with version 1.2 or later, because the requirements
+ *           for those versions are different.)
  *
  *   - Additional text is a human-readable description of the meaning of each
  *     error, used to explain the error to the user.  Any text enclosed in
@@ -61,7 +75,7 @@ struct ofpbuf;
 enum ofperr {
 /* Expected duplications. */
 
-    /* Expected: 3,5 in OF1.1 means both OFPBIC_BAD_EXPERIMENTER and
+    /* Expected: 0x0,3,5 in OF1.1 means both OFPBIC_BAD_EXPERIMENTER and
      * OFPBIC_BAD_EXP_TYPE. */
 
 /* ## ------------------ ## */
@@ -108,17 +122,17 @@ enum ofperr {
 
     /* NX1.0(1,512), OF1.1+(1,9).  Specified table-id invalid or does not exist.
      * [ A non-standard error (1,512), formerly OFPERR_NXBRC_BAD_TABLE_ID,
-     *   is used for OpenFlow 1.0 as there seems to be no appropriste error
+     *   is used for OpenFlow 1.0 as there seems to be no appropriate error
      *   code defined the specification. ] */
     OFPERR_OFPBRC_BAD_TABLE_ID,
 
     /* OF1.2+(1,10).  Denied because controller is slave. */
     OFPERR_OFPBRC_IS_SLAVE,
 
-    /* NX1.0(1,514), NX1.1(1,514), OF1.2+(1,11).  Invalid port.
-     * [ A non-standard error (1,514), formerly
-     *   OFPERR_NXBRC_BAD_IN_PORT is used for OpenFlow 1.0 and 1.1 as there
-     *   seems to be no appropriste error code defined the specifications. ] */
+    /* NX1.0-1.1(1,514), OF1.2+(1,11).  Invalid port.  [ A non-standard error
+     * (1,514), formerly OFPERR_NXBRC_BAD_IN_PORT is used for OpenFlow 1.0 and
+     * 1.1 as there seems to be no appropriate error code defined the
+     * specifications. ] */
     OFPERR_OFPBRC_BAD_PORT,
 
     /* OF1.2+(1,12).  Invalid packet in packet-out. */
@@ -127,41 +141,43 @@ enum ofperr {
     /* OF1.3+(1,13).  Multipart request overflowed the assigned buffer. */
     OFPERR_OFPBRC_MULTIPART_BUFFER_OVERFLOW,
 
-    /* NX1.0+(1,256).  Invalid NXM flow match. */
+    /* NX1.0-1.1(1,256), NX1.2+(2).  Invalid NXM flow match. */
     OFPERR_NXBRC_NXM_INVALID,
 
-    /* NX1.0+(1,257).  The nxm_type, or nxm_type taken in combination with
-     * nxm_hasmask or nxm_length or both, is invalid or not implemented. */
+    /* NX1.0-1.1(1,257), NX1.2+(3).  The nxm_type, or nxm_type taken in
+     * combination with nxm_hasmask or nxm_length or both, is invalid or not
+     * implemented. */
     OFPERR_NXBRC_NXM_BAD_TYPE,
 
-    /* NX1.0+(1,515).  Must-be-zero field had nonzero value. */
+    /* NX1.0-1.1(1,515), NX1.2+(4).  Must-be-zero field had nonzero value. */
     OFPERR_NXBRC_MUST_BE_ZERO,
 
-    /* NX1.0+(1,516).  The reason in an ofp_port_status message is not
-     * valid. */
+    /* NX1.0-1.1(1,516), NX1.2+(5).  The reason in an ofp_port_status message
+     * is not valid. */
     OFPERR_NXBRC_BAD_REASON,
 
-    /* NX1.0+(1,517).  The 'id' in an NXST_FLOW_MONITOR request is the same as
-     * an existing monitor id (or two monitors in the same NXST_FLOW_MONITOR
-     * request have the same 'id').  */
+    /* NX1.0-1.1(1,517), NX1.2+(6).  The 'id' in an NXST_FLOW_MONITOR request
+     * is the same as an existing monitor id (or two monitors in the same
+     * NXST_FLOW_MONITOR request have the same 'id').  */
     OFPERR_NXBRC_FM_DUPLICATE_ID,
 
-    /* NX1.0+(1,518).  The 'flags' in an NXST_FLOW_MONITOR request either does
-     * not specify at least one of the NXFMF_ADD, NXFMF_DELETE, or NXFMF_MODIFY
-     * flags, or specifies a flag bit that is not defined. */
+    /* NX1.0-1.1(1,518), NX1.2+(7).  The 'flags' in an NXST_FLOW_MONITOR
+     * request either does not specify at least one of the NXFMF_ADD,
+     * NXFMF_DELETE, or NXFMF_MODIFY flags, or specifies a flag bit that is not
+     * defined. */
     OFPERR_NXBRC_FM_BAD_FLAGS,
 
-    /* NX1.0+(1,519).  The 'id' in an NXT_FLOW_MONITOR_CANCEL request is not
-     * the id of any existing monitor. */
+    /* NX1.0-1.1(1,519), NX1.2+(8).  The 'id' in an NXT_FLOW_MONITOR_CANCEL
+     * request is not 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
+    /* NX1.0-1.1(1,520), NX1.2+(9).  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. */
+    /* NX1.0-1.1(1,521), NX1.2+(10).  The error that occurred cannot be
+     * represented in this OpenFlow version. */
     OFPERR_NXBRC_UNENCODABLE_ERROR,
 
 /* ## ---------------- ## */
@@ -217,7 +233,8 @@ enum ofperr {
     /* OF1.2+(2,15).  Bad argument in SET_FIELD action. */
     OFPERR_OFPBAC_ARGUMENT,
 
-    /* NX1.0+(2,256).  Must-be-zero action argument had nonzero value. */
+    /* NX1.0-1.1(2,256), NX1.2+(11).  Must-be-zero action argument had nonzero
+     * value. */
     OFPERR_NXBAC_MUST_BE_ZERO,
 
 /* ## --------------------- ## */
@@ -251,6 +268,9 @@ enum ofperr {
     /* OF1.2+(3,8).  Permissions error. */
     OFPERR_OFPBIC_EPERM,
 
+    /* ONF1.1+(2600).  Duplicate instruction. */
+    OFPERR_ONFBIC_DUP_INSTRUCTION,
+
 /* ## --------------- ## */
 /* ## OFPET_BAD_MATCH ## */
 /* ## --------------- ## */
@@ -282,15 +302,14 @@ enum ofperr {
      * field. */
     OFPERR_OFPBMC_BAD_VALUE,
 
-    /* NX1.0(1,259), NX1.1(1,259), OF1.2+(4,8).  Unsupported mask specified in
-     * the match, field is not dl-address or nw-address. */
+    /* NX1.0-1.1(1,259), OF1.2+(4,8).  Unsupported mask specified in the match,
+     * field is not dl-address or nw-address. */
     OFPERR_OFPBMC_BAD_MASK,
 
-    /* NX1.0(1,260), NX1.1(1,260), OF1.2+(4,9).  A prerequisite was not met. */
+    /* NX1.0-1.1(1,260), OF1.2+(4,9).  A prerequisite was not met. */
     OFPERR_OFPBMC_BAD_PREREQ,
 
-    /* NX1.0(1,261), NX1.1(1,261), OF1.2+(4,10).  A field type was
-     * duplicated. */
+    /* NX1.0-1.1(1,261), OF1.2+(4,10).  A field type was duplicated. */
     OFPERR_OFPBMC_DUP_FIELD,
 
     /* OF1.2+(4,11).  Permissions error. */
@@ -333,12 +352,12 @@ enum ofperr {
      * specified. */
     OFPERR_OFPFMFC_UNSUPPORTED,
 
-    /* NX1.0(3,256), NX1.1(5,256).  Generic hardware error. */
+    /* NX1.0-1.1(5,256), NX1.2+(12).  Generic hardware error. */
     OFPERR_NXFMFC_HARDWARE,
 
-    /* NX1.0(3,257), NX1.1(5,257).  A nonexistent table ID was specified in the
-     * "command" field of struct ofp_flow_mod, when the nxt_flow_mod_table_id
-     * extension is enabled. */
+    /* NX1.0-1.1(5,257), NX1.2+(13).  A nonexistent table ID was specified in
+     * the "command" field of struct ofp_flow_mod, when the
+     * nxt_flow_mod_table_id extension is enabled. */
     OFPERR_NXFMFC_BAD_TABLE_ID,
 
 /* ## ---------------------- ## */
@@ -388,13 +407,13 @@ enum ofperr {
     OFPERR_OFPGMFC_BAD_COMMAND,
 
     /* OF1.2+(6,12).  Error in bucket. */
-    OFPERR_OFPGMFC_OFPGMFC_BAD_BUCKET,
+    OFPERR_OFPGMFC_BAD_BUCKET,
 
     /* OF1.2+(6,13).  Error in watch port/group. */
-    OFPERR_OFPGMFC_OFPGMFC_BAD_WATCH,
+    OFPERR_OFPGMFC_BAD_WATCH,
 
     /* OF1.2+(6,14).  Permissions error. */
-    OFPERR_OFPGMFC_OFPGMFC_EPERM,
+    OFPERR_OFPGMFC_EPERM,
 
 /* ## --------------------- ## */
 /* ## OFPET_PORT_MOD_FAILED ## */
@@ -465,7 +484,7 @@ enum ofperr {
     /* OF1.2+(11,1).  Controller role change unsupported. */
     OFPERR_OFPRRFC_UNSUP,
 
-    /* NX1.0(1,513), NX1.1(1,513), OF1.2+(11,2).  Invalid role. */
+    /* NX1.0-1.1(1,513), OF1.2+(11,2).  Invalid role. */
     OFPERR_OFPRRFC_BAD_ROLE,
 
 /* ## ---------------------- ## */
@@ -542,7 +561,6 @@ const char *ofperr_domain_get_name(enum ofp_version);
 
 bool ofperr_is_valid(enum ofperr);
 
-enum ofperr ofperr_decode(enum ofp_version, uint16_t type, uint16_t code);
 enum ofperr ofperr_from_name(const char *);
 
 enum ofperr ofperr_decode_msg(const struct ofp_header *,
@@ -550,6 +568,7 @@ enum ofperr ofperr_decode_msg(const struct ofp_header *,
 struct ofpbuf *ofperr_encode_reply(enum ofperr, const struct ofp_header *);
 struct ofpbuf *ofperr_encode_hello(enum ofperr, enum ofp_version ofp_version,
                                    const char *);
+int ofperr_get_vendor(enum ofperr, enum ofp_version);
 int ofperr_get_type(enum ofperr, enum ofp_version);
 int ofperr_get_code(enum ofperr, enum ofp_version);
 
index 66ec448..2501e38 100644 (file)
@@ -217,7 +217,7 @@ enum ofpraw {
     /* NXT 1.0+ (19): struct nx_async_config. */
     OFPRAW_NXT_SET_ASYNC_CONFIG,
 
-    /* OFPT 1.3+ (29): struct ofp13_meter_mod. */
+    /* OFPT 1.3+ (29): struct ofp13_meter_mod, uint8_t[8][]. */
     OFPRAW_OFPT13_METER_MOD,
 
 /* Standard statistics. */
@@ -315,13 +315,13 @@ enum ofpraw {
     /* OFPST 1.3+ (9): struct ofp13_meter_multipart_request. */
     OFPRAW_OFPST13_METER_REQUEST,
 
-    /* OFPST 1.3+ (9): struct ofp13_meter_stats[]. */
+    /* OFPST 1.3+ (9): uint8_t[8][]. */
     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[]. */
+    /* OFPST 1.3+ (10): uint8_t[8][]. */
     OFPRAW_OFPST13_METER_CONFIG_REPLY,
 
     /* OFPST 1.3+ (11): void. */
index 1c5c761..b1e369c 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.
@@ -45,14 +45,14 @@ static void ofp_fatal(const char *flow, bool verbose, const char *format, ...)
     NO_RETURN;
 
 static uint8_t
-str_to_table_id(const char *str)
+str_to_u8(const char *str, const char *name)
 {
-    int table_id;
+    int value;
 
-    if (!str_to_int(str, 10, &table_id) || table_id < 0 || table_id > 255) {
-        ovs_fatal(0, "invalid table \"%s\"", str);
+    if (!str_to_int(str, 10, &value) || value < 0 || value > 255) {
+        ovs_fatal(0, "invalid %s \"%s\"", name, str);
     }
-    return table_id;
+    return value;
 }
 
 static uint16_t
@@ -135,7 +135,7 @@ parse_enqueue(char *arg, struct ofpbuf *ofpacts)
     }
 
     enqueue = ofpact_put_ENQUEUE(ofpacts);
-    enqueue->port = str_to_u32(port);
+    enqueue->port = u16_to_ofp(str_to_u32(port));
     enqueue->queue = str_to_u32(queue);
 }
 
@@ -152,7 +152,7 @@ parse_output(char *arg, struct ofpbuf *ofpacts)
         struct ofpact_output *output;
 
         output = ofpact_put_OUTPUT(ofpacts);
-        output->port = str_to_u32(arg);
+        output->port = u16_to_ofp(str_to_u32(arg));
         output->max_len = output->port == OFPP_CONTROLLER ? UINT16_MAX : 0;
     }
 }
@@ -650,7 +650,7 @@ str_to_ofpact__(char *pos, char *act, char *arg,
         }
         return false;
     } else {
-        uint16_t port;
+        ofp_port_t port;
         if (ofputil_port_from_string(act, &port)) {
             ofpact_put_OUTPUT(ofpacts)->port = port;
         } else {
@@ -705,6 +705,10 @@ parse_named_instruction(enum ovs_instruction_type type,
         ofpact_put_CLEAR_ACTIONS(ofpacts);
         break;
 
+    case OVSINST_OFPIT13_METER:
+        ofpact_put_METER(ofpacts)->meter_id = str_to_u32(arg);
+        break;
+
     case OVSINST_OFPIT11_WRITE_METADATA:
         parse_metadata(ofpacts, arg);
         break;
@@ -715,7 +719,7 @@ parse_named_instruction(enum ovs_instruction_type type,
         if (!table_s || !table_s[0]) {
             ovs_fatal(0, "instruction goto-table needs table id");
         }
-        ogt->table_id = str_to_table_id(table_s);
+        ogt->table_id = str_to_u8(table_s, "table");
         break;
     }
     }
@@ -739,7 +743,7 @@ str_to_inst_ofpacts(char *str, struct ofpbuf *ofpacts)
 
     pos = str;
     while (ofputil_parse_key_value(&pos, &inst, &arg)) {
-        type = ofpact_instruction_type_from_name(inst);
+        type = ovs_instruction_type_from_name(inst);
         if (type < 0) {
             if (!str_to_ofpact__(pos, inst, arg, ofpacts, n_actions)) {
                 break;
@@ -948,9 +952,9 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
             }
 
             if (!strcmp(name, "table")) {
-                fm->table_id = str_to_table_id(value);
+                fm->table_id = str_to_u8(value, name);
             } else if (!strcmp(name, "out_port")) {
-                if (!ofputil_port_from_string(name, &fm->out_port)) {
+                if (!ofputil_port_from_string(value, &fm->out_port)) {
                     ofp_fatal(str_, verbose, "%s is not a valid OpenFlow port",
                               name);
                 }
@@ -1012,7 +1016,7 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
         fm->ofpacts = ofpbuf_steal_data(&ofpacts);
 
         err = ofpacts_check(fm->ofpacts, fm->ofpacts_len, &fm->match.flow,
-                            OFPP_MAX);
+                            OFPP_MAX, 0);
         if (err) {
             exit(EXIT_FAILURE);
         }
@@ -1025,6 +1029,198 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
     free(string);
 }
 
+/* Convert 'str_' (as described in the Flow Syntax section of the ovs-ofctl man
+ * page) into 'mm' for sending the specified meter_mod 'command' to a switch.
+ */
+void
+parse_ofp_meter_mod_str(struct ofputil_meter_mod *mm, const char *str_,
+                        int command, bool verbose)
+{
+    enum {
+        F_METER = 1 << 0,
+        F_FLAGS = 1 << 1,
+        F_BANDS = 1 << 2,
+    } fields;
+    char *string = xstrdup(str_);
+    char *save_ptr = NULL;
+    char *band_str = NULL;
+    char *name;
+
+    switch (command) {
+    case -1:
+        fields = F_METER;
+        break;
+
+    case OFPMC13_ADD:
+        fields = F_METER | F_FLAGS | F_BANDS;
+        break;
+
+    case OFPMC13_DELETE:
+        fields = F_METER;
+        break;
+
+    case OFPMC13_MODIFY:
+        fields = F_METER | F_FLAGS | F_BANDS;
+        break;
+
+    default:
+        NOT_REACHED();
+    }
+
+    mm->command = command;
+    mm->meter.meter_id = 0;
+    mm->meter.flags = 0;
+    if (fields & F_BANDS) {
+        band_str = strstr(string, "band");
+        if (!band_str) {
+            ofp_fatal(str_, verbose, "must specify bands");
+        }
+        *band_str = '\0';
+
+        band_str = strchr(band_str + 1, '=');
+        if (!band_str) {
+            ofp_fatal(str_, verbose, "must specify bands");
+        }
+
+        band_str++;
+    }
+    for (name = strtok_r(string, "=, \t\r\n", &save_ptr); name;
+         name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) {
+
+        if (fields & F_FLAGS && !strcmp(name, "kbps")) {
+            mm->meter.flags |= OFPMF13_KBPS;
+        } else if (fields & F_FLAGS && !strcmp(name, "pktps")) {
+            mm->meter.flags |= OFPMF13_PKTPS;
+        } else if (fields & F_FLAGS && !strcmp(name, "burst")) {
+            mm->meter.flags |= OFPMF13_BURST;
+        } else if (fields & F_FLAGS && !strcmp(name, "stats")) {
+            mm->meter.flags |= OFPMF13_STATS;
+        } else {
+            char *value;
+
+            value = strtok_r(NULL, ", \t\r\n", &save_ptr);
+            if (!value) {
+                ofp_fatal(str_, verbose, "field %s missing value", name);
+            }
+
+            if (!strcmp(name, "meter")) {
+                if (!strcmp(value, "all")) {
+                    mm->meter.meter_id = OFPM13_ALL;
+                } else if (!strcmp(value, "controller")) {
+                    mm->meter.meter_id = OFPM13_CONTROLLER;
+                } else if (!strcmp(value, "slowpath")) {
+                    mm->meter.meter_id = OFPM13_SLOWPATH;
+                } else {
+                    mm->meter.meter_id = str_to_u32(value);
+                    if (mm->meter.meter_id > OFPM13_MAX) {
+                        ofp_fatal(str_, verbose, "invalid value for %s", name);
+                    }
+                }
+            } else {
+                ofp_fatal(str_, verbose, "unknown keyword %s", name);
+            }
+        }
+    }
+    if (fields & F_METER && !mm->meter.meter_id) {
+        ofp_fatal(str_, verbose, "must specify 'meter'");
+    }
+    if (fields & F_FLAGS && !mm->meter.flags) {
+        ofp_fatal(str_, verbose,
+                  "meter must specify either 'kbps' or 'pktps'");
+    }
+
+    if (fields & F_BANDS) {
+        struct ofpbuf bands;
+        uint16_t n_bands = 0;
+        struct ofputil_meter_band *band = NULL;
+        int i;
+
+        ofpbuf_init(&bands, 64);
+
+        for (name = strtok_r(band_str, "=, \t\r\n", &save_ptr); name;
+             name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) {
+
+            char *value;
+
+            value = strtok_r(NULL, ", \t\r\n", &save_ptr);
+            if (!value) {
+                ofp_fatal(str_, verbose, "field %s missing value", name);
+            }
+
+            if (!strcmp(name, "type")) {
+                /* Start a new band */
+                band = ofpbuf_put_zeros(&bands, sizeof *band);
+                n_bands++;
+
+                if (!strcmp(value, "drop")) {
+                    band->type = OFPMBT13_DROP;
+                } else if (!strcmp(value, "dscp_remark")) {
+                    band->type = OFPMBT13_DSCP_REMARK;
+                } else {
+                    ofp_fatal(str_, verbose, "field %s unknown value %s", name,
+                              value);
+                }
+            } else if (!band || !band->type) {
+                ofp_fatal(str_, verbose,
+                          "band must start with the 'type' keyword");
+            } else if (!strcmp(name, "rate")) {
+                band->rate = str_to_u32(value);
+            } else if (!strcmp(name, "burst_size")) {
+                band->burst_size = str_to_u32(value);
+            } else if (!strcmp(name, "prec_level")) {
+                band->prec_level = str_to_u8(value, name);
+            } else {
+                ofp_fatal(str_, verbose, "unknown keyword %s", name);
+            }
+        }
+        /* validate bands */
+        if (!n_bands) {
+            ofp_fatal(str_, verbose, "meter must have bands");
+        }
+
+        mm->meter.n_bands = n_bands;
+        mm->meter.bands = ofpbuf_steal_data(&bands);
+
+        for (i = 0; i < n_bands; ++i) {
+            band = &mm->meter.bands[i];
+
+            if (!band->type) {
+                ofp_fatal(str_, verbose, "band must have 'type'");
+            }
+            if (band->type == OFPMBT13_DSCP_REMARK) {
+                if (!band->prec_level) {
+                    ofp_fatal(str_, verbose, "'dscp_remark' band must have"
+                              " 'prec_level'");
+                }
+            } else {
+                if (band->prec_level) {
+                    ofp_fatal(str_, verbose, "Only 'dscp_remark' band may have"
+                              " 'prec_level'");
+                }
+            }
+            if (!band->rate) {
+                ofp_fatal(str_, verbose, "band must have 'rate'");
+            }
+            if (mm->meter.flags & OFPMF13_BURST) {
+                if (!band->burst_size) {
+                    ofp_fatal(str_, verbose, "band must have 'burst_size' "
+                              "when 'burst' flag is set");
+                }
+            } else {
+                if (band->burst_size) {
+                    ofp_fatal(str_, verbose, "band may have 'burst_size' only "
+                              "when 'burst' flag is set");
+                }
+            }
+        }
+    } else {
+        mm->meter.n_bands = 0;
+        mm->meter.bands = NULL;
+    }
+
+    free(string);
+}
+
 /* Convert 'str_' (as described in the documentation for the "monitor" command
  * in the ovs-ofctl man page) into 'fmr'. */
 void
@@ -1074,9 +1270,9 @@ parse_flow_monitor_request(struct ofputil_flow_monitor_request *fmr,
             }
 
             if (!strcmp(name, "table")) {
-                fmr->table_id = str_to_table_id(value);
+                fmr->table_id = str_to_u8(value, name);
             } else if (!strcmp(name, "out_port")) {
-                fmr->out_port = atoi(value);
+                fmr->out_port = u16_to_ofp(atoi(value));
             } else if (mf_from_name(name)) {
                 parse_field(mf_from_name(name), value, &fmr->match);
             } else {
@@ -1229,8 +1425,8 @@ parse_ofp_exact_flow(struct flow *flow, const char *s)
         }
     }
 
-    if (!flow->in_port) {
-        flow->in_port = OFPP_NONE;
+    if (!flow->in_port.ofp_port) {
+        flow->in_port.ofp_port = OFPP_NONE;
     }
 
 exit:
index d2d3c3c..6ee25a4 100644 (file)
@@ -28,6 +28,7 @@ struct ofpbuf;
 struct ofputil_flow_mod;
 struct ofputil_flow_monitor_request;
 struct ofputil_flow_stats_request;
+struct ofputil_meter_mod;
 
 void parse_ofp_str(struct ofputil_flow_mod *, int command, const char *str_,
                    bool verbose);
@@ -45,6 +46,9 @@ void parse_ofpacts(const char *, struct ofpbuf *ofpacts);
 
 char *parse_ofp_exact_flow(struct flow *, const char *);
 
+void parse_ofp_meter_mod_str(struct ofputil_meter_mod *, const char *string,
+                             int command, bool verbose);
+
 void parse_flow_monitor_request(struct ofputil_flow_monitor_request *,
                                 const char *);
 
index 8d99844..76bd09c 100644 (file)
@@ -62,7 +62,7 @@ ofp_packet_to_string(const void *data, size_t len)
     struct flow flow;
 
     ofpbuf_use_const(&buf, data, len);
-    flow_extract(&buf, 0, 0, NULL, 0, &flow);
+    flow_extract(&buf, 0, 0, NULL, NULL, &flow);
     flow_format(&ds, &flow);
 
     if (buf.l7) {
@@ -86,6 +86,7 @@ static void
 ofp_print_packet_in(struct ds *string, const struct ofp_header *oh,
                     int verbosity)
 {
+    char reasonbuf[OFPUTIL_PACKET_IN_REASON_BUFSIZE];
     struct ofputil_packet_in pin;
     int error;
     int i;
@@ -130,7 +131,8 @@ ofp_print_packet_in(struct ds *string, const struct ofp_header *oh,
     }
 
     ds_put_format(string, " (via %s)",
-                  ofputil_packet_in_reason_to_string(pin.reason));
+                  ofputil_packet_in_reason_to_string(pin.reason, reasonbuf,
+                                                     sizeof reasonbuf));
 
     ds_put_format(string, " data_len=%zu", pin.packet_len);
     if (pin.buffer_id == UINT32_MAX) {
@@ -151,6 +153,9 @@ ofp_print_packet_in(struct ds *string, const struct ofp_header *oh,
         ds_put_cstr(string, packet);
         free(packet);
     }
+    if (verbosity > 2) {
+        ds_put_hex_dump(string, pin.packet, pin.packet_len, 0, false);
+    }
 }
 
 static void
@@ -183,10 +188,12 @@ ofp_print_packet_out(struct ds *string, const struct ofp_header *oh,
             ds_put_cstr(string, packet);
             free(packet);
         }
+        if (verbosity > 2) {
+            ds_put_hex_dump(string, po.packet, po.packet_len, 0, false);
+        }
     } else {
         ds_put_format(string, " buffer=0x%08"PRIx32, po.buffer_id);
     }
-    ds_put_char(string, '\n');
 
     ofpbuf_uninit(&ofpacts);
 }
@@ -197,8 +204,8 @@ compare_ports(const void *a_, const void *b_)
 {
     const struct ofputil_phy_port *a = a_;
     const struct ofputil_phy_port *b = b_;
-    uint16_t ap = a->port_no;
-    uint16_t bp = b->port_no;
+    uint16_t ap = ofp_to_u16(a->port_no);
+    uint16_t bp = ofp_to_u16(b->port_no);
 
     return ap < bp ? -1 : ap > bp;
 }
@@ -578,7 +585,7 @@ static void print_wild(struct ds *string, const char *leader, int is_wild,
 
 static void
 print_wild_port(struct ds *string, const char *leader, int is_wild,
-                int verbosity, uint16_t port)
+                int verbosity, ofp_port_t port)
 {
     if (is_wild && verbosity < 2) {
         return;
@@ -658,7 +665,7 @@ ofp10_match_to_string(const struct ofp10_match *om, int verbosity)
         }
     }
     print_wild_port(&f, "in_port=", w & OFPFW10_IN_PORT, verbosity,
-                    ntohs(om->in_port));
+                    u16_to_ofp(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,
@@ -852,11 +859,14 @@ ofp_print_duration(struct ds *string, unsigned int sec, unsigned int nsec)
     ds_put_char(string, 's');
 }
 
+/* Returns a string form of 'reason'.  The return value is either a statically
+ * allocated constant string or the 'bufsize'-byte buffer 'reasonbuf'.
+ * 'bufsize' should be at least OFP_FLOW_REMOVED_REASON_BUFSIZE. */
+#define OFP_FLOW_REMOVED_REASON_BUFSIZE (INT_STRLEN(int) + 1)
 static const char *
-ofp_flow_removed_reason_to_string(enum ofp_flow_removed_reason reason)
+ofp_flow_removed_reason_to_string(enum ofp_flow_removed_reason reason,
+                                  char *reasonbuf, size_t bufsize)
 {
-    static char s[32];
-
     switch (reason) {
     case OFPRR_IDLE_TIMEOUT:
         return "idle";
@@ -868,15 +878,18 @@ ofp_flow_removed_reason_to_string(enum ofp_flow_removed_reason reason)
         return "group_delete";
     case OFPRR_EVICTION:
         return "eviction";
+    case OFPRR_METER_DELETE:
+        return "meter_delete";
     default:
-        sprintf(s, "%d", (int) reason);
-        return s;
+        snprintf(reasonbuf, bufsize, "%d", (int) reason);
+        return reasonbuf;
     }
 }
 
 static void
 ofp_print_flow_removed(struct ds *string, const struct ofp_header *oh)
 {
+    char reasonbuf[OFP_FLOW_REMOVED_REASON_BUFSIZE];
     struct ofputil_flow_removed fr;
     enum ofperr error;
 
@@ -890,7 +903,8 @@ ofp_print_flow_removed(struct ds *string, const struct ofp_header *oh)
     match_format(&fr.match, string, fr.priority);
 
     ds_put_format(string, " reason=%s",
-                  ofp_flow_removed_reason_to_string(fr.reason));
+                  ofp_flow_removed_reason_to_string(fr.reason, reasonbuf,
+                                                    sizeof reasonbuf));
 
     if (fr.table_id != 255) {
         ds_put_format(string, " table_id=%"PRIu8, fr.table_id);
@@ -942,6 +956,236 @@ ofp_print_port_mod(struct ds *string, const struct ofp_header *oh)
     }
 }
 
+static void
+ofp_print_meter_flags(struct ds *s, uint16_t flags)
+{
+    if (flags & OFPMF13_KBPS) {
+        ds_put_cstr(s, "kbps ");
+    }
+    if (flags & OFPMF13_PKTPS) {
+        ds_put_cstr(s, "pktps ");
+    }
+    if (flags & OFPMF13_BURST) {
+        ds_put_cstr(s, "burst ");
+    }
+    if (flags & OFPMF13_STATS) {
+        ds_put_cstr(s, "stats ");
+    }
+
+    flags &= ~(OFPMF13_KBPS | OFPMF13_PKTPS | OFPMF13_BURST | OFPMF13_STATS);
+    if (flags) {
+        ds_put_format(s, "flags:0x%"PRIx16" ", flags);
+    }
+}
+
+static void
+ofp_print_meter_band(struct ds *s, uint16_t flags,
+                     const struct ofputil_meter_band *mb)
+{
+    ds_put_cstr(s, "\ntype=");
+    switch (mb->type) {
+    case OFPMBT13_DROP:
+        ds_put_cstr(s, "drop");
+        break;
+    case OFPMBT13_DSCP_REMARK:
+        ds_put_cstr(s, "dscp_remark");
+        break;
+    default:
+        ds_put_format(s, "%u", mb->type);
+    }
+
+    ds_put_format(s, " rate=%"PRIu32, mb->rate);
+
+    if (flags & OFPMF13_BURST) {
+        ds_put_format(s, " burst_size=%"PRIu32, mb->burst_size);
+    }
+    if (mb->type == OFPMBT13_DSCP_REMARK) {
+        ds_put_format(s, " prec_level=%"PRIu8, mb->prec_level);
+    }
+}
+
+static void
+ofp_print_meter_stats(struct ds *s, const struct ofputil_meter_stats *ms)
+{
+    uint16_t i;
+
+    ds_put_format(s, "meter:%"PRIu32" ", ms->meter_id);
+    ds_put_format(s, "flow_count:%"PRIu32" ", ms->flow_count);
+    ds_put_format(s, "packet_in_count:%"PRIu64" ", ms->packet_in_count);
+    ds_put_format(s, "byte_in_count:%"PRIu64" ", ms->byte_in_count);
+    ds_put_cstr(s, "duration:");
+    ofp_print_duration(s, ms->duration_sec, ms->duration_nsec);
+    ds_put_char(s, ' ');
+
+    ds_put_cstr(s, "bands:\n");
+    for (i = 0; i < ms->n_bands; ++i) {
+        ds_put_format(s, "%d: ", i);
+        ds_put_format(s, "packet_count:%"PRIu64" ", ms->bands[i].packet_count);
+        ds_put_format(s, "byte_count:%"PRIu64"\n", ms->bands[i].byte_count);
+    }
+}
+
+static void
+ofp_print_meter_config(struct ds *s, const struct ofputil_meter_config *mc)
+{
+    uint16_t i;
+
+    ds_put_format(s, "meter=%"PRIu32" ", mc->meter_id);
+
+    ofp_print_meter_flags(s, mc->flags);
+
+    ds_put_cstr(s, "bands=");
+    for (i = 0; i < mc->n_bands; ++i) {
+        ofp_print_meter_band(s, mc->flags, &mc->bands[i]);
+    }
+    ds_put_char(s, '\n');
+}
+
+static void
+ofp_print_meter_mod(struct ds *s, const struct ofp_header *oh)
+{
+    struct ofputil_meter_mod mm;
+    struct ofpbuf bands;
+    enum ofperr error;
+
+    ofpbuf_init(&bands, 64);
+    error = ofputil_decode_meter_mod(oh, &mm, &bands);
+    if (error) {
+        ofpbuf_uninit(&bands);
+        ofp_print_error(s, error);
+        return;
+    }
+
+    switch (mm.command) {
+    case OFPMC13_ADD:
+        ds_put_cstr(s, " ADD ");
+        break;
+    case OFPMC13_MODIFY:
+        ds_put_cstr(s, " MOD ");
+        break;
+    case OFPMC13_DELETE:
+        ds_put_cstr(s, " DEL ");
+        break;
+    default:
+        ds_put_format(s, " cmd:%d ", mm.command);
+    }
+
+    ofp_print_meter_config(s, &mm.meter);
+    ofpbuf_uninit(&bands);
+}
+
+static void
+ofp_print_meter_stats_request(struct ds *s, const struct ofp_header *oh)
+{
+    uint32_t meter_id;
+
+    ofputil_decode_meter_request(oh, &meter_id);
+
+    ds_put_format(s, " meter=%"PRIu32, meter_id);
+}
+
+static const char *
+ofputil_meter_capabilities_to_name(uint32_t bit)
+{
+    enum ofp13_meter_flags flag = bit;
+
+    switch (flag) {
+    case OFPMF13_KBPS:    return "kbps";
+    case OFPMF13_PKTPS:   return "pktps";
+    case OFPMF13_BURST:   return "burst";
+    case OFPMF13_STATS:   return "stats";
+    }
+
+    return NULL;
+}
+
+static const char *
+ofputil_meter_band_types_to_name(uint32_t bit)
+{
+    /*
+     * Note: Meter band types start from 1.  We assume that the lowest bit
+     * in the band_types corresponds to DROP band type (1).
+     */
+    switch (bit) {
+    case 1 << (OFPMBT13_DROP - 1):          return "drop";
+    case 1 << (OFPMBT13_DSCP_REMARK - 1):   return "dscp_remark";
+    }
+
+    return NULL;
+}
+
+static void
+ofp_print_meter_features_reply(struct ds *s, const struct ofp_header *oh)
+{
+    struct ofputil_meter_features mf;
+
+    ofputil_decode_meter_features(oh, &mf);
+
+    ds_put_format(s, "\nmax_meter:%"PRIu32, mf.max_meters);
+    ds_put_format(s, " max_bands:%"PRIu8, mf.max_bands);
+    ds_put_format(s, " max_color:%"PRIu8"\n", mf.max_color);
+
+    ds_put_cstr(s, "band_types: ");
+    ofp_print_bit_names(s, mf.band_types,
+                        ofputil_meter_band_types_to_name, ' ');
+    ds_put_char(s, '\n');
+
+    ds_put_cstr(s, "capabilities: ");
+    ofp_print_bit_names(s, mf.capabilities,
+                        ofputil_meter_capabilities_to_name, ' ');
+    ds_put_char(s, '\n');
+}
+
+static void
+ofp_print_meter_config_reply(struct ds *s, const struct ofp_header *oh)
+{
+    struct ofpbuf bands;
+    struct ofpbuf b;
+
+    ofpbuf_use_const(&b, oh, ntohs(oh->length));
+    ofpbuf_init(&bands, 64);
+    for (;;) {
+        struct ofputil_meter_config mc;
+        int retval;
+
+        retval = ofputil_decode_meter_config(&b, &mc, &bands);
+        if (retval) {
+            if (retval != EOF) {
+                ofp_print_error(s, retval);
+            }
+            break;
+        }
+        ds_put_char(s, '\n');
+        ofp_print_meter_config(s, &mc);
+    }
+    ofpbuf_uninit(&bands);
+}
+
+static void
+ofp_print_meter_stats_reply(struct ds *s, const struct ofp_header *oh)
+{
+    struct ofpbuf bands;
+    struct ofpbuf b;
+
+    ofpbuf_use_const(&b, oh, ntohs(oh->length));
+    ofpbuf_init(&bands, 64);
+    for (;;) {
+        struct ofputil_meter_stats ms;
+        int retval;
+
+        retval = ofputil_decode_meter_stats(&b, &ms, &bands);
+        if (retval) {
+            if (retval != EOF) {
+                ofp_print_error(s, retval);
+            }
+            break;
+        }
+        ds_put_char(s, '\n');
+        ofp_print_meter_stats(s, &ms);
+    }
+    ofpbuf_uninit(&bands);
+}
+
 static void
 ofp_print_error(struct ds *string, enum ofperr error)
 {
@@ -1155,7 +1399,7 @@ print_port_stat(struct ds *string, const char *leader, uint64_t stat, int more)
 static void
 ofp_print_ofpst_port_request(struct ds *string, const struct ofp_header *oh)
 {
-    uint16_t ofp10_port;
+    ofp_port_t ofp10_port;
     enum ofperr error;
 
     error = ofputil_decode_port_stats_request(oh, &ofp10_port);
@@ -1193,7 +1437,7 @@ ofp_print_ofpst_port_reply(struct ds *string, const struct ofp_header *oh,
         }
 
         ds_put_cstr(string, "  port ");
-        if (ps.port_no < 10) {
+        if (ofp_to_u16(ps.port_no) < 10) {
             ds_put_char(string, ' ');
         }
         ofputil_format_port(ps.port_no, string);
@@ -1213,6 +1457,12 @@ ofp_print_ofpst_port_reply(struct ds *string, const struct ofp_header *oh,
         print_port_stat(string, "drop=", ps.stats.tx_dropped, 1);
         print_port_stat(string, "errs=", ps.stats.tx_errors, 1);
         print_port_stat(string, "coll=", ps.stats.collisions, 0);
+
+        if (ps.duration_sec != UINT32_MAX) {
+            ds_put_cstr(string, "           duration=");
+            ofp_print_duration(string, ps.duration_sec, ps.duration_nsec);
+            ds_put_char(string, '\n');
+        }
     }
 }
 
@@ -1615,11 +1865,14 @@ ofp_print_nxt_set_packet_in_format(struct ds *string,
     }
 }
 
+/* Returns a string form of 'reason'.  The return value is either a statically
+ * allocated constant string or the 'bufsize'-byte buffer 'reasonbuf'.
+ * 'bufsize' should be at least OFP_PORT_REASON_BUFSIZE. */
+#define OFP_PORT_REASON_BUFSIZE (INT_STRLEN(int) + 1)
 static const char *
-ofp_port_reason_to_string(enum ofp_port_reason reason)
+ofp_port_reason_to_string(enum ofp_port_reason reason,
+                          char *reasonbuf, size_t bufsize)
 {
-    static char s[32];
-
     switch (reason) {
     case OFPPR_ADD:
         return "add";
@@ -1631,8 +1884,8 @@ ofp_port_reason_to_string(enum ofp_port_reason reason)
         return "modify";
 
     default:
-        sprintf(s, "%d", (int) reason);
-        return s;
+        snprintf(reasonbuf, bufsize, "%d", (int) reason);
+        return reasonbuf;
     }
 }
 
@@ -1650,8 +1903,12 @@ ofp_print_nxt_set_async_config(struct ds *string,
         ds_put_cstr(string, "       PACKET_IN:");
         for (j = 0; j < 32; j++) {
             if (nac->packet_in_mask[i] & htonl(1u << j)) {
-                ds_put_format(string, " %s",
-                              ofputil_packet_in_reason_to_string(j));
+                char reasonbuf[OFPUTIL_PACKET_IN_REASON_BUFSIZE];
+                const char *reason;
+
+                reason = ofputil_packet_in_reason_to_string(j, reasonbuf,
+                                                            sizeof reasonbuf);
+                ds_put_format(string, " %s", reason);
             }
         }
         if (!nac->packet_in_mask[i]) {
@@ -1662,7 +1919,12 @@ ofp_print_nxt_set_async_config(struct ds *string,
         ds_put_cstr(string, "     PORT_STATUS:");
         for (j = 0; j < 32; j++) {
             if (nac->port_status_mask[i] & htonl(1u << j)) {
-                ds_put_format(string, " %s", ofp_port_reason_to_string(j));
+                char reasonbuf[OFP_PORT_REASON_BUFSIZE];
+                const char *reason;
+
+                reason = ofp_port_reason_to_string(j, reasonbuf,
+                                                   sizeof reasonbuf);
+                ds_put_format(string, " %s", reason);
             }
         }
         if (!nac->port_status_mask[i]) {
@@ -1673,8 +1935,12 @@ ofp_print_nxt_set_async_config(struct ds *string,
         ds_put_cstr(string, "    FLOW_REMOVED:");
         for (j = 0; j < 32; j++) {
             if (nac->flow_removed_mask[i] & htonl(1u << j)) {
-                ds_put_format(string, " %s",
-                              ofp_flow_removed_reason_to_string(j));
+                char reasonbuf[OFP_FLOW_REMOVED_REASON_BUFSIZE];
+                const char *reason;
+
+                reason = ofp_flow_removed_reason_to_string(j, reasonbuf,
+                                                           sizeof reasonbuf);
+                ds_put_format(string, " %s", reason);
             }
         }
         if (!nac->flow_removed_mask[i]) {
@@ -1765,6 +2031,7 @@ ofp_print_nxst_flow_monitor_reply(struct ds *string,
     ofpbuf_use_const(&b, oh, ntohs(oh->length));
     ofpbuf_use_stub(&ofpacts, ofpacts_stub, sizeof ofpacts_stub);
     for (;;) {
+        char reasonbuf[OFP_FLOW_REMOVED_REASON_BUFSIZE];
         struct ofputil_flow_update update;
         struct match match;
         int retval;
@@ -1787,7 +2054,9 @@ ofp_print_nxst_flow_monitor_reply(struct ds *string,
 
         case NXFME_DELETED:
             ds_put_format(string, "DELETED reason=%s",
-                          ofp_flow_removed_reason_to_string(update.reason));
+                          ofp_flow_removed_reason_to_string(update.reason,
+                                                            reasonbuf,
+                                                            sizeof reasonbuf));
             break;
 
         case NXFME_MODIFIED:
@@ -1873,19 +2142,12 @@ ofp_to_string__(const struct ofp_header *oh, enum ofpraw raw,
     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);
@@ -1943,6 +2205,10 @@ ofp_to_string__(const struct ofp_header *oh, enum ofpraw raw,
         ofp_print_port_mod(string, oh);
         break;
 
+    case OFPTYPE_METER_MOD:
+        ofp_print_meter_mod(string, oh);
+        break;
+
     case OFPTYPE_BARRIER_REQUEST:
     case OFPTYPE_BARRIER_REPLY:
         break;
@@ -1952,8 +2218,30 @@ ofp_to_string__(const struct ofp_header *oh, enum ofpraw raw,
         ofp_print_role_message(string, oh);
         break;
 
+    case OFPTYPE_METER_REQUEST:
+    case OFPTYPE_METER_CONFIG_REQUEST:
+        ofp_print_stats_request(string, oh);
+        ofp_print_meter_stats_request(string, oh);
+        break;
+
+    case OFPTYPE_METER_REPLY:
+        ofp_print_stats_reply(string, oh);
+        ofp_print_meter_stats_reply(string, oh);
+        break;
+
+    case OFPTYPE_METER_CONFIG_REPLY:
+        ofp_print_stats_reply(string, oh);
+        ofp_print_meter_config_reply(string, oh);
+        break;
+
+    case OFPTYPE_METER_FEATURES_REPLY:
+        ofp_print_stats_reply(string, oh);
+        ofp_print_meter_features_reply(string, oh);
+        break;
+
     case OFPTYPE_DESC_STATS_REQUEST:
     case OFPTYPE_PORT_DESC_STATS_REQUEST:
+    case OFPTYPE_METER_FEATURES_REQUEST:
         ofp_print_stats_request(string, oh);
         break;
 
index 2ca0077..aa4009d 100644 (file)
@@ -90,7 +90,7 @@ ofputil_wildcard_from_ofpfw10(uint32_t ofpfw, struct flow_wildcards *wc)
     flow_wildcards_init_catchall(wc);
 
     if (!(ofpfw & OFPFW10_IN_PORT)) {
-        wc->masks.in_port = UINT16_MAX;
+        wc->masks.in_port.ofp_port = u16_to_ofp(UINT16_MAX);
     }
 
     if (!(ofpfw & OFPFW10_NW_TOS)) {
@@ -145,7 +145,7 @@ ofputil_match_from_ofp10_match(const struct ofp10_match *ofmatch,
     /* Initialize most of match->flow. */
     match->flow.nw_src = ofmatch->nw_src;
     match->flow.nw_dst = ofmatch->nw_dst;
-    match->flow.in_port = ntohs(ofmatch->in_port);
+    match->flow.in_port.ofp_port = u16_to_ofp(ntohs(ofmatch->in_port));
     match->flow.dl_type = ofputil_dl_type_from_openflow(ofmatch->dl_type);
     match->flow.tp_src = ofmatch->tp_src;
     match->flow.tp_dst = ofmatch->tp_dst;
@@ -190,7 +190,7 @@ ofputil_match_to_ofp10_match(const struct match *match,
 
     /* Figure out most OpenFlow wildcards. */
     ofpfw = 0;
-    if (!wc->masks.in_port) {
+    if (!wc->masks.in_port.ofp_port) {
         ofpfw |= OFPFW10_IN_PORT;
     }
     if (!wc->masks.dl_type) {
@@ -244,7 +244,7 @@ ofputil_match_to_ofp10_match(const struct match *match,
 
     /* Compose most of the match structure. */
     ofmatch->wildcards = htonl(ofpfw);
-    ofmatch->in_port = htons(match->flow.in_port);
+    ofmatch->in_port = htons(ofp_to_u16(match->flow.in_port.ofp_port));
     memcpy(ofmatch->dl_src, match->flow.dl_src, ETH_ADDR_LEN);
     memcpy(ofmatch->dl_dst, match->flow.dl_dst, ETH_ADDR_LEN);
     ofmatch->dl_type = ofputil_dl_type_to_openflow(match->flow.dl_type);
@@ -311,7 +311,7 @@ ofputil_match_from_ofp11_match(const struct ofp11_match *ofmatch,
     match_init_catchall(match);
 
     if (!(wc & OFPFW11_IN_PORT)) {
-        uint16_t ofp_port;
+        ofp_port_t ofp_port;
         enum ofperr error;
 
         error = ofputil_port_from_ofp11(ofmatch->in_port, &ofp_port);
@@ -466,10 +466,10 @@ ofputil_match_to_ofp11_match(const struct match *match,
     ofmatch->omh.type = htons(OFPMT_STANDARD);
     ofmatch->omh.length = htons(OFPMT11_STANDARD_LENGTH);
 
-    if (!match->wc.masks.in_port) {
+    if (!match->wc.masks.in_port.ofp_port) {
         wc |= OFPFW11_IN_PORT;
     } else {
-        ofmatch->in_port = ofputil_port_to_ofp11(match->flow.in_port);
+        ofmatch->in_port = ofputil_port_to_ofp11(match->flow.in_port.ofp_port);
     }
 
     memcpy(ofmatch->dl_src, match->flow.dl_src, ETH_ADDR_LEN);
@@ -1086,8 +1086,19 @@ ofputil_usable_protocols(const struct match *match)
             | OFPUTIL_P_OF13_OXM;
     }
 
-    /* NXM and OXM support matching IPv6 traffic. */
-    if (match->flow.dl_type == htons(ETH_TYPE_IPV6)) {
+    /* NXM and OXM support matching L3 and L4 fields within IPv6.
+     *
+     * (arp_sha, arp_tha, nw_frag, and nw_ttl are covered elsewhere so they
+     * don't need to be included in this test too.) */
+    if (match->flow.dl_type == htons(ETH_TYPE_IPV6)
+        && (!ipv6_mask_is_any(&wc->masks.ipv6_src)
+            || !ipv6_mask_is_any(&wc->masks.ipv6_dst)
+            || !ipv6_mask_is_any(&wc->masks.nd_target)
+            || wc->masks.ipv6_label
+            || wc->masks.tp_src
+            || wc->masks.tp_dst
+            || wc->masks.nw_proto
+            || wc->masks.nw_tos)) {
         return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM
             | OFPUTIL_P_OF13_OXM;
     }
@@ -1112,12 +1123,6 @@ ofputil_usable_protocols(const struct match *match)
             | OFPUTIL_P_OF13_OXM;
     }
 
-    /* NXM and OXM support matching IPv6 flow label. */
-    if (wc->masks.ipv6_label) {
-        return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM
-            | OFPUTIL_P_OF13_OXM;
-    }
-
     /* NXM and OXM support matching IP ECN bits. */
     if (wc->masks.nw_tos & IP_ECN_MASK) {
         return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM
@@ -1566,7 +1571,7 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
             fm->idle_timeout = ntohs(ofm->idle_timeout);
             fm->hard_timeout = ntohs(ofm->hard_timeout);
             fm->buffer_id = ntohl(ofm->buffer_id);
-            fm->out_port = ntohs(ofm->out_port);
+            fm->out_port = u16_to_ofp(ntohs(ofm->out_port));
             fm->flags = ntohs(ofm->flags);
         } else if (raw == OFPRAW_NXT_FLOW_MOD) {
             /* Nicira extended flow_mod. */
@@ -1597,7 +1602,7 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
             fm->idle_timeout = ntohs(nfm->idle_timeout);
             fm->hard_timeout = ntohs(nfm->hard_timeout);
             fm->buffer_id = ntohl(nfm->buffer_id);
-            fm->out_port = ntohs(nfm->out_port);
+            fm->out_port = u16_to_ofp(ntohs(nfm->out_port));
             fm->flags = ntohs(nfm->flags);
         } else {
             NOT_REACHED();
@@ -1630,6 +1635,370 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
     return 0;
 }
 
+static enum ofperr
+ofputil_pull_bands(struct ofpbuf *msg, size_t len, uint16_t *n_bands,
+                   struct ofpbuf *bands)
+{
+    const struct ofp13_meter_band_header *ombh;
+    struct ofputil_meter_band *mb;
+    uint16_t n = 0;
+
+    ombh = ofpbuf_try_pull(msg, len);
+    if (!ombh) {
+        return OFPERR_OFPBRC_BAD_LEN;
+    }
+
+    while (len >= sizeof (struct ofp13_meter_band_drop)) {
+        size_t ombh_len = ntohs(ombh->len);
+        /* All supported band types have the same length. */
+        if (ombh_len != sizeof (struct ofp13_meter_band_drop)) {
+            return OFPERR_OFPBRC_BAD_LEN;
+        }
+        mb = ofpbuf_put_uninit(bands, sizeof *mb);
+        mb->type = ntohs(ombh->type);
+        mb->rate = ntohl(ombh->rate);
+        mb->burst_size = ntohl(ombh->burst_size);
+        mb->prec_level = (mb->type == OFPMBT13_DSCP_REMARK) ?
+            ((struct ofp13_meter_band_dscp_remark *)ombh)->prec_level : 0;
+        n++;
+        len -= ombh_len;
+        ombh = (struct ofp13_meter_band_header *)(((char *)ombh) + ombh_len);
+    }
+    if (len) {
+        return OFPERR_OFPBRC_BAD_LEN;
+    }
+    *n_bands = n;
+    return 0;
+}
+
+enum ofperr
+ofputil_decode_meter_mod(const struct ofp_header *oh,
+                         struct ofputil_meter_mod *mm,
+                         struct ofpbuf *bands)
+{
+    const struct ofp13_meter_mod *omm;
+    struct ofpbuf b;
+
+    ofpbuf_use_const(&b, oh, ntohs(oh->length));
+    ofpraw_pull_assert(&b);
+    omm = ofpbuf_pull(&b, sizeof *omm);
+
+    /* Translate the message. */
+    mm->command = ntohs(omm->command);
+    mm->meter.meter_id = ntohl(omm->meter_id);
+
+    if (mm->command == OFPMC13_DELETE) {
+        mm->meter.flags = 0;
+        mm->meter.n_bands = 0;
+        mm->meter.bands = NULL;
+    } else {
+        enum ofperr error;
+
+        mm->meter.flags = ntohs(omm->flags);
+        mm->meter.bands = bands->data;
+
+        error = ofputil_pull_bands(&b, b.size, &mm->meter.n_bands, bands);
+        if (error) {
+            return error;
+        }
+    }
+    return 0;
+}
+
+void
+ofputil_decode_meter_request(const struct ofp_header *oh, uint32_t *meter_id)
+{
+    const struct ofp13_meter_multipart_request *omr = ofpmsg_body(oh);
+    *meter_id = ntohl(omr->meter_id);
+}
+
+struct ofpbuf *
+ofputil_encode_meter_request(enum ofp_version ofp_version,
+                             enum ofputil_meter_request_type type,
+                             uint32_t meter_id)
+{
+    struct ofpbuf *msg;
+
+    enum ofpraw raw;
+
+    switch (type) {
+    case OFPUTIL_METER_CONFIG:
+        raw = OFPRAW_OFPST13_METER_CONFIG_REQUEST;
+        break;
+    case OFPUTIL_METER_STATS:
+        raw = OFPRAW_OFPST13_METER_REQUEST;
+        break;
+    default:
+    case OFPUTIL_METER_FEATURES:
+        raw = OFPRAW_OFPST13_METER_FEATURES_REQUEST;
+        break;
+    }
+
+    msg = ofpraw_alloc(raw, ofp_version, 0);
+
+    if (type != OFPUTIL_METER_FEATURES) {
+        struct ofp13_meter_multipart_request *omr;
+        omr = ofpbuf_put_zeros(msg, sizeof *omr);
+        omr->meter_id = htonl(meter_id);
+    }
+    return msg;
+}
+
+static void
+ofputil_put_bands(uint16_t n_bands, const struct ofputil_meter_band *mb,
+                  struct ofpbuf *msg)
+{
+    uint16_t n = 0;
+
+    for (n = 0; n < n_bands; ++n) {
+        /* Currently all band types have same size. */
+        struct ofp13_meter_band_dscp_remark *ombh;
+        size_t ombh_len = sizeof *ombh;
+
+        ombh = ofpbuf_put_zeros(msg, ombh_len);
+
+        ombh->type = htons(mb->type);
+        ombh->len = htons(ombh_len);
+        ombh->rate = htonl(mb->rate);
+        ombh->burst_size = htonl(mb->burst_size);
+        ombh->prec_level = mb->prec_level;
+
+        mb++;
+    }
+}
+
+/* Encode a meter stat for 'mc' and append it to 'replies'. */
+void
+ofputil_append_meter_config(struct list *replies,
+                            const struct ofputil_meter_config *mc)
+{
+    struct ofpbuf *msg = ofpbuf_from_list(list_back(replies));
+    size_t start_ofs = msg->size;
+    struct ofp13_meter_config *reply = ofpbuf_put_uninit(msg, sizeof *reply);
+    reply->flags = htons(mc->flags);
+    reply->meter_id = htonl(mc->meter_id);
+
+    ofputil_put_bands(mc->n_bands, mc->bands, msg);
+
+    reply->length = htons(msg->size - start_ofs);
+
+    ofpmp_postappend(replies, start_ofs);
+}
+
+/* Encode a meter stat for 'ms' and append it to 'replies'. */
+void
+ofputil_append_meter_stats(struct list *replies,
+                           const struct ofputil_meter_stats *ms)
+{
+    struct ofp13_meter_stats *reply;
+    uint16_t n = 0;
+    uint16_t len;
+
+    len = sizeof *reply + ms->n_bands * sizeof(struct ofp13_meter_band_stats);
+    reply = ofpmp_append(replies, len);
+
+    reply->meter_id = htonl(ms->meter_id);
+    reply->len = htons(len);
+    memset(reply->pad, 0, sizeof reply->pad);
+    reply->flow_count = htonl(ms->flow_count);
+    reply->packet_in_count = htonll(ms->packet_in_count);
+    reply->byte_in_count = htonll(ms->byte_in_count);
+    reply->duration_sec = htonl(ms->duration_sec);
+    reply->duration_nsec = htonl(ms->duration_nsec);
+
+    for (n = 0; n < ms->n_bands; ++n) {
+        const struct ofputil_meter_band_stats *src = &ms->bands[n];
+        struct ofp13_meter_band_stats *dst = &reply->band_stats[n];
+
+        dst->packet_band_count = htonll(src->packet_count);
+        dst->byte_band_count = htonll(src->byte_count);
+    }
+}
+
+/* Converts an OFPMP_METER_CONFIG reply in 'msg' into an abstract
+ * ofputil_meter_config in 'mc', with mc->bands pointing to bands decoded into
+ * 'bands'.  The caller must have initialized 'bands' and retains ownership of
+ * it across the call.
+ *
+ * Multiple OFPST13_METER_CONFIG replies can be packed into a single OpenFlow
+ * message.  Calling this function multiple times for a single 'msg' iterates
+ * through the replies.  'bands' is cleared for each reply.
+ *
+ * Returns 0 if successful, EOF if no replies were left in this 'msg',
+ * otherwise a positive errno value. */
+int
+ofputil_decode_meter_config(struct ofpbuf *msg,
+                            struct ofputil_meter_config *mc,
+                            struct ofpbuf *bands)
+{
+    const struct ofp13_meter_config *omc;
+    enum ofperr err;
+
+    /* Pull OpenFlow headers for the first call. */
+    if (!msg->l2) {
+        ofpraw_pull_assert(msg);
+    }
+
+    if (!msg->size) {
+        return EOF;
+    }
+
+    omc = ofpbuf_try_pull(msg, sizeof *omc);
+    if (!omc) {
+        VLOG_WARN_RL(&bad_ofmsg_rl,
+                     "OFPMP_METER_CONFIG reply has %zu leftover bytes at end",
+                     msg->size);
+        return OFPERR_OFPBRC_BAD_LEN;
+    }
+
+    ofpbuf_clear(bands);
+    err = ofputil_pull_bands(msg, ntohs(omc->length) - sizeof *omc,
+                             &mc->n_bands, bands);
+    if (err) {
+        return err;
+    }
+    mc->meter_id = ntohl(omc->meter_id);
+    mc->flags = ntohs(omc->flags);
+    mc->bands = bands->data;
+
+    return 0;
+}
+
+static enum ofperr
+ofputil_pull_band_stats(struct ofpbuf *msg, size_t len, uint16_t *n_bands,
+                        struct ofpbuf *bands)
+{
+    const struct ofp13_meter_band_stats *ombs;
+    struct ofputil_meter_band_stats *mbs;
+    uint16_t n, i;
+
+    ombs = ofpbuf_try_pull(msg, len);
+    if (!ombs) {
+        return OFPERR_OFPBRC_BAD_LEN;
+    }
+
+    n = len / sizeof *ombs;
+    if (len != n * sizeof *ombs) {
+        return OFPERR_OFPBRC_BAD_LEN;
+    }
+
+    mbs = ofpbuf_put_uninit(bands, len);
+
+    for (i = 0; i < n; ++i) {
+        mbs[i].packet_count = ntohll(ombs[i].packet_band_count);
+        mbs[i].byte_count = ntohll(ombs[i].byte_band_count);
+    }
+    *n_bands = n;
+    return 0;
+}
+
+/* Converts an OFPMP_METER reply in 'msg' into an abstract
+ * ofputil_meter_stats in 'ms', with ms->bands pointing to band stats
+ * decoded into 'bands'.
+ *
+ * Multiple OFPMP_METER replies can be packed into a single OpenFlow
+ * message.  Calling this function multiple times for a single 'msg' iterates
+ * through the replies.  'bands' is cleared for each reply.
+ *
+ * Returns 0 if successful, EOF if no replies were left in this 'msg',
+ * otherwise a positive errno value. */
+int
+ofputil_decode_meter_stats(struct ofpbuf *msg,
+                           struct ofputil_meter_stats *ms,
+                           struct ofpbuf *bands)
+{
+    const struct ofp13_meter_stats *oms;
+    enum ofperr err;
+
+    /* Pull OpenFlow headers for the first call. */
+    if (!msg->l2) {
+        ofpraw_pull_assert(msg);
+    }
+
+    if (!msg->size) {
+        return EOF;
+    }
+
+    oms = ofpbuf_try_pull(msg, sizeof *oms);
+    if (!oms) {
+        VLOG_WARN_RL(&bad_ofmsg_rl,
+                     "OFPMP_METER reply has %zu leftover bytes at end",
+                     msg->size);
+        return OFPERR_OFPBRC_BAD_LEN;
+    }
+
+    ofpbuf_clear(bands);
+    err = ofputil_pull_band_stats(msg, ntohs(oms->len) - sizeof *oms,
+                                  &ms->n_bands, bands);
+    if (err) {
+        return err;
+    }
+    ms->meter_id = ntohl(oms->meter_id);
+    ms->flow_count = ntohl(oms->flow_count);
+    ms->packet_in_count = ntohll(oms->packet_in_count);
+    ms->byte_in_count = ntohll(oms->byte_in_count);
+    ms->duration_sec = ntohl(oms->duration_sec);
+    ms->duration_nsec = ntohl(oms->duration_nsec);
+    ms->bands = bands->data;
+
+    return 0;
+}
+
+void
+ofputil_decode_meter_features(const struct ofp_header *oh,
+                              struct ofputil_meter_features *mf)
+{
+    const struct ofp13_meter_features *omf = ofpmsg_body(oh);
+
+    mf->max_meters = ntohl(omf->max_meter);
+    mf->band_types = ntohl(omf->band_types);
+    mf->capabilities = ntohl(omf->capabilities);
+    mf->max_bands = omf->max_bands;
+    mf->max_color = omf->max_color;
+}
+
+struct ofpbuf *
+ofputil_encode_meter_features_reply(const struct ofputil_meter_features *mf,
+                                    const struct ofp_header *request)
+{
+    struct ofpbuf *reply;
+    struct ofp13_meter_features *omf;
+
+    reply = ofpraw_alloc_stats_reply(request, 0);
+    omf = ofpbuf_put_zeros(reply, sizeof *omf);
+
+    omf->max_meter = htonl(mf->max_meters);
+    omf->band_types = htonl(mf->band_types);
+    omf->capabilities = htonl(mf->capabilities);
+    omf->max_bands = mf->max_bands;
+    omf->max_color = mf->max_color;
+
+    return reply;
+}
+
+struct ofpbuf *
+ofputil_encode_meter_mod(enum ofp_version ofp_version,
+                         const struct ofputil_meter_mod *mm)
+{
+    struct ofpbuf *msg;
+
+    struct ofp13_meter_mod *omm;
+
+    msg = ofpraw_alloc(OFPRAW_OFPT13_METER_MOD, ofp_version,
+                       NXM_TYPICAL_LEN + mm->meter.n_bands * 16);
+    omm = ofpbuf_put_zeros(msg, sizeof *omm);
+    omm->command = htons(mm->command);
+    if (mm->command != OFPMC13_DELETE) {
+        omm->flags = htons(mm->meter.flags);
+    }
+    omm->meter_id = htonl(mm->meter.meter_id);
+
+    ofputil_put_bands(mm->meter.n_bands, mm->meter.bands, msg);
+
+    ofpmsg_update_length(msg);
+    return msg;
+}
+
 static ovs_be16
 ofputil_tid_command(const struct ofputil_flow_mod *fm,
                     enum ofputil_protocol protocol)
@@ -1652,7 +2021,7 @@ ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm,
     case OFPUTIL_P_OF13_OXM: {
         struct ofp11_flow_mod *ofm;
 
-        msg = ofpraw_alloc(OFPRAW_OFPT11_FLOW_MOD, 
+        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);
@@ -1690,7 +2059,7 @@ ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm,
         ofm->hard_timeout = htons(fm->hard_timeout);
         ofm->priority = htons(fm->priority);
         ofm->buffer_id = htonl(fm->buffer_id);
-        ofm->out_port = htons(fm->out_port);
+        ofm->out_port = htons(ofp_to_u16(fm->out_port));
         ofm->flags = htons(fm->flags);
         ofpacts_put_openflow10(fm->ofpacts, fm->ofpacts_len, msg);
         break;
@@ -1712,7 +2081,7 @@ ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm,
         nfm->hard_timeout = htons(fm->hard_timeout);
         nfm->priority = htons(fm->priority);
         nfm->buffer_id = htonl(fm->buffer_id);
-        nfm->out_port = htons(fm->out_port);
+        nfm->out_port = htons(ofp_to_u16(fm->out_port));
         nfm->flags = htons(fm->flags);
         nfm->match_len = htons(match_len);
         ofpacts_put_openflow10(fm->ofpacts, fm->ofpacts_len, msg);
@@ -1765,7 +2134,7 @@ ofputil_decode_ofpst10_flow_request(struct ofputil_flow_stats_request *fsr,
 {
     fsr->aggregate = aggregate;
     ofputil_match_from_ofp10_match(&ofsr->match, &fsr->match);
-    fsr->out_port = ntohs(ofsr->out_port);
+    fsr->out_port = u16_to_ofp(ntohs(ofsr->out_port));
     fsr->table_id = ofsr->table_id;
     fsr->cookie = fsr->cookie_mask = htonll(0);
 
@@ -1817,7 +2186,7 @@ ofputil_decode_nxst_flow_request(struct ofputil_flow_stats_request *fsr,
     }
 
     fsr->aggregate = aggregate;
-    fsr->out_port = ntohs(nfsr->out_port);
+    fsr->out_port = u16_to_ofp(ntohs(nfsr->out_port));
     fsr->table_id = nfsr->table_id;
 
     return 0;
@@ -1901,7 +2270,7 @@ ofputil_encode_flow_stats_request(const struct ofputil_flow_stats_request *fsr,
         ofsr = ofpbuf_put_zeros(msg, sizeof *ofsr);
         ofputil_match_to_ofp10_match(&fsr->match, &ofsr->match);
         ofsr->table_id = fsr->table_id;
-        ofsr->out_port = htons(fsr->out_port);
+        ofsr->out_port = htons(ofp_to_u16(fsr->out_port));
         break;
     }
 
@@ -1919,7 +2288,7 @@ ofputil_encode_flow_stats_request(const struct ofputil_flow_stats_request *fsr,
                                  fsr->cookie, fsr->cookie_mask);
 
         nfsr = msg->l3;
-        nfsr->out_port = htons(fsr->out_port);
+        nfsr->out_port = htons(ofp_to_u16(fsr->out_port));
         nfsr->match_len = htons(match_len);
         nfsr->table_id = fsr->table_id;
         break;
@@ -2441,7 +2810,7 @@ ofputil_decode_packet_in_finish(struct ofputil_packet_in *pin,
     pin->packet = b->data;
     pin->packet_len = b->size;
 
-    pin->fmd.in_port = match->flow.in_port;
+    pin->fmd.in_port = match->flow.in_port.ofp_port;
     pin->fmd.tun_id = match->flow.tunnel.tun_id;
     pin->fmd.tun_src = match->flow.tunnel.ip_src;
     pin->fmd.tun_dst = match->flow.tunnel.ip_dst;
@@ -2500,7 +2869,7 @@ ofputil_decode_packet_in(struct ofputil_packet_in *pin,
         pin->packet = opi->data;
         pin->packet_len = b.size;
 
-        pin->fmd.in_port = ntohs(opi->in_port);
+        pin->fmd.in_port = u16_to_ofp(ntohs(opi->in_port));
         pin->reason = opi->reason;
         pin->buffer_id = ntohl(opi->buffer_id);
         pin->total_len = ntohs(opi->total_len);
@@ -2618,7 +2987,7 @@ ofputil_encode_packet_in(const struct ofputil_packet_in *pin,
                                   htonl(0), send_len);
         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->in_port = htons(ofp_to_u16(pin->fmd.in_port));
         opi->reason = pin->reason;
         opi->buffer_id = htonl(pin->buffer_id);
 
@@ -2654,11 +3023,13 @@ ofputil_encode_packet_in(const struct ofputil_packet_in *pin,
     return packet;
 }
 
+/* Returns a string form of 'reason'.  The return value is either a statically
+ * allocated constant string or the 'bufsize'-byte buffer 'reasonbuf'.
+ * 'bufsize' should be at least OFPUTIL_PACKET_IN_REASON_BUFSIZE. */
 const char *
-ofputil_packet_in_reason_to_string(enum ofp_packet_in_reason reason)
+ofputil_packet_in_reason_to_string(enum ofp_packet_in_reason reason,
+                                   char *reasonbuf, size_t bufsize)
 {
-    static char s[INT_STRLEN(int) + 1];
-
     switch (reason) {
     case OFPR_NO_MATCH:
         return "no_match";
@@ -2669,8 +3040,8 @@ ofputil_packet_in_reason_to_string(enum ofp_packet_in_reason reason)
 
     case OFPR_N_REASONS:
     default:
-        sprintf(s, "%d", (int) reason);
-        return s;
+        snprintf(reasonbuf, bufsize, "%d", (int) reason);
+        return reasonbuf;
     }
 }
 
@@ -2681,7 +3052,12 @@ ofputil_packet_in_reason_from_string(const char *s,
     int i;
 
     for (i = 0; i < OFPR_N_REASONS; i++) {
-        if (!strcasecmp(s, ofputil_packet_in_reason_to_string(i))) {
+        char reasonbuf[OFPUTIL_PACKET_IN_REASON_BUFSIZE];
+        const char *reason_s;
+
+        reason_s = ofputil_packet_in_reason_to_string(i, reasonbuf,
+                                                      sizeof reasonbuf);
+        if (!strcasecmp(s, reason_s)) {
             *reason = i;
             return true;
         }
@@ -2728,7 +3104,7 @@ ofputil_decode_packet_out(struct ofputil_packet_out *po,
         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);
+        po->in_port = u16_to_ofp(ntohs(opo->in_port));
 
         error = ofpacts_pull_openflow10(&b, ntohs(opo->actions_len), ofpacts);
         if (error) {
@@ -2738,7 +3114,8 @@ ofputil_decode_packet_out(struct ofputil_packet_out *po,
         NOT_REACHED();
     }
 
-    if (po->in_port >= OFPP_MAX && po->in_port != OFPP_LOCAL
+    if (ofp_to_u16(po->in_port) >= ofp_to_u16(OFPP_MAX)
+        && po->in_port != OFPP_LOCAL
         && po->in_port != OFPP_NONE && po->in_port != OFPP_CONTROLLER) {
         VLOG_WARN_RL(&bad_ofmsg_rl, "packet-out has bad input port %#"PRIx16,
                      po->in_port);
@@ -2825,7 +3202,7 @@ ofputil_decode_ofp10_phy_port(struct ofputil_phy_port *pp,
 {
     memset(pp, 0, sizeof *pp);
 
-    pp->port_no = ntohs(opp->port_no);
+    pp->port_no = u16_to_ofp(ntohs(opp->port_no));
     memcpy(pp->hw_addr, opp->hw_addr, OFP_ETH_ALEN);
     ovs_strlcpy(pp->name, opp->name, OFP_MAX_PORT_NAME_LEN);
 
@@ -2893,7 +3270,7 @@ ofputil_encode_ofp10_phy_port(const struct ofputil_phy_port *pp,
 {
     memset(opp, 0, sizeof *opp);
 
-    opp->port_no = htons(pp->port_no);
+    opp->port_no = htons(ofp_to_u16(pp->port_no));
     memcpy(opp->hw_addr, pp->hw_addr, ETH_ADDR_LEN);
     ovs_strlcpy(opp->name, pp->name, OFP_MAX_PORT_NAME_LEN);
 
@@ -3303,7 +3680,7 @@ ofputil_decode_port_mod(const struct ofp_header *oh,
     if (raw == OFPRAW_OFPT10_PORT_MOD) {
         const struct ofp10_port_mod *opm = b.data;
 
-        pm->port_no = ntohs(opm->port_no);
+        pm->port_no = u16_to_ofp(ntohs(opm->port_no));
         memcpy(pm->hw_addr, opm->hw_addr, ETH_ADDR_LEN);
         pm->config = ntohl(opm->config) & OFPPC10_ALL;
         pm->mask = ntohl(opm->mask) & OFPPC10_ALL;
@@ -3345,7 +3722,7 @@ ofputil_encode_port_mod(const struct ofputil_port_mod *pm,
 
         b = ofpraw_alloc(OFPRAW_OFPT10_PORT_MOD, ofp_version, 0);
         opm = ofpbuf_put_zeros(b, sizeof *opm);
-        opm->port_no = htons(pm->port_no);
+        opm->port_no = htons(ofp_to_u16(pm->port_no));
         memcpy(opm->hw_addr, pm->hw_addr, ETH_ADDR_LEN);
         opm->config = htonl(pm->config & OFPPC10_ALL);
         opm->mask = htonl(pm->mask & OFPPC10_ALL);
@@ -3681,7 +4058,7 @@ ofputil_decode_flow_monitor_request(struct ofputil_flow_monitor_request *rq,
 
     rq->id = ntohl(nfmr->id);
     rq->flags = flags;
-    rq->out_port = ntohs(nfmr->out_port);
+    rq->out_port = u16_to_ofp(ntohs(nfmr->out_port));
     rq->table_id = nfmr->table_id;
 
     return nx_pull_match(msg, ntohs(nfmr->match_len), &rq->match, NULL, NULL);
@@ -3706,7 +4083,7 @@ ofputil_append_flow_monitor_request(
     nfmr = ofpbuf_at_assert(msg, start_ofs, sizeof *nfmr);
     nfmr->id = htonl(rq->id);
     nfmr->flags = htons(rq->flags);
-    nfmr->out_port = htons(rq->out_port);
+    nfmr->out_port = htons(ofp_to_u16(rq->out_port));
     nfmr->match_len = htons(match_len);
     nfmr->table_id = rq->table_id;
 }
@@ -3914,7 +4291,7 @@ ofputil_encode_packet_out(const struct ofputil_packet_out *po,
 
         opo = msg->l3;
         opo->buffer_id = htonl(po->buffer_id);
-        opo->in_port = htons(po->in_port);
+        opo->in_port = htons(ofp_to_u16(po->in_port));
         opo->actions_len = htons(msg->size - actions_ofs);
         break;
     }
@@ -4029,25 +4406,26 @@ ofputil_frag_handling_from_string(const char *s, enum ofp_config_flags *flags)
 /* Converts the OpenFlow 1.1+ port number 'ofp11_port' into an OpenFlow 1.0
  * port number and stores the latter in '*ofp10_port', for the purpose of
  * decoding OpenFlow 1.1+ protocol messages.  Returns 0 if successful,
- * otherwise an OFPERR_* number.
+ * otherwise an OFPERR_* number.  On error, stores OFPP_NONE in '*ofp10_port'.
  *
  * See the definition of OFP11_MAX for an explanation of the mapping. */
 enum ofperr
-ofputil_port_from_ofp11(ovs_be32 ofp11_port, uint16_t *ofp10_port)
+ofputil_port_from_ofp11(ovs_be32 ofp11_port, ofp_port_t *ofp10_port)
 {
     uint32_t ofp11_port_h = ntohl(ofp11_port);
 
-    if (ofp11_port_h < OFPP_MAX) {
-        *ofp10_port = ofp11_port_h;
+    if (ofp11_port_h < ofp_to_u16(OFPP_MAX)) {
+        *ofp10_port = u16_to_ofp(ofp11_port_h);
         return 0;
-    } else if (ofp11_port_h >= OFPP11_MAX) {
-        *ofp10_port = ofp11_port_h - OFPP11_OFFSET;
+    } else if (ofp11_port_h >= ofp11_to_u32(OFPP11_MAX)) {
+        *ofp10_port = u16_to_ofp(ofp11_port_h - OFPP11_OFFSET);
         return 0;
     } else {
+        *ofp10_port = OFPP_NONE;
         VLOG_WARN_RL(&bad_ofmsg_rl, "port %"PRIu32" is outside the supported "
                      "range 0 through %d or 0x%"PRIx32" through 0x%"PRIx32,
-                     ofp11_port_h, OFPP_MAX - 1,
-                     (uint32_t) OFPP11_MAX, UINT32_MAX);
+                     ofp11_port_h, ofp_to_u16(OFPP_MAX) - 1,
+                     ofp11_to_u32(OFPP11_MAX), UINT32_MAX);
         return OFPERR_OFPBAC_BAD_OUT_PORT;
     }
 }
@@ -4057,18 +4435,18 @@ ofputil_port_from_ofp11(ovs_be32 ofp11_port, uint16_t *ofp10_port)
  *
  * See the definition of OFP11_MAX for an explanation of the mapping. */
 ovs_be32
-ofputil_port_to_ofp11(uint16_t ofp10_port)
+ofputil_port_to_ofp11(ofp_port_t ofp10_port)
 {
-    return htonl(ofp10_port < OFPP_MAX
-                 ? ofp10_port
-                 : ofp10_port + OFPP11_OFFSET);
+    return htonl(ofp_to_u16(ofp10_port) < ofp_to_u16(OFPP_MAX)
+                 ? ofp_to_u16(ofp10_port)
+                 : ofp_to_u16(ofp10_port) + OFPP11_OFFSET);
 }
 
 /* Checks that 'port' is a valid output port for the OFPAT10_OUTPUT action, given
  * that the switch will never have more than 'max_ports' ports.  Returns 0 if
  * 'port' is valid, otherwise an OpenFlow return code. */
 enum ofperr
-ofputil_check_output_port(uint16_t port, int max_ports)
+ofputil_check_output_port(ofp_port_t port, ofp_port_t max_ports)
 {
     switch (port) {
     case OFPP_IN_PORT:
@@ -4082,7 +4460,7 @@ ofputil_check_output_port(uint16_t port, int max_ports)
         return 0;
 
     default:
-        if (port < max_ports) {
+        if (ofp_to_u16(port) < ofp_to_u16(max_ports)) {
             return 0;
         }
         return OFPERR_OFPBAC_BAD_OUT_PORT;
@@ -4118,46 +4496,40 @@ ofputil_check_output_port(uint16_t port, int max_ports)
  * of OpenFlow 1.1+ port numbers, mapping those port numbers into the 16-bit
  * range as described in include/openflow/openflow-1.1.h. */
 bool
-ofputil_port_from_string(const char *s, uint16_t *portp)
+ofputil_port_from_string(const char *s, ofp_port_t *portp)
 {
-    unsigned int port32;
+    uint32_t port32;
 
     *portp = 0;
     if (str_to_uint(s, 10, &port32)) {
-        if (port32 < OFPP_MAX) {
-            *portp = port32;
-            return true;
-        } else if (port32 < OFPP_FIRST_RESV) {
+        if (port32 < ofp_to_u16(OFPP_MAX)) {
+            /* Pass. */
+        } else if (port32 < ofp_to_u16(OFPP_FIRST_RESV)) {
             VLOG_WARN("port %u is a reserved OF1.0 port number that will "
                       "be translated to %u when talking to an OF1.1 or "
                       "later controller", port32, port32 + OFPP11_OFFSET);
-            *portp = port32;
-            return true;
-        } else if (port32 <= OFPP_LAST_RESV) {
-            struct ds msg;
-
-            ds_init(&msg);
-            ofputil_format_port(port32, &msg);
-            VLOG_WARN_ONCE("referring to port %s as %u is deprecated for "
-                           "compatibility with future versions of OpenFlow",
-                           ds_cstr(&msg), port32);
-            ds_destroy(&msg);
-
-            *portp = port32;
-            return true;
-        } else if (port32 < OFPP11_MAX) {
+        } else if (port32 <= ofp_to_u16(OFPP_LAST_RESV)) {
+            char name[OFP_MAX_PORT_NAME_LEN];
+
+            ofputil_port_to_string(u16_to_ofp(port32), name, sizeof name);
+            VLOG_WARN_ONCE("referring to port %s as %"PRIu32" is deprecated "
+                           "for compatibility with OpenFlow 1.1 and later",
+                           name, port32);
+        } else if (port32 < ofp11_to_u32(OFPP11_MAX)) {
             VLOG_WARN("port %u is outside the supported range 0 through "
                       "%"PRIx16" or 0x%x through 0x%"PRIx32, port32,
-                      UINT16_MAX, (unsigned int) OFPP11_MAX, UINT32_MAX);
+                      UINT16_MAX, ofp11_to_u32(OFPP11_MAX), UINT32_MAX);
             return false;
         } else {
-            *portp = port32 - OFPP11_OFFSET;
-            return true;
+            port32 -= OFPP11_OFFSET;
         }
+
+        *portp = u16_to_ofp(port32);
+        return true;
     } else {
         struct pair {
             const char *name;
-            uint16_t value;
+            ofp_port_t value;
         };
         static const struct pair pairs[] = {
 #define OFPUTIL_NAMED_PORT(NAME) {#NAME, OFPP_##NAME},
@@ -4180,20 +4552,34 @@ ofputil_port_from_string(const char *s, uint16_t *portp)
  * Most ports' string representation is just the port number, but for special
  * ports, e.g. OFPP_LOCAL, it is the name, e.g. "LOCAL". */
 void
-ofputil_format_port(uint16_t port, struct ds *s)
+ofputil_format_port(ofp_port_t port, struct ds *s)
 {
-    const char *name;
+    char name[OFP_MAX_PORT_NAME_LEN];
+
+    ofputil_port_to_string(port, name, sizeof name);
+    ds_put_cstr(s, name);
+}
 
+/* Puts in the 'bufsize' byte in 'namebuf' a null-terminated string
+ * representation of OpenFlow port number 'port'.  Most ports are represented
+ * as just the port number, but special ports, e.g. OFPP_LOCAL, are represented
+ * by name, e.g. "LOCAL". */
+void
+ofputil_port_to_string(ofp_port_t port,
+                       char namebuf[OFP_MAX_PORT_NAME_LEN], size_t bufsize)
+{
     switch (port) {
-#define OFPUTIL_NAMED_PORT(NAME) case OFPP_##NAME: name = #NAME; break;
+#define OFPUTIL_NAMED_PORT(NAME)                        \
+        case OFPP_##NAME:                               \
+            ovs_strlcpy(namebuf, #NAME, bufsize);       \
+            break;
         OFPUTIL_NAMED_PORTS
 #undef OFPUTIL_NAMED_PORT
 
     default:
-        ds_put_format(s, "%"PRIu16, port);
-        return;
+        snprintf(namebuf, bufsize, "%"PRIu16, port);
+        break;
     }
-    ds_put_cstr(s, name);
 }
 
 /* Given a buffer 'b' that contains an array of OpenFlow ports of type
@@ -4521,7 +4907,7 @@ ofputil_parse_key_value(char **stringp, char **keyp, char **valuep)
  * 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, uint16_t port)
+ofputil_encode_dump_ports_request(enum ofp_version ofp_version, ofp_port_t port)
 {
     struct ofpbuf *request;
 
@@ -4530,7 +4916,7 @@ ofputil_encode_dump_ports_request(enum ofp_version ofp_version, uint16_t port)
         struct ofp10_port_stats_request *req;
         request = ofpraw_alloc(OFPRAW_OFPST10_PORT_REQUEST, ofp_version, 0);
         req = ofpbuf_put_zeros(request, sizeof *req);
-        req->port_no = htons(port);
+        req->port_no = htons(ofp_to_u16(port));
         break;
     }
     case OFP11_VERSION:
@@ -4553,7 +4939,7 @@ static void
 ofputil_port_stats_to_ofp10(const struct ofputil_port_stats *ops,
                             struct ofp10_port_stats *ps10)
 {
-    ps10->port_no = htons(ops->port_no);
+    ps10->port_no = htons(ofp_to_u16(ops->port_no));
     memset(ps10->pad, 0, sizeof ps10->pad);
     put_32aligned_be64(&ps10->rx_packets, htonll(ops->stats.rx_packets));
     put_32aligned_be64(&ps10->tx_packets, htonll(ops->stats.tx_packets));
@@ -4594,11 +4980,8 @@ 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);
+    ps13->duration_sec = htonl(ops->duration_sec);
+    ps13->duration_nsec = htonl(ops->duration_nsec);
 }
 
 
@@ -4640,7 +5023,7 @@ ofputil_port_stats_from_ofp10(struct ofputil_port_stats *ops,
 {
     memset(ops, 0, sizeof *ops);
 
-    ops->port_no = ntohs(ps10->port_no);
+    ops->port_no = u16_to_ofp(ntohs(ps10->port_no));
     ops->stats.rx_packets = ntohll(get_32aligned_be64(&ps10->rx_packets));
     ops->stats.tx_packets = ntohll(get_32aligned_be64(&ps10->tx_packets));
     ops->stats.rx_bytes = ntohll(get_32aligned_be64(&ps10->rx_bytes));
@@ -4654,6 +5037,7 @@ ofputil_port_stats_from_ofp10(struct ofputil_port_stats *ops,
     ops->stats.rx_over_errors = ntohll(get_32aligned_be64(&ps10->rx_over_err));
     ops->stats.rx_crc_errors = ntohll(get_32aligned_be64(&ps10->rx_crc_err));
     ops->stats.collisions = ntohll(get_32aligned_be64(&ps10->collisions));
+    ops->duration_sec = ops->duration_nsec = UINT32_MAX;
 
     return 0;
 }
@@ -4682,6 +5066,7 @@ ofputil_port_stats_from_ofp11(struct ofputil_port_stats *ops,
     ops->stats.rx_over_errors = ntohll(ps11->rx_over_err);
     ops->stats.rx_crc_errors = ntohll(ps11->rx_crc_err);
     ops->stats.collisions = ntohll(ps11->collisions);
+    ops->duration_sec = ops->duration_nsec = UINT32_MAX;
 
     return 0;
 }
@@ -4690,13 +5075,11 @@ 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);
+    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? */
+        ops->duration_sec = ntohl(ps13->duration_sec);
+        ops->duration_nsec = ntohl(ps13->duration_nsec);
     }
-
     return error;
 }
 
@@ -4780,7 +5163,7 @@ ofputil_decode_port_stats(struct ofputil_port_stats *ps, struct ofpbuf *msg)
  * Returns 0 if successful, otherwise an OFPERR_* number. */
 enum ofperr
 ofputil_decode_port_stats_request(const struct ofp_header *request,
-                                  uint16_t *ofp10_port)
+                                  ofp_port_t *ofp10_port)
 {
     switch ((enum ofp_version)request->version) {
     case OFP13_VERSION:
@@ -4792,7 +5175,7 @@ ofputil_decode_port_stats_request(const struct ofp_header *request,
 
     case OFP10_VERSION: {
         const struct ofp10_port_stats_request *psr10 = ofpmsg_body(request);
-        *ofp10_port = ntohs(psr10->port_no);
+        *ofp10_port = u16_to_ofp(ntohs(psr10->port_no));
         return 0;
     }
 
@@ -4819,7 +5202,7 @@ ofputil_decode_queue_stats_request(const struct ofp_header *request,
     case OFP10_VERSION: {
         const struct ofp10_queue_stats_request *qsr10 = ofpmsg_body(request);
         oqsr->queue_id = ntohl(qsr10->queue_id);
-        oqsr->port_no = ntohs(qsr10->port_no);
+        oqsr->port_no = u16_to_ofp(ntohs(qsr10->port_no));
         /* OF 1.0 uses OFPP_ALL for OFPP_ANY */
         if (oqsr->port_no == OFPP_ALL) {
             oqsr->port_no = OFPP_ANY;
@@ -4857,8 +5240,8 @@ ofputil_encode_queue_stats_request(enum ofp_version ofp_version,
         request = ofpraw_alloc(OFPRAW_OFPST10_QUEUE_REQUEST, ofp_version, 0);
         req = ofpbuf_put_zeros(request, sizeof *req);
         /* 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->port_no = htons(ofp_to_u16(oqsr->port_no == OFPP_ANY
+                                        ? OFPP_ALL : oqsr->port_no));
         req->queue_id = htonl(oqsr->queue_id);
         break;
     }
@@ -4888,7 +5271,7 @@ static enum ofperr
 ofputil_queue_stats_from_ofp10(struct ofputil_queue_stats *oqs,
                                const struct ofp10_queue_stats *qs10)
 {
-    oqs->port_no = ntohs(qs10->port_no);
+    oqs->port_no = u16_to_ofp(ntohs(qs10->port_no));
     oqs->queue_id = ntohl(qs10->queue_id);
     oqs->stats.tx_bytes = ntohll(get_32aligned_be64(&qs10->tx_bytes));
     oqs->stats.tx_packets = ntohll(get_32aligned_be64(&qs10->tx_packets));
@@ -4993,7 +5376,7 @@ static void
 ofputil_queue_stats_to_ofp10(const struct ofputil_queue_stats *oqs,
                              struct ofp10_queue_stats *qs10)
 {
-    qs10->port_no = htons(oqs->port_no);
+    qs10->port_no = htons(ofp_to_u16(oqs->port_no));
     memset(qs10->pad, 0, sizeof qs10->pad);
     qs10->queue_id = htonl(oqs->queue_id);
     put_32aligned_be64(&qs10->tx_bytes, htonll(oqs->stats.tx_bytes));
index 4d0d8ad..39c81be 100644 (file)
 #include "netdev.h"
 #include "openflow/nicira-ext.h"
 #include "openvswitch/types.h"
+#include "type-props.h"
 
 struct ofpbuf;
 
 /* Port numbers. */
-enum ofperr ofputil_port_from_ofp11(ovs_be32 ofp11_port, uint16_t *ofp10_port);
-ovs_be32 ofputil_port_to_ofp11(uint16_t ofp10_port);
+enum ofperr ofputil_port_from_ofp11(ovs_be32 ofp11_port,
+                                    ofp_port_t *ofp10_port);
+ovs_be32 ofputil_port_to_ofp11(ofp_port_t ofp10_port);
 
-enum ofperr ofputil_check_output_port(uint16_t ofp_port, int max_ports);
-bool ofputil_port_from_string(const char *, uint16_t *portp);
-void ofputil_format_port(uint16_t port, struct ds *);
+enum ofperr ofputil_check_output_port(ofp_port_t ofp_port,
+                                      ofp_port_t max_ports);
+bool ofputil_port_from_string(const char *, ofp_port_t *portp);
+void ofputil_format_port(ofp_port_t port, struct ds *);
+void ofputil_port_to_string(ofp_port_t, char namebuf[OFP_MAX_PORT_NAME_LEN],
+                            size_t bufsize);
 
 /* Converting OFPFW10_NW_SRC_MASK and OFPFW10_NW_DST_MASK wildcard bit counts
  * to and from IP bitmasks. */
@@ -218,7 +223,7 @@ struct ofputil_flow_mod {
     uint16_t idle_timeout;
     uint16_t hard_timeout;
     uint32_t buffer_id;
-    uint16_t out_port;
+    ofp_port_t out_port;
     uint16_t flags;
     struct ofpact *ofpacts;     /* Series of "struct ofpact"s. */
     size_t ofpacts_len;         /* Length of ofpacts, in bytes. */
@@ -240,7 +245,7 @@ struct ofputil_flow_stats_request {
     struct match match;
     ovs_be64 cookie;
     ovs_be64 cookie_mask;
-    uint16_t out_port;
+    ofp_port_t out_port;
     uint8_t table_id;
 };
 
@@ -334,7 +339,10 @@ struct ofpbuf *ofputil_encode_packet_in(const struct ofputil_packet_in *,
                                         enum ofputil_protocol protocol,
                                         enum nx_packet_in_format);
 
-const char *ofputil_packet_in_reason_to_string(enum ofp_packet_in_reason);
+enum { OFPUTIL_PACKET_IN_REASON_BUFSIZE = INT_STRLEN(int) + 1 };
+const char *ofputil_packet_in_reason_to_string(enum ofp_packet_in_reason,
+                                               char *reasonbuf,
+                                               size_t bufsize);
 bool ofputil_packet_in_reason_from_string(const char *,
                                           enum ofp_packet_in_reason *);
 
@@ -346,7 +354,7 @@ struct ofputil_packet_out {
     const void *packet;         /* Packet data, if buffer_id == UINT32_MAX. */
     size_t packet_len;          /* Length of packet data in bytes. */
     uint32_t buffer_id;         /* Buffer id or UINT32_MAX if no buffer. */
-    uint16_t in_port;           /* Packet's input port. */
+    ofp_port_t in_port;         /* Packet's input port. */
     struct ofpact *ofpacts;     /* Actions. */
     size_t ofpacts_len;         /* Size of ofpacts in bytes. */
 };
@@ -386,7 +394,7 @@ enum ofputil_port_state {
 
 /* Abstract ofp10_phy_port or ofp11_port. */
 struct ofputil_phy_port {
-    uint16_t port_no;
+    ofp_port_t port_no;
     uint8_t hw_addr[OFP_ETH_ALEN];
     char name[OFP_MAX_PORT_NAME_LEN];
     enum ofputil_port_config config;
@@ -494,7 +502,7 @@ struct ofpbuf *ofputil_encode_port_status(const struct ofputil_port_status *,
 
 /* Abstract ofp_port_mod. */
 struct ofputil_port_mod {
-    uint16_t port_no;
+    ofp_port_t port_no;
     uint8_t hw_addr[OFP_ETH_ALEN];
     enum ofputil_port_config config;
     enum ofputil_port_config mask;
@@ -506,6 +514,93 @@ enum ofperr ofputil_decode_port_mod(const struct ofp_header *,
 struct ofpbuf *ofputil_encode_port_mod(const struct ofputil_port_mod *,
                                        enum ofputil_protocol);
 
+/* Meter band configuration for all supported band types. */
+struct ofputil_meter_band {
+    uint16_t type;
+    uint8_t prec_level;         /* Non-zero if type == OFPMBT_DSCP_REMARK. */
+    uint32_t rate;
+    uint32_t burst_size;
+};
+
+struct ofputil_meter_band_stats {
+    uint64_t packet_count;
+    uint64_t byte_count;
+};
+
+struct ofputil_meter_config {
+    uint32_t meter_id;
+    uint16_t flags;
+    uint16_t n_bands;
+    struct ofputil_meter_band *bands;
+};
+
+/* Abstract ofp_meter_mod. */
+struct ofputil_meter_mod {
+    uint16_t command;
+    struct ofputil_meter_config meter;
+};
+
+struct ofputil_meter_stats {
+    uint32_t meter_id;
+    uint32_t flow_count;
+    uint64_t packet_in_count;
+    uint64_t byte_in_count;
+    uint32_t duration_sec;
+    uint32_t duration_nsec;
+    uint16_t n_bands;
+    struct ofputil_meter_band_stats *bands;
+};
+
+struct ofputil_meter_features {
+    uint32_t max_meters;        /* Maximum number of meters. */
+    uint32_t band_types;        /* Can support max 32 band types. */
+    uint32_t capabilities;      /* Supported flags. */
+    uint8_t  max_bands;
+    uint8_t  max_color;
+};
+
+enum ofperr ofputil_decode_meter_mod(const struct ofp_header *,
+                                     struct ofputil_meter_mod *,
+                                     struct ofpbuf *bands);
+struct ofpbuf *ofputil_encode_meter_mod(enum ofp_version,
+                                        const struct ofputil_meter_mod *);
+
+void ofputil_decode_meter_features(const struct ofp_header *,
+                                   struct ofputil_meter_features *);
+struct ofpbuf *ofputil_encode_meter_features_reply(const struct
+                                                   ofputil_meter_features *,
+                                                   const struct ofp_header *
+                                                   request);
+void ofputil_decode_meter_request(const struct ofp_header *,
+                                  uint32_t *meter_id);
+
+void ofputil_append_meter_config(struct list *replies,
+                                 const struct ofputil_meter_config *);
+
+void ofputil_append_meter_stats(struct list *replies,
+                                const struct ofputil_meter_stats *);
+
+enum ofputil_meter_request_type {
+    OFPUTIL_METER_FEATURES,
+    OFPUTIL_METER_CONFIG,
+    OFPUTIL_METER_STATS
+};
+
+struct ofpbuf *ofputil_encode_meter_request(enum ofp_version,
+                                            enum ofputil_meter_request_type,
+                                            uint32_t meter_id);
+
+int ofputil_decode_meter_stats(struct ofpbuf *,
+                               struct ofputil_meter_stats *,
+                               struct ofpbuf *bands);
+
+int ofputil_decode_meter_config(struct ofpbuf *,
+                                struct ofputil_meter_config *,
+                                struct ofpbuf *bands);
+
+/* Type for meter_id in ofproto provider interface, UINT32_MAX if invalid. */
+typedef struct { uint32_t uint32; } ofproto_meter_id;
+
 /* Abstract ofp_role_request and reply. */
 struct ofputil_role_request {
     enum ofp12_controller_role role;
@@ -531,7 +626,7 @@ struct ofpbuf *ofputil_encode_table_stats_reply(
 struct ofputil_flow_monitor_request {
     uint32_t id;
     enum nx_flow_monitor_flags flags;
-    uint16_t out_port;
+    ofp_port_t out_port;
     uint8_t table_id;
     struct match match;
 };
@@ -672,8 +767,6 @@ void *ofputil_put_action(enum ofputil_action_code, struct ofpbuf *buf);
 
 #define OFP_ACTION_ALIGN 8      /* Alignment of ofp_actions. */
 
-enum ofperr validate_actions(const union ofp_action *, size_t n_actions,
-                             const struct flow *, int max_ports);
 bool action_outputs_to_port(const union ofp_action *, ovs_be16 port);
 
 enum ofperr ofputil_pull_actions(struct ofpbuf *, unsigned int actions_len,
@@ -687,21 +780,23 @@ union ofp_action *ofputil_actions_clone(const union ofp_action *, size_t n);
 bool ofputil_parse_key_value(char **stringp, char **keyp, char **valuep);
 
 struct ofputil_port_stats {
-    uint16_t port_no;
+    ofp_port_t port_no;
     struct netdev_stats stats;
+    uint32_t duration_sec;      /* UINT32_MAX if unknown. */
+    uint32_t duration_nsec;
 };
 
 struct ofpbuf *ofputil_encode_dump_ports_request(enum ofp_version ofp_version,
-                                                 uint16_t port);
+                                                 ofp_port_t port);
 void ofputil_append_port_stat(struct list *replies,
                               const struct ofputil_port_stats *ops);
 size_t ofputil_count_port_stats(const struct ofp_header *);
 int ofputil_decode_port_stats(struct ofputil_port_stats *, struct ofpbuf *msg);
 enum ofperr ofputil_decode_port_stats_request(const struct ofp_header *request,
-                                              uint16_t *ofp10_port);
+                                              ofp_port_t *ofp10_port);
 
 struct ofputil_queue_stats_request {
-    uint16_t port_no;           /* OFPP_ANY means "all ports". */
+    ofp_port_t port_no;           /* OFPP_ANY means "all ports". */
     uint32_t queue_id;
 };
 
@@ -713,7 +808,7 @@ ofputil_encode_queue_stats_request(enum ofp_version ofp_version,
                                    const struct ofputil_queue_stats_request *oqsr);
 
 struct ofputil_queue_stats {
-    uint16_t port_no;
+    ofp_port_t port_no;
     uint32_t queue_id;
     struct netdev_queue_stats stats;
 };
index 35d79e6..84e83d8 100644 (file)
@@ -1,6 +1,7 @@
 #include <config.h>
 #include "ofp-util.h"
 #include "ofp-version-opt.h"
+#include "ovs-thread.h"
 #include "vlog.h"
 #include "dynamic-string.h"
 
@@ -17,12 +18,14 @@ get_allowed_ofp_versions(void)
 void
 set_allowed_ofp_versions(const char *string)
 {
+    assert_single_threaded();
     allowed_versions = ofputil_versions_from_string(string);
 }
 
 void
 mask_allowed_ofp_versions(uint32_t bitmap)
 {
+    assert_single_threaded();
     allowed_versions &= bitmap;
 }
 
index 8b03c7e..0c12162 100644 (file)
@@ -107,6 +107,12 @@ static inline struct ofpbuf *ofpbuf_from_list(const struct list *list)
 }
 void ofpbuf_list_delete(struct list *);
 
+static inline bool
+ofpbuf_equal(const struct ofpbuf *a, const struct ofpbuf *b)
+{
+    return a->size == b->size && memcmp(a->data, b->data, a->size) == 0;
+}
+
 #ifdef  __cplusplus
 }
 #endif
diff --git a/lib/ovs-atomic-c11.h b/lib/ovs-atomic-c11.h
new file mode 100644 (file)
index 0000000..9dc687c
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 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.
+ * 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 header implements atomic operation primitives on compilers that
+ * have built-in support for C11 <stdatomic.h>  */
+#ifndef IN_OVS_ATOMIC_H
+#error "This header should only be included indirectly via ovs-atomic.h."
+#endif
+
+#include <stdatomic.h>
+
+/* Nonstandard atomic types. */
+typedef _Atomic(uint8_t)   atomic_uint8_t;
+typedef _Atomic(uint16_t)  atomic_uint16_t;
+typedef _Atomic(uint32_t)  atomic_uint32_t;
+typedef _Atomic(uint64_t)  atomic_uint64_t;
+
+typedef _Atomic(int8_t)    atomic_int8_t;
+typedef _Atomic(int16_t)   atomic_int16_t;
+typedef _Atomic(int32_t)   atomic_int32_t;
+typedef _Atomic(int64_t)   atomic_int64_t;
+
+#define atomic_read(SRC, DST) \
+    atomic_read_explicit(SRC, DST, memory_order_seq_cst)
+#define atomic_read_explicit(SRC, DST, ORDER)   \
+    (*(DST) = atomic_load_explicit(SRC, ORDER), \
+     (void) 0)
+
+#define atomic_add(RMW, ARG, ORIG) \
+    atomic_add_explicit(RMW, ARG, ORIG, memory_order_seq_cst)
+#define atomic_sub(RMW, ARG, ORIG) \
+    atomic_sub_explicit(RMW, ARG, ORIG, memory_order_seq_cst)
+#define atomic_or(RMW, ARG, ORIG) \
+    atomic_or_explicit(RMW, ARG, ORIG, memory_order_seq_cst)
+#define atomic_xor(RMW, ARG, ORIG) \
+    atomic_xor_explicit(RMW, ARG, ORIG, memory_order_seq_cst)
+#define atomic_and(RMW, ARG, ORIG) \
+    atomic_and_explicit(RMW, ARG, ORIG, memory_order_seq_cst)
+
+#define atomic_add_explicit(RMW, ARG, ORIG, ORDER) \
+    (*(ORIG) = atomic_fetch_add_explicit(RMW, ARG, ORDER), (void) 0)
+#define atomic_sub_explicit(RMW, ARG, ORIG, ORDER) \
+    (*(ORIG) = atomic_fetch_sub_explicit(RMW, ARG, ORDER), (void) 0)
+#define atomic_or_explicit(RMW, ARG, ORIG, ORDER) \
+    (*(ORIG) = atomic_fetch_or_explicit(RMW, ARG, ORDER), (void) 0)
+#define atomic_xor_explicit(RMW, ARG, ORIG, ORDER) \
+    (*(ORIG) = atomic_fetch_xor_explicit(RMW, ARG, ORDER), (void) 0)
+#define atomic_and_explicit(RMW, ARG, ORIG, ORDER) \
+    (*(ORIG) = atomic_fetch_and_explicit(RMW, ARG, ORDER), (void) 0)
diff --git a/lib/ovs-atomic-gcc4+.c b/lib/ovs-atomic-gcc4+.c
new file mode 100644 (file)
index 0000000..aeff845
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 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.
+ * 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 "ovs-atomic.h"
+#include "ovs-thread.h"
+
+#if OVS_ATOMIC_GCC4P_IMPL
+static pthread_mutex_t mutex = PTHREAD_ADAPTIVE_MUTEX_INITIALIZER;
+
+#define DEFINE_LOCKED_OP(TYPE, NAME, OPERATOR)                          \
+    TYPE##_t                                                            \
+    locked_##TYPE##_##NAME(struct locked_##TYPE *u, TYPE##_t arg)       \
+    {                                                                   \
+        TYPE##_t old_value;                                             \
+                                                                        \
+        xpthread_mutex_lock(&mutex);                                    \
+        old_value = u->value;                                           \
+        u->value OPERATOR arg;                                          \
+        xpthread_mutex_unlock(&mutex);                                  \
+                                                                        \
+        return old_value;                                               \
+    }
+
+#define DEFINE_LOCKED_TYPE(TYPE)                                        \
+    TYPE##_t                                                            \
+    locked_##TYPE##_load(const struct locked_##TYPE *u)                 \
+    {                                                                   \
+        TYPE##_t value;                                                 \
+                                                                        \
+        xpthread_mutex_lock(&mutex);                                    \
+        value = u->value;                                               \
+        xpthread_mutex_unlock(&mutex);                                  \
+                                                                        \
+        return value;                                                   \
+    }                                                                   \
+                                                                        \
+    void                                                                \
+    locked_##TYPE##_store(struct locked_##TYPE *u, TYPE##_t value)      \
+    {                                                                   \
+        xpthread_mutex_lock(&mutex);                                    \
+        u->value = value;                                               \
+        xpthread_mutex_unlock(&mutex);                                  \
+    }                                                                   \
+    DEFINE_LOCKED_OP(TYPE, add, +=);                                    \
+    DEFINE_LOCKED_OP(TYPE, sub, -=);                                    \
+    DEFINE_LOCKED_OP(TYPE, or,  |=);                                    \
+    DEFINE_LOCKED_OP(TYPE, xor, ^=);                                    \
+    DEFINE_LOCKED_OP(TYPE, and, &=)
+
+DEFINE_LOCKED_TYPE(uint64);
+DEFINE_LOCKED_TYPE(int64);
+
+#endif  /* OVS_ATOMIC_GCC4P_IMPL */
diff --git a/lib/ovs-atomic-gcc4+.h b/lib/ovs-atomic-gcc4+.h
new file mode 100644 (file)
index 0000000..b8649ed
--- /dev/null
@@ -0,0 +1,265 @@
+/*
+ * Copyright (c) 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.
+ * 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 header implements atomic operation primitives on GCC 4.x. */
+#ifndef IN_OVS_ATOMIC_H
+#error "This header should only be included indirectly via ovs-atomic.h."
+#endif
+
+#define OVS_ATOMIC_GCC4P_IMPL 1
+
+#define DEFINE_LOCKLESS_ATOMIC(TYPE, NAME) typedef struct { TYPE value; } NAME
+
+#define ATOMIC_BOOL_LOCK_FREE 2
+DEFINE_LOCKLESS_ATOMIC(bool, atomic_bool);
+
+#define ATOMIC_CHAR_LOCK_FREE 2
+DEFINE_LOCKLESS_ATOMIC(char, atomic_char);
+DEFINE_LOCKLESS_ATOMIC(signed char, atomic_schar);
+DEFINE_LOCKLESS_ATOMIC(unsigned char, atomic_uchar);
+
+#define ATOMIC_SHORT_LOCK_FREE 2
+DEFINE_LOCKLESS_ATOMIC(short, atomic_short);
+DEFINE_LOCKLESS_ATOMIC(unsigned short, atomic_ushort);
+
+#define ATOMIC_INT_LOCK_FREE 2
+DEFINE_LOCKLESS_ATOMIC(int, atomic_int);
+DEFINE_LOCKLESS_ATOMIC(unsigned int, atomic_uint);
+
+#if ULONG_MAX <= UINTPTR_MAX
+    #define ATOMIC_LONG_LOCK_FREE 2
+    DEFINE_LOCKLESS_ATOMIC(long, atomic_long);
+    DEFINE_LOCKLESS_ATOMIC(unsigned long, atomic_ulong);
+#elif ULONG_MAX == UINT64_MAX
+    #define ATOMIC_LONG_LOCK_FREE 0
+    typedef struct locked_int64  atomic_long;
+    typedef struct locked_uint64 atomic_ulong;
+#else
+    #error "not implemented"
+#endif
+
+#if ULLONG_MAX <= UINTPTR_MAX
+    #define ATOMIC_LLONG_LOCK_FREE 2
+    DEFINE_LOCKLESS_ATOMIC(long long, atomic_llong);
+    DEFINE_LOCKLESS_ATOMIC(unsigned long long, atomic_ullong);
+#elif ULLONG_MAX == UINT64_MAX
+    #define ATOMIC_LLONG_LOCK_FREE 0
+    typedef struct locked_int64  atomic_llong;
+    typedef struct locked_uint64 atomic_ullong;
+#else
+    #error "not implemented"
+#endif
+
+#if SIZE_MAX <= UINTPTR_MAX
+    DEFINE_LOCKLESS_ATOMIC(size_t, atomic_size_t);
+    DEFINE_LOCKLESS_ATOMIC(ptrdiff_t, atomic_ptrdiff_t);
+#elif SIZE_MAX == UINT64_MAX
+    typedef struct locked_uint64 atomic_size_t;
+    typedef struct locked_int64  atomic_ptrdiff_t;
+#else
+    #error "not implemented"
+#endif
+
+#if UINTMAX_MAX <= UINTPTR_MAX
+    DEFINE_LOCKLESS_ATOMIC(intmax_t, atomic_intmax_t);
+    DEFINE_LOCKLESS_ATOMIC(uintmax_t, atomic_uintmax_t);
+#elif UINTMAX_MAX == UINT64_MAX
+    typedef struct locked_int64  atomic_intmax_t;
+    typedef struct locked_uint64 atomic_uintmax_t;
+#else
+    #error "not implemented"
+#endif
+
+#define ATOMIC_POINTER_LOCK_FREE 2
+DEFINE_LOCKLESS_ATOMIC(intptr_t, atomic_intptr_t);
+DEFINE_LOCKLESS_ATOMIC(uintptr_t, atomic_uintptr_t);
+
+/* Nonstandard atomic types. */
+DEFINE_LOCKLESS_ATOMIC(uint8_t,  atomic_uint8_t);
+DEFINE_LOCKLESS_ATOMIC(uint16_t, atomic_uint16_t);
+DEFINE_LOCKLESS_ATOMIC(uint32_t, atomic_uint32_t);
+DEFINE_LOCKLESS_ATOMIC(int8_t,   atomic_int8_t);
+DEFINE_LOCKLESS_ATOMIC(int16_t,  atomic_int16_t);
+DEFINE_LOCKLESS_ATOMIC(int32_t,  atomic_int32_t);
+#if UINT64_MAX <= UINTPTR_MAX
+    DEFINE_LOCKLESS_ATOMIC(uint64_t, atomic_uint64_t);
+    DEFINE_LOCKLESS_ATOMIC(int64_t,  atomic_int64_t);
+#else
+    typedef struct locked_uint64 atomic_uint64_t;
+    typedef struct locked_int64  atomic_int64_t;
+#endif
+
+typedef enum {
+    memory_order_relaxed,
+    memory_order_consume,
+    memory_order_acquire,
+    memory_order_release,
+    memory_order_acq_rel,
+    memory_order_seq_cst
+} memory_order;
+\f
+/* locked_uint64. */
+
+#define IF_LOCKED_UINT64(OBJECT, THEN, ELSE)                            \
+    __builtin_choose_expr(                                              \
+        __builtin_types_compatible_p(typeof(OBJECT), struct locked_uint64), \
+        (THEN), (ELSE))
+#define AS_LOCKED_UINT64(OBJECT) ((struct locked_uint64 *) (OBJECT))
+#define AS_UINT64(OBJECT) ((uint64_t *) (OBJECT))
+struct locked_uint64 {
+    uint64_t value;
+};
+
+uint64_t locked_uint64_load(const struct locked_uint64 *);
+void locked_uint64_store(struct locked_uint64 *, uint64_t);
+uint64_t locked_uint64_add(struct locked_uint64 *, uint64_t arg);
+uint64_t locked_uint64_sub(struct locked_uint64 *, uint64_t arg);
+uint64_t locked_uint64_or(struct locked_uint64 *, uint64_t arg);
+uint64_t locked_uint64_xor(struct locked_uint64 *, uint64_t arg);
+uint64_t locked_uint64_and(struct locked_uint64 *, uint64_t arg);
+\f
+#define IF_LOCKED_INT64(OBJECT, THEN, ELSE)                             \
+    __builtin_choose_expr(                                              \
+        __builtin_types_compatible_p(typeof(OBJECT), struct locked_int64), \
+        (THEN), (ELSE))
+#define AS_LOCKED_INT64(OBJECT) ((struct locked_int64 *) (OBJECT))
+#define AS_INT64(OBJECT) ((int64_t *) (OBJECT))
+struct locked_int64 {
+    int64_t value;
+};
+int64_t locked_int64_load(const struct locked_int64 *);
+void locked_int64_store(struct locked_int64 *, int64_t);
+int64_t locked_int64_add(struct locked_int64 *, int64_t arg);
+int64_t locked_int64_sub(struct locked_int64 *, int64_t arg);
+int64_t locked_int64_or(struct locked_int64 *, int64_t arg);
+int64_t locked_int64_xor(struct locked_int64 *, int64_t arg);
+int64_t locked_int64_and(struct locked_int64 *, int64_t arg);
+\f
+#define ATOMIC_VAR_INIT(VALUE) { .value = (VALUE) }
+#define atomic_init(OBJECT, VALUE) ((OBJECT)->value = (VALUE), (void) 0)
+
+static inline void
+atomic_thread_fence(memory_order order)
+{
+    if (order != memory_order_relaxed) {
+        __sync_synchronize();
+    }
+}
+
+static inline void
+atomic_thread_fence_if_seq_cst(memory_order order)
+{
+    if (order == memory_order_seq_cst) {
+        __sync_synchronize();
+    }
+}
+
+static inline void
+atomic_signal_fence(memory_order order OVS_UNUSED)
+{
+    if (order != memory_order_relaxed) {
+        asm volatile("" : : : "memory");
+    }
+}
+
+#define ATOMIC_SWITCH(OBJECT, LOCKLESS_CASE,                    \
+                      LOCKED_UINT64_CASE, LOCKED_INT64_CASE)    \
+    IF_LOCKED_UINT64(OBJECT, LOCKED_UINT64_CASE,                \
+                     IF_LOCKED_INT64(OBJECT, LOCKED_INT64_CASE, \
+                                     LOCKLESS_CASE))
+
+#define atomic_is_lock_free(OBJ)                \
+    ((void) (OBJ)->value,                       \
+     ATOMIC_SWITCH(OBJ, true, false, false))
+
+#define atomic_store(DST, SRC) \
+    atomic_store_explicit(DST, SRC, memory_order_seq_cst)
+#define atomic_store_explicit(DST, SRC, ORDER)                          \
+    (ATOMIC_SWITCH(DST,                                                 \
+                   (atomic_thread_fence(ORDER),                         \
+                    (DST)->value = (SRC),                               \
+                    atomic_thread_fence_if_seq_cst(ORDER)),             \
+                   locked_uint64_store(AS_LOCKED_UINT64(DST), SRC),     \
+                   locked_int64_store(AS_LOCKED_INT64(DST), SRC)),      \
+     (void) 0)
+
+#define atomic_read(SRC, DST) \
+    atomic_read_explicit(SRC, DST, memory_order_seq_cst)
+#define atomic_read_explicit(SRC, DST, ORDER)                           \
+    (ATOMIC_SWITCH(SRC,                                                 \
+                   (atomic_thread_fence_if_seq_cst(ORDER),              \
+                    (*DST) = (SRC)->value,                              \
+                    atomic_thread_fence(ORDER)),                        \
+                   *(DST) = locked_uint64_load(AS_LOCKED_UINT64(SRC)),  \
+                   *(DST) = locked_int64_load(AS_LOCKED_INT64(SRC))),   \
+     (void) 0)
+
+#define atomic_op__(RMW, OP, ARG, ORIG)                                 \
+    (ATOMIC_SWITCH(RMW,                                                 \
+                   *(ORIG) = __sync_fetch_and_##OP(&(RMW)->value, ARG), \
+                   *(ORIG) = locked_uint64_##OP(AS_LOCKED_UINT64(RMW), ARG), \
+                   *(ORIG) = locked_int64_##OP(AS_LOCKED_INT64(RMW), ARG)), \
+     (void) 0)
+
+#define atomic_add(RMW, ARG, ORIG) atomic_op__(RMW, add, ARG, ORIG)
+#define atomic_sub(RMW, ARG, ORIG) atomic_op__(RMW, sub, ARG, ORIG)
+#define atomic_or( RMW, ARG, ORIG) atomic_op__(RMW, or,  ARG, ORIG)
+#define atomic_xor(RMW, ARG, ORIG) atomic_op__(RMW, xor, ARG, ORIG)
+#define atomic_and(RMW, ARG, ORIG) atomic_op__(RMW, and, ARG, ORIG)
+
+#define atomic_add_explicit(RMW, OPERAND, ORIG, ORDER)  \
+    ((void) (ORDER), atomic_add(RMW, OPERAND, ORIG))
+#define atomic_sub_explicit(RMW, OPERAND, ORIG, ORDER)  \
+    ((void) (ORDER), atomic_sub(RMW, OPERAND, ORIG))
+#define atomic_or_explicit(RMW, OPERAND, ORIG, ORDER)   \
+    ((void) (ORDER), atomic_or(RMW, OPERAND, ORIG))
+#define atomic_xor_explicit(RMW, OPERAND, ORIG, ORDER)  \
+    ((void) (ORDER), atomic_xor(RMW, OPERAND, ORIG))
+#define atomic_and_explicit(RMW, OPERAND, ORIG, ORDER)  \
+    ((void) (ORDER), atomic_and(RMW, OPERAND, ORIG))
+\f
+/* atomic_flag */
+
+typedef struct {
+    int b;
+} atomic_flag;
+#define ATOMIC_FLAG_INIT { false }
+
+static inline bool
+atomic_flag_test_and_set(volatile atomic_flag *object)
+{
+    return __sync_lock_test_and_set(&object->b, 1);
+}
+
+static inline bool
+atomic_flag_test_and_set_explicit(volatile atomic_flag *object,
+                                  memory_order order OVS_UNUSED)
+{
+    return atomic_flag_test_and_set(object);
+}
+
+static inline void
+atomic_flag_clear(volatile atomic_flag *object)
+{
+    __sync_lock_release(&object->b);
+}
+
+static inline void
+atomic_flag_clear_explicit(volatile atomic_flag *object,
+                           memory_order order OVS_UNUSED)
+{
+    atomic_flag_clear(object);
+}
diff --git a/lib/ovs-atomic-gcc4.7+.h b/lib/ovs-atomic-gcc4.7+.h
new file mode 100644 (file)
index 0000000..07bef2a
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 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.
+ * 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 header implements atomic operation primitives on GCC 4.7 and later. */
+#ifndef IN_OVS_ATOMIC_H
+#error "This header should only be included indirectly via ovs-atomic.h."
+#endif
+
+/* C11 standardized atomic type. */
+typedef bool               atomic_bool;
+
+typedef char               atomic_char;
+typedef signed char        atomic_schar;
+typedef unsigned char      atomic_uchar;
+
+typedef short              atomic_short;
+typedef unsigned short     atomic_ushort;
+
+typedef int                atomic_int;
+typedef unsigned int       atomic_uint;
+
+typedef long               atomic_long;
+typedef unsigned long      atomic_ulong;
+
+typedef long long          atomic_llong;
+typedef unsigned long long atomic_ullong;
+
+typedef size_t             atomic_size_t;
+typedef ptrdiff_t          atomic_ptrdiff_t;
+
+typedef intmax_t           atomic_intmax_t;
+typedef uintmax_t          atomic_uintmax_t;
+
+typedef intptr_t           atomic_intptr_t;
+typedef uintptr_t          atomic_uintptr_t;
+
+/* Nonstandard atomic types. */
+typedef int8_t             atomic_int8_t;
+typedef uint8_t            atomic_uint8_t;
+
+typedef int16_t            atomic_int16_t;
+typedef uint16_t           atomic_uint16_t;
+
+typedef int32_t            atomic_int32_t;
+typedef uint32_t           atomic_uint32_t;
+
+typedef int64_t            atomic_int64_t;
+typedef uint64_t           atomic_uint64_t;
+
+typedef enum {
+    memory_order_relaxed = __ATOMIC_RELAXED,
+    memory_order_consume = __ATOMIC_CONSUME,
+    memory_order_acquire = __ATOMIC_ACQUIRE,
+    memory_order_release = __ATOMIC_RELEASE,
+    memory_order_acq_rel = __ATOMIC_ACQ_REL,
+    memory_order_seq_cst = __ATOMIC_SEQ_CST
+} memory_order;
+
+#define ATOMIC_VAR_INIT(VALUE) (VALUE)
+#define atomic_init(OBJECT, VALUE) (*(OBJECT) = (VALUE), (void) 0)
+
+#define atomic_thread_fence __atomic_thread_fence
+#define atomic_signal_fence __atomic_signal_fence
+#define atomic_is_lock_free __atomic_is_lock_free
+
+#define atomic_store(DST, SRC) \
+    atomic_store_explicit(DST, SRC, memory_order_seq_cst)
+#define atomic_store_explicit __atomic_store_n
+
+#define atomic_read(SRC, DST) \
+    atomic_read_explicit(SRC, DST, memory_order_seq_cst)
+#define atomic_read_explicit(SRC, DST, ORDER)   \
+    (*(DST) = __atomic_load_n(SRC, ORDER),      \
+     (void) 0)
+
+#define atomic_add(RMW, OPERAND, ORIG) \
+        atomic_add_explicit(RMW, OPERAND, ORIG, memory_order_seq_cst)
+#define atomic_sub(RMW, OPERAND, ORIG) \
+        atomic_sub_explicit(RMW, OPERAND, ORIG, memory_order_seq_cst)
+#define atomic_or(RMW, OPERAND, ORIG) \
+        atomic_or_explicit(RMW, OPERAND, ORIG, memory_order_seq_cst)
+#define atomic_xor(RMW, OPERAND, ORIG) \
+        atomic_xor_explicit(RMW, OPERAND, ORIG, memory_order_seq_cst)
+#define atomic_and(RMW, OPERAND, ORIG) \
+        atomic_and_explicit(RMW, OPERAND, ORIG, memory_order_seq_cst)
+
+#define atomic_add_explicit(RMW, OPERAND, ORIG, ORDER)  \
+    (*(ORIG) = __atomic_fetch_add(RMW, OPERAND, ORDER), (void) 0)
+#define atomic_sub_explicit(RMW, OPERAND, ORIG, ORDER)  \
+    (*(ORIG) = __atomic_fetch_sub(RMW, OPERAND, ORDER), (void) 0)
+#define atomic_or_explicit(RMW, OPERAND, ORIG, ORDER)  \
+    (*(ORIG) = __atomic_fetch_or(RMW, OPERAND, ORDER), (void) 0)
+#define atomic_xor_explicit(RMW, OPERAND, ORIG, ORDER)  \
+    (*(ORIG) = __atomic_fetch_xor(RMW, OPERAND, ORDER), (void) 0)
+#define atomic_and_explicit(RMW, OPERAND, ORIG, ORDER)  \
+    (*(ORIG) = __atomic_fetch_and(RMW, OPERAND, ORDER), (void) 0)
+\f
+/* atomic_flag */
+
+typedef struct {
+    unsigned char b;
+} atomic_flag;
+#define ATOMIC_FLAG_INIT { .b = false }
+
+static inline bool
+atomic_flag_test_and_set_explicit(volatile atomic_flag *object,
+                                  memory_order order)
+{
+    return __atomic_test_and_set(&object->b, order);
+}
+
+static inline bool
+atomic_flag_test_and_set(volatile atomic_flag *object)
+{
+    return atomic_flag_test_and_set_explicit(object, memory_order_seq_cst);
+}
+
+static inline void
+atomic_flag_clear_explicit(volatile atomic_flag *object, memory_order order)
+{
+    __atomic_clear(object, order);
+}
+
+static inline void
+atomic_flag_clear(volatile atomic_flag *object)
+{
+    atomic_flag_clear_explicit(object, memory_order_seq_cst);
+}
diff --git a/lib/ovs-atomic-pthreads.c b/lib/ovs-atomic-pthreads.c
new file mode 100644 (file)
index 0000000..a501b82
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 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.
+ * 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 "ovs-atomic.h"
+#include "ovs-thread.h"
+
+#if OVS_ATOMIC_PTHREADS_IMPL
+bool
+atomic_flag_test_and_set(volatile atomic_flag *flag_)
+{
+    atomic_flag *flag = CONST_CAST(atomic_flag *, flag_);
+    bool old_value;
+
+    xpthread_mutex_lock(&flag->mutex);
+    old_value = flag->b;
+    flag->b = true;
+    xpthread_mutex_unlock(&flag->mutex);
+
+    return old_value;
+}
+
+bool
+atomic_flag_test_and_set_explicit(volatile atomic_flag *flag,
+                                  memory_order order OVS_UNUSED)
+{
+    return atomic_flag_test_and_set(flag);
+}
+
+void
+atomic_flag_clear(volatile atomic_flag *flag_)
+{
+    atomic_flag *flag = CONST_CAST(atomic_flag *, flag_);
+
+    xpthread_mutex_lock(&flag->mutex);
+    flag->b = false;
+    xpthread_mutex_unlock(&flag->mutex);
+}
+
+void
+atomic_flag_clear_explicit(volatile atomic_flag *flag,
+                           memory_order order OVS_UNUSED)
+{
+    return atomic_flag_clear(flag);
+}
+
+#endif  /* OVS_ATOMIC_PTHREADS_IMPL */
diff --git a/lib/ovs-atomic-pthreads.h b/lib/ovs-atomic-pthreads.h
new file mode 100644 (file)
index 0000000..2f47a9c
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 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.
+ * 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 header implements atomic operation primitives using pthreads. */
+#ifndef IN_OVS_ATOMIC_H
+#error "This header should only be included indirectly via ovs-atomic.h."
+#endif
+
+#define OVS_ATOMIC_PTHREADS_IMPL 1
+
+#define DEFINE_PTHREAD_ATOMIC(TYPE, NAME)       \
+    typedef struct {                            \
+        TYPE value;                             \
+        pthread_mutex_t mutex;                  \
+    } NAME;
+
+#define ATOMIC_BOOL_LOCK_FREE 0
+DEFINE_PTHREAD_ATOMIC(bool, atomic_bool);
+
+#define ATOMIC_CHAR_LOCK_FREE 0
+DEFINE_PTHREAD_ATOMIC(char, atomic_char);
+DEFINE_PTHREAD_ATOMIC(signed char, atomic_schar);
+DEFINE_PTHREAD_ATOMIC(unsigned char, atomic_uchar);
+
+#define ATOMIC_SHORT_LOCK_FREE 0
+DEFINE_PTHREAD_ATOMIC(short, atomic_short);
+DEFINE_PTHREAD_ATOMIC(unsigned short, atomic_ushort);
+
+#define ATOMIC_INT_LOCK_FREE 0
+DEFINE_PTHREAD_ATOMIC(int, atomic_int);
+DEFINE_PTHREAD_ATOMIC(unsigned int, atomic_uint);
+
+#define ATOMIC_LONG_LOCK_FREE 0
+DEFINE_PTHREAD_ATOMIC(long, atomic_long);
+DEFINE_PTHREAD_ATOMIC(unsigned long, atomic_ulong);
+
+#define ATOMIC_LLONG_LOCK_FREE 0
+DEFINE_PTHREAD_ATOMIC(long long, atomic_llong);
+DEFINE_PTHREAD_ATOMIC(unsigned long long, atomic_ullong);
+
+DEFINE_PTHREAD_ATOMIC(size_t, atomic_size_t);
+DEFINE_PTHREAD_ATOMIC(ptrdiff_t, atomic_ptrdiff_t);
+
+DEFINE_PTHREAD_ATOMIC(intmax_t, atomic_intmax_t);
+DEFINE_PTHREAD_ATOMIC(uintmax_t, atomic_uintmax_t);
+
+#define ATOMIC_POINTER_LOCK_FREE 0
+DEFINE_PTHREAD_ATOMIC(intptr_t, atomic_intptr_t);
+DEFINE_PTHREAD_ATOMIC(uintptr_t, atomic_uintptr_t);
+
+/* Nonstandard atomic types. */
+DEFINE_PTHREAD_ATOMIC(uint8_t,  atomic_uint8_t);
+DEFINE_PTHREAD_ATOMIC(uint16_t, atomic_uint16_t);
+DEFINE_PTHREAD_ATOMIC(uint32_t, atomic_uint32_t);
+DEFINE_PTHREAD_ATOMIC(int8_t,   atomic_int8_t);
+DEFINE_PTHREAD_ATOMIC(int16_t,  atomic_int16_t);
+DEFINE_PTHREAD_ATOMIC(int32_t,  atomic_int32_t);
+DEFINE_PTHREAD_ATOMIC(uint64_t, atomic_uint64_t);
+DEFINE_PTHREAD_ATOMIC(int64_t,  atomic_int64_t);
+
+typedef enum {
+    memory_order_relaxed,
+    memory_order_consume,
+    memory_order_acquire,
+    memory_order_release,
+    memory_order_acq_rel,
+    memory_order_seq_cst
+} memory_order;
+
+#define ATOMIC_VAR_INIT(VALUE) { VALUE, PTHREAD_MUTEX_INITIALIZER }
+#define atomic_init(OBJECT, VALUE)                      \
+    ((OBJECT)->value = (VALUE),                         \
+     pthread_mutex_init(&(OBJECT)->mutex, NULL),        \
+     (void) 0)
+
+static inline void
+atomic_thread_fence(memory_order order OVS_UNUSED)
+{
+    /* Nothing to do. */
+}
+
+static inline void
+atomic_signal_fence(memory_order order OVS_UNUSED)
+{
+    /* Nothing to do. */
+}
+
+#define atomic_is_lock_free(OBJ) false
+
+#define atomic_store(DST, SRC)                  \
+    (pthread_mutex_lock(&(DST)->mutex),         \
+     (DST)->value = (SRC),                      \
+     pthread_mutex_unlock(&(DST)->mutex),       \
+     (void) 0)
+#define atomic_store_explicit(DST, SRC, ORDER) \
+    ((void) (ORDER), atomic_store(DST, SRC))
+
+#define atomic_read(SRC, DST)                                           \
+    (pthread_mutex_lock(CONST_CAST(pthread_mutex_t *, &(SRC)->mutex)),  \
+     *(DST) = (SRC)->value,                                             \
+     pthread_mutex_unlock(CONST_CAST(pthread_mutex_t *, &(SRC)->mutex)), \
+     (void) 0)
+#define atomic_read_explicit(SRC, DST, ORDER)   \
+    ((void) (ORDER), atomic_read(SRC, DST))
+
+#define atomic_op__(RMW, OPERATOR, OPERAND, ORIG)       \
+    (pthread_mutex_lock(&(RMW)->mutex),                 \
+     *(ORIG) = (RMW)->value,                            \
+     (RMW)->value OPERATOR (OPERAND),                   \
+     pthread_mutex_unlock(&(RMW)->mutex),               \
+     (void) 0)
+
+#define atomic_add(RMW, OPERAND, ORIG) atomic_op__(RMW, +=, OPERAND, ORIG)
+#define atomic_sub(RMW, OPERAND, ORIG) atomic_op__(RMW, -=, OPERAND, ORIG)
+#define atomic_or( RMW, OPERAND, ORIG) atomic_op__(RMW, |=, OPERAND, ORIG)
+#define atomic_xor(RMW, OPERAND, ORIG) atomic_op__(RMW, ^=, OPERAND, ORIG)
+#define atomic_and(RMW, OPERAND, ORIG) atomic_op__(RMW, &=, OPERAND, ORIG)
+
+#define atomic_add_explicit(RMW, OPERAND, ORIG, ORDER)  \
+    ((void) (ORDER), atomic_add(RMW, OPERAND, ORIG))
+#define atomic_sub_explicit(RMW, OPERAND, ORIG, ORDER)  \
+    ((void) (ORDER), atomic_sub(RMW, OPERAND, ORIG))
+#define atomic_or_explicit(RMW, OPERAND, ORIG, ORDER)   \
+    ((void) (ORDER), atomic_or(RMW, OPERAND, ORIG))
+#define atomic_xor_explicit(RMW, OPERAND, ORIG, ORDER)  \
+    ((void) (ORDER), atomic_xor(RMW, OPERAND, ORIG))
+#define atomic_and_explicit(RMW, OPERAND, ORIG, ORDER)  \
+    ((void) (ORDER), atomic_and(RMW, OPERAND, ORIG))
+\f
+/* atomic_flag */
+
+typedef struct {
+    bool b;
+    pthread_mutex_t mutex;
+} atomic_flag;
+#define ATOMIC_FLAG_INIT { false, PTHREAD_MUTEX_INITIALIZER }
+
+bool atomic_flag_test_and_set(volatile atomic_flag *);
+bool atomic_flag_test_and_set_explicit(volatile atomic_flag *, memory_order);
+
+void atomic_flag_clear(volatile atomic_flag *);
+void atomic_flag_clear_explicit(volatile atomic_flag *, memory_order);
diff --git a/lib/ovs-atomic.h b/lib/ovs-atomic.h
new file mode 100644 (file)
index 0000000..a0a34f3
--- /dev/null
@@ -0,0 +1,250 @@
+/*
+ * Copyright (c) 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.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef OVS_ATOMIC_H
+#define OVS_ATOMIC_H 1
+
+/* Atomic operations.
+ *
+ * This library implements atomic operations with an API based on the one
+ * defined in C11.  It includes multiple implementations for compilers and
+ * libraries with varying degrees of built-in support for C11, including an
+ * fallback implementation for systems that have pthreads but no other support
+ * for atomics.
+ *
+ * This comment describes the common features of all the implementations.
+ *
+ *
+ * Types
+ * =====
+ *
+ * The following atomic types are supported as typedefs for atomic versions of
+ * the listed ordinary types:
+ *
+ *     ordinary type            atomic version
+ *     -------------------      ----------------------
+ *     bool                     atomic_bool
+ *
+ *     char                     atomic_char
+ *     signed char              atomic_schar
+ *     unsigned char            atomic_uchar
+ *
+ *     short                    atomic_short
+ *     unsigned short           atomic_ushort
+ *
+ *     int                      atomic_int
+ *     unsigned int             atomic_uint
+ *
+ *     long                     atomic_long
+ *     unsigned long            atomic_ulong
+ *
+ *     long long                atomic_llong
+ *     unsigned long long       atomic_ullong
+ *
+ *     size_t                   atomic_size_t
+ *     ptrdiff_t                atomic_ptrdiff_t
+ *
+ *     intmax_t                 atomic_intmax_t
+ *     uintmax_t                atomic_uintmax_t
+ *
+ *     intptr_t                 atomic_intptr_t
+ *     uintptr_t                atomic_uintptr_t
+ *
+ *     uint8_t                  atomic_uint8_t     (*)
+ *     uint16_t                 atomic_uint16_t    (*)
+ *     uint32_t                 atomic_uint32_t    (*)
+ *     int8_t                   atomic_int8_t      (*)
+ *     int16_t                  atomic_int16_t     (*)
+ *     int32_t                  atomic_int32_t     (*)
+ *     uint64_t                 atomic_uint64_t    (*)
+ *     int64_t                  atomic_int64_t     (*)
+ *
+ *     (*) Not specified by C11.
+ *
+ * The atomic version of a type doesn't necessarily have the same size or
+ * representation as the ordinary version; for example, atomic_int might be a
+ * typedef for a struct that also includes a mutex.  The range of an atomic
+ * type does match the range of the corresponding ordinary type.
+ *
+ * C11 says that one may use the _Atomic keyword in place of the typedef name,
+ * e.g. "_Atomic int" instead of "atomic_int".  This library doesn't support
+ * that.
+ *
+ *
+ * Initialization
+ * ==============
+ *
+ * To initialize an atomic variable at its point of definition, use
+ * ATOMIC_VAR_INIT:
+ *
+ *     static atomic_int ai = ATOMIC_VAR_INIT(123);
+ *
+ * To initialize an atomic variable in code, use atomic_init():
+ *
+ *     static atomic_int ai;
+ * ...
+ *     atomic_init(&ai, 123);
+ *
+ *
+ * Barriers
+ * ========
+ *
+ * enum memory_order specifies the strictness of a memory barrier.  It has the
+ * following values:
+ *
+ *    memory_order_relaxed:
+ *
+ *        Compiler barrier only.  Does not imply any CPU memory ordering.
+ *
+ *    memory_order_acquire:
+ *
+ *        Memory accesses after an acquire barrier cannot be moved before the
+ *        barrier.  Memory accesses before an acquire barrier *can* be moved
+ *        after it.
+ *
+ *    memory_order_release:
+ *
+ *        Memory accesses before a release barrier cannot be moved after the
+ *        barrier.  Memory accesses after a release barrier *can* be moved
+ *        before it.
+ *
+ *    memory_order_acq_rel:
+ *
+ *        Memory accesses cannot be moved across an acquire-release barrier in
+ *        either direction.
+ *
+ *    memory_order_seq_cst:
+ *
+ *        Prevents movement of memory accesses like an acquire-release barrier,
+ *        but whereas acquire-release synchronizes cooperating threads,
+ *        sequential-consistency synchronizes the whole system.
+ *
+ *    memory_order_consume:
+ *
+ *        A slight relaxation of memory_order_acquire.
+ *
+ * The following functions insert explicit barriers.  Most of the other atomic
+ * functions also include barriers.
+ *
+ *     void atomic_thread_fence(memory_order order);
+ *
+ *         Inserts a barrier of the specified type.
+ *
+ *         For memory_order_relaxed, this is a no-op.
+ *
+ *     void atomic_signal_fence(memory_order order);
+ *
+ *         Inserts a barrier of the specified type, but only with respect to
+ *         signal handlers in the same thread as the barrier.  This is
+ *         basically a compiler optimization barrier, except for
+ *         memory_order_relaxed, which is a no-op.
+ *
+ *
+ * Atomic Operations
+ * =================
+ *
+ * In this section, A is an atomic type and C is the corresponding non-atomic
+ * type.
+ *
+ * The "store" primitives match C11:
+ *
+ *     void atomic_store(A *object, C value);
+ *     void atomic_store_explicit(A *object, C value, memory_order);
+ *
+ *         Atomically stores 'value' into '*object', respecting the given
+ *         memory order (or memory_order_seq_cst for atomic_store()).
+ *
+ * The following primitives differ from the C11 ones (and have different names)
+ * because there does not appear to be a way to implement the standard
+ * primitives in standard C:
+ *
+ *     void atomic_read(A *src, C *dst);
+ *     void atomic_read_explicit(A *src, C *dst, memory_order);
+ *
+ *         Atomically loads a value from 'src', writing the value read into
+ *         '*dst', respecting the given memory order (or memory_order_seq_cst
+ *         for atomic_read()).
+ *
+ *     void atomic_add(A *rmw, C arg, C *orig);
+ *     void atomic_sub(A *rmw, C arg, C *orig);
+ *     void atomic_or(A *rmw, C arg, C *orig);
+ *     void atomic_xor(A *rmw, C arg, C *orig);
+ *     void atomic_and(A *rmw, C arg, C *orig);
+ *     void atomic_add_explicit(A *rmw, C arg, C *orig, memory_order);
+ *     void atomic_sub_explicit(A *rmw, C arg, C *orig, memory_order);
+ *     void atomic_or_explicit(A *rmw, C arg, C *orig, memory_order);
+ *     void atomic_xor_explicit(A *rmw, C arg, C *orig, memory_order);
+ *     void atomic_and_explicit(A *rmw, C arg, C *orig, memory_order);
+ *
+ *         Atomically applies the given operation, with 'arg' as the second
+ *         operand, to '*rmw', and stores the original value of '*rmw' into
+ *         '*orig', respecting the given memory order (or memory_order_seq_cst
+ *         if none is specified).
+ *
+ *         The results are similar to those that would be obtained with +=, -=,
+ *         |=, ^=, or |= on non-atomic types.
+ *
+ *
+ * atomic_flag
+ * ===========
+ *
+ * atomic_flag is a typedef for a type with two states, set and clear, that
+ * provides atomic test-and-set functionality.
+ *
+ * ATOMIC_FLAG_INIT is an initializer for atomic_flag.  The initial state is
+ * "clear".
+ *
+ * The following functions are available.
+ *
+ *     bool atomic_flag_test_and_set(atomic_flag *object)
+ *     bool atomic_flag_test_and_set_explicit(atomic_flag *object,
+ *                                            memory_order);
+ *
+ *         Atomically sets '*object', respsecting the given memory order (or
+ *         memory_order_seq_cst for atomic_flag_test_and_set()).  Returns the
+ *         previous value of the flag (false for clear, true for set).
+ *
+ *     void atomic_flag_clear(atomic_flag *object);
+ *     void atomic_flag_clear_explicit(atomic_flag *object, memory_order);
+ *
+ *         Atomically clears '*object', respecting the given memory order (or
+ *         memory_order_seq_cst for atomic_flag_clear()).
+ */
+
+#include <limits.h>
+#include <pthread.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include "compiler.h"
+#include "util.h"
+
+#define IN_OVS_ATOMIC_H
+    #if __CHECKER__
+        /* sparse doesn't understand some GCC extensions we use. */
+        #include "ovs-atomic-pthreads.h"
+    #elif HAVE_STDATOMIC_H
+        #include "ovs-atomic-c11.h"
+    #elif __GNUC__ >= 4 && __GNUC_MINOR__ >= 7
+        #include "ovs-atomic-gcc4.7+.h"
+    #elif __GNUC__ >= 4
+        #include "ovs-atomic-gcc4+.h"
+    #else
+        #include "ovs-atomic-pthreads.h"
+    #endif
+#undef IN_OVS_ATOMIC_H
+
+#endif /* ovs-atomic.h */
diff --git a/lib/ovs-thread.c b/lib/ovs-thread.c
new file mode 100644 (file)
index 0000000..d08751c
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 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.
+ * 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 "ovs-thread.h"
+#include <errno.h>
+#include <poll.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "compiler.h"
+#include "poll-loop.h"
+#include "socket-util.h"
+#include "util.h"
+
+#ifdef __CHECKER__
+/* Omit the definitions in this file because they are somewhat difficult to
+ * write without prompting "sparse" complaints, without ugliness or
+ * cut-and-paste.  Since "sparse" is just a checker, not a compiler, it
+ * doesn't matter that we don't define them. */
+#else
+#include "vlog.h"
+
+VLOG_DEFINE_THIS_MODULE(ovs_thread);
+
+/* If there is a reason that we cannot fork anymore (unless the fork will be
+ * immediately followed by an exec), then this points to a string that
+ * explains why. */
+static const char *must_not_fork;
+
+/* True if we created any threads beyond the main initial thread. */
+static bool multithreaded;
+
+#define XPTHREAD_FUNC1(FUNCTION, PARAM1)                \
+    void                                                \
+    x##FUNCTION(PARAM1 arg1)                            \
+    {                                                   \
+        int error = FUNCTION(arg1);                     \
+        if (OVS_UNLIKELY(error)) {                      \
+            ovs_abort(error, "%s failed", #FUNCTION);   \
+        }                                               \
+    }
+#define XPTHREAD_TRY_FUNC1(FUNCTION, PARAM1)            \
+    int                                                 \
+    x##FUNCTION(PARAM1 arg1)                            \
+    {                                                   \
+        int error = FUNCTION(arg1);                     \
+        if (OVS_UNLIKELY(error && error != EBUSY)) {    \
+            ovs_abort(error, "%s failed", #FUNCTION);   \
+        }                                               \
+        return error;                                   \
+    }
+#define XPTHREAD_FUNC2(FUNCTION, PARAM1, PARAM2)        \
+    void                                                \
+    x##FUNCTION(PARAM1 arg1, PARAM2 arg2)               \
+    {                                                   \
+        int error = FUNCTION(arg1, arg2);               \
+        if (OVS_UNLIKELY(error)) {                      \
+            ovs_abort(error, "%s failed", #FUNCTION);   \
+        }                                               \
+    }
+
+XPTHREAD_FUNC2(pthread_mutex_init, pthread_mutex_t *, pthread_mutexattr_t *);
+XPTHREAD_FUNC1(pthread_mutex_lock, pthread_mutex_t *);
+XPTHREAD_FUNC1(pthread_mutex_unlock, pthread_mutex_t *);
+XPTHREAD_TRY_FUNC1(pthread_mutex_trylock, pthread_mutex_t *);
+
+XPTHREAD_FUNC2(pthread_rwlock_init,
+               pthread_rwlock_t *, pthread_rwlockattr_t *);
+XPTHREAD_FUNC1(pthread_rwlock_rdlock, pthread_rwlock_t *);
+XPTHREAD_FUNC1(pthread_rwlock_wrlock, pthread_rwlock_t *);
+XPTHREAD_FUNC1(pthread_rwlock_unlock, pthread_rwlock_t *);
+XPTHREAD_TRY_FUNC1(pthread_rwlock_tryrdlock, pthread_rwlock_t *);
+XPTHREAD_TRY_FUNC1(pthread_rwlock_trywrlock, pthread_rwlock_t *);
+
+XPTHREAD_FUNC2(pthread_cond_init, pthread_cond_t *, pthread_condattr_t *);
+XPTHREAD_FUNC1(pthread_cond_signal, pthread_cond_t *);
+XPTHREAD_FUNC1(pthread_cond_broadcast, pthread_cond_t *);
+XPTHREAD_FUNC2(pthread_cond_wait, pthread_cond_t *, pthread_mutex_t *);
+
+typedef void destructor_func(void *);
+XPTHREAD_FUNC2(pthread_key_create, pthread_key_t *, destructor_func *);
+
+void
+xpthread_create(pthread_t *threadp, pthread_attr_t *attr,
+                void *(*start)(void *), void *arg)
+{
+    pthread_t thread;
+    int error;
+
+    forbid_forking("multiple threads exist");
+    multithreaded = true;
+
+    error = pthread_create(threadp ? threadp : &thread, attr, start, arg);
+    if (error) {
+        ovs_abort(error, "pthread_create failed");
+    }
+}
+\f
+bool
+ovsthread_once_start__(struct ovsthread_once *once)
+{
+    xpthread_mutex_lock(&once->mutex);
+    if (!ovsthread_once_is_done__(once)) {
+        return false;
+    }
+    xpthread_mutex_unlock(&once->mutex);
+    return true;
+}
+
+void OVS_RELEASES(once)
+ovsthread_once_done(struct ovsthread_once *once)
+{
+    atomic_store(&once->done, true);
+    xpthread_mutex_unlock(&once->mutex);
+}
+\f
+/* Asserts that the process has not yet created any threads (beyond the initial
+ * thread).  */
+void
+(assert_single_threaded)(const char *where)
+{
+    if (multithreaded) {
+        VLOG_FATAL("%s: attempted operation not allowed when multithreaded",
+                   where);
+    }
+}
+
+/* Forks the current process (checking that this is allowed).  Aborts with
+ * VLOG_FATAL if fork() returns an error, and otherwise returns the value
+ * returned by fork().  */
+pid_t
+(xfork)(const char *where)
+{
+    pid_t pid;
+
+    if (must_not_fork) {
+        VLOG_FATAL("%s: attempted to fork but forking not allowed (%s)",
+                   where, must_not_fork);
+    }
+
+    pid = fork();
+    if (pid < 0) {
+        VLOG_FATAL("fork failed (%s)", ovs_strerror(errno));
+    }
+    return pid;
+}
+
+/* Notes that the process must not call fork() from now on, for the specified
+ * 'reason'.  (The process may still fork() if it execs itself immediately
+ * afterward.) */
+void
+forbid_forking(const char *reason)
+{
+    ovs_assert(reason != NULL);
+    must_not_fork = reason;
+}
+
+/* Returns true if the process is allowed to fork, false otherwise. */
+bool
+may_fork(void)
+{
+    return !must_not_fork;
+}
+#endif
diff --git a/lib/ovs-thread.h b/lib/ovs-thread.h
new file mode 100644 (file)
index 0000000..9c8023e
--- /dev/null
@@ -0,0 +1,380 @@
+/*
+ * Copyright (c) 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.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef OVS_THREAD_H
+#define OVS_THREAD_H 1
+
+#include <pthread.h>
+#include <stddef.h>
+#include <sys/types.h>
+#include "ovs-atomic.h"
+#include "util.h"
+
+/* glibc has some non-portable mutex types and initializers:
+ *
+ *    - PTHREAD_MUTEX_ADAPTIVE_NP is a mutex type that works as a spinlock that
+ *      falls back to a mutex after spinning for some number of iterations.
+ *
+ *    - PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP is a non-portable initializer
+ *      for an error-checking mutex.
+ *
+ * We use these definitions to fall back to PTHREAD_MUTEX_NORMAL instead in
+ * these cases.
+ *
+ * (glibc has other non-portable initializers, but we can't reasonably
+ * substitute for them here.) */
+#ifdef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP
+#define PTHREAD_MUTEX_ADAPTIVE PTHREAD_MUTEX_ADAPTIVE_NP
+#define PTHREAD_ADAPTIVE_MUTEX_INITIALIZER \
+    PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP
+#else
+#define PTHREAD_MUTEX_ADAPTIVE PTHREAD_MUTEX_NORMAL
+#define PTHREAD_ADAPTIVE_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER
+#endif
+
+#ifdef PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP
+#define PTHREAD_ERRORCHECK_MUTEX_INITIALIZER \
+    PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP
+#else
+#define PTHREAD_ERRORCHECK_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER
+#endif
+\f
+/* Simple wrappers for pthreads functions.  Most of these functions abort the
+ * process with an error message on any error.  The *_trylock() functions are
+ * exceptions: they pass through a 0 or EBUSY return value to the caller and
+ * abort on any other error. */
+void xpthread_mutex_init(pthread_mutex_t *, pthread_mutexattr_t *);
+void xpthread_mutex_lock(pthread_mutex_t *mutex) OVS_ACQUIRES(mutex);
+void xpthread_mutex_unlock(pthread_mutex_t *mutex) OVS_RELEASES(mutex);
+int xpthread_mutex_trylock(pthread_mutex_t *);
+
+void xpthread_rwlock_init(pthread_rwlock_t *, pthread_rwlockattr_t *);
+void xpthread_rwlock_rdlock(pthread_rwlock_t *rwlock) OVS_ACQUIRES(rwlock);
+void xpthread_rwlock_wrlock(pthread_rwlock_t *rwlock) OVS_ACQUIRES(rwlock);
+void xpthread_rwlock_unlock(pthread_rwlock_t *rwlock) OVS_RELEASES(rwlock);
+int xpthread_rwlock_tryrdlock(pthread_rwlock_t *);
+int xpthread_rwlock_trywrlock(pthread_rwlock_t *);
+
+void xpthread_cond_init(pthread_cond_t *, pthread_condattr_t *);
+void xpthread_cond_signal(pthread_cond_t *);
+void xpthread_cond_broadcast(pthread_cond_t *);
+void xpthread_cond_wait(pthread_cond_t *, pthread_mutex_t *mutex)
+    OVS_MUST_HOLD(mutex);
+
+#ifdef __CHECKER__
+/* Replace these functions by the macros already defined in the <pthread.h>
+ * annotations, because the macro definitions have correct semantics for the
+ * conditional acquisition that can't be captured in a function annotation.
+ * The difference in semantics from pthread_*() to xpthread_*() does not matter
+ * because sparse is not a compiler. */
+#define xpthread_mutex_trylock pthread_mutex_trylock
+#define xpthread_rwlock_tryrdlock pthread_rwlock_tryrdlock
+#define xpthread_rwlock_trywrlock pthread_rwlock_trywrlock
+#endif
+
+void xpthread_key_create(pthread_key_t *, void (*destructor)(void *));
+
+void xpthread_create(pthread_t *, pthread_attr_t *, void *(*)(void *), void *);
+\f
+/* Per-thread data.
+ *
+ * Multiple forms of per-thread data exist, each with its own pluses and
+ * minuses:
+ *
+ *     - POSIX per-thread data via pthread_key_t is portable to any pthreads
+ *       implementation, and allows a destructor function to be defined.  It
+ *       only (directly) supports per-thread pointers, which are always
+ *       initialized to NULL.  It requires once-only allocation of a
+ *       pthread_key_t value.  It is relatively slow.
+ *
+ *     - The thread_local feature newly defined in C11 <threads.h> works with
+ *       any data type and initializer, and it is fast.  thread_local does not
+ *       require once-only initialization like pthread_key_t.  C11 does not
+ *       define what happens if one attempts to access a thread_local object
+ *       from a thread other than the one to which that object belongs.  There
+ *       is no provision to call a user-specified destructor when a thread
+ *       ends.
+ *
+ *     - The __thread keyword is a GCC extension similar to thread_local but
+ *       with a longer history.  __thread is not portable to every GCC version
+ *       or environment.  __thread does not restrict the use of a thread-local
+ *       object outside its own thread.
+ *
+ * Here's a handy summary:
+ *
+ *                     pthread_key_t     thread_local       __thread
+ *                     -------------     ------------     -------------
+ * portability             high               low             medium
+ * speed                    low              high               high
+ * supports destructors?    yes                no                 no
+ * needs key allocation?    yes                no                 no
+ * arbitrary initializer?    no               yes                yes
+ * cross-thread access?     yes                no                yes
+ */
+
+/* DEFINE_PER_THREAD_DATA(TYPE, NAME, INITIALIZER).
+ *
+ * One should prefer to use POSIX per-thread data, via pthread_key_t, when its
+ * performance is acceptable, because of its portability (see the table above).
+ * This macro is an alternatives that takes advantage of thread_local (and
+ * __thread), for its performance, when it is available, and falls back to
+ * POSIX per-thread data otherwise.
+ *
+ * Defines per-thread variable NAME with the given TYPE, initialized to
+ * INITIALIZER (which must be valid as an initializer for a variable with
+ * static lifetime).
+ *
+ * The public interface to the variable is:
+ *
+ *    TYPE *NAME_get(void)
+ *    TYPE *NAME_get_unsafe(void)
+ *
+ *       Returns the address of this thread's instance of NAME.
+ *
+ *       Use NAME_get() in a context where this might be the first use of the
+ *       per-thread variable in the program.  Use NAME_get_unsafe(), which
+ *       avoids a conditional test and is thus slightly faster, in a context
+ *       where one knows that NAME_get() has already been called previously.
+ *
+ * There is no "NAME_set()" (or "NAME_set_unsafe()") function.  To set the
+ * value of the per-thread variable, dereference the pointer returned by
+ * TYPE_get() or TYPE_get_unsafe(), e.g. *TYPE_get() = 0.
+ */
+#if HAVE_THREAD_LOCAL || HAVE___THREAD
+
+#if HAVE_THREAD_LOCAL
+#include <threads.h>
+#elif HAVE___THREAD
+#define thread_local __thread
+#else
+#error
+#endif
+
+#define DEFINE_PER_THREAD_DATA(TYPE, NAME, ...)                 \
+    typedef TYPE NAME##_type;                                   \
+    static thread_local NAME##_type NAME##_var = __VA_ARGS__;   \
+                                                                \
+    static NAME##_type *                                        \
+    NAME##_get_unsafe(void)                                     \
+    {                                                           \
+        return &NAME##_var;                                     \
+    }                                                           \
+                                                                \
+    static NAME##_type *                                        \
+    NAME##_get(void)                                            \
+    {                                                           \
+        return NAME##_get_unsafe();                             \
+    }
+#else  /* no C implementation support for thread-local storage  */
+#define DEFINE_PER_THREAD_DATA(TYPE, NAME, ...)                         \
+    typedef TYPE NAME##_type;                                           \
+    static pthread_key_t NAME##_key;                                    \
+                                                                        \
+    static NAME##_type *                                                \
+    NAME##_get_unsafe(void)                                             \
+    {                                                                   \
+        return pthread_getspecific(NAME##_key);                         \
+    }                                                                   \
+                                                                        \
+    static void                                                         \
+    NAME##_once_init(void)                                              \
+    {                                                                   \
+        if (pthread_key_create(&NAME##_key, free)) {                    \
+            abort();                                                    \
+        }                                                               \
+    }                                                                   \
+                                                                        \
+    static NAME##_type *                                                \
+    NAME##_get(void)                                                    \
+    {                                                                   \
+        static pthread_once_t once = PTHREAD_ONCE_INIT;                 \
+        NAME##_type *value;                                             \
+                                                                        \
+        pthread_once(&once, NAME##_once_init);                          \
+        value = NAME##_get_unsafe();                                    \
+        if (!value) {                                                   \
+            static const NAME##_type initial_value = __VA_ARGS__;       \
+                                                                        \
+            value = xmalloc(sizeof *value);                             \
+            *value = initial_value;                                     \
+            pthread_setspecific(NAME##_key, value);                     \
+        }                                                               \
+        return value;                                                   \
+    }
+#endif
+
+/* DEFINE_PER_THREAD_MALLOCED_DATA(TYPE, NAME).
+ *
+ * This is a simple wrapper around POSIX per-thread data primitives.  It
+ * defines per-thread variable NAME with the given TYPE, which must be a
+ * pointer type.  In each thread, the per-thread variable is initialized to
+ * NULL.  When a thread terminates, the variable is freed with free().
+ *
+ * The public interface to the variable is:
+ *
+ *    TYPE NAME_get(void)
+ *    TYPE NAME_get_unsafe(void)
+ *
+ *       Returns the value of per-thread variable NAME in this thread.
+ *
+ *       Use NAME_get() in a context where this might be the first use of the
+ *       per-thread variable in the program.  Use NAME_get_unsafe(), which
+ *       avoids a conditional test and is thus slightly faster, in a context
+ *       where one knows that NAME_get() has already been called previously.
+ *
+ *    TYPE NAME_set(TYPE new_value)
+ *    TYPE NAME_set_unsafe(TYPE new_value)
+ *
+ *       Sets the value of per-thread variable NAME to 'new_value' in this
+ *       thread, and returns its previous value.
+ *
+ *       Use NAME_set() in a context where this might be the first use of the
+ *       per-thread variable in the program.  Use NAME_set_unsafe(), which
+ *       avoids a conditional test and is thus slightly faster, in a context
+ *       where one knows that NAME_set() has already been called previously.
+ */
+#define DEFINE_PER_THREAD_MALLOCED_DATA(TYPE, NAME)     \
+    static pthread_key_t NAME##_key;                    \
+                                                        \
+    static void                                         \
+    NAME##_once_init(void)                              \
+    {                                                   \
+        if (pthread_key_create(&NAME##_key, free)) {    \
+            abort();                                    \
+        }                                               \
+    }                                                   \
+                                                        \
+    static void                                         \
+    NAME##_init(void)                                   \
+    {                                                   \
+        static pthread_once_t once = PTHREAD_ONCE_INIT; \
+        pthread_once(&once, NAME##_once_init);          \
+    }                                                   \
+                                                        \
+    static TYPE                                         \
+    NAME##_get_unsafe(void)                             \
+    {                                                   \
+        return pthread_getspecific(NAME##_key);         \
+    }                                                   \
+                                                        \
+    static OVS_UNUSED TYPE                              \
+    NAME##_get(void)                                    \
+    {                                                   \
+        NAME##_init();                                  \
+        return NAME##_get_unsafe();                     \
+    }                                                   \
+                                                        \
+    static TYPE                                         \
+    NAME##_set_unsafe(TYPE value)                       \
+    {                                                   \
+        TYPE old_value = NAME##_get_unsafe();           \
+        pthread_setspecific(NAME##_key, value);         \
+        return old_value;                               \
+    }                                                   \
+                                                        \
+    static OVS_UNUSED TYPE                              \
+    NAME##_set(TYPE value)                              \
+    {                                                   \
+        NAME##_init();                                  \
+        return NAME##_set_unsafe(value);                \
+    }
+\f
+/* Convenient once-only execution.
+ *
+ *
+ * Problem
+ * =======
+ *
+ * POSIX provides pthread_once_t and pthread_once() as primitives for running a
+ * set of code only once per process execution.  They are used like this:
+ *
+ *     static void run_once(void) { ...initialization... }
+ *     static pthread_once_t once = PTHREAD_ONCE_INIT;
+ * ...
+ *     pthread_once(&once, run_once);
+ *
+ * pthread_once() does not allow passing any parameters to the initialization
+ * function, which is often inconvenient, because it means that the function
+ * can only access data declared at file scope.
+ *
+ *
+ * Solution
+ * ========
+ *
+ * Use ovsthread_once, like this, instead:
+ *
+ *     static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
+ *
+ *     if (ovsthread_once_start(&once)) {
+ *         ...initialization...
+ *         ovsthread_once_done(&once);
+ *     }
+ */
+
+struct ovsthread_once {
+    atomic_bool done;
+    pthread_mutex_t mutex;
+};
+
+#define OVSTHREAD_ONCE_INITIALIZER              \
+    {                                           \
+        ATOMIC_VAR_INIT(false),                 \
+        PTHREAD_ADAPTIVE_MUTEX_INITIALIZER,     \
+    }
+
+static inline bool ovsthread_once_start(struct ovsthread_once *);
+void ovsthread_once_done(struct ovsthread_once *once) OVS_RELEASES(once);
+
+bool ovsthread_once_start__(struct ovsthread_once *);
+
+static inline bool
+ovsthread_once_is_done__(const struct ovsthread_once *once)
+{
+    bool done;
+
+    atomic_read_explicit(&once->done, &done, memory_order_relaxed);
+    return done;
+}
+
+/* Returns true if this is the first call to ovsthread_once_start() for
+ * 'once'.  In this case, the caller should perform whatever initialization
+ * actions it needs to do, then call ovsthread_once_done() for 'once'.
+ *
+ * Returns false if this is not the first call to ovsthread_once_start() for
+ * 'once'.  In this case, the call will not return until after
+ * ovsthread_once_done() has been called. */
+static inline bool
+ovsthread_once_start(struct ovsthread_once *once)
+{
+    return OVS_UNLIKELY(!ovsthread_once_is_done__(once)
+                        && !ovsthread_once_start__(once));
+}
+
+#ifdef __CHECKER__
+#define ovsthread_once_start(ONCE) \
+    ((ONCE)->done ? false : ({ OVS_ACQUIRE(ONCE); true; }))
+#endif
+\f
+void assert_single_threaded(const char *where);
+#define assert_single_threaded() assert_single_threaded(SOURCE_LOCATOR)
+
+pid_t xfork(const char *where);
+#define xfork() xfork(SOURCE_LOCATOR)
+
+void forbid_forking(const char *reason);
+bool may_fork(void);
+
+#endif /* ovs-thread.h */
index 77aa7d3..7fe6513 100644 (file)
@@ -59,22 +59,22 @@ eth_addr_is_reserved(const uint8_t ea[ETH_ADDR_LEN])
 
     static struct eth_addr_node nodes[] = {
         /* STP, IEEE pause frames, and other reserved protocols. */
-        { HMAP_NODE_NULL_INITIALIZER, 0x0108c2000000ULL },
-        { HMAP_NODE_NULL_INITIALIZER, 0x0108c2000001ULL },
-        { HMAP_NODE_NULL_INITIALIZER, 0x0108c2000002ULL },
-        { HMAP_NODE_NULL_INITIALIZER, 0x0108c2000003ULL },
-        { HMAP_NODE_NULL_INITIALIZER, 0x0108c2000004ULL },
-        { HMAP_NODE_NULL_INITIALIZER, 0x0108c2000005ULL },
-        { HMAP_NODE_NULL_INITIALIZER, 0x0108c2000006ULL },
-        { HMAP_NODE_NULL_INITIALIZER, 0x0108c2000007ULL },
-        { HMAP_NODE_NULL_INITIALIZER, 0x0108c2000008ULL },
-        { HMAP_NODE_NULL_INITIALIZER, 0x0108c2000009ULL },
-        { HMAP_NODE_NULL_INITIALIZER, 0x0108c200000aULL },
-        { HMAP_NODE_NULL_INITIALIZER, 0x0108c200000bULL },
-        { HMAP_NODE_NULL_INITIALIZER, 0x0108c200000cULL },
-        { HMAP_NODE_NULL_INITIALIZER, 0x0108c200000dULL },
-        { HMAP_NODE_NULL_INITIALIZER, 0x0108c200000eULL },
-        { HMAP_NODE_NULL_INITIALIZER, 0x0108c200000fULL },
+        { HMAP_NODE_NULL_INITIALIZER, 0x0180c2000000ULL },
+        { HMAP_NODE_NULL_INITIALIZER, 0x0180c2000001ULL },
+        { HMAP_NODE_NULL_INITIALIZER, 0x0180c2000002ULL },
+        { HMAP_NODE_NULL_INITIALIZER, 0x0180c2000003ULL },
+        { HMAP_NODE_NULL_INITIALIZER, 0x0180c2000004ULL },
+        { HMAP_NODE_NULL_INITIALIZER, 0x0180c2000005ULL },
+        { HMAP_NODE_NULL_INITIALIZER, 0x0180c2000006ULL },
+        { HMAP_NODE_NULL_INITIALIZER, 0x0180c2000007ULL },
+        { HMAP_NODE_NULL_INITIALIZER, 0x0180c2000008ULL },
+        { HMAP_NODE_NULL_INITIALIZER, 0x0180c2000009ULL },
+        { HMAP_NODE_NULL_INITIALIZER, 0x0180c200000aULL },
+        { HMAP_NODE_NULL_INITIALIZER, 0x0180c200000bULL },
+        { HMAP_NODE_NULL_INITIALIZER, 0x0180c200000cULL },
+        { HMAP_NODE_NULL_INITIALIZER, 0x0180c200000dULL },
+        { HMAP_NODE_NULL_INITIALIZER, 0x0180c200000eULL },
+        { HMAP_NODE_NULL_INITIALIZER, 0x0180c200000fULL },
 
         /* Extreme protocols. */
         { HMAP_NODE_NULL_INITIALIZER, 0x00e02b000000ULL }, /* EDP. */
@@ -373,9 +373,8 @@ pop_mpls(struct ofpbuf *packet, ovs_be16 ethtype)
         size_t len;
         mh = packet->l2_5;
         len = (char*)packet->l2_5 - (char*)packet->l2;
-        /* If bottom of the stack set ethertype. */
+        set_ethertype(packet, ethtype);
         if (mh->mpls_lse & htonl(MPLS_BOS_MASK)) {
-            set_ethertype(packet, ethtype);
             packet->l2_5 = NULL;
         } else {
             packet->l2_5 = (char*)packet->l2_5 + MPLS_HLEN;
index b73ff63..cc9ab3d 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.
@@ -259,6 +259,7 @@ BUILD_ASSERT_DECL(LLC_SNAP_HEADER_LEN == sizeof(struct llc_snap_header));
 #define VLAN_PCP_SHIFT 13
 
 #define VLAN_CFI 0x1000
+#define VLAN_CFI_SHIFT 12
 
 /* Given the vlan_tci field from an 802.1Q header, in network byte order,
  * returns the VLAN ID in host byte order. */
@@ -276,6 +277,14 @@ vlan_tci_to_pcp(ovs_be16 vlan_tci)
     return (ntohs(vlan_tci) & VLAN_PCP_MASK) >> VLAN_PCP_SHIFT;
 }
 
+/* Given the vlan_tci field from an 802.1Q header, in network byte order,
+ * returns the Canonical Format Indicator (CFI). */
+static inline int
+vlan_tci_to_cfi(ovs_be16 vlan_tci)
+{
+    return (vlan_tci & htons(VLAN_CFI)) != 0;
+}
+
 #define VLAN_HEADER_LEN 4
 struct vlan_header {
     ovs_be16 vlan_tci;          /* Lowest 12 bits are VLAN ID. */
index 9855306..ea00d26 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.
@@ -249,7 +249,7 @@ poll_block(void)
     retval = time_poll(pollfds, n_pollfds, timeout_when, &elapsed);
     if (retval < 0) {
         static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
-        VLOG_ERR_RL(&rl, "poll: %s", strerror(-retval));
+        VLOG_ERR_RL(&rl, "poll: %s", ovs_strerror(-retval));
     } else if (!retval) {
         log_wakeup(timeout_where, NULL, elapsed);
     }
index 9fe742c..143347c 100644 (file)
@@ -28,6 +28,7 @@
 #include "dynamic-string.h"
 #include "fatal-signal.h"
 #include "list.h"
+#include "ovs-thread.h"
 #include "poll-loop.h"
 #include "signals.h"
 #include "socket-util.h"
@@ -36,8 +37,6 @@
 
 VLOG_DEFINE_THIS_MODULE(process);
 
-COVERAGE_DEFINE(process_run);
-COVERAGE_DEFINE(process_run_capture);
 COVERAGE_DEFINE(process_sigchld);
 COVERAGE_DEFINE(process_start);
 
@@ -46,9 +45,9 @@ struct process {
     char *name;
     pid_t pid;
 
-    /* Modified by signal handler. */
-    volatile bool exited;
-    volatile int status;
+    /* State. */
+    bool exited;
+    int status;
 };
 
 /* Pipe used to signal child termination. */
@@ -57,15 +56,13 @@ static int fds[2];
 /* All processes. */
 static struct list all_processes = LIST_INITIALIZER(&all_processes);
 
-static bool sigchld_is_blocked(void);
-static void block_sigchld(sigset_t *);
-static void unblock_sigchld(const sigset_t *);
 static void sigchld_handler(int signr OVS_UNUSED);
-static bool is_member(int x, const int *array, size_t);
 
 /* Initializes the process subsystem (if it is not already initialized).  Calls
  * exit() if initialization fails.
  *
+ * This function may not be called after creating any additional threads.
+ *
  * Calling this function is optional; it will be called automatically by
  * process_start() if necessary.  Calling it explicitly allows the client to
  * prevent the process from exiting at an unexpected time. */
@@ -75,6 +72,7 @@ process_init(void)
     static bool inited;
     struct sigaction sa;
 
+    assert_single_threaded();
     if (inited) {
         return;
     }
@@ -148,18 +146,13 @@ process_prestart(char **argv)
 }
 
 /* Creates and returns a new struct process with the specified 'name' and
- * 'pid'.
- *
- * This is racy unless SIGCHLD is blocked (and has been blocked since before
- * the fork()) that created the subprocess.  */
+ * 'pid'. */
 static struct process *
 process_register(const char *name, pid_t pid)
 {
     struct process *p;
     const char *slash;
 
-    ovs_assert(sigchld_is_blocked());
-
     p = xzalloc(sizeof *p);
     p->pid = pid;
     slash = strrchr(name, '/');
@@ -175,24 +168,22 @@ process_register(const char *name, pid_t pid)
  * argv[0] is used as the name of the process.  Searches the PATH environment
  * variable to find the program to execute.
  *
+ * This function may not be called after creating any additional threads.
+ *
  * All file descriptors are closed before executing the subprocess, except for
- * fds 0, 1, and 2 and the 'n_keep_fds' fds listed in 'keep_fds'.  Also, any of
- * the 'n_null_fds' fds listed in 'null_fds' are replaced by /dev/null.
+ * fds 0, 1, and 2.
  *
  * Returns 0 if successful, otherwise a positive errno value indicating the
  * error.  If successful, '*pp' is assigned a new struct process that may be
  * used to query the process's status.  On failure, '*pp' is set to NULL. */
 int
-process_start(char **argv,
-              const int keep_fds[], size_t n_keep_fds,
-              const int null_fds[], size_t n_null_fds,
-              struct process **pp)
+process_start(char **argv, struct process **pp)
 {
-    sigset_t oldsigs;
-    int nullfd;
     pid_t pid;
     int error;
 
+    assert_single_threaded();
+
     *pp = NULL;
     COVERAGE_INC(process_start);
     error = process_prestart(argv);
@@ -200,25 +191,13 @@ process_start(char **argv,
         return error;
     }
 
-    if (n_null_fds) {
-        nullfd = get_null_fd();
-        if (nullfd < 0) {
-            return -nullfd;
-        }
-    } else {
-        nullfd = -1;
-    }
-
-    block_sigchld(&oldsigs);
     pid = fork();
     if (pid < 0) {
-        unblock_sigchld(&oldsigs);
-        VLOG_WARN("fork failed: %s", strerror(errno));
+        VLOG_WARN("fork failed: %s", ovs_strerror(errno));
         return errno;
     } else if (pid) {
         /* Running in parent process. */
         *pp = process_register(argv[0], pid);
-        unblock_sigchld(&oldsigs);
         return 0;
     } else {
         /* Running in child process. */
@@ -226,23 +205,12 @@ process_start(char **argv,
         int fd;
 
         fatal_signal_fork();
-        unblock_sigchld(&oldsigs);
-        for (fd = 0; fd < fd_max; fd++) {
-            if (is_member(fd, null_fds, n_null_fds)) {
-                dup2(nullfd, fd);
-            } else if (fd >= 3 && fd != nullfd
-                       && !is_member(fd, keep_fds, n_keep_fds)) {
-                close(fd);
-            }
-        }
-        if (nullfd >= 0
-            && !is_member(nullfd, keep_fds, n_keep_fds)
-            && !is_member(nullfd, null_fds, n_null_fds)) {
-            close(nullfd);
+        for (fd = 3; fd < fd_max; fd++) {
+            close(fd);
         }
         execvp(argv[0], argv);
         fprintf(stderr, "execvp(\"%s\") failed: %s\n",
-                argv[0], strerror(errno));
+                argv[0], ovs_strerror(errno));
         _exit(1);
     }
 }
@@ -252,12 +220,7 @@ void
 process_destroy(struct process *p)
 {
     if (p) {
-        sigset_t oldsigs;
-
-        block_sigchld(&oldsigs);
         list_remove(&p->node);
-        unblock_sigchld(&oldsigs);
-
         free(p->name);
         free(p);
     }
@@ -292,13 +255,7 @@ process_name(const struct process *p)
 bool
 process_exited(struct process *p)
 {
-    if (p->exited) {
-        return true;
-    } else {
-        char buf[_POSIX_PIPE_BUF];
-        ignore(read(fds[0], buf, sizeof buf));
-        return false;
-    }
+    return p->exited;
 }
 
 /* Returns process 'p''s exit status, as reported by waitpid(2).
@@ -311,32 +268,6 @@ process_status(const struct process *p)
     return p->status;
 }
 
-int
-process_run(char **argv,
-            const int keep_fds[], size_t n_keep_fds,
-            const int null_fds[], size_t n_null_fds,
-            int *status)
-{
-    struct process *p;
-    int retval;
-
-    COVERAGE_INC(process_run);
-    retval = process_start(argv, keep_fds, n_keep_fds, null_fds, n_null_fds,
-                           &p);
-    if (retval) {
-        *status = 0;
-        return retval;
-    }
-
-    while (!process_exited(p)) {
-        process_wait(p);
-        poll_block();
-    }
-    *status = process_status(p);
-    process_destroy(p);
-    return 0;
-}
-
 /* Given 'status', which is a process status in the form reported by waitpid(2)
  * and returned by process_status(), returns a string describing how the
  * process terminated.  The caller is responsible for freeing the string when
@@ -348,9 +279,15 @@ process_status_msg(int status)
     if (WIFEXITED(status)) {
         ds_put_format(&ds, "exit status %d", WEXITSTATUS(status));
     } else if (WIFSIGNALED(status)) {
-        ds_put_format(&ds, "killed (%s)", signal_name(WTERMSIG(status)));
+        char namebuf[SIGNAL_NAME_BUFSIZE];
+
+        ds_put_format(&ds, "killed (%s)",
+                      signal_name(WTERMSIG(status), namebuf, sizeof namebuf));
     } else if (WIFSTOPPED(status)) {
-        ds_put_format(&ds, "stopped (%s)", signal_name(WSTOPSIG(status)));
+        char namebuf[SIGNAL_NAME_BUFSIZE];
+
+        ds_put_format(&ds, "stopped (%s)",
+                      signal_name(WSTOPSIG(status), namebuf, sizeof namebuf));
     } else {
         ds_put_format(&ds, "terminated abnormally (%x)", status);
     }
@@ -360,6 +297,35 @@ process_status_msg(int status)
     return ds_cstr(&ds);
 }
 
+/* Executes periodic maintenance activities required by the process module. */
+void
+process_run(void)
+{
+    char buf[_POSIX_PIPE_BUF];
+
+    if (!list_is_empty(&all_processes) && read(fds[0], buf, sizeof buf) > 0) {
+        struct process *p;
+
+        LIST_FOR_EACH (p, node, &all_processes) {
+            if (!p->exited) {
+                int retval, status;
+                do {
+                    retval = waitpid(p->pid, &status, WNOHANG);
+                } while (retval == -1 && errno == EINTR);
+                if (retval == p->pid) {
+                    p->exited = true;
+                    p->status = status;
+                } else if (retval < 0) {
+                    VLOG_WARN("waitpid: %s", ovs_strerror(errno));
+                    p->exited = true;
+                    p->status = -1;
+                }
+            }
+        }
+    }
+}
+
+
 /* Causes the next call to poll_block() to wake up when process 'p' has
  * exited. */
 void
@@ -397,265 +363,8 @@ process_search_path(const char *name)
     return NULL;
 }
 \f
-/* process_run_capture() and supporting functions. */
-
-struct stream {
-    size_t max_size;
-    struct ds log;
-    int fds[2];
-};
-
-static int
-stream_open(struct stream *s, size_t max_size)
-{
-    int error;
-
-    s->max_size = max_size;
-    ds_init(&s->log);
-    if (pipe(s->fds)) {
-        VLOG_WARN("failed to create pipe: %s", strerror(errno));
-        return errno;
-    }
-    error = set_nonblocking(s->fds[0]);
-    if (error) {
-        close(s->fds[0]);
-        close(s->fds[1]);
-    }
-    return error;
-}
-
-static void
-stream_read(struct stream *s)
-{
-    if (s->fds[0] < 0) {
-        return;
-    }
-
-    for (;;) {
-        char buffer[512];
-        int error;
-        size_t n;
-
-        error = read_fully(s->fds[0], buffer, sizeof buffer, &n);
-        ds_put_buffer(&s->log, buffer, n);
-        if (error) {
-            if (error == EAGAIN || error == EWOULDBLOCK) {
-                return;
-            } else {
-                if (error != EOF) {
-                    VLOG_WARN("error reading subprocess pipe: %s",
-                              strerror(error));
-                }
-                break;
-            }
-        } else if (s->log.length > s->max_size) {
-            VLOG_WARN("subprocess output overflowed %zu-byte buffer",
-                      s->max_size);
-            break;
-        }
-    }
-    close(s->fds[0]);
-    s->fds[0] = -1;
-}
-
-static void
-stream_wait(struct stream *s)
-{
-    if (s->fds[0] >= 0) {
-        poll_fd_wait(s->fds[0], POLLIN);
-    }
-}
-
-static void
-stream_close(struct stream *s)
-{
-    ds_destroy(&s->log);
-    if (s->fds[0] >= 0) {
-        close(s->fds[0]);
-    }
-    if (s->fds[1] >= 0) {
-        close(s->fds[1]);
-    }
-}
-
-/* Starts the process whose arguments are given in the null-terminated array
- * 'argv' and waits for it to exit.  On success returns 0 and stores the
- * process exit value (suitable for passing to process_status_msg()) in
- * '*status'.  On failure, returns a positive errno value and stores 0 in
- * '*status'.
- *
- * If 'stdout_log' is nonnull, then the subprocess's output to stdout (up to a
- * limit of 'log_max' bytes) is captured in a memory buffer, which
- * when this function returns 0 is stored as a null-terminated string in
- * '*stdout_log'.  The caller is responsible for freeing '*stdout_log' (by
- * passing it to free()).  When this function returns an error, '*stdout_log'
- * is set to NULL.
- *
- * If 'stderr_log' is nonnull, then it is treated like 'stdout_log' except
- * that it captures the subprocess's output to stderr. */
-int
-process_run_capture(char **argv, char **stdout_log, char **stderr_log,
-                    size_t max_log, int *status)
-{
-    struct stream s_stdout, s_stderr;
-    sigset_t oldsigs;
-    pid_t pid;
-    int error;
-
-    COVERAGE_INC(process_run_capture);
-    if (stdout_log) {
-        *stdout_log = NULL;
-    }
-    if (stderr_log) {
-        *stderr_log = NULL;
-    }
-    *status = 0;
-    error = process_prestart(argv);
-    if (error) {
-        return error;
-    }
-
-    error = stream_open(&s_stdout, max_log);
-    if (error) {
-        return error;
-    }
-
-    error = stream_open(&s_stderr, max_log);
-    if (error) {
-        stream_close(&s_stdout);
-        return error;
-    }
-
-    block_sigchld(&oldsigs);
-    pid = fork();
-    if (pid < 0) {
-        error = errno;
-
-        unblock_sigchld(&oldsigs);
-        VLOG_WARN("fork failed: %s", strerror(error));
-
-        stream_close(&s_stdout);
-        stream_close(&s_stderr);
-        *status = 0;
-        return error;
-    } else if (pid) {
-        /* Running in parent process. */
-        struct process *p;
-
-        p = process_register(argv[0], pid);
-        unblock_sigchld(&oldsigs);
-
-        close(s_stdout.fds[1]);
-        close(s_stderr.fds[1]);
-        while (!process_exited(p)) {
-            stream_read(&s_stdout);
-            stream_read(&s_stderr);
-
-            stream_wait(&s_stdout);
-            stream_wait(&s_stderr);
-            process_wait(p);
-            poll_block();
-        }
-        stream_read(&s_stdout);
-        stream_read(&s_stderr);
-
-        if (stdout_log) {
-            *stdout_log = ds_steal_cstr(&s_stdout.log);
-        }
-        if (stderr_log) {
-            *stderr_log = ds_steal_cstr(&s_stderr.log);
-        }
-
-        stream_close(&s_stdout);
-        stream_close(&s_stderr);
-
-        *status = process_status(p);
-        process_destroy(p);
-        return 0;
-    } else {
-        /* Running in child process. */
-        int max_fds;
-        int i;
-
-        fatal_signal_fork();
-        unblock_sigchld(&oldsigs);
-
-        dup2(get_null_fd(), 0);
-        dup2(s_stdout.fds[1], 1);
-        dup2(s_stderr.fds[1], 2);
-
-        max_fds = get_max_fds();
-        for (i = 3; i < max_fds; i++) {
-            close(i);
-        }
-
-        execvp(argv[0], argv);
-        fprintf(stderr, "execvp(\"%s\") failed: %s\n",
-                argv[0], strerror(errno));
-        exit(EXIT_FAILURE);
-    }
-}
-\f
 static void
 sigchld_handler(int signr OVS_UNUSED)
 {
-    struct process *p;
-
-    COVERAGE_INC(process_sigchld);
-    LIST_FOR_EACH (p, node, &all_processes) {
-        if (!p->exited) {
-            int retval, status;
-            do {
-                retval = waitpid(p->pid, &status, WNOHANG);
-            } while (retval == -1 && errno == EINTR);
-            if (retval == p->pid) {
-                p->exited = true;
-                p->status = status;
-            } else if (retval < 0) {
-                /* XXX We want to log something but we're in a signal
-                 * handler. */
-                p->exited = true;
-                p->status = -1;
-            }
-        }
-    }
     ignore(write(fds[1], "", 1));
 }
-
-static bool
-is_member(int x, const int *array, size_t n)
-{
-    size_t i;
-
-    for (i = 0; i < n; i++) {
-        if (array[i] == x) {
-            return true;
-        }
-    }
-    return false;
-}
-
-static bool
-sigchld_is_blocked(void)
-{
-    sigset_t sigs;
-
-    xpthread_sigmask(SIG_SETMASK, NULL, &sigs);
-    return sigismember(&sigs, SIGCHLD);
-}
-
-static void
-block_sigchld(sigset_t *oldsigs)
-{
-    sigset_t sigchld;
-
-    sigemptyset(&sigchld);
-    sigaddset(&sigchld, SIGCHLD);
-    xpthread_sigmask(SIG_BLOCK, &sigchld, oldsigs);
-}
-
-static void
-unblock_sigchld(const sigset_t *oldsigs)
-{
-    xpthread_sigmask(SIG_SETMASK, oldsigs, NULL);
-}
index d40c1ac..3feac7e 100644 (file)
 #include <sys/types.h>
 
 struct process;
+
+/* Starting and monitoring subprocesses.
+ *
+ * process_init() and process_start() may safely be called only from a
+ * single-threaded parent process.  The parent process may safely create
+ * additional threads afterward, as long as the remaining functions in this
+ * group are called only from a single thread at any given time. */
 void process_init(void);
-char *process_escape_args(char **argv);
-int process_start(char **argv,
-                  const int *keep_fds, size_t n_keep_fds,
-                  const int *null_fds, size_t n_null_fds,
-                  struct process **);
+int process_start(char **argv, struct process **);
 void process_destroy(struct process *);
 int process_kill(const struct process *, int signr);
-
-int process_run(char **argv,
-                const int *keep_fds, size_t n_keep_fds,
-                const int *null_fds, size_t n_null_fds,
-                int *status);
-
 pid_t process_pid(const struct process *);
 const char *process_name(const struct process *);
 bool process_exited(struct process *);
 int process_status(const struct process *);
-char *process_status_msg(int);
-
+void process_run(void);
 void process_wait(struct process *);
 
+/* These functions are thread-safe. */
+char *process_status_msg(int);
+char *process_escape_args(char **argv);
 char *process_search_path(const char *);
 
-int process_run_capture(char **argv, char **stdout_log, char **stderr_log,
-                        size_t max_log, int *status);
-
 #endif /* process.h */
index 4922a5c..e4caaa9 100644 (file)
@@ -379,7 +379,8 @@ reconnect(struct rconn *rc)
         rc->backoff_deadline = time_now() + rc->backoff;
         state_transition(rc, S_CONNECTING);
     } else {
-        VLOG_WARN("%s: connection failed (%s)", rc->name, strerror(retval));
+        VLOG_WARN("%s: connection failed (%s)",
+                  rc->name, ovs_strerror(retval));
         rc->backoff_deadline = TIME_MAX; /* Prevent resetting backoff. */
         disconnect(rc, retval);
     }
@@ -417,7 +418,7 @@ run_CONNECTING(struct rconn *rc)
     } else if (retval != EAGAIN) {
         if (rconn_logging_connection_attempts__(rc)) {
             VLOG_INFO("%s: connection failed (%s)",
-                      rc->name, strerror(retval));
+                      rc->name, ovs_strerror(retval));
         }
         disconnect(rc, retval);
     } else if (timed_out(rc)) {
@@ -962,7 +963,8 @@ report_error(struct rconn *rc, int error)
         enum vlog_level level = rc->reliable ? VLL_INFO : VLL_DBG;
         VLOG(level, "%s: connection closed by peer", rc->name);
     } else {
-        VLOG_WARN("%s: connection dropped (%s)", rc->name, strerror(error));
+        VLOG_WARN("%s: connection dropped (%s)",
+                  rc->name, ovs_strerror(error));
     }
 }
 
@@ -1137,19 +1139,12 @@ is_admitted_msg(const struct ofpbuf *b)
     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;
@@ -1160,6 +1155,7 @@ is_admitted_msg(const struct ofpbuf *b)
     case OFPTYPE_PACKET_OUT:
     case OFPTYPE_FLOW_MOD:
     case OFPTYPE_PORT_MOD:
+    case OFPTYPE_METER_MOD:
     case OFPTYPE_BARRIER_REQUEST:
     case OFPTYPE_BARRIER_REPLY:
     case OFPTYPE_DESC_STATS_REQUEST:
@@ -1176,6 +1172,12 @@ is_admitted_msg(const struct ofpbuf *b)
     case OFPTYPE_QUEUE_STATS_REPLY:
     case OFPTYPE_PORT_DESC_STATS_REQUEST:
     case OFPTYPE_PORT_DESC_STATS_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_ROLE_REQUEST:
     case OFPTYPE_ROLE_REPLY:
     case OFPTYPE_SET_FLOW_FORMAT:
index b914ef6..fae35b1 100644 (file)
@@ -331,7 +331,7 @@ reconnect_disconnected(struct reconnect *fsm, long long int now, int error)
         if (fsm->state & (S_ACTIVE | S_IDLE)) {
             if (error > 0) {
                 VLOG_WARN("%s: connection dropped (%s)",
-                          fsm->name, strerror(error));
+                          fsm->name, ovs_strerror(error));
             } else if (error == EOF) {
                 VLOG(fsm->info, "%s: connection closed by peer", fsm->name);
             } else {
@@ -340,7 +340,7 @@ reconnect_disconnected(struct reconnect *fsm, long long int now, int error)
         } else if (fsm->state == S_LISTENING) {
             if (error > 0) {
                 VLOG_WARN("%s: error listening for connections (%s)",
-                          fsm->name, strerror(error));
+                          fsm->name, ovs_strerror(error));
             } else {
                 VLOG(fsm->info, "%s: error listening for connections",
                      fsm->name);
@@ -349,7 +349,7 @@ reconnect_disconnected(struct reconnect *fsm, long long int now, int error)
             const char *type = fsm->passive ? "listen" : "connection";
             if (error > 0) {
                 VLOG_WARN("%s: %s attempt failed (%s)",
-                          fsm->name, type, strerror(error));
+                          fsm->name, type, ovs_strerror(error));
             } else {
                 VLOG(fsm->info, "%s: %s attempt timed out", fsm->name, type);
             }
index 5bdcfb0..5891ae8 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2012 Nicira, Inc.
+ * Copyright (c) 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.
@@ -225,7 +225,7 @@ route_table_reset(void)
     struct nl_dump dump;
     struct rtgenmsg *rtmsg;
     struct ofpbuf request, reply;
-    static struct nl_sock *rtnl_sock;
+    struct nl_sock *rtnl_sock;
 
     route_map_clear();
     route_table_valid = true;
@@ -272,7 +272,7 @@ route_table_parse(struct ofpbuf *buf, struct route_table_msg *change)
         [RTA_OIF] = { .type = NL_A_U32, .optional = false },
     };
 
-    static struct nlattr *attrs[ARRAY_SIZE(policy)];
+    struct nlattr *attrs[ARRAY_SIZE(policy)];
 
     parsed = nl_policy_parse(buf, NLMSG_HDRLEN + sizeof(struct rtmsg),
                              policy, attrs, ARRAY_SIZE(policy));
@@ -421,7 +421,7 @@ name_table_reset(void)
     struct nl_dump dump;
     struct rtgenmsg *rtmsg;
     struct ofpbuf request, reply;
-    static struct nl_sock *rtnl_sock;
+    struct nl_sock *rtnl_sock;
 
     name_table_valid = true;
     name_map_clear();
index c5fe03a..b706c20 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Gaetano Catalli.
+ * Copyright (c) 2011, 2013 Gaetano Catalli.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -55,13 +55,13 @@ rtbsd_notifier_register(struct rtbsd_notifier *notifier,
         notify_sock = socket(PF_ROUTE, SOCK_RAW, 0);
         if (notify_sock < 0) {
             VLOG_WARN("could not create PF_ROUTE socket: %s",
-                      strerror(errno));
+                      ovs_strerror(errno));
             return errno;
         }
         error = set_nonblocking(notify_sock);
         if (error) {
             VLOG_WARN("error set_nonblocking PF_ROUTE socket: %s",
-                    strerror(error));
+                    ovs_strerror(error));
             return error;
         }
     } else {
@@ -120,7 +120,7 @@ rtbsd_notifier_run(void)
                 VLOG_WARN_RL(&rl, "PF_ROUTE receive buffer overflowed");
             } else {
                 VLOG_WARN_RL(&rl, "error reading PF_ROUTE socket: %s",
-                             strerror(errno));
+                             ovs_strerror(errno));
             }
             rtbsd_report_notify_error();
         }
index 0b3710f..459e485 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010 Nicira, Inc.
+ * Copyright (c) 2009, 2010, 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 @@ rtnetlink_link_parse(struct ofpbuf *buf,
         [IFLA_ADDRESS] = { .type = NL_A_UNSPEC, .optional = true },
     };
 
-    static struct nlattr *attrs[ARRAY_SIZE(policy)];
+    struct nlattr *attrs[ARRAY_SIZE(policy)];
 
     parsed = nl_policy_parse(buf, NLMSG_HDRLEN + sizeof(struct ifinfomsg),
                              policy, attrs, ARRAY_SIZE(policy));
index f55e6d4..817420d 100644 (file)
@@ -6,6 +6,7 @@
  */
 
 #include "sflow_api.h"
+#include "util.h"
 
 static void * sflAlloc(SFLAgent *agent, size_t bytes);
 static void sflFree(SFLAgent *agent, void *obj);
@@ -469,7 +470,7 @@ void sfl_agent_error(SFLAgent *agent, char *modName, char *msg)
 void sfl_agent_sysError(SFLAgent *agent, char *modName, char *msg)
 {
     char errm[MAX_ERRMSG_LEN];
-    snprintf(errm, sizeof errm, "sfl_agent_sysError: %s: %s (errno = %d - %s)\n", modName, msg, errno, strerror(errno));
+    snprintf(errm, sizeof errm, "sfl_agent_sysError: %s: %s (errno = %d - %s)\n", modName, msg, errno, ovs_strerror(errno));
     if(agent->errorFn) (*agent->errorFn)(agent->magic, agent, errm);
     else {
        fprintf(stderr, "%s\n", errm);
index f31bc52..f11ed05 100644 (file)
@@ -40,80 +40,47 @@ VLOG_DEFINE_THIS_MODULE(signals);
 #endif
 
 struct signal {
-    struct sigaction saved_sa;
-    int signr;
+    int fds[2];
 };
 
-static volatile sig_atomic_t signaled[N_SIGNALS];
-
-static int fds[2];
+static struct signal signals[N_SIGNALS];
 
 static void signal_handler(int signr);
 
-/* Initializes the signals subsystem (if it is not already initialized).  Calls
- * exit() if initialization fails.
- *
- * Calling this function is optional; it will be called automatically by
- * signal_start() if necessary.  Calling it explicitly allows the client to
- * prevent the process from exiting at an unexpected time. */
-void
-signal_init(void)
-{
-    static bool inited;
-    if (!inited) {
-        inited = true;
-        xpipe_nonblocking(fds);
-    }
-}
-
 /* Sets up a handler for 'signr' and returns a structure that represents it.
  *
- * Only one handler for a given signal may be registered at a time. */
+ * Only one handler for a given signal may be registered. */
 struct signal *
 signal_register(int signr)
 {
     struct sigaction sa;
     struct signal *s;
 
-    signal_init();
+    ovs_assert(signr >= 1 && signr < N_SIGNALS);
 
-    s = xmalloc(sizeof *s);
-    s->signr = signr;
+    /* Create a pipe. */
+    s = &signals[signr];
+    ovs_assert(!s->fds[0] && !s->fds[1]);
+    xpipe_nonblocking(s->fds);
 
-    /* Set up signal handler. */
-    ovs_assert(signr >= 1 && signr < N_SIGNALS);
+    /* Install signal handler. */
     memset(&sa, 0, sizeof sa);
     sa.sa_handler = signal_handler;
     sigemptyset(&sa.sa_mask);
     sa.sa_flags = SA_RESTART;
-    xsigaction(signr, &sa, &s->saved_sa);
+    xsigaction(signr, &sa, NULL);
 
     return s;
 }
 
-/* Unregisters the handler for 's', restores the signal handler that was in
- * effect before signal_register() was called, and frees 's'. */
-void
-signal_unregister(struct signal *s)
-{
-    if (s) {
-        xsigaction(s->signr, &s->saved_sa, NULL);
-        free(s);
-    }
-}
-
 /* Returns true if signal 's' has been received since the last call to this
  * function with argument 's'. */
 bool
 signal_poll(struct signal *s)
 {
     char buf[_POSIX_PIPE_BUF];
-    ignore(read(fds[0], buf, sizeof buf));
-    if (signaled[s->signr]) {
-        signaled[s->signr] = 0;
-        return true;
-    }
-    return false;
+
+    return read(s->fds[0], buf, sizeof buf) > 0;
 }
 
 /* Causes the next call to poll_block() to wake up when signal_poll(s) would
@@ -121,49 +88,49 @@ signal_poll(struct signal *s)
 void
 signal_wait(struct signal *s)
 {
-    if (signaled[s->signr]) {
-        poll_immediate_wake();
-    } else {
-        poll_fd_wait(fds[0], POLLIN);
-    }
+    poll_fd_wait(s->fds[0], POLLIN);
 }
 \f
 static void
 signal_handler(int signr)
 {
     if (signr >= 1 && signr < N_SIGNALS) {
-        ignore(write(fds[1], "", 1));
-        signaled[signr] = true;
+        ignore(write(signals[signr].fds[1], "", 1));
     }
 }
 
-/* Returns the name of signal 'signum' as a string.  The string may be in a
- * static buffer that is reused from one call to the next.
+/* Returns the name of signal 'signum' as a string.  The return value is either
+ * a statically allocated constant string or the 'bufsize'-byte buffer
+ * 'namebuf'.  'bufsize' should be at least SIGNAL_NAME_BUFSIZE.
  *
  * The string is probably a (possibly multi-word) description of the signal
  * (e.g. "Hangup") instead of just the stringified version of the macro
  * (e.g. "SIGHUP"). */
 const char *
-signal_name(int signum)
+signal_name(int signum, char *namebuf, size_t bufsize)
 {
-    const char *name = NULL;
-#ifdef HAVE_STRSIGNAL
-    name = strsignal(signum);
-#endif
-    if (!name) {
-        static char buffer[7 + INT_STRLEN(int) + 1];
-        sprintf(buffer, "signal %d", signum);
-        name = buffer;
+#if HAVE_DECL_SYS_SIGLIST
+    if (signum >= 0 && signum < ARRAY_SIZE(sys_siglist)) {
+        const char *name = sys_siglist[signum];
+        if (name) {
+            return name;
+        }
     }
-    return name;
+#endif
+
+    snprintf(namebuf, bufsize, "signal %d", signum);
+    return namebuf;
 }
 
 void
 xsigaction(int signum, const struct sigaction *new, struct sigaction *old)
 {
     if (sigaction(signum, new, old)) {
+        char namebuf[SIGNAL_NAME_BUFSIZE];
+
         VLOG_FATAL("sigaction(%s) failed (%s)",
-                   signal_name(signum), strerror(errno));
+                   signal_name(signum, namebuf, sizeof namebuf),
+                   ovs_strerror(errno));
     }
 }
 
@@ -172,6 +139,6 @@ xpthread_sigmask(int how, const sigset_t *new, sigset_t *old)
 {
     int error = pthread_sigmask(how, new, old);
     if (error) {
-        VLOG_FATAL("pthread_sigmask failed (%s)", strerror(error));
+        VLOG_FATAL("pthread_sigmask failed (%s)", ovs_strerror(error));
     }
 }
index 641bcbb..3294293 100644 (file)
 
 #include <signal.h>
 #include <stdbool.h>
-
-void signal_init(void);
+#include <stddef.h>
+#include "type-props.h"
 
 struct signal *signal_register(int signr);
-void signal_unregister(struct signal *);
-
 bool signal_poll(struct signal *);
 void signal_wait(struct signal *);
 
-const char *signal_name(int signum);
+enum { SIGNAL_NAME_BUFSIZE = 7 + INT_STRLEN(int) + 1 };
+const char *signal_name(int signum, char *namebuf, size_t bufsize);
 
 void xsigaction(int signum, const struct sigaction *, struct sigaction *old);
 void xpthread_sigmask(int how, const sigset_t *, sigset_t *old);
index 2dff9f5..fa55480 100644 (file)
@@ -72,11 +72,11 @@ set_nonblocking(int fd)
         if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) != -1) {
             return 0;
         } else {
-            VLOG_ERR("fcntl(F_SETFL) failed: %s", strerror(errno));
+            VLOG_ERR("fcntl(F_SETFL) failed: %s", ovs_strerror(errno));
             return errno;
         }
     } else {
-        VLOG_ERR("fcntl(F_GETFL) failed: %s", strerror(errno));
+        VLOG_ERR("fcntl(F_GETFL) failed: %s", ovs_strerror(errno));
         return errno;
     }
 }
@@ -259,7 +259,7 @@ check_connection_completion(int fd)
         }
         return 0;
     } else if (retval < 0) {
-        VLOG_ERR_RL(&rl, "poll: %s", strerror(errno));
+        VLOG_ERR_RL(&rl, "poll: %s", ovs_strerror(errno));
         return errno;
     } else {
         return EAGAIN;
@@ -452,7 +452,8 @@ make_unix_socket(int style, bool nonblock,
         int dirfd;
 
         if (unlink(bind_path) && errno != ENOENT) {
-            VLOG_WARN("unlinking \"%s\": %s\n", bind_path, strerror(errno));
+            VLOG_WARN("unlinking \"%s\": %s\n",
+                      bind_path, ovs_strerror(errno));
         }
         fatal_signal_add_file_to_unlink(bind_path);
 
@@ -602,7 +603,7 @@ inet_open_active(int style, const char *target, uint16_t default_port,
     /* Create non-blocking socket. */
     fd = socket(AF_INET, style, 0);
     if (fd < 0) {
-        VLOG_ERR("%s: socket: %s", target, strerror(errno));
+        VLOG_ERR("%s: socket: %s", target, ovs_strerror(errno));
         error = errno;
         goto exit;
     }
@@ -616,7 +617,7 @@ inet_open_active(int style, const char *target, uint16_t default_port,
      * connect(), the handshake SYN frames will be sent with a TOS of 0. */
     error = set_dscp(fd, dscp);
     if (error) {
-        VLOG_ERR("%s: socket: %s", target, strerror(error));
+        VLOG_ERR("%s: socket: %s", target, ovs_strerror(error));
         goto exit;
     }
 
@@ -728,7 +729,7 @@ inet_open_passive(int style, const char *target, int default_port,
     fd = socket(AF_INET, style, 0);
     if (fd < 0) {
         error = errno;
-        VLOG_ERR("%s: socket: %s", target, strerror(error));
+        VLOG_ERR("%s: socket: %s", target, ovs_strerror(error));
         return -error;
     }
     error = set_nonblocking(fd);
@@ -738,14 +739,15 @@ inet_open_passive(int style, const char *target, int default_port,
     if (style == SOCK_STREAM
         && setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes) < 0) {
         error = errno;
-        VLOG_ERR("%s: setsockopt(SO_REUSEADDR): %s", target, strerror(error));
+        VLOG_ERR("%s: setsockopt(SO_REUSEADDR): %s",
+                 target, ovs_strerror(error));
         goto error;
     }
 
     /* Bind. */
     if (bind(fd, (struct sockaddr *) &sin, sizeof sin) < 0) {
         error = errno;
-        VLOG_ERR("%s: bind: %s", target, strerror(error));
+        VLOG_ERR("%s: bind: %s", target, ovs_strerror(error));
         goto error;
     }
 
@@ -754,14 +756,14 @@ inet_open_passive(int style, const char *target, int default_port,
      * connect(), the handshake SYN frames will be sent with a TOS of 0. */
     error = set_dscp(fd, dscp);
     if (error) {
-        VLOG_ERR("%s: socket: %s", target, strerror(error));
+        VLOG_ERR("%s: socket: %s", target, ovs_strerror(error));
         goto error;
     }
 
     /* Listen. */
     if (style == SOCK_STREAM && listen(fd, 10) < 0) {
         error = errno;
-        VLOG_ERR("%s: listen: %s", target, strerror(error));
+        VLOG_ERR("%s: listen: %s", target, ovs_strerror(error));
         goto error;
     }
 
@@ -770,7 +772,7 @@ inet_open_passive(int style, const char *target, int default_port,
         socklen_t sin_len = sizeof sin;
         if (getsockname(fd, (struct sockaddr *) &sin, &sin_len) < 0) {
             error = errno;
-            VLOG_ERR("%s: getsockname: %s", target, strerror(error));
+            VLOG_ERR("%s: getsockname: %s", target, ovs_strerror(error));
             goto error;
         }
         if (sin.sin_family != AF_INET || sin_len != sizeof sin) {
@@ -805,7 +807,7 @@ get_null_fd(void)
         null_fd = open("/dev/null", O_RDWR);
         if (null_fd < 0) {
             int error = errno;
-            VLOG_ERR("could not open /dev/null: %s", strerror(error));
+            VLOG_ERR("could not open /dev/null: %s", ovs_strerror(error));
             return -error;
         }
     }
@@ -873,13 +875,13 @@ fsync_parent_dir(const char *file_name)
                  * really an error. */
             } else {
                 error = errno;
-                VLOG_ERR("%s: fsync failed (%s)", dir, strerror(error));
+                VLOG_ERR("%s: fsync failed (%s)", dir, ovs_strerror(error));
             }
         }
         close(fd);
     } else {
         error = errno;
-        VLOG_ERR("%s: open failed (%s)", dir, strerror(error));
+        VLOG_ERR("%s: open failed (%s)", dir, ovs_strerror(error));
     }
     free(dir);
 
@@ -917,7 +919,7 @@ void
 xpipe(int fds[2])
 {
     if (pipe(fds)) {
-        VLOG_FATAL("failed to create pipe (%s)", strerror(errno));
+        VLOG_FATAL("failed to create pipe (%s)", ovs_strerror(errno));
     }
 }
 
@@ -933,7 +935,7 @@ void
 xsocketpair(int domain, int type, int protocol, int fds[2])
 {
     if (socketpair(domain, type, protocol, fds)) {
-        VLOG_FATAL("failed to create socketpair (%s)", strerror(errno));
+        VLOG_FATAL("failed to create socketpair (%s)", ovs_strerror(errno));
     }
 }
 
@@ -948,7 +950,7 @@ getsockopt_int(int fd, int level, int option, const char *optname, int *valuep)
     len = sizeof value;
     if (getsockopt(fd, level, option, &value, &len)) {
         error = errno;
-        VLOG_ERR_RL(&rl, "getsockopt(%s): %s", optname, strerror(error));
+        VLOG_ERR_RL(&rl, "getsockopt(%s): %s", optname, ovs_strerror(error));
     } else if (len != sizeof value) {
         error = EINVAL;
         VLOG_ERR_RL(&rl, "getsockopt(%s): value is %u bytes (expected %zu)",
@@ -1076,7 +1078,7 @@ describe_fd(int fd)
 
     ds_init(&string);
     if (fstat(fd, &s)) {
-        ds_put_format(&string, "fstat failed (%s)", strerror(errno));
+        ds_put_format(&string, "fstat failed (%s)", ovs_strerror(errno));
     } else if (S_ISSOCK(s.st_mode)) {
         describe_sockaddr(&string, fd, getsockname);
         ds_put_cstr(&string, "<->");
index 90d328a..d102582 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2012 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 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.
@@ -24,7 +24,6 @@
 #include <sys/types.h>
 #include <unistd.h>
 #include "fatal-signal.h"
-#include "leak-checker.h"
 #include "poll-loop.h"
 #include "socket-util.h"
 #include "stress.h"
@@ -234,7 +233,7 @@ pfd_accept(struct pstream *pstream, struct stream **new_streamp)
     if (new_fd < 0) {
         retval = errno;
         if (retval != EAGAIN) {
-            VLOG_DBG_RL(&rl, "accept: %s", strerror(retval));
+            VLOG_DBG_RL(&rl, "accept: %s", ovs_strerror(retval));
         }
         return retval;
     }
index ddf65a3..3b9270f 100644 (file)
@@ -35,7 +35,6 @@
 #include "coverage.h"
 #include "dynamic-string.h"
 #include "entropy.h"
-#include "leak-checker.h"
 #include "ofpbuf.h"
 #include "openflow/openflow.h"
 #include "packets.h"
@@ -247,7 +246,7 @@ new_ssl_stream(const char *name, int fd, enum session_type type,
     /* Disable Nagle. */
     retval = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof on);
     if (retval) {
-        VLOG_ERR("%s: setsockopt(TCP_NODELAY): %s", name, strerror(errno));
+        VLOG_ERR("%s: setsockopt(TCP_NODELAY): %s", name, ovs_strerror(errno));
         retval = errno;
         goto error;
     }
@@ -324,7 +323,7 @@ ssl_open(const char *name, char *suffix, struct stream **streamp, uint8_t dscp)
         int state = error ? STATE_TCP_CONNECTING : STATE_SSL_CONNECTING;
         return new_ssl_stream(name, fd, CLIENT, state, &sin, streamp);
     } else {
-        VLOG_ERR("%s: connect: %s", name, strerror(error));
+        VLOG_ERR("%s: connect: %s", name, ovs_strerror(error));
         return error;
     }
 }
@@ -370,7 +369,7 @@ do_ca_cert_bootstrap(struct stream *stream)
             return EPROTO;
         } else {
             VLOG_ERR("could not bootstrap CA cert: creating %s failed: %s",
-                     ca_cert.file_name, strerror(errno));
+                     ca_cert.file_name, ovs_strerror(errno));
             return errno;
         }
     }
@@ -379,7 +378,7 @@ do_ca_cert_bootstrap(struct stream *stream)
     if (!file) {
         error = errno;
         VLOG_ERR("could not bootstrap CA cert: fdopen failed: %s",
-                 strerror(error));
+                 ovs_strerror(error));
         unlink(ca_cert.file_name);
         return error;
     }
@@ -396,7 +395,7 @@ do_ca_cert_bootstrap(struct stream *stream)
     if (fclose(file)) {
         error = errno;
         VLOG_ERR("could not bootstrap CA cert: writing %s failed: %s",
-                 ca_cert.file_name, strerror(error));
+                 ca_cert.file_name, ovs_strerror(error));
         unlink(ca_cert.file_name);
         return error;
     }
@@ -442,7 +441,7 @@ ssl_connect(struct stream *stream)
 
     case STATE_SSL_CONNECTING:
         /* Capture the first few bytes of received data so that we can guess
-         * what kind of funny data we've been sent if SSL negotation fails. */
+         * what kind of funny data we've been sent if SSL negotiation fails. */
         if (sslv->n_head <= 0) {
             sslv->n_head = recv(sslv->fd, sslv->head, sizeof sslv->head,
                                 MSG_PEEK);
@@ -565,7 +564,7 @@ interpret_ssl_error(const char *function, int ret, int error,
             if (ret < 0) {
                 int status = errno;
                 VLOG_WARN_RL(&rl, "%s: system error (%s)",
-                             function, strerror(status));
+                             function, ovs_strerror(status));
                 return status;
             } else {
                 VLOG_WARN_RL(&rl, "%s: unexpected SSL connection close",
@@ -674,7 +673,6 @@ ssl_send(struct stream *stream, const void *buffer, size_t n)
             ssl_clear_txbuf(sslv);
             return n;
         case EAGAIN:
-            leak_checker_claim(buffer);
             return n;
         default:
             sslv->txbuf = NULL;
@@ -836,7 +834,7 @@ pssl_accept(struct pstream *pstream, struct stream **new_streamp)
     if (new_fd < 0) {
         error = errno;
         if (error != EAGAIN) {
-            VLOG_DBG_RL(&rl, "accept: %s", strerror(error));
+            VLOG_DBG_RL(&rl, "accept: %s", ovs_strerror(error));
         }
         return error;
     }
@@ -1017,7 +1015,8 @@ update_ssl_config(struct ssl_config_file *config, const char *file_name)
      * here. */
     error = get_mtime(file_name, &mtime);
     if (error && error != ENOENT) {
-        VLOG_ERR_RL(&rl, "%s: stat failed (%s)", file_name, strerror(error));
+        VLOG_ERR_RL(&rl, "%s: stat failed (%s)",
+                    file_name, ovs_strerror(error));
     }
     if (config->file_name
         && !strcmp(config->file_name, file_name)
@@ -1125,7 +1124,7 @@ read_cert_file(const char *file_name, X509 ***certs, size_t *n_certs)
     file = fopen(file_name, "r");
     if (!file) {
         VLOG_ERR("failed to open %s for reading: %s",
-                 file_name, strerror(errno));
+                 file_name, ovs_strerror(errno));
         return errno;
     }
 
index 1767fe4..d507208 100644 (file)
@@ -53,7 +53,7 @@ new_tcp_stream(const char *name, int fd, int connect_status,
 
     retval = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof on);
     if (retval) {
-        VLOG_ERR("%s: setsockopt(TCP_NODELAY): %s", name, strerror(errno));
+        VLOG_ERR("%s: setsockopt(TCP_NODELAY): %s", name, ovs_strerror(errno));
         close(fd);
         return errno;
     }
@@ -79,7 +79,7 @@ tcp_open(const char *name, char *suffix, struct stream **streamp, uint8_t dscp)
     if (fd >= 0) {
         return new_tcp_stream(name, fd, error, &sin, streamp);
     } else {
-        VLOG_ERR("%s: connect: %s", name, strerror(error));
+        VLOG_ERR("%s: connect: %s", name, ovs_strerror(error));
         return error;
     }
 }
index dbee135..e4b7e77 100644 (file)
@@ -50,7 +50,8 @@ unix_open(const char *name, char *suffix, struct stream **streamp,
     fd = make_unix_socket(SOCK_STREAM, true, NULL, connect_path);
 
     if (fd < 0) {
-        VLOG_DBG("%s: connection failed (%s)", connect_path, strerror(-fd));
+        VLOG_DBG("%s: connection failed (%s)",
+                 connect_path, ovs_strerror(-fd));
         free(connect_path);
         return -fd;
     }
@@ -87,14 +88,14 @@ punix_open(const char *name OVS_UNUSED, char *suffix,
     bind_path = abs_file_name(ovs_rundir(), suffix);
     fd = make_unix_socket(SOCK_STREAM, true, bind_path, NULL);
     if (fd < 0) {
-        VLOG_ERR("%s: binding failed: %s", bind_path, strerror(errno));
+        VLOG_ERR("%s: binding failed: %s", bind_path, ovs_strerror(errno));
         free(bind_path);
         return errno;
     }
 
     if (listen(fd, 10) < 0) {
         error = errno;
-        VLOG_ERR("%s: listen: %s", name, strerror(error));
+        VLOG_ERR("%s: listen: %s", name, ovs_strerror(error));
         close(fd);
         free(bind_path);
         return error;
index 498deef..8f3d359 100644 (file)
@@ -53,7 +53,7 @@ struct stress_option {
  *
  * DEFAULT is the default value for the option.  Specify 0 to disable the
  * option by default, which should be the usual choice.  But some options can
- * be left on at low levels without noticable impact to the end user.  An
+ * be left on at low levels without noticeable impact to the end user.  An
  * example would be failing to allocate a buffer for every 100000th packet
  * processed by the system.
  */
index f687c96..0ccfa42 100644 (file)
@@ -30,6 +30,7 @@
 #include "fatal-signal.h"
 #include "hash.h"
 #include "hmap.h"
+#include "ovs-thread.h"
 #include "signals.h"
 #include "unixctl.h"
 #include "util.h"
@@ -167,35 +168,6 @@ set_up_signal(int flags)
     xsigaction(SIGALRM, &sa, NULL);
 }
 
-/* Remove SA_RESTART from the flags for SIGALRM, so that any system call that
- * is interrupted by the periodic timer interrupt will return EINTR instead of
- * continuing after the signal handler returns.
- *
- * time_disable_restart() and time_enable_restart() may be usefully wrapped
- * around function calls that might otherwise block forever unless interrupted
- * by a signal, e.g.:
- *
- *   time_disable_restart();
- *   fcntl(fd, F_SETLKW, &lock);
- *   time_enable_restart();
- */
-void
-time_disable_restart(void)
-{
-    time_init();
-    set_up_signal(0);
-}
-
-/* Add SA_RESTART to the flags for SIGALRM, so that any system call that
- * is interrupted by the periodic timer interrupt will continue after the
- * signal handler returns instead of returning EINTR. */
-void
-time_enable_restart(void)
-{
-    time_init();
-    set_up_signal(SA_RESTART);
-}
-
 static void
 set_up_timer(void)
 {
@@ -207,7 +179,7 @@ set_up_timer(void)
     }
 
     if (timer_create(monotonic_clock, NULL, &timer_id)) {
-        VLOG_FATAL("timer_create failed (%s)", strerror(errno));
+        VLOG_FATAL("timer_create failed (%s)", ovs_strerror(errno));
     }
 
     itimer.it_interval.tv_sec = 0;
@@ -215,7 +187,7 @@ set_up_timer(void)
     itimer.it_value = itimer.it_interval;
 
     if (timer_settime(timer_id, 0, &itimer, NULL)) {
-        VLOG_FATAL("timer_settime failed (%s)", strerror(errno));
+        VLOG_FATAL("timer_settime failed (%s)", ovs_strerror(errno));
     }
 }
 
@@ -326,17 +298,13 @@ time_alarm(unsigned int secs)
     long long int now;
     long long int msecs;
 
-    sigset_t oldsigs;
-
+    assert_single_threaded();
     time_init();
     time_refresh();
 
     now = time_msec();
     msecs = secs * 1000LL;
-
-    block_sigalrm(&oldsigs);
     deadline = now < LLONG_MAX - msecs ? now + msecs : LLONG_MAX;
-    unblock_sigalrm(&oldsigs);
 }
 
 /* Like poll(), except:
@@ -488,7 +456,7 @@ void
 xgettimeofday(struct timeval *tv)
 {
     if (gettimeofday(tv, NULL) == -1) {
-        VLOG_FATAL("gettimeofday failed (%s)", strerror(errno));
+        VLOG_FATAL("gettimeofday failed (%s)", ovs_strerror(errno));
     }
 }
 
index b74fe12..eff28e2 100644 (file)
@@ -57,8 +57,6 @@ BUILD_ASSERT_DECL(TYPE_IS_SIGNED(time_t));
 #endif
 #endif /* ifndef CACHE_TIME */
 
-void time_disable_restart(void);
-void time_enable_restart(void);
 void time_postfork(void);
 void time_refresh(void);
 time_t time_now(void);
index 104a2d9..253e953 100644 (file)
@@ -168,7 +168,7 @@ unixctl_command_reply(struct unixctl_conn *conn, const char *result)
 }
 
 /* Replies to the active unixctl connection 'conn'. 'error' is sent to the
- * client indicating an error occured processing the command.  Only one call to
+ * client indicating an error occurred processing the command.  Only one call to
  * unixctl_command_reply() or unixctl_command_reply_error() may be made per
  * request. */
 void
@@ -358,7 +358,7 @@ unixctl_server_run(struct unixctl_server *server)
         } else {
             VLOG_WARN_RL(&rl, "%s: accept failed: %s",
                          pstream_get_name(server->listener),
-                         strerror(error));
+                         ovs_strerror(error));
         }
     }
 
index cd4019e..0ba1ed5 100644 (file)
@@ -18,6 +18,7 @@
 #include "util.h"
 #include <errno.h>
 #include <limits.h>
+#include <pthread.h>
 #include <stdarg.h>
 #include <stdint.h>
 #include <stdio.h>
@@ -27,7 +28,7 @@
 #include <unistd.h>
 #include "byte-order.h"
 #include "coverage.h"
-#include "openvswitch/types.h"
+#include "ovs-thread.h"
 #include "vlog.h"
 
 VLOG_DEFINE_THIS_MODULE(util);
@@ -44,6 +45,9 @@ const char *subprogram_name = "";
 /* --version option output. */
 static char *program_version;
 
+/* Buffer used by ovs_strerror(). */
+DEFINE_PER_THREAD_DATA(struct { char s[128]; }, strerror_buffer, { "" });
+
 void
 ovs_assert_failure(const char *where, const char *function,
                    const char *condition)
@@ -306,19 +310,41 @@ ovs_error_valist(int err_no, const char *format, va_list args)
 const char *
 ovs_retval_to_string(int retval)
 {
-    static char unknown[48];
+    return (!retval ? ""
+            : retval == EOF ? "End of file"
+            : ovs_strerror(retval));
+}
 
-    if (!retval) {
-        return "";
-    }
-    if (retval > 0) {
-        return strerror(retval);
-    }
-    if (retval == EOF) {
-        return "End of file";
+const char *
+ovs_strerror(int error)
+{
+    enum { BUFSIZE = sizeof strerror_buffer_get()->s };
+    int save_errno;
+    char *buffer;
+    char *s;
+
+    save_errno = errno;
+    buffer = strerror_buffer_get()->s;
+
+#if STRERROR_R_CHAR_P
+    /* GNU style strerror_r() might return an immutable static string, or it
+     * might write and return 'buffer', but in either case we can pass the
+     * returned string directly to the caller. */
+    s = strerror_r(error, buffer, BUFSIZE);
+#else  /* strerror_r() returns an int. */
+    s = buffer;
+    if (strerror_r(error, buffer, BUFSIZE)) {
+        /* strerror_r() is only allowed to fail on ERANGE (because the buffer
+         * is too short).  We don't check the actual failure reason because
+         * POSIX requires strerror_r() to return the error but old glibc
+         * (before 2.13) returns -1 and sets errno. */
+        snprintf(buffer, BUFSIZE, "Unknown error %d", error);
     }
-    snprintf(unknown, sizeof unknown, "***unknown return value: %d***", retval);
-    return unknown;
+#endif
+
+    errno = save_errno;
+
+    return s;
 }
 
 /* Sets global "program_name" and "program_version" variables.  Should
@@ -340,6 +366,9 @@ set_program_name__(const char *argv0, const char *version, const char *date,
                    const char *time)
 {
     const char *slash = strrchr(argv0, '/');
+
+    assert_single_threaded();
+
     program_name = slash ? slash + 1 : argv0;
 
     free(program_version);
@@ -587,7 +616,7 @@ get_cwd(void)
             int error = errno;
             free(buf);
             if (error != ERANGE) {
-                VLOG_WARN("getcwd failed (%s)", strerror(error));
+                VLOG_WARN("getcwd failed (%s)", ovs_strerror(error));
                 return NULL;
             }
             size *= 2;
@@ -725,7 +754,8 @@ follow_symlinks(const char *filename)
 
         linkname = xreadlink(fn);
         if (!linkname) {
-            VLOG_WARN("%s: readlink failed (%s)", filename, strerror(errno));
+            VLOG_WARN("%s: readlink failed (%s)",
+                      filename, ovs_strerror(errno));
             return fn;
         }
 
index f5589e3..c71f027 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.
@@ -26,6 +26,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include "compiler.h"
+#include "openvswitch/types.h"
 
 #ifndef va_copy
 #ifdef __va_copy
@@ -213,6 +214,7 @@ void ovs_error(int err_no, const char *format, ...) PRINTF_FORMAT(2, 3);
 void ovs_error_valist(int err_no, const char *format, va_list)
     PRINTF_FORMAT(2, 0);
 const char *ovs_retval_to_string(int);
+const char *ovs_strerror(int);
 void ovs_hex_dump(FILE *, const void *, size_t, uintptr_t offset, bool ascii);
 
 bool str_to_int(const char *, int base, int *);
index e91d92c..92076d9 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.
@@ -22,7 +22,6 @@
 #include <sys/types.h>
 #include <unistd.h>
 #include "fatal-signal.h"
-#include "leak-checker.h"
 #include "ofpbuf.h"
 #include "openflow/openflow.h"
 #include "poll-loop.h"
@@ -210,7 +209,6 @@ vconn_stream_send(struct vconn *vconn, struct ofpbuf *buffer)
         ofpbuf_delete(buffer);
         return 0;
     } else if (retval >= 0 || retval == -EAGAIN) {
-        leak_checker_claim(buffer);
         s->txbuf = buffer;
         if (retval > 0) {
             ofpbuf_pull(buffer, retval);
@@ -235,7 +233,7 @@ vconn_stream_run(struct vconn *vconn)
     retval = stream_send(s->stream, s->txbuf->data, s->txbuf->size);
     if (retval < 0) {
         if (retval != -EAGAIN) {
-            VLOG_ERR_RL(&rl, "send: %s", strerror(-retval));
+            VLOG_ERR_RL(&rl, "send: %s", ovs_strerror(-retval));
             vconn_stream_clear_txbuf(s);
             return;
         }
@@ -350,7 +348,7 @@ pvconn_pstream_accept(struct pvconn *pvconn, struct vconn **new_vconnp)
     if (error) {
         if (error != EAGAIN) {
             VLOG_DBG_RL(&rl, "%s: accept: %s",
-                        pstream_get_name(ps->pstream), strerror(error));
+                        pstream_get_name(ps->pstream), ovs_strerror(error));
         }
         return error;
     }
index 71e6d05..449a36e 100644 (file)
@@ -695,7 +695,7 @@ do_send(struct vconn *vconn, struct ofpbuf *msg)
         retval = (vconn->class->send)(vconn, msg);
         if (retval != EAGAIN) {
             VLOG_DBG_RL(&ofmsg_rl, "%s: sent (%s): %s",
-                        vconn->name, strerror(retval), s);
+                        vconn->name, ovs_strerror(retval), s);
         }
         free(s);
     }
@@ -754,7 +754,7 @@ vconn_recv_block(struct vconn *vconn, struct ofpbuf **msgp)
     return retval;
 }
 
-/* Waits until a message with a transaction ID matching 'xid' is recived on
+/* Waits until a message with a transaction ID matching 'xid' is received on
  * 'vconn'.  Returns 0 if successful, in which case the reply is stored in
  * '*replyp' for the caller to examine and free.  Otherwise returns a positive
  * errno value, or EOF, and sets '*replyp' to null.
index 2440def..5f58f10 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Nicira, Inc.
+ * Copyright (c) 2011, 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.
 #include "vlandev.h"
 
 #include <errno.h>
+#include <net/if.h>
 #include <sys/ioctl.h>
 #include <sys/stat.h>
 
+#include "dummy.h"
 #include "hash.h"
 #include "shash.h"
 #include "vlog.h"
 
 VLOG_DEFINE_THIS_MODULE(vlandev);
 
+/* A vlandev implementation. */
+struct vlandev_class {
+    int (*vd_refresh)(void);
+    int (*vd_add)(const char *real_dev, int vid);
+    int (*vd_del)(const char *vlan_dev);
+};
+
+#ifdef LINUX_DATAPATH
+static const struct vlandev_class vlandev_linux_class;
+#endif
+static const struct vlandev_class vlandev_stub_class;
+static const struct vlandev_class vlandev_dummy_class;
+
+/* The in-use vlandev implementation. */
+static const struct vlandev_class *vd_class;
+
+/* Maps from a VLAN device name (e.g. "eth0.10") to struct vlan_dev. */
+static struct shash vlan_devs = SHASH_INITIALIZER(&vlan_devs);
+
+/* Maps from a VLAN real device name (e.g. "eth0") to struct vlan_real_dev. */
+static struct shash vlan_real_devs = SHASH_INITIALIZER(&vlan_real_devs);
+
+static int vlandev_add__(const char *vlan_dev, const char *real_dev, int vid);
+static int vlandev_del__(const char *vlan_dev);
+static void vlandev_clear__(void);
+
+static const struct vlandev_class *
+vlandev_get_class(void)
+{
+    if (!vd_class) {
+#ifdef LINUX_DATAPATH
+        vd_class = &vlandev_linux_class;
+#else
+        vd_class = &vlandev_stub_class;
+#endif
+    }
+    return vd_class;
+}
+
+/* On Linux, the default implementation of VLAN devices creates and destroys
+ * Linux VLAN devices.  On other OSess, the default implementation is a
+ * nonfunctional stub.  In either case, this function replaces this default
+ * implementation by a "dummy" implementation that simply reports back whatever
+ * the client sets up with vlandev_add() and vlandev_del().
+ *
+ * Don't call this function directly; use dummy_enable() from dummy.h. */
+void
+vlandev_dummy_enable(void)
+{
+    if (vd_class != &vlandev_dummy_class) {
+        vd_class = &vlandev_dummy_class;
+        vlandev_clear__();
+    }
+}
+
+/* Creates a new VLAN device for VLAN 'vid' on top of real Ethernet device
+ * 'real_dev'.  Returns 0 if successful, otherwise a positive errno value.  On
+ * OSes other than Linux, in the absence of dummies (see
+ * vlandev_dummy_enable()), this always fails.
+ *
+ * The name of the new VLAN device is not easily predictable, because Linux
+ * provides multiple naming schemes, does not allow the client to specify a
+ * name, and does not directly report the new VLAN device's name.  Use
+ * vlandev_refresh() then vlandev_get_name() to find out the new VLAN device's
+ * name,. */
+int
+vlandev_add(const char *real_dev, int vid)
+{
+    return vlandev_get_class()->vd_add(real_dev, vid);
+}
+
+/* Deletes the VLAN device named 'vlan_dev'.  Returns 0 if successful,
+ * otherwise a positive errno value.  On OSes other than Linux, in the absence
+ * of dummies (see vlandev_dummy_enable()), this always fails. */
+int
+vlandev_del(const char *vlan_dev)
+{
+    return vlandev_get_class()->vd_del(vlan_dev);
+}
+
+/* Refreshes the cache of real device to VLAN device mappings reported by
+ * vlandev_get_real_devs() and vlandev_get_name().  Without calling this
+ * function, changes made by vlandev_add() and vlandev_del() may not be
+ * reflected by vlandev_get_real_devs() and vlandev_get_name() output. */
+int
+vlandev_refresh(void)
+{
+    const struct vlandev_class *class = vlandev_get_class();
+    return class->vd_refresh ? class->vd_refresh() : 0;
+}
+
+/* Returns a shash mapping from the name of real Ethernet devices used as the
+ * basis of VLAN devices to struct vlan_real_devs.  The caller must not modify
+ * or free anything in the returned shash.
+ *
+ * Changes made by vlandev_add() and vlandev_del() may not be reflected in this
+ * function's output without an intervening call to vlandev_refresh(). */
+struct shash *
+vlandev_get_real_devs(void)
+{
+    return &vlan_real_devs;
+}
+
+/* Returns the name of the VLAN device for VLAN 'vid' on top of
+ * 'real_dev_name', or NULL if there is no such VLAN device.
+ *
+ * Changes made by vlandev_add() and vlandev_del() may not be reflected in this
+ * function's output without an intervening call to vlandev_refresh(). */
+const char *
+vlandev_get_name(const char *real_dev_name, int vid)
+{
+    const struct vlan_real_dev *real_dev;
+
+    real_dev = shash_find_data(&vlan_real_devs, real_dev_name);
+    if (real_dev) {
+        const struct vlan_dev *vlan_dev;
+
+        HMAP_FOR_EACH_WITH_HASH (vlan_dev, hmap_node, hash_int(vid, 0),
+                                 &real_dev->vlan_devs) {
+            if (vlan_dev->vid == vid) {
+                return vlan_dev->name;
+            }
+        }
+    }
+
+    return NULL;
+}
+\f
+/* The Linux vlandev implementation. */
+
 #ifdef LINUX_DATAPATH
 #include "rtnetlink-link.h"
 #include <linux/if_vlan.h>
@@ -35,8 +167,6 @@ VLOG_DEFINE_THIS_MODULE(vlandev);
 #include "netdev-linux.h"
 
 static struct nln_notifier *vlan_cache_notifier;
-static struct shash vlan_devs = SHASH_INITIALIZER(&vlan_devs);
-static struct shash vlan_real_devs = SHASH_INITIALIZER(&vlan_real_devs);
 static bool cache_valid;
 
 static void
@@ -46,11 +176,10 @@ vlan_cache_cb(const struct rtnetlink_link_change *change OVS_UNUSED,
     cache_valid = false;
 }
 
-int
-vlandev_refresh(void)
+static int
+vlandev_linux_refresh(void)
 {
     const char *fn = "/proc/net/vlan/config";
-    struct shash_node *node;
     char line[128];
     FILE *stream;
 
@@ -66,17 +195,7 @@ vlandev_refresh(void)
         return 0;
     }
 
-    /* Free old cache.
-     *
-     * The 'name' members point to strings owned by the "shash"es so we do not
-     * free them ourselves. */
-    shash_clear_free_data(&vlan_devs);
-    SHASH_FOR_EACH (node, &vlan_real_devs) {
-        struct vlan_real_dev *vrd = node->data;
-
-        hmap_destroy(&vrd->vlan_devs);
-    }
-    shash_clear_free_data(&vlan_real_devs);
+    vlandev_clear__();
 
     /* Repopulate cache. */
     stream = fopen(fn, "r");
@@ -93,7 +212,7 @@ vlandev_refresh(void)
             return 0;
         }
 
-        VLOG_WARN_RL(&rl, "%s: open failed (%s)", fn, strerror(error));
+        VLOG_WARN_RL(&rl, "%s: open failed (%s)", fn, ovs_strerror(error));
         return error;
     }
 
@@ -101,26 +220,9 @@ vlandev_refresh(void)
         char vlan_dev[16], real_dev[16];
         int vid;
 
-        if (sscanf(line, "%15[^ |] | %d | %15s", vlan_dev, &vid, real_dev) == 3
-            && vid >= 0 && vid <= 4095
-            && !shash_find(&vlan_devs, vlan_dev)) {
-            struct vlan_real_dev *vrd;
-            struct vlan_dev *vd;
-
-            vrd = shash_find_data(&vlan_real_devs, real_dev);
-            if (!vrd) {
-                vrd = xmalloc(sizeof *vrd);
-                vrd->name = xstrdup(real_dev);
-                hmap_init(&vrd->vlan_devs);
-                shash_add_nocopy(&vlan_real_devs, vrd->name, vrd);
-            }
-
-            vd = xmalloc(sizeof *vd);
-            hmap_insert(&vrd->vlan_devs, &vd->hmap_node, hash_int(vid, 0));
-            vd->name = xstrdup(vlan_dev);
-            vd->vid = vid;
-            vd->real_dev = vrd;
-            shash_add_nocopy(&vlan_devs, vd->name, vd);
+        if (sscanf(line, "%15[^ |] | %d | %15s",
+                   vlan_dev, &vid, real_dev) == 3) {
+            vlandev_add__(vlan_dev, real_dev, vid);
         }
     }
     fclose(stream);
@@ -129,32 +231,6 @@ vlandev_refresh(void)
     return 0;
 }
 
-struct shash *
-vlandev_get_real_devs(void)
-{
-    return &vlan_real_devs;
-}
-
-const char *
-vlandev_get_name(const char *real_dev_name, int vid)
-{
-    const struct vlan_real_dev *real_dev;
-
-    real_dev = shash_find_data(&vlan_real_devs, real_dev_name);
-    if (real_dev) {
-        const struct vlan_dev *vlan_dev;
-
-        HMAP_FOR_EACH_WITH_HASH (vlan_dev, hmap_node, hash_int(vid, 0),
-                                 &real_dev->vlan_devs) {
-            if (vlan_dev->vid == vid) {
-                return vlan_dev->name;
-            }
-        }
-    }
-
-    return NULL;
-}
-
 static int
 do_vlan_ioctl(const char *netdev_name, struct vlan_ioctl_args *via,
               int cmd, const char *cmd_name)
@@ -174,13 +250,13 @@ do_vlan_ioctl(const char *netdev_name, struct vlan_ioctl_args *via,
     error = ioctl(sock, SIOCSIFVLAN, via) < 0 ? errno : 0;
     if (error) {
         VLOG_WARN_RL(&rl, "%s: VLAN ioctl %s failed (%s)",
-                     netdev_name, cmd_name, strerror(error));
+                     netdev_name, cmd_name, ovs_strerror(error));
     }
     return error;
 }
 
-int
-vlandev_add(const char *real_dev, int vid)
+static int
+vlandev_linux_add(const char *real_dev, int vid)
 {
     struct vlan_ioctl_args via;
     int error;
@@ -195,8 +271,8 @@ vlandev_add(const char *real_dev, int vid)
     return error;
 }
 
-int
-vlandev_del(const char *vlan_dev)
+static int
+vlandev_linux_del(const char *vlan_dev)
 {
     struct vlan_ioctl_args via;
     int error;
@@ -208,40 +284,134 @@ vlandev_del(const char *vlan_dev)
     }
     return error;
 }
-#else  /* !LINUX_DATAPATH */
-/* Stubs. */
 
-int
-vlandev_refresh(void)
+static const struct vlandev_class vlandev_linux_class = {
+    vlandev_linux_refresh,
+    vlandev_linux_add,
+    vlandev_linux_del
+};
+#endif
+\f
+/* Stub implementation. */
+
+static int
+vlandev_stub_add(const char *real_dev OVS_UNUSED, int vid OVS_UNUSED)
 {
-    return 0;
+    VLOG_ERR("not supported on non-Linux platform");
+    return EOPNOTSUPP;
 }
 
-struct shash *
-vlandev_get_real_devs(void)
+static int
+vlandev_stub_del(const char *vlan_dev OVS_UNUSED)
 {
-    static struct shash vlan_real_devs = SHASH_INITIALIZER(&vlan_real_devs);
+    VLOG_ERR("not supported on non-Linux platform");
+    return EOPNOTSUPP;
+}
 
-    return &vlan_real_devs;
+static const struct vlandev_class vlandev_stub_class = {
+    NULL,                       /* vd_refresh */
+    vlandev_stub_add,
+    vlandev_stub_del
+};
+\f
+/* Dummy implementation. */
+
+static int
+vlandev_dummy_add(const char *real_dev, int vid)
+{
+    char name[IFNAMSIZ];
+
+    if (snprintf(name, sizeof name, "%s.%d", real_dev, vid) >= sizeof name) {
+        return ENAMETOOLONG;
+    }
+    return vlandev_add__(name, real_dev, vid);
 }
 
-const char *
-vlandev_get_name(const char *real_dev_name OVS_UNUSED, int vid OVS_UNUSED)
+static int
+vlandev_dummy_del(const char *vlan_dev)
 {
-    return NULL;
+    return vlandev_del__(vlan_dev);
 }
 
-int
-vlandev_add(const char *real_dev OVS_UNUSED, int vid OVS_UNUSED)
+static const struct vlandev_class vlandev_dummy_class = {
+    NULL,                       /* vd_refresh */
+    vlandev_dummy_add,
+    vlandev_dummy_del
+};
+\f
+static int
+vlandev_add__(const char *vlan_dev, const char *real_dev, int vid)
 {
-    VLOG_ERR("not supported on non-Linux platform");
-    return EOPNOTSUPP;
+    uint32_t vid_hash = hash_int(vid, 0);
+    struct vlan_real_dev *vrd;
+    struct vlan_dev *vd;
+
+    if (vid < 0 || vid > 4095) {
+        return EINVAL;
+    } else if (shash_find(&vlan_devs, vlan_dev)) {
+        return EEXIST;
+    }
+
+    vrd = shash_find_data(&vlan_real_devs, real_dev);
+    if (!vrd) {
+        vrd = xmalloc(sizeof *vrd);
+        vrd->name = xstrdup(real_dev);
+        hmap_init(&vrd->vlan_devs);
+        shash_add_nocopy(&vlan_real_devs, vrd->name, vrd);
+    } else {
+        HMAP_FOR_EACH_WITH_HASH (vd, hmap_node, vid_hash, &vrd->vlan_devs) {
+            if (vd->vid == vid) {
+                return EEXIST;
+            }
+        }
+    }
+
+    vd = xmalloc(sizeof *vd);
+    hmap_insert(&vrd->vlan_devs, &vd->hmap_node, vid_hash);
+    vd->name = xstrdup(vlan_dev);
+    vd->vid = vid;
+    vd->real_dev = vrd;
+    shash_add_nocopy(&vlan_devs, vd->name, vd);
+
+    return 0;
 }
 
-int
-vlandev_del(const char *vlan_dev OVS_UNUSED)
+static int
+vlandev_del__(const char *vlan_dev)
 {
-    VLOG_ERR("not supported on non-Linux platform");
-    return EOPNOTSUPP;
+    struct shash_node *vd_node = shash_find(&vlan_devs, vlan_dev);
+    if (!vd_node) {
+        struct vlan_dev *vd = vd_node->data;
+        struct vlan_real_dev *vrd = vd->real_dev;
+
+        hmap_remove(&vrd->vlan_devs, &vd->hmap_node);
+        if (hmap_is_empty(&vrd->vlan_devs)) {
+            shash_find_and_delete_assert(&vlan_real_devs, vrd->name);
+            free(vrd);
+        }
+
+        shash_delete(&vlan_devs, vd_node);
+        free(vd);
+
+        return 0;
+    } else {
+        return ENOENT;
+    }
+}
+
+/* Clear 'vlan_devs' and 'vlan_real_devs' in preparation for repopulating. */
+static void
+vlandev_clear__(void)
+{
+    /* We do not free the 'name' members of struct vlan_dev and struct
+     * vlan_real_dev, because the "shash"es own them.. */
+    struct shash_node *node;
+
+    shash_clear_free_data(&vlan_devs);
+    SHASH_FOR_EACH (node, &vlan_real_devs) {
+        struct vlan_real_dev *vrd = node->data;
+
+        hmap_destroy(&vrd->vlan_devs);
+    }
+    shash_clear_free_data(&vlan_real_devs);
 }
-#endif
index ab74ecd..e25ffcb 100644 (file)
@@ -40,13 +40,11 @@ struct vlan_real_dev {
     struct hmap vlan_devs;      /* All child VLAN devices, hashed by VID. */
 };
 
-int vlandev_refresh(void);
+int vlandev_add(const char *real_dev, int vid);
+int vlandev_del(const char *vlan_dev);
 
+int vlandev_refresh(void);
 struct shash *vlandev_get_real_devs(void);
-
 const char *vlandev_get_name(const char *real_dev_name, int vid);
 
-int vlandev_add(const char *real_dev, int vid);
-int vlandev_del(const char *vlan_dev);
-
 #endif /* vlandev.h */
index 193e4f7..f87f48f 100644 (file)
@@ -320,7 +320,7 @@ vlog_set_log_file(const char *file_name)
     /* Log success or failure. */
     if (log_fd < 0) {
         VLOG_WARN("failed to open %s for logging: %s",
-                  log_file_name, strerror(errno));
+                  log_file_name, ovs_strerror(errno));
         error = errno;
     } else {
         VLOG_INFO("opened log file %s", log_file_name);
@@ -489,7 +489,7 @@ vlog_unixctl_reopen(struct unixctl_conn *conn, int argc OVS_UNUSED,
     if (log_file_name) {
         int error = vlog_reopen_log_file();
         if (error) {
-            unixctl_command_reply_error(conn, strerror(errno));
+            unixctl_command_reply_error(conn, ovs_strerror(errno));
         } else {
             unixctl_command_reply(conn, NULL);
         }
index 4c947a4..6904fdd 100644 (file)
@@ -233,7 +233,7 @@ worker_send_iovec(const struct iovec iovs[], size_t n_iovs,
         } while (error == EINTR);
         if (error) {
             worker_broke();
-            VLOG_ABORT("poll failed (%s)", strerror(error));
+            VLOG_ABORT("poll failed (%s)", ovs_strerror(error));
         }
     }
 }
@@ -264,7 +264,7 @@ worker_request_iovec(const struct iovec iovs[], size_t n_iovs,
     error = worker_send_iovec(all_iovs, n_iovs + 1, fds, n_fds);
     if (error) {
         worker_broke();
-        VLOG_ABORT("send failed (%s)", strerror(error));
+        VLOG_ABORT("send failed (%s)", ovs_strerror(error));
     }
     free(all_iovs);
 
@@ -347,9 +347,9 @@ worker_reply_iovec(const struct iovec *iovs, size_t n_iovs,
     if (error == EPIPE) {
         /* Parent probably died.  Continue processing any RPCs still buffered,
          * to avoid missing log messages. */
-        VLOG_INFO("send failed (%s)", strerror(error));
+        VLOG_INFO("send failed (%s)", ovs_strerror(error));
     } else if (error) {
-        VLOG_FATAL("send failed (%s)", strerror(error));
+        VLOG_FATAL("send failed (%s)", ovs_strerror(error));
     }
 
     free(all_iovs);
index ac0c7d5..ca80506 100644 (file)
@@ -1,6 +1,6 @@
 # -*- autoconf -*-
 
-# 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.
@@ -169,7 +169,9 @@ AC_DEFUN([OVS_CHECK_DBDIR],
 
 dnl Defines HAVE_BACKTRACE if backtrace() is found.
 AC_DEFUN([OVS_CHECK_BACKTRACE],
-  [AC_SEARCH_LIBS([backtrace], [execinfo ubacktrace])])
+  [AC_SEARCH_LIBS([backtrace], [execinfo ubacktrace],
+                  [AC_DEFINE([HAVE_BACKTRACE], [1],
+                             [Define to 1 if you have backtrace(3).])])])
 
 dnl Checks for __malloc_hook, etc., supported by glibc.
 AC_DEFUN([OVS_CHECK_MALLOC_HOOKS],
@@ -388,3 +390,59 @@ AC_DEFUN([OVS_CHECK_GROFF],
        ovs_cv_groff=no
      fi])
    AM_CONDITIONAL([HAVE_GROFF], [test "$ovs_cv_groff" = yes])])
+
+dnl Checks for thread-local storage support.
+dnl
+dnl Checks whether the compiler and linker support the C11
+dnl thread_local macro from <threads.h>, and if so defines
+dnl HAVE_THREAD_LOCAL.  If not, checks whether the compiler and linker
+dnl support the GCC __thread extension, and if so defines
+dnl HAVE___THREAD.
+AC_DEFUN([OVS_CHECK_TLS],
+  [AC_CACHE_CHECK(
+     [whether $CC has <threads.h> that supports thread_local],
+     [ovs_cv_thread_local],
+     [AC_LINK_IFELSE(
+        [AC_LANG_PROGRAM([#include <threads.h>
+static thread_local int var;], [return var;])],
+        [ovs_cv_thread_local=yes],
+        [ovs_cv_thread_local=no])])
+   if test $ovs_cv_thread_local = yes; then
+     AC_DEFINE([HAVE_THREAD_LOCAL], [1],
+               [Define to 1 if the C compiler and linker supports the C11
+                thread_local matcro defined in <threads.h>.])
+   else
+     AC_CACHE_CHECK(
+       [whether $CC supports __thread],
+       [ovs_cv___thread],
+       [AC_LINK_IFELSE(
+          [AC_LANG_PROGRAM([static __thread int var;], [return var;])],
+          [ovs_cv___thread=yes],
+          [ovs_cv___thread=no])])
+     if test $ovs_cv___thread = yes; then
+       AC_DEFINE([HAVE___THREAD], [1],
+                 [Define to 1 if the C compiler and linker supports the
+                  GCC __thread extenions.])
+     fi
+   fi])
+
+dnl OVS_CHECK_ATOMIC_ALWAYS_LOCK_FREE(SIZE)
+dnl
+dnl Checks __atomic_always_lock_free(SIZE, 0)
+AC_DEFUN([OVS_CHECK_ATOMIC_ALWAYS_LOCK_FREE], 
+  [AC_CACHE_CHECK(
+    [value of __atomic_always_lock_free($1)],
+    [ovs_cv_atomic_always_lock_free_$1],
+    [AC_COMPUTE_INT(
+        [ovs_cv_atomic_always_lock_free_$1],
+        [__atomic_always_lock_free($1, 0)],
+        [],
+        [ovs_cv_atomic_always_lock_free_$1=unsupported])])
+   if test ovs_cv_atomic_always_lock_free_$1 != unsupported; then
+     AC_DEFINE_UNQUOTED(
+       [ATOMIC_ALWAYS_LOCK_FREE_$1B],
+       [$ovs_cv_atomic_always_lock_free_$1],
+       [If the C compiler is GCC 4.7 or later, define to the return value of
+        __atomic_always_lock_free($1, 0).  If the C compiler is not GCC or is
+        an older version of GCC, the value does not matter.])
+   fi])
index a14f968..ed0d999 100644 (file)
@@ -229,7 +229,6 @@ vswitchd/ovs-vswitchd.8: \
        lib/common.man \
        lib/coverage-unixctl.man \
        lib/daemon.man \
-       lib/leak-checker.man \
        lib/memory-unixctl.man \
        lib/ssl-bootstrap.man \
        lib/ssl.man \
@@ -244,7 +243,6 @@ vswitchd/ovs-vswitchd.8.in:
 lib/common.man:
 lib/coverage-unixctl.man:
 lib/daemon.man:
-lib/leak-checker.man:
 lib/memory-unixctl.man:
 lib/ssl-bootstrap.man:
 lib/ssl.man:
index e4ea41d..b4d0876 100644 (file)
@@ -21,12 +21,15 @@ ofproto_libofproto_a_SOURCES = \
        ofproto/ofproto.c \
        ofproto/ofproto.h \
        ofproto/ofproto-dpif.c \
+       ofproto/ofproto-dpif.h \
        ofproto/ofproto-dpif-governor.c \
        ofproto/ofproto-dpif-governor.h \
        ofproto/ofproto-dpif-ipfix.c \
        ofproto/ofproto-dpif-ipfix.h \
        ofproto/ofproto-dpif-sflow.c \
        ofproto/ofproto-dpif-sflow.h \
+       ofproto/ofproto-dpif-xlate.c \
+       ofproto/ofproto-dpif-xlate.h \
        ofproto/ofproto-provider.h \
        ofproto/pktbuf.c \
        ofproto/pktbuf.h \
index 14572db..919fa1b 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 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.
@@ -70,7 +70,7 @@ collectors_create(const struct sset *targets, uint16_t default_port,
             static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
 
             VLOG_WARN_RL(&rl, "couldn't open connection to collector %s (%s)",
-                         name, strerror(error));
+                         name, ovs_strerror(error));
             if (!retval) {
                 retval = error;
             }
@@ -113,7 +113,7 @@ collectors_send(const struct collectors *c, const void *payload, size_t n)
             static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
             if (send(c->fds[i], payload, n, 0) == -1) {
                 VLOG_WARN_RL(&rl, "sending to collector failed: %s",
-                             strerror(errno));
+                             ovs_strerror(errno));
             }
         }
     }
index b8cdfa5..4264934 100644 (file)
@@ -108,7 +108,7 @@ static void ofconn_reconfigure(struct ofconn *,
 
 static void ofconn_run(struct ofconn *,
                        bool (*handle_openflow)(struct ofconn *,
-                                               struct ofpbuf *ofp_msg));
+                                               const struct ofpbuf *ofp_msg));
 static void ofconn_wait(struct ofconn *, bool handling_openflow);
 
 static const char *ofconn_get_target(const struct ofconn *);
@@ -269,7 +269,8 @@ connmgr_destroy(struct connmgr *mgr)
  * fail-open processing) are suppressed too. */
 void
 connmgr_run(struct connmgr *mgr,
-            bool (*handle_openflow)(struct ofconn *, struct ofpbuf *ofp_msg))
+            bool (*handle_openflow)(struct ofconn *,
+                                    const struct ofpbuf *ofp_msg))
 {
     struct ofconn *ofconn, *next_ofconn;
     struct ofservice *ofservice;
@@ -314,7 +315,7 @@ connmgr_run(struct connmgr *mgr,
             ofconn_set_rate_limit(ofconn, ofservice->rate_limit,
                                   ofservice->burst_limit);
         } else if (retval != EAGAIN) {
-            VLOG_WARN_RL(&rl, "accept failed (%s)", strerror(retval));
+            VLOG_WARN_RL(&rl, "accept failed (%s)", ovs_strerror(retval));
         }
     }
 
@@ -326,7 +327,7 @@ connmgr_run(struct connmgr *mgr,
         if (!retval) {
             add_snooper(mgr, vconn);
         } else if (retval != EAGAIN) {
-            VLOG_WARN_RL(&rl, "accept failed (%s)", strerror(retval));
+            VLOG_WARN_RL(&rl, "accept failed (%s)", ovs_strerror(retval));
         }
     }
 }
@@ -757,7 +758,7 @@ set_pvconns(struct pvconn ***pvconnsp, size_t *n_pvconnsp,
         if (!error) {
             pvconns[n_pvconns++] = pvconn;
         } else {
-            VLOG_ERR("failed to listen on %s: %s", name, strerror(error));
+            VLOG_ERR("failed to listen on %s: %s", name, ovs_strerror(error));
             if (!retval) {
                 retval = error;
             }
@@ -1038,7 +1039,7 @@ ofconn_send_error(const struct ofconn *ofconn,
 /* Same as pktbuf_retrieve(), using the pktbuf owned by 'ofconn'. */
 enum ofperr
 ofconn_pktbuf_retrieve(struct ofconn *ofconn, uint32_t id,
-                       struct ofpbuf **bufferp, uint16_t *in_port)
+                       struct ofpbuf **bufferp, ofp_port_t *in_port)
 {
     return pktbuf_retrieve(ofconn->pktbuf, id, bufferp, in_port);
 }
@@ -1234,7 +1235,8 @@ ofconn_may_recv(const struct ofconn *ofconn)
 
 static void
 ofconn_run(struct ofconn *ofconn,
-           bool (*handle_openflow)(struct ofconn *, struct ofpbuf *ofp_msg))
+           bool (*handle_openflow)(struct ofconn *,
+                                   const struct ofpbuf *ofp_msg))
 {
     struct connmgr *mgr = ofconn->connmgr;
     size_t i;
@@ -1643,20 +1645,9 @@ any_extras_changed(const struct connmgr *mgr,
 /* In-band implementation. */
 
 bool
-connmgr_msg_in_hook(struct connmgr *mgr, const struct flow *flow,
-                    const struct ofpbuf *packet)
+connmgr_has_in_band(struct connmgr *mgr)
 {
-    return mgr->in_band && in_band_msg_in_hook(mgr->in_band, flow, packet);
-}
-
-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, local_odp_port,
-                                               odp_actions, actions_len);
+    return mgr->in_band != NULL;
 }
 \f
 /* Fail-open and in-band implementation. */
index 48b8119..f92a523 100644 (file)
@@ -71,7 +71,7 @@ void connmgr_destroy(struct connmgr *);
 
 void connmgr_run(struct connmgr *,
                  bool (*handle_openflow)(struct ofconn *,
-                                         struct ofpbuf *ofp_msg));
+                                         const struct ofpbuf *ofp_msg));
 void connmgr_wait(struct connmgr *, bool handling_openflow);
 
 void connmgr_get_memory_usage(const struct connmgr *, struct simap *usage);
@@ -125,7 +125,7 @@ void ofconn_send_error(const struct ofconn *, const struct ofp_header *request,
                        enum ofperr);
 
 enum ofperr ofconn_pktbuf_retrieve(struct ofconn *, uint32_t id,
-                                   struct ofpbuf **bufferp, uint16_t *in_port);
+                                   struct ofpbuf **bufferp, ofp_port_t *in_port);
 
 bool ofconn_has_pending_opgroups(const struct ofconn *);
 void ofconn_add_opgroup(struct ofconn *, struct list *);
@@ -156,12 +156,7 @@ void connmgr_set_extra_in_band_remotes(struct connmgr *,
 void connmgr_set_in_band_queue(struct connmgr *, int queue_id);
 
 /* In-band implementation. */
-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);
+bool connmgr_has_in_band(struct connmgr *);
 
 /* Fail-open and in-band implementation. */
 void connmgr_flushed(struct connmgr *);
@@ -175,7 +170,7 @@ struct ofmonitor {
     enum nx_flow_monitor_flags flags;
 
     /* Matching. */
-    uint16_t out_port;
+    ofp_port_t out_port;
     uint8_t table_id;
     struct minimatch match;
 };
index 1a08fcc..265dcb2 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.
@@ -120,7 +120,8 @@ 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.s_addr), strerror(retval));
+                  IP_ARGS(r->remote_addr.sin_addr.s_addr),
+                  ovs_strerror(retval));
         return 1;
     }
     if (!next_hop_inaddr.s_addr) {
@@ -138,7 +139,7 @@ refresh_remote(struct in_band *ib, struct in_band_remote *r)
             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.s_addr),
-                         strerror(retval));
+                         ovs_strerror(retval));
             free(next_hop_dev);
             return 1;
         }
@@ -150,7 +151,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), ovs_strerror(retval));
     }
 
     /* If we don't have a MAC address, then refresh quickly, since we probably
@@ -222,62 +223,16 @@ refresh_local(struct in_band *ib)
     return true;
 }
 
-/* Returns true if 'packet' should be sent to the local port regardless
- * of the flow table. */
+/* Returns true if packets in 'flow' should be directed to the local port.
+ * (This keeps the flow table from preventing DHCP replies from being seen by
+ * the local port.) */
 bool
-in_band_msg_in_hook(struct in_band *in_band, const struct flow *flow,
-                    const struct ofpbuf *packet)
+in_band_must_output_to_local_port(const struct flow *flow)
 {
-    /* Regardless of how the flow table is configured, we want to be
-     * able to see replies to our DHCP requests. */
-    if (flow->dl_type == htons(ETH_TYPE_IP)
+    return (flow->dl_type == htons(ETH_TYPE_IP)
             && flow->nw_proto == IPPROTO_UDP
             && flow->tp_src == htons(DHCP_SERVER_PORT)
-            && flow->tp_dst == htons(DHCP_CLIENT_PORT)
-            && packet->l7) {
-        struct dhcp_header *dhcp;
-
-        dhcp = ofpbuf_at(packet, (char *)packet->l7 - (char *)packet->data,
-                         sizeof *dhcp);
-        if (!dhcp) {
-            return false;
-        }
-
-        refresh_local(in_band);
-        if (!eth_addr_is_zero(in_band->local_mac)
-            && eth_addr_equals(dhcp->chaddr, in_band->local_mac)) {
-            return true;
-        }
-    }
-
-    return false;
-}
-
-/* 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, 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
-     * by the local port. */
-    if (flow->dl_type == htons(ETH_TYPE_IP)
-            && flow->nw_proto == IPPROTO_UDP
-            && flow->tp_src == htons(DHCP_SERVER_PORT)
-            && flow->tp_dst == htons(DHCP_CLIENT_PORT)) {
-        const struct nlattr *a;
-        unsigned int left;
-
-        NL_ATTR_FOR_EACH_UNSAFE (a, left, actions, actions_len) {
-            if (nl_attr_type(a) == OVS_ACTION_ATTR_OUTPUT
-                && nl_attr_get_u32(a) == local_odp_port) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    return true;
+            && flow->tp_dst == htons(DHCP_CLIENT_PORT));
 }
 
 static void
@@ -467,7 +422,8 @@ in_band_create(struct ofproto *ofproto, const char *local_name,
     error = netdev_open(local_name, "internal", &local_netdev);
     if (error) {
         VLOG_ERR("failed to initialize in-band control: cannot open "
-                 "datapath local port %s (%s)", local_name, strerror(error));
+                 "datapath local port %s (%s)",
+                 local_name, ovs_strerror(error));
         return error;
     }
 
index 71de6ff..ad16dc2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 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 <stddef.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
+#include "flow.h"
 
 struct flow;
 struct in_band;
@@ -39,9 +40,6 @@ void in_band_set_remotes(struct in_band *,
 bool in_band_run(struct in_band *);
 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 *, uint32_t local_odp_port,
-                        const struct nlattr *odp_actions, size_t actions_len);
+bool in_band_must_output_to_local_port(const struct flow *);
 
 #endif /* in-band.h */
index aec3968..19ca80f 100644 (file)
@@ -41,8 +41,8 @@ struct netflow {
     uint8_t engine_id;            /* Value of engine_id to use. */
     long long int boot_time;      /* Time when netflow_create() was called. */
     struct collectors *collectors; /* NetFlow collectors. */
-    bool add_id_to_iface;         /* Put the 7 least signficiant bits of
-                                   * 'engine_id' into the most signficant
+    bool add_id_to_iface;         /* Put the 7 least significiant bits of
+                                   * 'engine_id' into the most significant
                                    * bits of the interface fields. */
     uint32_t netflow_cnt;         /* Flow sequence number for NetFlow. */
     struct ofpbuf packet;         /* NetFlow packet being accumulated. */
@@ -51,6 +51,20 @@ struct netflow {
     long long int reconfig_time;  /* When we reconfigured the timeouts. */
 };
 
+void
+netflow_mask_wc(struct flow *flow, struct flow_wildcards *wc)
+{
+    if (flow->dl_type != htons(ETH_TYPE_IP)) {
+        return;
+    }
+    memset(&wc->masks.nw_proto, 0xff, sizeof wc->masks.nw_proto);
+    memset(&wc->masks.nw_src, 0xff, sizeof wc->masks.nw_src);
+    memset(&wc->masks.nw_dst, 0xff, sizeof wc->masks.nw_dst);
+    memset(&wc->masks.tp_src, 0xff, sizeof wc->masks.tp_src);
+    memset(&wc->masks.tp_dst, 0xff, sizeof wc->masks.tp_dst);
+    wc->masks.nw_tos |= IP_DSCP_MASK;
+}
+
 static void
 gen_netflow_rec(struct netflow *nf, struct netflow_flow *nf_flow,
                 struct ofexpired *expired,
@@ -85,11 +99,13 @@ gen_netflow_rec(struct netflow *nf, struct netflow_flow *nf_flow,
     nf_rec->nexthop = htonl(0);
     if (nf->add_id_to_iface) {
         uint16_t iface = (nf->engine_id & 0x7f) << 9;
-        nf_rec->input = htons(iface | (expired->flow.in_port & 0x1ff));
-        nf_rec->output = htons(iface | (nf_flow->output_iface & 0x1ff));
+        nf_rec->input = htons(iface
+            | (ofp_to_u16(expired->flow.in_port.ofp_port) & 0x1ff));
+        nf_rec->output = htons(iface
+            | (ofp_to_u16(nf_flow->output_iface) & 0x1ff));
     } else {
-        nf_rec->input = htons(expired->flow.in_port);
-        nf_rec->output = htons(nf_flow->output_iface);
+        nf_rec->input = htons(ofp_to_u16(expired->flow.in_port.ofp_port));
+        nf_rec->output = htons(ofp_to_u16(nf_flow->output_iface));
     }
     nf_rec->packet_count = htonl(packet_count);
     nf_rec->byte_count = htonl(byte_count);
@@ -263,7 +279,7 @@ netflow_flow_init(struct netflow_flow *nf_flow OVS_UNUSED)
 void
 netflow_flow_clear(struct netflow_flow *nf_flow)
 {
-    uint16_t output_iface = nf_flow->output_iface;
+    ofp_port_t output_iface = nf_flow->output_iface;
 
     memset(nf_flow, 0, sizeof *nf_flow);
     nf_flow->output_iface = output_iface;
index c01ff15..e2366f6 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 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.
@@ -38,11 +38,9 @@ struct netflow_options {
     bool add_id_to_iface;
 };
 
-enum netflow_output_ports {
-    NF_OUT_FLOOD = UINT16_MAX,
-    NF_OUT_MULTI = UINT16_MAX - 1,
-    NF_OUT_DROP = UINT16_MAX - 2
-};
+#define NF_OUT_FLOOD OFP_PORT_C(UINT16_MAX)
+#define NF_OUT_MULTI OFP_PORT_C(UINT16_MAX - 1)
+#define NF_OUT_DROP  OFP_PORT_C(UINT16_MAX - 2)
 
 struct netflow_flow {
     long long int last_expired;   /* Time this flow last timed out. */
@@ -51,7 +49,7 @@ struct netflow_flow {
     uint64_t packet_count_off;    /* Packet count at last time out. */
     uint64_t byte_count_off;      /* Byte count at last time out. */
 
-    uint16_t output_iface;        /* Output interface index. */
+    ofp_port_t output_iface;      /* Output interface index. */
     uint8_t tcp_flags;            /* Bitwise-OR of all TCP flags seen. */
 };
 
@@ -64,6 +62,8 @@ void netflow_expire(struct netflow *, struct netflow_flow *,
 bool netflow_run(struct netflow *);
 void netflow_wait(struct netflow *);
 
+void netflow_mask_wc(struct flow *, struct flow_wildcards *);
+
 void netflow_flow_init(struct netflow_flow *);
 void netflow_flow_clear(struct netflow_flow *);
 void netflow_flow_update_time(struct netflow *, struct netflow_flow *,
index 75379d1..459f871 100644 (file)
@@ -53,13 +53,11 @@ enum { MAX_ELAPSED = 5000 }; /* In milliseconds. */
 
 static void governor_new_generation(struct governor *, unsigned int size);
 
-/* Creates and returns a new governor named 'name' (which is used only for log
- * messages). */
+/* Creates and returns a new governor. */
 struct governor *
-governor_create(const char *name)
+governor_create(void)
 {
     struct governor *g = xzalloc(sizeof *g);
-    g->name = xstrdup(name);
     governor_new_generation(g, MIN_SIZE);
     return g;
 }
@@ -69,8 +67,7 @@ void
 governor_destroy(struct governor *g)
 {
     if (g) {
-        VLOG_INFO("%s: disengaging", g->name);
-        free(g->name);
+        VLOG_INFO("disengaging");
         free(g->table);
         free(g);
     }
@@ -182,13 +179,12 @@ governor_new_generation(struct governor *g, unsigned int size)
     /* Allocate new table, if necessary. */
     if (g->size != size) {
         if (!g->size) {
-            VLOG_INFO("%s: engaging governor with %u kB hash table",
-                      g->name, size / 1024);
+            VLOG_INFO("engaging governor with %u kB hash table", size / 1024);
         } else {
-            VLOG_INFO("%s: processed %u packets in %.2f s, "
+            VLOG_INFO("processed %u packets in %.2f s, "
                       "%s hash table to %u kB "
                       "(%u hashes, %u setups, %u shortcuts)",
-                      g->name, g->n_packets,
+                      g->n_packets,
                       (time_msec() - g->start) / 1000.0,
                       size > g->size ? "enlarging" : "shrinking",
                       size / 1024,
@@ -199,9 +195,9 @@ governor_new_generation(struct governor *g, unsigned int size)
         g->table = xmalloc(size * sizeof *g->table);
         g->size = size;
     } else {
-        VLOG_DBG("%s: processed %u packets in %.2f s with %u kB hash table "
+        VLOG_DBG("processed %u packets in %.2f s with %u kB hash table "
                  "(%u hashes, %u setups, %u shortcuts)",
-                 g->name, g->n_packets, (time_msec() - g->start) / 1000.0,
+                 g->n_packets, (time_msec() - g->start) / 1000.0,
                  size / 1024, g->n_flows, g->n_setups, g->n_shortcuts);
     }
 
index 6dbd0d5..7e6ec92 100644 (file)
@@ -45,7 +45,7 @@ struct governor {
     unsigned int n_shortcuts;   /* Number of flows set up based on history. */
 };
 
-struct governor *governor_create(const char *name);
+struct governor *governor_create(void);
 void governor_destroy(struct governor *);
 
 void governor_run(struct governor *);
index 9de8b1c..ef0e980 100644 (file)
@@ -62,6 +62,7 @@ struct dpif_ipfix_flow_exporter_map_node {
 struct dpif_ipfix {
     struct dpif_ipfix_bridge_exporter bridge_exporter;
     struct hmap flow_exporter_map;  /* dpif_ipfix_flow_exporter_map_nodes. */
+    int ref_cnt;
 };
 
 #define IPFIX_VERSION 0x000a
@@ -464,6 +465,18 @@ dpif_ipfix_create(void)
     di = xzalloc(sizeof *di);
     dpif_ipfix_exporter_clear(&di->bridge_exporter.exporter);
     hmap_init(&di->flow_exporter_map);
+    di->ref_cnt = 1;
+    return di;
+}
+
+struct dpif_ipfix *
+dpif_ipfix_ref(const struct dpif_ipfix *di_)
+{
+    struct dpif_ipfix *di = CONST_CAST(struct dpif_ipfix *, di_);
+    if (di) {
+        ovs_assert(di->ref_cnt > 0);
+        di->ref_cnt++;
+    }
     return di;
 }
 
@@ -488,9 +501,14 @@ dpif_ipfix_clear(struct dpif_ipfix *di)
 }
 
 void
-dpif_ipfix_destroy(struct dpif_ipfix *di)
+dpif_ipfix_unref(struct dpif_ipfix *di)
 {
-    if (di) {
+    if (!di) {
+        return;
+    }
+
+    ovs_assert(di->ref_cnt > 0);
+    if (!--di->ref_cnt) {
         dpif_ipfix_clear(di);
         hmap_destroy(&di->flow_exporter_map);
         free(di);
@@ -651,7 +669,7 @@ ipfix_send_template_msg(struct dpif_ipfix_exporter *exporter,
     set_hdr = (struct ipfix_set_header*)((uint8_t*)msg.data + set_hdr_offset);
     set_hdr->length = htons(msg.size - set_hdr_offset);
 
-    /* TODO: Add Options Template Sets, at least to define a Flow Keys
+    /* XXX: Add Options Template Sets, at least to define a Flow Keys
      * Option Template. */
 
     ipfix_send_msg(exporter->collectors, &msg);
index 26b02f1..c050dba 100644 (file)
@@ -26,8 +26,10 @@ struct ofproto_ipfix_bridge_exporter_options;
 struct ofproto_ipfix_flow_exporter_options;
 
 struct dpif_ipfix *dpif_ipfix_create(void);
+struct dpif_ipfix *dpif_ipfix_ref(const struct dpif_ipfix *);
+void dpif_ipfix_unref(struct dpif_ipfix *);
+
 uint32_t dpif_ipfix_get_bridge_exporter_probability(const struct dpif_ipfix *);
-void dpif_ipfix_destroy(struct dpif_ipfix *);
 void dpif_ipfix_set_options(
     struct dpif_ipfix *,
     const struct ofproto_ipfix_bridge_exporter_options *,
index 9ad0eaf..64e6c96 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
  * Copyright (c) 2009 InMon Corp.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -46,7 +46,7 @@ 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;
+    odp_port_t odp_port;
 };
 
 struct dpif_sflow {
@@ -57,6 +57,7 @@ struct dpif_sflow {
     size_t n_flood, n_all;
     struct hmap ports;          /* Contains "struct dpif_sflow_port"s. */
     uint32_t probability;
+    int ref_cnt;
 };
 
 static void dpif_sflow_del_port__(struct dpif_sflow *,
@@ -142,12 +143,12 @@ 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, uint32_t odp_port)
+dpif_sflow_find_port(const struct dpif_sflow *ds, odp_port_t odp_port)
 {
     struct dpif_sflow_port *dsp;
 
-    HMAP_FOR_EACH_IN_BUCKET (dsp, hmap_node,
-                             hash_int(odp_port, 0), &ds->ports) {
+    HMAP_FOR_EACH_IN_BUCKET (dsp, hmap_node, hash_odp_port(odp_port),
+                             &ds->ports) {
         if (dsp->odp_port == odp_port) {
             return dsp;
         }
@@ -167,7 +168,7 @@ sflow_agent_get_counters(void *ds_, SFLPoller *poller,
     struct netdev_stats stats;
     enum netdev_flags flags;
 
-    dsp = dpif_sflow_find_port(ds, poller->bridgePort);
+    dsp = dpif_sflow_find_port(ds, u32_to_odp(poller->bridgePort));
     if (!dsp) {
         return;
     }
@@ -302,10 +303,22 @@ dpif_sflow_create(void)
     hmap_init(&ds->ports);
     ds->probability = 0;
     route_table_register();
+    ds->ref_cnt = 1;
 
     return ds;
 }
 
+struct dpif_sflow *
+dpif_sflow_ref(const struct dpif_sflow *ds_)
+{
+    struct dpif_sflow *ds = CONST_CAST(struct dpif_sflow *, ds_);
+    if (ds) {
+        ovs_assert(ds->ref_cnt > 0);
+        ds->ref_cnt++;
+    }
+    return ds;
+}
+
 /* 32-bit fraction of packets to sample with.  A value of 0 samples no packets,
  * a value of %UINT32_MAX samples all packets and intermediate values sample
  * intermediate fractions of packets. */
@@ -316,9 +329,14 @@ dpif_sflow_get_probability(const struct dpif_sflow *ds)
 }
 
 void
-dpif_sflow_destroy(struct dpif_sflow *ds)
+dpif_sflow_unref(struct dpif_sflow *ds)
 {
-    if (ds) {
+    if (!ds) {
+        return;
+    }
+
+    ovs_assert(ds->ref_cnt > 0);
+    if (!--ds->ref_cnt) {
         struct dpif_sflow_port *dsp, *next;
 
         route_table_unregister();
@@ -338,12 +356,12 @@ 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, dsp->odp_port);
+    sfl_poller_set_bridgePort(poller, odp_to_u32(dsp->odp_port));
 }
 
 void
 dpif_sflow_add_port(struct dpif_sflow *ds, struct ofport *ofport,
-                    uint32_t odp_port)
+                    odp_port_t odp_port)
 {
     struct dpif_sflow_port *dsp;
     int ifindex;
@@ -362,7 +380,7 @@ dpif_sflow_add_port(struct dpif_sflow *ds, struct ofport *ofport,
     dsp->ofport = ofport;
     dsp->odp_port = odp_port;
     SFL_DS_SET(dsp->dsi, SFL_DSCLASS_IFINDEX, ifindex, 0);
-    hmap_insert(&ds->ports, &dsp->hmap_node, hash_int(odp_port, 0));
+    hmap_insert(&ds->ports, &dsp->hmap_node, hash_odp_port(odp_port));
 
     /* Add poller. */
     if (ds->sflow_agent) {
@@ -382,7 +400,7 @@ dpif_sflow_del_port__(struct dpif_sflow *ds, struct dpif_sflow_port *dsp)
 }
 
 void
-dpif_sflow_del_port(struct dpif_sflow *ds, uint32_t odp_port)
+dpif_sflow_del_port(struct dpif_sflow *ds, odp_port_t odp_port)
 {
     struct dpif_sflow_port *dsp = dpif_sflow_find_port(ds, odp_port);
     if (dsp) {
@@ -488,7 +506,7 @@ dpif_sflow_set_options(struct dpif_sflow *ds,
 
 int
 dpif_sflow_odp_port_to_ifindex(const struct dpif_sflow *ds,
-                               uint32_t odp_port)
+                               odp_port_t odp_port)
 {
     struct dpif_sflow_port *dsp = dpif_sflow_find_port(ds, odp_port);
     return dsp ? SFL_DS_INDEX(dsp->dsi) : 0;
@@ -496,7 +514,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, uint32_t odp_in_port,
+                    const struct flow *flow, odp_port_t odp_in_port,
                     const union user_action_cookie *cookie)
 {
     SFL_FLOW_SAMPLE_TYPE fs;
index 02a0f17..d53c95c 100644 (file)
@@ -29,17 +29,19 @@ struct ofproto_sflow_options;
 struct ofport;
 
 struct dpif_sflow *dpif_sflow_create(void);
+struct dpif_sflow *dpif_sflow_ref(const struct dpif_sflow *);
+void dpif_sflow_unref(struct dpif_sflow *);
+
 uint32_t dpif_sflow_get_probability(const struct dpif_sflow *);
 
-void dpif_sflow_destroy(struct dpif_sflow *);
 void dpif_sflow_set_options(struct dpif_sflow *,
                             const struct ofproto_sflow_options *);
 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,
-                         uint32_t odp_port);
-void dpif_sflow_del_port(struct dpif_sflow *, uint32_t odp_port);
+                         odp_port_t odp_port);
+void dpif_sflow_del_port(struct dpif_sflow *, odp_port_t odp_port);
 
 void dpif_sflow_run(struct dpif_sflow *);
 void dpif_sflow_wait(struct dpif_sflow *);
@@ -47,9 +49,10 @@ void dpif_sflow_wait(struct dpif_sflow *);
 void dpif_sflow_received(struct dpif_sflow *,
                          struct ofpbuf *,
                          const struct flow *,
-                         uint32_t odp_port,
+                         odp_port_t odp_port,
                          const union user_action_cookie *);
 
-int dpif_sflow_odp_port_to_ifindex(const struct dpif_sflow *, uint32_t);
+int dpif_sflow_odp_port_to_ifindex(const struct dpif_sflow *,
+                                   odp_port_t odp_port);
 
 #endif /* ofproto/ofproto-dpif-sflow.h */
index daacd3a..0512b21 100644 (file)
@@ -5,15 +5,11 @@ 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...]"
+.IP "\fBdpif/show\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
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
new file mode 100644 (file)
index 0000000..0e7b9a0
--- /dev/null
@@ -0,0 +1,2027 @@
+/* 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.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License. */
+
+#include <config.h>
+
+#include "ofproto/ofproto-dpif-xlate.h"
+
+#include "bfd.h"
+#include "bitmap.h"
+#include "bond.h"
+#include "bundle.h"
+#include "byte-order.h"
+#include "cfm.h"
+#include "connmgr.h"
+#include "coverage.h"
+#include "dpif.h"
+#include "dynamic-string.h"
+#include "in-band.h"
+#include "lacp.h"
+#include "learn.h"
+#include "mac-learning.h"
+#include "meta-flow.h"
+#include "multipath.h"
+#include "netdev-vport.h"
+#include "netlink.h"
+#include "nx-match.h"
+#include "odp-execute.h"
+#include "ofp-actions.h"
+#include "ofproto/ofproto-dpif-ipfix.h"
+#include "ofproto/ofproto-dpif-sflow.h"
+#include "ofproto/ofproto-dpif.h"
+#include "tunnel.h"
+#include "vlog.h"
+
+COVERAGE_DEFINE(ofproto_dpif_xlate);
+
+VLOG_DEFINE_THIS_MODULE(ofproto_dpif_xlate);
+
+/* Maximum depth of flow table recursion (due to resubmit actions) in a
+ * flow translation. */
+#define MAX_RESUBMIT_RECURSION 64
+
+struct xlate_ctx {
+    struct xlate_in *xin;
+    struct xlate_out *xout;
+
+    struct ofproto_dpif *ofproto;
+
+    /* Flow at the last commit. */
+    struct flow base_flow;
+
+    /* Tunnel IP destination address as received.  This is stored separately
+     * as the base_flow.tunnel is cleared on init to reflect the datapath
+     * behavior.  Used to make sure not to send tunneled output to ourselves,
+     * which might lead to an infinite loop.  This could happen easily
+     * if a tunnel is marked as 'ip_remote=flow', and the flow does not
+     * actually set the tun_dst field. */
+    ovs_be32 orig_tunnel_ip_dst;
+
+    /* Stack for the push and pop actions.  Each stack element is of type
+     * "union mf_subvalue". */
+    union mf_subvalue init_stack[1024 / sizeof(union mf_subvalue)];
+    struct ofpbuf stack;
+
+    /* The rule that we are currently translating, or NULL. */
+    struct rule_dpif *rule;
+
+    int recurse;                /* Recursion level, via xlate_table_action. */
+    bool max_resubmit_trigger;  /* Recursed too deeply during translation. */
+    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. */
+    odp_port_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. */
+};
+
+/* A controller may use OFPP_NONE as the ingress port to indicate that
+ * it did not arrive on a "real" port.  'ofpp_none_bundle' exists for
+ * when an input bundle is needed for validation (e.g., mirroring or
+ * OFPP_NORMAL processing).  It is not connected to an 'ofproto' or have
+ * any 'port' structs, so care must be taken when dealing with it. */
+static struct ofbundle ofpp_none_bundle = {
+    .name      = "OFPP_NONE",
+    .vlan_mode = PORT_VLAN_TRUNK
+};
+
+static bool may_receive(const struct ofport_dpif *, struct xlate_ctx *);
+static void do_xlate_actions(const struct ofpact *, size_t ofpacts_len,
+                             struct xlate_ctx *);
+static void xlate_normal(struct xlate_ctx *);
+static void xlate_report(struct xlate_ctx *, const char *);
+static void xlate_table_action(struct xlate_ctx *, ofp_port_t in_port,
+                               uint8_t table_id, bool may_packet_in);
+static bool input_vid_is_valid(uint16_t vid, struct ofbundle *, bool warn);
+static uint16_t input_vid_to_vlan(const struct ofbundle *, uint16_t vid);
+static void output_normal(struct xlate_ctx *, const struct ofbundle *,
+                          uint16_t vlan);
+static void compose_output_action(struct xlate_ctx *, ofp_port_t ofp_port);
+
+static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+
+static bool
+ofbundle_trunks_vlan(const struct ofbundle *bundle, uint16_t vlan)
+{
+    return (bundle->vlan_mode != PORT_VLAN_ACCESS
+            && (!bundle->trunks || bitmap_is_set(bundle->trunks, vlan)));
+}
+
+static bool
+ofbundle_includes_vlan(const struct ofbundle *bundle, uint16_t vlan)
+{
+    return vlan == bundle->vlan || ofbundle_trunks_vlan(bundle, vlan);
+}
+
+static bool
+vlan_is_mirrored(const struct ofmirror *m, int vlan)
+{
+    return !m->vlans || bitmap_is_set(m->vlans, vlan);
+}
+
+static struct ofbundle *
+lookup_input_bundle(const struct ofproto_dpif *ofproto, ofp_port_t in_port,
+                    bool warn, struct ofport_dpif **in_ofportp)
+{
+    struct ofport_dpif *ofport;
+
+    /* Find the port and bundle for the received packet. */
+    ofport = get_ofp_port(ofproto, in_port);
+    if (in_ofportp) {
+        *in_ofportp = ofport;
+    }
+    if (ofport && ofport->bundle) {
+        return ofport->bundle;
+    }
+
+    /* Special-case OFPP_NONE, which a controller may use as the ingress
+     * port for traffic that it is sourcing. */
+    if (in_port == OFPP_NONE) {
+        return &ofpp_none_bundle;
+    }
+
+    /* Odd.  A few possible reasons here:
+     *
+     * - We deleted a port but there are still a few packets queued up
+     *   from it.
+     *
+     * - Someone externally added a port (e.g. "ovs-dpctl add-if") that
+     *   we don't know about.
+     *
+     * - The ofproto client didn't configure the port as part of a bundle.
+     *   This is particularly likely to happen if a packet was received on the
+     *   port after it was created, but before the client had a chance to
+     *   configure its bundle.
+     */
+    if (warn) {
+        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+
+        VLOG_WARN_RL(&rl, "bridge %s: received packet on unknown "
+                     "port %"PRIu16, ofproto->up.name, in_port);
+    }
+    return NULL;
+}
+
+static void
+add_mirror_actions(struct xlate_ctx *ctx, const struct flow *orig_flow)
+{
+    struct ofproto_dpif *ofproto = ctx->ofproto;
+    mirror_mask_t mirrors;
+    struct ofbundle *in_bundle;
+    uint16_t vlan;
+    uint16_t vid;
+    const struct nlattr *a;
+    size_t left;
+
+    in_bundle = lookup_input_bundle(ctx->ofproto, orig_flow->in_port.ofp_port,
+                                    ctx->xin->packet != NULL, NULL);
+    if (!in_bundle) {
+        return;
+    }
+    mirrors = in_bundle->src_mirrors;
+
+    /* Drop frames on bundles reserved for mirroring. */
+    if (in_bundle->mirror_out) {
+        if (ctx->xin->packet != NULL) {
+            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+            VLOG_WARN_RL(&rl, "bridge %s: dropping packet received on port "
+                         "%s, which is reserved exclusively for mirroring",
+                         ctx->ofproto->up.name, in_bundle->name);
+        }
+        return;
+    }
+
+    /* Check VLAN. */
+    vid = vlan_tci_to_vid(orig_flow->vlan_tci);
+    if (!input_vid_is_valid(vid, in_bundle, ctx->xin->packet != NULL)) {
+        return;
+    }
+    vlan = input_vid_to_vlan(in_bundle, vid);
+
+    /* Look at the output ports to check for destination selections. */
+
+    NL_ATTR_FOR_EACH (a, left, ctx->xout->odp_actions.data,
+                      ctx->xout->odp_actions.size) {
+        enum ovs_action_attr type = nl_attr_type(a);
+        struct ofport_dpif *ofport;
+
+        if (type != OVS_ACTION_ATTR_OUTPUT) {
+            continue;
+        }
+
+        ofport = get_odp_port(ofproto, nl_attr_get_odp_port(a));
+        if (ofport && ofport->bundle) {
+            mirrors |= ofport->bundle->dst_mirrors;
+        }
+    }
+
+    if (!mirrors) {
+        return;
+    }
+
+    /* Restore the original packet before adding the mirror actions. */
+    ctx->xin->flow = *orig_flow;
+
+    while (mirrors) {
+        struct ofmirror *m;
+
+        m = ofproto->mirrors[mirror_mask_ffs(mirrors) - 1];
+
+        if (m->vlans) {
+            ctx->xout->wc.masks.vlan_tci |= htons(VLAN_CFI | VLAN_VID_MASK);
+        }
+
+        if (!vlan_is_mirrored(m, vlan)) {
+            mirrors = zero_rightmost_1bit(mirrors);
+            continue;
+        }
+
+        mirrors &= ~m->dup_mirrors;
+        ctx->xout->mirrors |= m->dup_mirrors;
+        if (m->out) {
+            output_normal(ctx, m->out, vlan);
+        } else if (vlan != m->out_vlan
+                   && !eth_addr_is_reserved(orig_flow->dl_dst)) {
+            struct ofbundle *bundle;
+
+            HMAP_FOR_EACH (bundle, hmap_node, &ofproto->bundles) {
+                if (ofbundle_includes_vlan(bundle, m->out_vlan)
+                    && !bundle->mirror_out) {
+                    output_normal(ctx, bundle, m->out_vlan);
+                }
+            }
+        }
+    }
+}
+
+/* Given 'vid', the VID obtained from the 802.1Q header that was received as
+ * part of a packet (specify 0 if there was no 802.1Q header), and 'in_bundle',
+ * the bundle on which the packet was received, returns the VLAN to which the
+ * packet belongs.
+ *
+ * Both 'vid' and the return value are in the range 0...4095. */
+static uint16_t
+input_vid_to_vlan(const struct ofbundle *in_bundle, uint16_t vid)
+{
+    switch (in_bundle->vlan_mode) {
+    case PORT_VLAN_ACCESS:
+        return in_bundle->vlan;
+        break;
+
+    case PORT_VLAN_TRUNK:
+        return vid;
+
+    case PORT_VLAN_NATIVE_UNTAGGED:
+    case PORT_VLAN_NATIVE_TAGGED:
+        return vid ? vid : in_bundle->vlan;
+
+    default:
+        NOT_REACHED();
+    }
+}
+
+/* Checks whether a packet with the given 'vid' may ingress on 'in_bundle'.
+ * If so, returns true.  Otherwise, returns false and, if 'warn' is true, logs
+ * a warning.
+ *
+ * 'vid' should be the VID obtained from the 802.1Q header that was received as
+ * part of a packet (specify 0 if there was no 802.1Q header), in the range
+ * 0...4095. */
+static bool
+input_vid_is_valid(uint16_t vid, struct ofbundle *in_bundle, bool warn)
+{
+    /* Allow any VID on the OFPP_NONE port. */
+    if (in_bundle == &ofpp_none_bundle) {
+        return true;
+    }
+
+    switch (in_bundle->vlan_mode) {
+    case PORT_VLAN_ACCESS:
+        if (vid) {
+            if (warn) {
+                static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+                VLOG_WARN_RL(&rl, "bridge %s: dropping VLAN %"PRIu16" tagged "
+                             "packet received on port %s configured as VLAN "
+                             "%"PRIu16" access port",
+                             in_bundle->ofproto->up.name, vid,
+                             in_bundle->name, in_bundle->vlan);
+            }
+            return false;
+        }
+        return true;
+
+    case PORT_VLAN_NATIVE_UNTAGGED:
+    case PORT_VLAN_NATIVE_TAGGED:
+        if (!vid) {
+            /* Port must always carry its native VLAN. */
+            return true;
+        }
+        /* Fall through. */
+    case PORT_VLAN_TRUNK:
+        if (!ofbundle_includes_vlan(in_bundle, vid)) {
+            if (warn) {
+                static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+                VLOG_WARN_RL(&rl, "bridge %s: dropping VLAN %"PRIu16" packet "
+                             "received on port %s not configured for trunking "
+                             "VLAN %"PRIu16,
+                             in_bundle->ofproto->up.name, vid,
+                             in_bundle->name, vid);
+            }
+            return false;
+        }
+        return true;
+
+    default:
+        NOT_REACHED();
+    }
+
+}
+
+/* Given 'vlan', the VLAN that a packet belongs to, and
+ * 'out_bundle', a bundle on which the packet is to be output, returns the VID
+ * that should be included in the 802.1Q header.  (If the return value is 0,
+ * then the 802.1Q header should only be included in the packet if there is a
+ * nonzero PCP.)
+ *
+ * Both 'vlan' and the return value are in the range 0...4095. */
+static uint16_t
+output_vlan_to_vid(const struct ofbundle *out_bundle, uint16_t vlan)
+{
+    switch (out_bundle->vlan_mode) {
+    case PORT_VLAN_ACCESS:
+        return 0;
+
+    case PORT_VLAN_TRUNK:
+    case PORT_VLAN_NATIVE_TAGGED:
+        return vlan;
+
+    case PORT_VLAN_NATIVE_UNTAGGED:
+        return vlan == out_bundle->vlan ? 0 : vlan;
+
+    default:
+        NOT_REACHED();
+    }
+}
+
+static void
+output_normal(struct xlate_ctx *ctx, const struct ofbundle *out_bundle,
+              uint16_t vlan)
+{
+    ovs_be16 *flow_tci = &ctx->xin->flow.vlan_tci;
+    struct ofport_dpif *port;
+    uint16_t vid;
+    ovs_be16 tci, old_tci;
+
+    vid = output_vlan_to_vid(out_bundle, vlan);
+    if (!out_bundle->bond) {
+        port = ofbundle_get_a_port(out_bundle);
+    } else {
+        port = bond_choose_output_slave(out_bundle->bond, &ctx->xin->flow,
+                                        &ctx->xout->wc, vid, &ctx->xout->tags);
+        if (!port) {
+            /* No slaves enabled, so drop packet. */
+            return;
+        }
+    }
+
+    old_tci = *flow_tci;
+    tci = htons(vid);
+    if (tci || out_bundle->use_priority_tags) {
+        tci |= *flow_tci & htons(VLAN_PCP_MASK);
+        if (tci) {
+            tci |= htons(VLAN_CFI);
+        }
+    }
+    *flow_tci = tci;
+
+    compose_output_action(ctx, port->up.ofp_port);
+    *flow_tci = old_tci;
+}
+
+/* A VM broadcasts a gratuitous ARP to indicate that it has resumed after
+ * migration.  Older Citrix-patched Linux DomU used gratuitous ARP replies to
+ * indicate this; newer upstream kernels use gratuitous ARP requests. */
+static bool
+is_gratuitous_arp(const struct flow *flow, struct flow_wildcards *wc)
+{
+    if (flow->dl_type != htons(ETH_TYPE_ARP)) {
+        return false;
+    }
+
+    memset(&wc->masks.dl_dst, 0xff, sizeof wc->masks.dl_dst);
+    if (!eth_addr_is_broadcast(flow->dl_dst)) {
+        return false;
+    }
+
+    memset(&wc->masks.nw_proto, 0xff, sizeof wc->masks.nw_proto);
+    if (flow->nw_proto == ARP_OP_REPLY) {
+        return true;
+    } else if (flow->nw_proto == ARP_OP_REQUEST) {
+        memset(&wc->masks.nw_src, 0xff, sizeof wc->masks.nw_src);
+        memset(&wc->masks.nw_dst, 0xff, sizeof wc->masks.nw_dst);
+
+        return flow->nw_src == flow->nw_dst;
+    } else {
+        return false;
+    }
+}
+
+static void
+update_learning_table(struct ofproto_dpif *ofproto,
+                      const struct flow *flow, struct flow_wildcards *wc,
+                      int vlan, struct ofbundle *in_bundle)
+{
+    struct mac_entry *mac;
+
+    /* Don't learn the OFPP_NONE port. */
+    if (in_bundle == &ofpp_none_bundle) {
+        return;
+    }
+
+    if (!mac_learning_may_learn(ofproto->ml, flow->dl_src, vlan)) {
+        return;
+    }
+
+    mac = mac_learning_insert(ofproto->ml, flow->dl_src, vlan);
+    if (is_gratuitous_arp(flow, wc)) {
+        /* We don't want to learn from gratuitous ARP packets that are
+         * reflected back over bond slaves so we lock the learning table. */
+        if (!in_bundle->bond) {
+            mac_entry_set_grat_arp_lock(mac);
+        } else if (mac_entry_is_grat_arp_locked(mac)) {
+            return;
+        }
+    }
+
+    if (mac_entry_is_new(mac) || mac->port.p != in_bundle) {
+        /* The log messages here could actually be useful in debugging,
+         * so keep the rate limit relatively high. */
+        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(30, 300);
+        VLOG_DBG_RL(&rl, "bridge %s: learned that "ETH_ADDR_FMT" is "
+                    "on port %s in VLAN %d",
+                    ofproto->up.name, ETH_ADDR_ARGS(flow->dl_src),
+                    in_bundle->name, vlan);
+
+        mac->port.p = in_bundle;
+        mac_learning_changed(ofproto->ml, mac);
+    }
+}
+
+/* Determines whether packets in 'flow' within 'ofproto' should be forwarded or
+ * dropped.  Returns true if they may be forwarded, false if they should be
+ * dropped.
+ *
+ * 'in_port' must be the ofport_dpif that corresponds to flow->in_port.
+ * 'in_port' must be part of a bundle (e.g. in_port->bundle must be nonnull).
+ *
+ * 'vlan' must be the VLAN that corresponds to flow->vlan_tci on 'in_port', as
+ * returned by input_vid_to_vlan().  It must be a valid VLAN for 'in_port', as
+ * checked by input_vid_is_valid().
+ *
+ * May also add tags to '*tags', although the current implementation only does
+ * so in one special case.
+ */
+static bool
+is_admissible(struct xlate_ctx *ctx, struct ofport_dpif *in_port,
+              uint16_t vlan)
+{
+    struct ofproto_dpif *ofproto = ctx->ofproto;
+    struct flow *flow = &ctx->xin->flow;
+    struct ofbundle *in_bundle = in_port->bundle;
+
+    /* Drop frames for reserved multicast addresses
+     * only if forward_bpdu option is absent. */
+    if (!ofproto->up.forward_bpdu && eth_addr_is_reserved(flow->dl_dst)) {
+        xlate_report(ctx, "packet has reserved destination MAC, dropping");
+        return false;
+    }
+
+    if (in_bundle->bond) {
+        struct mac_entry *mac;
+
+        switch (bond_check_admissibility(in_bundle->bond, in_port,
+                                         flow->dl_dst, &ctx->xout->tags)) {
+        case BV_ACCEPT:
+            break;
+
+        case BV_DROP:
+            xlate_report(ctx, "bonding refused admissibility, dropping");
+            return false;
+
+        case BV_DROP_IF_MOVED:
+            mac = mac_learning_lookup(ofproto->ml, flow->dl_src, vlan, NULL);
+            if (mac && mac->port.p != in_bundle &&
+                (!is_gratuitous_arp(flow, &ctx->xout->wc)
+                 || mac_entry_is_grat_arp_locked(mac))) {
+                xlate_report(ctx, "SLB bond thinks this packet looped back, "
+                            "dropping");
+                return false;
+            }
+            break;
+        }
+    }
+
+    return true;
+}
+
+static void
+xlate_normal(struct xlate_ctx *ctx)
+{
+    struct flow_wildcards *wc = &ctx->xout->wc;
+    struct flow *flow = &ctx->xin->flow;
+    struct ofport_dpif *in_port;
+    struct ofbundle *in_bundle;
+    struct mac_entry *mac;
+    uint16_t vlan;
+    uint16_t vid;
+
+    ctx->xout->has_normal = true;
+
+    memset(&wc->masks.dl_src, 0xff, sizeof wc->masks.dl_src);
+    memset(&wc->masks.dl_dst, 0xff, sizeof wc->masks.dl_dst);
+    wc->masks.vlan_tci |= htons(VLAN_VID_MASK | VLAN_CFI);
+
+    in_bundle = lookup_input_bundle(ctx->ofproto, flow->in_port.ofp_port,
+                                    ctx->xin->packet != NULL, &in_port);
+    if (!in_bundle) {
+        xlate_report(ctx, "no input bundle, dropping");
+        return;
+    }
+
+    /* Drop malformed frames. */
+    if (flow->dl_type == htons(ETH_TYPE_VLAN) &&
+        !(flow->vlan_tci & htons(VLAN_CFI))) {
+        if (ctx->xin->packet != NULL) {
+            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+            VLOG_WARN_RL(&rl, "bridge %s: dropping packet with partial "
+                         "VLAN tag received on port %s",
+                         ctx->ofproto->up.name, in_bundle->name);
+        }
+        xlate_report(ctx, "partial VLAN tag, dropping");
+        return;
+    }
+
+    /* Drop frames on bundles reserved for mirroring. */
+    if (in_bundle->mirror_out) {
+        if (ctx->xin->packet != NULL) {
+            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+            VLOG_WARN_RL(&rl, "bridge %s: dropping packet received on port "
+                         "%s, which is reserved exclusively for mirroring",
+                         ctx->ofproto->up.name, in_bundle->name);
+        }
+        xlate_report(ctx, "input port is mirror output port, dropping");
+        return;
+    }
+
+    /* Check VLAN. */
+    vid = vlan_tci_to_vid(flow->vlan_tci);
+    if (!input_vid_is_valid(vid, in_bundle, ctx->xin->packet != NULL)) {
+        xlate_report(ctx, "disallowed VLAN VID for this input port, dropping");
+        return;
+    }
+    vlan = input_vid_to_vlan(in_bundle, vid);
+
+    /* Check other admissibility requirements. */
+    if (in_port && !is_admissible(ctx, in_port, vlan)) {
+        return;
+    }
+
+    /* Learn source MAC. */
+    if (ctx->xin->may_learn) {
+        update_learning_table(ctx->ofproto, flow, wc, vlan, in_bundle);
+    }
+
+    /* Determine output bundle. */
+    mac = mac_learning_lookup(ctx->ofproto->ml, flow->dl_dst, vlan,
+                              &ctx->xout->tags);
+    if (mac) {
+        if (mac->port.p != in_bundle) {
+            xlate_report(ctx, "forwarding to learned port");
+            output_normal(ctx, mac->port.p, vlan);
+        } else {
+            xlate_report(ctx, "learned port is input port, dropping");
+        }
+    } else {
+        struct ofbundle *bundle;
+
+        xlate_report(ctx, "no learned MAC for destination, flooding");
+        HMAP_FOR_EACH (bundle, hmap_node, &ctx->ofproto->bundles) {
+            if (bundle != in_bundle
+                && ofbundle_includes_vlan(bundle, vlan)
+                && bundle->floodable
+                && !bundle->mirror_out) {
+                output_normal(ctx, bundle, vlan);
+            }
+        }
+        ctx->xout->nf_output_iface = NF_OUT_FLOOD;
+    }
+}
+
+/* Compose SAMPLE action for sFlow or IPFIX.  The given probability is
+ * the number of packets out of UINT32_MAX to sample.  The given
+ * cookie is passed back in the callback for each sampled packet.
+ */
+static size_t
+compose_sample_action(const struct ofproto_dpif *ofproto,
+                      struct ofpbuf *odp_actions,
+                      const struct flow *flow,
+                      const uint32_t probability,
+                      const union user_action_cookie *cookie,
+                      const size_t cookie_size)
+{
+    size_t sample_offset, actions_offset;
+    int cookie_offset;
+
+    sample_offset = nl_msg_start_nested(odp_actions, OVS_ACTION_ATTR_SAMPLE);
+
+    nl_msg_put_u32(odp_actions, OVS_SAMPLE_ATTR_PROBABILITY, probability);
+
+    actions_offset = nl_msg_start_nested(odp_actions, OVS_SAMPLE_ATTR_ACTIONS);
+    cookie_offset = put_userspace_action(ofproto, odp_actions, flow, cookie,
+                                         cookie_size);
+
+    nl_msg_end_nested(odp_actions, actions_offset);
+    nl_msg_end_nested(odp_actions, sample_offset);
+    return cookie_offset;
+}
+
+static void
+compose_sflow_cookie(const struct ofproto_dpif *ofproto,
+                     ovs_be16 vlan_tci, odp_port_t odp_port,
+                     unsigned int n_outputs, union user_action_cookie *cookie)
+{
+    int ifindex;
+
+    cookie->type = USER_ACTION_COOKIE_SFLOW;
+    cookie->sflow.vlan_tci = vlan_tci;
+
+    /* See http://www.sflow.org/sflow_version_5.txt (search for "Input/output
+     * port information") for the interpretation of cookie->output. */
+    switch (n_outputs) {
+    case 0:
+        /* 0x40000000 | 256 means "packet dropped for unknown reason". */
+        cookie->sflow.output = 0x40000000 | 256;
+        break;
+
+    case 1:
+        ifindex = dpif_sflow_odp_port_to_ifindex(ofproto->sflow, odp_port);
+        if (ifindex) {
+            cookie->sflow.output = ifindex;
+            break;
+        }
+        /* Fall through. */
+    default:
+        /* 0x80000000 means "multiple output ports. */
+        cookie->sflow.output = 0x80000000 | n_outputs;
+        break;
+    }
+}
+
+/* Compose SAMPLE action for sFlow bridge sampling. */
+static size_t
+compose_sflow_action(const struct ofproto_dpif *ofproto,
+                     struct ofpbuf *odp_actions,
+                     const struct flow *flow,
+                     odp_port_t odp_port)
+{
+    uint32_t probability;
+    union user_action_cookie cookie;
+
+    if (!ofproto->sflow || flow->in_port.ofp_port == OFPP_NONE) {
+        return 0;
+    }
+
+    probability = dpif_sflow_get_probability(ofproto->sflow);
+    compose_sflow_cookie(ofproto, htons(0), odp_port,
+                         odp_port == ODPP_NONE ? 0 : 1, &cookie);
+
+    return compose_sample_action(ofproto, odp_actions, flow,  probability,
+                                 &cookie, sizeof cookie.sflow);
+}
+
+static void
+compose_flow_sample_cookie(uint16_t probability, uint32_t collector_set_id,
+                           uint32_t obs_domain_id, uint32_t obs_point_id,
+                           union user_action_cookie *cookie)
+{
+    cookie->type = USER_ACTION_COOKIE_FLOW_SAMPLE;
+    cookie->flow_sample.probability = probability;
+    cookie->flow_sample.collector_set_id = collector_set_id;
+    cookie->flow_sample.obs_domain_id = obs_domain_id;
+    cookie->flow_sample.obs_point_id = obs_point_id;
+}
+
+static void
+compose_ipfix_cookie(union user_action_cookie *cookie)
+{
+    cookie->type = USER_ACTION_COOKIE_IPFIX;
+}
+
+/* Compose SAMPLE action for IPFIX bridge sampling. */
+static void
+compose_ipfix_action(const struct ofproto_dpif *ofproto,
+                     struct ofpbuf *odp_actions,
+                     const struct flow *flow)
+{
+    uint32_t probability;
+    union user_action_cookie cookie;
+
+    if (!ofproto->ipfix || flow->in_port.ofp_port == OFPP_NONE) {
+        return;
+    }
+
+    probability = dpif_ipfix_get_bridge_exporter_probability(ofproto->ipfix);
+    compose_ipfix_cookie(&cookie);
+
+    compose_sample_action(ofproto, odp_actions, flow,  probability,
+                          &cookie, sizeof cookie.ipfix);
+}
+
+/* SAMPLE action for sFlow must be first action in any given list of
+ * actions.  At this point we do not have all information required to
+ * build it. So try to build sample action as complete as possible. */
+static void
+add_sflow_action(struct xlate_ctx *ctx)
+{
+    ctx->user_cookie_offset = compose_sflow_action(ctx->ofproto,
+                                                   &ctx->xout->odp_actions,
+                                                   &ctx->xin->flow, ODPP_NONE);
+    ctx->sflow_odp_port = 0;
+    ctx->sflow_n_outputs = 0;
+}
+
+/* SAMPLE action for IPFIX must be 1st or 2nd action in any given list
+ * of actions, eventually after the SAMPLE action for sFlow. */
+static void
+add_ipfix_action(struct xlate_ctx *ctx)
+{
+    compose_ipfix_action(ctx->ofproto, &ctx->xout->odp_actions,
+                         &ctx->xin->flow);
+}
+
+/* Fix SAMPLE action according to data collected while composing ODP actions.
+ * We need to fix SAMPLE actions OVS_SAMPLE_ATTR_ACTIONS attribute, i.e. nested
+ * USERSPACE action's user-cookie which is required for sflow. */
+static void
+fix_sflow_action(struct xlate_ctx *ctx)
+{
+    const struct flow *base = &ctx->base_flow;
+    union user_action_cookie *cookie;
+
+    if (!ctx->user_cookie_offset) {
+        return;
+    }
+
+    cookie = ofpbuf_at(&ctx->xout->odp_actions, ctx->user_cookie_offset,
+                       sizeof cookie->sflow);
+    ovs_assert(cookie->type == USER_ACTION_COOKIE_SFLOW);
+
+    compose_sflow_cookie(ctx->ofproto, base->vlan_tci,
+                         ctx->sflow_odp_port, ctx->sflow_n_outputs, cookie);
+}
+
+static enum slow_path_reason
+process_special(struct xlate_ctx *ctx, const struct flow *flow,
+                const struct ofport_dpif *ofport, const struct ofpbuf *packet)
+{
+    struct ofproto_dpif *ofproto = ctx->ofproto;
+    struct flow_wildcards *wc = &ctx->xout->wc;
+
+    if (!ofport) {
+        return 0;
+    } else if (ofport->cfm && cfm_should_process_flow(ofport->cfm, flow, wc)) {
+        if (packet) {
+            cfm_process_heartbeat(ofport->cfm, packet);
+        }
+        return SLOW_CFM;
+    } else if (ofport->bfd && bfd_should_process_flow(flow, wc)) {
+        if (packet) {
+            bfd_process_packet(ofport->bfd, flow, packet);
+        }
+        return SLOW_BFD;
+    } else if (ofport->bundle && ofport->bundle->lacp
+               && flow->dl_type == htons(ETH_TYPE_LACP)) {
+        if (packet) {
+            lacp_process_packet(ofport->bundle->lacp, ofport, packet);
+        }
+        return SLOW_LACP;
+    } else if (ofproto->stp && stp_should_process_flow(flow, wc)) {
+        if (packet) {
+            stp_process_packet(ofport, packet);
+        }
+        return SLOW_STP;
+    } else {
+        return 0;
+    }
+}
+
+static void
+compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
+                        bool check_stp)
+{
+    const struct ofport_dpif *ofport = get_ofp_port(ctx->ofproto, ofp_port);
+    struct flow_wildcards *wc = &ctx->xout->wc;
+    struct flow *flow = &ctx->xin->flow;
+    ovs_be16 flow_vlan_tci;
+    uint32_t flow_skb_mark;
+    uint8_t flow_nw_tos;
+    odp_port_t out_port, odp_port;
+    uint8_t dscp;
+
+    /* If 'struct flow' gets additional metadata, we'll need to zero it out
+     * before traversing a patch port. */
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 20);
+
+    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;
+    }
+
+    if (ofport->peer) {
+        struct ofport_dpif *peer = ofport->peer;
+        struct flow old_flow = ctx->xin->flow;
+        enum slow_path_reason special;
+
+        ctx->ofproto = ofproto_dpif_cast(peer->up.ofproto);
+        flow->in_port.ofp_port = peer->up.ofp_port;
+        flow->metadata = htonll(0);
+        memset(&flow->tunnel, 0, sizeof flow->tunnel);
+        memset(flow->regs, 0, sizeof flow->regs);
+
+        special = process_special(ctx, &ctx->xin->flow, peer,
+                                  ctx->xin->packet);
+        if (special) {
+            ctx->xout->slow = special;
+        } else if (may_receive(peer, ctx)) {
+            if (stp_forward_in_state(peer->stp_state)) {
+                xlate_table_action(ctx, flow->in_port.ofp_port, 0, true);
+            } else {
+                /* Forwarding is disabled by STP.  Let OFPP_NORMAL and the
+                 * learning action look at the packet, then drop it. */
+                struct flow old_base_flow = ctx->base_flow;
+                size_t old_size = ctx->xout->odp_actions.size;
+                xlate_table_action(ctx, flow->in_port.ofp_port, 0, true);
+                ctx->base_flow = old_base_flow;
+                ctx->xout->odp_actions.size = old_size;
+            }
+        }
+
+        ctx->xin->flow = old_flow;
+        ctx->ofproto = ofproto_dpif_cast(ofport->up.ofproto);
+
+        if (ctx->xin->resubmit_stats) {
+            netdev_vport_inc_tx(ofport->up.netdev, ctx->xin->resubmit_stats);
+            netdev_vport_inc_rx(peer->up.netdev, ctx->xin->resubmit_stats);
+        }
+
+        return;
+    }
+
+    flow_vlan_tci = flow->vlan_tci;
+    flow_skb_mark = flow->skb_mark;
+    flow_nw_tos = flow->nw_tos;
+
+    if (ofproto_dpif_dscp_from_priority(ofport, flow->skb_priority, &dscp)) {
+        wc->masks.nw_tos |= IP_ECN_MASK;
+        flow->nw_tos &= ~IP_DSCP_MASK;
+        flow->nw_tos |= dscp;
+    }
+
+    if (ofport->is_tunnel) {
+         /* Save tunnel metadata so that changes made due to
+          * the Logical (tunnel) Port are not visible for any further
+          * matches, while explicit set actions on tunnel metadata are.
+          */
+        struct flow_tnl flow_tnl = flow->tunnel;
+        odp_port = tnl_port_send(ofport, flow, &ctx->xout->wc);
+        if (odp_port == ODPP_NONE) {
+            xlate_report(ctx, "Tunneling decided against output");
+            goto out; /* restore flow_nw_tos */
+        }
+        if (flow->tunnel.ip_dst == ctx->orig_tunnel_ip_dst) {
+            xlate_report(ctx, "Not tunneling to our own address");
+            goto out; /* restore flow_nw_tos */
+        }
+        if (ctx->xin->resubmit_stats) {
+            netdev_vport_inc_tx(ofport->up.netdev, ctx->xin->resubmit_stats);
+        }
+        out_port = odp_port;
+        commit_odp_tunnel_action(flow, &ctx->base_flow,
+                                 &ctx->xout->odp_actions);
+        flow->tunnel = flow_tnl; /* Restore tunnel metadata */
+    } else {
+        ofp_port_t vlandev_port;
+
+        odp_port = ofport->odp_port;
+        if (!hmap_is_empty(&ctx->ofproto->realdev_vid_map)) {
+            wc->masks.vlan_tci |= htons(VLAN_VID_MASK | VLAN_CFI);
+        }
+        vlandev_port = vsp_realdev_to_vlandev(ctx->ofproto, ofp_port,
+                                              flow->vlan_tci);
+        if (vlandev_port == ofp_port) {
+            out_port = odp_port;
+        } else {
+            out_port = ofp_port_to_odp_port(ctx->ofproto, vlandev_port);
+            flow->vlan_tci = htons(0);
+        }
+        flow->skb_mark &= ~IPSEC_MARK;
+    }
+
+    if (out_port != ODPP_NONE) {
+        commit_odp_actions(flow, &ctx->base_flow,
+                           &ctx->xout->odp_actions, &ctx->xout->wc);
+        nl_msg_put_odp_port(&ctx->xout->odp_actions, OVS_ACTION_ATTR_OUTPUT,
+                            out_port);
+
+        ctx->sflow_odp_port = odp_port;
+        ctx->sflow_n_outputs++;
+        ctx->xout->nf_output_iface = ofp_port;
+    }
+
+ out:
+    /* Restore flow */
+    flow->vlan_tci = flow_vlan_tci;
+    flow->skb_mark = flow_skb_mark;
+    flow->nw_tos = flow_nw_tos;
+}
+
+static void
+compose_output_action(struct xlate_ctx *ctx, ofp_port_t ofp_port)
+{
+    compose_output_action__(ctx, ofp_port, true);
+}
+
+/* Common rule processing in one place to avoid duplicating code. */
+static struct rule_dpif *
+ctx_rule_hooks(struct xlate_ctx *ctx, struct rule_dpif *rule,
+               bool may_packet_in)
+{
+    if (ctx->xin->resubmit_hook) {
+        ctx->xin->resubmit_hook(ctx->xin, rule, ctx->recurse);
+    }
+    if (rule == NULL && may_packet_in) {
+        /* XXX
+         * check if table configuration flags
+         * OFPTC_TABLE_MISS_CONTROLLER, default.
+         * OFPTC_TABLE_MISS_CONTINUE,
+         * OFPTC_TABLE_MISS_DROP
+         * When OF1.0, OFPTC_TABLE_MISS_CONTINUE is used. What to do?
+         */
+        rule = rule_dpif_miss_rule(ctx->ofproto, &ctx->xin->flow);
+    }
+    if (rule && ctx->xin->resubmit_stats) {
+        rule_credit_stats(rule, ctx->xin->resubmit_stats);
+    }
+    return rule;
+}
+
+static void
+xlate_table_action(struct xlate_ctx *ctx,
+                   ofp_port_t in_port, uint8_t table_id, bool may_packet_in)
+{
+    if (ctx->recurse < MAX_RESUBMIT_RECURSION) {
+        struct rule_dpif *rule;
+        ofp_port_t old_in_port = ctx->xin->flow.in_port.ofp_port;
+        uint8_t old_table_id = ctx->table_id;
+
+        ctx->table_id = table_id;
+
+        /* Look up a flow with 'in_port' as the input port. */
+        ctx->xin->flow.in_port.ofp_port = in_port;
+        rule = rule_dpif_lookup_in_table(ctx->ofproto, &ctx->xin->flow,
+                                         &ctx->xout->wc, table_id);
+
+        ctx->xout->tags |= calculate_flow_tag(ctx->ofproto, &ctx->xin->flow,
+                                              ctx->table_id, rule);
+
+        /* Restore the original input port.  Otherwise OFPP_NORMAL and
+         * OFPP_IN_PORT will have surprising behavior. */
+        ctx->xin->flow.in_port.ofp_port = old_in_port;
+
+        rule = ctx_rule_hooks(ctx, rule, may_packet_in);
+
+        if (rule) {
+            struct rule_dpif *old_rule = ctx->rule;
+
+            ctx->recurse++;
+            ctx->rule = rule;
+            do_xlate_actions(rule->up.ofpacts, rule->up.ofpacts_len, ctx);
+            ctx->rule = old_rule;
+            ctx->recurse--;
+        }
+
+        ctx->table_id = old_table_id;
+    } else {
+        static struct vlog_rate_limit recurse_rl = VLOG_RATE_LIMIT_INIT(1, 1);
+
+        VLOG_ERR_RL(&recurse_rl, "resubmit actions recursed over %d times",
+                    MAX_RESUBMIT_RECURSION);
+        ctx->max_resubmit_trigger = true;
+    }
+}
+
+static void
+xlate_ofpact_resubmit(struct xlate_ctx *ctx,
+                      const struct ofpact_resubmit *resubmit)
+{
+    ofp_port_t in_port;
+    uint8_t table_id;
+
+    in_port = resubmit->in_port;
+    if (in_port == OFPP_IN_PORT) {
+        in_port = ctx->xin->flow.in_port.ofp_port;
+    }
+
+    table_id = resubmit->table_id;
+    if (table_id == 255) {
+        table_id = ctx->table_id;
+    }
+
+    xlate_table_action(ctx, in_port, table_id, false);
+}
+
+static void
+flood_packets(struct xlate_ctx *ctx, bool all)
+{
+    struct ofport_dpif *ofport;
+
+    HMAP_FOR_EACH (ofport, up.hmap_node, &ctx->ofproto->up.ports) {
+        ofp_port_t ofp_port = ofport->up.ofp_port;
+
+        if (ofp_port == ctx->xin->flow.in_port.ofp_port) {
+            continue;
+        }
+
+        if (all) {
+            compose_output_action__(ctx, ofp_port, false);
+        } else if (!(ofport->up.pp.config & OFPUTIL_PC_NO_FLOOD)) {
+            compose_output_action(ctx, ofp_port);
+        }
+    }
+
+    ctx->xout->nf_output_iface = NF_OUT_FLOOD;
+}
+
+static void
+execute_controller_action(struct xlate_ctx *ctx, int len,
+                          enum ofp_packet_in_reason reason,
+                          uint16_t controller_id)
+{
+    struct ofputil_packet_in pin;
+    struct ofpbuf *packet;
+    struct flow key;
+
+    ovs_assert(!ctx->xout->slow || ctx->xout->slow == SLOW_CONTROLLER);
+    ctx->xout->slow = SLOW_CONTROLLER;
+    if (!ctx->xin->packet) {
+        return;
+    }
+
+    packet = ofpbuf_clone(ctx->xin->packet);
+
+    key.skb_priority = 0;
+    key.skb_mark = 0;
+    memset(&key.tunnel, 0, sizeof key.tunnel);
+
+    commit_odp_actions(&ctx->xin->flow, &ctx->base_flow,
+                       &ctx->xout->odp_actions, &ctx->xout->wc);
+
+    odp_execute_actions(NULL, packet, &key, ctx->xout->odp_actions.data,
+                        ctx->xout->odp_actions.size, NULL, NULL);
+
+    pin.packet = packet->data;
+    pin.packet_len = packet->size;
+    pin.reason = reason;
+    pin.controller_id = controller_id;
+    pin.table_id = ctx->table_id;
+    pin.cookie = ctx->rule ? ctx->rule->up.flow_cookie : 0;
+
+    pin.send_len = len;
+    flow_get_metadata(&ctx->xin->flow, &pin.fmd);
+
+    connmgr_send_packet_in(ctx->ofproto->up.connmgr, &pin);
+    ofpbuf_delete(packet);
+}
+
+static void
+compose_mpls_push_action(struct xlate_ctx *ctx, ovs_be16 eth_type)
+{
+    struct flow_wildcards *wc = &ctx->xout->wc;
+    struct flow *flow = &ctx->xin->flow;
+
+    ovs_assert(eth_type_mpls(eth_type));
+
+    memset(&wc->masks.mpls_lse, 0xff, sizeof wc->masks.mpls_lse);
+    memset(&wc->masks.mpls_depth, 0xff, sizeof wc->masks.mpls_depth);
+
+    if (flow->mpls_depth) {
+        flow->mpls_lse &= ~htonl(MPLS_BOS_MASK);
+        flow->mpls_depth++;
+    } else {
+        ovs_be32 label;
+        uint8_t tc, ttl;
+
+        if (flow->dl_type == htons(ETH_TYPE_IPV6)) {
+            label = htonl(0x2); /* IPV6 Explicit Null. */
+        } else {
+            label = htonl(0x0); /* IPV4 Explicit Null. */
+        }
+        wc->masks.nw_tos |= IP_DSCP_MASK;
+        wc->masks.nw_ttl = 0xff;
+        tc = (flow->nw_tos & IP_DSCP_MASK) >> 2;
+        ttl = flow->nw_ttl ? flow->nw_ttl : 0x40;
+        flow->mpls_lse = set_mpls_lse_values(ttl, tc, 1, label);
+        flow->mpls_depth = 1;
+    }
+    flow->dl_type = eth_type;
+}
+
+static void
+compose_mpls_pop_action(struct xlate_ctx *ctx, ovs_be16 eth_type)
+{
+    struct flow_wildcards *wc = &ctx->xout->wc;
+    struct flow *flow = &ctx->xin->flow;
+
+    ovs_assert(eth_type_mpls(ctx->xin->flow.dl_type));
+    ovs_assert(!eth_type_mpls(eth_type));
+
+    memset(&wc->masks.mpls_lse, 0xff, sizeof wc->masks.mpls_lse);
+    memset(&wc->masks.mpls_depth, 0xff, sizeof wc->masks.mpls_depth);
+
+    if (flow->mpls_depth) {
+        flow->mpls_depth--;
+        flow->mpls_lse = htonl(0);
+        if (!flow->mpls_depth) {
+            flow->dl_type = eth_type;
+        }
+    }
+}
+
+static bool
+compose_dec_ttl(struct xlate_ctx *ctx, struct ofpact_cnt_ids *ids)
+{
+    struct flow *flow = &ctx->xin->flow;
+
+    if (!is_ip_any(flow)) {
+        return false;
+    }
+
+    ctx->xout->wc.masks.nw_ttl = 0xff;
+    if (flow->nw_ttl > 1) {
+        flow->nw_ttl--;
+        return false;
+    } else {
+        size_t i;
+
+        for (i = 0; i < ids->n_controllers; i++) {
+            execute_controller_action(ctx, UINT16_MAX, OFPR_INVALID_TTL,
+                                      ids->cnt_ids[i]);
+        }
+
+        /* Stop processing for current table. */
+        return true;
+    }
+}
+
+static bool
+compose_set_mpls_ttl_action(struct xlate_ctx *ctx, uint8_t ttl)
+{
+    if (!eth_type_mpls(ctx->xin->flow.dl_type)) {
+        return true;
+    }
+
+    set_mpls_lse_ttl(&ctx->xin->flow.mpls_lse, ttl);
+    return false;
+}
+
+static bool
+compose_dec_mpls_ttl_action(struct xlate_ctx *ctx)
+{
+    struct flow *flow = &ctx->xin->flow;
+    uint8_t ttl = mpls_lse_to_ttl(flow->mpls_lse);
+    struct flow_wildcards *wc = &ctx->xout->wc;
+
+    memset(&wc->masks.mpls_lse, 0xff, sizeof wc->masks.mpls_lse);
+
+    if (!eth_type_mpls(flow->dl_type)) {
+        return false;
+    }
+
+    if (ttl > 1) {
+        ttl--;
+        set_mpls_lse_ttl(&flow->mpls_lse, ttl);
+        return false;
+    } else {
+        execute_controller_action(ctx, UINT16_MAX, OFPR_INVALID_TTL, 0);
+
+        /* Stop processing for current table. */
+        return true;
+    }
+}
+
+static void
+xlate_output_action(struct xlate_ctx *ctx,
+                    ofp_port_t port, uint16_t max_len, bool may_packet_in)
+{
+    ofp_port_t prev_nf_output_iface = ctx->xout->nf_output_iface;
+
+    ctx->xout->nf_output_iface = NF_OUT_DROP;
+
+    switch (port) {
+    case OFPP_IN_PORT:
+        compose_output_action(ctx, ctx->xin->flow.in_port.ofp_port);
+        break;
+    case OFPP_TABLE:
+        xlate_table_action(ctx, ctx->xin->flow.in_port.ofp_port,
+                           0, may_packet_in);
+        break;
+    case OFPP_NORMAL:
+        xlate_normal(ctx);
+        break;
+    case OFPP_FLOOD:
+        flood_packets(ctx,  false);
+        break;
+    case OFPP_ALL:
+        flood_packets(ctx, true);
+        break;
+    case OFPP_CONTROLLER:
+        execute_controller_action(ctx, max_len, OFPR_ACTION, 0);
+        break;
+    case OFPP_NONE:
+        break;
+    case OFPP_LOCAL:
+    default:
+        if (port != ctx->xin->flow.in_port.ofp_port) {
+            compose_output_action(ctx, port);
+        } else {
+            xlate_report(ctx, "skipping output to input port");
+        }
+        break;
+    }
+
+    if (prev_nf_output_iface == NF_OUT_FLOOD) {
+        ctx->xout->nf_output_iface = NF_OUT_FLOOD;
+    } else if (ctx->xout->nf_output_iface == NF_OUT_DROP) {
+        ctx->xout->nf_output_iface = prev_nf_output_iface;
+    } else if (prev_nf_output_iface != NF_OUT_DROP &&
+               ctx->xout->nf_output_iface != NF_OUT_FLOOD) {
+        ctx->xout->nf_output_iface = NF_OUT_MULTI;
+    }
+}
+
+static void
+xlate_output_reg_action(struct xlate_ctx *ctx,
+                        const struct ofpact_output_reg *or)
+{
+    uint64_t port = mf_get_subfield(&or->src, &ctx->xin->flow);
+    if (port <= UINT16_MAX) {
+        union mf_subvalue value;
+
+        memset(&value, 0xff, sizeof value);
+        mf_write_subfield_flow(&or->src, &value, &ctx->xout->wc.masks);
+        xlate_output_action(ctx, u16_to_ofp(port),
+                            or->max_len, false);
+    }
+}
+
+static void
+xlate_enqueue_action(struct xlate_ctx *ctx,
+                     const struct ofpact_enqueue *enqueue)
+{
+    ofp_port_t ofp_port = enqueue->port;
+    uint32_t queue_id = enqueue->queue;
+    uint32_t flow_priority, priority;
+    int error;
+
+    /* Translate queue to priority. */
+    error = ofproto_dpif_queue_to_priority(ctx->ofproto, queue_id, &priority);
+    if (error) {
+        /* Fall back to ordinary output action. */
+        xlate_output_action(ctx, enqueue->port, 0, false);
+        return;
+    }
+
+    /* Check output port. */
+    if (ofp_port == OFPP_IN_PORT) {
+        ofp_port = ctx->xin->flow.in_port.ofp_port;
+    } else if (ofp_port == ctx->xin->flow.in_port.ofp_port) {
+        return;
+    }
+
+    /* Add datapath actions. */
+    flow_priority = ctx->xin->flow.skb_priority;
+    ctx->xin->flow.skb_priority = priority;
+    compose_output_action(ctx, ofp_port);
+    ctx->xin->flow.skb_priority = flow_priority;
+
+    /* Update NetFlow output port. */
+    if (ctx->xout->nf_output_iface == NF_OUT_DROP) {
+        ctx->xout->nf_output_iface = ofp_port;
+    } else if (ctx->xout->nf_output_iface != NF_OUT_FLOOD) {
+        ctx->xout->nf_output_iface = NF_OUT_MULTI;
+    }
+}
+
+static void
+xlate_set_queue_action(struct xlate_ctx *ctx, uint32_t queue_id)
+{
+    uint32_t skb_priority;
+
+    if (!ofproto_dpif_queue_to_priority(ctx->ofproto, queue_id,
+                                        &skb_priority)) {
+        ctx->xin->flow.skb_priority = skb_priority;
+    } else {
+        /* Couldn't translate queue to a priority.  Nothing to do.  A warning
+         * has already been logged. */
+    }
+}
+
+static bool
+slave_enabled_cb(ofp_port_t ofp_port, void *ofproto_)
+{
+    struct ofproto_dpif *ofproto = ofproto_;
+    struct ofport_dpif *port;
+
+    switch (ofp_port) {
+    case OFPP_IN_PORT:
+    case OFPP_TABLE:
+    case OFPP_NORMAL:
+    case OFPP_FLOOD:
+    case OFPP_ALL:
+    case OFPP_NONE:
+        return true;
+    case OFPP_CONTROLLER: /* Not supported by the bundle action. */
+        return false;
+    default:
+        port = get_ofp_port(ofproto, ofp_port);
+        return port ? port->may_enable : false;
+    }
+}
+
+static void
+xlate_bundle_action(struct xlate_ctx *ctx,
+                    const struct ofpact_bundle *bundle)
+{
+    ofp_port_t port;
+
+    port = bundle_execute(bundle, &ctx->xin->flow, &ctx->xout->wc,
+                          slave_enabled_cb, ctx->ofproto);
+    if (bundle->dst.field) {
+        nxm_reg_load(&bundle->dst, ofp_to_u16(port), &ctx->xin->flow);
+    } else {
+        xlate_output_action(ctx, port, 0, false);
+    }
+}
+
+static void
+xlate_learn_action(struct xlate_ctx *ctx,
+                   const struct ofpact_learn *learn)
+{
+    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
+    struct ofputil_flow_mod fm;
+    uint64_t ofpacts_stub[1024 / 8];
+    struct ofpbuf ofpacts;
+    int error;
+
+    ctx->xout->has_learn = true;
+
+    learn_mask(learn, &ctx->xout->wc);
+
+    if (!ctx->xin->may_learn) {
+        return;
+    }
+
+    ofpbuf_use_stack(&ofpacts, ofpacts_stub, sizeof ofpacts_stub);
+    learn_execute(learn, &ctx->xin->flow, &fm, &ofpacts);
+
+    error = ofproto_flow_mod(&ctx->ofproto->up, &fm);
+    if (error && !VLOG_DROP_WARN(&rl)) {
+        VLOG_WARN("learning action failed to modify flow table (%s)",
+                  ofperr_get_name(error));
+    }
+
+    ofpbuf_uninit(&ofpacts);
+}
+
+/* Reduces '*timeout' to no more than 'max'.  A value of zero in either case
+ * means "infinite". */
+static void
+reduce_timeout(uint16_t max, uint16_t *timeout)
+{
+    if (max && (!*timeout || *timeout > max)) {
+        *timeout = max;
+    }
+}
+
+static void
+xlate_fin_timeout(struct xlate_ctx *ctx,
+                  const struct ofpact_fin_timeout *oft)
+{
+    if (ctx->xin->tcp_flags & (TCP_FIN | TCP_RST) && ctx->rule) {
+        struct rule_dpif *rule = ctx->rule;
+
+        reduce_timeout(oft->fin_idle_timeout, &rule->up.idle_timeout);
+        reduce_timeout(oft->fin_hard_timeout, &rule->up.hard_timeout);
+    }
+}
+
+static void
+xlate_sample_action(struct xlate_ctx *ctx,
+                    const struct ofpact_sample *os)
+{
+  union user_action_cookie cookie;
+  /* Scale the probability from 16-bit to 32-bit while representing
+   * the same percentage. */
+  uint32_t probability = (os->probability << 16) | os->probability;
+
+  commit_odp_actions(&ctx->xin->flow, &ctx->base_flow,
+                     &ctx->xout->odp_actions, &ctx->xout->wc);
+
+  compose_flow_sample_cookie(os->probability, os->collector_set_id,
+                             os->obs_domain_id, os->obs_point_id, &cookie);
+  compose_sample_action(ctx->ofproto, &ctx->xout->odp_actions, &ctx->xin->flow,
+                        probability, &cookie, sizeof cookie.flow_sample);
+}
+
+static bool
+may_receive(const struct ofport_dpif *port, struct xlate_ctx *ctx)
+{
+    if (port->up.pp.config & (eth_addr_equals(ctx->xin->flow.dl_dst,
+                                              eth_addr_stp)
+                              ? OFPUTIL_PC_NO_RECV_STP
+                              : OFPUTIL_PC_NO_RECV)) {
+        return false;
+    }
+
+    /* Only drop packets here if both forwarding and learning are
+     * disabled.  If just learning is enabled, we need to have
+     * OFPP_NORMAL and the learning action have a look at the packet
+     * before we can drop it. */
+    if (!stp_forward_in_state(port->stp_state)
+            && !stp_learn_in_state(port->stp_state)) {
+        return false;
+    }
+
+    return true;
+}
+
+static bool
+tunnel_ecn_ok(struct xlate_ctx *ctx)
+{
+    if (is_ip_any(&ctx->base_flow)
+        && (ctx->xin->flow.tunnel.ip_tos & IP_ECN_MASK) == IP_ECN_CE) {
+        if ((ctx->base_flow.nw_tos & IP_ECN_MASK) == IP_ECN_NOT_ECT) {
+            VLOG_WARN_RL(&rl, "dropping tunnel packet marked ECN CE"
+                         " but is not ECN capable");
+            return false;
+        } else {
+            /* Set the ECN CE value in the tunneled packet. */
+            ctx->xin->flow.nw_tos |= IP_ECN_CE;
+        }
+    }
+
+    return true;
+}
+
+static void
+do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
+                 struct xlate_ctx *ctx)
+{
+    struct flow_wildcards *wc = &ctx->xout->wc;
+    struct flow *flow = &ctx->xin->flow;
+    bool was_evictable = true;
+    const struct ofpact *a;
+
+    if (ctx->rule) {
+        /* Don't let the rule we're working on get evicted underneath us. */
+        was_evictable = ctx->rule->up.evictable;
+        ctx->rule->up.evictable = false;
+    }
+
+ do_xlate_actions_again:
+    OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
+        struct ofpact_controller *controller;
+        const struct ofpact_metadata *metadata;
+
+        if (ctx->exit) {
+            break;
+        }
+
+        switch (a->type) {
+        case OFPACT_OUTPUT:
+            xlate_output_action(ctx, ofpact_get_OUTPUT(a)->port,
+                                ofpact_get_OUTPUT(a)->max_len, true);
+            break;
+
+        case OFPACT_CONTROLLER:
+            controller = ofpact_get_CONTROLLER(a);
+            execute_controller_action(ctx, controller->max_len,
+                                      controller->reason,
+                                      controller->controller_id);
+            break;
+
+        case OFPACT_ENQUEUE:
+            xlate_enqueue_action(ctx, ofpact_get_ENQUEUE(a));
+            break;
+
+        case OFPACT_SET_VLAN_VID:
+            flow->vlan_tci &= ~htons(VLAN_VID_MASK);
+            flow->vlan_tci |= (htons(ofpact_get_SET_VLAN_VID(a)->vlan_vid)
+                               | htons(VLAN_CFI));
+            break;
+
+        case OFPACT_SET_VLAN_PCP:
+            flow->vlan_tci &= ~htons(VLAN_PCP_MASK);
+            flow->vlan_tci |=
+                htons((ofpact_get_SET_VLAN_PCP(a)->vlan_pcp << VLAN_PCP_SHIFT)
+                      | VLAN_CFI);
+            break;
+
+        case OFPACT_STRIP_VLAN:
+            flow->vlan_tci = htons(0);
+            break;
+
+        case OFPACT_PUSH_VLAN:
+            /* XXX 802.1AD(QinQ) */
+            flow->vlan_tci = htons(VLAN_CFI);
+            break;
+
+        case OFPACT_SET_ETH_SRC:
+            memcpy(flow->dl_src, ofpact_get_SET_ETH_SRC(a)->mac, ETH_ADDR_LEN);
+            break;
+
+        case OFPACT_SET_ETH_DST:
+            memcpy(flow->dl_dst, ofpact_get_SET_ETH_DST(a)->mac, ETH_ADDR_LEN);
+            break;
+
+        case OFPACT_SET_IPV4_SRC:
+            if (flow->dl_type == htons(ETH_TYPE_IP)) {
+                flow->nw_src = ofpact_get_SET_IPV4_SRC(a)->ipv4;
+            }
+            break;
+
+        case OFPACT_SET_IPV4_DST:
+            if (flow->dl_type == htons(ETH_TYPE_IP)) {
+                flow->nw_dst = ofpact_get_SET_IPV4_DST(a)->ipv4;
+            }
+            break;
+
+        case OFPACT_SET_IPV4_DSCP:
+            /* OpenFlow 1.0 only supports IPv4. */
+            if (flow->dl_type == htons(ETH_TYPE_IP)) {
+                flow->nw_tos &= ~IP_DSCP_MASK;
+                flow->nw_tos |= ofpact_get_SET_IPV4_DSCP(a)->dscp;
+            }
+            break;
+
+        case OFPACT_SET_L4_SRC_PORT:
+            memset(&wc->masks.nw_proto, 0xff, sizeof wc->masks.nw_proto);
+            if (is_ip_any(flow)) {
+                flow->tp_src = htons(ofpact_get_SET_L4_SRC_PORT(a)->port);
+            }
+            break;
+
+        case OFPACT_SET_L4_DST_PORT:
+            memset(&wc->masks.nw_proto, 0xff, sizeof wc->masks.nw_proto);
+            if (is_ip_any(flow)) {
+                flow->tp_dst = htons(ofpact_get_SET_L4_DST_PORT(a)->port);
+            }
+            break;
+
+        case OFPACT_RESUBMIT:
+            xlate_ofpact_resubmit(ctx, ofpact_get_RESUBMIT(a));
+            break;
+
+        case OFPACT_SET_TUNNEL:
+            flow->tunnel.tun_id = htonll(ofpact_get_SET_TUNNEL(a)->tun_id);
+            break;
+
+        case OFPACT_SET_QUEUE:
+            xlate_set_queue_action(ctx, ofpact_get_SET_QUEUE(a)->queue_id);
+            break;
+
+        case OFPACT_POP_QUEUE:
+            flow->skb_priority = ctx->orig_skb_priority;
+            break;
+
+        case OFPACT_REG_MOVE:
+            nxm_execute_reg_move(ofpact_get_REG_MOVE(a), flow, wc);
+            break;
+
+        case OFPACT_REG_LOAD:
+            nxm_execute_reg_load(ofpact_get_REG_LOAD(a), flow);
+            break;
+
+        case OFPACT_STACK_PUSH:
+            nxm_execute_stack_push(ofpact_get_STACK_PUSH(a), flow, wc,
+                                   &ctx->stack);
+            break;
+
+        case OFPACT_STACK_POP:
+            nxm_execute_stack_pop(ofpact_get_STACK_POP(a), flow, &ctx->stack);
+            break;
+
+        case OFPACT_PUSH_MPLS:
+            compose_mpls_push_action(ctx, ofpact_get_PUSH_MPLS(a)->ethertype);
+            break;
+
+        case OFPACT_POP_MPLS:
+            compose_mpls_pop_action(ctx, ofpact_get_POP_MPLS(a)->ethertype);
+            break;
+
+        case OFPACT_SET_MPLS_TTL:
+            if (compose_set_mpls_ttl_action(ctx,
+                                            ofpact_get_SET_MPLS_TTL(a)->ttl)) {
+                goto out;
+            }
+            break;
+
+        case OFPACT_DEC_MPLS_TTL:
+            if (compose_dec_mpls_ttl_action(ctx)) {
+                goto out;
+            }
+            break;
+
+        case OFPACT_DEC_TTL:
+            if (compose_dec_ttl(ctx, ofpact_get_DEC_TTL(a))) {
+                goto out;
+            }
+            break;
+
+        case OFPACT_NOTE:
+            /* Nothing to do. */
+            break;
+
+        case OFPACT_MULTIPATH:
+            multipath_execute(ofpact_get_MULTIPATH(a), flow, wc);
+            break;
+
+        case OFPACT_BUNDLE:
+            xlate_bundle_action(ctx, ofpact_get_BUNDLE(a));
+            break;
+
+        case OFPACT_OUTPUT_REG:
+            xlate_output_reg_action(ctx, ofpact_get_OUTPUT_REG(a));
+            break;
+
+        case OFPACT_LEARN:
+            xlate_learn_action(ctx, ofpact_get_LEARN(a));
+            break;
+
+        case OFPACT_EXIT:
+            ctx->exit = true;
+            break;
+
+        case OFPACT_FIN_TIMEOUT:
+            memset(&wc->masks.nw_proto, 0xff, sizeof wc->masks.nw_proto);
+            ctx->xout->has_fin_timeout = true;
+            xlate_fin_timeout(ctx, ofpact_get_FIN_TIMEOUT(a));
+            break;
+
+        case OFPACT_CLEAR_ACTIONS:
+            /* 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.
+             */
+            break;
+
+        case OFPACT_WRITE_METADATA:
+            metadata = ofpact_get_WRITE_METADATA(a);
+            flow->metadata &= ~metadata->mask;
+            flow->metadata |= metadata->metadata & metadata->mask;
+            break;
+
+        case OFPACT_METER:
+            /* Not implemented yet. */
+            break;
+
+        case OFPACT_GOTO_TABLE: {
+            /* It is assumed that goto-table is the last action. */
+            struct ofpact_goto_table *ogt = ofpact_get_GOTO_TABLE(a);
+            struct rule_dpif *rule;
+
+            ovs_assert(ctx->table_id < ogt->table_id);
+
+            ctx->table_id = ogt->table_id;
+
+            /* Look up a flow from the new table. */
+            rule = rule_dpif_lookup_in_table(ctx->ofproto, flow, wc,
+                                             ctx->table_id);
+
+            ctx->xout->tags = calculate_flow_tag(ctx->ofproto, &ctx->xin->flow,
+                                                 ctx->table_id, rule);
+
+            rule = ctx_rule_hooks(ctx, rule, true);
+
+            if (rule) {
+                if (ctx->rule) {
+                    ctx->rule->up.evictable = was_evictable;
+                }
+                ctx->rule = rule;
+                was_evictable = rule->up.evictable;
+                rule->up.evictable = false;
+
+                /* Tail recursion removal. */
+                ofpacts = rule->up.ofpacts;
+                ofpacts_len = rule->up.ofpacts_len;
+                goto do_xlate_actions_again;
+            }
+            break;
+        }
+
+        case OFPACT_SAMPLE:
+            xlate_sample_action(ctx, ofpact_get_SAMPLE(a));
+            break;
+        }
+    }
+
+out:
+    if (ctx->rule) {
+        ctx->rule->up.evictable = was_evictable;
+    }
+}
+
+void
+xlate_in_init(struct xlate_in *xin, struct ofproto_dpif *ofproto,
+              const struct flow *flow, struct rule_dpif *rule,
+              uint8_t tcp_flags, const struct ofpbuf *packet)
+{
+    xin->ofproto = ofproto;
+    xin->flow = *flow;
+    xin->packet = packet;
+    xin->may_learn = packet != NULL;
+    xin->rule = rule;
+    xin->ofpacts = NULL;
+    xin->ofpacts_len = 0;
+    xin->tcp_flags = tcp_flags;
+    xin->resubmit_hook = NULL;
+    xin->report_hook = NULL;
+    xin->resubmit_stats = NULL;
+}
+
+void
+xlate_out_uninit(struct xlate_out *xout)
+{
+    if (xout) {
+        ofpbuf_uninit(&xout->odp_actions);
+    }
+}
+
+/* Translates the 'ofpacts_len' bytes of "struct ofpact"s starting at 'ofpacts'
+ * into datapath actions, using 'ctx', and discards the datapath actions. */
+void
+xlate_actions_for_side_effects(struct xlate_in *xin)
+{
+    struct xlate_out xout;
+
+    xlate_actions(xin, &xout);
+    xlate_out_uninit(&xout);
+}
+
+static void
+xlate_report(struct xlate_ctx *ctx, const char *s)
+{
+    if (ctx->xin->report_hook) {
+        ctx->xin->report_hook(ctx->xin, s, ctx->recurse);
+    }
+}
+
+void
+xlate_out_copy(struct xlate_out *dst, const struct xlate_out *src)
+{
+    dst->wc = src->wc;
+    dst->tags = src->tags;
+    dst->slow = src->slow;
+    dst->has_learn = src->has_learn;
+    dst->has_normal = src->has_normal;
+    dst->has_fin_timeout = src->has_fin_timeout;
+    dst->nf_output_iface = src->nf_output_iface;
+    dst->mirrors = src->mirrors;
+
+    ofpbuf_use_stub(&dst->odp_actions, dst->odp_actions_stub,
+                    sizeof dst->odp_actions_stub);
+    ofpbuf_put(&dst->odp_actions, src->odp_actions.data,
+               src->odp_actions.size);
+}
+\f
+static bool
+actions_output_to_local_port(const struct xlate_ctx *ctx)
+{
+    odp_port_t local_odp_port = ofp_port_to_odp_port(ctx->ofproto, OFPP_LOCAL);
+    const struct nlattr *a;
+    unsigned int left;
+
+    NL_ATTR_FOR_EACH_UNSAFE (a, left, ctx->xout->odp_actions.data,
+                             ctx->xout->odp_actions.size) {
+        if (nl_attr_type(a) == OVS_ACTION_ATTR_OUTPUT
+            && nl_attr_get_odp_port(a) == local_odp_port) {
+            return true;
+        }
+    }
+    return false;
+}
+
+/* Translates the 'ofpacts_len' bytes of "struct ofpacts" starting at 'ofpacts'
+ * into datapath actions in 'odp_actions', using 'ctx'. */
+void
+xlate_actions(struct xlate_in *xin, struct xlate_out *xout)
+{
+    /* Normally false.  Set to true if we ever hit MAX_RESUBMIT_RECURSION, so
+     * that in the future we always keep a copy of the original flow for
+     * tracing purposes. */
+    static bool hit_resubmit_limit;
+
+    struct flow_wildcards *wc = &xout->wc;
+    struct flow *flow = &xin->flow;
+
+    enum slow_path_reason special;
+    const struct ofpact *ofpacts;
+    struct ofport_dpif *in_port;
+    struct flow orig_flow;
+    struct xlate_ctx ctx;
+    size_t ofpacts_len;
+
+    COVERAGE_INC(ofproto_dpif_xlate);
+
+    /* 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 metadata as received is retained in 'flow'. This allows
+     *   tunnel metadata matching also in later tables.
+     *   Since a kernel action for setting the tunnel metadata will only be
+     *   generated with actual tunnel output, changing the tunnel metadata
+     *   values in 'flow' (such as tun_id) will only have effect with a later
+     *   tunnel output action.
+     * - 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.xin = xin;
+    ctx.xout = xout;
+
+    ctx.ofproto = xin->ofproto;
+    ctx.rule = xin->rule;
+
+    ctx.base_flow = *flow;
+    memset(&ctx.base_flow.tunnel, 0, sizeof ctx.base_flow.tunnel);
+    ctx.orig_tunnel_ip_dst = flow->tunnel.ip_dst;
+
+    flow_wildcards_init_catchall(wc);
+    memset(&wc->masks.in_port, 0xff, sizeof wc->masks.in_port);
+    memset(&wc->masks.skb_priority, 0xff, sizeof wc->masks.skb_priority);
+    memset(&wc->masks.dl_type, 0xff, sizeof wc->masks.dl_type);
+    wc->masks.nw_frag |= FLOW_NW_FRAG_MASK;
+
+    if (tnl_port_should_receive(&ctx.xin->flow)) {
+        memset(&wc->masks.tunnel, 0xff, sizeof wc->masks.tunnel);
+    }
+    if (xin->ofproto->netflow) {
+        netflow_mask_wc(flow, wc);
+    }
+
+    ctx.xout->tags = 0;
+    ctx.xout->slow = 0;
+    ctx.xout->has_learn = false;
+    ctx.xout->has_normal = false;
+    ctx.xout->has_fin_timeout = false;
+    ctx.xout->nf_output_iface = NF_OUT_DROP;
+    ctx.xout->mirrors = 0;
+
+    ofpbuf_use_stub(&ctx.xout->odp_actions, ctx.xout->odp_actions_stub,
+                    sizeof ctx.xout->odp_actions_stub);
+    ofpbuf_reserve(&ctx.xout->odp_actions, NL_A_U32_SIZE);
+
+    ctx.recurse = 0;
+    ctx.max_resubmit_trigger = false;
+    ctx.orig_skb_priority = flow->skb_priority;
+    ctx.table_id = 0;
+    ctx.exit = false;
+
+    if (xin->ofpacts) {
+        ofpacts = xin->ofpacts;
+        ofpacts_len = xin->ofpacts_len;
+    } else if (xin->rule) {
+        ofpacts = xin->rule->up.ofpacts;
+        ofpacts_len = xin->rule->up.ofpacts_len;
+    } else {
+        NOT_REACHED();
+    }
+
+    ofpbuf_use_stub(&ctx.stack, ctx.init_stack, sizeof ctx.init_stack);
+
+    if (ctx.ofproto->has_mirrors || hit_resubmit_limit) {
+        /* Do this conditionally because the copy is expensive enough that it
+         * shows up in profiles. */
+        orig_flow = *flow;
+    }
+
+    if (flow->nw_frag & FLOW_NW_FRAG_ANY) {
+        switch (ctx.ofproto->up.frag_handling) {
+        case OFPC_FRAG_NORMAL:
+            /* We must pretend that transport ports are unavailable. */
+            flow->tp_src = ctx.base_flow.tp_src = htons(0);
+            flow->tp_dst = ctx.base_flow.tp_dst = htons(0);
+            break;
+
+        case OFPC_FRAG_DROP:
+            return;
+
+        case OFPC_FRAG_REASM:
+            NOT_REACHED();
+
+        case OFPC_FRAG_NX_MATCH:
+            /* Nothing to do. */
+            break;
+
+        case OFPC_INVALID_TTL_TO_CONTROLLER:
+            NOT_REACHED();
+        }
+    }
+
+    in_port = get_ofp_port(ctx.ofproto, flow->in_port.ofp_port);
+    special = process_special(&ctx, flow, in_port, ctx.xin->packet);
+    if (special) {
+        ctx.xout->slow = special;
+    } else {
+        static struct vlog_rate_limit trace_rl = VLOG_RATE_LIMIT_INIT(1, 1);
+        size_t sample_actions_len;
+
+        if (flow->in_port.ofp_port
+            != vsp_realdev_to_vlandev(ctx.ofproto, flow->in_port.ofp_port,
+                                      flow->vlan_tci)) {
+            ctx.base_flow.vlan_tci = 0;
+        }
+
+        add_sflow_action(&ctx);
+        add_ipfix_action(&ctx);
+        sample_actions_len = ctx.xout->odp_actions.size;
+
+        if (tunnel_ecn_ok(&ctx) && (!in_port || may_receive(in_port, &ctx))) {
+            do_xlate_actions(ofpacts, ofpacts_len, &ctx);
+
+            /* We've let OFPP_NORMAL and the learning action look at the
+             * packet, so drop it now if forwarding is disabled. */
+            if (in_port && !stp_forward_in_state(in_port->stp_state)) {
+                ctx.xout->odp_actions.size = sample_actions_len;
+            }
+        }
+
+        if (ctx.max_resubmit_trigger && !ctx.xin->resubmit_hook) {
+            if (!hit_resubmit_limit) {
+                /* We didn't record the original flow.  Make sure we do from
+                 * now on. */
+                hit_resubmit_limit = true;
+            } else if (!VLOG_DROP_ERR(&trace_rl)) {
+                struct ds ds = DS_EMPTY_INITIALIZER;
+
+                ofproto_trace(ctx.ofproto, &orig_flow, ctx.xin->packet, &ds);
+                VLOG_ERR("Trace triggered by excessive resubmit "
+                         "recursion:\n%s", ds_cstr(&ds));
+                ds_destroy(&ds);
+            }
+        }
+
+        if (connmgr_has_in_band(ctx.ofproto->up.connmgr)
+            && in_band_must_output_to_local_port(flow)
+            && !actions_output_to_local_port(&ctx)) {
+            compose_output_action(&ctx, OFPP_LOCAL);
+        }
+        if (ctx.ofproto->has_mirrors) {
+            add_mirror_actions(&ctx, &orig_flow);
+        }
+        fix_sflow_action(&ctx);
+    }
+
+    ofpbuf_uninit(&ctx.stack);
+
+    /* Clear the metadata and register wildcard masks, because we won't
+     * use non-header fields as part of the cache. */
+    memset(&wc->masks.metadata, 0, sizeof wc->masks.metadata);
+    memset(&wc->masks.regs, 0, sizeof wc->masks.regs);
+}
diff --git a/ofproto/ofproto-dpif-xlate.h b/ofproto/ofproto-dpif-xlate.h
new file mode 100644 (file)
index 0000000..f5f0cd0
--- /dev/null
@@ -0,0 +1,114 @@
+/* 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.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License. */
+
+#ifndef OFPROT_DPIF_XLATE_H
+#define OFPROT_DPIF_XLATE_H 1
+
+#include "flow.h"
+#include "meta-flow.h"
+#include "odp-util.h"
+#include "ofpbuf.h"
+#include "ofproto-dpif.h"
+#include "tag.h"
+
+struct xlate_out {
+    /* Wildcards relevant in translation.  Any fields that were used to
+     * calculate the action must be set for caching and kernel
+     * wildcarding to work.  For example, if the flow lookup involved
+     * performing the "normal" action on IPv4 and ARP packets, 'wc'
+     * would have the 'in_port' (always set), 'dl_type' (flow match),
+     * 'vlan_tci' (normal action), and 'dl_dst' (normal action) fields
+     * set. */
+    struct flow_wildcards wc;
+
+    tag_type tags;              /* Tags associated with actions. */
+    enum slow_path_reason slow; /* 0 if fast path may be used. */
+    bool has_learn;             /* Actions include NXAST_LEARN? */
+    bool has_normal;            /* Actions output to OFPP_NORMAL? */
+    bool has_fin_timeout;       /* Actions include NXAST_FIN_TIMEOUT? */
+    ofp_port_t nf_output_iface; /* Output interface index for NetFlow. */
+    mirror_mask_t mirrors;      /* Bitmap of associated mirrors. */
+
+    uint64_t odp_actions_stub[256 / 8];
+    struct ofpbuf odp_actions;
+};
+
+struct xlate_in {
+    struct ofproto_dpif *ofproto;
+
+    /* Flow to which the OpenFlow actions apply.  xlate_actions() will modify
+     * this flow when actions change header fields. */
+    struct flow flow;
+
+    /* The packet corresponding to 'flow', or a null pointer if we are
+     * revalidating without a packet to refer to. */
+    const struct ofpbuf *packet;
+
+    /* Should OFPP_NORMAL update the MAC learning table?  Should "learn"
+     * actions update the flow table?
+     *
+     * We want to update these tables if we are actually processing a packet,
+     * or if we are accounting for packets that the datapath has processed, but
+     * not if we are just revalidating. */
+    bool may_learn;
+
+    /* The rule initiating translation or NULL. */
+    struct rule_dpif *rule;
+
+    /* The actions to translate.  If 'rule' is not NULL, these may be NULL. */
+    const struct ofpact *ofpacts;
+    size_t ofpacts_len;
+
+    /* Union of the set of TCP flags seen so far in this flow.  (Used only by
+     * NXAST_FIN_TIMEOUT.  Set to zero to avoid updating updating rules'
+     * timeouts.) */
+    uint8_t tcp_flags;
+
+    /* If nonnull, flow translation calls this function just before executing a
+     * resubmit or OFPP_TABLE action.  In addition, disables logging of traces
+     * when the recursion depth is exceeded.
+     *
+     * 'rule' is the rule being submitted into.  It will be null if the
+     * resubmit or OFPP_TABLE action didn't find a matching rule.
+     *
+     * 'recurse' is the resubmit recursion depth at time of invocation.
+     *
+     * This is normally null so the client has to set it manually after
+     * calling xlate_in_init(). */
+    void (*resubmit_hook)(struct xlate_in *, struct rule_dpif *rule,
+                          int recurse);
+
+    /* If nonnull, flow translation calls this function to report some
+     * significant decision, e.g. to explain why OFPP_NORMAL translation
+     * dropped a packet.  'recurse' is the resubmit recursion depth at time of
+     * invocation. */
+    void (*report_hook)(struct xlate_in *, const char *s, int recurse);
+
+    /* If nonnull, flow translation credits the specified statistics to each
+     * rule reached through a resubmit or OFPP_TABLE action.
+     *
+     * This is normally null so the client has to set it manually after
+     * calling xlate_in_init(). */
+    const struct dpif_flow_stats *resubmit_stats;
+};
+
+void xlate_actions(struct xlate_in *, struct xlate_out *);
+void xlate_in_init(struct xlate_in *, struct ofproto_dpif *,
+                   const struct flow *, struct rule_dpif *,
+                   uint8_t tcp_flags, const struct ofpbuf *packet);
+void xlate_out_uninit(struct xlate_out *);
+void xlate_actions_for_side_effects(struct xlate_in *);
+void xlate_out_copy(struct xlate_out *dst, const struct xlate_out *src);
+
+#endif /* ofproto-dpif-xlate.h */
index 1da8ffb..5ca16b7 100644 (file)
@@ -16,6 +16,7 @@
 
 #include <config.h>
 
+#include "ofproto/ofproto-dpif.h"
 #include "ofproto/ofproto-provider.h"
 
 #include <errno.h>
@@ -41,6 +42,7 @@
 #include "netlink.h"
 #include "nx-match.h"
 #include "odp-util.h"
+#include "odp-execute.h"
 #include "ofp-util.h"
 #include "ofpbuf.h"
 #include "ofp-actions.h"
@@ -49,6 +51,7 @@
 #include "ofproto-dpif-governor.h"
 #include "ofproto-dpif-ipfix.h"
 #include "ofproto-dpif-sflow.h"
+#include "ofproto-dpif-xlate.h"
 #include "poll-loop.h"
 #include "simap.h"
 #include "smap.h"
 VLOG_DEFINE_THIS_MODULE(ofproto_dpif);
 
 COVERAGE_DEFINE(ofproto_dpif_expired);
-COVERAGE_DEFINE(ofproto_dpif_xlate);
 COVERAGE_DEFINE(facet_changed_rule);
 COVERAGE_DEFINE(facet_revalidate);
 COVERAGE_DEFINE(facet_unexpected);
 COVERAGE_DEFINE(facet_suppress);
+COVERAGE_DEFINE(subfacet_install_fail);
 
-/* Maximum depth of flow table recursion (due to resubmit actions) in a
- * flow translation. */
-#define MAX_RESUBMIT_RECURSION 64
-
-/* Number of implemented OpenFlow tables. */
-enum { N_TABLES = 255 };
-enum { TBL_INTERNAL = N_TABLES - 1 };    /* Used for internal hidden rules. */
-BUILD_ASSERT_DECL(N_TABLES >= 2 && N_TABLES <= 255);
-
-struct ofport_dpif;
-struct ofproto_dpif;
 struct flow_miss;
 struct facet;
 
-struct rule_dpif {
-    struct rule up;
-
-    /* These statistics:
-     *
-     *   - Do include packets and bytes from facets that have been deleted or
-     *     whose own statistics have been folded into the rule.
-     *
-     *   - Do include packets and bytes sent "by hand" that were accounted to
-     *     the rule without any facet being involved (this is a rare corner
-     *     case in rule_execute()).
-     *
-     *   - Do not include packet or bytes that can be obtained from any facet's
-     *     packet_count or byte_count member or that can be obtained from the
-     *     datapath by, e.g., dpif_flow_get() for any subfacet.
-     */
-    uint64_t packet_count;       /* Number of packets received. */
-    uint64_t byte_count;         /* Number of bytes received. */
-
-    tag_type tag;                /* Caches rule_calculate_tag() result. */
-
-    struct list facets;          /* List of "struct facet"s. */
-};
-
-static struct rule_dpif *rule_dpif_cast(const struct rule *rule)
-{
-    return rule ? CONTAINER_OF(rule, struct rule_dpif, up) : NULL;
-}
-
 static struct rule_dpif *rule_dpif_lookup(struct ofproto_dpif *,
-                                          const struct flow *);
-static struct rule_dpif *rule_dpif_lookup__(struct ofproto_dpif *,
-                                            const struct flow *,
-                                            uint8_t table);
-static struct rule_dpif *rule_dpif_miss_rule(struct ofproto_dpif *ofproto,
-                                             const struct flow *flow);
-
-static void rule_credit_stats(struct rule_dpif *,
-                              const struct dpif_flow_stats *);
-static void flow_push_stats(struct facet *, const struct dpif_flow_stats *);
-static tag_type rule_calculate_tag(const struct flow *,
-                                   const struct minimask *, uint32_t basis);
-static void rule_invalidate(const struct rule_dpif *);
-
-#define MAX_MIRRORS 32
-typedef uint32_t mirror_mask_t;
-#define MIRROR_MASK_C(X) UINT32_C(X)
-BUILD_ASSERT_DECL(sizeof(mirror_mask_t) * CHAR_BIT >= MAX_MIRRORS);
-struct ofmirror {
-    struct ofproto_dpif *ofproto; /* Owning ofproto. */
-    size_t idx;                 /* In ofproto's "mirrors" array. */
-    void *aux;                  /* Key supplied by ofproto's client. */
-    char *name;                 /* Identifier for log messages. */
-
-    /* Selection criteria. */
-    struct hmapx srcs;          /* Contains "struct ofbundle *"s. */
-    struct hmapx dsts;          /* Contains "struct ofbundle *"s. */
-    unsigned long *vlans;       /* Bitmap of chosen VLANs, NULL selects all. */
-
-    /* Output (exactly one of out == NULL and out_vlan == -1 is true). */
-    struct ofbundle *out;       /* Output port or NULL. */
-    int out_vlan;               /* Output VLAN or -1. */
-    mirror_mask_t dup_mirrors;  /* Bitmap of mirrors with the same output. */
+                                          const struct flow *,
+                                          struct flow_wildcards *wc);
 
-    /* Counters. */
-    int64_t packet_count;       /* Number of packets sent. */
-    int64_t byte_count;         /* Number of bytes sent. */
-};
+static void rule_get_stats(struct rule *, uint64_t *packets, uint64_t *bytes);
+static void rule_invalidate(const struct rule_dpif *);
 
 static void mirror_destroy(struct ofmirror *);
 static void update_mirror_stats(struct ofproto_dpif *ofproto,
                                 mirror_mask_t mirrors,
                                 uint64_t packets, uint64_t bytes);
 
-struct ofbundle {
-    struct hmap_node hmap_node; /* In struct ofproto's "bundles" hmap. */
-    struct ofproto_dpif *ofproto; /* Owning ofproto. */
-    void *aux;                  /* Key supplied by ofproto's client. */
-    char *name;                 /* Identifier for log messages. */
-
-    /* Configuration. */
-    struct list ports;          /* Contains "struct ofport"s. */
-    enum port_vlan_mode vlan_mode; /* VLAN mode */
-    int vlan;                   /* -1=trunk port, else a 12-bit VLAN ID. */
-    unsigned long *trunks;      /* Bitmap of trunked VLANs, if 'vlan' == -1.
-                                 * NULL if all VLANs are trunked. */
-    struct lacp *lacp;          /* LACP if LACP is enabled, otherwise NULL. */
-    struct bond *bond;          /* Nonnull iff more than one port. */
-    bool use_priority_tags;     /* Use 802.1p tag for frames in VLAN 0? */
-
-    /* Status. */
-    bool floodable;          /* True if no port has OFPUTIL_PC_NO_FLOOD set. */
-
-    /* Port mirroring info. */
-    mirror_mask_t src_mirrors;  /* Mirrors triggered when packet received. */
-    mirror_mask_t dst_mirrors;  /* Mirrors triggered when packet sent. */
-    mirror_mask_t mirror_out;   /* Mirrors that output to this bundle. */
-};
-
 static void bundle_remove(struct ofport *);
 static void bundle_update(struct ofbundle *);
 static void bundle_destroy(struct ofbundle *);
 static void bundle_del_port(struct ofport_dpif *);
 static void bundle_run(struct ofbundle *);
 static void bundle_wait(struct ofbundle *);
-static struct ofbundle *lookup_input_bundle(const struct ofproto_dpif *,
-                                            uint16_t in_port, bool warn,
-                                            struct ofport_dpif **in_ofportp);
-
-/* A controller may use OFPP_NONE as the ingress port to indicate that
- * it did not arrive on a "real" port.  'ofpp_none_bundle' exists for
- * when an input bundle is needed for validation (e.g., mirroring or
- * OFPP_NORMAL processing).  It is not connected to an 'ofproto' or have
- * any 'port' structs, so care must be taken when dealing with it. */
-static struct ofbundle ofpp_none_bundle = {
-    .name      = "OFPP_NONE",
-    .vlan_mode = PORT_VLAN_TRUNK
-};
 
 static void stp_run(struct ofproto_dpif *ofproto);
 static void stp_wait(struct ofproto_dpif *ofproto);
 static int set_stp_port(struct ofport *,
                         const struct ofproto_port_stp_settings *);
 
-static bool ofbundle_includes_vlan(const struct ofbundle *, uint16_t vlan);
-
-struct action_xlate_ctx {
-/* action_xlate_ctx_init() initializes these members. */
-
-    /* The ofproto. */
-    struct ofproto_dpif *ofproto;
-
-    /* Flow to which the OpenFlow actions apply.  xlate_actions() will modify
-     * this flow when actions change header fields. */
-    struct flow flow;
-
-    /* Flow at the last commit. */
-    struct flow base_flow;
-
-    /* Tunnel IP destination address as received.  This is stored separately
-     * as the base_flow.tunnel is cleared on init to reflect the datapath
-     * behavior.  Used to make sure not to send tunneled output to ourselves,
-     * which might lead to an infinite loop.  This could happen easily
-     * if a tunnel is marked as 'ip_remote=flow', and the flow does not
-     * actually set the tun_dst field. */
-    ovs_be32 orig_tunnel_ip_dst;
-
-    /* stack for the push and pop actions.
-     * Each stack element is of the type "union mf_subvalue". */
-    struct ofpbuf stack;
-    union mf_subvalue init_stack[1024 / sizeof(union mf_subvalue)];
-
-    /* The packet corresponding to 'flow', or a null pointer if we are
-     * revalidating without a packet to refer to. */
-    const struct ofpbuf *packet;
-
-    /* Should OFPP_NORMAL update the MAC learning table?  Should "learn"
-     * actions update the flow table?
-     *
-     * We want to update these tables if we are actually processing a packet,
-     * or if we are accounting for packets that the datapath has processed, but
-     * not if we are just revalidating. */
-    bool may_learn;
-
-    /* The rule that we are currently translating, or NULL. */
-    struct rule_dpif *rule;
-
-    /* Union of the set of TCP flags seen so far in this flow.  (Used only by
-     * NXAST_FIN_TIMEOUT.  Set to zero to avoid updating updating rules'
-     * timeouts.) */
-    uint8_t tcp_flags;
-
-    /* If nonnull, flow translation calls this function just before executing a
-     * resubmit or OFPP_TABLE action.  In addition, disables logging of traces
-     * when the recursion depth is exceeded.
-     *
-     * 'rule' is the rule being submitted into.  It will be null if the
-     * resubmit or OFPP_TABLE action didn't find a matching rule.
-     *
-     * This is normally null so the client has to set it manually after
-     * calling action_xlate_ctx_init(). */
-    void (*resubmit_hook)(struct action_xlate_ctx *, struct rule_dpif *rule);
-
-    /* If nonnull, flow translation calls this function to report some
-     * significant decision, e.g. to explain why OFPP_NORMAL translation
-     * dropped a packet. */
-    void (*report_hook)(struct action_xlate_ctx *, const char *s);
-
-    /* If nonnull, flow translation credits the specified statistics to each
-     * rule reached through a resubmit or OFPP_TABLE action.
-     *
-     * This is normally null so the client has to set it manually after
-     * calling action_xlate_ctx_init(). */
-    const struct dpif_flow_stats *resubmit_stats;
-
-/* xlate_actions() initializes and uses these members.  The client might want
- * to look at them after it returns. */
-
-    struct ofpbuf *odp_actions; /* Datapath actions. */
-    tag_type tags;              /* Tags associated with actions. */
-    enum slow_path_reason slow; /* 0 if fast path may be used. */
-    bool has_learn;             /* Actions include NXAST_LEARN? */
-    bool has_normal;            /* Actions output to OFPP_NORMAL? */
-    bool has_fin_timeout;       /* Actions include NXAST_FIN_TIMEOUT? */
-    uint16_t nf_output_iface;   /* Output interface index for NetFlow. */
-    mirror_mask_t mirrors;      /* Bitmap of associated mirrors. */
-
-/* xlate_actions() initializes and uses these members, but the client has no
- * reason to look at them. */
-
-    int recurse;                /* Recursion level, via xlate_table_action. */
-    bool max_resubmit_trigger;  /* Recursed too deeply during translation. */
-    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. */
-    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. */
-};
-
-/* Initial values of fields of the packet that may be changed during
- * flow processing and needed later. */
-struct initial_vals {
-   /* This is the value of vlan_tci in the packet as actually received from
-    * dpif.  This is the same as the facet's flow.vlan_tci unless the packet
-    * was received via a VLAN splinter.  In that case, this value is 0
-    * (because the packet as actually received from the dpif had no 802.1Q
-    * tag) but the facet's flow.vlan_tci is set to the VLAN that the splinter
-    * represents.
-    *
-    * This member should be removed when the VLAN splinters feature is no
-    * longer needed. */
-    ovs_be16 vlan_tci;
-};
-
-static void action_xlate_ctx_init(struct action_xlate_ctx *,
-                                  struct ofproto_dpif *, const struct flow *,
-                                  const struct initial_vals *initial_vals,
-                                  struct rule_dpif *,
-                                  uint8_t tcp_flags, const struct ofpbuf *);
-static void xlate_actions(struct action_xlate_ctx *,
-                          const struct ofpact *ofpacts, size_t ofpacts_len,
-                          struct ofpbuf *odp_actions);
-static void xlate_actions_for_side_effects(struct action_xlate_ctx *,
-                                           const struct ofpact *ofpacts,
-                                           size_t ofpacts_len);
-static void xlate_table_action(struct action_xlate_ctx *, uint16_t in_port,
-                               uint8_t table_id, bool may_packet_in);
-
-static size_t put_userspace_action(const struct ofproto_dpif *,
-                                   struct ofpbuf *odp_actions,
-                                   const struct flow *,
-                                   const union user_action_cookie *,
-                                   const size_t);
-
 static void compose_slow_path(const struct ofproto_dpif *, const struct flow *,
                               enum slow_path_reason,
                               uint64_t *stub, size_t stub_size,
                               const struct nlattr **actionsp,
                               size_t *actions_lenp);
 
-static void xlate_report(struct action_xlate_ctx *ctx, const char *s);
-
 /* A subfacet (see "struct subfacet" below) has three possible installation
  * states:
  *
@@ -364,8 +123,6 @@ enum subfacet_path {
     SF_SLOW_PATH,               /* Send-to-userspace action is installed. */
 };
 
-static const char *subfacet_path_to_string(enum subfacet_path);
-
 /* A dpif flow and actions associated with a facet.
  *
  * See also the large comment on struct facet. */
@@ -374,6 +131,7 @@ struct subfacet {
     struct hmap_node hmap_node; /* In struct ofproto_dpif 'subfacets' list. */
     struct list list_node;      /* In struct facet's 'facets' list. */
     struct facet *facet;        /* Owning facet. */
+    struct dpif_backer *backer; /* Owning backer. */
 
     enum odp_key_fitness key_fitness;
     struct nlattr *key;
@@ -385,67 +143,49 @@ struct subfacet {
     uint64_t dp_packet_count;   /* Last known packet count in the datapath. */
     uint64_t dp_byte_count;     /* Last known byte count in the datapath. */
 
-    /* Datapath actions.
-     *
-     * These should be essentially identical for every subfacet in a facet, but
-     * may differ in trivial ways due to VLAN splinters. */
-    size_t actions_len;         /* Number of bytes in actions[]. */
-    struct nlattr *actions;     /* Datapath actions. */
-
-    enum slow_path_reason slow; /* 0 if fast path may be used. */
     enum subfacet_path path;    /* Installed in datapath? */
-
-    /* Initial values of the packet that may be needed later. */
-    struct initial_vals initial_vals;
-
-    /* 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;
 };
 
 #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 *,
+static struct subfacet *subfacet_find(struct dpif_backer *,
                                       const struct nlattr *key, size_t key_len,
                                       uint32_t key_hash);
 static void subfacet_destroy(struct subfacet *);
 static void subfacet_destroy__(struct subfacet *);
-static void subfacet_destroy_batch(struct ofproto_dpif *,
+static void subfacet_destroy_batch(struct dpif_backer *,
                                    struct subfacet **, int n);
 static void subfacet_reset_dp_stats(struct subfacet *,
                                     struct dpif_flow_stats *);
-static void subfacet_update_time(struct subfacet *, long long int used);
 static void subfacet_update_stats(struct subfacet *,
                                   const struct dpif_flow_stats *);
-static void subfacet_make_actions(struct subfacet *,
-                                  const struct ofpbuf *packet,
-                                  struct ofpbuf *odp_actions);
 static int subfacet_install(struct subfacet *,
-                            const struct nlattr *actions, size_t actions_len,
-                            struct dpif_flow_stats *, enum slow_path_reason);
+                            const struct ofpbuf *odp_actions,
+                            struct dpif_flow_stats *);
 static void subfacet_uninstall(struct subfacet *);
 
-static enum subfacet_path subfacet_want_path(enum slow_path_reason);
-
-/* An exact-match instantiation of an OpenFlow flow.
+/* A unique, non-overlapping instantiation of an OpenFlow flow.
  *
  * A facet associates a "struct flow", which represents the Open vSwitch
- * userspace idea of an exact-match flow, with one or more subfacets.  Each
- * subfacet tracks the datapath's idea of the exact-match flow equivalent to
- * the facet.  When the kernel module (or other dpif implementation) and Open
- * vSwitch userspace agree on the definition of a flow key, there is exactly
- * one subfacet per facet.  If the dpif implementation supports more-specific
- * flow matching than userspace, however, a facet can have more than one
- * subfacet, each of which corresponds to some distinction in flow that
- * userspace simply doesn't understand.
+ * userspace idea of an exact-match flow, with one or more subfacets.
+ * While the facet is created based on an exact-match flow, it is stored
+ * within the ofproto based on the wildcards that could be expressed
+ * based on the flow table and other configuration.  (See the 'wc'
+ * description in "struct xlate_out" for more details.)
  *
- * Flow expiration works in terms of subfacets, so a facet must have at least
- * one subfacet or it will never expire, leaking memory. */
+ * Each subfacet tracks the datapath's idea of the flow equivalent to
+ * the facet.  When the kernel module (or other dpif implementation) and
+ * Open vSwitch userspace agree on the definition of a flow key, there
+ * is exactly one subfacet per facet.  If the dpif implementation
+ * supports more-specific flow matching than userspace, however, a facet
+ * can have more than one subfacet.  Examples include the dpif
+ * implementation not supporting the same wildcards as userspace or some
+ * distinction in flow that userspace simply doesn't understand.
+ *
+ * Flow expiration works in terms of subfacets, so a facet must have at
+ * least one subfacet or it will never expire, leaking memory. */
 struct facet {
     /* Owners. */
     struct hmap_node hmap_node;  /* In owning ofproto's 'facets' hmap. */
@@ -457,7 +197,8 @@ struct facet {
     long long int used;         /* Time last used; time created if not used. */
 
     /* Key. */
-    struct flow flow;
+    struct flow flow;           /* Flow of the creating subfacet. */
+    struct cls_rule cr;         /* In 'ofproto_dpif's facets classifier. */
 
     /* These statistics:
      *
@@ -484,17 +225,7 @@ struct facet {
     struct netflow_flow nf_flow; /* Per-flow NetFlow tracking data. */
     uint8_t tcp_flags;           /* TCP flags seen for this 'rule'. */
 
-    /* Properties of datapath actions.
-     *
-     * Every subfacet has its own actions because actions can differ slightly
-     * between splintered and non-splintered subfacets due to the VLAN tag
-     * being initially different (present vs. absent).  All of them have these
-     * properties in common so we just store one copy of them here. */
-    bool has_learn;              /* Actions include NXAST_LEARN? */
-    bool has_normal;             /* Actions output to OFPP_NORMAL? */
-    bool has_fin_timeout;        /* Actions include NXAST_FIN_TIMEOUT? */
-    tag_type tags;               /* Tags that would require revalidation. */
-    mirror_mask_t mirrors;       /* Bitmap of dependent mirrors. */
+    struct xlate_out xout;
 
     /* Storage for a single subfacet, to reduce malloc() time and space
      * overhead.  (A facet always has at least one subfacet and in the common
@@ -506,62 +237,28 @@ struct facet {
     long long int learn_rl;      /* Rate limiter for facet_learn(). */
 };
 
-static struct facet *facet_create(struct rule_dpif *,
-                                  const struct flow *, uint32_t hash);
+static struct facet *facet_create(const struct flow_miss *, struct rule_dpif *,
+                                  struct xlate_out *,
+                                  struct dpif_flow_stats *);
 static void facet_remove(struct facet *);
 static void facet_free(struct facet *);
 
-static struct facet *facet_find(struct ofproto_dpif *,
-                                const struct flow *, uint32_t hash);
+static struct facet *facet_find(struct ofproto_dpif *, const struct flow *);
 static struct facet *facet_lookup_valid(struct ofproto_dpif *,
-                                        const struct flow *, uint32_t hash);
-static void facet_revalidate(struct facet *);
+                                        const struct flow *);
+static bool facet_revalidate(struct facet *);
 static bool facet_check_consistency(struct facet *);
 
 static void facet_flush_stats(struct facet *);
 
-static void facet_update_time(struct facet *, long long int used);
 static void facet_reset_counters(struct facet *);
-static void facet_push_stats(struct facet *);
+static void facet_push_stats(struct facet *, bool may_learn);
 static void facet_learn(struct facet *);
 static void facet_account(struct facet *);
 static void push_all_stats(void);
 
-static struct subfacet *facet_get_subfacet(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;
-    struct ofbundle *bundle;    /* Bundle that contains this port, if any. */
-    struct list bundle_node;    /* In struct ofbundle's "ports" list. */
-    struct cfm *cfm;            /* Connectivity Fault Management, if any. */
-    struct bfd *bfd;            /* BFD, if any. */
-    tag_type tag;               /* Tag associated with this port. */
-    bool may_enable;            /* May be enabled in bonds. */
-    long long int carrier_seq;  /* Carrier status changes. */
-    struct tnl_port *tnl_port;  /* Tunnel handle, or null. */
-
-    /* Spanning tree. */
-    struct stp_port *stp_port;  /* Spanning Tree Protocol, if any. */
-    enum stp_state stp_state;   /* Always STP_DISABLED if STP not in use. */
-    long long int stp_state_entered;
-
-    struct hmap priorities;     /* Map of attached 'priority_to_dscp's. */
-
-    /* Linux VLAN device support (e.g. "eth0.10" for VLAN 10.)
-     *
-     * This is deprecated.  It is only for compatibility with broken device
-     * drivers in old versions of Linux that do not properly support VLANs when
-     * VLAN devices are not used.  When broken device drivers are no longer in
-     * widespread use, we will delete these interfaces. */
-    uint16_t realdev_ofp_port;
-    int vlandev_vid;
-};
-
 /* Node in 'ofport_dpif''s 'priorities' map.  Used to maintain a map from
  * 'priority' (the datapath's term for QoS queue) to the dscp bits which all
  * traffic egressing the 'ofport' with that priority should be marked with. */
@@ -581,21 +278,17 @@ struct priority_to_dscp {
 struct vlan_splinter {
     struct hmap_node realdev_vid_node;
     struct hmap_node vlandev_node;
-    uint16_t realdev_ofp_port;
-    uint16_t vlandev_ofp_port;
+    ofp_port_t realdev_ofp_port;
+    ofp_port_t vlandev_ofp_port;
     int vid;
 };
 
-static uint32_t vsp_realdev_to_vlandev(const struct ofproto_dpif *,
-                                       uint32_t realdev, ovs_be16 vlan_tci);
 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 void vsp_add(struct ofport_dpif *, ofp_port_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 ofp_port_t odp_port_to_ofp_port(const struct ofproto_dpif *,
+                                       odp_port_t odp_port);
 
 static struct ofport_dpif *
 ofport_dpif_cast(const struct ofport *ofport)
@@ -606,8 +299,10 @@ ofport_dpif_cast(const struct ofport *ofport)
 static void port_run(struct ofport_dpif *);
 static void port_run_fast(struct ofport_dpif *);
 static void port_wait(struct ofport_dpif *);
+static int set_bfd(struct ofport *, const struct smap *);
 static int set_cfm(struct ofport *, const struct cfm_settings *);
 static void ofport_clear_priorities(struct ofport_dpif *);
+static void ofport_update_peer(struct ofport_dpif *);
 static void run_fast_rl(void);
 
 struct dpif_completion {
@@ -615,17 +310,6 @@ struct dpif_completion {
     struct ofoperation *op;
 };
 
-/* Extra information about a classifier table.
- * Currently used just for optimized flow revalidation. */
-struct table_dpif {
-    /* If either of these is nonnull, then this table has a form that allows
-     * flows to be tagged to avoid revalidating most flows for the most common
-     * kinds of flow table changes. */
-    struct cls_table *catchall_table; /* Table that wildcards all fields. */
-    struct cls_table *other_table;    /* Table with any other wildcard set. */
-    uint32_t basis;                   /* Keeps each table's tags separate. */
-};
-
 /* Reasons that we might need to revalidate every facet, and corresponding
  * coverage counters.
  *
@@ -656,6 +340,11 @@ struct drop_key {
     size_t key_len;
 };
 
+struct avg_subfacet_rates {
+    double add_rate;   /* Moving average of new flows created per minute. */
+    double del_rate;   /* Moving average of flows deleted per minute. */
+};
+
 /* All datapaths of a given type share a single dpif backer instance. */
 struct dpif_backer {
     char *type;
@@ -671,80 +360,10 @@ struct dpif_backer {
     struct tag_set revalidate_set; /* Revalidate only matching facets. */
 
     struct hmap drop_keys; /* Set of dropped odp keys. */
-};
-
-/* All existing ofproto_backer instances, indexed by ofproto->up.type. */
-static struct shash all_dpif_backers = SHASH_INITIALIZER(&all_dpif_backers);
+    bool recv_set_enable; /* Enables or disables receiving packets. */
 
-static void drop_key_clear(struct dpif_backer *);
-static struct ofport_dpif *
-odp_port_to_ofport(const struct dpif_backer *, uint32_t odp_port);
-
-static void dpif_stats_update_hit_count(struct ofproto_dpif *ofproto,
-                                        uint64_t delta);
-struct avg_subfacet_rates {
-    double add_rate;     /* Moving average of new flows created per minute. */
-    double del_rate;     /* Moving average of flows deleted per minute. */
-};
-static void show_dp_rates(struct ds *ds, const char *heading,
-                          const struct avg_subfacet_rates *rates);
-static void exp_mavg(double *avg, int base, double new);
-
-struct ofproto_dpif {
-    struct hmap_node all_ofproto_dpifs_node; /* In 'all_ofproto_dpifs'. */
-    struct ofproto up;
-    struct dpif_backer *backer;
-
-    /* Special OpenFlow rules. */
-    struct rule_dpif *miss_rule; /* Sends flow table misses to controller. */
-    struct rule_dpif *no_packet_in_rule; /* Drops flow table misses. */
-
-    /* Statistics. */
-    uint64_t n_matches;
-
-    /* Bridging. */
-    struct netflow *netflow;
-    struct dpif_sflow *sflow;
-    struct dpif_ipfix *ipfix;
-    struct hmap bundles;        /* Contains "struct ofbundle"s. */
-    struct mac_learning *ml;
-    struct ofmirror *mirrors[MAX_MIRRORS];
-    bool has_mirrors;
-    bool has_bonded_bundles;
-
-    /* Facets. */
-    struct hmap facets;
     struct hmap subfacets;
     struct governor *governor;
-    long long int consistency_rl;
-
-    /* Revalidation. */
-    struct table_dpif tables[N_TABLES];
-
-    /* Support for debugging async flow mods. */
-    struct list completions;
-
-    bool has_bundle_action; /* True when the first bundle action appears. */
-    struct netdev_stats stats; /* To account packets generated and consumed in
-                                * userspace. */
-
-    /* Spanning tree. */
-    struct stp *stp;
-    long long int stp_last_tick;
-
-    /* VLAN splinters. */
-    struct hmap realdev_vid_map; /* (realdev,vid) -> vlandev. */
-    struct hmap vlandev_map;     /* vlandev -> (realdev,vid). */
-
-    /* Ports. */
-    struct sset ports;             /* Set of standard port names. */
-    struct sset ghost_ports;       /* Ports with no datapath port. */
-    struct sset port_poll_set;     /* Queued names for port_poll() reply. */
-    int port_poll_errno;           /* Last errno for port_poll() reply. */
-
-    /* Per ofproto's dpif stats. */
-    uint64_t n_hit;
-    uint64_t n_missed;
 
     /* Subfacet statistics.
      *
@@ -753,40 +372,33 @@ struct ofproto_dpif {
      * exposed via "ovs-appctl dpif/show".  The goal is to learn about
      * traffic patterns in ways that we can use later to improve Open vSwitch
      * performance in new situations.  */
-    long long int created;         /* Time when it is created. */
-    unsigned int max_n_subfacet;   /* Maximum number of flows */
+    long long int created;           /* Time when it is created. */
+    unsigned max_n_subfacet;         /* Maximum number of flows */
+    unsigned avg_n_subfacet;         /* Average number of flows. */
+    long long int avg_subfacet_life; /* Average life span of subfacets. */
 
     /* The average number of subfacets... */
-    struct avg_subfacet_rates hourly; /* ...over the last hour. */
-    struct avg_subfacet_rates daily;  /* ...over the last day. */
-    long long int last_minute;        /* Last time 'hourly' was updated. */
+    struct avg_subfacet_rates hourly;   /* ...over the last hour. */
+    struct avg_subfacet_rates daily;    /* ...over the last day. */
+    struct avg_subfacet_rates lifetime; /* ...over the switch lifetime. */
+    long long int last_minute;          /* Last time 'hourly' was updated. */
 
     /* Number of subfacets added or deleted since 'last_minute'. */
-    unsigned int subfacet_add_count;
-    unsigned int subfacet_del_count;
+    unsigned subfacet_add_count;
+    unsigned subfacet_del_count;
 
     /* Number of subfacets added or deleted from 'created' to 'last_minute.' */
     unsigned long long int total_subfacet_add_count;
     unsigned long long int total_subfacet_del_count;
+};
 
-    /* Sum of the number of milliseconds that each subfacet existed,
-     * over the subfacets that have been added and then later deleted. */
-    unsigned long long int total_subfacet_life_span;
-
-    /* Incremented by the number of currently existing subfacets, each
-     * time we pull statistics from the kernel. */
-    unsigned long long int total_subfacet_count;
+/* All existing ofproto_backer instances, indexed by ofproto->up.type. */
+static struct shash all_dpif_backers = SHASH_INITIALIZER(&all_dpif_backers);
 
-    /* Number of times we pull statistics from the kernel. */
-    unsigned long long int n_update_stats;
-};
-static unsigned long long int avg_subfacet_life_span(
-                                        const struct ofproto_dpif *);
-static double avg_subfacet_count(const struct ofproto_dpif *ofproto);
-static void update_moving_averages(struct ofproto_dpif *ofproto);
-static void dpif_stats_update_hit_count(struct ofproto_dpif *ofproto,
-                                        uint64_t delta);
-static void update_max_subfacet_count(struct ofproto_dpif *ofproto);
+static void drop_key_clear(struct dpif_backer *);
+static struct ofport_dpif *
+odp_port_to_ofport(const struct dpif_backer *, odp_port_t odp_port);
+static void update_moving_averages(struct dpif_backer *backer);
 
 /* Defer flow mod completion until "ovs-appctl ofproto/unclog"?  (Useful only
  * for debugging the asynchronous flow_mod implementation.) */
@@ -797,25 +409,6 @@ static struct hmap all_ofproto_dpifs = HMAP_INITIALIZER(&all_ofproto_dpifs);
 
 static void ofproto_dpif_unixctl_init(void);
 
-static struct ofproto_dpif *
-ofproto_dpif_cast(const struct ofproto *ofproto)
-{
-    ovs_assert(ofproto->ofproto_class == &ofproto_dpif_class);
-    return CONTAINER_OF(ofproto, struct ofproto_dpif, up);
-}
-
-static struct ofport_dpif *get_ofp_port(const struct ofproto_dpif *,
-                                        uint16_t ofp_port);
-static struct ofport_dpif *get_odp_port(const struct ofproto_dpif *,
-                                        uint32_t odp_port);
-static void ofproto_trace(struct ofproto_dpif *, const struct flow *,
-                          const struct ofpbuf *,
-                          const struct initial_vals *, struct ds *);
-
-/* Packet processing. */
-static void update_learning_table(struct ofproto_dpif *,
-                                  const struct flow *, int vlan,
-                                  struct ofbundle *);
 /* Upcalls. */
 #define FLOW_MISS_MAX_BATCH 50
 static int handle_upcalls(struct dpif_backer *, unsigned int max_batch);
@@ -828,14 +421,7 @@ static void send_netflow_active_timeouts(struct ofproto_dpif *);
 
 /* Utilities. */
 static int send_packet(const struct ofport_dpif *, struct ofpbuf *packet);
-static size_t compose_sflow_action(const struct ofproto_dpif *,
-                                   struct ofpbuf *odp_actions,
-                                   const struct flow *, uint32_t odp_port);
-static void compose_ipfix_action(const struct ofproto_dpif *,
-                                 struct ofpbuf *odp_actions,
-                                 const struct flow *);
-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);
 
@@ -947,6 +533,21 @@ type_run(const char *type)
         push_all_stats();
     }
 
+    /* If vswitchd started with other_config:flow_restore_wait set as "true",
+     * and the configuration has now changed to "false", enable receiving
+     * packets from the datapath. */
+    if (!backer->recv_set_enable && !ofproto_get_flow_restore_wait()) {
+        backer->recv_set_enable = true;
+
+        error = dpif_recv_set(backer->dpif, backer->recv_set_enable);
+        if (error) {
+            VLOG_ERR("Failed to enable receiving packets in dpif.");
+            return error;
+        }
+        dpif_flow_flush(backer->dpif);
+        backer->need_revalidate = REV_RECONFIGURE;
+    }
+
     if (backer->need_revalidate
         || !tag_set_is_empty(&backer->revalidate_set)) {
         struct tag_set revalidate_set = backer->revalidate_set;
@@ -967,13 +568,15 @@ type_run(const char *type)
             }
 
             HMAP_FOR_EACH (iter, up.hmap_node, &ofproto->up.ports) {
+                char namebuf[NETDEV_VPORT_NAME_BUFSIZE];
                 const char *dp_port;
 
-                if (!iter->tnl_port) {
+                if (!iter->is_tunnel) {
                     continue;
                 }
 
-                dp_port = netdev_vport_get_dpif_port(iter->up.netdev);
+                dp_port = netdev_vport_get_dpif_port(iter->up.netdev,
+                                                     namebuf, sizeof namebuf);
                 node = simap_find(&tmp_backers, dp_port);
                 if (node) {
                     simap_put(&backer->tnl_backers, dp_port, node->data);
@@ -982,26 +585,27 @@ type_run(const char *type)
                 } else {
                     node = simap_find(&backer->tnl_backers, dp_port);
                     if (!node) {
-                        uint32_t odp_port = UINT32_MAX;
+                        odp_port_t odp_port = ODPP_NONE;
 
                         if (!dpif_port_add(backer->dpif, iter->up.netdev,
                                            &odp_port)) {
-                            simap_put(&backer->tnl_backers, dp_port, odp_port);
+                            simap_put(&backer->tnl_backers, dp_port,
+                                      odp_to_u32(odp_port));
                             node = simap_find(&backer->tnl_backers, dp_port);
                         }
                     }
                 }
 
-                iter->odp_port = node ? node->data : OVSP_NONE;
-                if (tnl_port_reconfigure(&iter->up, iter->odp_port,
-                                         &iter->tnl_port)) {
+                iter->odp_port = node ? u32_to_odp(node->data) : ODPP_NONE;
+                if (tnl_port_reconfigure(iter, iter->up.netdev,
+                                         iter->odp_port)) {
                     backer->need_revalidate = REV_RECONFIGURE;
                 }
             }
         }
 
         SIMAP_FOR_EACH (node, &tmp_backers) {
-            dpif_port_del(backer->dpif, node->data);
+            dpif_port_del(backer->dpif, u32_to_odp(node->data));
         }
         simap_destroy(&tmp_backers);
 
@@ -1025,14 +629,16 @@ type_run(const char *type)
 
         HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
             struct facet *facet, *next;
+            struct cls_cursor cursor;
 
             if (ofproto->backer != backer) {
                 continue;
             }
 
-            HMAP_FOR_EACH_SAFE (facet, next, hmap_node, &ofproto->facets) {
+            cls_cursor_init(&cursor, &ofproto->facets, NULL);
+            CLS_CURSOR_FOR_EACH_SAFE (facet, next, cr, &cursor) {
                 if (need_revalidate
-                    || tag_set_intersects(&revalidate_set, facet->tags)) {
+                    || tag_set_intersects(&revalidate_set, facet->xout.tags)) {
                     facet_revalidate(facet);
                     run_fast_rl();
                 }
@@ -1040,7 +646,10 @@ type_run(const char *type)
         }
     }
 
-    if (timer_expired(&backer->next_expiration)) {
+    if (!backer->recv_set_enable) {
+        /* Wake up before a max of 1000ms. */
+        timer_set_duration(&backer->next_expiration, 1000);
+    } else if (timer_expired(&backer->next_expiration)) {
         int delay = expire(backer);
         timer_set_duration(&backer->next_expiration, delay);
     }
@@ -1098,6 +707,24 @@ type_run(const char *type)
         }
     }
 
+    if (backer->governor) {
+        size_t n_subfacets;
+
+        governor_run(backer->governor);
+
+        /* If the governor has shrunk to its minimum size and the number of
+         * subfacets has dwindled, then drop the governor entirely.
+         *
+         * For hysteresis, the number of subfacets to drop the governor is
+         * smaller than the number needed to trigger its creation. */
+        n_subfacets = hmap_count(&backer->subfacets);
+        if (n_subfacets * 4 < flow_eviction_threshold
+            && governor_is_idle(backer->governor)) {
+            governor_destroy(backer->governor);
+            backer->governor = NULL;
+        }
+    }
+
     return 0;
 }
 
@@ -1106,6 +733,11 @@ dpif_backer_run_fast(struct dpif_backer *backer, int max_batch)
 {
     unsigned int work;
 
+    /* If recv_set_enable is false, we should not handle upcalls. */
+    if (!backer->recv_set_enable) {
+        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.
      *
@@ -1191,6 +823,10 @@ type_wait(const char *type)
         return;
     }
 
+    if (backer->governor) {
+        governor_wait(backer->governor);
+    }
+
     timer_wait(&backer->next_expiration);
 }
 \f
@@ -1233,13 +869,17 @@ close_dpif_backer(struct dpif_backer *backer)
     shash_delete(&all_dpif_backers, node);
     dpif_close(backer->dpif);
 
+    ovs_assert(hmap_is_empty(&backer->subfacets));
+    hmap_destroy(&backer->subfacets);
+    governor_destroy(backer->governor);
+
     free(backer);
 }
 
 /* Datapath port slated for removal from datapath. */
 struct odp_garbage {
     struct list list_node;
-    uint32_t odp_port;
+    odp_port_t odp_port;
 };
 
 static int
@@ -1292,22 +932,27 @@ open_dpif_backer(const char *type, struct dpif_backer **backerp)
     free(backer_name);
     if (error) {
         VLOG_ERR("failed to open datapath of type %s: %s", type,
-                 strerror(error));
+                 ovs_strerror(error));
         free(backer);
         return error;
     }
 
     backer->type = xstrdup(type);
+    backer->governor = NULL;
     backer->refcount = 1;
     hmap_init(&backer->odp_to_ofport_map);
     hmap_init(&backer->drop_keys);
+    hmap_init(&backer->subfacets);
     timer_set_duration(&backer->next_expiration, 1000);
     backer->need_revalidate = 0;
     simap_init(&backer->tnl_backers);
     tag_set_init(&backer->revalidate_set);
+    backer->recv_set_enable = !ofproto_get_flow_restore_wait();
     *backerp = backer;
 
-    dpif_flow_flush(backer->dpif);
+    if (backer->recv_set_enable) {
+        dpif_flow_flush(backer->dpif);
+    }
 
     /* Loop through the ports already on the datapath and remove any
      * that we don't need anymore. */
@@ -1331,14 +976,27 @@ open_dpif_backer(const char *type, struct dpif_backer **backerp)
 
     shash_add(&all_dpif_backers, type, backer);
 
-    error = dpif_recv_set(backer->dpif, true);
+    error = dpif_recv_set(backer->dpif, backer->recv_set_enable);
     if (error) {
         VLOG_ERR("failed to listen on datapath of type %s: %s",
-                 type, strerror(error));
+                 type, ovs_strerror(error));
         close_dpif_backer(backer);
         return error;
     }
 
+    backer->max_n_subfacet = 0;
+    backer->created = time_msec();
+    backer->last_minute = backer->created;
+    memset(&backer->hourly, 0, sizeof backer->hourly);
+    memset(&backer->daily, 0, sizeof backer->daily);
+    memset(&backer->lifetime, 0, sizeof backer->lifetime);
+    backer->subfacet_add_count = 0;
+    backer->subfacet_del_count = 0;
+    backer->total_subfacet_add_count = 0;
+    backer->total_subfacet_del_count = 0;
+    backer->avg_n_subfacet = 0;
+    backer->avg_subfacet_life = 0;
+
     return error;
 }
 
@@ -1347,7 +1005,7 @@ construct(struct ofproto *ofproto_)
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
     struct shash_node *node, *next;
-    int max_ports;
+    odp_port_t max_ports;
     int error;
     int i;
 
@@ -1357,9 +1015,8 @@ construct(struct ofproto *ofproto_)
     }
 
     max_ports = dpif_get_max_ports(ofproto->backer->dpif);
-    ofproto_init_max_ports(ofproto_, MIN(max_ports, OFPP_MAX));
-
-    ofproto->n_matches = 0;
+    ofproto_init_max_ports(ofproto_, u16_to_ofp(MIN(odp_to_u32(max_ports),
+                                                    ofp_to_u16(OFPP_MAX))));
 
     ofproto->netflow = NULL;
     ofproto->sflow = NULL;
@@ -1372,9 +1029,7 @@ construct(struct ofproto *ofproto_)
     }
     ofproto->has_bonded_bundles = false;
 
-    hmap_init(&ofproto->facets);
-    hmap_init(&ofproto->subfacets);
-    ofproto->governor = NULL;
+    classifier_init(&ofproto->facets);
     ofproto->consistency_rl = LLONG_MIN;
 
     for (i = 0; i < N_TABLES; i++) {
@@ -1390,8 +1045,6 @@ construct(struct ofproto *ofproto_)
     ofproto_dpif_unixctl_init();
 
     ofproto->has_mirrors = false;
-    ofproto->has_bundle_action = false;
-
     hmap_init(&ofproto->vlandev_map);
     hmap_init(&ofproto->realdev_vid_map);
 
@@ -1427,19 +1080,6 @@ construct(struct ofproto *ofproto_)
     ofproto->n_hit = 0;
     ofproto->n_missed = 0;
 
-    ofproto->max_n_subfacet = 0;
-    ofproto->created = time_msec();
-    ofproto->last_minute = ofproto->created;
-    memset(&ofproto->hourly, 0, sizeof ofproto->hourly);
-    memset(&ofproto->daily, 0, sizeof ofproto->daily);
-    ofproto->subfacet_add_count = 0;
-    ofproto->subfacet_del_count = 0;
-    ofproto->total_subfacet_add_count = 0;
-    ofproto->total_subfacet_del_count = 0;
-    ofproto->total_subfacet_life_span = 0;
-    ofproto->total_subfacet_count = 0;
-    ofproto->n_update_stats = 0;
-
     return error;
 }
 
@@ -1473,7 +1113,8 @@ add_internal_flow(struct ofproto_dpif *ofproto, int id,
         return error;
     }
 
-    *rulep = rule_dpif_lookup__(ofproto, &fm.match.flow, TBL_INTERNAL);
+    *rulep = rule_dpif_lookup_in_table(ofproto, &fm.match.flow, NULL,
+                                       TBL_INTERNAL);
     ovs_assert(*rulep != NULL);
 
     return 0;
@@ -1505,6 +1146,12 @@ add_internal_flows(struct ofproto_dpif *ofproto)
     ofpbuf_clear(&ofpacts);
     error = add_internal_flow(ofproto, id++, &ofpacts,
                               &ofproto->no_packet_in_rule);
+    if (error) {
+        return error;
+    }
+
+    error = add_internal_flow(ofproto, id++, &ofpacts,
+                              &ofproto->drop_frags_rule);
     return error;
 }
 
@@ -1528,6 +1175,7 @@ destruct(struct ofproto *ofproto_)
     struct oftable *table;
     int i;
 
+    ofproto->backer->need_revalidate = REV_RECONFIGURE;
     hmap_remove(&all_ofproto_dpifs, &ofproto->all_ofproto_dpifs_node);
     complete_operations(ofproto);
 
@@ -1545,13 +1193,11 @@ destruct(struct ofproto *ofproto_)
     }
 
     netflow_destroy(ofproto->netflow);
-    dpif_sflow_destroy(ofproto->sflow);
+    dpif_sflow_unref(ofproto->sflow);
     hmap_destroy(&ofproto->bundles);
-    mac_learning_destroy(ofproto->ml);
+    mac_learning_unref(ofproto->ml);
 
-    hmap_destroy(&ofproto->facets);
-    hmap_destroy(&ofproto->subfacets);
-    governor_destroy(ofproto->governor);
+    classifier_destroy(&ofproto->facets);
 
     hmap_destroy(&ofproto->vlandev_map);
     hmap_destroy(&ofproto->realdev_vid_map);
@@ -1569,6 +1215,12 @@ run_fast(struct ofproto *ofproto_)
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
     struct ofport_dpif *ofport;
 
+    /* Do not perform any periodic activity required by 'ofproto' while
+     * waiting for flow restore to complete. */
+    if (ofproto_get_flow_restore_wait()) {
+        return 0;
+    }
+
     HMAP_FOR_EACH (ofport, up.hmap_node, &ofproto->up.ports) {
         port_run_fast(ofport);
     }
@@ -1588,6 +1240,12 @@ run(struct ofproto *ofproto_)
         complete_operations(ofproto);
     }
 
+    /* Do not perform any periodic activity below required by 'ofproto' while
+     * waiting for flow restore to complete. */
+    if (ofproto_get_flow_restore_wait()) {
+        return 0;
+    }
+
     error = run_fast(ofproto_);
     if (error) {
         return error;
@@ -1614,40 +1272,28 @@ run(struct ofproto *ofproto_)
 
     /* Check the consistency of a random facet, to aid debugging. */
     if (time_msec() >= ofproto->consistency_rl
-        && !hmap_is_empty(&ofproto->facets)
+        && !classifier_is_empty(&ofproto->facets)
         && !ofproto->backer->need_revalidate) {
+        struct cls_table *table;
+        struct cls_rule *cr;
         struct facet *facet;
 
         ofproto->consistency_rl = time_msec() + 250;
 
-        facet = CONTAINER_OF(hmap_random_node(&ofproto->facets),
-                             struct facet, hmap_node);
+        table = CONTAINER_OF(hmap_random_node(&ofproto->facets.tables),
+                             struct cls_table, hmap_node);
+        cr = CONTAINER_OF(hmap_random_node(&table->rules), struct cls_rule,
+                          hmap_node);
+        facet = CONTAINER_OF(cr, struct facet, cr);
+
         if (!tag_set_intersects(&ofproto->backer->revalidate_set,
-                                facet->tags)) {
+                                facet->xout.tags)) {
             if (!facet_check_consistency(facet)) {
                 ofproto->backer->need_revalidate = REV_INCONSISTENCY;
             }
         }
     }
 
-    if (ofproto->governor) {
-        size_t n_subfacets;
-
-        governor_run(ofproto->governor);
-
-        /* If the governor has shrunk to its minimum size and the number of
-         * subfacets has dwindled, then drop the governor entirely.
-         *
-         * For hysteresis, the number of subfacets to drop the governor is
-         * smaller than the number needed to trigger its creation. */
-        n_subfacets = hmap_count(&ofproto->subfacets);
-        if (n_subfacets * 4 < ofproto->up.flow_eviction_threshold
-            && governor_is_idle(ofproto->governor)) {
-            governor_destroy(ofproto->governor);
-            ofproto->governor = NULL;
-        }
-    }
-
     return 0;
 }
 
@@ -1662,6 +1308,10 @@ wait(struct ofproto *ofproto_)
         poll_immediate_wake();
     }
 
+    if (ofproto_get_flow_restore_wait()) {
+        return;
+    }
+
     dpif_wait(ofproto->backer->dpif);
     dpif_recv_wait(ofproto->backer->dpif);
     if (ofproto->sflow) {
@@ -1686,18 +1336,23 @@ wait(struct ofproto *ofproto_)
         VLOG_DBG_RL(&rl, "need revalidate in ofproto_wait_cb()");
         poll_immediate_wake();
     }
-    if (ofproto->governor) {
-        governor_wait(ofproto->governor);
-    }
 }
 
 static void
 get_memory_usage(const struct ofproto *ofproto_, struct simap *usage)
 {
     const struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
+    struct cls_cursor cursor;
+    size_t n_subfacets = 0;
+    struct facet *facet;
+
+    simap_increase(usage, "facets", classifier_count(&ofproto->facets));
 
-    simap_increase(usage, "facets", hmap_count(&ofproto->facets));
-    simap_increase(usage, "subfacets", hmap_count(&ofproto->subfacets));
+    cls_cursor_init(&cursor, &ofproto->facets, NULL);
+    CLS_CURSOR_FOR_EACH (facet, cr, &cursor) {
+        n_subfacets += list_size(&facet->subfacets);
+    }
+    simap_increase(usage, "subfacets", n_subfacets);
 }
 
 static void
@@ -1710,11 +1365,15 @@ flush(struct ofproto *ofproto_)
 
     n_batch = 0;
     HMAP_FOR_EACH_SAFE (subfacet, next_subfacet, hmap_node,
-                        &ofproto->subfacets) {
+                        &ofproto->backer->subfacets) {
+        if (ofproto_dpif_cast(subfacet->facet->rule->up.ofproto) != ofproto) {
+            continue;
+        }
+
         if (subfacet->path != SF_NOT_INSTALLED) {
             batch[n_batch++] = subfacet;
             if (n_batch >= SUBFACET_DESTROY_MAX_BATCH) {
-                subfacet_destroy_batch(ofproto, batch, n_batch);
+                subfacet_destroy_batch(ofproto->backer, batch, n_batch);
                 n_batch = 0;
             }
         } else {
@@ -1723,7 +1382,7 @@ flush(struct ofproto *ofproto_)
     }
 
     if (n_batch > 0) {
-        subfacet_destroy_batch(ofproto, batch, n_batch);
+        subfacet_destroy_batch(ofproto->backer, batch, n_batch);
     }
 }
 
@@ -1751,13 +1410,19 @@ get_tables(struct ofproto *ofproto_, struct ofp12_table_stats *ots)
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
     struct dpif_dp_stats s;
+    uint64_t n_miss, n_no_pkt_in, n_bytes, n_dropped_frags;
+    uint64_t n_lookup;
 
     strcpy(ots->name, "classifier");
 
     dpif_get_dp_stats(ofproto->backer->dpif, &s);
+    rule_get_stats(&ofproto->miss_rule->up, &n_miss, &n_bytes);
+    rule_get_stats(&ofproto->no_packet_in_rule->up, &n_no_pkt_in, &n_bytes);
+    rule_get_stats(&ofproto->drop_frags_rule->up, &n_dropped_frags, &n_bytes);
 
-    ots->lookup_count = htonll(s.n_hit + s.n_missed);
-    ots->matched_count = htonll(s.n_hit + ofproto->n_matches);
+    n_lookup = s.n_hit + s.n_missed - n_dropped_frags;
+    ots->lookup_count = htonll(n_lookup);
+    ots->matched_count = htonll(n_lookup - n_miss - n_no_pkt_in);
 }
 
 static struct ofport *
@@ -1780,6 +1445,7 @@ port_construct(struct ofport *port_)
     struct ofport_dpif *port = ofport_dpif_cast(port_);
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(port->up.ofproto);
     const struct netdev *netdev = port->up.netdev;
+    char namebuf[NETDEV_VPORT_NAME_BUFSIZE];
     struct dpif_port dpif_port;
     int error;
 
@@ -1791,7 +1457,8 @@ port_construct(struct ofport *port_)
     port->may_enable = true;
     port->stp_port = NULL;
     port->stp_state = STP_DISABLED;
-    port->tnl_port = NULL;
+    port->is_tunnel = false;
+    port->peer = NULL;
     hmap_init(&port->priorities);
     port->realdev_ofp_port = 0;
     port->vlandev_vid = 0;
@@ -1803,12 +1470,14 @@ port_construct(struct ofport *port_)
         * because the patch port represents an interface that sFlow considers
         * to be "internal" to the switch as a whole, and therefore not an
         * candidate for counter polling. */
-        port->odp_port = OVSP_NONE;
+        port->odp_port = ODPP_NONE;
+        ofport_update_peer(port);
         return 0;
     }
 
     error = dpif_port_query_by_name(ofproto->backer->dpif,
-                                    netdev_vport_get_dpif_port(netdev),
+                                    netdev_vport_get_dpif_port(netdev, namebuf,
+                                                               sizeof namebuf),
                                     &dpif_port);
     if (error) {
         return error;
@@ -1817,7 +1486,8 @@ port_construct(struct ofport *port_)
     port->odp_port = dpif_port.port_no;
 
     if (netdev_get_tunnel_config(netdev)) {
-        port->tnl_port = tnl_port_add(&port->up, port->odp_port);
+        tnl_port_add(port, port->up.netdev, port->odp_port);
+        port->is_tunnel = true;
     } else {
         /* Sanity-check that a mapping doesn't already exist.  This
          * shouldn't happen for non-tunnel ports. */
@@ -1829,7 +1499,7 @@ port_construct(struct ofport *port_)
         }
 
         hmap_insert(&ofproto->backer->odp_to_ofport_map, &port->odp_port_node,
-                    hash_int(port->odp_port, 0));
+                    hash_odp_port(port->odp_port));
     }
     dpif_port_destroy(&dpif_port);
 
@@ -1845,30 +1515,39 @@ 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 *dp_port_name = netdev_vport_get_dpif_port(port->up.netdev);
     const char *devname = netdev_get_name(port->up.netdev);
+    char namebuf[NETDEV_VPORT_NAME_BUFSIZE];
+    const char *dp_port_name;
+
+    ofproto->backer->need_revalidate = REV_RECONFIGURE;
 
+    dp_port_name = netdev_vport_get_dpif_port(port->up.netdev, namebuf,
+                                              sizeof namebuf);
     if (dpif_port_exists(ofproto->backer->dpif, dp_port_name)) {
         /* 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. */
-        if (!port->tnl_port) {
+        if (!port->is_tunnel) {
             dpif_port_del(ofproto->backer->dpif, port->odp_port);
         }
-        ofproto->backer->need_revalidate = REV_RECONFIGURE;
     }
 
-    if (port->odp_port != OVSP_NONE && !port->tnl_port) {
+    if (port->peer) {
+        port->peer->peer = NULL;
+        port->peer = NULL;
+    }
+
+    if (port->odp_port != ODPP_NONE && !port->is_tunnel) {
         hmap_remove(&ofproto->backer->odp_to_ofport_map, &port->odp_port_node);
     }
 
-    tnl_port_del(port->tnl_port);
+    tnl_port_del(port);
     sset_find_and_delete(&ofproto->ports, devname);
     sset_find_and_delete(&ofproto->ghost_ports, devname);
-    ofproto->backer->need_revalidate = REV_RECONFIGURE;
     bundle_remove(port_);
     set_cfm(port_, NULL);
+    set_bfd(port_, NULL);
     if (ofproto->sflow) {
         dpif_sflow_del_port(ofproto->sflow, port->odp_port);
     }
@@ -1885,6 +1564,18 @@ port_modified(struct ofport *port_)
     if (port->bundle && port->bundle->bond) {
         bond_slave_set_netdev(port->bundle->bond, port, port->up.netdev);
     }
+
+    if (port->cfm) {
+        cfm_set_netdev(port->cfm, port->up.netdev);
+    }
+
+    if (port->is_tunnel && tnl_port_reconfigure(port, port->up.netdev,
+                                                port->odp_port)) {
+        ofproto_dpif_cast(port->up.ofproto)->backer->need_revalidate =
+            REV_RECONFIGURE;
+    }
+
+    ofport_update_peer(port);
 }
 
 static void
@@ -1925,7 +1616,7 @@ set_sflow(struct ofproto *ofproto_,
         dpif_sflow_set_options(ds, sflow_options);
     } else {
         if (ds) {
-            dpif_sflow_destroy(ds);
+            dpif_sflow_unref(ds);
             ofproto->backer->need_revalidate = REV_RECONFIGURE;
             ofproto->sflow = NULL;
         }
@@ -1952,7 +1643,7 @@ set_ipfix(
             n_flow_exporters_options);
     } else {
         if (di) {
-            dpif_ipfix_destroy(di);
+            dpif_ipfix_unref(di);
             ofproto->ipfix = NULL;
         }
     }
@@ -1973,7 +1664,7 @@ set_cfm(struct ofport *ofport_, const struct cfm_settings *s)
 
             ofproto = ofproto_dpif_cast(ofport->up.ofproto);
             ofproto->backer->need_revalidate = REV_RECONFIGURE;
-            ofport->cfm = cfm_create(netdev_get_name(ofport->up.netdev));
+            ofport->cfm = cfm_create(ofport->up.netdev);
         }
 
         if (cfm_configure(ofport->cfm, s)) {
@@ -1982,7 +1673,7 @@ set_cfm(struct ofport *ofport_, const struct cfm_settings *s)
 
         error = EINVAL;
     }
-    cfm_destroy(ofport->cfm);
+    cfm_unref(ofport->cfm);
     ofport->cfm = NULL;
     return error;
 }
@@ -2255,14 +1946,16 @@ stp_wait(struct ofproto_dpif *ofproto)
     }
 }
 
-/* Returns true if STP should process 'flow'. */
-static bool
-stp_should_process_flow(const struct flow *flow)
+/* Returns true if STP should process 'flow'.  Sets fields in 'wc' that
+ * were used to make the determination.*/
+bool
+stp_should_process_flow(const struct flow *flow, struct flow_wildcards *wc)
 {
+    memset(&wc->masks.dl_dst, 0xff, sizeof wc->masks.dl_dst);
     return eth_addr_equals(flow->dl_dst, eth_addr_stp);
 }
 
-static void
+void
 stp_process_packet(const struct ofport_dpif *ofport,
                    const struct ofpbuf *packet)
 {
@@ -2286,6 +1979,13 @@ stp_process_packet(const struct ofport_dpif *ofport,
     }
 }
 \f
+int
+ofproto_dpif_queue_to_priority(const struct ofproto_dpif *ofproto,
+                               uint32_t queue_id, uint32_t *priority)
+{
+    return dpif_queue_to_priority(ofproto->backer->dpif, queue_id, priority);
+}
+
 static struct priority_to_dscp *
 get_priority(const struct ofport_dpif *ofport, uint32_t priority)
 {
@@ -2301,6 +2001,15 @@ get_priority(const struct ofport_dpif *ofport, uint32_t priority)
     return NULL;
 }
 
+bool
+ofproto_dpif_dscp_from_priority(const struct ofport_dpif *ofport,
+                                uint32_t priority, uint8_t *dscp)
+{
+    struct priority_to_dscp *pdscp = get_priority(ofport, priority);
+    *dscp = pdscp ? pdscp->dscp : 0;
+    return pdscp != NULL;
+}
+
 static void
 ofport_clear_priorities(struct ofport_dpif *ofport)
 {
@@ -2472,7 +2181,7 @@ bundle_del_port(struct ofport_dpif *port)
 }
 
 static bool
-bundle_add_port(struct ofbundle *bundle, uint32_t ofp_port,
+bundle_add_port(struct ofbundle *bundle, ofp_port_t ofp_port,
                 struct lacp_slave_settings *lacp)
 {
     struct ofport_dpif *port;
@@ -2535,8 +2244,8 @@ bundle_destroy(struct ofbundle *bundle)
     hmap_remove(&ofproto->bundles, &bundle->hmap_node);
     free(bundle->name);
     free(bundle->trunks);
-    lacp_destroy(bundle->lacp);
-    bond_destroy(bundle->bond);
+    lacp_unref(bundle->lacp);
+    bond_unref(bundle->bond);
     free(bundle);
 }
 
@@ -2599,7 +2308,7 @@ bundle_set(struct ofproto *ofproto_, void *aux,
         }
         lacp_configure(bundle->lacp, s->lacp);
     } else {
-        lacp_destroy(bundle->lacp);
+        lacp_unref(bundle->lacp);
         bundle->lacp = NULL;
     }
 
@@ -2710,7 +2419,7 @@ bundle_set(struct ofproto *ofproto_, void *aux,
             bond_slave_register(bundle->bond, port, port->up.netdev);
         }
     } else {
-        bond_destroy(bundle->bond);
+        bond_unref(bundle->bond);
         bundle->bond = NULL;
     }
 
@@ -2734,7 +2443,7 @@ bundle_remove(struct ofport *port_)
         if (list_is_empty(&bundle->ports)) {
             bundle_destroy(bundle);
         } else if (list_is_short(&bundle->ports)) {
-            bond_destroy(bundle->bond);
+            bond_unref(bundle->bond);
             bundle->bond = NULL;
         }
     }
@@ -2763,7 +2472,7 @@ send_pdu_cb(void *port_, const void *pdu, size_t pdu_size)
     } else {
         VLOG_ERR_RL(&rl, "port %s: cannot obtain Ethernet address of iface "
                     "%s (%s)", port->bundle->name,
-                    netdev_get_name(port->up.netdev), strerror(error));
+                    netdev_get_name(port->up.netdev), ovs_strerror(error));
     }
 }
 
@@ -2802,7 +2511,7 @@ bundle_send_learning_packets(struct ofbundle *bundle)
         static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
         VLOG_WARN_RL(&rl, "bond %s: %d errors sending %d gratuitous learning "
                      "packets, last error was: %s",
-                     bundle->name, n_errors, n_packets, strerror(error));
+                     bundle->name, n_errors, n_packets, ovs_strerror(error));
     } else {
         VLOG_DBG("bond %s: sent %d gratuitous learning packets",
                  bundle->name, n_packets);
@@ -3114,15 +2823,15 @@ set_mac_table_config(struct ofproto *ofproto_, unsigned int idle_time,
 \f
 /* Ports. */
 
-static struct ofport_dpif *
-get_ofp_port(const struct ofproto_dpif *ofproto, uint16_t ofp_port)
+struct ofport_dpif *
+get_ofp_port(const struct ofproto_dpif *ofproto, ofp_port_t ofp_port)
 {
     struct ofport *ofport = ofproto_get_port(&ofproto->up, ofp_port);
     return ofport ? ofport_dpif_cast(ofport) : NULL;
 }
 
-static struct ofport_dpif *
-get_odp_port(const struct ofproto_dpif *ofproto, uint32_t odp_port)
+struct ofport_dpif *
+get_odp_port(const struct ofproto_dpif *ofproto, odp_port_t odp_port)
 {
     struct ofport_dpif *port = odp_port_to_ofport(ofproto->backer, odp_port);
     return port && &ofproto->up == port->up.ofproto ? port : NULL;
@@ -3138,26 +2847,54 @@ ofproto_port_from_dpif_port(struct ofproto_dpif *ofproto,
     ofproto_port->ofp_port = odp_port_to_ofp_port(ofproto, dpif_port->port_no);
 }
 
-static struct ofport_dpif *
-ofport_get_peer(const struct ofport_dpif *ofport_dpif)
+static void
+ofport_update_peer(struct ofport_dpif *ofport)
 {
     const struct ofproto_dpif *ofproto;
-    const char *peer;
+    struct dpif_backer *backer;
+    const char *peer_name;
 
-    peer = netdev_vport_patch_peer(ofport_dpif->up.netdev);
-    if (!peer) {
-        return NULL;
+    if (!netdev_vport_is_patch(ofport->up.netdev)) {
+        return;
+    }
+
+    backer = ofproto_dpif_cast(ofport->up.ofproto)->backer;
+    backer->need_revalidate = REV_RECONFIGURE;
+
+    if (ofport->peer) {
+        ofport->peer->peer = NULL;
+        ofport->peer = NULL;
+    }
+
+    peer_name = netdev_vport_patch_peer(ofport->up.netdev);
+    if (!peer_name) {
+        return;
     }
 
     HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
-        struct ofport *ofport;
+        struct ofport *peer_ofport;
+        struct ofport_dpif *peer;
+        const char *peer_peer;
+
+        if (ofproto->backer != backer) {
+            continue;
+        }
+
+        peer_ofport = shash_find_data(&ofproto->up.port_by_name, peer_name);
+        if (!peer_ofport) {
+            continue;
+        }
 
-        ofport = shash_find_data(&ofproto->up.port_by_name, peer);
-        if (ofport && ofport->ofproto->ofproto_class == &ofproto_dpif_class) {
-            return ofport_dpif_cast(ofport);
+        peer = ofport_dpif_cast(peer_ofport);
+        peer_peer = netdev_vport_patch_peer(peer->up.netdev);
+        if (peer_peer && !strcmp(netdev_get_name(ofport->up.netdev),
+                                 peer_peer)) {
+            ofport->peer = peer;
+            ofport->peer->peer = ofport;
         }
+
+        return;
     }
-    return NULL;
 }
 
 static void
@@ -3193,12 +2930,6 @@ port_run(struct ofport_dpif *ofport)
 
     port_run_fast(ofport);
 
-    if (ofport->tnl_port
-        && tnl_port_reconfigure(&ofport->up, ofport->odp_port,
-                                &ofport->tnl_port)) {
-        ofproto_dpif_cast(ofport->up.ofproto)->backer->need_revalidate = true;
-    }
-
     if (ofport->cfm) {
         int cfm_opup = cfm_get_opup(ofport->cfm);
 
@@ -3224,10 +2955,7 @@ port_run(struct ofport_dpif *ofport)
 
     if (ofport->may_enable != enable) {
         struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport->up.ofproto);
-
-        if (ofproto->has_bundle_action) {
-            ofproto->backer->need_revalidate = REV_PORT_TOGGLED;
-        }
+        ofproto->backer->need_revalidate = REV_PORT_TOGGLED;
     }
 
     ofport->may_enable = enable;
@@ -3286,16 +3014,18 @@ static int
 port_add(struct ofproto *ofproto_, struct netdev *netdev)
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
-    const char *dp_port_name = netdev_vport_get_dpif_port(netdev);
     const char *devname = netdev_get_name(netdev);
+    char namebuf[NETDEV_VPORT_NAME_BUFSIZE];
+    const char *dp_port_name;
 
     if (netdev_vport_is_patch(netdev)) {
         sset_add(&ofproto->ghost_ports, netdev_get_name(netdev));
         return 0;
     }
 
+    dp_port_name = netdev_vport_get_dpif_port(netdev, namebuf, sizeof namebuf);
     if (!dpif_port_exists(ofproto->backer->dpif, dp_port_name)) {
-        uint32_t port_no = UINT32_MAX;
+        odp_port_t port_no = ODPP_NONE;
         int error;
 
         error = dpif_port_add(ofproto->backer->dpif, netdev, &port_no);
@@ -3303,7 +3033,8 @@ port_add(struct ofproto *ofproto_, struct netdev *netdev)
             return error;
         }
         if (netdev_get_tunnel_config(netdev)) {
-            simap_put(&ofproto->backer->tnl_backers, dp_port_name, port_no);
+            simap_put(&ofproto->backer->tnl_backers,
+                      dp_port_name, odp_to_u32(port_no));
         }
     }
 
@@ -3316,7 +3047,7 @@ port_add(struct ofproto *ofproto_, struct netdev *netdev)
 }
 
 static int
-port_del(struct ofproto *ofproto_, uint16_t ofp_port)
+port_del(struct ofproto *ofproto_, ofp_port_t ofp_port)
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
     struct ofport_dpif *ofport = get_ofp_port(ofproto, ofp_port);
@@ -3329,7 +3060,7 @@ port_del(struct ofproto *ofproto_, uint16_t ofp_port)
     sset_find_and_delete(&ofproto->ghost_ports,
                          netdev_get_name(ofport->up.netdev));
     ofproto->backer->need_revalidate = REV_RECONFIGURE;
-    if (!ofport->tnl_port) {
+    if (!ofport->is_tunnel) {
         error = dpif_port_del(ofproto->backer->dpif, ofport->odp_port);
         if (!error) {
             /* The caller is going to close ofport->up.netdev.  If this is a
@@ -3502,16 +3233,23 @@ struct flow_miss {
     enum odp_key_fitness key_fitness;
     const struct nlattr *key;
     size_t key_len;
-    struct initial_vals initial_vals;
     struct list packets;
     enum dpif_upcall_type upcall_type;
-    uint32_t odp_in_port;
 };
 
 struct flow_miss_op {
     struct dpif_op dpif_op;
-    void *garbage;              /* Pointer to pass to free(), NULL if none. */
-    uint64_t stub[1024 / 8];    /* Temporary buffer. */
+
+    uint64_t slow_stub[128 / 8]; /* Buffer for compose_slow_path() */
+    struct xlate_out xout;
+    bool xout_garbage;           /* 'xout' needs to be uninitialized? */
+
+    struct ofpbuf mask;          /* Flow mask for "put" ops. */
+    struct odputil_keybuf maskbuf;
+
+    /* If this is a "put" op, then a pointer to the subfacet that should
+     * be marked as uninstalled if the operation fails. */
+    struct subfacet *subfacet;
 };
 
 /* Sends an OFPT_PACKET_IN message for 'packet' of type OFPR_NO_MATCH to each
@@ -3538,38 +3276,6 @@ send_packet_in_miss(struct ofproto_dpif *ofproto, const struct ofpbuf *packet,
     connmgr_send_packet_in(ofproto->up.connmgr, &pin);
 }
 
-static enum slow_path_reason
-process_special(struct ofproto_dpif *ofproto, const struct flow *flow,
-                const struct ofport_dpif *ofport, const struct ofpbuf *packet)
-{
-    if (!ofport) {
-        return 0;
-    } else if (ofport->cfm && cfm_should_process_flow(ofport->cfm, flow)) {
-        if (packet) {
-            cfm_process_heartbeat(ofport->cfm, packet);
-        }
-        return SLOW_CFM;
-    } else if (ofport->bfd && bfd_should_process_flow(flow)) {
-        if (packet) {
-            bfd_process_packet(ofport->bfd, flow, packet);
-        }
-        return SLOW_BFD;
-    } else if (ofport->bundle && ofport->bundle->lacp
-               && flow->dl_type == htons(ETH_TYPE_LACP)) {
-        if (packet) {
-            lacp_process_packet(ofport->bundle->lacp, ofport, packet);
-        }
-        return SLOW_LACP;
-    } else if (ofproto->stp && stp_should_process_flow(flow)) {
-        if (packet) {
-            stp_process_packet(ofport, packet);
-        }
-        return SLOW_STP;
-    } else {
-        return 0;
-    }
-}
-
 static struct flow_miss *
 flow_miss_find(struct hmap *todo, const struct ofproto_dpif *ofproto,
                const struct flow *flow, uint32_t hash)
@@ -3594,7 +3300,9 @@ static void
 init_flow_miss_execute_op(struct flow_miss *miss, struct ofpbuf *packet,
                           struct flow_miss_op *op)
 {
-    if (miss->flow.vlan_tci != miss->initial_vals.vlan_tci) {
+    if (miss->flow.in_port.ofp_port
+        != vsp_realdev_to_vlandev(miss->ofproto, miss->flow.in_port.ofp_port,
+                                  miss->flow.vlan_tci)) {
         /* This packet was received on a VLAN splinter port.  We
          * added a VLAN to the packet to make the packet resemble
          * the flow, but the actions were composed assuming that
@@ -3604,11 +3312,13 @@ init_flow_miss_execute_op(struct flow_miss *miss, struct ofpbuf *packet,
         eth_pop_vlan(packet);
     }
 
-    op->garbage = NULL;
+    op->subfacet = NULL;
+    op->xout_garbage = false;
     op->dpif_op.type = DPIF_OP_EXECUTE;
     op->dpif_op.u.execute.key = miss->key;
     op->dpif_op.u.execute.key_len = miss->key_len;
     op->dpif_op.u.execute.packet = packet;
+    ofpbuf_use_stack(&op->mask, &op->maskbuf, sizeof op->maskbuf);
 }
 
 /* Helper for handle_flow_miss_without_facet() and
@@ -3619,8 +3329,6 @@ handle_flow_miss_common(struct rule_dpif *rule,
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
 
-    ofproto->n_matches++;
-
     if (rule->up.cr.priority == FAIL_OPEN_PRIORITY) {
         /*
          * Extra-special case for fail-open mode.
@@ -3637,72 +3345,77 @@ handle_flow_miss_common(struct rule_dpif *rule,
 }
 
 /* Figures out whether a flow that missed in 'ofproto', whose details are in
- * 'miss', is likely to be worth tracking in detail in userspace and (usually)
- * installing a datapath flow.  The answer is usually "yes" (a return value of
- * true).  However, for short flows the cost of bookkeeping is much higher than
- * the benefits, so when the datapath holds a large number of flows we impose
- * some heuristics to decide which flows are likely to be worth tracking. */
+ * 'miss' masked by 'wc', is likely to be worth tracking in detail in userspace
+ * and (usually) installing a datapath flow.  The answer is usually "yes" (a
+ * return value of true).  However, for short flows the cost of bookkeeping is
+ * much higher than the benefits, so when the datapath holds a large number of
+ * flows we impose some heuristics to decide which flows are likely to be worth
+ * tracking. */
 static bool
-flow_miss_should_make_facet(struct ofproto_dpif *ofproto,
-                            struct flow_miss *miss, uint32_t hash)
+flow_miss_should_make_facet(struct flow_miss *miss, struct flow_wildcards *wc)
 {
-    if (!ofproto->governor) {
+    struct dpif_backer *backer = miss->ofproto->backer;
+    uint32_t hash;
+
+    switch (flow_miss_model) {
+    case OFPROTO_HANDLE_MISS_AUTO:
+        break;
+    case OFPROTO_HANDLE_MISS_WITH_FACETS:
+        return true;
+    case OFPROTO_HANDLE_MISS_WITHOUT_FACETS:
+        return false;
+    }
+
+    if (!backer->governor) {
         size_t n_subfacets;
 
-        n_subfacets = hmap_count(&ofproto->subfacets);
-        if (n_subfacets * 2 <= ofproto->up.flow_eviction_threshold) {
+        n_subfacets = hmap_count(&backer->subfacets);
+        if (n_subfacets * 2 <= flow_eviction_threshold) {
             return true;
         }
 
-        ofproto->governor = governor_create(ofproto->up.name);
+        backer->governor = governor_create();
     }
 
-    return governor_should_install_flow(ofproto->governor, hash,
+    hash = flow_hash_in_wildcards(&miss->flow, wc, 0);
+    return governor_should_install_flow(backer->governor, hash,
                                         list_size(&miss->packets));
 }
 
-/* Handles 'miss', which matches 'rule', without creating a facet or subfacet
- * or creating any datapath flow.  May add an "execute" operation to 'ops' and
- * increment '*n_ops'. */
+/* Handles 'miss' without creating a facet or subfacet or creating any datapath
+ * flow.  'miss->flow' must have matched 'rule' and been xlated into 'xout'.
+ * May add an "execute" operation to 'ops' and increment '*n_ops'. */
 static void
-handle_flow_miss_without_facet(struct flow_miss *miss,
-                               struct rule_dpif *rule,
+handle_flow_miss_without_facet(struct rule_dpif *rule, struct xlate_out *xout,
+                               struct flow_miss *miss,
                                struct flow_miss_op *ops, size_t *n_ops)
 {
-    struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
-    long long int now = time_msec();
-    struct action_xlate_ctx ctx;
     struct ofpbuf *packet;
 
     LIST_FOR_EACH (packet, list_node, &miss->packets) {
-        struct flow_miss_op *op = &ops[*n_ops];
-        struct dpif_flow_stats stats;
-        struct ofpbuf odp_actions;
 
         COVERAGE_INC(facet_suppress);
 
-        ofpbuf_use_stub(&odp_actions, op->stub, sizeof op->stub);
+        handle_flow_miss_common(rule, packet, &miss->flow);
 
-        dpif_flow_stats_extract(&miss->flow, packet, now, &stats);
-        rule_credit_stats(rule, &stats);
+        if (xout->slow) {
+            struct xlate_in xin;
 
-        action_xlate_ctx_init(&ctx, ofproto, &miss->flow,
-                              &miss->initial_vals, rule, 0, packet);
-        ctx.resubmit_stats = &stats;
-        xlate_actions(&ctx, rule->up.ofpacts, rule->up.ofpacts_len,
-                      &odp_actions);
+            xlate_in_init(&xin, miss->ofproto, &miss->flow, rule, 0, packet);
+            xlate_actions_for_side_effects(&xin);
+        }
 
-        if (odp_actions.size) {
+        if (xout->odp_actions.size) {
+            struct flow_miss_op *op = &ops[*n_ops];
             struct dpif_execute *execute = &op->dpif_op.u.execute;
 
             init_flow_miss_execute_op(miss, packet, op);
-            execute->actions = odp_actions.data;
-            execute->actions_len = odp_actions.size;
-            op->garbage = ofpbuf_get_uninit_pointer(&odp_actions);
+            xlate_out_copy(&op->xout, xout);
+            execute->actions = op->xout.odp_actions.data;
+            execute->actions_len = op->xout.odp_actions.size;
+            op->xout_garbage = true;
 
             (*n_ops)++;
-        } else {
-            ofpbuf_uninit(&odp_actions);
         }
     }
 }
@@ -3715,10 +3428,12 @@ handle_flow_miss_without_facet(struct flow_miss *miss,
  * here, then the new subfacet or its packets could look (occasionally) as
  * though it was used some time after the facet was used.  That can make a
  * one-packet flow look like it has a nonzero duration, which looks odd in
- * e.g. NetFlow statistics. */
+ * e.g. NetFlow statistics.
+ *
+ * If non-null, 'stats' will be folded into 'facet'. */
 static void
 handle_flow_miss_with_facet(struct flow_miss *miss, struct facet *facet,
-                            long long int now,
+                            long long int now, struct dpif_flow_stats *stats,
                             struct flow_miss_op *ops, size_t *n_ops)
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(facet->rule->up.ofproto);
@@ -3727,60 +3442,58 @@ handle_flow_miss_with_facet(struct flow_miss *miss, struct facet *facet,
     struct ofpbuf *packet;
 
     subfacet = subfacet_create(facet, miss, now);
+    want_path = facet->xout.slow ? SF_SLOW_PATH : SF_FAST_PATH;
+    if (stats) {
+        subfacet_update_stats(subfacet, stats);
+    }
 
     LIST_FOR_EACH (packet, list_node, &miss->packets) {
         struct flow_miss_op *op = &ops[*n_ops];
-        struct dpif_flow_stats stats;
-        struct ofpbuf odp_actions;
 
         handle_flow_miss_common(facet->rule, packet, &miss->flow);
 
-        ofpbuf_use_stub(&odp_actions, op->stub, sizeof op->stub);
-        if (!subfacet->actions || subfacet->slow) {
-            subfacet_make_actions(subfacet, packet, &odp_actions);
-        }
+        if (want_path != SF_FAST_PATH) {
+            struct xlate_in xin;
 
-        dpif_flow_stats_extract(&facet->flow, packet, now, &stats);
-        subfacet_update_stats(subfacet, &stats);
+            xlate_in_init(&xin, ofproto, &miss->flow, facet->rule, 0, packet);
+            xlate_actions_for_side_effects(&xin);
+        }
 
-        if (subfacet->actions_len) {
+        if (facet->xout.odp_actions.size) {
             struct dpif_execute *execute = &op->dpif_op.u.execute;
 
             init_flow_miss_execute_op(miss, packet, op);
-            if (!subfacet->slow) {
-                execute->actions = subfacet->actions;
-                execute->actions_len = subfacet->actions_len;
-                ofpbuf_uninit(&odp_actions);
-            } else {
-                execute->actions = odp_actions.data;
-                execute->actions_len = odp_actions.size;
-                op->garbage = ofpbuf_get_uninit_pointer(&odp_actions);
-            }
-
+            execute->actions = facet->xout.odp_actions.data,
+            execute->actions_len = facet->xout.odp_actions.size;
             (*n_ops)++;
-        } else {
-            ofpbuf_uninit(&odp_actions);
         }
     }
 
-    want_path = subfacet_want_path(subfacet->slow);
     if (miss->upcall_type == DPIF_UC_MISS || subfacet->path != want_path) {
         struct flow_miss_op *op = &ops[(*n_ops)++];
         struct dpif_flow_put *put = &op->dpif_op.u.flow_put;
 
         subfacet->path = want_path;
 
-        op->garbage = NULL;
+        ofpbuf_use_stack(&op->mask, &op->maskbuf, sizeof op->maskbuf);
+        odp_flow_key_from_mask(&op->mask, &facet->xout.wc.masks,
+                               &miss->flow, UINT32_MAX);
+
+        op->xout_garbage = false;
         op->dpif_op.type = DPIF_OP_FLOW_PUT;
+        op->subfacet = subfacet;
         put->flags = DPIF_FP_CREATE | DPIF_FP_MODIFY;
         put->key = miss->key;
         put->key_len = miss->key_len;
+        put->mask = op->mask.data;
+        put->mask_len = op->mask.size;
+
         if (want_path == SF_FAST_PATH) {
-            put->actions = subfacet->actions;
-            put->actions_len = subfacet->actions_len;
+            put->actions = facet->xout.odp_actions.data;
+            put->actions_len = facet->xout.odp_actions.size;
         } else {
-            compose_slow_path(ofproto, &facet->flow, subfacet->slow,
-                              op->stub, sizeof op->stub,
+            compose_slow_path(ofproto, &miss->flow, facet->xout.slow,
+                              op->slow_stub, sizeof op->slow_stub,
                               &put->actions, &put->actions_len);
         }
         put->stats = NULL;
@@ -3794,29 +3507,54 @@ handle_flow_miss(struct flow_miss *miss, struct flow_miss_op *ops,
                  size_t *n_ops)
 {
     struct ofproto_dpif *ofproto = miss->ofproto;
+    struct dpif_flow_stats stats__;
+    struct dpif_flow_stats *stats = &stats__;
+    struct ofpbuf *packet;
     struct facet *facet;
     long long int now;
-    uint32_t hash;
 
-    /* The caller must ensure that miss->hmap_node.hash contains
-     * flow_hash(miss->flow, 0). */
-    hash = miss->hmap_node.hash;
+    now = time_msec();
+    memset(stats, 0, sizeof *stats);
+    stats->used = now;
+    LIST_FOR_EACH (packet, list_node, &miss->packets) {
+        stats->tcp_flags |= packet_get_tcp_flags(packet, &miss->flow);
+        stats->n_bytes += packet->size;
+        stats->n_packets++;
+    }
 
-    facet = facet_lookup_valid(ofproto, &miss->flow, hash);
+    facet = facet_lookup_valid(ofproto, &miss->flow);
     if (!facet) {
-        struct rule_dpif *rule = rule_dpif_lookup(ofproto, &miss->flow);
-
-        if (!flow_miss_should_make_facet(ofproto, miss, hash)) {
-            handle_flow_miss_without_facet(miss, rule, ops, n_ops);
+        struct flow_wildcards wc;
+        struct rule_dpif *rule;
+        struct xlate_out xout;
+        struct xlate_in xin;
+
+        flow_wildcards_init_catchall(&wc);
+        rule = rule_dpif_lookup(ofproto, &miss->flow, &wc);
+        rule_credit_stats(rule, stats);
+
+        xlate_in_init(&xin, ofproto, &miss->flow, rule, stats->tcp_flags,
+                      NULL);
+        xin.resubmit_stats = stats;
+        xin.may_learn = true;
+        xlate_actions(&xin, &xout);
+        flow_wildcards_or(&xout.wc, &xout.wc, &wc);
+
+        /* There does not exist a bijection between 'struct flow' and datapath
+         * flow keys with fitness ODP_FIT_TO_LITTLE.  This breaks a fundamental
+         * assumption used throughout the facet and subfacet handling code.
+         * Since we have to handle these misses in userspace anyway, we simply
+         * skip facet creation, avoiding the problem altogether. */
+        if (miss->key_fitness == ODP_FIT_TOO_LITTLE
+            || !flow_miss_should_make_facet(miss, &xout.wc)) {
+            handle_flow_miss_without_facet(rule, &xout, miss, ops, n_ops);
             return;
         }
 
-        facet = facet_create(rule, &miss->flow, hash);
-        now = facet->used;
-    } else {
-        now = time_msec();
+        facet = facet_create(miss, rule, &xout, stats);
+        stats = NULL;
     }
-    handle_flow_miss_with_facet(miss, facet, now, ops, n_ops);
+    handle_flow_miss_with_facet(miss, facet, now, stats, ops, n_ops);
 }
 
 static struct drop_key *
@@ -3849,8 +3587,8 @@ drop_key_clear(struct dpif_backer *backer)
         if (error && !VLOG_DROP_WARN(&rl)) {
             struct ds ds = DS_EMPTY_INITIALIZER;
             odp_flow_key_format(drop_key->key, drop_key->key_len, &ds);
-            VLOG_WARN("Failed to delete drop key (%s) (%s)", strerror(error),
-                      ds_cstr(&ds));
+            VLOG_WARN("Failed to delete drop key (%s) (%s)",
+                      ovs_strerror(error), ds_cstr(&ds));
             ds_destroy(&ds);
         }
 
@@ -3877,12 +3615,6 @@ drop_key_clear(struct dpif_backer *backer)
  * 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 'initial_vals' is nonnull, sets 'initial_vals->vlan_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.)
- *
  * Similarly, this function also includes some logic to help with tunnels.  It
  * may modify 'flow' as necessary to make the tunneling implementation
  * transparent to the upcall processing logic.
@@ -3893,8 +3625,7 @@ 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,
-                struct initial_vals *initial_vals)
+                struct ofproto_dpif **ofproto, odp_port_t *odp_in_port)
 {
     const struct ofport_dpif *port;
     enum odp_key_fitness fitness;
@@ -3906,18 +3637,14 @@ ofproto_receive(const struct dpif_backer *backer, struct ofpbuf *packet,
         goto exit;
     }
 
-    if (initial_vals) {
-        initial_vals->vlan_tci = flow->vlan_tci;
-    }
-
     if (odp_in_port) {
-        *odp_in_port = flow->in_port;
+        *odp_in_port = flow->in_port.odp_port;
     }
 
     port = (tnl_port_should_receive(flow)
-            ? ofport_dpif_cast(tnl_port_receive(flow))
-            : odp_port_to_ofport(backer, flow->in_port));
-    flow->in_port = port ? port->up.ofp_port : OFPP_NONE;
+            ? tnl_port_receive(flow)
+            : odp_port_to_ofport(backer, flow->in_port.odp_port));
+    flow->in_port.ofp_port = port ? port->up.ofp_port : OFPP_NONE;
     if (!port) {
         goto exit;
     }
@@ -3992,25 +3719,25 @@ handle_miss_upcalls(struct dpif_backer *backer, struct dpif_upcall *upcalls,
         struct flow_miss *miss = &misses[n_misses];
         struct flow_miss *existing_miss;
         struct ofproto_dpif *ofproto;
-        uint32_t odp_in_port;
+        odp_port_t odp_in_port;
         struct flow flow;
         uint32_t hash;
         int error;
 
         error = ofproto_receive(backer, upcall->packet, upcall->key,
                                 upcall->key_len, &flow, &miss->key_fitness,
-                                &ofproto, &odp_in_port, &miss->initial_vals);
+                                &ofproto, &odp_in_port);
         if (error == ENODEV) {
             struct drop_key *drop_key;
 
-            /* 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
+            /* Received packet on datapath 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.  Install a drop flow so
              * that future packets of the flow are inexpensively dropped
              * in the kernel. */
-            VLOG_INFO_RL(&rl, "received packet on unassociated port %"PRIu32,
-                         flow.in_port);
+            VLOG_INFO_RL(&rl, "received packet on unassociated datapath port "
+                              "%"PRIu32, odp_in_port);
 
             drop_key = drop_key_lookup(backer, upcall->key, upcall->key_len);
             if (!drop_key) {
@@ -4021,7 +3748,8 @@ handle_miss_upcalls(struct dpif_backer *backer, struct dpif_upcall *upcalls,
                 hmap_insert(&backer->drop_keys, &drop_key->hmap_node,
                             hash_bytes(drop_key->key, drop_key->key_len, 0));
                 dpif_flow_put(backer->dpif, DPIF_FP_CREATE | DPIF_FP_MODIFY,
-                              drop_key->key, drop_key->key_len, NULL, 0, NULL);
+                              drop_key->key, drop_key->key_len,
+                              NULL, 0, NULL, 0, NULL);
             }
             continue;
         }
@@ -4031,7 +3759,7 @@ handle_miss_upcalls(struct dpif_backer *backer, struct dpif_upcall *upcalls,
 
         ofproto->n_missed++;
         flow_extract(upcall->packet, flow.skb_priority, flow.skb_mark,
-                     &flow.tunnel, flow.in_port, &miss->flow);
+                     &flow.tunnel, &flow.in_port, &miss->flow);
 
         /* Add other packets to a to-do list. */
         hash = flow_hash(&miss->flow, 0);
@@ -4042,7 +3770,6 @@ handle_miss_upcalls(struct dpif_backer *backer, struct dpif_upcall *upcalls,
             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++;
@@ -4066,9 +3793,21 @@ handle_miss_upcalls(struct dpif_backer *backer, struct dpif_upcall *upcalls,
     }
     dpif_operate(backer->dpif, dpif_ops, n_ops);
 
-    /* Free memory. */
     for (i = 0; i < n_ops; i++) {
-        free(flow_miss_ops[i].garbage);
+        if (dpif_ops[i]->error != 0
+            && flow_miss_ops[i].dpif_op.type == DPIF_OP_FLOW_PUT
+            && flow_miss_ops[i].subfacet) {
+            struct subfacet *subfacet = flow_miss_ops[i].subfacet;
+
+            COVERAGE_INC(subfacet_install_fail);
+
+            subfacet->path = SF_NOT_INSTALLED;
+        }
+
+        /* Free memory. */
+        if (flow_miss_ops[i].xout_garbage) {
+            xlate_out_uninit(&flow_miss_ops[i].xout);
+        }
     }
     hmap_destroy(&todo);
 }
@@ -4134,10 +3873,10 @@ handle_sflow_upcall(struct dpif_backer *backer,
     struct ofproto_dpif *ofproto;
     union user_action_cookie cookie;
     struct flow flow;
-    uint32_t odp_in_port;
+    odp_port_t odp_in_port;
 
     if (ofproto_receive(backer, upcall->packet, upcall->key, upcall->key_len,
-                        &flow, NULL, &ofproto, &odp_in_port, NULL)
+                        &flow, NULL, &ofproto, &odp_in_port)
         || !ofproto->sflow) {
         return;
     }
@@ -4157,7 +3896,7 @@ handle_flow_sample_upcall(struct dpif_backer *backer,
     struct flow flow;
 
     if (ofproto_receive(backer, upcall->packet, upcall->key, upcall->key_len,
-                        &flow, NULL, &ofproto, NULL, NULL)
+                        &flow, NULL, &ofproto, NULL)
         || !ofproto->ipfix) {
         return;
     }
@@ -4182,7 +3921,7 @@ handle_ipfix_upcall(struct dpif_backer *backer,
     struct flow flow;
 
     if (ofproto_receive(backer, upcall->packet, upcall->key, upcall->key_len,
-                        &flow, NULL, &ofproto, NULL, NULL)
+                        &flow, NULL, &ofproto, NULL)
         || !ofproto->ipfix) {
         return;
     }
@@ -4256,10 +3995,10 @@ handle_upcalls(struct dpif_backer *backer, unsigned int max_batch)
 \f
 /* Flow expiration. */
 
-static int subfacet_max_idle(const struct ofproto_dpif *);
+static int subfacet_max_idle(const struct dpif_backer *);
 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);
+static void expire_subfacets(struct dpif_backer *, int dp_max_idle);
 
 /* This function is called periodically by run().  Its job is to collect
  * updates for the flows that have been installed into the datapath, most
@@ -4271,7 +4010,8 @@ static int
 expire(struct dpif_backer *backer)
 {
     struct ofproto_dpif *ofproto;
-    int max_idle = INT32_MAX;
+    size_t n_subfacets;
+    int max_idle;
 
     /* Periodically clear out the drop keys in an effort to keep them
      * relatively few. */
@@ -4280,23 +4020,35 @@ expire(struct dpif_backer *backer)
     /* Update stats for each flow in the backer. */
     update_stats(backer);
 
+    n_subfacets = hmap_count(&backer->subfacets);
+    if (n_subfacets) {
+        struct subfacet *subfacet;
+        long long int total, now;
+
+        total = 0;
+        now = time_msec();
+        HMAP_FOR_EACH (subfacet, hmap_node, &backer->subfacets) {
+            total += now - subfacet->created;
+        }
+        backer->avg_subfacet_life += total / n_subfacets;
+    }
+    backer->avg_subfacet_life /= 2;
+
+    backer->avg_n_subfacet += n_subfacets;
+    backer->avg_n_subfacet /= 2;
+
+    backer->max_n_subfacet = MAX(backer->max_n_subfacet, n_subfacets);
+
+    max_idle = subfacet_max_idle(backer);
+    expire_subfacets(backer, max_idle);
+
     HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
         struct rule *rule, *next_rule;
-        int dp_max_idle;
 
         if (ofproto->backer != backer) {
             continue;
         }
 
-        /* Keep track of the max number of flows per ofproto_dpif. */
-        update_max_subfacet_count(ofproto);
-
-        /* 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. */
         LIST_FOR_EACH_SAFE (rule, next_rule, expirable,
@@ -4327,26 +4079,31 @@ update_subfacet_stats(struct subfacet *subfacet,
                       const struct dpif_flow_stats *stats)
 {
     struct facet *facet = subfacet->facet;
+    struct ofproto_dpif *ofproto = ofproto_dpif_cast(facet->rule->up.ofproto);
+    struct dpif_flow_stats diff;
+
+    diff.tcp_flags = stats->tcp_flags;
+    diff.used = stats->used;
 
     if (stats->n_packets >= subfacet->dp_packet_count) {
-        uint64_t extra = stats->n_packets - subfacet->dp_packet_count;
-        facet->packet_count += extra;
+        diff.n_packets = stats->n_packets - subfacet->dp_packet_count;
     } else {
         VLOG_WARN_RL(&rl, "unexpected packet count from the datapath");
+        diff.n_packets = 0;
     }
 
     if (stats->n_bytes >= subfacet->dp_byte_count) {
-        facet->byte_count += stats->n_bytes - subfacet->dp_byte_count;
+        diff.n_bytes = stats->n_bytes - subfacet->dp_byte_count;
     } else {
         VLOG_WARN_RL(&rl, "unexpected byte count from datapath");
+        diff.n_bytes = 0;
     }
 
+    ofproto->n_hit += diff.n_packets;
     subfacet->dp_packet_count = stats->n_packets;
     subfacet->dp_byte_count = stats->n_bytes;
+    subfacet_update_stats(subfacet, &diff);
 
-    facet->tcp_flags |= stats->tcp_flags;
-
-    subfacet_update_time(subfacet, stats->used);
     if (facet->accounted_bytes < facet->byte_count) {
         facet_learn(facet);
         facet_account(facet);
@@ -4357,7 +4114,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 ofproto_dpif *ofproto,
+delete_unexpected_flow(struct dpif_backer *backer,
                        const struct nlattr *key, size_t key_len)
 {
     if (!VLOG_DROP_WARN(&rl)) {
@@ -4365,12 +4122,12 @@ delete_unexpected_flow(struct ofproto_dpif *ofproto,
 
         ds_init(&s);
         odp_flow_key_format(key, key_len, &s);
-        VLOG_WARN("unexpected flow on %s: %s", ofproto->up.name, ds_cstr(&s));
+        VLOG_WARN("unexpected flow: %s", ds_cstr(&s));
         ds_destroy(&s);
     }
 
     COVERAGE_INC(facet_unexpected);
-    dpif_flow_del(ofproto->backer->dpif, key, key_len, NULL);
+    dpif_flow_del(backer->dpif, key, key_len, NULL);
 }
 
 /* Update 'packet_count', 'byte_count', and 'used' members of installed facets.
@@ -4394,40 +4151,19 @@ update_stats(struct dpif_backer *backer)
 {
     const struct dpif_flow_stats *stats;
     struct dpif_flow_dump dump;
-    const struct nlattr *key;
-    struct ofproto_dpif *ofproto;
-    size_t key_len;
+    const struct nlattr *key, *mask;
+    size_t key_len, mask_len;
 
     dpif_flow_dump_start(&dump, backer->dpif);
-    while (dpif_flow_dump_next(&dump, &key, &key_len, NULL, NULL, &stats)) {
-        struct flow flow;
+    while (dpif_flow_dump_next(&dump, &key, &key_len,
+                               &mask, &mask_len, NULL, NULL, &stats)) {
         struct subfacet *subfacet;
-        struct ofport_dpif *ofport;
         uint32_t key_hash;
 
-        if (ofproto_receive(backer, NULL, key, key_len, &flow, NULL, &ofproto,
-                            NULL, NULL)) {
-            continue;
-        }
-
-        ofproto->total_subfacet_count += hmap_count(&ofproto->subfacets);
-        ofproto->n_update_stats++;
-
-        ofport = get_ofp_port(ofproto, flow.in_port);
-        if (ofport && ofport->tnl_port) {
-            netdev_vport_inc_rx(ofport->up.netdev, stats);
-        }
-
         key_hash = odp_flow_key_hash(key, key_len);
-        subfacet = subfacet_find(ofproto, key, key_len, key_hash);
+        subfacet = subfacet_find(backer, key, key_len, key_hash);
         switch (subfacet ? subfacet->path : SF_NOT_INSTALLED) {
         case SF_FAST_PATH:
-            /* Update ofproto_dpif's hit count. */
-            if (stats->n_packets > subfacet->dp_packet_count) {
-                uint64_t delta = stats->n_packets - subfacet->dp_packet_count;
-                dpif_stats_update_hit_count(ofproto, delta);
-            }
-
             update_subfacet_stats(subfacet, stats);
             break;
 
@@ -4437,17 +4173,14 @@ update_stats(struct dpif_backer *backer)
 
         case SF_NOT_INSTALLED:
         default:
-            delete_unexpected_flow(ofproto, key, key_len);
+            delete_unexpected_flow(backer, key, key_len);
             break;
         }
         run_fast_rl();
     }
     dpif_flow_dump_done(&dump);
 
-    HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
-        update_moving_averages(ofproto);
-    }
-
+    update_moving_averages(backer);
 }
 
 /* Calculates and returns the number of milliseconds of idle time after which
@@ -4455,7 +4188,7 @@ update_stats(struct dpif_backer *backer)
  * its statistics into its facet, and when a facet's last subfacet expires, we
  * fold its statistic into its rule. */
 static int
-subfacet_max_idle(const struct ofproto_dpif *ofproto)
+subfacet_max_idle(const struct dpif_backer *backer)
 {
     /*
      * Idle time histogram.
@@ -4478,7 +4211,7 @@ subfacet_max_idle(const struct ofproto_dpif *ofproto)
      * that is installed in the kernel gets dropped in the appropriate bucket.
      * After the histogram has been built, we compute the cutoff so that only
      * the most-recently-used 1% of subfacets (but at least
-     * ofproto->up.flow_eviction_threshold flows) are kept cached.  At least
+     * flow_eviction_threshold flows) are kept cached.  At least
      * the most-recently-used bucket of subfacets is kept, so actually an
      * arbitrary number of subfacets can be kept in any given expiration run
      * (though the next run will delete most of those unless they receive
@@ -4496,14 +4229,14 @@ subfacet_max_idle(const struct ofproto_dpif *ofproto)
     long long int now;
     int i;
 
-    total = hmap_count(&ofproto->subfacets);
-    if (total <= ofproto->up.flow_eviction_threshold) {
+    total = hmap_count(&backer->subfacets);
+    if (total <= flow_eviction_threshold) {
         return N_BUCKETS * BUCKET_WIDTH;
     }
 
     /* Build histogram. */
     now = time_msec();
-    HMAP_FOR_EACH (subfacet, hmap_node, &ofproto->subfacets) {
+    HMAP_FOR_EACH (subfacet, hmap_node, &backer->subfacets) {
         long long int idle = now - subfacet->used;
         int bucket = (idle <= 0 ? 0
                       : idle >= BUCKET_WIDTH * N_BUCKETS ? N_BUCKETS - 1
@@ -4516,7 +4249,7 @@ subfacet_max_idle(const struct ofproto_dpif *ofproto)
     do {
         subtotal += buckets[bucket++];
     } while (bucket < N_BUCKETS &&
-             subtotal < MAX(ofproto->up.flow_eviction_threshold, total / 100));
+             subtotal < MAX(flow_eviction_threshold, total / 100));
 
     if (VLOG_IS_DBG_ENABLED()) {
         struct ds s;
@@ -4531,7 +4264,7 @@ subfacet_max_idle(const struct ofproto_dpif *ofproto)
                 ds_put_format(&s, " %d:%d", i * BUCKET_WIDTH, buckets[i]);
             }
         }
-        VLOG_INFO("%s: %s (msec:count)", ofproto->up.name, ds_cstr(&s));
+        VLOG_INFO("%s (msec:count)", ds_cstr(&s));
         ds_destroy(&s);
     }
 
@@ -4539,7 +4272,7 @@ subfacet_max_idle(const struct ofproto_dpif *ofproto)
 }
 
 static void
-expire_subfacets(struct ofproto_dpif *ofproto, int dp_max_idle)
+expire_subfacets(struct dpif_backer *backer, int dp_max_idle)
 {
     /* Cutoff time for most flows. */
     long long int normal_cutoff = time_msec() - dp_max_idle;
@@ -4554,17 +4287,18 @@ expire_subfacets(struct ofproto_dpif *ofproto, int dp_max_idle)
 
     n_batch = 0;
     HMAP_FOR_EACH_SAFE (subfacet, next_subfacet, hmap_node,
-                        &ofproto->subfacets) {
+                        &backer->subfacets) {
         long long int cutoff;
 
-        cutoff = (subfacet->slow & (SLOW_CFM | SLOW_BFD | SLOW_LACP | SLOW_STP)
+        cutoff = (subfacet->facet->xout.slow & (SLOW_CFM | SLOW_BFD | SLOW_LACP
+                                                | SLOW_STP)
                   ? special_cutoff
                   : normal_cutoff);
         if (subfacet->used < cutoff) {
             if (subfacet->path != SF_NOT_INSTALLED) {
                 batch[n_batch++] = subfacet;
                 if (n_batch >= SUBFACET_DESTROY_MAX_BATCH) {
-                    subfacet_destroy_batch(ofproto, batch, n_batch);
+                    subfacet_destroy_batch(backer, batch, n_batch);
                     n_batch = 0;
                 }
             } else {
@@ -4574,7 +4308,7 @@ expire_subfacets(struct ofproto_dpif *ofproto, int dp_max_idle)
     }
 
     if (n_batch > 0) {
-        subfacet_destroy_batch(ofproto, batch, n_batch);
+        subfacet_destroy_batch(backer, batch, n_batch);
     }
 }
 
@@ -4618,33 +4352,46 @@ rule_expire(struct rule_dpif *rule)
 \f
 /* Facets. */
 
-/* Creates and returns a new facet owned by 'rule', given a 'flow'.
+/* Creates and returns a new facet based on 'miss'.
  *
  * The caller must already have determined that no facet with an identical
- * 'flow' exists in 'ofproto' and that 'flow' is the best match for 'rule' in
- * the ofproto's classifier table.
+ * 'miss->flow' exists in 'miss->ofproto'.
+ *
+ * 'rule' and 'xout' must have been created based on 'miss'.
  *
- * 'hash' must be the return value of flow_hash(flow, 0).
+ * 'facet'' statistics are initialized based on 'stats'.
  *
  * The facet will initially have no subfacets.  The caller should create (at
  * least) one subfacet with subfacet_create(). */
 static struct facet *
-facet_create(struct rule_dpif *rule, const struct flow *flow, uint32_t hash)
+facet_create(const struct flow_miss *miss, struct rule_dpif *rule,
+             struct xlate_out *xout, struct dpif_flow_stats *stats)
 {
-    struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
+    struct ofproto_dpif *ofproto = miss->ofproto;
     struct facet *facet;
+    struct match match;
 
     facet = xzalloc(sizeof *facet);
-    facet->used = time_msec();
-    hmap_insert(&ofproto->facets, &facet->hmap_node, hash);
-    list_push_back(&rule->facets, &facet->list_node);
+    facet->packet_count = facet->prev_packet_count = stats->n_packets;
+    facet->byte_count = facet->prev_byte_count = stats->n_bytes;
+    facet->tcp_flags = stats->tcp_flags;
+    facet->used = stats->used;
+    facet->flow = miss->flow;
+    facet->learn_rl = time_msec() + 500;
     facet->rule = rule;
-    facet->flow = *flow;
+
+    list_push_back(&facet->rule->facets, &facet->list_node);
     list_init(&facet->subfacets);
     netflow_flow_init(&facet->nf_flow);
     netflow_flow_update_time(ofproto->netflow, &facet->nf_flow, facet->used);
 
-    facet->learn_rl = time_msec() + 500;
+    xlate_out_copy(&facet->xout, xout);
+
+    match_init(&match, &facet->flow, &facet->xout.wc);
+    cls_rule_init(&facet->cr, &match, OFP_DEFAULT_PRIORITY);
+    classifier_insert(&ofproto->facets, &facet->cr);
+
+    facet->nf_flow.output_iface = facet->xout.nf_output_iface;
 
     return facet;
 }
@@ -4652,7 +4399,10 @@ facet_create(struct rule_dpif *rule, const struct flow *flow, uint32_t hash)
 static void
 facet_free(struct facet *facet)
 {
-    free(facet);
+    if (facet) {
+        xlate_out_uninit(&facet->xout);
+        free(facet);
+    }
 }
 
 /* Executes, within 'ofproto', the 'n_actions' actions in 'actions' on
@@ -4668,14 +4418,14 @@ execute_odp_actions(struct ofproto_dpif *ofproto, const struct flow *flow,
 
     ofpbuf_use_stack(&key, &keybuf, sizeof keybuf);
     odp_flow_key_from_flow(&key, flow,
-                           ofp_port_to_odp_port(ofproto, flow->in_port));
+                           ofp_port_to_odp_port(ofproto, flow->in_port.ofp_port));
 
     error = dpif_execute(ofproto->backer->dpif, key.data, key.size,
                          odp_actions, actions_len, packet);
     return !error;
 }
 
-/* Remove 'facet' from 'ofproto' and free up the associated memory:
+/* Remove 'facet' from its ofproto and free up the associated memory:
  *
  *   - If 'facet' was installed in the datapath, uninstalls it and updates its
  *     rule's statistics, via subfacet_uninstall().
@@ -4707,7 +4457,8 @@ facet_remove(struct facet *facet)
                         &facet->subfacets) {
         subfacet_destroy__(subfacet);
     }
-    hmap_remove(&ofproto->facets, &facet->hmap_node);
+    classifier_remove(&ofproto->facets, &facet->cr);
+    cls_rule_destroy(&facet->cr);
     list_remove(&facet->list_node);
     facet_free(facet);
 }
@@ -4717,44 +4468,34 @@ facet_remove(struct facet *facet)
 static void
 facet_learn(struct facet *facet)
 {
-    struct ofproto_dpif *ofproto = ofproto_dpif_cast(facet->rule->up.ofproto);
-    struct subfacet *subfacet= CONTAINER_OF(list_front(&facet->subfacets),
-                                            struct subfacet, list_node);
     long long int now = time_msec();
-    struct action_xlate_ctx ctx;
 
-    if (!facet->has_fin_timeout && now < facet->learn_rl) {
+    if (!facet->xout.has_fin_timeout && now < facet->learn_rl) {
         return;
     }
 
     facet->learn_rl = now + 500;
 
-    if (!facet->has_learn
-        && !facet->has_normal
-        && (!facet->has_fin_timeout
+    if (!facet->xout.has_learn
+        && !facet->xout.has_normal
+        && (!facet->xout.has_fin_timeout
             || !(facet->tcp_flags & (TCP_FIN | TCP_RST)))) {
         return;
     }
 
-    action_xlate_ctx_init(&ctx, ofproto, &facet->flow,
-                          &subfacet->initial_vals,
-                          facet->rule, facet->tcp_flags, NULL);
-    ctx.may_learn = true;
-    xlate_actions_for_side_effects(&ctx, facet->rule->up.ofpacts,
-                                   facet->rule->up.ofpacts_len);
+    facet_push_stats(facet, true);
 }
 
 static void
 facet_account(struct facet *facet)
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(facet->rule->up.ofproto);
-    struct subfacet *subfacet = facet_get_subfacet(facet);
     const struct nlattr *a;
     unsigned int left;
     ovs_be16 vlan_tci;
     uint64_t n_bytes;
 
-    if (!facet->has_normal || !ofproto->has_bonded_bundles) {
+    if (!facet->xout.has_normal || !ofproto->has_bonded_bundles) {
         return;
     }
     n_bytes = facet->byte_count - facet->accounted_bytes;
@@ -4768,14 +4509,14 @@ facet_account(struct facet *facet)
      * We use the actions from an arbitrary subfacet because they should all
      * be equally valid for our purpose. */
     vlan_tci = facet->flow.vlan_tci;
-    NL_ATTR_FOR_EACH_UNSAFE (a, left,
-                             subfacet->actions, subfacet->actions_len) {
+    NL_ATTR_FOR_EACH_UNSAFE (a, left, facet->xout.odp_actions.data,
+                             facet->xout.odp_actions.size) {
         const struct ovs_action_push_vlan *vlan;
         struct ofport_dpif *port;
 
         switch (nl_attr_type(a)) {
         case OVS_ACTION_ATTR_OUTPUT:
-            port = get_odp_port(ofproto, nl_attr_get_u32(a));
+            port = get_odp_port(ofproto, nl_attr_get_odp_port(a));
             if (port && port->bundle && port->bundle->bond) {
                 bond_account(port->bundle->bond, &facet->flow,
                              vlan_tci_to_vid(vlan_tci), n_bytes);
@@ -4829,7 +4570,7 @@ facet_flush_stats(struct facet *facet)
         ovs_assert(!subfacet->dp_packet_count);
     }
 
-    facet_push_stats(facet);
+    facet_push_stats(facet, false);
     if (facet->accounted_bytes < facet->byte_count) {
         facet_account(facet);
         facet->accounted_bytes = facet->byte_count;
@@ -4852,100 +4593,39 @@ facet_flush_stats(struct facet *facet)
     facet->tcp_flags = 0;
 }
 
-/* Searches 'ofproto''s table of facets for one exactly equal to 'flow'.
- * Returns it if found, otherwise a null pointer.
- *
- * 'hash' must be the return value of flow_hash(flow, 0).
+/* Searches 'ofproto''s table of facets for one which would be responsible for
+ * 'flow'.  Returns it if found, otherwise a null pointer.
  *
  * The returned facet might need revalidation; use facet_lookup_valid()
  * instead if that is important. */
 static struct facet *
-facet_find(struct ofproto_dpif *ofproto,
-           const struct flow *flow, uint32_t hash)
+facet_find(struct ofproto_dpif *ofproto, const struct flow *flow)
 {
-    struct facet *facet;
-
-    HMAP_FOR_EACH_WITH_HASH (facet, hmap_node, hash, &ofproto->facets) {
-        if (flow_equal(flow, &facet->flow)) {
-            return facet;
-        }
-    }
-
-    return NULL;
+    struct cls_rule *cr = classifier_lookup(&ofproto->facets, flow, NULL);
+    return cr ? CONTAINER_OF(cr, struct facet, cr) : NULL;
 }
 
-/* Searches 'ofproto''s table of facets for one exactly equal to 'flow'.
- * Returns it if found, otherwise a null pointer.
- *
- * 'hash' must be the return value of flow_hash(flow, 0).
+/* Searches 'ofproto''s table of facets for one capable that covers
+ * 'flow'.  Returns it if found, otherwise a null pointer.
  *
  * The returned facet is guaranteed to be valid. */
 static struct facet *
-facet_lookup_valid(struct ofproto_dpif *ofproto, const struct flow *flow,
-                   uint32_t hash)
+facet_lookup_valid(struct ofproto_dpif *ofproto, const struct flow *flow)
 {
     struct facet *facet;
 
-    facet = facet_find(ofproto, flow, hash);
+    facet = facet_find(ofproto, flow);
     if (facet
         && (ofproto->backer->need_revalidate
             || tag_set_intersects(&ofproto->backer->revalidate_set,
-                                  facet->tags))) {
-        facet_revalidate(facet);
-
-        /* facet_revalidate() may have destroyed 'facet'. */
-        facet = facet_find(ofproto, flow, hash);
+                                  facet->xout.tags))
+        && !facet_revalidate(facet)) {
+        return NULL;
     }
 
     return facet;
 }
 
-/* Return a subfacet from 'facet'.  A facet consists of one or more
- * subfacets, and this function returns one of them. */
-static struct subfacet *facet_get_subfacet(struct facet *facet)
-{
-    return CONTAINER_OF(list_front(&facet->subfacets), struct subfacet,
-                        list_node);
-}
-
-static const char *
-subfacet_path_to_string(enum subfacet_path path)
-{
-    switch (path) {
-    case SF_NOT_INSTALLED:
-        return "not installed";
-    case SF_FAST_PATH:
-        return "in fast path";
-    case SF_SLOW_PATH:
-        return "in slow path";
-    default:
-        return "<error>";
-    }
-}
-
-/* Returns the path in which a subfacet should be installed if its 'slow'
- * member has the specified value. */
-static enum subfacet_path
-subfacet_want_path(enum slow_path_reason slow)
-{
-    return slow ? SF_SLOW_PATH : SF_FAST_PATH;
-}
-
-/* Returns true if 'subfacet' needs to have its datapath flow updated,
- * supposing that its actions have been recalculated as 'want_actions' and that
- * 'slow' is nonzero iff 'subfacet' should be in the slow path. */
-static bool
-subfacet_should_install(struct subfacet *subfacet, enum slow_path_reason slow,
-                        const struct ofpbuf *want_actions)
-{
-    enum subfacet_path want_path = subfacet_want_path(slow);
-    return (want_path != subfacet->path
-            || (want_path == SF_FAST_PATH
-                && (subfacet->actions_len != want_actions->size
-                    || memcmp(subfacet->actions, want_actions->data,
-                              subfacet->actions_len))));
-}
-
 static bool
 facet_check_consistency(struct facet *facet)
 {
@@ -4953,23 +4633,18 @@ facet_check_consistency(struct facet *facet)
 
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(facet->rule->up.ofproto);
 
-    uint64_t odp_actions_stub[1024 / 8];
-    struct ofpbuf odp_actions;
+    struct xlate_out xout;
+    struct xlate_in xin;
 
     struct rule_dpif *rule;
-    struct subfacet *subfacet;
-    bool may_log = false;
     bool ok;
 
     /* Check the rule for consistency. */
-    rule = rule_dpif_lookup(ofproto, &facet->flow);
-    ok = rule == facet->rule;
-    if (!ok) {
-        may_log = !VLOG_DROP_WARN(&rl);
-        if (may_log) {
-            struct ds s;
-
-            ds_init(&s);
+    rule = rule_dpif_lookup(ofproto, &facet->flow, NULL);
+    if (rule != facet->rule) {
+        if (!VLOG_DROP_WARN(&rl)) {
+            struct ds s = DS_EMPTY_INITIALIZER;
+
             flow_format(&s, &facet->flow);
             ds_put_format(&s, ": facet associated with wrong rule (was "
                           "table=%"PRIu8",", facet->rule->up.table_id);
@@ -4982,76 +4657,39 @@ facet_check_consistency(struct facet *facet)
             VLOG_WARN("%s", ds_cstr(&s));
             ds_destroy(&s);
         }
+        return false;
     }
 
     /* Check the datapath actions for consistency. */
-    ofpbuf_use_stub(&odp_actions, odp_actions_stub, sizeof odp_actions_stub);
-    LIST_FOR_EACH (subfacet, list_node, &facet->subfacets) {
-        enum subfacet_path want_path;
-        struct action_xlate_ctx ctx;
-        struct ds s;
-
-        action_xlate_ctx_init(&ctx, ofproto, &facet->flow,
-                              &subfacet->initial_vals, rule, 0, NULL);
-        xlate_actions(&ctx, rule->up.ofpacts, rule->up.ofpacts_len,
-                      &odp_actions);
-
-        if (subfacet->path == SF_NOT_INSTALLED) {
-            /* This only happens if the datapath reported an error when we
-             * tried to install the flow.  Don't flag another error here. */
-            continue;
-        }
-
-        want_path = subfacet_want_path(subfacet->slow);
-        if (want_path == SF_SLOW_PATH && subfacet->path == SF_SLOW_PATH) {
-            /* The actions for slow-path flows may legitimately vary from one
-             * packet to the next.  We're done. */
-            continue;
-        }
+    xlate_in_init(&xin, ofproto, &facet->flow, rule, 0, NULL);
+    xlate_actions(&xin, &xout);
 
-        if (!subfacet_should_install(subfacet, subfacet->slow, &odp_actions)) {
-            continue;
-        }
+    ok = ofpbuf_equal(&facet->xout.odp_actions, &xout.odp_actions)
+        && facet->xout.slow == xout.slow;
+    if (!ok && !VLOG_DROP_WARN(&rl)) {
+        struct ds s = DS_EMPTY_INITIALIZER;
 
-        /* Inconsistency! */
-        if (ok) {
-            may_log = !VLOG_DROP_WARN(&rl);
-            ok = false;
-        }
-        if (!may_log) {
-            /* Rate-limited, skip reporting. */
-            continue;
-        }
+        flow_format(&s, &facet->flow);
+        ds_put_cstr(&s, ": inconsistency in facet");
 
-        ds_init(&s);
-        odp_flow_key_format(subfacet->key, subfacet->key_len, &s);
-
-        ds_put_cstr(&s, ": inconsistency in subfacet");
-        if (want_path != subfacet->path) {
-            enum odp_key_fitness fitness = subfacet->key_fitness;
-
-            ds_put_format(&s, " (%s, fitness=%s)",
-                          subfacet_path_to_string(subfacet->path),
-                          odp_key_fitness_to_string(fitness));
-            ds_put_format(&s, " (should have been %s)",
-                          subfacet_path_to_string(want_path));
-        } else if (want_path == SF_FAST_PATH) {
+        if (!ofpbuf_equal(&facet->xout.odp_actions, &xout.odp_actions)) {
             ds_put_cstr(&s, " (actions were: ");
-            format_odp_actions(&s, subfacet->actions,
-                               subfacet->actions_len);
+            format_odp_actions(&s, facet->xout.odp_actions.data,
+                               facet->xout.odp_actions.size);
             ds_put_cstr(&s, ") (correct actions: ");
-            format_odp_actions(&s, odp_actions.data, odp_actions.size);
-            ds_put_char(&s, ')');
-        } else {
-            ds_put_cstr(&s, " (actions: ");
-            format_odp_actions(&s, subfacet->actions,
-                               subfacet->actions_len);
+            format_odp_actions(&s, xout.odp_actions.data,
+                               xout.odp_actions.size);
             ds_put_char(&s, ')');
         }
+
+        if (facet->xout.slow != xout.slow) {
+            ds_put_format(&s, " slow path incorrect. should be %d", xout.slow);
+        }
+
         VLOG_WARN("%s", ds_cstr(&s));
         ds_destroy(&s);
     }
-    ofpbuf_uninit(&odp_actions);
+    xlate_out_uninit(&xout);
 
     return ok;
 }
@@ -5065,24 +4703,18 @@ facet_check_consistency(struct facet *facet)
  *     where it is and recompiles its actions anyway.
  *
  *   - If any of 'facet''s subfacets correspond to a new flow according to
- *     ofproto_receive(), 'facet' is removed. */
-static void
+ *     ofproto_receive(), 'facet' is removed.
+ *
+ *   Returns true if 'facet' is still valid.  False if 'facet' was removed. */
+static bool
 facet_revalidate(struct facet *facet)
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(facet->rule->up.ofproto);
-    struct actions {
-        struct nlattr *odp_actions;
-        size_t actions_len;
-    };
-    struct actions *new_actions;
-
-    struct action_xlate_ctx ctx;
-    uint64_t odp_actions_stub[1024 / 8];
-    struct ofpbuf odp_actions;
-
     struct rule_dpif *new_rule;
     struct subfacet *subfacet;
-    int i;
+    struct flow_wildcards wc;
+    struct xlate_out xout;
+    struct xlate_in xin;
 
     COVERAGE_INC(facet_revalidate);
 
@@ -5096,82 +4728,68 @@ facet_revalidate(struct facet *facet)
 
         error = ofproto_receive(ofproto->backer, NULL, subfacet->key,
                                 subfacet->key_len, &recv_flow, NULL,
-                                &recv_ofproto, NULL, NULL);
+                                &recv_ofproto, NULL);
         if (error
             || recv_ofproto != ofproto
-            || memcmp(&recv_flow, &facet->flow, sizeof recv_flow)) {
+            || facet != facet_find(ofproto, &recv_flow)) {
             facet_remove(facet);
-            return;
+            return false;
         }
     }
 
-    new_rule = rule_dpif_lookup(ofproto, &facet->flow);
+    flow_wildcards_init_catchall(&wc);
+    new_rule = rule_dpif_lookup(ofproto, &facet->flow, &wc);
 
     /* Calculate new datapath actions.
      *
      * We do not modify any 'facet' state yet, because we might need to, e.g.,
      * emit a NetFlow expiration and, if so, we need to have the old state
      * around to properly compose it. */
+    xlate_in_init(&xin, ofproto, &facet->flow, new_rule, 0, NULL);
+    xlate_actions(&xin, &xout);
+    flow_wildcards_or(&xout.wc, &xout.wc, &wc);
 
-    /* If the datapath actions changed or the installability changed,
-     * then we need to talk to the datapath. */
-    i = 0;
-    new_actions = NULL;
-    memset(&ctx, 0, sizeof ctx);
-    ofpbuf_use_stub(&odp_actions, odp_actions_stub, sizeof odp_actions_stub);
-    LIST_FOR_EACH (subfacet, list_node, &facet->subfacets) {
-        enum slow_path_reason slow;
-
-        action_xlate_ctx_init(&ctx, ofproto, &facet->flow,
-                              &subfacet->initial_vals, new_rule, 0, NULL);
-        xlate_actions(&ctx, new_rule->up.ofpacts, new_rule->up.ofpacts_len,
-                      &odp_actions);
-
-        slow = (subfacet->slow & SLOW_MATCH) | ctx.slow;
-        if (subfacet_should_install(subfacet, slow, &odp_actions)) {
-            struct dpif_flow_stats stats;
+    /* A facet's slow path reason should only change under dramatic
+     * circumstances.  Rather than try to update everything, it's simpler to
+     * remove the facet and start over.
+     *
+     * More importantly, if a facet's wildcards change, it will be relatively
+     * difficult to figure out if its subfacets still belong to it, and if not
+     * which facet they may belong to.  Again, to avoid the complexity, we
+     * simply give up instead. */
+    if (facet->xout.slow != xout.slow
+        || memcmp(&facet->xout.wc, &xout.wc, sizeof xout.wc)) {
+        facet_remove(facet);
+        xlate_out_uninit(&xout);
+        return false;
+    }
 
-            subfacet_install(subfacet,
-                             odp_actions.data, odp_actions.size, &stats, slow);
-            subfacet_update_stats(subfacet, &stats);
+    if (!ofpbuf_equal(&facet->xout.odp_actions, &xout.odp_actions)) {
+        LIST_FOR_EACH(subfacet, list_node, &facet->subfacets) {
+            if (subfacet->path == SF_FAST_PATH) {
+                struct dpif_flow_stats stats;
 
-            if (!new_actions) {
-                new_actions = xcalloc(list_size(&facet->subfacets),
-                                      sizeof *new_actions);
+                subfacet_install(subfacet, &xout.odp_actions, &stats);
+                subfacet_update_stats(subfacet, &stats);
             }
-            new_actions[i].odp_actions = xmemdup(odp_actions.data,
-                                                 odp_actions.size);
-            new_actions[i].actions_len = odp_actions.size;
         }
 
-        i++;
-    }
-    ofpbuf_uninit(&odp_actions);
-
-    if (new_actions) {
         facet_flush_stats(facet);
+
+        ofpbuf_clear(&facet->xout.odp_actions);
+        ofpbuf_put(&facet->xout.odp_actions, xout.odp_actions.data,
+                   xout.odp_actions.size);
     }
 
     /* Update 'facet' now that we've taken care of all the old state. */
-    facet->tags = ctx.tags;
-    facet->nf_flow.output_iface = ctx.nf_output_iface;
-    facet->has_learn = ctx.has_learn;
-    facet->has_normal = ctx.has_normal;
-    facet->has_fin_timeout = ctx.has_fin_timeout;
-    facet->mirrors = ctx.mirrors;
-
-    i = 0;
-    LIST_FOR_EACH (subfacet, list_node, &facet->subfacets) {
-        subfacet->slow = (subfacet->slow & SLOW_MATCH) | ctx.slow;
-
-        if (new_actions && new_actions[i].odp_actions) {
-            free(subfacet->actions);
-            subfacet->actions = new_actions[i].odp_actions;
-            subfacet->actions_len = new_actions[i].actions_len;
-        }
-        i++;
-    }
-    free(new_actions);
+    facet->xout.tags = xout.tags;
+    facet->xout.slow = xout.slow;
+    facet->xout.has_learn = xout.has_learn;
+    facet->xout.has_normal = xout.has_normal;
+    facet->xout.has_fin_timeout = xout.has_fin_timeout;
+    facet->xout.nf_output_iface = xout.nf_output_iface;
+    facet->xout.mirrors = xout.mirrors;
+    facet->nf_flow.output_iface = facet->xout.nf_output_iface;
 
     if (facet->rule != new_rule) {
         COVERAGE_INC(facet_changed_rule);
@@ -5181,19 +4799,9 @@ facet_revalidate(struct facet *facet)
         facet->used = new_rule->up.created;
         facet->prev_used = facet->used;
     }
-}
 
-/* Updates 'facet''s used time.  Caller is responsible for calling
- * facet_push_stats() to update the flows which 'facet' resubmits into. */
-static void
-facet_update_time(struct facet *facet, long long int used)
-{
-    struct ofproto_dpif *ofproto = ofproto_dpif_cast(facet->rule->up.ofproto);
-    if (used > facet->used) {
-        facet->used = used;
-        ofproto_rule_update_used(&facet->rule->up, used);
-        netflow_flow_update_time(ofproto->netflow, &facet->nf_flow, used);
-    }
+    xlate_out_uninit(&xout);
+    return true;
 }
 
 static void
@@ -5207,7 +4815,7 @@ facet_reset_counters(struct facet *facet)
 }
 
 static void
-facet_push_stats(struct facet *facet)
+facet_push_stats(struct facet *facet, bool may_learn)
 {
     struct dpif_flow_stats stats;
 
@@ -5218,18 +4826,36 @@ facet_push_stats(struct facet *facet)
     stats.n_packets = facet->packet_count - facet->prev_packet_count;
     stats.n_bytes = facet->byte_count - facet->prev_byte_count;
     stats.used = facet->used;
-    stats.tcp_flags = 0;
+    stats.tcp_flags = facet->tcp_flags;
+
+    if (may_learn || stats.n_packets || facet->used > facet->prev_used) {
+        struct ofproto_dpif *ofproto =
+            ofproto_dpif_cast(facet->rule->up.ofproto);
+
+        struct ofport_dpif *in_port;
+        struct xlate_in xin;
 
-    if (stats.n_packets || stats.n_bytes || facet->used > facet->prev_used) {
         facet->prev_packet_count = facet->packet_count;
         facet->prev_byte_count = facet->byte_count;
         facet->prev_used = facet->used;
 
+        in_port = get_ofp_port(ofproto, facet->flow.in_port.ofp_port);
+        if (in_port && in_port->is_tunnel) {
+            netdev_vport_inc_rx(in_port->up.netdev, &stats);
+        }
+
         rule_credit_stats(facet->rule, &stats);
-        flow_push_stats(facet, &stats);
+        netflow_flow_update_time(ofproto->netflow, &facet->nf_flow,
+                                 facet->used);
+        netflow_flow_update_flags(&facet->nf_flow, facet->tcp_flags);
+        update_mirror_stats(ofproto, facet->xout.mirrors, stats.n_packets,
+                            stats.n_bytes);
 
-        update_mirror_stats(ofproto_dpif_cast(facet->rule->up.ofproto),
-                            facet->mirrors, stats.n_packets, stats.n_bytes);
+        xlate_in_init(&xin, ofproto, &facet->flow, facet->rule,
+                      stats.tcp_flags, NULL);
+        xin.resubmit_stats = &stats;
+        xin.may_learn = may_learn;
+        xlate_actions_for_side_effects(&xin);
     }
 }
 
@@ -5244,10 +4870,12 @@ push_all_stats__(bool run_fast)
     }
 
     HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
+        struct cls_cursor cursor;
         struct facet *facet;
 
-        HMAP_FOR_EACH (facet, hmap_node, &ofproto->facets) {
-            facet_push_stats(facet);
+        cls_cursor_init(&cursor, &ofproto->facets, NULL);
+        CLS_CURSOR_FOR_EACH (facet, cr, &cursor) {
+            facet_push_stats(facet, false);
             if (run_fast) {
                 run_fast_rl();
             }
@@ -5263,43 +4891,24 @@ push_all_stats(void)
     push_all_stats__(true);
 }
 
-static void
+void
 rule_credit_stats(struct rule_dpif *rule, const struct dpif_flow_stats *stats)
 {
     rule->packet_count += stats->n_packets;
     rule->byte_count += stats->n_bytes;
     ofproto_rule_update_used(&rule->up, stats->used);
 }
-
-/* Pushes flow statistics to the rules which 'facet->flow' resubmits
- * into given 'facet->rule''s actions and mirrors. */
-static void
-flow_push_stats(struct facet *facet, const struct dpif_flow_stats *stats)
-{
-    struct rule_dpif *rule = facet->rule;
-    struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
-    struct subfacet *subfacet = facet_get_subfacet(facet);
-    struct action_xlate_ctx ctx;
-
-    ofproto_rule_update_used(&rule->up, stats->used);
-
-    action_xlate_ctx_init(&ctx, ofproto, &facet->flow,
-                          &subfacet->initial_vals, rule, 0, NULL);
-    ctx.resubmit_stats = stats;
-    xlate_actions_for_side_effects(&ctx, rule->up.ofpacts,
-                                   rule->up.ofpacts_len);
-}
 \f
 /* Subfacets. */
 
 static struct subfacet *
-subfacet_find(struct ofproto_dpif *ofproto,
-              const struct nlattr *key, size_t key_len, uint32_t key_hash)
+subfacet_find(struct dpif_backer *backer, const struct nlattr *key,
+              size_t key_len, uint32_t key_hash)
 {
     struct subfacet *subfacet;
 
     HMAP_FOR_EACH_WITH_HASH (subfacet, hmap_node, key_hash,
-                             &ofproto->subfacets) {
+                             &backer->subfacets) {
         if (subfacet->key_len == key_len
             && !memcmp(key, subfacet->key, key_len)) {
             return subfacet;
@@ -5312,16 +4921,12 @@ subfacet_find(struct ofproto_dpif *ofproto,
 /* Searches 'facet' (within 'ofproto') for a subfacet with the specified
  * '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(). */
+ * new subfacet. */
 static struct subfacet *
 subfacet_create(struct facet *facet, struct flow_miss *miss,
                 long long int now)
 {
-    struct ofproto_dpif *ofproto = ofproto_dpif_cast(facet->rule->up.ofproto);
+    struct dpif_backer *backer = miss->ofproto->backer;
     enum odp_key_fitness key_fitness = miss->key_fitness;
     const struct nlattr *key = miss->key;
     size_t key_len = miss->key_len;
@@ -5333,7 +4938,7 @@ subfacet_create(struct facet *facet, struct flow_miss *miss,
     if (list_is_empty(&facet->subfacets)) {
         subfacet = &facet->one_subfacet;
     } else {
-        subfacet = subfacet_find(ofproto, key, key_len, key_hash);
+        subfacet = subfacet_find(backer, key, key_len, key_hash);
         if (subfacet) {
             if (subfacet->facet == facet) {
                 return subfacet;
@@ -5347,7 +4952,7 @@ subfacet_create(struct facet *facet, struct flow_miss *miss,
         subfacet = xmalloc(sizeof *subfacet);
     }
 
-    hmap_insert(&ofproto->subfacets, &subfacet->hmap_node, key_hash);
+    hmap_insert(&backer->subfacets, &subfacet->hmap_node, key_hash);
     list_push_back(&facet->subfacets, &subfacet->list_node);
     subfacet->facet = facet;
     subfacet->key_fitness = key_fitness;
@@ -5357,16 +4962,10 @@ subfacet_create(struct facet *facet, struct flow_miss *miss,
     subfacet->created = now;
     subfacet->dp_packet_count = 0;
     subfacet->dp_byte_count = 0;
-    subfacet->actions_len = 0;
-    subfacet->actions = NULL;
-    subfacet->slow = (subfacet->key_fitness == ODP_FIT_TOO_LITTLE
-                      ? SLOW_MATCH
-                      : 0);
     subfacet->path = SF_NOT_INSTALLED;
-    subfacet->initial_vals = miss->initial_vals;
-    subfacet->odp_in_port = miss->odp_in_port;
+    subfacet->backer = backer;
 
-    ofproto->subfacet_add_count++;
+    backer->subfacet_add_count++;
     return subfacet;
 }
 
@@ -5379,14 +4978,12 @@ subfacet_destroy__(struct subfacet *subfacet)
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(facet->rule->up.ofproto);
 
     /* Update ofproto stats before uninstall the subfacet. */
-    ofproto->subfacet_del_count++;
-    ofproto->total_subfacet_life_span += (time_msec() - subfacet->created);
+    ofproto->backer->subfacet_del_count++;
 
     subfacet_uninstall(subfacet);
-    hmap_remove(&ofproto->subfacets, &subfacet->hmap_node);
+    hmap_remove(&subfacet->backer->subfacets, &subfacet->hmap_node);
     list_remove(&subfacet->list_node);
     free(subfacet->key);
-    free(subfacet->actions);
     if (subfacet != &facet->one_subfacet) {
         free(subfacet);
     }
@@ -5408,7 +5005,7 @@ subfacet_destroy(struct subfacet *subfacet)
 }
 
 static void
-subfacet_destroy_batch(struct ofproto_dpif *ofproto,
+subfacet_destroy_batch(struct dpif_backer *backer,
                        struct subfacet **subfacets, int n)
 {
     struct dpif_op ops[SUBFACET_DESTROY_MAX_BATCH];
@@ -5424,7 +5021,7 @@ subfacet_destroy_batch(struct ofproto_dpif *ofproto,
         opsp[i] = &ops[i];
     }
 
-    dpif_operate(ofproto->backer->dpif, opsp, n);
+    dpif_operate(backer->dpif, opsp, n);
     for (i = 0; i < n; i++) {
         subfacet_reset_dp_stats(subfacets[i], &stats[i]);
         subfacets[i]->path = SF_NOT_INSTALLED;
@@ -5433,38 +5030,6 @@ subfacet_destroy_batch(struct ofproto_dpif *ofproto,
     }
 }
 
-/* Composes the datapath actions for 'subfacet' based on its rule's actions.
- * Translates the actions into 'odp_actions', which the caller must have
- * initialized and is responsible for uninitializing. */
-static void
-subfacet_make_actions(struct subfacet *subfacet, const struct ofpbuf *packet,
-                      struct ofpbuf *odp_actions)
-{
-    struct facet *facet = subfacet->facet;
-    struct rule_dpif *rule = facet->rule;
-    struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
-
-    struct action_xlate_ctx ctx;
-
-    action_xlate_ctx_init(&ctx, ofproto, &facet->flow,
-                          &subfacet->initial_vals, rule, 0, packet);
-    xlate_actions(&ctx, rule->up.ofpacts, rule->up.ofpacts_len, odp_actions);
-    facet->tags = ctx.tags;
-    facet->has_learn = ctx.has_learn;
-    facet->has_normal = ctx.has_normal;
-    facet->has_fin_timeout = ctx.has_fin_timeout;
-    facet->nf_flow.output_iface = ctx.nf_output_iface;
-    facet->mirrors = ctx.mirrors;
-
-    subfacet->slow = (subfacet->slow & SLOW_MATCH) | ctx.slow;
-    if (subfacet->actions_len != odp_actions->size
-        || memcmp(subfacet->actions, odp_actions->data, odp_actions->size)) {
-        free(subfacet->actions);
-        subfacet->actions_len = odp_actions->size;
-        subfacet->actions = xmemdup(odp_actions->data, odp_actions->size);
-    }
-}
-
 /* Updates 'subfacet''s datapath flow, setting its actions to 'actions_len'
  * bytes of actions in 'actions'.  If 'stats' is non-null, statistics counters
  * in the datapath will be zeroed and 'stats' will be updated with traffic new
@@ -5472,14 +5037,17 @@ subfacet_make_actions(struct subfacet *subfacet, const struct ofpbuf *packet,
  *
  * Returns 0 if successful, otherwise a positive errno value. */
 static int
-subfacet_install(struct subfacet *subfacet,
-                 const struct nlattr *actions, size_t actions_len,
-                 struct dpif_flow_stats *stats,
-                 enum slow_path_reason slow)
+subfacet_install(struct subfacet *subfacet, const struct ofpbuf *odp_actions,
+                 struct dpif_flow_stats *stats)
 {
     struct facet *facet = subfacet->facet;
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(facet->rule->up.ofproto);
-    enum subfacet_path path = subfacet_want_path(slow);
+    enum subfacet_path path = facet->xout.slow ? SF_SLOW_PATH : SF_FAST_PATH;
+    const struct nlattr *actions = odp_actions->data;
+    size_t actions_len = odp_actions->size;
+    struct odputil_keybuf maskbuf;
+    struct ofpbuf mask;
+
     uint64_t slow_path_stub[128 / 8];
     enum dpif_flow_put_flags flags;
     int ret;
@@ -5490,31 +5058,31 @@ subfacet_install(struct subfacet *subfacet,
     }
 
     if (path == SF_SLOW_PATH) {
-        compose_slow_path(ofproto, &facet->flow, slow,
+        compose_slow_path(ofproto, &facet->flow, facet->xout.slow,
                           slow_path_stub, sizeof slow_path_stub,
                           &actions, &actions_len);
     }
 
-    ret = dpif_flow_put(ofproto->backer->dpif, flags, subfacet->key,
-                        subfacet->key_len, actions, actions_len, stats);
+    ofpbuf_use_stack(&mask, &maskbuf, sizeof maskbuf);
+    odp_flow_key_from_mask(&mask, &facet->xout.wc.masks,
+                           &facet->flow, UINT32_MAX);
+
+    ret = dpif_flow_put(subfacet->backer->dpif, flags, subfacet->key,
+                        subfacet->key_len,  mask.data, mask.size,
+                        actions, actions_len, stats);
 
     if (stats) {
         subfacet_reset_dp_stats(subfacet, stats);
     }
 
-    if (!ret) {
+    if (ret) {
+        COVERAGE_INC(subfacet_install_fail);
+    } else {
         subfacet->path = path;
     }
     return ret;
 }
 
-static int
-subfacet_reinstall(struct subfacet *subfacet, struct dpif_flow_stats *stats)
-{
-    return subfacet_install(subfacet, subfacet->actions, subfacet->actions_len,
-                            stats, subfacet->slow);
-}
-
 /* If 'subfacet' is installed in the datapath, uninstalls it. */
 static void
 subfacet_uninstall(struct subfacet *subfacet)
@@ -5558,17 +5126,6 @@ subfacet_reset_dp_stats(struct subfacet *subfacet,
     subfacet->dp_byte_count = 0;
 }
 
-/* Updates 'subfacet''s used time.  The caller is responsible for calling
- * facet_push_stats() to update the flows which 'subfacet' resubmits into. */
-static void
-subfacet_update_time(struct subfacet *subfacet, long long int used)
-{
-    if (used > subfacet->used) {
-        subfacet->used = used;
-        facet_update_time(subfacet->facet, used);
-    }
-}
-
 /* Folds the statistics from 'stats' into the counters in 'subfacet'.
  *
  * Because of the meaning of a subfacet's counters, it only makes sense to do
@@ -5582,22 +5139,25 @@ subfacet_update_stats(struct subfacet *subfacet,
     if (stats->n_packets || stats->used > subfacet->used) {
         struct facet *facet = subfacet->facet;
 
-        subfacet_update_time(subfacet, stats->used);
+        subfacet->used = MAX(subfacet->used, stats->used);
+        facet->used = MAX(facet->used, stats->used);
         facet->packet_count += stats->n_packets;
         facet->byte_count += stats->n_bytes;
         facet->tcp_flags |= stats->tcp_flags;
-        netflow_flow_update_flags(&facet->nf_flow, stats->tcp_flags);
     }
 }
 \f
 /* Rules. */
 
+/* Lookup 'flow' in 'ofproto''s classifier.  If 'wc' is non-null, sets
+ * the fields that were relevant as part of the lookup. */
 static struct rule_dpif *
-rule_dpif_lookup(struct ofproto_dpif *ofproto, const struct flow *flow)
+rule_dpif_lookup(struct ofproto_dpif *ofproto, const struct flow *flow,
+                 struct flow_wildcards *wc)
 {
     struct rule_dpif *rule;
 
-    rule = rule_dpif_lookup__(ofproto, flow, 0);
+    rule = rule_dpif_lookup_in_table(ofproto, flow, wc, 0);
     if (rule) {
         return rule;
     }
@@ -5605,40 +5165,52 @@ rule_dpif_lookup(struct ofproto_dpif *ofproto, const struct flow *flow)
     return rule_dpif_miss_rule(ofproto, flow);
 }
 
-static struct rule_dpif *
-rule_dpif_lookup__(struct ofproto_dpif *ofproto, const struct flow *flow,
-                   uint8_t table_id)
+struct rule_dpif *
+rule_dpif_lookup_in_table(struct ofproto_dpif *ofproto,
+                          const struct flow *flow, struct flow_wildcards *wc,
+                          uint8_t table_id)
 {
     struct cls_rule *cls_rule;
     struct classifier *cls;
+    bool frag;
 
     if (table_id >= N_TABLES) {
         return NULL;
     }
 
+    if (wc) {
+        memset(&wc->masks.dl_type, 0xff, sizeof wc->masks.dl_type);
+        wc->masks.nw_frag |= FLOW_NW_FRAG_MASK;
+    }
+
     cls = &ofproto->up.tables[table_id].cls;
-    if (flow->nw_frag & FLOW_NW_FRAG_ANY
-        && ofproto->up.frag_handling == OFPC_FRAG_NORMAL) {
-        /* For OFPC_NORMAL frag_handling, we must pretend that transport ports
-         * are unavailable. */
+    frag = (flow->nw_frag & FLOW_NW_FRAG_ANY) != 0;
+    if (frag && ofproto->up.frag_handling == OFPC_FRAG_NORMAL) {
+        /* We must pretend that transport ports are unavailable. */
         struct flow ofpc_normal_flow = *flow;
         ofpc_normal_flow.tp_src = htons(0);
         ofpc_normal_flow.tp_dst = htons(0);
-        cls_rule = classifier_lookup(cls, &ofpc_normal_flow);
+        cls_rule = classifier_lookup(cls, &ofpc_normal_flow, wc);
+    } else if (frag && ofproto->up.frag_handling == OFPC_FRAG_DROP) {
+        cls_rule = &ofproto->drop_frags_rule->up.cr;
+        if (wc) {
+            flow_wildcards_init_exact(wc);
+        }
     } else {
-        cls_rule = classifier_lookup(cls, flow);
+        cls_rule = classifier_lookup(cls, flow, wc);
     }
     return rule_dpif_cast(rule_from_cls_rule(cls_rule));
 }
 
-static struct rule_dpif *
+struct rule_dpif *
 rule_dpif_miss_rule(struct ofproto_dpif *ofproto, const struct flow *flow)
 {
     struct ofport_dpif *port;
 
-    port = get_ofp_port(ofproto, flow->in_port);
+    port = get_ofp_port(ofproto, flow->in_port.ofp_port);
     if (!port) {
-        VLOG_WARN_RL(&rl, "packet-in on unknown port %"PRIu16, flow->in_port);
+        VLOG_WARN_RL(&rl, "packet-in on unknown OpenFlow port %"PRIu16,
+                     flow->in_port.ofp_port);
         return ofproto->miss_rule;
     }
 
@@ -5761,26 +5333,21 @@ rule_dpif_execute(struct rule_dpif *rule, const struct flow *flow,
                   struct ofpbuf *packet)
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
-    struct initial_vals initial_vals;
     struct dpif_flow_stats stats;
-    struct action_xlate_ctx ctx;
-    uint64_t odp_actions_stub[1024 / 8];
-    struct ofpbuf odp_actions;
+    struct xlate_out xout;
+    struct xlate_in xin;
 
     dpif_flow_stats_extract(flow, packet, time_msec(), &stats);
     rule_credit_stats(rule, &stats);
 
-    initial_vals.vlan_tci = flow->vlan_tci;
-    ofpbuf_use_stub(&odp_actions, odp_actions_stub, sizeof odp_actions_stub);
-    action_xlate_ctx_init(&ctx, ofproto, flow, &initial_vals,
-                          rule, stats.tcp_flags, packet);
-    ctx.resubmit_stats = &stats;
-    xlate_actions(&ctx, rule->up.ofpacts, rule->up.ofpacts_len, &odp_actions);
+    xlate_in_init(&xin, ofproto, flow, rule, stats.tcp_flags, packet);
+    xin.resubmit_stats = &stats;
+    xlate_actions(&xin, &xout);
 
-    execute_odp_actions(ofproto, flow, odp_actions.data,
-                        odp_actions.size, packet);
+    execute_odp_actions(ofproto, flow, xout.odp_actions.data,
+                        xout.odp_actions.size, packet);
 
-    ofpbuf_uninit(&odp_actions);
+    xlate_out_uninit(&xout);
 }
 
 static enum ofperr
@@ -5812,15 +5379,18 @@ send_packet(const struct ofport_dpif *ofport, struct ofpbuf *packet)
     struct dpif_flow_stats stats;
     struct odputil_keybuf keybuf;
     struct ofpact_output output;
-    struct action_xlate_ctx ctx;
+    struct xlate_out xout;
+    struct xlate_in xin;
     struct flow flow;
+    union flow_in_port in_port_;
     int error;
 
     ofpbuf_use_stub(&odp_actions, odp_actions_stub, sizeof odp_actions_stub);
     ofpbuf_use_stack(&key, &keybuf, sizeof keybuf);
 
     /* Use OFPP_NONE as the in_port to avoid special packet processing. */
-    flow_extract(packet, 0, 0, NULL, OFPP_NONE, &flow);
+    in_port_.ofp_port = OFPP_NONE;
+    flow_extract(packet, 0, 0, NULL, &in_port_, &flow);
     odp_flow_key_from_flow(&key, &flow, ofp_port_to_odp_port(ofproto,
                                                              OFPP_LOCAL));
     dpif_flow_stats_extract(&flow, packet, time_msec(), &stats);
@@ -5829,33 +5399,28 @@ send_packet(const struct ofport_dpif *ofport, struct ofpbuf *packet)
     output.port = ofport->up.ofp_port;
     output.max_len = 0;
 
-    action_xlate_ctx_init(&ctx, ofproto, &flow, NULL, NULL, 0, packet);
-    ctx.resubmit_stats = &stats;
-    xlate_actions(&ctx, &output.ofpact, sizeof output, &odp_actions);
+    xlate_in_init(&xin, ofproto, &flow, NULL, 0, packet);
+    xin.ofpacts_len = sizeof output;
+    xin.ofpacts = &output.ofpact;
+    xin.resubmit_stats = &stats;
+    xlate_actions(&xin, &xout);
 
     error = dpif_execute(ofproto->backer->dpif,
                          key.data, key.size,
-                         odp_actions.data, odp_actions.size,
+                         xout.odp_actions.data, xout.odp_actions.size,
                          packet);
-    ofpbuf_uninit(&odp_actions);
+    xlate_out_uninit(&xout);
 
     if (error) {
         VLOG_WARN_RL(&rl, "%s: failed to send packet on port %s (%s)",
                      ofproto->up.name, netdev_get_name(ofport->up.netdev),
-                     strerror(error));
+                     ovs_strerror(error));
     }
 
     ofproto->stats.tx_packets++;
     ofproto->stats.tx_bytes += packet->size;
     return error;
 }
-\f
-/* OpenFlow to datapath action translation. */
-
-static bool may_receive(const struct ofport_dpif *, struct action_xlate_ctx *);
-static void do_xlate_actions(const struct ofpact *, size_t ofpacts_len,
-                             struct action_xlate_ctx *);
-static void xlate_normal(struct action_xlate_ctx *);
 
 /* Composes an ODP action for a "slow path" action for 'flow' within 'ofproto'.
  * The action will state 'slow' as the reason that the action is in the slow
@@ -5882,7 +5447,8 @@ compose_slow_path(const struct ofproto_dpif *ofproto, const struct flow *flow,
 
     ofpbuf_use_stack(&buf, stub, stub_size);
     if (slow & (SLOW_CFM | SLOW_BFD | SLOW_LACP | SLOW_STP)) {
-        uint32_t pid = dpif_port_get_pid(ofproto->backer->dpif, UINT32_MAX);
+        uint32_t pid = dpif_port_get_pid(ofproto->backer->dpif,
+                                         ODPP_NONE);
         odp_put_userspace_action(pid, &cookie, sizeof cookie.slow_path, &buf);
     } else {
         put_userspace_action(ofproto, &buf, flow, &cookie,
@@ -5892,7 +5458,7 @@ compose_slow_path(const struct ofproto_dpif *ofproto, const struct flow *flow,
     *actions_lenp = buf.size;
 }
 
-static size_t
+size_t
 put_userspace_action(const struct ofproto_dpif *ofproto,
                      struct ofpbuf *odp_actions,
                      const struct flow *flow,
@@ -5902,1845 +5468,56 @@ put_userspace_action(const struct ofproto_dpif *ofproto,
     uint32_t pid;
 
     pid = dpif_port_get_pid(ofproto->backer->dpif,
-                            ofp_port_to_odp_port(ofproto, flow->in_port));
+                            ofp_port_to_odp_port(ofproto,
+                                                 flow->in_port.ofp_port));
 
     return odp_put_userspace_action(pid, cookie, cookie_size, odp_actions);
 }
 
-/* Compose SAMPLE action for sFlow or IPFIX.  The given probability is
- * the number of packets out of UINT32_MAX to sample.  The given
- * cookie is passed back in the callback for each sampled packet.
- */
-static size_t
-compose_sample_action(const struct ofproto_dpif *ofproto,
-                      struct ofpbuf *odp_actions,
-                      const struct flow *flow,
-                      const uint32_t probability,
-                      const union user_action_cookie *cookie,
-                      const size_t cookie_size)
-{
-    size_t sample_offset, actions_offset;
-    int cookie_offset;
-
-    sample_offset = nl_msg_start_nested(odp_actions, OVS_ACTION_ATTR_SAMPLE);
-
-    nl_msg_put_u32(odp_actions, OVS_SAMPLE_ATTR_PROBABILITY, probability);
-
-    actions_offset = nl_msg_start_nested(odp_actions, OVS_SAMPLE_ATTR_ACTIONS);
-    cookie_offset = put_userspace_action(ofproto, odp_actions, flow, cookie,
-                                         cookie_size);
-
-    nl_msg_end_nested(odp_actions, actions_offset);
-    nl_msg_end_nested(odp_actions, sample_offset);
-    return cookie_offset;
-}
 
 static void
-compose_sflow_cookie(const struct ofproto_dpif *ofproto,
-                     ovs_be16 vlan_tci, uint32_t odp_port,
-                     unsigned int n_outputs, union user_action_cookie *cookie)
+update_mirror_stats(struct ofproto_dpif *ofproto, mirror_mask_t mirrors,
+                    uint64_t packets, uint64_t bytes)
 {
-    int ifindex;
+    if (!mirrors) {
+        return;
+    }
 
-    cookie->type = USER_ACTION_COOKIE_SFLOW;
-    cookie->sflow.vlan_tci = vlan_tci;
+    for (; mirrors; mirrors = zero_rightmost_1bit(mirrors)) {
+        struct ofmirror *m;
 
-    /* See http://www.sflow.org/sflow_version_5.txt (search for "Input/output
-     * port information") for the interpretation of cookie->output. */
-    switch (n_outputs) {
-    case 0:
-        /* 0x40000000 | 256 means "packet dropped for unknown reason". */
-        cookie->sflow.output = 0x40000000 | 256;
-        break;
+        m = ofproto->mirrors[mirror_mask_ffs(mirrors) - 1];
 
-    case 1:
-        ifindex = dpif_sflow_odp_port_to_ifindex(ofproto->sflow, odp_port);
-        if (ifindex) {
-            cookie->sflow.output = ifindex;
-            break;
+        if (!m) {
+            /* In normal circumstances 'm' will not be NULL.  However,
+             * if mirrors are reconfigured, we can temporarily get out
+             * of sync in facet_revalidate().  We could "correct" the
+             * mirror list before reaching here, but doing that would
+             * not properly account the traffic stats we've currently
+             * accumulated for previous mirror configuration. */
+            continue;
         }
-        /* Fall through. */
-    default:
-        /* 0x80000000 means "multiple output ports. */
-        cookie->sflow.output = 0x80000000 | n_outputs;
-        break;
-    }
-}
-
-/* Compose SAMPLE action for sFlow bridge sampling. */
-static size_t
-compose_sflow_action(const struct ofproto_dpif *ofproto,
-                     struct ofpbuf *odp_actions,
-                     const struct flow *flow,
-                     uint32_t odp_port)
-{
-    uint32_t probability;
-    union user_action_cookie cookie;
 
-    if (!ofproto->sflow || flow->in_port == OFPP_NONE) {
-        return 0;
+        m->packet_count += packets;
+        m->byte_count += bytes;
     }
-
-    probability = dpif_sflow_get_probability(ofproto->sflow);
-    compose_sflow_cookie(ofproto, htons(0), odp_port,
-                         odp_port == OVSP_NONE ? 0 : 1, &cookie);
-
-    return compose_sample_action(ofproto, odp_actions, flow,  probability,
-                                 &cookie, sizeof cookie.sflow);
 }
 
-static void
-compose_flow_sample_cookie(uint16_t probability, uint32_t collector_set_id,
-                           uint32_t obs_domain_id, uint32_t obs_point_id,
-                           union user_action_cookie *cookie)
+tag_type
+calculate_flow_tag(struct ofproto_dpif *ofproto, const struct flow *flow,
+                   uint8_t table_id, struct rule_dpif *rule)
 {
-    cookie->type = USER_ACTION_COOKIE_FLOW_SAMPLE;
-    cookie->flow_sample.probability = probability;
-    cookie->flow_sample.collector_set_id = collector_set_id;
-    cookie->flow_sample.obs_domain_id = obs_domain_id;
-    cookie->flow_sample.obs_point_id = obs_point_id;
-}
+    if (table_id > 0 && table_id < N_TABLES) {
+        struct table_dpif *table = &ofproto->tables[table_id];
+        if (table->other_table) {
+            return (rule && rule->tag
+                    ? rule->tag
+                    : rule_calculate_tag(flow, &table->other_table->mask,
+                                         table->basis));
+        }
+    }
 
-static void
-compose_ipfix_cookie(union user_action_cookie *cookie)
-{
-    cookie->type = USER_ACTION_COOKIE_IPFIX;
-}
-
-/* Compose SAMPLE action for IPFIX bridge sampling. */
-static void
-compose_ipfix_action(const struct ofproto_dpif *ofproto,
-                     struct ofpbuf *odp_actions,
-                     const struct flow *flow)
-{
-    uint32_t probability;
-    union user_action_cookie cookie;
-
-    if (!ofproto->ipfix || flow->in_port == OFPP_NONE) {
-        return;
-    }
-
-    probability = dpif_ipfix_get_bridge_exporter_probability(ofproto->ipfix);
-    compose_ipfix_cookie(&cookie);
-
-    compose_sample_action(ofproto, odp_actions, flow,  probability,
-                          &cookie, sizeof cookie.ipfix);
-}
-
-/* SAMPLE action for sFlow must be first action in any given list of
- * actions.  At this point we do not have all information required to
- * build it. So try to build sample action as complete as possible. */
-static void
-add_sflow_action(struct action_xlate_ctx *ctx)
-{
-    ctx->user_cookie_offset = compose_sflow_action(ctx->ofproto,
-                                                   ctx->odp_actions,
-                                                   &ctx->flow, OVSP_NONE);
-    ctx->sflow_odp_port = 0;
-    ctx->sflow_n_outputs = 0;
-}
-
-/* SAMPLE action for IPFIX must be 1st or 2nd action in any given list
- * of actions, eventually after the SAMPLE action for sFlow. */
-static void
-add_ipfix_action(struct action_xlate_ctx *ctx)
-{
-    compose_ipfix_action(ctx->ofproto, ctx->odp_actions, &ctx->flow);
-}
-
-/* Fix SAMPLE action according to data collected while composing ODP actions.
- * We need to fix SAMPLE actions OVS_SAMPLE_ATTR_ACTIONS attribute, i.e. nested
- * USERSPACE action's user-cookie which is required for sflow. */
-static void
-fix_sflow_action(struct action_xlate_ctx *ctx)
-{
-    const struct flow *base = &ctx->base_flow;
-    union user_action_cookie *cookie;
-
-    if (!ctx->user_cookie_offset) {
-        return;
-    }
-
-    cookie = ofpbuf_at(ctx->odp_actions, ctx->user_cookie_offset,
-                       sizeof cookie->sflow);
-    ovs_assert(cookie->type == USER_ACTION_COOKIE_SFLOW);
-
-    compose_sflow_cookie(ctx->ofproto, base->vlan_tci,
-                         ctx->sflow_odp_port, ctx->sflow_n_outputs, cookie);
-}
-
-static void
-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);
-    ovs_be16 flow_vlan_tci;
-    uint32_t flow_skb_mark;
-    uint8_t flow_nw_tos;
-    struct priority_to_dscp *pdscp;
-    uint32_t out_port, odp_port;
-
-    /* If 'struct flow' gets additional metadata, we'll need to zero it out
-     * before traversing a patch port. */
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 20);
-
-    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;
-    }
-
-    if (netdev_vport_is_patch(ofport->up.netdev)) {
-        struct ofport_dpif *peer = ofport_get_peer(ofport);
-        struct flow old_flow = ctx->flow;
-        const struct ofproto_dpif *peer_ofproto;
-        enum slow_path_reason special;
-        struct ofport_dpif *in_port;
-
-        if (!peer) {
-            xlate_report(ctx, "Nonexistent patch port peer");
-            return;
-        }
-
-        peer_ofproto = ofproto_dpif_cast(peer->up.ofproto);
-        if (peer_ofproto->backer != ctx->ofproto->backer) {
-            xlate_report(ctx, "Patch port peer on a different datapath");
-            return;
-        }
-
-        ctx->ofproto = ofproto_dpif_cast(peer->up.ofproto);
-        ctx->flow.in_port = peer->up.ofp_port;
-        ctx->flow.metadata = htonll(0);
-        memset(&ctx->flow.tunnel, 0, sizeof ctx->flow.tunnel);
-        memset(ctx->flow.regs, 0, sizeof ctx->flow.regs);
-
-        in_port = get_ofp_port(ctx->ofproto, ctx->flow.in_port);
-        special = process_special(ctx->ofproto, &ctx->flow, in_port,
-                                  ctx->packet);
-        if (special) {
-            ctx->slow |= special;
-        } else if (!in_port || may_receive(in_port, ctx)) {
-            if (!in_port || stp_forward_in_state(in_port->stp_state)) {
-                xlate_table_action(ctx, ctx->flow.in_port, 0, true);
-            } else {
-                /* Forwarding is disabled by STP.  Let OFPP_NORMAL and the
-                 * learning action look at the packet, then drop it. */
-                struct flow old_base_flow = ctx->base_flow;
-                size_t old_size = ctx->odp_actions->size;
-                xlate_table_action(ctx, ctx->flow.in_port, 0, true);
-                ctx->base_flow = old_base_flow;
-                ctx->odp_actions->size = old_size;
-            }
-        }
-
-        ctx->flow = old_flow;
-        ctx->ofproto = ofproto_dpif_cast(ofport->up.ofproto);
-
-        if (ctx->resubmit_stats) {
-            netdev_vport_inc_tx(ofport->up.netdev, ctx->resubmit_stats);
-            netdev_vport_inc_rx(peer->up.netdev, ctx->resubmit_stats);
-        }
-
-        return;
-    }
-
-    flow_vlan_tci = ctx->flow.vlan_tci;
-    flow_skb_mark = ctx->flow.skb_mark;
-    flow_nw_tos = ctx->flow.nw_tos;
-
-    pdscp = get_priority(ofport, ctx->flow.skb_priority);
-    if (pdscp) {
-        ctx->flow.nw_tos &= ~IP_DSCP_MASK;
-        ctx->flow.nw_tos |= pdscp->dscp;
-    }
-
-    if (ofport->tnl_port) {
-         /* Save tunnel metadata so that changes made due to
-          * the Logical (tunnel) Port are not visible for any further
-          * matches, while explicit set actions on tunnel metadata are.
-          */
-        struct flow_tnl flow_tnl = ctx->flow.tunnel;
-        odp_port = tnl_port_send(ofport->tnl_port, &ctx->flow);
-        if (odp_port == OVSP_NONE) {
-            xlate_report(ctx, "Tunneling decided against output");
-            goto out; /* restore flow_nw_tos */
-        }
-        if (ctx->flow.tunnel.ip_dst == ctx->orig_tunnel_ip_dst) {
-            xlate_report(ctx, "Not tunneling to our own address");
-            goto out; /* restore flow_nw_tos */
-        }
-        if (ctx->resubmit_stats) {
-            netdev_vport_inc_tx(ofport->up.netdev, ctx->resubmit_stats);
-        }
-        out_port = odp_port;
-        commit_odp_tunnel_action(&ctx->flow, &ctx->base_flow,
-                                 ctx->odp_actions);
-        ctx->flow.tunnel = flow_tnl; /* Restore tunnel metadata */
-    } else {
-        odp_port = ofport->odp_port;
-        out_port = vsp_realdev_to_vlandev(ctx->ofproto, odp_port,
-                                          ctx->flow.vlan_tci);
-        if (out_port != odp_port) {
-            ctx->flow.vlan_tci = htons(0);
-        }
-        ctx->flow.skb_mark &= ~IPSEC_MARK;
-    }
-    commit_odp_actions(&ctx->flow, &ctx->base_flow, ctx->odp_actions);
-    nl_msg_put_u32(ctx->odp_actions, OVS_ACTION_ATTR_OUTPUT, out_port);
-
-    ctx->sflow_odp_port = odp_port;
-    ctx->sflow_n_outputs++;
-    ctx->nf_output_iface = ofp_port;
-
-    /* Restore flow */
-    ctx->flow.vlan_tci = flow_vlan_tci;
-    ctx->flow.skb_mark = flow_skb_mark;
- out:
-    ctx->flow.nw_tos = flow_nw_tos;
-}
-
-static void
-compose_output_action(struct action_xlate_ctx *ctx, uint16_t ofp_port)
-{
-    compose_output_action__(ctx, ofp_port, true);
-}
-
-static void
-tag_the_flow(struct action_xlate_ctx *ctx, struct rule_dpif *rule)
-{
-    struct ofproto_dpif *ofproto = ctx->ofproto;
-    uint8_t table_id = ctx->table_id;
-
-    if (table_id > 0 && table_id < N_TABLES) {
-        struct table_dpif *table = &ofproto->tables[table_id];
-        if (table->other_table) {
-            ctx->tags |= (rule && rule->tag
-                          ? rule->tag
-                          : rule_calculate_tag(&ctx->flow,
-                                               &table->other_table->mask,
-                                               table->basis));
-        }
-    }
-}
-
-/* Common rule processing in one place to avoid duplicating code. */
-static struct rule_dpif *
-ctx_rule_hooks(struct action_xlate_ctx *ctx, struct rule_dpif *rule,
-               bool may_packet_in)
-{
-    if (ctx->resubmit_hook) {
-        ctx->resubmit_hook(ctx, rule);
-    }
-    if (rule == NULL && may_packet_in) {
-        /* XXX
-         * check if table configuration flags
-         * OFPTC_TABLE_MISS_CONTROLLER, default.
-         * OFPTC_TABLE_MISS_CONTINUE,
-         * OFPTC_TABLE_MISS_DROP
-         * When OF1.0, OFPTC_TABLE_MISS_CONTINUE is used. What to do?
-         */
-        rule = rule_dpif_miss_rule(ctx->ofproto, &ctx->flow);
-    }
-    if (rule && ctx->resubmit_stats) {
-        rule_credit_stats(rule, ctx->resubmit_stats);
-    }
-    return rule;
-}
-
-static void
-xlate_table_action(struct action_xlate_ctx *ctx,
-                   uint16_t in_port, uint8_t table_id, bool may_packet_in)
-{
-    if (ctx->recurse < MAX_RESUBMIT_RECURSION) {
-        struct rule_dpif *rule;
-        uint16_t old_in_port = ctx->flow.in_port;
-        uint8_t old_table_id = ctx->table_id;
-
-        ctx->table_id = table_id;
-
-        /* Look up a flow with 'in_port' as the input port. */
-        ctx->flow.in_port = in_port;
-        rule = rule_dpif_lookup__(ctx->ofproto, &ctx->flow, table_id);
-
-        tag_the_flow(ctx, rule);
-
-        /* Restore the original input port.  Otherwise OFPP_NORMAL and
-         * OFPP_IN_PORT will have surprising behavior. */
-        ctx->flow.in_port = old_in_port;
-
-        rule = ctx_rule_hooks(ctx, rule, may_packet_in);
-
-        if (rule) {
-            struct rule_dpif *old_rule = ctx->rule;
-
-            ctx->recurse++;
-            ctx->rule = rule;
-            do_xlate_actions(rule->up.ofpacts, rule->up.ofpacts_len, ctx);
-            ctx->rule = old_rule;
-            ctx->recurse--;
-        }
-
-        ctx->table_id = old_table_id;
-    } else {
-        static struct vlog_rate_limit recurse_rl = VLOG_RATE_LIMIT_INIT(1, 1);
-
-        VLOG_ERR_RL(&recurse_rl, "resubmit actions recursed over %d times",
-                    MAX_RESUBMIT_RECURSION);
-        ctx->max_resubmit_trigger = true;
-    }
-}
-
-static void
-xlate_ofpact_resubmit(struct action_xlate_ctx *ctx,
-                      const struct ofpact_resubmit *resubmit)
-{
-    uint16_t in_port;
-    uint8_t table_id;
-
-    in_port = resubmit->in_port;
-    if (in_port == OFPP_IN_PORT) {
-        in_port = ctx->flow.in_port;
-    }
-
-    table_id = resubmit->table_id;
-    if (table_id == 255) {
-        table_id = ctx->table_id;
-    }
-
-    xlate_table_action(ctx, in_port, table_id, false);
-}
-
-static void
-flood_packets(struct action_xlate_ctx *ctx, bool all)
-{
-    struct ofport_dpif *ofport;
-
-    HMAP_FOR_EACH (ofport, up.hmap_node, &ctx->ofproto->up.ports) {
-        uint16_t ofp_port = ofport->up.ofp_port;
-
-        if (ofp_port == ctx->flow.in_port) {
-            continue;
-        }
-
-        if (all) {
-            compose_output_action__(ctx, ofp_port, false);
-        } else if (!(ofport->up.pp.config & OFPUTIL_PC_NO_FLOOD)) {
-            compose_output_action(ctx, ofp_port);
-        }
-    }
-
-    ctx->nf_output_iface = NF_OUT_FLOOD;
-}
-
-static void
-execute_controller_action(struct action_xlate_ctx *ctx, int len,
-                          enum ofp_packet_in_reason reason,
-                          uint16_t controller_id)
-{
-    struct ofputil_packet_in pin;
-    struct ofpbuf *packet;
-
-    ctx->slow |= SLOW_CONTROLLER;
-    if (!ctx->packet) {
-        return;
-    }
-
-    packet = ofpbuf_clone(ctx->packet);
-
-    if (packet->l2 && packet->l3) {
-        struct eth_header *eh;
-        uint16_t mpls_depth;
-
-        eth_pop_vlan(packet);
-        eh = packet->l2;
-
-        memcpy(eh->eth_src, ctx->flow.dl_src, sizeof eh->eth_src);
-        memcpy(eh->eth_dst, ctx->flow.dl_dst, sizeof eh->eth_dst);
-
-        if (ctx->flow.vlan_tci & htons(VLAN_CFI)) {
-            eth_push_vlan(packet, ctx->flow.vlan_tci);
-        }
-
-        mpls_depth = eth_mpls_depth(packet);
-
-        if (mpls_depth < ctx->flow.mpls_depth) {
-            push_mpls(packet, ctx->flow.dl_type, ctx->flow.mpls_lse);
-        } else if (mpls_depth > ctx->flow.mpls_depth) {
-            pop_mpls(packet, ctx->flow.dl_type);
-        } else if (mpls_depth) {
-            set_mpls_lse(packet, ctx->flow.mpls_lse);
-        }
-
-        if (packet->l4) {
-            if (ctx->flow.dl_type == htons(ETH_TYPE_IP)) {
-                packet_set_ipv4(packet, ctx->flow.nw_src, ctx->flow.nw_dst,
-                                ctx->flow.nw_tos, ctx->flow.nw_ttl);
-            }
-
-            if (packet->l7) {
-                if (ctx->flow.nw_proto == IPPROTO_TCP) {
-                    packet_set_tcp_port(packet, ctx->flow.tp_src,
-                                        ctx->flow.tp_dst);
-                } else if (ctx->flow.nw_proto == IPPROTO_UDP) {
-                    packet_set_udp_port(packet, ctx->flow.tp_src,
-                                        ctx->flow.tp_dst);
-                }
-            }
-        }
-    }
-
-    pin.packet = packet->data;
-    pin.packet_len = packet->size;
-    pin.reason = reason;
-    pin.controller_id = controller_id;
-    pin.table_id = ctx->table_id;
-    pin.cookie = ctx->rule ? ctx->rule->up.flow_cookie : 0;
-
-    pin.send_len = len;
-    flow_get_metadata(&ctx->flow, &pin.fmd);
-
-    connmgr_send_packet_in(ctx->ofproto->up.connmgr, &pin);
-    ofpbuf_delete(packet);
-}
-
-static void
-execute_mpls_push_action(struct action_xlate_ctx *ctx, ovs_be16 eth_type)
-{
-    ovs_assert(eth_type_mpls(eth_type));
-
-    if (ctx->base_flow.mpls_depth) {
-        ctx->flow.mpls_lse &= ~htonl(MPLS_BOS_MASK);
-        ctx->flow.mpls_depth++;
-    } else {
-        ovs_be32 label;
-        uint8_t tc, ttl;
-
-        if (ctx->flow.dl_type == htons(ETH_TYPE_IPV6)) {
-            label = htonl(0x2); /* IPV6 Explicit Null. */
-        } else {
-            label = htonl(0x0); /* IPV4 Explicit Null. */
-        }
-        tc = (ctx->flow.nw_tos & IP_DSCP_MASK) >> 2;
-        ttl = ctx->flow.nw_ttl ? ctx->flow.nw_ttl : 0x40;
-        ctx->flow.mpls_lse = set_mpls_lse_values(ttl, tc, 1, label);
-        ctx->flow.mpls_depth = 1;
-    }
-    ctx->flow.dl_type = eth_type;
-}
-
-static void
-execute_mpls_pop_action(struct action_xlate_ctx *ctx, ovs_be16 eth_type)
-{
-    ovs_assert(eth_type_mpls(ctx->flow.dl_type));
-    ovs_assert(!eth_type_mpls(eth_type));
-
-    if (ctx->flow.mpls_depth) {
-        ctx->flow.mpls_depth--;
-        ctx->flow.mpls_lse = htonl(0);
-        if (!ctx->flow.mpls_depth) {
-            ctx->flow.dl_type = eth_type;
-        }
-    }
-}
-
-static bool
-compose_dec_ttl(struct action_xlate_ctx *ctx, struct ofpact_cnt_ids *ids)
-{
-    if (ctx->flow.dl_type != htons(ETH_TYPE_IP) &&
-        ctx->flow.dl_type != htons(ETH_TYPE_IPV6)) {
-        return false;
-    }
-
-    if (ctx->flow.nw_ttl > 1) {
-        ctx->flow.nw_ttl--;
-        return false;
-    } else {
-        size_t i;
-
-        for (i = 0; i < ids->n_controllers; i++) {
-            execute_controller_action(ctx, UINT16_MAX, OFPR_INVALID_TTL,
-                                      ids->cnt_ids[i]);
-        }
-
-        /* Stop processing for current table. */
-        return true;
-    }
-}
-
-static bool
-execute_set_mpls_ttl_action(struct action_xlate_ctx *ctx, uint8_t ttl)
-{
-    if (!eth_type_mpls(ctx->flow.dl_type)) {
-        return true;
-    }
-
-    set_mpls_lse_ttl(&ctx->flow.mpls_lse, ttl);
-    return false;
-}
-
-static bool
-execute_dec_mpls_ttl_action(struct action_xlate_ctx *ctx)
-{
-    uint8_t ttl = mpls_lse_to_ttl(ctx->flow.mpls_lse);
-
-    if (!eth_type_mpls(ctx->flow.dl_type)) {
-        return false;
-    }
-
-    if (ttl > 1) {
-        ttl--;
-        set_mpls_lse_ttl(&ctx->flow.mpls_lse, ttl);
-        return false;
-    } else {
-        execute_controller_action(ctx, UINT16_MAX, OFPR_INVALID_TTL, 0);
-
-        /* Stop processing for current table. */
-        return true;
-    }
-}
-
-static void
-xlate_output_action(struct action_xlate_ctx *ctx,
-                    uint16_t port, uint16_t max_len, bool may_packet_in)
-{
-    uint16_t prev_nf_output_iface = ctx->nf_output_iface;
-
-    ctx->nf_output_iface = NF_OUT_DROP;
-
-    switch (port) {
-    case OFPP_IN_PORT:
-        compose_output_action(ctx, ctx->flow.in_port);
-        break;
-    case OFPP_TABLE:
-        xlate_table_action(ctx, ctx->flow.in_port, 0, may_packet_in);
-        break;
-    case OFPP_NORMAL:
-        xlate_normal(ctx);
-        break;
-    case OFPP_FLOOD:
-        flood_packets(ctx,  false);
-        break;
-    case OFPP_ALL:
-        flood_packets(ctx, true);
-        break;
-    case OFPP_CONTROLLER:
-        execute_controller_action(ctx, max_len, OFPR_ACTION, 0);
-        break;
-    case OFPP_NONE:
-        break;
-    case OFPP_LOCAL:
-    default:
-        if (port != ctx->flow.in_port) {
-            compose_output_action(ctx, port);
-        } else {
-            xlate_report(ctx, "skipping output to input port");
-        }
-        break;
-    }
-
-    if (prev_nf_output_iface == NF_OUT_FLOOD) {
-        ctx->nf_output_iface = NF_OUT_FLOOD;
-    } else if (ctx->nf_output_iface == NF_OUT_DROP) {
-        ctx->nf_output_iface = prev_nf_output_iface;
-    } else if (prev_nf_output_iface != NF_OUT_DROP &&
-               ctx->nf_output_iface != NF_OUT_FLOOD) {
-        ctx->nf_output_iface = NF_OUT_MULTI;
-    }
-}
-
-static void
-xlate_output_reg_action(struct action_xlate_ctx *ctx,
-                        const struct ofpact_output_reg *or)
-{
-    uint64_t port = mf_get_subfield(&or->src, &ctx->flow);
-    if (port <= UINT16_MAX) {
-        xlate_output_action(ctx, port, or->max_len, false);
-    }
-}
-
-static void
-xlate_enqueue_action(struct action_xlate_ctx *ctx,
-                     const struct ofpact_enqueue *enqueue)
-{
-    uint16_t ofp_port = enqueue->port;
-    uint32_t queue_id = enqueue->queue;
-    uint32_t flow_priority, priority;
-    int error;
-
-    /* Translate queue to 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);
-        return;
-    }
-
-    /* Check output port. */
-    if (ofp_port == OFPP_IN_PORT) {
-        ofp_port = ctx->flow.in_port;
-    } else if (ofp_port == ctx->flow.in_port) {
-        return;
-    }
-
-    /* Add datapath actions. */
-    flow_priority = ctx->flow.skb_priority;
-    ctx->flow.skb_priority = priority;
-    compose_output_action(ctx, ofp_port);
-    ctx->flow.skb_priority = flow_priority;
-
-    /* Update NetFlow output port. */
-    if (ctx->nf_output_iface == NF_OUT_DROP) {
-        ctx->nf_output_iface = ofp_port;
-    } else if (ctx->nf_output_iface != NF_OUT_FLOOD) {
-        ctx->nf_output_iface = NF_OUT_MULTI;
-    }
-}
-
-static void
-xlate_set_queue_action(struct action_xlate_ctx *ctx, uint32_t queue_id)
-{
-    uint32_t 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
-         * has already been logged. */
-    }
-}
-
-static bool
-slave_enabled_cb(uint16_t ofp_port, void *ofproto_)
-{
-    struct ofproto_dpif *ofproto = ofproto_;
-    struct ofport_dpif *port;
-
-    switch (ofp_port) {
-    case OFPP_IN_PORT:
-    case OFPP_TABLE:
-    case OFPP_NORMAL:
-    case OFPP_FLOOD:
-    case OFPP_ALL:
-    case OFPP_NONE:
-        return true;
-    case OFPP_CONTROLLER: /* Not supported by the bundle action. */
-        return false;
-    default:
-        port = get_ofp_port(ofproto, ofp_port);
-        return port ? port->may_enable : false;
-    }
-}
-
-static void
-xlate_bundle_action(struct action_xlate_ctx *ctx,
-                    const struct ofpact_bundle *bundle)
-{
-    uint16_t port;
-
-    port = bundle_execute(bundle, &ctx->flow, slave_enabled_cb, ctx->ofproto);
-    if (bundle->dst.field) {
-        nxm_reg_load(&bundle->dst, port, &ctx->flow);
-    } else {
-        xlate_output_action(ctx, port, 0, false);
-    }
-}
-
-static void
-xlate_learn_action(struct action_xlate_ctx *ctx,
-                   const struct ofpact_learn *learn)
-{
-    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
-    struct ofputil_flow_mod fm;
-    uint64_t ofpacts_stub[1024 / 8];
-    struct ofpbuf ofpacts;
-    int error;
-
-    ofpbuf_use_stack(&ofpacts, ofpacts_stub, sizeof ofpacts_stub);
-    learn_execute(learn, &ctx->flow, &fm, &ofpacts);
-
-    error = ofproto_flow_mod(&ctx->ofproto->up, &fm);
-    if (error && !VLOG_DROP_WARN(&rl)) {
-        VLOG_WARN("learning action failed to modify flow table (%s)",
-                  ofperr_get_name(error));
-    }
-
-    ofpbuf_uninit(&ofpacts);
-}
-
-/* Reduces '*timeout' to no more than 'max'.  A value of zero in either case
- * means "infinite". */
-static void
-reduce_timeout(uint16_t max, uint16_t *timeout)
-{
-    if (max && (!*timeout || *timeout > max)) {
-        *timeout = max;
-    }
-}
-
-static void
-xlate_fin_timeout(struct action_xlate_ctx *ctx,
-                  const struct ofpact_fin_timeout *oft)
-{
-    if (ctx->tcp_flags & (TCP_FIN | TCP_RST) && ctx->rule) {
-        struct rule_dpif *rule = ctx->rule;
-
-        reduce_timeout(oft->fin_idle_timeout, &rule->up.idle_timeout);
-        reduce_timeout(oft->fin_hard_timeout, &rule->up.hard_timeout);
-    }
-}
-
-static void
-xlate_sample_action(struct action_xlate_ctx *ctx,
-                    const struct ofpact_sample *os)
-{
-  union user_action_cookie cookie;
-  /* Scale the probability from 16-bit to 32-bit while representing
-   * the same percentage. */
-  uint32_t probability = (os->probability << 16) | os->probability;
-
-  commit_odp_actions(&ctx->flow, &ctx->base_flow, ctx->odp_actions);
-
-  compose_flow_sample_cookie(os->probability, os->collector_set_id,
-                             os->obs_domain_id, os->obs_point_id, &cookie);
-  compose_sample_action(ctx->ofproto, ctx->odp_actions, &ctx->flow,
-                        probability, &cookie, sizeof cookie.flow_sample);
-}
-
-static bool
-may_receive(const struct ofport_dpif *port, struct action_xlate_ctx *ctx)
-{
-    if (port->up.pp.config & (eth_addr_equals(ctx->flow.dl_dst, eth_addr_stp)
-                              ? OFPUTIL_PC_NO_RECV_STP
-                              : OFPUTIL_PC_NO_RECV)) {
-        return false;
-    }
-
-    /* Only drop packets here if both forwarding and learning are
-     * disabled.  If just learning is enabled, we need to have
-     * OFPP_NORMAL and the learning action have a look at the packet
-     * before we can drop it. */
-    if (!stp_forward_in_state(port->stp_state)
-            && !stp_learn_in_state(port->stp_state)) {
-        return false;
-    }
-
-    return true;
-}
-
-static bool
-tunnel_ecn_ok(struct action_xlate_ctx *ctx)
-{
-    if (is_ip_any(&ctx->base_flow)
-        && (ctx->flow.tunnel.ip_tos & IP_ECN_MASK) == IP_ECN_CE) {
-        if ((ctx->base_flow.nw_tos & IP_ECN_MASK) == IP_ECN_NOT_ECT) {
-            VLOG_WARN_RL(&rl, "dropping tunnel packet marked ECN CE"
-                         " but is not ECN capable");
-            return false;
-        } else {
-            /* Set the ECN CE value in the tunneled packet. */
-            ctx->flow.nw_tos |= IP_ECN_CE;
-        }
-    }
-
-    return true;
-}
-
-static void
-do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
-                 struct action_xlate_ctx *ctx)
-{
-    bool was_evictable = true;
-    const struct ofpact *a;
-
-    if (ctx->rule) {
-        /* Don't let the rule we're working on get evicted underneath us. */
-        was_evictable = ctx->rule->up.evictable;
-        ctx->rule->up.evictable = false;
-    }
-
- do_xlate_actions_again:
-    OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
-        struct ofpact_controller *controller;
-        const struct ofpact_metadata *metadata;
-
-        if (ctx->exit) {
-            break;
-        }
-
-        switch (a->type) {
-        case OFPACT_OUTPUT:
-            xlate_output_action(ctx, ofpact_get_OUTPUT(a)->port,
-                                ofpact_get_OUTPUT(a)->max_len, true);
-            break;
-
-        case OFPACT_CONTROLLER:
-            controller = ofpact_get_CONTROLLER(a);
-            execute_controller_action(ctx, controller->max_len,
-                                      controller->reason,
-                                      controller->controller_id);
-            break;
-
-        case OFPACT_ENQUEUE:
-            xlate_enqueue_action(ctx, ofpact_get_ENQUEUE(a));
-            break;
-
-        case OFPACT_SET_VLAN_VID:
-            ctx->flow.vlan_tci &= ~htons(VLAN_VID_MASK);
-            ctx->flow.vlan_tci |= (htons(ofpact_get_SET_VLAN_VID(a)->vlan_vid)
-                                   | htons(VLAN_CFI));
-            break;
-
-        case OFPACT_SET_VLAN_PCP:
-            ctx->flow.vlan_tci &= ~htons(VLAN_PCP_MASK);
-            ctx->flow.vlan_tci |= htons((ofpact_get_SET_VLAN_PCP(a)->vlan_pcp
-                                         << VLAN_PCP_SHIFT)
-                                        | VLAN_CFI);
-            break;
-
-        case OFPACT_STRIP_VLAN:
-            ctx->flow.vlan_tci = htons(0);
-            break;
-
-        case OFPACT_PUSH_VLAN:
-            /* XXX 802.1AD(QinQ) */
-            ctx->flow.vlan_tci = htons(VLAN_CFI);
-            break;
-
-        case OFPACT_SET_ETH_SRC:
-            memcpy(ctx->flow.dl_src, ofpact_get_SET_ETH_SRC(a)->mac,
-                   ETH_ADDR_LEN);
-            break;
-
-        case OFPACT_SET_ETH_DST:
-            memcpy(ctx->flow.dl_dst, ofpact_get_SET_ETH_DST(a)->mac,
-                   ETH_ADDR_LEN);
-            break;
-
-        case OFPACT_SET_IPV4_SRC:
-            if (ctx->flow.dl_type == htons(ETH_TYPE_IP)) {
-                ctx->flow.nw_src = ofpact_get_SET_IPV4_SRC(a)->ipv4;
-            }
-            break;
-
-        case OFPACT_SET_IPV4_DST:
-            if (ctx->flow.dl_type == htons(ETH_TYPE_IP)) {
-                ctx->flow.nw_dst = ofpact_get_SET_IPV4_DST(a)->ipv4;
-            }
-            break;
-
-        case OFPACT_SET_IPV4_DSCP:
-            /* OpenFlow 1.0 only supports IPv4. */
-            if (ctx->flow.dl_type == htons(ETH_TYPE_IP)) {
-                ctx->flow.nw_tos &= ~IP_DSCP_MASK;
-                ctx->flow.nw_tos |= ofpact_get_SET_IPV4_DSCP(a)->dscp;
-            }
-            break;
-
-        case OFPACT_SET_L4_SRC_PORT:
-            if (is_ip_any(&ctx->flow)) {
-                ctx->flow.tp_src = htons(ofpact_get_SET_L4_SRC_PORT(a)->port);
-            }
-            break;
-
-        case OFPACT_SET_L4_DST_PORT:
-            if (is_ip_any(&ctx->flow)) {
-                ctx->flow.tp_dst = htons(ofpact_get_SET_L4_DST_PORT(a)->port);
-            }
-            break;
-
-        case OFPACT_RESUBMIT:
-            xlate_ofpact_resubmit(ctx, ofpact_get_RESUBMIT(a));
-            break;
-
-        case OFPACT_SET_TUNNEL:
-            ctx->flow.tunnel.tun_id = htonll(ofpact_get_SET_TUNNEL(a)->tun_id);
-            break;
-
-        case OFPACT_SET_QUEUE:
-            xlate_set_queue_action(ctx, ofpact_get_SET_QUEUE(a)->queue_id);
-            break;
-
-        case OFPACT_POP_QUEUE:
-            ctx->flow.skb_priority = ctx->orig_skb_priority;
-            break;
-
-        case OFPACT_REG_MOVE:
-            nxm_execute_reg_move(ofpact_get_REG_MOVE(a), &ctx->flow);
-            break;
-
-        case OFPACT_REG_LOAD:
-            nxm_execute_reg_load(ofpact_get_REG_LOAD(a), &ctx->flow);
-            break;
-
-        case OFPACT_STACK_PUSH:
-            nxm_execute_stack_push(ofpact_get_STACK_PUSH(a), &ctx->flow,
-                                   &ctx->stack);
-            break;
-
-        case OFPACT_STACK_POP:
-            nxm_execute_stack_pop(ofpact_get_STACK_POP(a), &ctx->flow,
-                                  &ctx->stack);
-            break;
-
-        case OFPACT_PUSH_MPLS:
-            execute_mpls_push_action(ctx, ofpact_get_PUSH_MPLS(a)->ethertype);
-            break;
-
-        case OFPACT_POP_MPLS:
-            execute_mpls_pop_action(ctx, ofpact_get_POP_MPLS(a)->ethertype);
-            break;
-
-        case OFPACT_SET_MPLS_TTL:
-            if (execute_set_mpls_ttl_action(ctx, ofpact_get_SET_MPLS_TTL(a)->ttl)) {
-                goto out;
-            }
-            break;
-
-        case OFPACT_DEC_MPLS_TTL:
-            if (execute_dec_mpls_ttl_action(ctx)) {
-                goto out;
-            }
-            break;
-
-        case OFPACT_DEC_TTL:
-            if (compose_dec_ttl(ctx, ofpact_get_DEC_TTL(a))) {
-                goto out;
-            }
-            break;
-
-        case OFPACT_NOTE:
-            /* Nothing to do. */
-            break;
-
-        case OFPACT_MULTIPATH:
-            multipath_execute(ofpact_get_MULTIPATH(a), &ctx->flow);
-            break;
-
-        case OFPACT_BUNDLE:
-            ctx->ofproto->has_bundle_action = true;
-            xlate_bundle_action(ctx, ofpact_get_BUNDLE(a));
-            break;
-
-        case OFPACT_OUTPUT_REG:
-            xlate_output_reg_action(ctx, ofpact_get_OUTPUT_REG(a));
-            break;
-
-        case OFPACT_LEARN:
-            ctx->has_learn = true;
-            if (ctx->may_learn) {
-                xlate_learn_action(ctx, ofpact_get_LEARN(a));
-            }
-            break;
-
-        case OFPACT_EXIT:
-            ctx->exit = true;
-            break;
-
-        case OFPACT_FIN_TIMEOUT:
-            ctx->has_fin_timeout = true;
-            xlate_fin_timeout(ctx, ofpact_get_FIN_TIMEOUT(a));
-            break;
-
-        case OFPACT_CLEAR_ACTIONS:
-            /* 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.
-             */
-            break;
-
-        case OFPACT_WRITE_METADATA:
-            metadata = ofpact_get_WRITE_METADATA(a);
-            ctx->flow.metadata &= ~metadata->mask;
-            ctx->flow.metadata |= metadata->metadata & metadata->mask;
-            break;
-
-        case OFPACT_GOTO_TABLE: {
-            /* It is assumed that goto-table is the last action. */
-            struct ofpact_goto_table *ogt = ofpact_get_GOTO_TABLE(a);
-            struct rule_dpif *rule;
-
-            ovs_assert(ctx->table_id < ogt->table_id);
-
-            ctx->table_id = ogt->table_id;
-
-            /* Look up a flow from the new table. */
-            rule = rule_dpif_lookup__(ctx->ofproto, &ctx->flow, ctx->table_id);
-
-            tag_the_flow(ctx, rule);
-
-            rule = ctx_rule_hooks(ctx, rule, true);
-
-            if (rule) {
-                if (ctx->rule) {
-                    ctx->rule->up.evictable = was_evictable;
-                }
-                ctx->rule = rule;
-                was_evictable = rule->up.evictable;
-                rule->up.evictable = false;
-
-                /* Tail recursion removal. */
-                ofpacts = rule->up.ofpacts;
-                ofpacts_len = rule->up.ofpacts_len;
-                goto do_xlate_actions_again;
-            }
-            break;
-        }
-
-        case OFPACT_SAMPLE:
-            xlate_sample_action(ctx, ofpact_get_SAMPLE(a));
-            break;
-        }
-    }
-
-out:
-    if (ctx->rule) {
-        ctx->rule->up.evictable = was_evictable;
-    }
-}
-
-static void
-action_xlate_ctx_init(struct action_xlate_ctx *ctx,
-                      struct ofproto_dpif *ofproto, const struct flow *flow,
-                      const struct initial_vals *initial_vals,
-                      struct rule_dpif *rule,
-                      uint8_t tcp_flags, const struct ofpbuf *packet)
-{
-    /* 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 metadata as received is retained in 'flow'. This allows
-     *   tunnel metadata matching also in later tables.
-     *   Since a kernel action for setting the tunnel metadata will only be
-     *   generated with actual tunnel output, changing the tunnel metadata
-     *   values in 'flow' (such as tun_id) will only have effect with a later
-     *   tunnel output action.
-     * - 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;
-    ctx->base_flow = ctx->flow;
-    memset(&ctx->base_flow.tunnel, 0, sizeof ctx->base_flow.tunnel);
-    ctx->orig_tunnel_ip_dst = flow->tunnel.ip_dst;
-    ctx->rule = rule;
-    ctx->packet = packet;
-    ctx->may_learn = packet != NULL;
-    ctx->tcp_flags = tcp_flags;
-    ctx->resubmit_hook = NULL;
-    ctx->report_hook = NULL;
-    ctx->resubmit_stats = NULL;
-
-    if (initial_vals) {
-        ctx->base_flow.vlan_tci = initial_vals->vlan_tci;
-    }
-}
-
-/* Translates the 'ofpacts_len' bytes of "struct ofpacts" starting at 'ofpacts'
- * into datapath actions in 'odp_actions', using 'ctx'. */
-static void
-xlate_actions(struct action_xlate_ctx *ctx,
-              const struct ofpact *ofpacts, size_t ofpacts_len,
-              struct ofpbuf *odp_actions)
-{
-    /* Normally false.  Set to true if we ever hit MAX_RESUBMIT_RECURSION, so
-     * that in the future we always keep a copy of the original flow for
-     * tracing purposes. */
-    static bool hit_resubmit_limit;
-
-    enum slow_path_reason special;
-    struct ofport_dpif *in_port;
-    struct flow orig_flow;
-
-    COVERAGE_INC(ofproto_dpif_xlate);
-
-    ofpbuf_clear(odp_actions);
-    ofpbuf_reserve(odp_actions, NL_A_U32_SIZE);
-
-    ctx->odp_actions = odp_actions;
-    ctx->tags = 0;
-    ctx->slow = 0;
-    ctx->has_learn = false;
-    ctx->has_normal = false;
-    ctx->has_fin_timeout = false;
-    ctx->nf_output_iface = NF_OUT_DROP;
-    ctx->mirrors = 0;
-    ctx->recurse = 0;
-    ctx->max_resubmit_trigger = false;
-    ctx->orig_skb_priority = ctx->flow.skb_priority;
-    ctx->table_id = 0;
-    ctx->exit = false;
-
-    ofpbuf_use_stub(&ctx->stack, ctx->init_stack, sizeof ctx->init_stack);
-
-    if (ctx->ofproto->has_mirrors || hit_resubmit_limit) {
-        /* Do this conditionally because the copy is expensive enough that it
-         * shows up in profiles. */
-        orig_flow = ctx->flow;
-    }
-
-    if (ctx->flow.nw_frag & FLOW_NW_FRAG_ANY) {
-        switch (ctx->ofproto->up.frag_handling) {
-        case OFPC_FRAG_NORMAL:
-            /* We must pretend that transport ports are unavailable. */
-            ctx->flow.tp_src = ctx->base_flow.tp_src = htons(0);
-            ctx->flow.tp_dst = ctx->base_flow.tp_dst = htons(0);
-            break;
-
-        case OFPC_FRAG_DROP:
-            return;
-
-        case OFPC_FRAG_REASM:
-            NOT_REACHED();
-
-        case OFPC_FRAG_NX_MATCH:
-            /* Nothing to do. */
-            break;
-
-        case OFPC_INVALID_TTL_TO_CONTROLLER:
-            NOT_REACHED();
-        }
-    }
-
-    in_port = get_ofp_port(ctx->ofproto, ctx->flow.in_port);
-    special = process_special(ctx->ofproto, &ctx->flow, in_port, ctx->packet);
-    if (special) {
-        ctx->slow |= special;
-    } else {
-        static struct vlog_rate_limit trace_rl = VLOG_RATE_LIMIT_INIT(1, 1);
-        struct initial_vals initial_vals;
-        size_t sample_actions_len;
-        uint32_t local_odp_port;
-
-        initial_vals.vlan_tci = ctx->base_flow.vlan_tci;
-
-        add_sflow_action(ctx);
-        add_ipfix_action(ctx);
-        sample_actions_len = ctx->odp_actions->size;
-
-        if (tunnel_ecn_ok(ctx) && (!in_port || may_receive(in_port, ctx))) {
-            do_xlate_actions(ofpacts, ofpacts_len, ctx);
-
-            /* We've let OFPP_NORMAL and the learning action look at the
-             * packet, so drop it now if forwarding is disabled. */
-            if (in_port && !stp_forward_in_state(in_port->stp_state)) {
-                ctx->odp_actions->size = sample_actions_len;
-            }
-        }
-
-        if (ctx->max_resubmit_trigger && !ctx->resubmit_hook) {
-            if (!hit_resubmit_limit) {
-                /* We didn't record the original flow.  Make sure we do from
-                 * now on. */
-                hit_resubmit_limit = true;
-            } else if (!VLOG_DROP_ERR(&trace_rl)) {
-                struct ds ds = DS_EMPTY_INITIALIZER;
-
-                ofproto_trace(ctx->ofproto, &orig_flow, ctx->packet,
-                              &initial_vals, &ds);
-                VLOG_ERR("Trace triggered by excessive resubmit "
-                         "recursion:\n%s", ds_cstr(&ds));
-                ds_destroy(&ds);
-            }
-        }
-
-        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;
-            if (ctx->packet
-                && connmgr_msg_in_hook(ctx->ofproto->up.connmgr, &ctx->flow,
-                                       ctx->packet)) {
-                compose_output_action(ctx, OFPP_LOCAL);
-            }
-        }
-        if (ctx->ofproto->has_mirrors) {
-            add_mirror_actions(ctx, &orig_flow);
-        }
-        fix_sflow_action(ctx);
-    }
-
-    ofpbuf_uninit(&ctx->stack);
-}
-
-/* Translates the 'ofpacts_len' bytes of "struct ofpact"s starting at 'ofpacts'
- * into datapath actions, using 'ctx', and discards the datapath actions. */
-static void
-xlate_actions_for_side_effects(struct action_xlate_ctx *ctx,
-                               const struct ofpact *ofpacts,
-                               size_t ofpacts_len)
-{
-    uint64_t odp_actions_stub[1024 / 8];
-    struct ofpbuf odp_actions;
-
-    ofpbuf_use_stub(&odp_actions, odp_actions_stub, sizeof odp_actions_stub);
-    xlate_actions(ctx, ofpacts, ofpacts_len, &odp_actions);
-    ofpbuf_uninit(&odp_actions);
-}
-
-static void
-xlate_report(struct action_xlate_ctx *ctx, const char *s)
-{
-    if (ctx->report_hook) {
-        ctx->report_hook(ctx, s);
-    }
-}
-\f
-/* OFPP_NORMAL implementation. */
-
-static struct ofport_dpif *ofbundle_get_a_port(const struct ofbundle *);
-
-/* Given 'vid', the VID obtained from the 802.1Q header that was received as
- * part of a packet (specify 0 if there was no 802.1Q header), and 'in_bundle',
- * the bundle on which the packet was received, returns the VLAN to which the
- * packet belongs.
- *
- * Both 'vid' and the return value are in the range 0...4095. */
-static uint16_t
-input_vid_to_vlan(const struct ofbundle *in_bundle, uint16_t vid)
-{
-    switch (in_bundle->vlan_mode) {
-    case PORT_VLAN_ACCESS:
-        return in_bundle->vlan;
-        break;
-
-    case PORT_VLAN_TRUNK:
-        return vid;
-
-    case PORT_VLAN_NATIVE_UNTAGGED:
-    case PORT_VLAN_NATIVE_TAGGED:
-        return vid ? vid : in_bundle->vlan;
-
-    default:
-        NOT_REACHED();
-    }
-}
-
-/* Checks whether a packet with the given 'vid' may ingress on 'in_bundle'.
- * If so, returns true.  Otherwise, returns false and, if 'warn' is true, logs
- * a warning.
- *
- * 'vid' should be the VID obtained from the 802.1Q header that was received as
- * part of a packet (specify 0 if there was no 802.1Q header), in the range
- * 0...4095. */
-static bool
-input_vid_is_valid(uint16_t vid, struct ofbundle *in_bundle, bool warn)
-{
-    /* Allow any VID on the OFPP_NONE port. */
-    if (in_bundle == &ofpp_none_bundle) {
-        return true;
-    }
-
-    switch (in_bundle->vlan_mode) {
-    case PORT_VLAN_ACCESS:
-        if (vid) {
-            if (warn) {
-                static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
-                VLOG_WARN_RL(&rl, "bridge %s: dropping VLAN %"PRIu16" tagged "
-                             "packet received on port %s configured as VLAN "
-                             "%"PRIu16" access port",
-                             in_bundle->ofproto->up.name, vid,
-                             in_bundle->name, in_bundle->vlan);
-            }
-            return false;
-        }
-        return true;
-
-    case PORT_VLAN_NATIVE_UNTAGGED:
-    case PORT_VLAN_NATIVE_TAGGED:
-        if (!vid) {
-            /* Port must always carry its native VLAN. */
-            return true;
-        }
-        /* Fall through. */
-    case PORT_VLAN_TRUNK:
-        if (!ofbundle_includes_vlan(in_bundle, vid)) {
-            if (warn) {
-                static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
-                VLOG_WARN_RL(&rl, "bridge %s: dropping VLAN %"PRIu16" packet "
-                             "received on port %s not configured for trunking "
-                             "VLAN %"PRIu16,
-                             in_bundle->ofproto->up.name, vid,
-                             in_bundle->name, vid);
-            }
-            return false;
-        }
-        return true;
-
-    default:
-        NOT_REACHED();
-    }
-
-}
-
-/* Given 'vlan', the VLAN that a packet belongs to, and
- * 'out_bundle', a bundle on which the packet is to be output, returns the VID
- * that should be included in the 802.1Q header.  (If the return value is 0,
- * then the 802.1Q header should only be included in the packet if there is a
- * nonzero PCP.)
- *
- * Both 'vlan' and the return value are in the range 0...4095. */
-static uint16_t
-output_vlan_to_vid(const struct ofbundle *out_bundle, uint16_t vlan)
-{
-    switch (out_bundle->vlan_mode) {
-    case PORT_VLAN_ACCESS:
-        return 0;
-
-    case PORT_VLAN_TRUNK:
-    case PORT_VLAN_NATIVE_TAGGED:
-        return vlan;
-
-    case PORT_VLAN_NATIVE_UNTAGGED:
-        return vlan == out_bundle->vlan ? 0 : vlan;
-
-    default:
-        NOT_REACHED();
-    }
-}
-
-static void
-output_normal(struct action_xlate_ctx *ctx, const struct ofbundle *out_bundle,
-              uint16_t vlan)
-{
-    struct ofport_dpif *port;
-    uint16_t vid;
-    ovs_be16 tci, old_tci;
-
-    vid = output_vlan_to_vid(out_bundle, vlan);
-    if (!out_bundle->bond) {
-        port = ofbundle_get_a_port(out_bundle);
-    } else {
-        port = bond_choose_output_slave(out_bundle->bond, &ctx->flow,
-                                        vid, &ctx->tags);
-        if (!port) {
-            /* No slaves enabled, so drop packet. */
-            return;
-        }
-    }
-
-    old_tci = ctx->flow.vlan_tci;
-    tci = htons(vid);
-    if (tci || out_bundle->use_priority_tags) {
-        tci |= ctx->flow.vlan_tci & htons(VLAN_PCP_MASK);
-        if (tci) {
-            tci |= htons(VLAN_CFI);
-        }
-    }
-    ctx->flow.vlan_tci = tci;
-
-    compose_output_action(ctx, port->up.ofp_port);
-    ctx->flow.vlan_tci = old_tci;
-}
-
-static int
-mirror_mask_ffs(mirror_mask_t mask)
-{
-    BUILD_ASSERT_DECL(sizeof(unsigned int) >= sizeof(mask));
-    return ffs(mask);
-}
-
-static bool
-ofbundle_trunks_vlan(const struct ofbundle *bundle, uint16_t vlan)
-{
-    return (bundle->vlan_mode != PORT_VLAN_ACCESS
-            && (!bundle->trunks || bitmap_is_set(bundle->trunks, vlan)));
-}
-
-static bool
-ofbundle_includes_vlan(const struct ofbundle *bundle, uint16_t vlan)
-{
-    return vlan == bundle->vlan || ofbundle_trunks_vlan(bundle, vlan);
-}
-
-/* Returns an arbitrary interface within 'bundle'. */
-static struct ofport_dpif *
-ofbundle_get_a_port(const struct ofbundle *bundle)
-{
-    return CONTAINER_OF(list_front(&bundle->ports),
-                        struct ofport_dpif, bundle_node);
-}
-
-static bool
-vlan_is_mirrored(const struct ofmirror *m, int vlan)
-{
-    return !m->vlans || bitmap_is_set(m->vlans, vlan);
-}
-
-static void
-add_mirror_actions(struct action_xlate_ctx *ctx, const struct flow *orig_flow)
-{
-    struct ofproto_dpif *ofproto = ctx->ofproto;
-    mirror_mask_t mirrors;
-    struct ofbundle *in_bundle;
-    uint16_t vlan;
-    uint16_t vid;
-    const struct nlattr *a;
-    size_t left;
-
-    in_bundle = lookup_input_bundle(ctx->ofproto, orig_flow->in_port,
-                                    ctx->packet != NULL, NULL);
-    if (!in_bundle) {
-        return;
-    }
-    mirrors = in_bundle->src_mirrors;
-
-    /* Drop frames on bundles reserved for mirroring. */
-    if (in_bundle->mirror_out) {
-        if (ctx->packet != NULL) {
-            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
-            VLOG_WARN_RL(&rl, "bridge %s: dropping packet received on port "
-                         "%s, which is reserved exclusively for mirroring",
-                         ctx->ofproto->up.name, in_bundle->name);
-        }
-        return;
-    }
-
-    /* Check VLAN. */
-    vid = vlan_tci_to_vid(orig_flow->vlan_tci);
-    if (!input_vid_is_valid(vid, in_bundle, ctx->packet != NULL)) {
-        return;
-    }
-    vlan = input_vid_to_vlan(in_bundle, vid);
-
-    /* Look at the output ports to check for destination selections. */
-
-    NL_ATTR_FOR_EACH (a, left, ctx->odp_actions->data,
-                      ctx->odp_actions->size) {
-        enum ovs_action_attr type = nl_attr_type(a);
-        struct ofport_dpif *ofport;
-
-        if (type != OVS_ACTION_ATTR_OUTPUT) {
-            continue;
-        }
-
-        ofport = get_odp_port(ofproto, nl_attr_get_u32(a));
-        if (ofport && ofport->bundle) {
-            mirrors |= ofport->bundle->dst_mirrors;
-        }
-    }
-
-    if (!mirrors) {
-        return;
-    }
-
-    /* Restore the original packet before adding the mirror actions. */
-    ctx->flow = *orig_flow;
-
-    while (mirrors) {
-        struct ofmirror *m;
-
-        m = ofproto->mirrors[mirror_mask_ffs(mirrors) - 1];
-
-        if (!vlan_is_mirrored(m, vlan)) {
-            mirrors = zero_rightmost_1bit(mirrors);
-            continue;
-        }
-
-        mirrors &= ~m->dup_mirrors;
-        ctx->mirrors |= m->dup_mirrors;
-        if (m->out) {
-            output_normal(ctx, m->out, vlan);
-        } else if (vlan != m->out_vlan
-                   && !eth_addr_is_reserved(orig_flow->dl_dst)) {
-            struct ofbundle *bundle;
-
-            HMAP_FOR_EACH (bundle, hmap_node, &ofproto->bundles) {
-                if (ofbundle_includes_vlan(bundle, m->out_vlan)
-                    && !bundle->mirror_out) {
-                    output_normal(ctx, bundle, m->out_vlan);
-                }
-            }
-        }
-    }
-}
-
-static void
-update_mirror_stats(struct ofproto_dpif *ofproto, mirror_mask_t mirrors,
-                    uint64_t packets, uint64_t bytes)
-{
-    if (!mirrors) {
-        return;
-    }
-
-    for (; mirrors; mirrors = zero_rightmost_1bit(mirrors)) {
-        struct ofmirror *m;
-
-        m = ofproto->mirrors[mirror_mask_ffs(mirrors) - 1];
-
-        if (!m) {
-            /* In normal circumstances 'm' will not be NULL.  However,
-             * if mirrors are reconfigured, we can temporarily get out
-             * of sync in facet_revalidate().  We could "correct" the
-             * mirror list before reaching here, but doing that would
-             * not properly account the traffic stats we've currently
-             * accumulated for previous mirror configuration. */
-            continue;
-        }
-
-        m->packet_count += packets;
-        m->byte_count += bytes;
-    }
-}
-
-/* A VM broadcasts a gratuitous ARP to indicate that it has resumed after
- * migration.  Older Citrix-patched Linux DomU used gratuitous ARP replies to
- * indicate this; newer upstream kernels use gratuitous ARP requests. */
-static bool
-is_gratuitous_arp(const struct flow *flow)
-{
-    return (flow->dl_type == htons(ETH_TYPE_ARP)
-            && eth_addr_is_broadcast(flow->dl_dst)
-            && (flow->nw_proto == ARP_OP_REPLY
-                || (flow->nw_proto == ARP_OP_REQUEST
-                    && flow->nw_src == flow->nw_dst)));
-}
-
-static void
-update_learning_table(struct ofproto_dpif *ofproto,
-                      const struct flow *flow, int vlan,
-                      struct ofbundle *in_bundle)
-{
-    struct mac_entry *mac;
-
-    /* Don't learn the OFPP_NONE port. */
-    if (in_bundle == &ofpp_none_bundle) {
-        return;
-    }
-
-    if (!mac_learning_may_learn(ofproto->ml, flow->dl_src, vlan)) {
-        return;
-    }
-
-    mac = mac_learning_insert(ofproto->ml, flow->dl_src, vlan);
-    if (is_gratuitous_arp(flow)) {
-        /* We don't want to learn from gratuitous ARP packets that are
-         * reflected back over bond slaves so we lock the learning table. */
-        if (!in_bundle->bond) {
-            mac_entry_set_grat_arp_lock(mac);
-        } else if (mac_entry_is_grat_arp_locked(mac)) {
-            return;
-        }
-    }
-
-    if (mac_entry_is_new(mac) || mac->port.p != in_bundle) {
-        /* The log messages here could actually be useful in debugging,
-         * so keep the rate limit relatively high. */
-        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(30, 300);
-        VLOG_DBG_RL(&rl, "bridge %s: learned that "ETH_ADDR_FMT" is "
-                    "on port %s in VLAN %d",
-                    ofproto->up.name, ETH_ADDR_ARGS(flow->dl_src),
-                    in_bundle->name, vlan);
-
-        mac->port.p = in_bundle;
-        tag_set_add(&ofproto->backer->revalidate_set,
-                    mac_learning_changed(ofproto->ml, mac));
-    }
-}
-
-static struct ofbundle *
-lookup_input_bundle(const struct ofproto_dpif *ofproto, uint16_t in_port,
-                    bool warn, struct ofport_dpif **in_ofportp)
-{
-    struct ofport_dpif *ofport;
-
-    /* Find the port and bundle for the received packet. */
-    ofport = get_ofp_port(ofproto, in_port);
-    if (in_ofportp) {
-        *in_ofportp = ofport;
-    }
-    if (ofport && ofport->bundle) {
-        return ofport->bundle;
-    }
-
-    /* Special-case OFPP_NONE, which a controller may use as the ingress
-     * port for traffic that it is sourcing. */
-    if (in_port == OFPP_NONE) {
-        return &ofpp_none_bundle;
-    }
-
-    /* Odd.  A few possible reasons here:
-     *
-     * - We deleted a port but there are still a few packets queued up
-     *   from it.
-     *
-     * - Someone externally added a port (e.g. "ovs-dpctl add-if") that
-     *   we don't know about.
-     *
-     * - The ofproto client didn't configure the port as part of a bundle.
-     *   This is particularly likely to happen if a packet was received on the
-     *   port after it was created, but before the client had a chance to
-     *   configure its bundle.
-     */
-    if (warn) {
-        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
-
-        VLOG_WARN_RL(&rl, "bridge %s: received packet on unknown "
-                     "port %"PRIu16, ofproto->up.name, in_port);
-    }
-    return NULL;
-}
-
-/* Determines whether packets in 'flow' within 'ofproto' should be forwarded or
- * dropped.  Returns true if they may be forwarded, false if they should be
- * dropped.
- *
- * 'in_port' must be the ofport_dpif that corresponds to flow->in_port.
- * 'in_port' must be part of a bundle (e.g. in_port->bundle must be nonnull).
- *
- * 'vlan' must be the VLAN that corresponds to flow->vlan_tci on 'in_port', as
- * returned by input_vid_to_vlan().  It must be a valid VLAN for 'in_port', as
- * checked by input_vid_is_valid().
- *
- * May also add tags to '*tags', although the current implementation only does
- * so in one special case.
- */
-static bool
-is_admissible(struct action_xlate_ctx *ctx, struct ofport_dpif *in_port,
-              uint16_t vlan)
-{
-    struct ofproto_dpif *ofproto = ctx->ofproto;
-    struct flow *flow = &ctx->flow;
-    struct ofbundle *in_bundle = in_port->bundle;
-
-    /* Drop frames for reserved multicast addresses
-     * only if forward_bpdu option is absent. */
-    if (!ofproto->up.forward_bpdu && eth_addr_is_reserved(flow->dl_dst)) {
-        xlate_report(ctx, "packet has reserved destination MAC, dropping");
-        return false;
-    }
-
-    if (in_bundle->bond) {
-        struct mac_entry *mac;
-
-        switch (bond_check_admissibility(in_bundle->bond, in_port,
-                                         flow->dl_dst, &ctx->tags)) {
-        case BV_ACCEPT:
-            break;
-
-        case BV_DROP:
-            xlate_report(ctx, "bonding refused admissibility, dropping");
-            return false;
-
-        case BV_DROP_IF_MOVED:
-            mac = mac_learning_lookup(ofproto->ml, flow->dl_src, vlan, NULL);
-            if (mac && mac->port.p != in_bundle &&
-                (!is_gratuitous_arp(flow)
-                 || mac_entry_is_grat_arp_locked(mac))) {
-                xlate_report(ctx, "SLB bond thinks this packet looped back, "
-                            "dropping");
-                return false;
-            }
-            break;
-        }
-    }
-
-    return true;
-}
-
-static void
-xlate_normal(struct action_xlate_ctx *ctx)
-{
-    struct ofport_dpif *in_port;
-    struct ofbundle *in_bundle;
-    struct mac_entry *mac;
-    uint16_t vlan;
-    uint16_t vid;
-
-    ctx->has_normal = true;
-
-    in_bundle = lookup_input_bundle(ctx->ofproto, ctx->flow.in_port,
-                                    ctx->packet != NULL, &in_port);
-    if (!in_bundle) {
-        xlate_report(ctx, "no input bundle, dropping");
-        return;
-    }
-
-    /* Drop malformed frames. */
-    if (ctx->flow.dl_type == htons(ETH_TYPE_VLAN) &&
-        !(ctx->flow.vlan_tci & htons(VLAN_CFI))) {
-        if (ctx->packet != NULL) {
-            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
-            VLOG_WARN_RL(&rl, "bridge %s: dropping packet with partial "
-                         "VLAN tag received on port %s",
-                         ctx->ofproto->up.name, in_bundle->name);
-        }
-        xlate_report(ctx, "partial VLAN tag, dropping");
-        return;
-    }
-
-    /* Drop frames on bundles reserved for mirroring. */
-    if (in_bundle->mirror_out) {
-        if (ctx->packet != NULL) {
-            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
-            VLOG_WARN_RL(&rl, "bridge %s: dropping packet received on port "
-                         "%s, which is reserved exclusively for mirroring",
-                         ctx->ofproto->up.name, in_bundle->name);
-        }
-        xlate_report(ctx, "input port is mirror output port, dropping");
-        return;
-    }
-
-    /* Check VLAN. */
-    vid = vlan_tci_to_vid(ctx->flow.vlan_tci);
-    if (!input_vid_is_valid(vid, in_bundle, ctx->packet != NULL)) {
-        xlate_report(ctx, "disallowed VLAN VID for this input port, dropping");
-        return;
-    }
-    vlan = input_vid_to_vlan(in_bundle, vid);
-
-    /* Check other admissibility requirements. */
-    if (in_port && !is_admissible(ctx, in_port, vlan)) {
-        return;
-    }
-
-    /* Learn source MAC. */
-    if (ctx->may_learn) {
-        update_learning_table(ctx->ofproto, &ctx->flow, vlan, in_bundle);
-    }
-
-    /* Determine output bundle. */
-    mac = mac_learning_lookup(ctx->ofproto->ml, ctx->flow.dl_dst, vlan,
-                              &ctx->tags);
-    if (mac) {
-        if (mac->port.p != in_bundle) {
-            xlate_report(ctx, "forwarding to learned port");
-            output_normal(ctx, mac->port.p, vlan);
-        } else {
-            xlate_report(ctx, "learned port is input port, dropping");
-        }
-    } else {
-        struct ofbundle *bundle;
-
-        xlate_report(ctx, "no learned MAC for destination, flooding");
-        HMAP_FOR_EACH (bundle, hmap_node, &ctx->ofproto->bundles) {
-            if (bundle != in_bundle
-                && ofbundle_includes_vlan(bundle, vlan)
-                && bundle->floodable
-                && !bundle->mirror_out) {
-                output_normal(ctx, bundle, vlan);
-            }
-        }
-        ctx->nf_output_iface = NF_OUT_FLOOD;
-    }
+    return 0;
 }
 \f
 /* Optimized flow revalidation.
@@ -7766,7 +5543,7 @@ xlate_normal(struct action_xlate_ctx *ctx)
 
 /* Calculates the tag to use for 'flow' and mask 'mask' when it is inserted
  * into an OpenFlow table with the given 'basis'. */
-static tag_type
+tag_type
 rule_calculate_tag(const struct flow *flow, const struct minimask *mask,
                    uint32_t secret)
 {
@@ -7875,33 +5652,29 @@ packet_out(struct ofproto *ofproto_, struct ofpbuf *packet,
            const struct ofpact *ofpacts, size_t ofpacts_len)
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
-    struct initial_vals initial_vals;
     struct odputil_keybuf keybuf;
     struct dpif_flow_stats stats;
-
+    struct xlate_out xout;
+    struct xlate_in xin;
     struct ofpbuf key;
 
-    struct action_xlate_ctx ctx;
-    uint64_t odp_actions_stub[1024 / 8];
-    struct ofpbuf odp_actions;
 
     ofpbuf_use_stack(&key, &keybuf, sizeof keybuf);
     odp_flow_key_from_flow(&key, flow,
-                           ofp_port_to_odp_port(ofproto, flow->in_port));
+                           ofp_port_to_odp_port(ofproto,
+                                      flow->in_port.ofp_port));
 
     dpif_flow_stats_extract(flow, packet, time_msec(), &stats);
 
-    initial_vals.vlan_tci = flow->vlan_tci;
-    action_xlate_ctx_init(&ctx, ofproto, flow, &initial_vals, NULL,
-                          packet_get_tcp_flags(packet, flow), packet);
-    ctx.resubmit_stats = &stats;
+    xlate_in_init(&xin, ofproto, flow, NULL, stats.tcp_flags, packet);
+    xin.resubmit_stats = &stats;
+    xin.ofpacts_len = ofpacts_len;
+    xin.ofpacts = ofpacts;
 
-    ofpbuf_use_stub(&odp_actions,
-                    odp_actions_stub, sizeof odp_actions_stub);
-    xlate_actions(&ctx, ofpacts, ofpacts_len, &odp_actions);
+    xlate_actions(&xin, &xout);
     dpif_execute(ofproto->backer->dpif, key.data, key.size,
-                 odp_actions.data, odp_actions.size, packet);
-    ofpbuf_uninit(&odp_actions);
+                 xout.odp_actions.data, xout.odp_actions.size, packet);
+    xlate_out_uninit(&xout);
 
     return 0;
 }
@@ -7917,13 +5690,16 @@ set_netflow(struct ofproto *ofproto_,
     if (netflow_options) {
         if (!ofproto->netflow) {
             ofproto->netflow = netflow_create();
+            ofproto->backer->need_revalidate = REV_RECONFIGURE;
         }
         return netflow_set_options(ofproto->netflow, netflow_options);
-    } else {
+    } else if (ofproto->netflow) {
+        ofproto->backer->need_revalidate = REV_RECONFIGURE;
         netflow_destroy(ofproto->netflow);
         ofproto->netflow = NULL;
-        return 0;
     }
+
+    return 0;
 }
 
 static void
@@ -7947,7 +5723,8 @@ send_active_timeout(struct ofproto_dpif *ofproto, struct facet *facet)
             if (subfacet->path == SF_FAST_PATH) {
                 struct dpif_flow_stats stats;
 
-                subfacet_reinstall(subfacet, &stats);
+                subfacet_install(subfacet, &facet->xout.odp_actions,
+                                 &stats);
                 subfacet_update_stats(subfacet, &stats);
             }
         }
@@ -7963,9 +5740,11 @@ send_active_timeout(struct ofproto_dpif *ofproto, struct facet *facet)
 static void
 send_netflow_active_timeouts(struct ofproto_dpif *ofproto)
 {
+    struct cls_cursor cursor;
     struct facet *facet;
 
-    HMAP_FOR_EACH (facet, hmap_node, &ofproto->facets) {
+    cls_cursor_init(&cursor, &ofproto->facets, NULL);
+    CLS_CURSOR_FOR_EACH (facet, cr, &cursor) {
         send_active_timeout(ofproto, facet);
     }
 }
@@ -8023,9 +5802,12 @@ ofproto_unixctl_fdb_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
     ds_put_cstr(&ds, " port  VLAN  MAC                Age\n");
     LIST_FOR_EACH (e, lru_node, &ofproto->ml->lrus) {
         struct ofbundle *bundle = e->port.p;
-        ds_put_format(&ds, "%5d  %4d  "ETH_ADDR_FMT"  %3d\n",
-                      ofbundle_get_a_port(bundle)->odp_port,
-                      e->vlan, ETH_ADDR_ARGS(e->mac),
+        char name[OFP_MAX_PORT_NAME_LEN];
+
+        ofputil_port_to_string(ofbundle_get_a_port(bundle)->up.ofp_port,
+                               name, sizeof name);
+        ds_put_format(&ds, "%5s  %4d  "ETH_ADDR_FMT"  %3d\n",
+                      name, e->vlan, ETH_ADDR_ARGS(e->mac),
                       mac_entry_age(ofproto->ml, e));
     }
     unixctl_command_reply(conn, ds_cstr(&ds));
@@ -8033,14 +5815,14 @@ ofproto_unixctl_fdb_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
 }
 
 struct trace_ctx {
-    struct action_xlate_ctx ctx;
+    struct xlate_out xout;
+    struct xlate_in xin;
     struct flow flow;
     struct ds *result;
 };
 
 static void
-trace_format_rule(struct ds *result, uint8_t table_id, int level,
-                  const struct rule_dpif *rule)
+trace_format_rule(struct ds *result, int level, const struct rule_dpif *rule)
 {
     ds_put_char_multiple(result, '\t', level);
     if (!rule) {
@@ -8049,7 +5831,7 @@ trace_format_rule(struct ds *result, uint8_t table_id, int level,
     }
 
     ds_put_format(result, "Rule: table=%"PRIu8" cookie=%#"PRIx64" ",
-                  table_id, ntohll(rule->up.flow_cookie));
+                  rule ? rule->up.table_id : 0, ntohll(rule->up.flow_cookie));
     cls_rule_format(&rule->up.cr, result);
     ds_put_char(result, '\n');
 
@@ -8061,15 +5843,15 @@ trace_format_rule(struct ds *result, uint8_t table_id, int level,
 
 static void
 trace_format_flow(struct ds *result, int level, const char *title,
-                 struct trace_ctx *trace)
+                  struct trace_ctx *trace)
 {
     ds_put_char_multiple(result, '\t', level);
     ds_put_format(result, "%s: ", title);
-    if (flow_equal(&trace->ctx.flow, &trace->flow)) {
+    if (flow_equal(&trace->xin.flow, &trace->flow)) {
         ds_put_cstr(result, "unchanged");
     } else {
-        flow_format(result, &trace->ctx.flow);
-        trace->flow = trace->ctx.flow;
+        flow_format(result, &trace->xin.flow);
+        trace->flow = trace->xin.flow;
     }
     ds_put_char(result, '\n');
 }
@@ -8092,7 +5874,7 @@ static void
 trace_format_odp(struct ds *result, int level, const char *title,
                  struct trace_ctx *trace)
 {
-    struct ofpbuf *odp_actions = trace->ctx.odp_actions;
+    struct ofpbuf *odp_actions = &trace->xout.odp_actions;
 
     ds_put_char_multiple(result, '\t', level);
     ds_put_format(result, "%s: ", title);
@@ -8101,25 +5883,25 @@ trace_format_odp(struct ds *result, int level, const char *title,
 }
 
 static void
-trace_resubmit(struct action_xlate_ctx *ctx, struct rule_dpif *rule)
+trace_resubmit(struct xlate_in *xin, struct rule_dpif *rule, int recurse)
 {
-    struct trace_ctx *trace = CONTAINER_OF(ctx, struct trace_ctx, ctx);
+    struct trace_ctx *trace = CONTAINER_OF(xin, struct trace_ctx, xin);
     struct ds *result = trace->result;
 
     ds_put_char(result, '\n');
-    trace_format_flow(result, ctx->recurse + 1, "Resubmitted flow", trace);
-    trace_format_regs(result, ctx->recurse + 1, "Resubmitted regs", trace);
-    trace_format_odp(result,  ctx->recurse + 1, "Resubmitted  odp", trace);
-    trace_format_rule(result, ctx->table_id, ctx->recurse + 1, rule);
+    trace_format_flow(result, recurse + 1, "Resubmitted flow", trace);
+    trace_format_regs(result, recurse + 1, "Resubmitted regs", trace);
+    trace_format_odp(result,  recurse + 1, "Resubmitted  odp", trace);
+    trace_format_rule(result, recurse + 1, rule);
 }
 
 static void
-trace_report(struct action_xlate_ctx *ctx, const char *s)
+trace_report(struct xlate_in *xin, const char *s, int recurse)
 {
-    struct trace_ctx *trace = CONTAINER_OF(ctx, struct trace_ctx, ctx);
+    struct trace_ctx *trace = CONTAINER_OF(xin, struct trace_ctx, xin);
     struct ds *result = trace->result;
 
-    ds_put_char_multiple(result, '\t', ctx->recurse);
+    ds_put_char_multiple(result, '\t', recurse);
     ds_put_cstr(result, s);
     ds_put_char(result, '\n');
 }
@@ -8128,124 +5910,125 @@ static void
 ofproto_unixctl_trace(struct unixctl_conn *conn, int argc, const char *argv[],
                       void *aux OVS_UNUSED)
 {
-    const char *dpname = argv[1];
+    const struct dpif_backer *backer;
     struct ofproto_dpif *ofproto;
-    struct ofpbuf odp_key;
+    struct ofpbuf odp_key, odp_mask;
     struct ofpbuf *packet;
-    struct initial_vals initial_vals;
     struct ds result;
     struct flow flow;
     char *s;
 
     packet = NULL;
-    ofpbuf_init(&odp_key, 0);
+    backer = NULL;
     ds_init(&result);
+    ofpbuf_init(&odp_key, 0);
+    ofpbuf_init(&odp_mask, 0);
 
-    ofproto = ofproto_dpif_lookup(dpname);
-    if (!ofproto) {
-        unixctl_command_reply_error(conn, "Unknown ofproto (use ofproto/list "
-                                    "for help)");
-        goto exit;
+    /* Handle "-generate" or a hex string as the last argument. */
+    if (!strcmp(argv[argc - 1], "-generate")) {
+        packet = ofpbuf_new(0);
+        argc--;
+    } else {
+        const char *error = eth_from_hex(argv[argc - 1], &packet);
+        if (!error) {
+            argc--;
+        } else if (argc == 4) {
+            /* The 3-argument form must end in "-generate' or a hex string. */
+            unixctl_command_reply_error(conn, error);
+            goto exit;
+        }
     }
-    if (argc == 3 || (argc == 4 && !strcmp(argv[3], "-generate"))) {
-        /* ofproto/trace dpname flow [-generate] */
-        const char *flow_s = argv[2];
-        const char *generate_s = argv[3];
 
-        /* Allow 'flow_s' to be either a datapath flow or an OpenFlow-like
-         * flow.  We guess which type it is based on whether 'flow_s' contains
-         * an '(', since a datapath flow always contains '(') but an
-         * OpenFlow-like flow should not (in fact it's allowed but I believe
-         * that's not documented anywhere).
-         *
-         * An alternative would be to try to parse 'flow_s' both ways, but then
-         * it would be tricky giving a sensible error message.  After all, do
-         * you just say "syntax error" or do you present both error messages?
-         * Both choices seem lousy. */
-        if (strchr(flow_s, '(')) {
-            int error;
-
-            /* Convert string to datapath key. */
-            ofpbuf_init(&odp_key, 0);
-            error = odp_flow_key_from_string(flow_s, NULL, &odp_key);
-            if (error) {
-                unixctl_command_reply_error(conn, "Bad flow syntax");
-                goto exit;
+    /* Parse the flow and determine whether a datapath or
+     * bridge is specified. If function odp_flow_key_from_string()
+     * returns 0, the flow is a odp_flow. If function
+     * parse_ofp_exact_flow() returns 0, the flow is a br_flow. */
+    if (!odp_flow_from_string(argv[argc - 1], NULL, &odp_key, &odp_mask)) {
+        /* If the odp_flow is the second argument,
+         * the datapath name is the first argument. */
+        if (argc == 3) {
+            const char *dp_type;
+            if (!strncmp(argv[1], "ovs-", 4)) {
+                dp_type = argv[1] + 4;
+            } else {
+                dp_type = argv[1];
             }
-
-            /* The user might have specified the wrong ofproto but within the
-             * same backer.  That's OK, ofproto_receive() can find the right
-             * one for us. */
-            if (ofproto_receive(ofproto->backer, NULL, odp_key.data,
-                                odp_key.size, &flow, NULL, &ofproto, NULL,
-                                &initial_vals)) {
-                unixctl_command_reply_error(conn, "Invalid flow");
+            backer = shash_find_data(&all_dpif_backers, dp_type);
+            if (!backer) {
+                unixctl_command_reply_error(conn, "Cannot find datapath "
+                               "of this name");
                 goto exit;
             }
-            ds_put_format(&result, "Bridge: %s\n", ofproto->up.name);
         } else {
-            char *error_s;
-
-            error_s = parse_ofp_exact_flow(&flow, argv[2]);
-            if (error_s) {
-                unixctl_command_reply_error(conn, error_s);
-                free(error_s);
+            /* No datapath name specified, so there should be only one
+             * datapath. */
+            struct shash_node *node;
+            if (shash_count(&all_dpif_backers) != 1) {
+                unixctl_command_reply_error(conn, "Must specify datapath "
+                         "name, there is more than one type of datapath");
                 goto exit;
             }
-
-            initial_vals.vlan_tci = flow.vlan_tci;
+            node = shash_first(&all_dpif_backers);
+            backer = node->data;
         }
 
-        /* Generate a packet, if requested. */
-        if (generate_s) {
-            packet = ofpbuf_new(0);
-            flow_compose(packet, &flow);
+        /* Extract the ofproto_dpif object from the ofproto_receive()
+         * function. */
+        if (ofproto_receive(backer, NULL, odp_key.data,
+                            odp_key.size, &flow, NULL, &ofproto, NULL)) {
+            unixctl_command_reply_error(conn, "Invalid datapath flow");
+            goto exit;
         }
-    } 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 *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);
-        if (msg) {
-            unixctl_command_reply_error(conn, msg);
+        ds_put_format(&result, "Bridge: %s\n", ofproto->up.name);
+    } else if (!parse_ofp_exact_flow(&flow, argv[argc - 1])) {
+        if (argc != 3) {
+            unixctl_command_reply_error(conn, "Must specify bridge name");
             goto exit;
         }
 
-        ds_put_cstr(&result, "Packet: ");
-        s = ofp_packet_to_string(packet->data, packet->size);
-        ds_put_cstr(&result, s);
-        free(s);
-
-        flow_extract(packet, priority, mark, NULL, in_port, &flow);
-        flow.tunnel.tun_id = tun_id;
-        initial_vals.vlan_tci = flow.vlan_tci;
+        ofproto = ofproto_dpif_lookup(argv[1]);
+        if (!ofproto) {
+            unixctl_command_reply_error(conn, "Unknown bridge name");
+            goto exit;
+        }
     } else {
-        unixctl_command_reply_error(conn, "Bad command syntax");
+        unixctl_command_reply_error(conn, "Bad flow syntax");
         goto exit;
     }
 
-    ofproto_trace(ofproto, &flow, packet, &initial_vals, &result);
+    /* Generate a packet, if requested. */
+    if (packet) {
+        if (!packet->size) {
+            flow_compose(packet, &flow);
+        } else {
+            union flow_in_port in_port_;
+
+            in_port_ = flow.in_port;
+            ds_put_cstr(&result, "Packet: ");
+            s = ofp_packet_to_string(packet->data, packet->size);
+            ds_put_cstr(&result, s);
+            free(s);
+
+            /* Use the metadata from the flow and the packet argument
+             * to reconstruct the flow. */
+            flow_extract(packet, flow.skb_priority, flow.skb_mark, NULL,
+                         &in_port_, &flow);
+        }
+    }
+
+    ofproto_trace(ofproto, &flow, packet, &result);
     unixctl_command_reply(conn, ds_cstr(&result));
 
 exit:
     ds_destroy(&result);
     ofpbuf_delete(packet);
     ofpbuf_uninit(&odp_key);
+    ofpbuf_uninit(&odp_mask);
 }
 
-static void
+void
 ofproto_trace(struct ofproto_dpif *ofproto, const struct flow *flow,
-              const struct ofpbuf *packet,
-              const struct initial_vals *initial_vals, struct ds *ds)
+              const struct ofpbuf *packet, struct ds *ds)
 {
     struct rule_dpif *rule;
 
@@ -8253,21 +6036,24 @@ ofproto_trace(struct ofproto_dpif *ofproto, const struct flow *flow,
     flow_format(ds, flow);
     ds_put_char(ds, '\n');
 
-    rule = rule_dpif_lookup(ofproto, flow);
+    rule = rule_dpif_lookup(ofproto, flow, NULL);
 
-    trace_format_rule(ds, 0, 0, rule);
+    trace_format_rule(ds, 0, rule);
     if (rule == ofproto->miss_rule) {
         ds_put_cstr(ds, "\nNo match, flow generates \"packet in\"s.\n");
     } else if (rule == ofproto->no_packet_in_rule) {
         ds_put_cstr(ds, "\nNo match, packets dropped because "
                     "OFPPC_NO_PACKET_IN is set on in_port.\n");
+    } else if (rule == ofproto->drop_frags_rule) {
+        ds_put_cstr(ds, "\nPackets dropped because they are IP fragments "
+                    "and the fragment handling mode is \"drop\".\n");
     }
 
     if (rule) {
         uint64_t odp_actions_stub[1024 / 8];
         struct ofpbuf odp_actions;
-
         struct trace_ctx trace;
+        struct match match;
         uint8_t tcp_flags;
 
         tcp_flags = packet ? packet_get_tcp_flags(packet, flow) : 0;
@@ -8275,67 +6061,50 @@ ofproto_trace(struct ofproto_dpif *ofproto, const struct flow *flow,
         trace.flow = *flow;
         ofpbuf_use_stub(&odp_actions,
                         odp_actions_stub, sizeof odp_actions_stub);
-        action_xlate_ctx_init(&trace.ctx, ofproto, flow, initial_vals,
-                              rule, tcp_flags, packet);
-        trace.ctx.resubmit_hook = trace_resubmit;
-        trace.ctx.report_hook = trace_report;
-        xlate_actions(&trace.ctx, rule->up.ofpacts, rule->up.ofpacts_len,
-                      &odp_actions);
+        xlate_in_init(&trace.xin, ofproto, flow, rule, tcp_flags, packet);
+        trace.xin.resubmit_hook = trace_resubmit;
+        trace.xin.report_hook = trace_report;
+
+        xlate_actions(&trace.xin, &trace.xout);
 
         ds_put_char(ds, '\n');
         trace_format_flow(ds, 0, "Final flow", &trace);
-        ds_put_cstr(ds, "Datapath actions: ");
-        format_odp_actions(ds, odp_actions.data, odp_actions.size);
-        ofpbuf_uninit(&odp_actions);
 
-        if (trace.ctx.slow) {
-            enum slow_path_reason slow;
+        match_init(&match, flow, &trace.xout.wc);
+        ds_put_cstr(ds, "Relevant fields: ");
+        match_format(&match, ds, OFP_DEFAULT_PRIORITY);
+        ds_put_char(ds, '\n');
+
+        ds_put_cstr(ds, "Datapath actions: ");
+        format_odp_actions(ds, trace.xout.odp_actions.data,
+                           trace.xout.odp_actions.size);
 
+        if (trace.xout.slow) {
             ds_put_cstr(ds, "\nThis flow is handled by the userspace "
                         "slow path because it:");
-            for (slow = trace.ctx.slow; slow; ) {
-                enum slow_path_reason bit = rightmost_1bit(slow);
-
-                switch (bit) {
-                case SLOW_CFM:
-                    ds_put_cstr(ds, "\n\t- Consists of CFM packets.");
-                    break;
-                case SLOW_LACP:
-                    ds_put_cstr(ds, "\n\t- Consists of LACP packets.");
-                    break;
-                case SLOW_STP:
-                    ds_put_cstr(ds, "\n\t- Consists of STP packets.");
-                    break;
-                case SLOW_BFD:
-                    ds_put_cstr(ds, "\n\t- Consists of BFD packets.");
-                    break;
-                case SLOW_IN_BAND:
-                    ds_put_cstr(ds, "\n\t- Needs in-band special case "
-                                "processing.");
-                    if (!packet) {
-                        ds_put_cstr(ds, "\n\t  (The datapath actions are "
-                                    "incomplete--for complete actions, "
-                                    "please supply a packet.)");
-                    }
-                    break;
-                case SLOW_CONTROLLER:
-                    ds_put_cstr(ds, "\n\t- Sends \"packet-in\" messages "
-                                "to the OpenFlow controller.");
-                    break;
-                case SLOW_MATCH:
-                    ds_put_cstr(ds, "\n\t- Needs more specific matching "
-                                "than the datapath supports.");
-                    break;
-                }
-
-                slow &= ~bit;
-            }
-
-            if (slow & ~SLOW_MATCH) {
-                ds_put_cstr(ds, "\nThe datapath actions above do not reflect "
-                            "the special slow-path processing.");
+            switch (trace.xout.slow) {
+            case SLOW_CFM:
+                ds_put_cstr(ds, "\n\t- Consists of CFM packets.");
+                break;
+            case SLOW_LACP:
+                ds_put_cstr(ds, "\n\t- Consists of LACP packets.");
+                break;
+            case SLOW_STP:
+                ds_put_cstr(ds, "\n\t- Consists of STP packets.");
+                break;
+            case SLOW_BFD:
+                ds_put_cstr(ds, "\n\t- Consists of BFD packets.");
+                break;
+            case SLOW_CONTROLLER:
+                ds_put_cstr(ds, "\n\t- Sends \"packet-in\" messages "
+                            "to the OpenFlow controller.");
+                break;
+            case __SLOW_MAX:
+                NOT_REACHED();
             }
         }
+
+        xlate_out_uninit(&trace.xout);
     }
 }
 
@@ -8360,11 +6129,13 @@ ofproto_dpif_unclog(struct unixctl_conn *conn OVS_UNUSED, int argc OVS_UNUSED,
 static void
 ofproto_dpif_self_check__(struct ofproto_dpif *ofproto, struct ds *reply)
 {
+    struct cls_cursor cursor;
     struct facet *facet;
     int errors;
 
     errors = 0;
-    HMAP_FOR_EACH (facet, hmap_node, &ofproto->facets) {
+    cls_cursor_init(&cursor, &ofproto->facets, NULL);
+    CLS_CURSOR_FOR_EACH (facet, cr, &cursor) {
         if (!facet_check_consistency(facet)) {
             errors++;
         }
@@ -8447,132 +6218,172 @@ ofproto_unixctl_dpif_dump_dps(struct unixctl_conn *conn, int argc OVS_UNUSED,
 }
 
 static void
-show_dp_format(const struct ofproto_dpif *ofproto, struct ds *ds)
+show_dp_rates(struct ds *ds, const char *heading,
+              const struct avg_subfacet_rates *rates)
 {
-    const struct shash_node **ports;
-    int i;
-    struct avg_subfacet_rates lifetime;
-    unsigned long long int minutes;
-    const int min_ms = 60 * 1000; /* milliseconds in one minute. */
+    ds_put_format(ds, "%s add rate: %5.3f/min, del rate: %5.3f/min\n",
+                  heading, rates->add_rate, rates->del_rate);
+}
+
+static void
+dpif_show_backer(const struct dpif_backer *backer, struct ds *ds)
+{
+    const struct shash_node **ofprotos;
+    struct ofproto_dpif *ofproto;
+    struct shash ofproto_shash;
+    uint64_t n_hit, n_missed;
+    long long int minutes;
+    size_t i;
+
+    n_hit = n_missed = 0;
+    HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
+        if (ofproto->backer == backer) {
+            n_missed += ofproto->n_missed;
+            n_hit += ofproto->n_hit;
+        }
+    }
 
-    minutes = (time_msec() - ofproto->created) / min_ms;
+    ds_put_format(ds, "%s: hit:%"PRIu64" missed:%"PRIu64"\n",
+                  dpif_name(backer->dpif), n_hit, n_missed);
+    ds_put_format(ds, "\tflows: cur: %zu, avg: %u, max: %u,"
+                  " life span: %lldms\n", hmap_count(&backer->subfacets),
+                  backer->avg_n_subfacet, backer->max_n_subfacet,
+                  backer->avg_subfacet_life);
 
-    if (minutes > 0) {
-        lifetime.add_rate = (double)ofproto->total_subfacet_add_count
-                            / minutes;
-        lifetime.del_rate = (double)ofproto->total_subfacet_del_count
-                            / minutes;
-    }else {
-        lifetime.add_rate = 0.0;
-        lifetime.del_rate = 0.0;
-    }
-
-    ds_put_format(ds, "%s (%s):\n", ofproto->up.name,
-                  dpif_name(ofproto->backer->dpif));
-    ds_put_format(ds,
-                  "\tlookups: hit:%"PRIu64" missed:%"PRIu64"\n",
-                  ofproto->n_hit, ofproto->n_missed);
-    ds_put_format(ds, "\tflows: cur: %zu, avg: %5.3f, max: %d,"
-                  " life span: %llu(ms)\n",
-                  hmap_count(&ofproto->subfacets),
-                  avg_subfacet_count(ofproto),
-                  ofproto->max_n_subfacet,
-                  avg_subfacet_life_span(ofproto));
+    minutes = (time_msec() - backer->created) / (1000 * 60);
     if (minutes >= 60) {
-        show_dp_rates(ds, "\t\thourly avg:", &ofproto->hourly);
+        show_dp_rates(ds, "\thourly avg:", &backer->hourly);
     }
     if (minutes >= 60 * 24) {
-        show_dp_rates(ds, "\t\tdaily avg:",  &ofproto->daily);
+        show_dp_rates(ds, "\tdaily avg:",  &backer->daily);
     }
-    show_dp_rates(ds, "\t\toverall avg:",  &lifetime);
+    show_dp_rates(ds, "\toverall avg:",  &backer->lifetime);
 
-    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);
-        uint32_t odp_port;
-
-        ds_put_format(ds, "\t%s %u/", name, ofport->ofp_port);
+    shash_init(&ofproto_shash);
+    ofprotos = get_ofprotos(&ofproto_shash);
+    for (i = 0; i < shash_count(&ofproto_shash); i++) {
+        struct ofproto_dpif *ofproto = ofprotos[i]->data;
+        const struct shash_node **ports;
+        size_t j;
 
-        odp_port = ofp_port_to_odp_port(ofproto, ofport->ofp_port);
-        if (odp_port != OVSP_NONE) {
-            ds_put_format(ds, "%"PRIu32":", odp_port);
-        } else {
-            ds_put_cstr(ds, "none:");
+        if (ofproto->backer != backer) {
+            continue;
         }
 
-        if (strcmp(type, "system")) {
-            struct netdev *netdev;
-            int error;
+        ds_put_format(ds, "\t%s: hit:%"PRIu64" missed:%"PRIu64"\n",
+                      ofproto->up.name, ofproto->n_hit, ofproto->n_missed);
 
-            ds_put_format(ds, " (%s", type);
+        ports = shash_sort(&ofproto->up.port_by_name);
+        for (j = 0; j < shash_count(&ofproto->up.port_by_name); j++) {
+            const struct shash_node *node = ports[j];
+            struct ofport *ofport = node->data;
+            struct smap config;
+            odp_port_t odp_port;
 
-            error = netdev_open(name, type, &netdev);
-            if (!error) {
-                struct smap config;
+            ds_put_format(ds, "\t\t%s %u/", netdev_get_name(ofport->netdev),
+                          ofport->ofp_port);
 
-                smap_init(&config);
-                error = netdev_get_config(netdev, &config);
-                if (!error) {
-                    const struct smap_node **nodes;
-                    size_t i;
+            odp_port = ofp_port_to_odp_port(ofproto, ofport->ofp_port);
+            if (odp_port != ODPP_NONE) {
+                ds_put_format(ds, "%"PRIu32":", odp_port);
+            } else {
+                ds_put_cstr(ds, "none:");
+            }
 
-                    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);
+            ds_put_format(ds, " (%s", netdev_get_type(ofport->netdev));
 
-                netdev_close(netdev);
+            smap_init(&config);
+            if (!netdev_get_config(ofport->netdev, &config)) {
+                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);
+
             ds_put_char(ds, ')');
+            ds_put_char(ds, '\n');
         }
-        ds_put_char(ds, '\n');
+        free(ports);
     }
-    free(ports);
+    shash_destroy(&ofproto_shash);
+    free(ofprotos);
 }
 
 static void
-ofproto_unixctl_dpif_show(struct unixctl_conn *conn, int argc,
-                          const char *argv[], void *aux OVS_UNUSED)
+ofproto_unixctl_dpif_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
+                          const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
+{
+    struct ds ds = DS_EMPTY_INITIALIZER;
+    const struct shash_node **backers;
+    int i;
+
+    backers = shash_sort(&all_dpif_backers);
+    for (i = 0; i < shash_count(&all_dpif_backers); i++) {
+        dpif_show_backer(backers[i]->data, &ds);
+    }
+    free(backers);
+
+    unixctl_command_reply(conn, ds_cstr(&ds));
+    ds_destroy(&ds);
+}
+
+/* Dump the megaflow (facet) cache.  This is useful to check the
+ * correctness of flow wildcarding, since the same mechanism is used for
+ * both xlate caching and kernel wildcarding.
+ *
+ * It's important to note that in the output the flow description uses
+ * OpenFlow (OFP) ports, but the actions use datapath (ODP) ports.
+ *
+ * This command is only needed for advanced debugging, so it's not
+ * documented in the man page. */
+static void
+ofproto_unixctl_dpif_dump_megaflows(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;
+    long long int now = time_msec();
+    struct cls_cursor cursor;
+    struct facet *facet;
 
-    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;
+    ofproto = ofproto_dpif_lookup(argv[1]);
+    if (!ofproto) {
+        unixctl_command_reply_error(conn, "no such bridge");
+        return;
+    }
 
-        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);
-        }
+    cls_cursor_init(&cursor, &ofproto->facets, NULL);
+    CLS_CURSOR_FOR_EACH (facet, cr, &cursor) {
+        cls_rule_format(&facet->cr, &ds);
+        ds_put_cstr(&ds, ", ");
+        ds_put_format(&ds, "n_subfacets:%zu, ", list_size(&facet->subfacets));
+        ds_put_format(&ds, "used:%.3fs, ", (now - facet->used) / 1000.0);
+        ds_put_cstr(&ds, "Datapath actions: ");
+        if (facet->xout.slow) {
+            uint64_t slow_path_stub[128 / 8];
+            const struct nlattr *actions;
+            size_t actions_len;
 
-        shash_destroy(&ofproto_shash);
-        free(sorted_ofprotos);
+            compose_slow_path(ofproto, &facet->flow, facet->xout.slow,
+                              slow_path_stub, sizeof slow_path_stub,
+                              &actions, &actions_len);
+            format_odp_actions(&ds, actions, actions_len);
+        } else {
+            format_odp_actions(&ds, facet->xout.odp_actions.data,
+                               facet->xout.odp_actions.size);
+        }
+        ds_put_cstr(&ds, "\n");
     }
 
+    ds_chomp(&ds, '\n');
     unixctl_command_reply(conn, ds_cstr(&ds));
     ds_destroy(&ds);
 }
@@ -8594,7 +6405,13 @@ ofproto_unixctl_dpif_dump_flows(struct unixctl_conn *conn,
 
     update_stats(ofproto->backer);
 
-    HMAP_FOR_EACH (subfacet, hmap_node, &ofproto->subfacets) {
+    HMAP_FOR_EACH (subfacet, hmap_node, &ofproto->backer->subfacets) {
+        struct facet *facet = subfacet->facet;
+
+        if (ofproto_dpif_cast(facet->rule->up.ofproto) != ofproto) {
+            continue;
+        }
+
         odp_flow_key_format(subfacet->key, subfacet->key_len, &ds);
 
         ds_put_format(&ds, ", packets:%"PRIu64", bytes:%"PRIu64", used:",
@@ -8611,17 +6428,18 @@ ofproto_unixctl_dpif_dump_flows(struct unixctl_conn *conn,
         }
 
         ds_put_cstr(&ds, ", actions:");
-        if (subfacet->slow) {
+        if (facet->xout.slow) {
             uint64_t slow_path_stub[128 / 8];
             const struct nlattr *actions;
             size_t actions_len;
 
-            compose_slow_path(ofproto, &subfacet->facet->flow, subfacet->slow,
+            compose_slow_path(ofproto, &facet->flow, facet->xout.slow,
                               slow_path_stub, sizeof slow_path_stub,
                               &actions, &actions_len);
             format_odp_actions(&ds, actions, actions_len);
         } else {
-            format_odp_actions(&ds, subfacet->actions, subfacet->actions_len);
+            format_odp_actions(&ds, facet->xout.odp_actions.data,
+                               facet->xout.odp_actions.size);
         }
         ds_put_char(&ds, '\n');
     }
@@ -8661,8 +6479,8 @@ ofproto_dpif_unixctl_init(void)
 
     unixctl_command_register(
         "ofproto/trace",
-        "bridge {priority tun_id in_port mark packet | odp_flow [-generate]}",
-        2, 6, ofproto_unixctl_trace, NULL);
+        "[dp_name]|bridge odp_flow|br_flow [-generate|packet]",
+        1, 3, 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,
@@ -8675,12 +6493,14 @@ ofproto_dpif_unixctl_init(void)
                              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/show", "", 0, 0, 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);
+    unixctl_command_register("dpif/dump-megaflows", "bridge", 1, 1,
+                             ofproto_unixctl_dpif_dump_megaflows, NULL);
 }
 \f
 /* Linux VLAN device support (e.g. "eth0.10" for VLAN 10.)
@@ -8691,7 +6511,7 @@ ofproto_dpif_unixctl_init(void)
  * widespread use, we will delete these interfaces. */
 
 static int
-set_realdev(struct ofport *ofport_, uint16_t realdev_ofp_port, int vid)
+set_realdev(struct ofport *ofport_, ofp_port_t realdev_ofp_port, int vid)
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport_->ofproto);
     struct ofport_dpif *ofport = ofport_dpif_cast(ofport_);
@@ -8723,46 +6543,45 @@ set_realdev(struct ofport *ofport_, uint16_t realdev_ofp_port, int vid)
 }
 
 static uint32_t
-hash_realdev_vid(uint16_t realdev_ofp_port, int vid)
+hash_realdev_vid(ofp_port_t realdev_ofp_port, int vid)
 {
-    return hash_2words(realdev_ofp_port, vid);
+    return hash_2words(ofp_to_u16(realdev_ofp_port), vid);
 }
 
-/* Returns the ODP port number of the Linux VLAN device that corresponds to
- * 'vlan_tci' on the network device with port number 'realdev_odp_port' in
- * 'ofproto'.  For example, given 'realdev_odp_port' of eth0 and 'vlan_tci' 9,
- * it would return the port number of eth0.9.
+/* Returns the OFP port number of the Linux VLAN device that corresponds to
+ * 'vlan_tci' on the network device with port number 'realdev_ofp_port' in
+ * 'struct ofport_dpif'.  For example, given 'realdev_ofp_port' of eth0 and
+ * 'vlan_tci' 9, it would return the port number of eth0.9.
  *
- * Unless VLAN splinters are enabled for port 'realdev_odp_port', this
- * function just returns its 'realdev_odp_port' argument. */
-static uint32_t
+ * Unless VLAN splinters are enabled for port 'realdev_ofp_port', this
+ * function just returns its 'realdev_ofp_port' argument. */
+ofp_port_t
 vsp_realdev_to_vlandev(const struct ofproto_dpif *ofproto,
-                       uint32_t realdev_odp_port, ovs_be16 vlan_tci)
+                       ofp_port_t realdev_ofp_port, ovs_be16 vlan_tci)
 {
     if (!hmap_is_empty(&ofproto->realdev_vid_map)) {
-        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(ofproto, vsp->vlandev_ofp_port);
+                return vsp->vlandev_ofp_port;
             }
         }
     }
-    return realdev_odp_port;
+    return realdev_ofp_port;
 }
 
 static struct vlan_splinter *
-vlandev_find(const struct ofproto_dpif *ofproto, uint16_t vlandev_ofp_port)
+vlandev_find(const struct ofproto_dpif *ofproto, ofp_port_t vlandev_ofp_port)
 {
     struct vlan_splinter *vsp;
 
-    HMAP_FOR_EACH_WITH_HASH (vsp, vlandev_node, hash_int(vlandev_ofp_port, 0),
+    HMAP_FOR_EACH_WITH_HASH (vsp, vlandev_node,
+                             hash_ofp_port(vlandev_ofp_port),
                              &ofproto->vlandev_map) {
         if (vsp->vlandev_ofp_port == vlandev_ofp_port) {
             return vsp;
@@ -8781,9 +6600,9 @@ vlandev_find(const struct ofproto_dpif *ofproto, uint16_t vlandev_ofp_port)
  * Returns 0 and does not modify '*vid' if 'vlandev_ofp_port' is not a Linux
  * VLAN device.  Unless VLAN splinters are enabled, this is what this function
  * always does.*/
-static uint16_t
+static ofp_port_t
 vsp_vlandev_to_realdev(const struct ofproto_dpif *ofproto,
-                       uint16_t vlandev_ofp_port, int *vid)
+                       ofp_port_t vlandev_ofp_port, int *vid)
 {
     if (!hmap_is_empty(&ofproto->vlandev_map)) {
         const struct vlan_splinter *vsp;
@@ -8808,17 +6627,17 @@ vsp_vlandev_to_realdev(const struct ofproto_dpif *ofproto,
 static bool
 vsp_adjust_flow(const struct ofproto_dpif *ofproto, struct flow *flow)
 {
-    uint16_t realdev;
+    ofp_port_t realdev;
     int vid;
 
-    realdev = vsp_vlandev_to_realdev(ofproto, flow->in_port, &vid);
+    realdev = vsp_vlandev_to_realdev(ofproto, flow->in_port.ofp_port, &vid);
     if (!realdev) {
         return false;
     }
 
     /* Cause the flow to be processed as if it came in on the real device with
      * the VLAN device's VLAN ID. */
-    flow->in_port = realdev;
+    flow->in_port.ofp_port = realdev;
     flow->vlan_tci = htons((vid & VLAN_VID_MASK) | VLAN_CFI);
     return true;
 }
@@ -8842,7 +6661,7 @@ vsp_remove(struct ofport_dpif *port)
 }
 
 static void
-vsp_add(struct ofport_dpif *port, uint16_t realdev_ofp_port, int vid)
+vsp_add(struct ofport_dpif *port, ofp_port_t realdev_ofp_port, int vid)
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(port->up.ofproto);
 
@@ -8853,7 +6672,7 @@ vsp_add(struct ofport_dpif *port, uint16_t realdev_ofp_port, int vid)
 
         vsp = xmalloc(sizeof *vsp);
         hmap_insert(&ofproto->vlandev_map, &vsp->vlandev_node,
-                    hash_int(port->up.ofp_port, 0));
+                    hash_ofp_port(port->up.ofp_port));
         hmap_insert(&ofproto->realdev_vid_map, &vsp->realdev_vid_node,
                     hash_realdev_vid(realdev_ofp_port, vid));
         vsp->realdev_ofp_port = realdev_ofp_port;
@@ -8866,20 +6685,19 @@ vsp_add(struct ofport_dpif *port, uint16_t realdev_ofp_port, int vid)
     }
 }
 
-static uint32_t
-ofp_port_to_odp_port(const struct ofproto_dpif *ofproto, uint16_t ofp_port)
+odp_port_t
+ofp_port_to_odp_port(const struct ofproto_dpif *ofproto, ofp_port_t ofp_port)
 {
     const struct ofport_dpif *ofport = get_ofp_port(ofproto, ofp_port);
-    return ofport ? ofport->odp_port : OVSP_NONE;
+    return ofport ? ofport->odp_port : ODPP_NONE;
 }
 
 static struct ofport_dpif *
-odp_port_to_ofport(const struct dpif_backer *backer, uint32_t odp_port)
+odp_port_to_ofport(const struct dpif_backer *backer, odp_port_t odp_port)
 {
     struct ofport_dpif *port;
 
-    HMAP_FOR_EACH_IN_BUCKET (port, odp_port_node,
-                             hash_int(odp_port, 0),
+    HMAP_FOR_EACH_IN_BUCKET (port, odp_port_node, hash_odp_port(odp_port),
                              &backer->odp_to_ofport_map) {
         if (port->odp_port == odp_port) {
             return port;
@@ -8889,8 +6707,8 @@ odp_port_to_ofport(const struct dpif_backer *backer, uint32_t odp_port)
     return NULL;
 }
 
-static uint16_t
-odp_port_to_ofp_port(const struct ofproto_dpif *ofproto, uint32_t odp_port)
+static ofp_port_t
+odp_port_to_ofp_port(const struct ofproto_dpif *ofproto, odp_port_t odp_port)
 {
     struct ofport_dpif *port;
 
@@ -8901,45 +6719,6 @@ odp_port_to_ofp_port(const struct ofproto_dpif *ofproto, uint32_t odp_port)
         return OFPP_NONE;
     }
 }
-static unsigned long long int
-avg_subfacet_life_span(const struct ofproto_dpif *ofproto)
-{
-    unsigned long long int dc;
-    unsigned long long int avg;
-
-    dc = ofproto->total_subfacet_del_count + ofproto->subfacet_del_count;
-    avg = dc ? ofproto->total_subfacet_life_span / dc : 0;
-
-    return avg;
-}
-
-static double
-avg_subfacet_count(const struct ofproto_dpif *ofproto)
-{
-    double avg_c = 0.0;
-
-    if (ofproto->n_update_stats) {
-        avg_c = (double)ofproto->total_subfacet_count
-                / ofproto->n_update_stats;
-    }
-
-    return avg_c;
-}
-
-static void
-show_dp_rates(struct ds *ds, const char *heading,
-              const struct avg_subfacet_rates *rates)
-{
-    ds_put_format(ds, "%s add rate: %5.3f/min, del rate: %5.3f/min\n",
-                  heading, rates->add_rate, rates->del_rate);
-}
-
-static void
-update_max_subfacet_count(struct ofproto_dpif *ofproto)
-{
-    ofproto->max_n_subfacet = MAX(ofproto->max_n_subfacet,
-                                  hmap_count(&ofproto->subfacets));
-}
 
 /* Compute exponentially weighted moving average, adding 'new' as the newest,
  * most heavily weighted element.  'base' designates the rate of decay: after
@@ -8952,35 +6731,40 @@ exp_mavg(double *avg, int base, double new)
 }
 
 static void
-update_moving_averages(struct ofproto_dpif *ofproto)
+update_moving_averages(struct dpif_backer *backer)
 {
     const int min_ms = 60 * 1000; /* milliseconds in one minute. */
+    long long int minutes = (time_msec() - backer->created) / min_ms;
+
+    if (minutes > 0) {
+        backer->lifetime.add_rate = (double) backer->total_subfacet_add_count
+            / minutes;
+        backer->lifetime.del_rate = (double) backer->total_subfacet_del_count
+            / minutes;
+    } else {
+        backer->lifetime.add_rate = 0.0;
+        backer->lifetime.del_rate = 0.0;
+    }
 
     /* Update hourly averages on the minute boundaries. */
-    if (time_msec() - ofproto->last_minute >= min_ms) {
-        exp_mavg(&ofproto->hourly.add_rate, 60, ofproto->subfacet_add_count);
-        exp_mavg(&ofproto->hourly.del_rate, 60, ofproto->subfacet_del_count);
+    if (time_msec() - backer->last_minute >= min_ms) {
+        exp_mavg(&backer->hourly.add_rate, 60, backer->subfacet_add_count);
+        exp_mavg(&backer->hourly.del_rate, 60, backer->subfacet_del_count);
 
         /* Update daily averages on the hour boundaries. */
-        if ((ofproto->last_minute - ofproto->created) / min_ms % 60 == 59) {
-            exp_mavg(&ofproto->daily.add_rate, 24, ofproto->hourly.add_rate);
-            exp_mavg(&ofproto->daily.del_rate, 24, ofproto->hourly.del_rate);
+        if ((backer->last_minute - backer->created) / min_ms % 60 == 59) {
+            exp_mavg(&backer->daily.add_rate, 24, backer->hourly.add_rate);
+            exp_mavg(&backer->daily.del_rate, 24, backer->hourly.del_rate);
         }
 
-        ofproto->total_subfacet_add_count += ofproto->subfacet_add_count;
-        ofproto->total_subfacet_del_count += ofproto->subfacet_del_count;
-        ofproto->subfacet_add_count = 0;
-        ofproto->subfacet_del_count = 0;
-        ofproto->last_minute += min_ms;
+        backer->total_subfacet_add_count += backer->subfacet_add_count;
+        backer->total_subfacet_del_count += backer->subfacet_del_count;
+        backer->subfacet_add_count = 0;
+        backer->subfacet_del_count = 0;
+        backer->last_minute += min_ms;
     }
 }
 
-static void
-dpif_stats_update_hit_count(struct ofproto_dpif *ofproto, uint64_t delta)
-{
-    ofproto->n_hit += delta;
-}
-
 const struct ofproto_class ofproto_dpif_class = {
     init,
     enumerate_types,
@@ -9049,4 +6833,8 @@ const struct ofproto_class ofproto_dpif_class = {
     forward_bpdu_changed,
     set_mac_table_config,
     set_realdev,
+    NULL,                       /* meter_get_features */
+    NULL,                       /* meter_set */
+    NULL,                       /* meter_get */
+    NULL,                       /* meter_del */
 };
diff --git a/ofproto/ofproto-dpif.h b/ofproto/ofproto-dpif.h
new file mode 100644 (file)
index 0000000..0704297
--- /dev/null
@@ -0,0 +1,274 @@
+/* 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.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License. */
+
+#ifndef OFPROTO_DPIF_H
+#define OFPROTO_DPIF_H 1
+
+#include <stdint.h>
+
+#include "hmapx.h"
+#include "ofproto/ofproto-provider.h"
+#include "tag.h"
+#include "timer.h"
+#include "util.h"
+
+union user_action_cookie;
+
+#define MAX_MIRRORS 32
+typedef uint32_t mirror_mask_t;
+#define MIRROR_MASK_C(X) UINT32_C(X)
+BUILD_ASSERT_DECL(sizeof(mirror_mask_t) * CHAR_BIT >= MAX_MIRRORS);
+
+/* Number of implemented OpenFlow tables. */
+enum { N_TABLES = 255 };
+enum { TBL_INTERNAL = N_TABLES - 1 };    /* Used for internal hidden rules. */
+BUILD_ASSERT_DECL(N_TABLES >= 2 && N_TABLES <= 255);
+
+struct rule_dpif {
+    struct rule up;
+
+    /* These statistics:
+     *
+     *   - Do include packets and bytes from facets that have been deleted or
+     *     whose own statistics have been folded into the rule.
+     *
+     *   - Do include packets and bytes sent "by hand" that were accounted to
+     *     the rule without any facet being involved (this is a rare corner
+     *     case in rule_execute()).
+     *
+     *   - Do not include packet or bytes that can be obtained from any facet's
+     *     packet_count or byte_count member or that can be obtained from the
+     *     datapath by, e.g., dpif_flow_get() for any subfacet.
+     */
+    uint64_t packet_count;       /* Number of packets received. */
+    uint64_t byte_count;         /* Number of bytes received. */
+
+    tag_type tag;                /* Caches rule_calculate_tag() result. */
+
+    struct list facets;          /* List of "struct facet"s. */
+};
+
+/* Extra information about a classifier table.
+ * Currently used just for optimized flow revalidation. */
+struct table_dpif {
+    /* If either of these is nonnull, then this table has a form that allows
+     * flows to be tagged to avoid revalidating most flows for the most common
+     * kinds of flow table changes. */
+    struct cls_table *catchall_table; /* Table that wildcards all fields. */
+    struct cls_table *other_table;    /* Table with any other wildcard set. */
+    uint32_t basis;                   /* Keeps each table's tags separate. */
+};
+
+struct ofproto_dpif {
+    struct hmap_node all_ofproto_dpifs_node; /* In 'all_ofproto_dpifs'. */
+    struct ofproto up;
+    struct dpif_backer *backer;
+
+    /* Special OpenFlow rules. */
+    struct rule_dpif *miss_rule; /* Sends flow table misses to controller. */
+    struct rule_dpif *no_packet_in_rule; /* Drops flow table misses. */
+    struct rule_dpif *drop_frags_rule; /* Used in OFPC_FRAG_DROP mode. */
+
+    /* Bridging. */
+    struct netflow *netflow;
+    struct dpif_sflow *sflow;
+    struct dpif_ipfix *ipfix;
+    struct hmap bundles;        /* Contains "struct ofbundle"s. */
+    struct mac_learning *ml;
+    struct ofmirror *mirrors[MAX_MIRRORS];
+    bool has_mirrors;
+    bool has_bonded_bundles;
+
+    /* Facets. */
+    struct classifier facets;     /* Contains 'struct facet's. */
+    long long int consistency_rl;
+
+    /* Revalidation. */
+    struct table_dpif tables[N_TABLES];
+
+    /* Support for debugging async flow mods. */
+    struct list completions;
+
+    struct netdev_stats stats; /* To account packets generated and consumed in
+                                * userspace. */
+
+    /* Spanning tree. */
+    struct stp *stp;
+    long long int stp_last_tick;
+
+    /* VLAN splinters. */
+    struct hmap realdev_vid_map; /* (realdev,vid) -> vlandev. */
+    struct hmap vlandev_map;     /* vlandev -> (realdev,vid). */
+
+    /* Ports. */
+    struct sset ports;             /* Set of standard port names. */
+    struct sset ghost_ports;       /* Ports with no datapath port. */
+    struct sset port_poll_set;     /* Queued names for port_poll() reply. */
+    int port_poll_errno;           /* Last errno for port_poll() reply. */
+
+    /* Per ofproto's dpif stats. */
+    uint64_t n_hit;
+    uint64_t n_missed;
+};
+
+struct ofport_dpif {
+    struct hmap_node odp_port_node; /* In dpif_backer's "odp_to_ofport_map". */
+    struct ofport up;
+
+    odp_port_t odp_port;
+    struct ofbundle *bundle;    /* Bundle that contains this port, if any. */
+    struct list bundle_node;    /* In struct ofbundle's "ports" list. */
+    struct cfm *cfm;            /* Connectivity Fault Management, if any. */
+    struct bfd *bfd;            /* BFD, if any. */
+    tag_type tag;               /* Tag associated with this port. */
+    bool may_enable;            /* May be enabled in bonds. */
+    bool is_tunnel;             /* This port is a tunnel. */
+    long long int carrier_seq;  /* Carrier status changes. */
+    struct ofport_dpif *peer;   /* Peer if patch port. */
+
+    /* Spanning tree. */
+    struct stp_port *stp_port;  /* Spanning Tree Protocol, if any. */
+    enum stp_state stp_state;   /* Always STP_DISABLED if STP not in use. */
+    long long int stp_state_entered;
+
+    struct hmap priorities;     /* Map of attached 'priority_to_dscp's. */
+
+    /* Linux VLAN device support (e.g. "eth0.10" for VLAN 10.)
+     *
+     * This is deprecated.  It is only for compatibility with broken device
+     * drivers in old versions of Linux that do not properly support VLANs when
+     * VLAN devices are not used.  When broken device drivers are no longer in
+     * widespread use, we will delete these interfaces. */
+    ofp_port_t realdev_ofp_port;
+    int vlandev_vid;
+};
+
+struct ofbundle {
+    struct hmap_node hmap_node; /* In struct ofproto's "bundles" hmap. */
+    struct ofproto_dpif *ofproto; /* Owning ofproto. */
+    void *aux;                  /* Key supplied by ofproto's client. */
+    char *name;                 /* Identifier for log messages. */
+
+    /* Configuration. */
+    struct list ports;          /* Contains "struct ofport"s. */
+    enum port_vlan_mode vlan_mode; /* VLAN mode */
+    int vlan;                   /* -1=trunk port, else a 12-bit VLAN ID. */
+    unsigned long *trunks;      /* Bitmap of trunked VLANs, if 'vlan' == -1.
+                                 * NULL if all VLANs are trunked. */
+    struct lacp *lacp;          /* LACP if LACP is enabled, otherwise NULL. */
+    struct bond *bond;          /* Nonnull iff more than one port. */
+    bool use_priority_tags;     /* Use 802.1p tag for frames in VLAN 0? */
+
+    /* Status. */
+    bool floodable;          /* True if no port has OFPUTIL_PC_NO_FLOOD set. */
+
+    /* Port mirroring info. */
+    mirror_mask_t src_mirrors;  /* Mirrors triggered when packet received. */
+    mirror_mask_t dst_mirrors;  /* Mirrors triggered when packet sent. */
+    mirror_mask_t mirror_out;   /* Mirrors that output to this bundle. */
+};
+
+struct ofmirror {
+    struct ofproto_dpif *ofproto; /* Owning ofproto. */
+    size_t idx;                 /* In ofproto's "mirrors" array. */
+    void *aux;                  /* Key supplied by ofproto's client. */
+    char *name;                 /* Identifier for log messages. */
+
+    /* Selection criteria. */
+    struct hmapx srcs;          /* Contains "struct ofbundle *"s. */
+    struct hmapx dsts;          /* Contains "struct ofbundle *"s. */
+    unsigned long *vlans;       /* Bitmap of chosen VLANs, NULL selects all. */
+
+    /* Output (exactly one of out == NULL and out_vlan == -1 is true). */
+    struct ofbundle *out;       /* Output port or NULL. */
+    int out_vlan;               /* Output VLAN or -1. */
+    mirror_mask_t dup_mirrors;  /* Bitmap of mirrors with the same output. */
+
+    /* Counters. */
+    int64_t packet_count;       /* Number of packets sent. */
+    int64_t byte_count;         /* Number of bytes sent. */
+};
+
+static inline struct rule_dpif *rule_dpif_cast(const struct rule *rule)
+{
+    return rule ? CONTAINER_OF(rule, struct rule_dpif, up) : NULL;
+}
+
+static inline struct ofproto_dpif *
+ofproto_dpif_cast(const struct ofproto *ofproto)
+{
+    ovs_assert(ofproto->ofproto_class == &ofproto_dpif_class);
+    return CONTAINER_OF(ofproto, struct ofproto_dpif, up);
+}
+
+static inline struct ofport_dpif *
+ofbundle_get_a_port(const struct ofbundle *bundle)
+{
+    return CONTAINER_OF(list_front(&bundle->ports), struct ofport_dpif,
+                        bundle_node);
+}
+
+static inline int
+mirror_mask_ffs(mirror_mask_t mask)
+{
+    BUILD_ASSERT_DECL(sizeof(unsigned int) >= sizeof(mask));
+    return ffs(mask);
+}
+
+struct ofport_dpif *get_ofp_port(const struct ofproto_dpif *,
+                                 ofp_port_t ofp_port);
+
+struct ofport_dpif *get_odp_port(const struct ofproto_dpif *,
+                                        odp_port_t odp_port);
+
+odp_port_t ofp_port_to_odp_port(const struct ofproto_dpif *,
+                              ofp_port_t ofp_port);
+
+struct rule_dpif *rule_dpif_lookup_in_table(struct ofproto_dpif *,
+                                            const struct flow *,
+                                            struct flow_wildcards *,
+                                            uint8_t table_id);
+
+tag_type rule_calculate_tag(const struct flow *flow, const struct minimask *,
+                            uint32_t secret);
+
+struct rule_dpif *rule_dpif_miss_rule(struct ofproto_dpif *ofproto,
+                                      const struct flow *);
+
+void rule_credit_stats(struct rule_dpif *, const struct dpif_flow_stats *);
+
+void ofproto_trace(struct ofproto_dpif *, const struct flow *,
+                   const struct ofpbuf *packet, struct ds *);
+
+size_t put_userspace_action(const struct ofproto_dpif *,
+                            struct ofpbuf *odp_actions, const struct flow *,
+                            const union user_action_cookie *,
+                            const size_t cookie_size);
+
+bool stp_should_process_flow(const struct flow *, struct flow_wildcards *);
+void stp_process_packet(const struct ofport_dpif *,
+                        const struct ofpbuf *packet);
+
+ofp_port_t vsp_realdev_to_vlandev(const struct ofproto_dpif *,
+                                  ofp_port_t realdev_ofp_port,
+                                  ovs_be16 vlan_tci);
+
+bool ofproto_dpif_dscp_from_priority(const struct ofport_dpif *,
+                                     uint32_t priority, uint8_t *dscp);
+int ofproto_dpif_queue_to_priority(const struct ofproto_dpif *,
+                                   uint32_t queue_id, uint32_t *priority);
+tag_type calculate_flow_tag(struct ofproto_dpif *, const struct flow *,
+                            uint8_t table_id, struct rule_dpif *);
+
+#endif /* ofproto-dpif.h */
index b9d6f0d..41d106a 100644 (file)
@@ -23,6 +23,7 @@
 #include "cfm.h"
 #include "classifier.h"
 #include "heap.h"
+#include "hindex.h"
 #include "list.h"
 #include "ofp-errors.h"
 #include "ofp-util.h"
@@ -34,6 +35,7 @@ struct match;
 struct ofpact;
 struct ofputil_flow_mod;
 struct bfd_cfg;
+struct meter;
 
 /* An OpenFlow switch.
  *
@@ -48,9 +50,6 @@ struct ofproto {
     /* Settings. */
     uint64_t fallback_dpid;     /* Datapath ID if no better choice found. */
     uint64_t datapath_id;       /* Datapath ID. */
-    unsigned flow_eviction_threshold; /* Threshold at which to begin flow
-                                       * table eviction. Only affects the
-                                       * ofproto-dpif implementation */
     bool forward_bpdu;          /* Option to allow forwarding of BPDU frames
                                  * when NORMAL action is invoked. */
     char *mfr_desc;             /* Manufacturer (NULL for default)b. */
@@ -65,17 +64,26 @@ struct ofproto {
     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. */
+    ofp_port_t alloc_port_no;   /* Last allocated OpenFlow port number. */
+    ofp_port_t max_ports;       /* Max possible OpenFlow port num, plus one. */
 
     /* Flow tables. */
     struct oftable *tables;
     int n_tables;
 
+    struct hindex cookies;      /* Rules indexed on their cookie values. */
+
     /* Optimisation for flow expiry.
      * These flows should all be present in tables. */
     struct list expirable;      /* Expirable 'struct rule"s in all tables. */
 
+    /* Meter table.
+     * OpenFlow meters start at 1.  To avoid confusion we leave the first
+     * pointer in the array un-used, and index directly with the OpenFlow
+     * meter_id. */
+    struct ofputil_meter_features meter_features;
+    struct meter **meters; /* 'meter_features.max_meter' + 1 pointers. */
+
     /* OpenFlow connections. */
     struct connmgr *connmgr;
 
@@ -103,10 +111,10 @@ struct ofproto {
 };
 
 void ofproto_init_tables(struct ofproto *, int n_tables);
-void ofproto_init_max_ports(struct ofproto *, uint16_t max_ports);
+void ofproto_init_max_ports(struct ofproto *, ofp_port_t max_ports);
 
 struct ofproto *ofproto_lookup(const char *name);
-struct ofport *ofproto_get_port(const struct ofproto *, uint16_t ofp_port);
+struct ofport *ofproto_get_port(const struct ofproto *, ofp_port_t ofp_port);
 
 /* An OpenFlow port within a "struct ofproto".
  *
@@ -117,8 +125,9 @@ struct ofport {
     struct ofproto *ofproto;    /* The ofproto that contains this port. */
     struct netdev *netdev;
     struct ofputil_phy_port pp;
-    uint16_t ofp_port;          /* OpenFlow port number. */
+    ofp_port_t ofp_port;        /* OpenFlow port number. */
     unsigned int change_seq;
+    long long int created;      /* Time created, in msec. */
     int mtu;
 };
 
@@ -205,6 +214,7 @@ struct rule {
     struct ofoperation *pending; /* Operation now in progress, if nonnull. */
 
     ovs_be64 flow_cookie;        /* Controller-issued identifier. */
+    struct hindex_node cookie_node; /* In owning ofproto's 'cookies' index. */
 
     long long int created;       /* Creation time. */
     long long int modified;      /* Time of last modification. */
@@ -222,6 +232,9 @@ struct rule {
     struct ofpact *ofpacts;      /* Sequence of "struct ofpacts". */
     unsigned int ofpacts_len;    /* Size of 'ofpacts', in bytes. */
 
+    uint32_t meter_id;           /* Non-zero OF meter_id, or zero. */
+    struct list meter_list_node; /* In owning meter's 'rules' list. */
+
     /* Flow monitors. */
     enum nx_flow_monitor_flags monitor_flags;
     uint64_t add_seqno;         /* Sequence number when added. */
@@ -232,6 +245,14 @@ struct rule {
                                  * is expirable, otherwise empty. */
 };
 
+/* Threshold at which to begin flow table eviction. Only affects the
+ * ofproto-dpif implementation */
+extern unsigned flow_eviction_threshold;
+
+/* Determines which model to use for handling misses in the ofproto-dpif
+ * implementation */
+extern enum ofproto_flow_miss_model flow_miss_model;
+
 static inline struct rule *
 rule_from_cls_rule(const struct cls_rule *cls_rule)
 {
@@ -242,12 +263,12 @@ void ofproto_rule_update_used(struct rule *, long long int used);
 void ofproto_rule_expire(struct rule *, uint8_t reason);
 void ofproto_rule_destroy(struct rule *);
 
-bool ofproto_rule_has_out_port(const struct rule *, uint16_t out_port);
+bool ofproto_rule_has_out_port(const struct rule *, ofp_port_t out_port);
 
 void ofoperation_complete(struct ofoperation *, enum ofperr);
 struct rule *ofoperation_get_victim(struct ofoperation *);
 
-bool ofoperation_has_out_port(const struct ofoperation *, uint16_t out_port);
+bool ofoperation_has_out_port(const struct ofoperation *, ofp_port_t out_port);
 
 bool ofproto_rule_is_hidden(const struct rule *);
 
@@ -688,7 +709,7 @@ struct ofproto_class {
      * 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_del)(struct ofproto *ofproto, uint16_t ofp_port);
+    int (*port_del)(struct ofproto *ofproto, ofp_port_t ofp_port);
 
     /* Get port stats */
     int (*port_get_stats)(const struct ofport *port,
@@ -1311,10 +1332,52 @@ struct ofproto_class {
      * If 'realdev_ofp_port' is zero, then this function deconfigures 'ofport'
      * as a VLAN splinter port.
      *
-     * This function should be NULL if a an implementation does not support
-     * it. */
+     * This function should be NULL if an implementation does not support it.
+     */
     int (*set_realdev)(struct ofport *ofport,
-                       uint16_t realdev_ofp_port, int vid);
+                       ofp_port_t realdev_ofp_port, int vid);
+
+/* ## ------------------------ ## */
+/* ## OpenFlow meter functions ## */
+/* ## ------------------------ ## */
+
+    /* These functions should be NULL if an implementation does not support
+     * them.  They must be all null or all non-null.. */
+
+    /* Initializes 'features' to describe the metering features supported by
+     * 'ofproto'. */
+    void (*meter_get_features)(const struct ofproto *ofproto,
+                               struct ofputil_meter_features *features);
+
+    /* If '*id' is UINT32_MAX, adds a new meter with the given 'config'.  On
+     * success the function must store a provider meter ID other than
+     * UINT32_MAX in '*id'.  All further references to the meter will be made
+     * with the returned provider meter id rather than the OpenFlow meter id.
+     * The caller does not try to interpret the provider meter id, giving the
+     * implementation the freedom to either use the OpenFlow meter_id value
+     * provided in the meter configuration, or any other value suitable for the
+     * implementation.
+     *
+     * If '*id' is a value other than UINT32_MAX, modifies the existing meter
+     * with that meter provider ID to have configuration 'config'.  On failure,
+     * the existing meter configuration is left intact.  Regardless of success,
+     * any change to '*id' updates the provider meter id used for this
+     * meter. */
+    enum ofperr (*meter_set)(struct ofproto *ofproto, ofproto_meter_id *id,
+                             const struct ofputil_meter_config *config);
+
+    /* Gets the meter and meter band packet and byte counts for maximum of
+     * 'stats->n_bands' bands for the meter with provider ID 'id' within
+     * 'ofproto'.  The caller fills in the other stats values.  The band stats
+     * are copied to memory at 'stats->bands' provided by the caller.  The
+     * number of returned band stats is returned in 'stats->n_bands'. */
+    enum ofperr (*meter_get)(const struct ofproto *ofproto,
+                             ofproto_meter_id id,
+                             struct ofputil_meter_stats *stats);
+
+    /* Deletes a meter, making the 'ofproto_meter_id' invalid for any
+     * further calls. */
+    void (*meter_del)(struct ofproto *, ofproto_meter_id);
 };
 
 extern const struct ofproto_class ofproto_dpif_class;
@@ -1334,7 +1397,7 @@ int ofproto_class_unregister(const struct ofproto_class *);
 enum { OFPROTO_POSTPONE = 1 << 16 };
 BUILD_ASSERT_DECL(OFPROTO_POSTPONE < OFPERR_OFS);
 
-int ofproto_flow_mod(struct ofproto *, const struct ofputil_flow_mod *);
+int ofproto_flow_mod(struct ofproto *, struct ofputil_flow_mod *);
 void ofproto_add_flow(struct ofproto *, const struct match *,
                       unsigned int priority,
                       const struct ofpact *ofpacts, size_t ofpacts_len);
index 8890343..8141de9 100644 (file)
@@ -6,70 +6,98 @@ 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 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
-(one of those listed by \fBofproto/list\fR).  The first form specifies
-a packet's contents explicitly:
+.IP "\fBofproto/trace\fR [\fIdpname\fR] \fIodp_flow\fR [\fB\-generate \fR| \
+\fIpacket\fR]"
+.IQ "\fBofproto/trace\fR \fIbridge\fR \fIbr_flow\fR \
+[\fB\-generate \fR| \fIpacket\fR]"
+Traces the path of an imaginary packet through \fIswitch\fR and
+reports the path that it took.  The packet's headers (e.g. source and
+destination) and metadata (e.g. input port), together called its
+``flow,'' are usually all that matter for this purpose.  You can
+specify the flow in the following ways:
+.
 .RS
-.IP "\fIpriority\fR"
-Packet QoS priority. Use \fB0\fR if QoS is not setup.
-.IP "\fItun_id\fR"
-The tunnel ID on which the packet arrived.  Use
-\fB0\fR if the packet did not arrive through a tunnel.
-.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
-hex digits.  Obviously, it is inconvenient to type in the hex digits
-by hand, so the \fBovs\-pcap\fR(1) and \fBovs\-tcpundump\fR(1)
-utilities provide easier ways.
+.IP "\fIdpname\fR \fIodp_flow\fR"
+\fIodp_flow\fR is a flow in the form printed by \fBovs\-dpctl\fR(8)'s
+\fBdump\-flows\fR command.  If all of your bridges have the same type,
+which is the common case, then you can omit \fIdpname\fR, but if you
+have bridges of different types (say, both \fBovs-netdev\fR and
+\fBovs-system\fR), then you need to specify a \fIdpname\fR to disambiguate.
+.
+.IP "\fIbridge\fR \fIbr_flow\fR"
+\fIbr_flow\fR is a flow in the form similar to that accepted by
+\fBovs\-ofctl\fR(8)'s \fBadd\-flow\fR command.  (This is not an
+OpenFlow flow: besides other differences, it never contains
+wildcards.)  \fIbridge\fR names of the bridge through which
+\fIbr_flow\fR should be traced.
 .RE
+.
 .IP
-The second form specifies the packet's contents implicitly:
+Most commonly, one specifies only a flow, using one of the forms
+above, but sometimes one might need to specify an actual packet
+instead of just a flow:
+.
 .RS
-.IP "\fIflow\fR"
-A flow in one of two forms: either the form printed by
-\fBovs\-dpctl\fR(8)'s \fBdump\-flows\fR command, or in a format
-similar to that accepted by \fBovs\-ofctl\fR(8)'s \fBadd\-flow\fR
-command.  This is not an OpenFlow flow: besides other differences, it
-never contains wildcards.  \fB\*(PN\fR generates an arbitrary packet
-that has the specified \fIflow\fR.
-.RE
+.IP "Side effects."
+Some actions have side effects.  For example, the \fBnormal\fR action
+can update the MAC learning table, and the \fBlearn\fR action can
+change OpenFlow tables.  \fBofproto/trace\fR only performs side
+effects when a packet is specified.  If you want side effects to take
+place, then you must supply a packet.
+.
 .IP
-\fB\*(PN\fR will respond with extensive information on how the packet
-would be handled if it were to be received.  The packet will not
-actually be sent, but side effects such as MAC learning will occur.
+(Output actions are obviously side effects too, but
+\fBofproto/trace\fR never executes them, even when one specifies a
+packet.)
 .
-.IP "\fBofproto/trace \fIswitch flow\fR"
-Traces the path of a packet in an imaginary flow through
-\fIswitch\fR.  The arguments are:
-.RS
-.IP "\fIswitch\fR"
-The switch on which the packet arrived (one of those listed by
-\fBofproto/list\fR).
-.IP "\fIflow\fR"
-A flow in one of two forms: either the form printed by
-\fBovs\-dpctl\fR(8)'s \fBdump\-flows\fR command, or in a format
-similar to that accepted by \fBovs\-ofctl\fR(8)'s \fBadd\-flow\fR
-command.  This is not an OpenFlow flow: besides other differences, it
-never contains wildcards.
+.IP "Incomplete information."
+Most of the time, Open vSwitch can figure out everything about the
+path of a packet using just the flow, but in some special
+circumstances it needs to look at parts of the packet that are not
+included in the flow.  When this is the case, and you do not supply a
+packet, then \fBofproto/trace\fR will tell you it needs a packet.
 .RE
+.
 .IP
-\fB\*(PN\fR will respond with extensive information on how a packet
-in \fIflow\fR would be handled if it were received by
-\fIswitch\fR.  No packet will actually be sent.  Some side effects may
-occur, but MAC learning in particular will not.
+If you wish to include a packet as part of the \fBofproto/trace\fR
+operation, there are two ways to do it:
+.
+.RS
+.IP \fB\-generate\fR
+This option, added to one of the ways to specify a flow already
+described, causes Open vSwitch to internally generate a packet with
+the flow described and then to use that packet.  If your goal is to
+execute side effects, then \fB\-generate\fR is the easiest way to do
+it, but \fB\-generate\fR is not a good way to fill in incomplete
+information, because it generates packets based on only the flow
+information, which means that the packets really do not have any more
+information than the flow.
+.
+.IP \fIpacket\fR
+This form supplies an explicit \fIpacket\fR as a sequence of hex
+digits.  An Ethernet frame is at least 14 bytes long, so there must be
+at least 28 hex digits.  Obviously, it is inconvenient to type in the
+hex digits by hand, so the \fBovs\-pcap\fR(1) and
+\fBovs\-tcpundump\fR(1) utilities provide easier ways.
 .IP
-This form of \fBofproto/trace\fR cannot determine the complete set of
-datapath actions in some corner cases.  If the results say that this
-is the case, rerun \fBofproto/trace\fR supplying a packet in the flow
-to get complete results.
+With this form, packet headers are extracted directly from
+\fIpacket\fR, so the \fIodp_flow\fR or \fIbr_flow\fR should specify
+only metadata. The metadata can be:
+.RS
+.IP \fIskb_priority\fR
+Packet QoS priority.
+.IP \fIskb_mark\fR
+SKB mark of the packet.
+.IP \fItun_id\fR
+The tunnel ID on which the packet arrived.
+.IP \fIin_port\fR
+The port on which the packet arrived.
+.RE
+.
+The in_port value is kernel datapath port number for the first format
+and OpenFlow port number for the second format. The numbering of these
+two types of port usually differs and there is no relationship.
+.RE
 .IP "\fBofproto/self\-check\fR [\fIswitch\fR]"
 Runs an internal consistency check on \fIswitch\fR, if specified,
 otherwise on all ofproto instances, and responds with a brief summary
index 28df181..522c839 100644 (file)
@@ -126,6 +126,7 @@ struct ofoperation {
     /* OFOPERATION_MODIFY: The old actions, if the actions are changing. */
     struct ofpact *ofpacts;
     size_t ofpacts_len;
+    uint32_t meter_id;
 
     /* OFOPERATION_DELETE. */
     enum ofp_flow_removed_reason reason; /* Reason flow was removed. */
@@ -196,13 +197,16 @@ static bool rule_is_modifiable(const struct rule *);
 
 /* OpenFlow. */
 static enum ofperr add_flow(struct ofproto *, struct ofconn *,
-                            const struct ofputil_flow_mod *,
+                            struct ofputil_flow_mod *,
                             const struct ofp_header *);
-static void delete_flow__(struct rule *, struct ofopgroup *);
-static bool handle_openflow(struct ofconn *, struct ofpbuf *);
+static void delete_flow__(struct rule *, struct ofopgroup *,
+                          enum ofp_flow_removed_reason);
+static bool handle_openflow(struct ofconn *, const struct ofpbuf *);
 static enum ofperr handle_flow_mod__(struct ofproto *, struct ofconn *,
-                                     const struct ofputil_flow_mod *,
+                                     struct ofputil_flow_mod *,
                                      const struct ofp_header *);
+static void calc_duration(long long int start, long long int now,
+                          uint32_t *sec, uint32_t *nsec);
 
 /* ofproto. */
 static uint64_t pick_datapath_id(const struct ofproto *);
@@ -218,6 +222,9 @@ static const struct ofproto_class **ofproto_classes;
 static size_t n_ofproto_classes;
 static size_t allocated_ofproto_classes;
 
+unsigned flow_eviction_threshold = OFPROTO_FLOW_EVICTION_THRESHOLD_DEFAULT;
+enum ofproto_flow_miss_model flow_miss_model = OFPROTO_HANDLE_MISS_AUTO;
+
 /* Map from datapath name to struct ofproto, for use by unixctl commands. */
 static struct hmap all_ofprotos = HMAP_INITIALIZER(&all_ofprotos);
 
@@ -226,6 +233,9 @@ static struct shash init_ofp_ports = SHASH_INITIALIZER(&init_ofp_ports);
 
 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
 
+/* The default value of true waits for flow restore. */
+static bool flow_restore_wait = true;
+
 /* Must be called to initialize the ofproto library.
  *
  * The caller may pass in 'iface_hints', which contains an shash of
@@ -403,8 +413,6 @@ ofproto_create(const char *datapath_name, const char *datapath_type,
     hmap_insert(&all_ofprotos, &ofproto->hmap_node,
                 hash_string(ofproto->name, 0));
     ofproto->datapath_id = 0;
-    ofproto_set_flow_eviction_threshold(ofproto,
-                                        OFPROTO_FLOW_EVICTION_THRESHOLD_DEFAULT);
     ofproto->forward_bpdu = false;
     ofproto->fallback_dpid = pick_fallback_dpid();
     ofproto->mfr_desc = NULL;
@@ -419,6 +427,7 @@ ofproto_create(const char *datapath_name, const char *datapath_type,
     ofproto->max_ports = OFPP_MAX;
     ofproto->tables = NULL;
     ofproto->n_tables = 0;
+    hindex_init(&ofproto->cookies);
     list_init(&ofproto->expirable);
     ofproto->connmgr = connmgr_create(ofproto, datapath_name, datapath_name);
     ofproto->state = S_OPENFLOW;
@@ -436,14 +445,14 @@ ofproto_create(const char *datapath_name, const char *datapath_type,
     error = ofproto->ofproto_class->construct(ofproto);
     if (error) {
         VLOG_ERR("failed to open datapath %s: %s",
-                 datapath_name, strerror(error));
+                 datapath_name, ovs_strerror(error));
         ofproto_destroy__(ofproto);
         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);
+    ofproto->ofp_port_ids = bitmap_allocate(ofp_to_u16(ofproto->max_ports));
     bitmap_set1(ofproto->ofp_port_ids, 0);
 
     /* Check that hidden tables, if any, are at the end. */
@@ -458,6 +467,16 @@ ofproto_create(const char *datapath_name, const char *datapath_type,
     ofproto->datapath_id = pick_datapath_id(ofproto);
     init_ports(ofproto);
 
+    /* Initialize meters table. */
+    if (ofproto->ofproto_class->meter_get_features) {
+        ofproto->ofproto_class->meter_get_features(ofproto,
+                                                   &ofproto->meter_features);
+    } else {
+        memset(&ofproto->meter_features, 0, sizeof ofproto->meter_features);
+    }
+    ofproto->meters = xzalloc((ofproto->meter_features.max_meters + 1)
+                              * sizeof(struct meter *));
+
     *ofprotop = ofproto;
     return 0;
 }
@@ -492,9 +511,9 @@ ofproto_init_tables(struct ofproto *ofproto, int n_tables)
  * Reserved ports numbered OFPP_MAX and higher are special and not subject to
  * the 'max_ports' restriction. */
 void
-ofproto_init_max_ports(struct ofproto *ofproto, uint16_t max_ports)
+ofproto_init_max_ports(struct ofproto *ofproto, ofp_port_t max_ports)
 {
-    ovs_assert(max_ports <= OFPP_MAX);
+    ovs_assert(ofp_to_u16(max_ports) <= ofp_to_u16(OFPP_MAX));
     ofproto->max_ports = max_ports;
 }
 
@@ -561,13 +580,17 @@ ofproto_set_in_band_queue(struct ofproto *ofproto, int queue_id)
 /* Sets the number of flows at which eviction from the kernel flow table
  * will occur. */
 void
-ofproto_set_flow_eviction_threshold(struct ofproto *ofproto, unsigned threshold)
+ofproto_set_flow_eviction_threshold(unsigned threshold)
 {
-    if (threshold < OFPROTO_FLOW_EVICTION_THRESHOLD_MIN) {
-        ofproto->flow_eviction_threshold = OFPROTO_FLOW_EVICTION_THRESHOLD_MIN;
-    } else {
-        ofproto->flow_eviction_threshold = threshold;
-    }
+    flow_eviction_threshold = MAX(OFPROTO_FLOW_EVICTION_THRESHOLD_MIN,
+                                  threshold);
+}
+
+/* Sets the path for handling flow misses. */
+void
+ofproto_set_flow_miss_model(unsigned model)
+{
+    flow_miss_model = model;
 }
 
 /* If forward_bpdu is true, the NORMAL action will forward frames with
@@ -653,6 +676,19 @@ ofproto_set_ipfix(struct ofproto *ofproto,
         return (bo || fo) ? EOPNOTSUPP : 0;
     }
 }
+
+void
+ofproto_set_flow_restore_wait(bool flow_restore_wait_db)
+{
+    flow_restore_wait = flow_restore_wait_db;
+}
+
+bool
+ofproto_get_flow_restore_wait(void)
+{
+    return flow_restore_wait;
+}
+
 \f
 /* Spanning Tree Protocol (STP) configuration. */
 
@@ -691,7 +727,7 @@ ofproto_get_stp_status(struct ofproto *ofproto,
  *
  * Returns 0 if successful, otherwise a positive errno value.*/
 int
-ofproto_port_set_stp(struct ofproto *ofproto, uint16_t ofp_port,
+ofproto_port_set_stp(struct ofproto *ofproto, ofp_port_t ofp_port,
                      const struct ofproto_port_stp_settings *s)
 {
     struct ofport *ofport = ofproto_get_port(ofproto, ofp_port);
@@ -712,7 +748,7 @@ ofproto_port_set_stp(struct ofproto *ofproto, uint16_t ofp_port,
  *
  * Returns 0 if successful, otherwise a positive errno value.*/
 int
-ofproto_port_get_stp_status(struct ofproto *ofproto, uint16_t ofp_port,
+ofproto_port_get_stp_status(struct ofproto *ofproto, ofp_port_t ofp_port,
                             struct ofproto_port_stp_status *s)
 {
     struct ofport *ofport = ofproto_get_port(ofproto, ofp_port);
@@ -737,7 +773,7 @@ ofproto_port_get_stp_status(struct ofproto *ofproto, uint16_t ofp_port,
  *
  * Returns 0 if successful, otherwise a positive errno value. */
 int
-ofproto_port_set_queues(struct ofproto *ofproto, uint16_t ofp_port,
+ofproto_port_set_queues(struct ofproto *ofproto, ofp_port_t ofp_port,
                         const struct ofproto_port_queue *queues,
                         size_t n_queues)
 {
@@ -758,7 +794,7 @@ ofproto_port_set_queues(struct ofproto *ofproto, uint16_t ofp_port,
 
 /* Clears the CFM configuration from 'ofp_port' on 'ofproto'. */
 void
-ofproto_port_clear_cfm(struct ofproto *ofproto, uint16_t ofp_port)
+ofproto_port_clear_cfm(struct ofproto *ofproto, ofp_port_t ofp_port)
 {
     struct ofport *ofport = ofproto_get_port(ofproto, ofp_port);
     if (ofport && ofproto->ofproto_class->set_cfm) {
@@ -773,7 +809,7 @@ ofproto_port_clear_cfm(struct ofproto *ofproto, uint16_t ofp_port)
  *
  * This function has no effect if 'ofproto' does not have a port 'ofp_port'. */
 void
-ofproto_port_set_cfm(struct ofproto *ofproto, uint16_t ofp_port,
+ofproto_port_set_cfm(struct ofproto *ofproto, ofp_port_t ofp_port,
                      const struct cfm_settings *s)
 {
     struct ofport *ofport;
@@ -795,14 +831,14 @@ ofproto_port_set_cfm(struct ofproto *ofproto, uint16_t ofp_port,
     if (error) {
         VLOG_WARN("%s: CFM configuration on port %"PRIu16" (%s) failed (%s)",
                   ofproto->name, ofp_port, netdev_get_name(ofport->netdev),
-                  strerror(error));
+                  ovs_strerror(error));
     }
 }
 
 /* Configures BFD on 'ofp_port' in 'ofproto'.  This function has no effect if
  * 'ofproto' does not have a port 'ofp_port'. */
 void
-ofproto_port_set_bfd(struct ofproto *ofproto, uint16_t ofp_port,
+ofproto_port_set_bfd(struct ofproto *ofproto, ofp_port_t ofp_port,
                      const struct smap *cfg)
 {
     struct ofport *ofport;
@@ -812,6 +848,7 @@ ofproto_port_set_bfd(struct ofproto *ofproto, uint16_t ofp_port,
     if (!ofport) {
         VLOG_WARN("%s: cannot configure bfd on nonexistent port %"PRIu16,
                   ofproto->name, ofp_port);
+        return;
     }
 
     error = (ofproto->ofproto_class->set_bfd
@@ -820,7 +857,7 @@ ofproto_port_set_bfd(struct ofproto *ofproto, uint16_t ofp_port,
     if (error) {
         VLOG_WARN("%s: bfd configuration on port %"PRIu16" (%s) failed (%s)",
                   ofproto->name, ofp_port, netdev_get_name(ofport->netdev),
-                  strerror(error));
+                  ovs_strerror(error));
     }
 }
 
@@ -829,7 +866,7 @@ ofproto_port_set_bfd(struct ofproto *ofproto, uint16_t ofp_port,
  * OVS database.  Has no effect if 'ofp_port' is not na OpenFlow port in
  * 'ofproto'. */
 int
-ofproto_port_get_bfd_status(struct ofproto *ofproto, uint16_t ofp_port,
+ofproto_port_get_bfd_status(struct ofproto *ofproto, ofp_port_t ofp_port,
                             struct smap *status)
 {
     struct ofport *ofport = ofproto_get_port(ofproto, ofp_port);
@@ -843,7 +880,7 @@ ofproto_port_get_bfd_status(struct ofproto *ofproto, uint16_t ofp_port,
  * 0 if LACP partner information is not current (generally indicating a
  * connectivity problem), or -1 if LACP is not enabled on 'ofp_port'. */
 int
-ofproto_port_is_lacp_current(struct ofproto *ofproto, uint16_t ofp_port)
+ofproto_port_is_lacp_current(struct ofproto *ofproto, ofp_port_t ofp_port)
 {
     struct ofport *ofport = ofproto_get_port(ofproto, ofp_port);
     return (ofport && ofproto->ofproto_class->port_is_lacp_current
@@ -1131,7 +1168,7 @@ ofproto_type_run(const char *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));
+                    datapath_type, ovs_strerror(error));
     }
     return error;
 }
@@ -1148,7 +1185,7 @@ ofproto_type_run_fast(const char *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));
+                    datapath_type, ovs_strerror(error));
     }
     return error;
 }
@@ -1176,7 +1213,7 @@ ofproto_run(struct ofproto *p)
 
     error = p->ofproto_class->run(p);
     if (error && error != EAGAIN) {
-        VLOG_ERR_RL(&rl, "%s: run failed (%s)", p->name, strerror(error));
+        VLOG_ERR_RL(&rl, "%s: run failed (%s)", p->name, ovs_strerror(error));
     }
 
     if (p->ofproto_class->port_poll) {
@@ -1286,7 +1323,7 @@ ofproto_run_fast(struct ofproto *p)
     error = p->ofproto_class->run_fast ? p->ofproto_class->run_fast(p) : 0;
     if (error && error != EAGAIN) {
         VLOG_ERR_RL(&rl, "%s: fastpath run failed (%s)",
-                    p->name, strerror(error));
+                    p->name, ovs_strerror(error));
     }
     return error;
 }
@@ -1480,16 +1517,17 @@ ofproto_port_open_type(const char *datapath_type, const char *port_type)
  * 'ofp_portp' is non-null). */
 int
 ofproto_port_add(struct ofproto *ofproto, struct netdev *netdev,
-                 uint16_t *ofp_portp)
+                 ofp_port_t *ofp_portp)
 {
-    uint16_t ofp_port = ofp_portp ? *ofp_portp : OFPP_NONE;
+    ofp_port_t ofp_port = ofp_portp ? *ofp_portp : OFPP_NONE;
     int error;
 
     error = ofproto->ofproto_class->port_add(ofproto, netdev);
     if (!error) {
         const char *netdev_name = netdev_get_name(netdev);
 
-        simap_put(&ofproto->ofp_requests, netdev_name, ofp_port);
+        simap_put(&ofproto->ofp_requests, netdev_name,
+                  ofp_to_u16(ofp_port));
         update_port(ofproto, netdev_name);
     }
     if (ofp_portp) {
@@ -1525,7 +1563,7 @@ ofproto_port_query_by_name(const struct ofproto *ofproto, const char *devname,
 /* Deletes port number 'ofp_port' from the datapath for 'ofproto'.
  * Returns 0 if successful, otherwise a positive errno. */
 int
-ofproto_port_del(struct ofproto *ofproto, uint16_t ofp_port)
+ofproto_port_del(struct ofproto *ofproto, ofp_port_t ofp_port)
 {
     struct ofport *ofport = ofproto_get_port(ofproto, ofp_port);
     const char *name = ofport ? netdev_get_name(ofport->netdev) : "<unknown>";
@@ -1591,7 +1629,7 @@ ofproto_add_flow(struct ofproto *ofproto, const struct match *match,
  *
  * This is a helper function for in-band control and fail-open. */
 int
-ofproto_flow_mod(struct ofproto *ofproto, const struct ofputil_flow_mod *fm)
+ofproto_flow_mod(struct ofproto *ofproto, struct ofputil_flow_mod *fm)
 {
     return handle_flow_mod__(ofproto, NULL, fm, NULL);
 }
@@ -1662,43 +1700,48 @@ reinit_ports(struct ofproto *p)
     sset_destroy(&devnames);
 }
 
-static uint16_t
+static ofp_port_t
 alloc_ofp_port(struct ofproto *ofproto, const char *netdev_name)
 {
-    uint16_t ofp_port;
-    uint16_t end_port_no = ofproto->alloc_port_no;
+    uint16_t max_ports = ofp_to_u16(ofproto->max_ports);
+    uint16_t port_idx;
 
-    ofp_port = simap_get(&ofproto->ofp_requests, netdev_name);
-    ofp_port = ofp_port ? ofp_port : OFPP_NONE;
+    port_idx = simap_get(&ofproto->ofp_requests, netdev_name);
+    if (!port_idx) {
+        port_idx = UINT16_MAX;
+    }
+
+    if (port_idx >= max_ports
+        || bitmap_is_set(ofproto->ofp_port_ids, port_idx)) {
+        uint16_t end_port_no = ofp_to_u16(ofproto->alloc_port_no);
+        uint16_t alloc_port_no = end_port_no;
 
-    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 (++alloc_port_no >= max_ports) {
+                alloc_port_no = 0;
             }
-            if (!bitmap_is_set(ofproto->ofp_port_ids,
-                               ofproto->alloc_port_no)) {
-                ofp_port = ofproto->alloc_port_no;
+            if (!bitmap_is_set(ofproto->ofp_port_ids, alloc_port_no)) {
+                port_idx = alloc_port_no;
+                ofproto->alloc_port_no = u16_to_ofp(alloc_port_no);
                 break;
             }
-            if (ofproto->alloc_port_no == end_port_no) {
+            if (alloc_port_no == end_port_no) {
                 return OFPP_NONE;
             }
         }
     }
-    bitmap_set1(ofproto->ofp_port_ids, ofp_port);
-    return ofp_port;
+    bitmap_set1(ofproto->ofp_port_ids, port_idx);
+    return u16_to_ofp(port_idx);
 }
 
 static void
-dealloc_ofp_port(const struct ofproto *ofproto, uint16_t ofp_port)
+dealloc_ofp_port(const struct ofproto *ofproto, ofp_port_t ofp_port)
 {
-    if (ofp_port < ofproto->max_ports) {
-        bitmap_set0(ofproto->ofp_port_ids, ofp_port);
+    if (ofp_to_u16(ofp_port) < ofp_to_u16(ofproto->max_ports)) {
+        bitmap_set0(ofproto->ofp_port_ids, ofp_to_u16(ofp_port));
     }
 }
 
@@ -1720,7 +1763,7 @@ ofport_open(struct ofproto *ofproto,
                      "cannot be opened (%s)",
                      ofproto->name,
                      ofproto_port->name, ofproto_port->ofp_port,
-                     ofproto_port->name, strerror(error));
+                     ofproto_port->name, ovs_strerror(error));
         return NULL;
     }
 
@@ -1786,9 +1829,11 @@ ofport_install(struct ofproto *p,
     ofport->change_seq = netdev_change_seq(netdev);
     ofport->pp = *pp;
     ofport->ofp_port = pp->port_no;
+    ofport->created = time_msec();
 
     /* Add port to 'p'. */
-    hmap_insert(&p->ports, &ofport->hmap_node, hash_int(ofport->ofp_port, 0));
+    hmap_insert(&p->ports, &ofport->hmap_node,
+                hash_ofp_port(ofport->ofp_port));
     shash_add(&p->port_by_name, netdev_name, ofport);
 
     update_mtu(p, ofport);
@@ -1803,7 +1848,7 @@ ofport_install(struct ofproto *p,
 
 error:
     VLOG_WARN_RL(&rl, "%s: could not add port %s (%s)",
-                 p->name, netdev_name, strerror(error));
+                 p->name, netdev_name, ovs_strerror(error));
     if (ofport) {
         ofport_destroy__(ofport);
     } else {
@@ -1864,7 +1909,7 @@ ofproto_port_set_state(struct ofport *port, enum ofputil_port_state state)
 }
 
 void
-ofproto_port_unregister(struct ofproto *ofproto, uint16_t ofp_port)
+ofproto_port_unregister(struct ofproto *ofproto, ofp_port_t ofp_port)
 {
     struct ofport *port = ofproto_get_port(ofproto, ofp_port);
     if (port) {
@@ -1908,12 +1953,12 @@ ofport_destroy(struct ofport *port)
 }
 
 struct ofport *
-ofproto_get_port(const struct ofproto *ofproto, uint16_t ofp_port)
+ofproto_get_port(const struct ofproto *ofproto, ofp_port_t ofp_port)
 {
     struct ofport *port;
 
-    HMAP_FOR_EACH_IN_BUCKET (port, hmap_node,
-                             hash_int(ofp_port, 0), &ofproto->ports) {
+    HMAP_FOR_EACH_IN_BUCKET (port, hmap_node, hash_ofp_port(ofp_port),
+                             &ofproto->ports) {
         if (port->ofp_port == ofp_port) {
             return port;
         }
@@ -1950,6 +1995,7 @@ update_port(struct ofproto *ofproto, const char *name)
     netdev = (!ofproto_port_query_by_name(ofproto, name, &ofproto_port)
               ? ofport_open(ofproto, &ofproto_port, &pp)
               : NULL);
+
     if (netdev) {
         port = ofproto_get_port(ofproto, ofproto_port.ofp_port);
         if (port && !strcmp(netdev_get_name(port->netdev), name)) {
@@ -2011,7 +2057,8 @@ init_ports(struct ofproto *p)
             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);
+                simap_put(&p->ofp_requests, name,
+                          ofp_to_u16(iface_hint->ofp_port));
             }
 
             netdev = ofport_open(p, &ofproto_port, &pp);
@@ -2135,7 +2182,7 @@ ofproto_rule_destroy(struct rule *rule)
 /* Returns true if 'rule' has an OpenFlow OFPAT_OUTPUT or OFPAT_ENQUEUE action
  * that outputs to 'port' (output to OFPP_FLOOD and OFPP_ALL doesn't count). */
 bool
-ofproto_rule_has_out_port(const struct rule *rule, uint16_t port)
+ofproto_rule_has_out_port(const struct rule *rule, ofp_port_t port)
 {
     return (port == OFPP_ANY
             || ofpacts_output_to_port(rule->ofpacts, rule->ofpacts_len, port));
@@ -2144,7 +2191,7 @@ ofproto_rule_has_out_port(const struct rule *rule, uint16_t port)
 /* Returns true if a rule related to 'op' has an OpenFlow OFPAT_OUTPUT or
  * OFPAT_ENQUEUE action that outputs to 'out_port'. */
 bool
-ofoperation_has_out_port(const struct ofoperation *op, uint16_t out_port)
+ofoperation_has_out_port(const struct ofoperation *op, ofp_port_t out_port)
 {
     if (ofproto_rule_has_out_port(op->rule, out_port)) {
         return true;
@@ -2173,13 +2220,15 @@ ofoperation_has_out_port(const struct ofoperation *op, uint16_t out_port)
  *
  * Takes ownership of 'packet'. */
 static int
-rule_execute(struct rule *rule, uint16_t in_port, struct ofpbuf *packet)
+rule_execute(struct rule *rule, ofp_port_t in_port, struct ofpbuf *packet)
 {
     struct flow flow;
+    union flow_in_port in_port_;
 
     ovs_assert(ofpbuf_headroom(packet) >= sizeof(struct ofp10_packet_in));
 
-    flow_extract(packet, 0, 0, NULL, in_port, &flow);
+    in_port_.ofp_port = in_port;
+    flow_extract(packet, 0, 0, NULL, &in_port_, &flow);
     return rule->ofproto->ofproto_class->rule_execute(rule, &flow, packet);
 }
 
@@ -2331,6 +2380,55 @@ reject_slave_controller(struct ofconn *ofconn)
     }
 }
 
+/* Finds the OFPACT_METER action, if any, in the 'ofpacts_len' bytes of
+ * 'ofpacts'.  If found, returns its meter ID; if not, returns 0.
+ *
+ * This function relies on the order of 'ofpacts' being correct (as checked by
+ * ofpacts_verify()). */
+static uint32_t
+find_meter(const struct ofpact ofpacts[], size_t ofpacts_len)
+{
+    const struct ofpact *a;
+
+    OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
+        enum ovs_instruction_type inst;
+
+        inst = ovs_instruction_type_from_ofpact_type(a->type);
+        if (a->type == OFPACT_METER) {
+            return ofpact_get_METER(a)->meter_id;
+        } else if (inst > OVSINST_OFPIT13_METER) {
+            break;
+        }
+    }
+
+    return 0;
+}
+
+/* Checks that the 'ofpacts_len' bytes of actions in 'ofpacts' are appropriate
+ * for a packet with the prerequisites satisfied by 'flow' in table 'table_id'.
+ * 'flow' may be temporarily modified, but is restored at return.
+ */
+static enum ofperr
+ofproto_check_ofpacts(struct ofproto *ofproto,
+                      const struct ofpact ofpacts[], size_t ofpacts_len,
+                      struct flow *flow, uint8_t table_id)
+{
+    enum ofperr error;
+    uint32_t mid;
+
+    error = ofpacts_check(ofpacts, ofpacts_len, flow, ofproto->max_ports,
+                          table_id);
+    if (error) {
+        return error;
+    }
+
+    mid = find_meter(ofpacts, ofpacts_len);
+    if (mid && ofproto_get_provider_meter_id(ofproto, mid) == UINT32_MAX) {
+        return OFPERR_OFPMMFC_INVALID_METER;
+    }
+    return 0;
+}
+
 static enum ofperr
 handle_packet_out(struct ofconn *ofconn, const struct ofp_header *oh)
 {
@@ -2340,6 +2438,7 @@ handle_packet_out(struct ofconn *ofconn, const struct ofp_header *oh)
     uint64_t ofpacts_stub[1024 / 8];
     struct ofpbuf ofpacts;
     struct flow flow;
+    union flow_in_port in_port_;
     enum ofperr error;
 
     COVERAGE_INC(ofproto_packet_out);
@@ -2355,7 +2454,8 @@ handle_packet_out(struct ofconn *ofconn, const struct ofp_header *oh)
     if (error) {
         goto exit_free_ofpacts;
     }
-    if (po.in_port >= p->max_ports && po.in_port < OFPP_MAX) {
+    if (ofp_to_u16(po.in_port) >= ofp_to_u16(p->max_ports)
+        && ofp_to_u16(po.in_port) < ofp_to_u16(OFPP_MAX)) {
         error = OFPERR_OFPBRC_BAD_PORT;
         goto exit_free_ofpacts;
     }
@@ -2373,8 +2473,9 @@ handle_packet_out(struct ofconn *ofconn, const struct ofp_header *oh)
     }
 
     /* Verify actions against packet, then send packet if successful. */
-    flow_extract(payload, 0, 0, NULL, po.in_port, &flow);
-    error = ofpacts_check(po.ofpacts, po.ofpacts_len, &flow, p->max_ports);
+    in_port_.ofp_port = po.in_port;
+    flow_extract(payload, 0, 0, NULL, &in_port_, &flow);
+    error = ofproto_check_ofpacts(p, po.ofpacts, po.ofpacts_len, &flow, 0);
     if (!error) {
         error = p->ofproto_class->packet_out(p, payload, &flow,
                                              po.ofpacts, po.ofpacts_len);
@@ -2543,6 +2644,9 @@ append_port_stat(struct ofport *port, struct list *replies)
 {
     struct ofputil_port_stats ops = { .port_no = port->pp.port_no };
 
+    calc_duration(port->created, time_msec(),
+                  &ops.duration_sec, &ops.duration_nsec);
+
     /* Intentionally ignore return value, since errors will set
      * 'stats' to all-1s, which is correct for OpenFlow, and
      * netdev_get_stats() will log errors. */
@@ -2558,7 +2662,7 @@ handle_port_stats_request(struct ofconn *ofconn,
     struct ofproto *p = ofconn_get_ofproto(ofconn);
     struct ofport *port;
     struct list replies;
-    uint16_t port_no;
+    ofp_port_t port_no;
     enum ofperr error;
 
     error = ofputil_decode_port_stats_request(request, &port_no);
@@ -2602,9 +2706,42 @@ handle_port_desc_stats_request(struct ofconn *ofconn,
     return 0;
 }
 
+static uint32_t
+hash_cookie(ovs_be64 cookie)
+{
+    return hash_2words((OVS_FORCE uint64_t)cookie >> 32,
+                       (OVS_FORCE uint64_t)cookie);
+}
+
+static void
+cookies_insert(struct ofproto *ofproto, struct rule *rule)
+{
+    hindex_insert(&ofproto->cookies, &rule->cookie_node,
+                  hash_cookie(rule->flow_cookie));
+}
+
+static void
+cookies_remove(struct ofproto *ofproto, struct rule *rule)
+{
+    hindex_remove(&ofproto->cookies, &rule->cookie_node);
+}
+
+static void
+ofproto_rule_change_cookie(struct ofproto *ofproto, struct rule *rule,
+                           ovs_be64 new_cookie)
+{
+    if (new_cookie != rule->flow_cookie) {
+        cookies_remove(ofproto, rule);
+
+        rule->flow_cookie = new_cookie;
+
+        cookies_insert(ofproto, rule);
+    }
+}
+
 static void
-calc_flow_duration__(long long int start, long long int now,
-                     uint32_t *sec, uint32_t *nsec)
+calc_duration(long long int start, long long int now,
+              uint32_t *sec, uint32_t *nsec)
 {
     long long int msecs = now - start;
     *sec = msecs / 1000;
@@ -2694,7 +2831,7 @@ static enum ofperr
 collect_rules_loose(struct ofproto *ofproto, uint8_t table_id,
                     const struct match *match,
                     ovs_be64 cookie, ovs_be64 cookie_mask,
-                    uint16_t out_port, struct list *rules)
+                    ofp_port_t out_port, struct list *rules)
 {
     struct oftable *table;
     struct cls_rule cr;
@@ -2707,6 +2844,32 @@ collect_rules_loose(struct ofproto *ofproto, uint8_t table_id,
 
     list_init(rules);
     cls_rule_init(&cr, match, 0);
+
+    if (cookie_mask == htonll(UINT64_MAX)) {
+        struct rule *rule;
+
+        HINDEX_FOR_EACH_WITH_HASH (rule, cookie_node, hash_cookie(cookie),
+                                   &ofproto->cookies) {
+            if (table_id != rule->table_id && table_id != 0xff) {
+                continue;
+            }
+            if (ofproto_rule_is_hidden(rule)) {
+                continue;
+            }
+            if (cls_rule_is_loose_match(&rule->cr, &cr.match)) {
+                if (rule->pending) {
+                    error = OFPROTO_POSTPONE;
+                    goto exit;
+                }
+                if (rule->flow_cookie == cookie /* Hash collisions possible. */
+                    && ofproto_rule_has_out_port(rule, out_port)) {
+                    list_push_back(rules, &rule->ofproto_node);
+                }
+            }
+        }
+        goto exit;
+    }
+
     FOR_EACH_MATCHING_TABLE (table, table_id, ofproto) {
         struct cls_cursor cursor;
         struct rule *rule;
@@ -2745,7 +2908,7 @@ static enum ofperr
 collect_rules_strict(struct ofproto *ofproto, uint8_t table_id,
                      const struct match *match, unsigned int priority,
                      ovs_be64 cookie, ovs_be64 cookie_mask,
-                     uint16_t out_port, struct list *rules)
+                     ofp_port_t out_port, struct list *rules)
 {
     struct oftable *table;
     struct cls_rule cr;
@@ -2758,6 +2921,32 @@ collect_rules_strict(struct ofproto *ofproto, uint8_t table_id,
 
     list_init(rules);
     cls_rule_init(&cr, match, priority);
+
+    if (cookie_mask == htonll(UINT64_MAX)) {
+        struct rule *rule;
+
+        HINDEX_FOR_EACH_WITH_HASH (rule, cookie_node, hash_cookie(cookie),
+                                   &ofproto->cookies) {
+            if (table_id != rule->table_id && table_id != 0xff) {
+                continue;
+            }
+            if (ofproto_rule_is_hidden(rule)) {
+                continue;
+            }
+            if (cls_rule_equal(&rule->cr, &cr)) {
+                if (rule->pending) {
+                    error = OFPROTO_POSTPONE;
+                    goto exit;
+                }
+                if (rule->flow_cookie == cookie /* Hash collisions possible. */
+                    && ofproto_rule_has_out_port(rule, out_port)) {
+                    list_push_back(rules, &rule->ofproto_node);
+                }
+            }
+        }
+        goto exit;
+    }
+
     FOR_EACH_MATCHING_TABLE (table, table_id, ofproto) {
         struct rule *rule;
 
@@ -2823,8 +3012,7 @@ handle_flow_stats_request(struct ofconn *ofconn,
         fs.priority = rule->cr.priority;
         fs.cookie = rule->flow_cookie;
         fs.table_id = rule->table_id;
-        calc_flow_duration__(rule->created, now, &fs.duration_sec,
-                             &fs.duration_nsec);
+        calc_duration(rule->created, now, &fs.duration_sec, &fs.duration_nsec);
         fs.idle_timeout = rule->idle_timeout;
         fs.hard_timeout = rule->hard_timeout;
         fs.idle_age = age_secs(now - rule->used);
@@ -2903,7 +3091,7 @@ ofproto_get_netflow_ids(const struct ofproto *ofproto,
  * The caller must provide and owns '*status', but it does not own and must not
  * modify or free the array returned in 'status->rmps'. */
 bool
-ofproto_port_get_cfm_status(const struct ofproto *ofproto, uint16_t ofp_port,
+ofproto_port_get_cfm_status(const struct ofproto *ofproto, ofp_port_t ofp_port,
                             struct ofproto_cfm_status *status)
 {
     struct ofport *ofport = ofproto_get_port(ofproto, ofp_port);
@@ -3097,13 +3285,13 @@ is_flow_deletion_pending(const struct ofproto *ofproto,
  * if any. */
 static enum ofperr
 add_flow(struct ofproto *ofproto, struct ofconn *ofconn,
-         const struct ofputil_flow_mod *fm, const struct ofp_header *request)
+         struct ofputil_flow_mod *fm, const struct ofp_header *request)
 {
     struct oftable *table;
     struct ofopgroup *group;
     struct rule *victim;
-    struct cls_rule cr;
     struct rule *rule;
+    uint8_t table_id;
     int error;
 
     error = check_table_id(ofproto, fm->table_id);
@@ -3113,7 +3301,6 @@ add_flow(struct ofproto *ofproto, struct ofconn *ofconn,
 
     /* Pick table. */
     if (fm->table_id == 0xff) {
-        uint8_t table_id;
         if (ofproto->ofproto_class->rule_choose_table) {
             error = ofproto->ofproto_class->rule_choose_table(ofproto,
                                                               &fm->match,
@@ -3122,31 +3309,39 @@ add_flow(struct ofproto *ofproto, struct ofconn *ofconn,
                 return error;
             }
             ovs_assert(table_id < ofproto->n_tables);
-            table = &ofproto->tables[table_id];
         } else {
-            table = &ofproto->tables[0];
+            table_id = 0;
         }
     } else if (fm->table_id < ofproto->n_tables) {
-        table = &ofproto->tables[fm->table_id];
+        table_id = fm->table_id;
     } else {
         return OFPERR_OFPBRC_BAD_TABLE_ID;
     }
 
+    table = &ofproto->tables[table_id];
+
     if (table->flags & OFTABLE_READONLY) {
         return OFPERR_OFPBRC_EPERM;
     }
 
+    /* Verify actions. */
+    error = ofproto_check_ofpacts(ofproto, fm->ofpacts, fm->ofpacts_len,
+                                  &fm->match.flow, table_id);
+    if (error) {
+        return error;
+    }
+
     /* Allocate new rule and initialize classifier rule. */
     rule = ofproto->ofproto_class->rule_alloc();
     if (!rule) {
         VLOG_WARN_RL(&rl, "%s: failed to create rule (%s)",
-                     ofproto->name, strerror(error));
+                     ofproto->name, ovs_strerror(error));
         return ENOMEM;
     }
     cls_rule_init(&rule->cr, &fm->match, fm->priority);
 
     /* Serialize against pending deletion. */
-    if (is_flow_deletion_pending(ofproto, &cr, table - ofproto->tables)) {
+    if (is_flow_deletion_pending(ofproto, &rule->cr, table_id)) {
         cls_rule_destroy(&rule->cr);
         ofproto->ofproto_class->rule_dealloc(rule);
         return OFPROTO_POSTPONE;
@@ -3174,6 +3369,8 @@ add_flow(struct ofproto *ofproto, struct ofconn *ofconn,
        and OFPFF13_NO_BYT_COUNTS */
     rule->ofpacts = xmemdup(fm->ofpacts, fm->ofpacts_len);
     rule->ofpacts_len = fm->ofpacts_len;
+    rule->meter_id = find_meter(rule->ofpacts, rule->ofpacts_len);
+    list_init(&rule->meter_list_node);
     rule->evictable = true;
     rule->eviction_group = NULL;
     list_init(&rule->expirable);
@@ -3219,7 +3416,7 @@ add_flow(struct ofproto *ofproto, struct ofconn *ofconn,
             op->group->n_running--;
             ofoperation_destroy(rule->pending);
         } else if (evict) {
-            delete_flow__(evict, group);
+            delete_flow__(evict, group, OFPRR_EVICTION);
         }
         ofopgroup_submit(group);
     }
@@ -3244,8 +3441,8 @@ exit:
  * Returns 0 on success, otherwise an OpenFlow error code. */
 static enum ofperr
 modify_flows__(struct ofproto *ofproto, struct ofconn *ofconn,
-               const struct ofputil_flow_mod *fm,
-               const struct ofp_header *request, struct list *rules)
+               struct ofputil_flow_mod *fm, const struct ofp_header *request,
+               struct list *rules)
 {
     struct ofopgroup *group;
     struct rule *rule;
@@ -3256,7 +3453,6 @@ modify_flows__(struct ofproto *ofproto, struct ofconn *ofconn,
     LIST_FOR_EACH (rule, ofproto_node, rules) {
         struct ofoperation *op;
         bool actions_changed;
-        ovs_be64 new_cookie;
 
         /* FIXME: Implement OFPFF12_RESET_COUNTS */
 
@@ -3267,19 +3463,28 @@ modify_flows__(struct ofproto *ofproto, struct ofconn *ofconn,
             continue;
         }
 
+        /* Verify actions. */
+        error = ofpacts_check(fm->ofpacts, fm->ofpacts_len, &fm->match.flow,
+                              ofproto->max_ports, rule->table_id);
+        if (error) {
+            return error;
+        }
+
         actions_changed = !ofpacts_equal(fm->ofpacts, fm->ofpacts_len,
                                          rule->ofpacts, rule->ofpacts_len);
-        new_cookie = (fm->new_cookie != htonll(UINT64_MAX)
-                      ? fm->new_cookie
-                      : rule->flow_cookie);
 
         op = ofoperation_create(group, rule, OFOPERATION_MODIFY, 0);
-        rule->flow_cookie = new_cookie;
+
+        if (fm->new_cookie != htonll(UINT64_MAX)) {
+            ofproto_rule_change_cookie(ofproto, rule, fm->new_cookie);
+        }
         if (actions_changed) {
             op->ofpacts = rule->ofpacts;
             op->ofpacts_len = rule->ofpacts_len;
+            op->meter_id = rule->meter_id;
             rule->ofpacts = xmemdup(fm->ofpacts, fm->ofpacts_len);
             rule->ofpacts_len = fm->ofpacts_len;
+            rule->meter_id = find_meter(rule->ofpacts, rule->ofpacts_len);
             rule->ofproto->ofproto_class->rule_modify_actions(rule);
         } else {
             ofoperation_complete(op, 0);
@@ -3292,8 +3497,7 @@ modify_flows__(struct ofproto *ofproto, struct ofconn *ofconn,
 
 static enum ofperr
 modify_flows_add(struct ofproto *ofproto, struct ofconn *ofconn,
-                 const struct ofputil_flow_mod *fm,
-                 const struct ofp_header *request)
+                 struct ofputil_flow_mod *fm, const struct ofp_header *request)
 {
     if (fm->cookie_mask != htonll(0) || fm->new_cookie == htonll(UINT64_MAX)) {
         return 0;
@@ -3308,7 +3512,7 @@ modify_flows_add(struct ofproto *ofproto, struct ofconn *ofconn,
  * if any. */
 static enum ofperr
 modify_flows_loose(struct ofproto *ofproto, struct ofconn *ofconn,
-                   const struct ofputil_flow_mod *fm,
+                   struct ofputil_flow_mod *fm,
                    const struct ofp_header *request)
 {
     struct list rules;
@@ -3333,7 +3537,7 @@ modify_flows_loose(struct ofproto *ofproto, struct ofconn *ofconn,
  * if any. */
 static enum ofperr
 modify_flow_strict(struct ofproto *ofproto, struct ofconn *ofconn,
-                   const struct ofputil_flow_mod *fm,
+                   struct ofputil_flow_mod *fm,
                    const struct ofp_header *request)
 {
     struct list rules;
@@ -3357,13 +3561,14 @@ modify_flow_strict(struct ofproto *ofproto, struct ofconn *ofconn,
 /* OFPFC_DELETE implementation. */
 
 static void
-delete_flow__(struct rule *rule, struct ofopgroup *group)
+delete_flow__(struct rule *rule, struct ofopgroup *group,
+              enum ofp_flow_removed_reason reason)
 {
     struct ofproto *ofproto = rule->ofproto;
 
-    ofproto_rule_send_removed(rule, OFPRR_DELETE);
+    ofproto_rule_send_removed(rule, reason);
 
-    ofoperation_create(group, rule, OFOPERATION_DELETE, OFPRR_DELETE);
+    ofoperation_create(group, rule, OFOPERATION_DELETE, reason);
     oftable_remove_rule(rule);
     ofproto->ofproto_class->rule_destruct(rule);
 }
@@ -3373,14 +3578,15 @@ delete_flow__(struct rule *rule, struct ofopgroup *group)
  * Returns 0 on success, otherwise an OpenFlow error code. */
 static enum ofperr
 delete_flows__(struct ofproto *ofproto, struct ofconn *ofconn,
-               const struct ofp_header *request, struct list *rules)
+               const struct ofp_header *request, struct list *rules,
+               enum ofp_flow_removed_reason reason)
 {
     struct rule *rule, *next;
     struct ofopgroup *group;
 
     group = ofopgroup_create(ofproto, ofconn, request, UINT32_MAX);
     LIST_FOR_EACH_SAFE (rule, next, ofproto_node, rules) {
-        delete_flow__(rule, group);
+        delete_flow__(rule, group, reason);
     }
     ofopgroup_submit(group);
 
@@ -3401,7 +3607,7 @@ delete_flows_loose(struct ofproto *ofproto, struct ofconn *ofconn,
                                 fm->out_port, &rules);
     return (error ? error
             : !list_is_empty(&rules) ? delete_flows__(ofproto, ofconn, request,
-                                                      &rules)
+                                                      &rules, OFPRR_DELETE)
             : 0);
 }
 
@@ -3419,7 +3625,8 @@ delete_flow_strict(struct ofproto *ofproto, struct ofconn *ofconn,
                                  fm->out_port, &rules);
     return (error ? error
             : list_is_singleton(&rules) ? delete_flows__(ofproto, ofconn,
-                                                         request, &rules)
+                                                         request, &rules,
+                                                         OFPRR_DELETE)
             : 0);
 }
 
@@ -3437,8 +3644,8 @@ ofproto_rule_send_removed(struct rule *rule, uint8_t reason)
     fr.cookie = rule->flow_cookie;
     fr.reason = reason;
     fr.table_id = rule->table_id;
-    calc_flow_duration__(rule->created, time_msec(),
-                         &fr.duration_sec, &fr.duration_nsec);
+    calc_duration(rule->created, time_msec(),
+                  &fr.duration_sec, &fr.duration_nsec);
     fr.idle_timeout = rule->idle_timeout;
     fr.hard_timeout = rule->hard_timeout;
     rule->ofproto->ofproto_class->rule_get_stats(rule, &fr.packet_count,
@@ -3505,10 +3712,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) {
-        error = ofpacts_check(fm.ofpacts, fm.ofpacts_len,
-                              &fm.match.flow, ofproto->max_ports);
-    }
     if (!error) {
         error = handle_flow_mod__(ofproto, ofconn, &fm, oh);
     }
@@ -3550,8 +3753,7 @@ exit:
 
 static enum ofperr
 handle_flow_mod__(struct ofproto *ofproto, struct ofconn *ofconn,
-                  const struct ofputil_flow_mod *fm,
-                  const struct ofp_header *oh)
+                  struct ofputil_flow_mod *fm, const struct ofp_header *oh)
 {
     if (ofproto->n_pending >= 50) {
         ovs_assert(!list_is_empty(&ofproto->pending));
@@ -3987,6 +4189,311 @@ handle_flow_monitor_cancel(struct ofconn *ofconn, const struct ofp_header *oh)
     return 0;
 }
 
+/* Meters implementation.
+ *
+ * Meter table entry, indexed by the OpenFlow meter_id.
+ * These are always dynamically allocated to allocate enough space for
+ * the bands.
+ * 'created' is used to compute the duration for meter stats.
+ * 'list rules' is needed so that we can delete the dependent rules when the
+ * meter table entry is deleted.
+ * 'provider_meter_id' is for the provider's private use.
+ */
+struct meter {
+    long long int created;      /* Time created. */
+    struct list rules;          /* List of "struct rule_dpif"s. */
+    ofproto_meter_id provider_meter_id;
+    uint16_t flags;             /* Meter flags. */
+    uint16_t n_bands;           /* Number of meter bands. */
+    struct ofputil_meter_band *bands;
+};
+
+/*
+ * This is used in instruction validation at flow set-up time,
+ * as flows may not use non-existing meters.
+ * This is also used by ofproto-providers to translate OpenFlow meter_ids
+ * in METER instructions to the corresponding provider meter IDs.
+ * Return value of UINT32_MAX signifies an invalid meter.
+ */
+uint32_t
+ofproto_get_provider_meter_id(const struct ofproto * ofproto,
+                              uint32_t of_meter_id)
+{
+    if (of_meter_id && of_meter_id <= ofproto->meter_features.max_meters) {
+        const struct meter *meter = ofproto->meters[of_meter_id];
+        if (meter) {
+            return meter->provider_meter_id.uint32;
+        }
+    }
+    return UINT32_MAX;
+}
+
+static void
+meter_update(struct meter *meter, const struct ofputil_meter_config *config)
+{
+    free(meter->bands);
+
+    meter->flags = config->flags;
+    meter->n_bands = config->n_bands;
+    meter->bands = xmemdup(config->bands,
+                           config->n_bands * sizeof *meter->bands);
+}
+
+static struct meter *
+meter_create(const struct ofputil_meter_config *config,
+             ofproto_meter_id provider_meter_id)
+{
+    struct meter *meter;
+
+    meter = xzalloc(sizeof *meter);
+    meter->provider_meter_id = provider_meter_id;
+    meter->created = time_msec();
+    list_init(&meter->rules);
+
+    meter_update(meter, config);
+
+    return meter;
+}
+
+static enum ofperr
+handle_add_meter(struct ofproto *ofproto, struct ofputil_meter_mod *mm)
+{
+    ofproto_meter_id provider_meter_id = { UINT32_MAX };
+    struct meter **meterp = &ofproto->meters[mm->meter.meter_id];
+    enum ofperr error;
+
+    if (*meterp) {
+        return OFPERR_OFPMMFC_METER_EXISTS;
+    }
+
+    error = ofproto->ofproto_class->meter_set(ofproto, &provider_meter_id,
+                                              &mm->meter);
+    if (!error) {
+        ovs_assert(provider_meter_id.uint32 != UINT32_MAX);
+        *meterp = meter_create(&mm->meter, provider_meter_id);
+    }
+    return 0;
+}
+
+static enum ofperr
+handle_modify_meter(struct ofproto *ofproto, struct ofputil_meter_mod *mm)
+{
+    struct meter *meter = ofproto->meters[mm->meter.meter_id];
+    enum ofperr error;
+
+    if (!meter) {
+        return OFPERR_OFPMMFC_UNKNOWN_METER;
+    }
+
+    error = ofproto->ofproto_class->meter_set(ofproto,
+                                              &meter->provider_meter_id,
+                                              &mm->meter);
+    ovs_assert(meter->provider_meter_id.uint32 != UINT32_MAX);
+    if (!error) {
+        meter_update(meter, &mm->meter);
+    }
+    return error;
+}
+
+static enum ofperr
+handle_delete_meter(struct ofconn *ofconn, const struct ofp_header *oh,
+                    struct ofputil_meter_mod *mm)
+{
+    struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
+    uint32_t meter_id = mm->meter.meter_id;
+    uint32_t first, last;
+    struct list rules;
+
+    if (meter_id == OFPM13_ALL) {
+        first = 1;
+        last = ofproto->meter_features.max_meters;
+    } else {
+        if (!meter_id || meter_id > ofproto->meter_features.max_meters) {
+            return 0;
+        }
+        first = last = meter_id;
+    }
+
+    /* First delete the rules that use this meter.  If any of those rules are
+     * currently being modified, postpone the whole operation until later. */
+    list_init(&rules);
+    for (meter_id = first; meter_id <= last; ++meter_id) {
+        struct meter *meter = ofproto->meters[meter_id];
+        if (meter && !list_is_empty(&meter->rules)) {
+            struct rule *rule;
+
+            LIST_FOR_EACH (rule, meter_list_node, &meter->rules) {
+                if (rule->pending) {
+                    return OFPROTO_POSTPONE;
+                }
+                list_push_back(&rules, &rule->ofproto_node);
+            }
+        }
+    }
+    if (!list_is_empty(&rules)) {
+        delete_flows__(ofproto, ofconn, oh, &rules, OFPRR_METER_DELETE);
+    }
+
+    /* Delete the meters. */
+    for (meter_id = first; meter_id <= last; ++meter_id) {
+        struct meter *meter = ofproto->meters[meter_id];
+        if (meter) {
+            ofproto->meters[meter_id] = NULL;
+            ofproto->ofproto_class->meter_del(ofproto,
+                                              meter->provider_meter_id);
+            free(meter->bands);
+            free(meter);
+        }
+    }
+
+    return 0;
+}
+
+static enum ofperr
+handle_meter_mod(struct ofconn *ofconn, const struct ofp_header *oh)
+{
+    struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
+    struct ofputil_meter_mod mm;
+    uint64_t bands_stub[256 / 8];
+    struct ofpbuf bands;
+    uint32_t meter_id;
+    enum ofperr error;
+
+    error = reject_slave_controller(ofconn);
+    if (error) {
+        return error;
+    }
+
+    ofpbuf_use_stub(&bands, bands_stub, sizeof bands_stub);
+
+    error = ofputil_decode_meter_mod(oh, &mm, &bands);
+    if (error) {
+        goto exit_free_bands;
+    }
+
+    meter_id = mm.meter.meter_id;
+
+    if (mm.command != OFPMC13_DELETE) {
+        /* Fails also when meters are not implemented by the provider. */
+        if (!meter_id || meter_id > ofproto->meter_features.max_meters) {
+            error = OFPERR_OFPMMFC_INVALID_METER;
+            goto exit_free_bands;
+        }
+        if (mm.meter.n_bands > ofproto->meter_features.max_bands) {
+            error = OFPERR_OFPMMFC_OUT_OF_BANDS;
+            goto exit_free_bands;
+        }
+    }
+
+    switch (mm.command) {
+    case OFPMC13_ADD:
+        error = handle_add_meter(ofproto, &mm);
+        break;
+
+    case OFPMC13_MODIFY:
+        error = handle_modify_meter(ofproto, &mm);
+        break;
+
+    case OFPMC13_DELETE:
+        error = handle_delete_meter(ofconn, oh, &mm);
+        break;
+
+    default:
+        error = OFPERR_OFPMMFC_BAD_COMMAND;
+        break;
+    }
+
+exit_free_bands:
+    ofpbuf_uninit(&bands);
+    return error;
+}
+
+static enum ofperr
+handle_meter_features_request(struct ofconn *ofconn,
+                              const struct ofp_header *request)
+{
+    struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
+    struct ofputil_meter_features features;
+    struct ofpbuf *b;
+
+    if (ofproto->ofproto_class->meter_get_features) {
+        ofproto->ofproto_class->meter_get_features(ofproto, &features);
+    } else {
+        memset(&features, 0, sizeof features);
+    }
+    b = ofputil_encode_meter_features_reply(&features, request);
+
+    ofconn_send_reply(ofconn, b);
+    return 0;
+}
+
+static enum ofperr
+handle_meter_request(struct ofconn *ofconn, const struct ofp_header *request,
+                     enum ofptype type)
+{
+    struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
+    struct list replies;
+    uint64_t bands_stub[256 / 8];
+    struct ofpbuf bands;
+    uint32_t meter_id, first, last;
+
+    ofputil_decode_meter_request(request, &meter_id);
+
+    if (meter_id == OFPM13_ALL) {
+        first = 1;
+        last = ofproto->meter_features.max_meters;
+    } else {
+        if (!meter_id || meter_id > ofproto->meter_features.max_meters ||
+            !ofproto->meters[meter_id]) {
+            return OFPERR_OFPMMFC_UNKNOWN_METER;
+        }
+        first = last = meter_id;
+    }
+
+    ofpbuf_use_stub(&bands, bands_stub, sizeof bands_stub);
+    ofpmp_init(&replies, request);
+
+    for (meter_id = first; meter_id <= last; ++meter_id) {
+        struct meter *meter = ofproto->meters[meter_id];
+        if (!meter) {
+            continue; /* Skip non-existing meters. */
+        }
+        if (type == OFPTYPE_METER_REQUEST) {
+            struct ofputil_meter_stats stats;
+
+            stats.meter_id = meter_id;
+
+            /* Provider sets the packet and byte counts, we do the rest. */
+            stats.flow_count = list_size(&meter->rules);
+            calc_duration(meter->created, time_msec(),
+                          &stats.duration_sec, &stats.duration_nsec);
+            stats.n_bands = meter->n_bands;
+            ofpbuf_clear(&bands);
+            stats.bands
+                = ofpbuf_put_uninit(&bands,
+                                    meter->n_bands * sizeof *stats.bands);
+
+            if (!ofproto->ofproto_class->meter_get(ofproto,
+                                                   meter->provider_meter_id,
+                                                   &stats)) {
+                ofputil_append_meter_stats(&replies, &stats);
+            }
+        } else { /* type == OFPTYPE_METER_CONFIG_REQUEST */
+            struct ofputil_meter_config config;
+
+            config.meter_id = meter_id;
+            config.flags = meter->flags;
+            config.n_bands = meter->n_bands;
+            config.bands = meter->bands;
+            ofputil_append_meter_config(&replies, &config);
+        }
+    }
+
+    ofconn_send_replies(ofconn, &replies);
+    ofpbuf_uninit(&bands);
+    return 0;
+}
+
 static enum ofperr
 handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg)
 {
@@ -4022,6 +4529,9 @@ handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg)
     case OFPTYPE_FLOW_MOD:
         return handle_flow_mod(ofconn, oh);
 
+    case OFPTYPE_METER_MOD:
+        return handle_meter_mod(ofconn, oh);
+
     case OFPTYPE_BARRIER_REQUEST:
         return handle_barrier_request(ofconn, oh);
 
@@ -4080,16 +4590,19 @@ handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg)
     case OFPTYPE_FLOW_MONITOR_STATS_REQUEST:
         return handle_flow_monitor_request(ofconn, oh);
 
+    case OFPTYPE_METER_REQUEST:
+    case OFPTYPE_METER_CONFIG_REQUEST:
+        return handle_meter_request(ofconn, oh, type);
+
+    case OFPTYPE_METER_FEATURES_REQUEST:
+        return handle_meter_features_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;
 
@@ -4127,7 +4640,7 @@ handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg)
 }
 
 static bool
-handle_openflow(struct ofconn *ofconn, struct ofpbuf *ofp_msg)
+handle_openflow(struct ofconn *ofconn, const struct ofpbuf *ofp_msg)
 {
     int error = handle_openflow__(ofconn, ofp_msg);
     if (error && error != OFPROTO_POSTPONE) {
@@ -4227,7 +4740,7 @@ ofopgroup_complete(struct ofopgroup *group)
         LIST_FOR_EACH (op, group_node, &group->ops) {
             if (op->type != OFOPERATION_DELETE) {
                 struct ofpbuf *packet;
-                uint16_t in_port;
+                ofp_port_t in_port;
 
                 error = ofconn_pktbuf_retrieve(group->ofconn, group->buffer_id,
                                                &packet, &in_port);
@@ -4313,7 +4826,7 @@ ofopgroup_complete(struct ofopgroup *group)
             if (!op->error) {
                 rule->modified = time_msec();
             } else {
-                rule->flow_cookie = op->flow_cookie;
+                ofproto_rule_change_cookie(ofproto, rule, op->flow_cookie);
                 if (op->ofpacts) {
                     free(rule->ofpacts);
                     rule->ofpacts = op->ofpacts;
@@ -4468,7 +4981,7 @@ pick_datapath_id(const struct ofproto *ofproto)
         }
         VLOG_WARN("%s: could not get MAC address for %s (%s)",
                   ofproto->name, netdev_get_name(port->netdev),
-                  strerror(error));
+                  ovs_strerror(error));
     }
     return ofproto->fallback_dpid;
 }
@@ -4842,10 +5355,17 @@ oftable_remove_rule(struct rule *rule)
     struct oftable *table = &ofproto->tables[rule->table_id];
 
     classifier_remove(&table->cls, &rule->cr);
+    if (rule->meter_id) {
+        list_remove(&rule->meter_list_node);
+    }
+    cookies_remove(ofproto, rule);
     eviction_group_remove_rule(rule);
     if (!list_is_empty(&rule->expirable)) {
         list_remove(&rule->expirable);
     }
+    if (!list_is_empty(&rule->meter_list_node)) {
+        list_remove(&rule->meter_list_node);
+    }
 }
 
 /* Inserts 'rule' into its oftable.  Removes any existing rule from 'rule''s
@@ -4862,9 +5382,18 @@ oftable_replace_rule(struct rule *rule)
     if (may_expire) {
         list_insert(&ofproto->expirable, &rule->expirable);
     }
-
+    cookies_insert(ofproto, rule);
+    if (rule->meter_id) {
+        struct meter *meter = ofproto->meters[rule->meter_id];
+        list_insert(&meter->rules, &rule->meter_list_node);
+    }
     victim = rule_from_cls_rule(classifier_replace(&table->cls, &rule->cr));
     if (victim) {
+        if (victim->meter_id) {
+            list_remove(&victim->meter_list_node);
+        }
+        cookies_remove(ofproto, victim);
+
         if (!list_is_empty(&victim->expirable)) {
             list_remove(&victim->expirable);
         }
@@ -4980,8 +5509,8 @@ ofproto_has_vlan_usage_changed(const struct ofproto *ofproto)
  * device as a VLAN splinter for VLAN ID 'vid'.  If 'realdev_ofp_port' is zero,
  * then the VLAN device is un-enslaved. */
 int
-ofproto_port_set_realdev(struct ofproto *ofproto, uint16_t vlandev_ofp_port,
-                         uint16_t realdev_ofp_port, int vid)
+ofproto_port_set_realdev(struct ofproto *ofproto, ofp_port_t vlandev_ofp_port,
+                         ofp_port_t realdev_ofp_port, int vid)
 {
     struct ofport *ofport;
     int error;
@@ -5007,7 +5536,7 @@ ofproto_port_set_realdev(struct ofproto *ofproto, uint16_t vlandev_ofp_port,
     if (error) {
         VLOG_WARN("%s: setting realdev on port %"PRIu16" (%s) failed (%s)",
                   ofproto->name, vlandev_ofp_port,
-                  netdev_get_name(ofport->netdev), strerror(error));
+                  netdev_get_name(ofport->netdev), ovs_strerror(error));
     }
     return error;
 }
index acb1790..792df89 100644 (file)
@@ -158,7 +158,7 @@ void ofproto_parse_name(const char *name, char **dp_name, char **dp_type);
 struct iface_hint {
     char *br_name;              /* Name of owning bridge. */
     char *br_type;              /* Type of owning bridge. */
-    uint16_t ofp_port;          /* OpenFlow port number. */
+    ofp_port_t ofp_port;        /* OpenFlow port number. */
 };
 
 void ofproto_init(const struct shash *iface_hints);
@@ -185,7 +185,7 @@ void ofproto_get_memory_usage(const struct ofproto *, struct simap *);
 struct ofproto_port {
     char *name;                 /* Network device name, e.g. "eth0". */
     char *type;                 /* Network device type, e.g. "system". */
-    uint16_t ofp_port;          /* OpenFlow port number. */
+    ofp_port_t ofp_port;        /* OpenFlow port number. */
 };
 void ofproto_port_clone(struct ofproto_port *, const struct ofproto_port *);
 void ofproto_port_destroy(struct ofproto_port *);
@@ -216,10 +216,17 @@ int ofproto_port_dump_done(struct ofproto_port_dump *);
 #define OFPROTO_FLOW_EVICTION_THRESHOLD_DEFAULT  2500
 #define OFPROTO_FLOW_EVICTION_THRESHOLD_MIN 100
 
+/* How flow misses should be handled in ofproto-dpif */
+enum ofproto_flow_miss_model {
+    OFPROTO_HANDLE_MISS_AUTO,           /* Based on flow eviction threshold. */
+    OFPROTO_HANDLE_MISS_WITH_FACETS,    /* Always create facets. */
+    OFPROTO_HANDLE_MISS_WITHOUT_FACETS  /* Always handle without facets.*/
+};
+
 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_add(struct ofproto *, struct netdev *, ofp_port_t *ofp_portp);
+int ofproto_port_del(struct ofproto *, ofp_port_t ofp_port);
 int ofproto_port_get_stats(const struct ofport *, struct netdev_stats *stats);
 
 int ofproto_port_query_by_name(const struct ofproto *, const char *devname,
@@ -236,7 +243,8 @@ void ofproto_reconnect_controllers(struct ofproto *);
 void ofproto_set_extra_in_band_remotes(struct ofproto *,
                                        const struct sockaddr_in *, size_t n);
 void ofproto_set_in_band_queue(struct ofproto *, int queue_id);
-void ofproto_set_flow_eviction_threshold(struct ofproto *, unsigned threshold);
+void ofproto_set_flow_eviction_threshold(unsigned threshold);
+void ofproto_set_flow_miss_model(unsigned model);
 void ofproto_set_forward_bpdu(struct ofproto *, bool forward_bpdu);
 void ofproto_set_mac_table_config(struct ofproto *, unsigned idle_time,
                                   size_t max_entries);
@@ -249,25 +257,27 @@ int ofproto_set_ipfix(struct ofproto *,
                       const struct ofproto_ipfix_bridge_exporter_options *,
                       const struct ofproto_ipfix_flow_exporter_options *,
                       size_t);
+void ofproto_set_flow_restore_wait(bool flow_restore_wait_db);
+bool ofproto_get_flow_restore_wait(void);
 int ofproto_set_stp(struct ofproto *, const struct ofproto_stp_settings *);
 int ofproto_get_stp_status(struct ofproto *, struct ofproto_stp_status *);
 
 /* Configuration of ports. */
-void ofproto_port_unregister(struct ofproto *, uint16_t ofp_port);
+void ofproto_port_unregister(struct ofproto *, ofp_port_t ofp_port);
 
-void ofproto_port_clear_cfm(struct ofproto *, uint16_t ofp_port);
-void ofproto_port_set_cfm(struct ofproto *, uint16_t ofp_port,
+void ofproto_port_clear_cfm(struct ofproto *, ofp_port_t ofp_port);
+void ofproto_port_set_cfm(struct ofproto *, ofp_port_t ofp_port,
                           const struct cfm_settings *);
-void ofproto_port_set_bfd(struct ofproto *, uint16_t ofp_port,
+void ofproto_port_set_bfd(struct ofproto *, ofp_port_t ofp_port,
                           const struct smap *cfg);
-int ofproto_port_get_bfd_status(struct ofproto *, uint16_t ofp_port,
+int ofproto_port_get_bfd_status(struct ofproto *, ofp_port_t ofp_port,
                                 struct smap *);
-int ofproto_port_is_lacp_current(struct ofproto *, uint16_t ofp_port);
-int ofproto_port_set_stp(struct ofproto *, uint16_t ofp_port,
+int ofproto_port_is_lacp_current(struct ofproto *, ofp_port_t ofp_port);
+int ofproto_port_set_stp(struct ofproto *, ofp_port_t ofp_port,
                          const struct ofproto_port_stp_settings *);
-int ofproto_port_get_stp_status(struct ofproto *, uint16_t ofp_port,
+int ofproto_port_get_stp_status(struct ofproto *, ofp_port_t ofp_port,
                                 struct ofproto_port_stp_status *);
-int ofproto_port_set_queues(struct ofproto *, uint16_t ofp_port,
+int ofproto_port_set_queues(struct ofproto *, ofp_port_t ofp_port,
                             const struct ofproto_port_queue *,
                             size_t n_queues);
 
@@ -296,7 +306,7 @@ enum port_vlan_mode {
 struct ofproto_bundle_settings {
     char *name;                 /* For use in log messages. */
 
-    uint16_t *slaves;           /* OpenFlow port numbers for slaves. */
+    ofp_port_t *slaves;         /* OpenFlow port numbers for slaves. */
     size_t n_slaves;
 
     enum port_vlan_mode vlan_mode; /* Selects mode for vlan and trunks */
@@ -315,7 +325,7 @@ struct ofproto_bundle_settings {
      * drivers in old versions of Linux that do not properly support VLANs when
      * VLAN devices are not used.  When broken device drivers are no longer in
      * widespread use, we will delete these interfaces. */
-    uint16_t realdev_ofp_port;  /* OpenFlow port number of real device. */
+    ofp_port_t realdev_ofp_port;/* OpenFlow port number of real device. */
 };
 
 int ofproto_bundle_register(struct ofproto *, void *aux,
@@ -404,7 +414,8 @@ struct ofproto_cfm_status {
     size_t n_rmps;
 };
 
-bool ofproto_port_get_cfm_status(const struct ofproto *, uint16_t ofp_port,
+bool ofproto_port_get_cfm_status(const struct ofproto *,
+                                 ofp_port_t ofp_port,
                                  struct ofproto_cfm_status *);
 \f
 /* Linux VLAN device support (e.g. "eth0.10" for VLAN 10.)
@@ -416,8 +427,11 @@ bool ofproto_port_get_cfm_status(const struct ofproto *, uint16_t ofp_port,
 
 void ofproto_get_vlan_usage(struct ofproto *, unsigned long int *vlan_bitmap);
 bool ofproto_has_vlan_usage_changed(const struct ofproto *);
-int ofproto_port_set_realdev(struct ofproto *, uint16_t vlandev_ofp_port,
-                             uint16_t realdev_ofp_port, int vid);
+int ofproto_port_set_realdev(struct ofproto *, ofp_port_t vlandev_ofp_port,
+                             ofp_port_t realdev_ofp_port, int vid);
+
+uint32_t ofproto_get_provider_meter_id(const struct ofproto *,
+                                       uint32_t of_meter_id);
 
 #ifdef  __cplusplus
 }
index 57e8e23..91e9c41 100644 (file)
@@ -21,6 +21,7 @@
 #include <arpa/inet.h>
 #include <stdint.h>
 #include <stdlib.h>
+#include "flow.h"
 #include "hash.h"
 #include "hmap.h"
 #include "ofpbuf.h"
@@ -35,7 +36,7 @@
 
 struct pinqueue {
     struct hmap_node node;      /* In struct pinsched's 'queues' hmap. */
-    uint16_t port_no;           /* Port number. */
+    ofp_port_t port_no;           /* Port number. */
     struct list packets;        /* Contains "struct ofpbuf"s. */
     int n;                      /* Number of packets in 'packets'. */
 };
@@ -101,9 +102,9 @@ pinqueue_destroy(struct pinsched *ps, struct pinqueue *q)
 }
 
 static struct pinqueue *
-pinqueue_get(struct pinsched *ps, uint16_t port_no)
+pinqueue_get(struct pinsched *ps, ofp_port_t port_no)
 {
-    uint32_t hash = hash_int(port_no, 0);
+    uint32_t hash = hash_ofp_port(port_no);
     struct pinqueue *q;
 
     HMAP_FOR_EACH_IN_BUCKET (q, node, hash, &ps->queues) {
@@ -184,7 +185,7 @@ get_token(struct pinsched *ps)
 }
 
 void
-pinsched_send(struct pinsched *ps, uint16_t port_no,
+pinsched_send(struct pinsched *ps, ofp_port_t port_no,
               struct ofpbuf *packet, pinsched_tx_cb *cb, void *aux)
 {
     if (!ps) {
index 061cb01..06b22f4 100644 (file)
@@ -18,6 +18,7 @@
 #define PINSCHED_H_H 1
 
 #include <stdint.h>
+#include "flow.h"
 
 struct ofpbuf;
 
@@ -27,7 +28,7 @@ void pinsched_get_limits(const struct pinsched *,
                          int *rate_limit, int *burst_limit);
 void pinsched_set_limits(struct pinsched *, int rate_limit, int burst_limit);
 void pinsched_destroy(struct pinsched *);
-void pinsched_send(struct pinsched *, uint16_t port_no, struct ofpbuf *,
+void pinsched_send(struct pinsched *, ofp_port_t port_no, struct ofpbuf *,
                    pinsched_tx_cb *, void *aux);
 void pinsched_run(struct pinsched *, pinsched_tx_cb *, void *aux);
 void pinsched_wait(struct pinsched *);
index 902b19d..65fcef6 100644 (file)
@@ -51,7 +51,7 @@ struct packet {
     struct ofpbuf *buffer;
     uint32_t cookie;
     long long int timeout;
-    uint16_t in_port;
+    ofp_port_t in_port;
 };
 
 struct pktbuf {
@@ -104,7 +104,7 @@ make_id(unsigned int buffer_idx, unsigned int cookie)
  * The caller retains ownership of 'buffer'. */
 uint32_t
 pktbuf_save(struct pktbuf *pb, const void *buffer, size_t buffer_size,
-            uint16_t in_port)
+            ofp_port_t in_port)
 {
     struct packet *p = &pb->packets[pb->buffer_idx];
     pb->buffer_idx = (pb->buffer_idx + 1) & PKTBUF_MASK;
@@ -158,10 +158,10 @@ pktbuf_get_null(void)
  * 0 if successful, otherwise an OpenFlow error code.
  *
  * On success, ordinarily stores the buffered packet in '*bufferp' and the
- * datapath port number on which the packet was received in '*in_port'.  The
+ * OpenFlow port number on which the packet was received in '*in_port'.  The
  * caller becomes responsible for freeing the buffer.  However, if 'id'
  * identifies a "null" packet buffer (created with pktbuf_get_null()), stores
- * NULL in '*bufferp' and UINT16_max in '*in_port'.
+ * NULL in '*bufferp' and OFPP_NONE in '*in_port'.
  *
  * 'in_port' may be NULL if the input port is not of interest.
  *
@@ -171,7 +171,7 @@ pktbuf_get_null(void)
  * On failure, stores NULL in in '*bufferp' and UINT16_MAX in '*in_port'. */
 enum ofperr
 pktbuf_retrieve(struct pktbuf *pb, uint32_t id, struct ofpbuf **bufferp,
-                uint16_t *in_port)
+                ofp_port_t *in_port)
 {
     static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 20);
     struct packet *p;
@@ -218,7 +218,7 @@ pktbuf_retrieve(struct pktbuf *pb, uint32_t id, struct ofpbuf **bufferp,
 error:
     *bufferp = NULL;
     if (in_port) {
-        *in_port = UINT16_MAX;
+        *in_port = OFPP_NONE;
     }
     return error;
 }
index ec99aea..eb1b1ff 100644 (file)
@@ -30,10 +30,10 @@ int pktbuf_capacity(void);
 struct pktbuf *pktbuf_create(void);
 void pktbuf_destroy(struct pktbuf *);
 uint32_t pktbuf_save(struct pktbuf *, const void *buffer, size_t buffer_size,
-                     uint16_t in_port);
+                     ofp_port_t in_port);
 uint32_t pktbuf_get_null(void);
 enum ofperr pktbuf_retrieve(struct pktbuf *, uint32_t id,
-                            struct ofpbuf **bufferp, uint16_t *in_port);
+                            struct ofpbuf **bufferp, ofp_port_t *in_port);
 void pktbuf_discard(struct pktbuf *, uint32_t id);
 
 unsigned int pktbuf_count_packets(const struct pktbuf *);
index ffc4057..4b7f304 100644 (file)
 
 #include <errno.h>
 
-#include "ofproto/ofproto-provider.h"
 #include "byte-order.h"
 #include "dynamic-string.h"
 #include "hash.h"
 #include "hmap.h"
-#include "netdev-vport.h"
+#include "netdev.h"
 #include "odp-util.h"
 #include "packets.h"
 #include "smap.h"
 #include "tunnel.h"
 #include "vlog.h"
 
-/* XXX:
- *
- * Disallow netdevs with names like "gre64_system" to prevent collisions. */
-
 VLOG_DEFINE_THIS_MODULE(tunnel);
 
 struct tnl_match {
     ovs_be64 in_key;
     ovs_be32 ip_src;
     ovs_be32 ip_dst;
-    uint32_t odp_port;
+    odp_port_t odp_port;
     uint32_t skb_mark;
     bool in_key_flow;
     bool ip_src_flow;
@@ -48,46 +43,47 @@ struct tnl_match {
 };
 
 struct tnl_port {
+    struct hmap_node ofport_node;
     struct hmap_node match_node;
 
-    const struct ofport *ofport;
+    const struct ofport_dpif *ofport;
     unsigned int netdev_seq;
+    struct netdev *netdev;
+
     struct tnl_match match;
 };
 
 static struct hmap tnl_match_map = HMAP_INITIALIZER(&tnl_match_map);
-
-/* Returned to callers when their ofport will never be used to receive or send
- * tunnel traffic. Alternatively, we could ask the caller to delete their
- * ofport, but this would be unclean in the reconfguration case.  For the first
- * time, an ofproto provider would have to call ofproto_port_del() on itself.*/
-static struct tnl_port void_tnl_port;
+static struct hmap ofport_map = HMAP_INITIALIZER(&ofport_map);
 
 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
 static struct vlog_rate_limit dbg_rl = VLOG_RATE_LIMIT_INIT(60, 60);
 
 static struct tnl_port *tnl_find(struct tnl_match *);
 static struct tnl_port *tnl_find_exact(struct tnl_match *);
+static struct tnl_port *tnl_find_ofport(const struct ofport_dpif *);
+
 static uint32_t tnl_hash(struct tnl_match *);
 static void tnl_match_fmt(const struct tnl_match *, struct ds *);
 static char *tnl_port_fmt(const struct tnl_port *);
 static void tnl_port_mod_log(const struct tnl_port *, const char *action);
 static const char *tnl_port_get_name(const struct tnl_port *);
 
-static struct tnl_port *
-tnl_port_add__(const struct ofport *ofport, uint32_t odp_port,
-               bool warn)
+static bool
+tnl_port_add__(const struct ofport_dpif *ofport, const struct netdev *netdev,
+               odp_port_t odp_port, bool warn)
 {
     const struct netdev_tunnel_config *cfg;
     struct tnl_port *existing_port;
     struct tnl_port *tnl_port;
 
-    cfg = netdev_get_tunnel_config(ofport->netdev);
+    cfg = netdev_get_tunnel_config(netdev);
     ovs_assert(cfg);
 
     tnl_port = xzalloc(sizeof *tnl_port);
     tnl_port->ofport = ofport;
-    tnl_port->netdev_seq = netdev_change_seq(tnl_port->ofport->netdev);
+    tnl_port->netdev = netdev_ref(netdev);
+    tnl_port->netdev_seq = netdev_change_seq(tnl_port->netdev);
 
     tnl_port->match.in_key = cfg->in_key;
     tnl_port->match.ip_src = cfg->ip_src;
@@ -109,56 +105,60 @@ tnl_port_add__(const struct ofport *ofport, uint32_t odp_port,
             ds_destroy(&ds);
             free(tnl_port);
         }
-        return &void_tnl_port;
+        return false;
     }
 
+    hmap_insert(&ofport_map, &tnl_port->ofport_node, hash_pointer(ofport, 0));
     hmap_insert(&tnl_match_map, &tnl_port->match_node,
                 tnl_hash(&tnl_port->match));
     tnl_port_mod_log(tnl_port, "adding");
-    return tnl_port;
+    return true;
 }
 
 /* Adds 'ofport' to the module with datapath port number 'odp_port'. 'ofport's
  * must be added before they can be used by the module. 'ofport' must be a
  * tunnel. */
-struct tnl_port *
-tnl_port_add(const struct ofport *ofport, uint32_t odp_port)
+void
+tnl_port_add(const struct ofport_dpif *ofport, const struct netdev *netdev,
+             odp_port_t odp_port)
 {
-    return tnl_port_add__(ofport, odp_port, true);
+    tnl_port_add__(ofport, netdev, odp_port, true);
 }
 
-/* Checks if the tnl_port pointed to by 'tnl_portp' needs reconfiguration due
- * to changes in its netdev_tunnel_config.  If it does, updates 'tnl_portp' to
- * point to a new tnl_port and returns true.  Otherwise, returns false.
- * 'ofport' and 'odp_port' should be the same as would be passed to
+/* Checks if the tunnel represented by 'ofport' reconfiguration due to changes
+ * in its netdev_tunnel_config.  If it does, returns true. Otherwise, returns
+ * false.  'ofport' and 'odp_port' should be the same as would be passed to
  * tnl_port_add(). */
 bool
-tnl_port_reconfigure(const struct ofport *ofport, uint32_t odp_port,
-                     struct tnl_port **tnl_portp)
+tnl_port_reconfigure(const struct ofport_dpif *ofport,
+                     const struct netdev *netdev, odp_port_t odp_port)
 {
-    struct tnl_port *tnl_port = *tnl_portp;
+    struct tnl_port *tnl_port = tnl_find_ofport(ofport);
 
-    if (tnl_port == &void_tnl_port) {
-        *tnl_portp = tnl_port_add__(ofport, odp_port, false);
-        return *tnl_portp != &void_tnl_port;
-    } else if (tnl_port->ofport != ofport
+    if (!tnl_port) {
+        return tnl_port_add__(ofport, netdev, odp_port, false);
+    } else if (tnl_port->netdev != netdev
                || tnl_port->match.odp_port != odp_port
-               || tnl_port->netdev_seq != netdev_change_seq(ofport->netdev)) {
+               || tnl_port->netdev_seq != netdev_change_seq(netdev)) {
         VLOG_DBG("reconfiguring %s", tnl_port_get_name(tnl_port));
-        tnl_port_del(tnl_port);
-        *tnl_portp = tnl_port_add(ofport, odp_port);
+        tnl_port_del(ofport);
+        tnl_port_add(ofport, netdev, odp_port);
         return true;
     }
     return false;
 }
 
-/* Removes 'tnl_port' from the module. */
+/* Removes 'ofport' from the module. */
 void
-tnl_port_del(struct tnl_port *tnl_port)
+tnl_port_del(const struct ofport_dpif *ofport)
 {
-    if (tnl_port && tnl_port != &void_tnl_port) {
+    struct tnl_port *tnl_port = ofport ? tnl_find_ofport(ofport) : NULL;
+
+    if (tnl_port) {
         tnl_port_mod_log(tnl_port, "removing");
         hmap_remove(&tnl_match_map, &tnl_port->match_node);
+        hmap_remove(&ofport_map, &tnl_port->ofport_node);
+        netdev_close(tnl_port->netdev);
         free(tnl_port);
     }
 }
@@ -169,7 +169,7 @@ tnl_port_del(struct tnl_port *tnl_port)
  *
  * Callers should verify that 'flow' needs to be received by calling
  * tnl_port_should_receive() before this function. */
-const struct ofport *
+const struct ofport_dpif *
 tnl_port_receive(const struct flow *flow)
 {
     char *pre_flow_str = NULL;
@@ -177,7 +177,7 @@ tnl_port_receive(const struct flow *flow)
     struct tnl_match match;
 
     memset(&match, 0, sizeof match);
-    match.odp_port = flow->in_port;
+    match.odp_port = flow->in_port.odp_port;
     match.ip_src = flow->tunnel.ip_dst;
     match.ip_dst = flow->tunnel.ip_src;
     match.in_key = flow->tunnel.tun_id;
@@ -214,19 +214,21 @@ tnl_port_receive(const struct flow *flow)
 
 /* Given that 'flow' should be output to the ofport corresponding to
  * 'tnl_port', updates 'flow''s tunnel headers and returns the actual datapath
- * port that the output should happen on.  May return OVSP_NONE if the output
+ * port that the output should happen on.  May return ODPP_NONE if the output
  * shouldn't occur. */
-uint32_t
-tnl_port_send(const struct tnl_port *tnl_port, struct flow *flow)
+odp_port_t
+tnl_port_send(const struct ofport_dpif *ofport, struct flow *flow,
+              struct flow_wildcards *wc)
 {
+    struct tnl_port *tnl_port = tnl_find_ofport(ofport);
     const struct netdev_tunnel_config *cfg;
     char *pre_flow_str = NULL;
 
-    if (tnl_port == &void_tnl_port) {
-        return OVSP_NONE;
+    if (!tnl_port) {
+        return ODPP_NONE;
     }
 
-    cfg = netdev_get_tunnel_config(tnl_port->ofport->netdev);
+    cfg = netdev_get_tunnel_config(tnl_port->netdev);
     ovs_assert(cfg);
 
     if (!VLOG_DROP_DBG(&dbg_rl)) {
@@ -246,17 +248,24 @@ tnl_port_send(const struct tnl_port *tnl_port, struct flow *flow)
     }
 
     if (cfg->ttl_inherit && is_ip_any(flow)) {
+        wc->masks.nw_ttl = 0xff;
         flow->tunnel.ip_ttl = flow->nw_ttl;
     } else {
         flow->tunnel.ip_ttl = cfg->ttl;
     }
 
     if (cfg->tos_inherit && is_ip_any(flow)) {
+        wc->masks.nw_tos = 0xff;
         flow->tunnel.ip_tos = flow->nw_tos & IP_DSCP_MASK;
     } else {
         flow->tunnel.ip_tos = cfg->tos;
     }
 
+    /* ECN fields are always inherited. */
+    if (is_ip_any(flow)) {
+        wc->masks.nw_tos |= IP_ECN_MASK;
+    }
+
     if ((flow->nw_tos & IP_ECN_MASK) == IP_ECN_CE) {
         flow->tunnel.ip_tos |= IP_ECN_ECT_0;
     } else {
@@ -290,6 +299,20 @@ tnl_hash(struct tnl_match *match)
     return hash_words((uint32_t *) match, sizeof *match / sizeof(uint32_t), 0);
 }
 
+static struct tnl_port *
+tnl_find_ofport(const struct ofport_dpif *ofport)
+{
+    struct tnl_port *tnl_port;
+
+    HMAP_FOR_EACH_IN_BUCKET (tnl_port, ofport_node, hash_pointer(ofport, 0),
+                             &ofport_map) {
+        if (tnl_port->ofport == ofport) {
+            return tnl_port;
+        }
+    }
+    return NULL;
+}
+
 static struct tnl_port *
 tnl_find_exact(struct tnl_match *match)
 {
@@ -397,12 +420,12 @@ static char *
 tnl_port_fmt(const struct tnl_port *tnl_port)
 {
     const struct netdev_tunnel_config *cfg =
-        netdev_get_tunnel_config(tnl_port->ofport->netdev);
+        netdev_get_tunnel_config(tnl_port->netdev);
     struct ds ds = DS_EMPTY_INITIALIZER;
 
     ds_put_format(&ds, "port %"PRIu32": %s (%s: ", tnl_port->match.odp_port,
                   tnl_port_get_name(tnl_port),
-                  netdev_get_type(tnl_port->ofport->netdev));
+                  netdev_get_type(tnl_port->netdev));
     tnl_match_fmt(&tnl_port->match, &ds);
 
     if (cfg->out_key != cfg->in_key ||
@@ -446,5 +469,5 @@ tnl_port_fmt(const struct tnl_port *tnl_port)
 static const char *
 tnl_port_get_name(const struct tnl_port *tnl_port)
 {
-    return netdev_get_name(tnl_port->ofport->netdev);
+    return netdev_get_name(tnl_port->netdev);
 }
index 34c1133..f175f1a 100644 (file)
  * These functions emulate tunnel virtual ports based on the outer
  * header information from the kernel. */
 
-struct ofport;
-struct tnl_port;
+struct ofport_dpif;
+struct netdev;
 
-bool tnl_port_reconfigure(const struct ofport *, uint32_t odp_port,
-                          struct tnl_port **);
+bool tnl_port_reconfigure(const struct ofport_dpif *, const struct netdev *,
+                          odp_port_t);
 
-struct tnl_port *tnl_port_add(const struct ofport *, uint32_t odp_port);
-void tnl_port_del(struct tnl_port *);
+void tnl_port_add(const struct ofport_dpif *, const struct netdev *,
+                  odp_port_t odp_port);
+void tnl_port_del(const struct ofport_dpif *);
 
-const struct ofport *tnl_port_receive(const struct flow *);
-uint32_t tnl_port_send(const struct tnl_port *, struct flow *);
+const struct ofport_dpif *tnl_port_receive(const struct flow *);
+odp_port_t tnl_port_send(const struct ofport_dpif *, struct flow *,
+                         struct flow_wildcards *wc);
 
 /* Returns true if 'flow' should be submitted to tnl_port_receive(). */
 static inline bool
index 9f99d64..4cba4d9 100644 (file)
@@ -129,9 +129,36 @@ ovsdb_jsonrpc_server_create(void)
 bool
 ovsdb_jsonrpc_server_add_db(struct ovsdb_jsonrpc_server *svr, struct ovsdb *db)
 {
+    /* The OVSDB protocol doesn't have a way to notify a client that a
+     * database has been added.  If some client tried to use the database
+     * that we're adding and failed, then forcing it to reconnect seems like
+     * a reasonable way to make it try again.
+     *
+     * If this is too big of a hammer in practice, we could be more selective,
+     * e.g. disconnect only connections that actually tried to use a database
+     * with 'db''s name. */
+    ovsdb_jsonrpc_server_reconnect(svr);
+
     return ovsdb_server_add_db(&svr->up, db);
 }
 
+/* Removes 'db' from the set of databases served out by 'svr'.  Returns
+ * true if successful, false if there is no database associated with 'db'. */
+bool
+ovsdb_jsonrpc_server_remove_db(struct ovsdb_jsonrpc_server *svr,
+                               struct ovsdb *db)
+{
+    /* There might be pointers to 'db' from 'svr', such as monitors or
+     * outstanding transactions.  Disconnect all JSON-RPC connections to avoid
+     * accesses to freed memory.
+     *
+     * If this is too big of a hammer in practice, we could be more selective,
+     * e.g. disconnect only connections that actually reference 'db'. */
+    ovsdb_jsonrpc_server_reconnect(svr);
+
+    return ovsdb_server_remove_db(&svr->up, db);
+}
+
 void
 ovsdb_jsonrpc_server_destroy(struct ovsdb_jsonrpc_server *svr)
 {
@@ -200,7 +227,7 @@ ovsdb_jsonrpc_server_add_remote(struct ovsdb_jsonrpc_server *svr,
 
     error = jsonrpc_pstream_open(name, &listener, options->dscp);
     if (error && error != EAFNOSUPPORT) {
-        VLOG_ERR_RL(&rl, "%s: listen failed: %s", name, strerror(error));
+        VLOG_ERR_RL(&rl, "%s: listen failed: %s", name, ovs_strerror(error));
         return NULL;
     }
 
@@ -291,7 +318,7 @@ ovsdb_jsonrpc_server_run(struct ovsdb_jsonrpc_server *svr)
             } else if (error != EAGAIN) {
                 VLOG_WARN_RL(&rl, "%s: accept failed: %s",
                              pstream_get_name(remote->listener),
-                             strerror(error));
+                             ovs_strerror(error));
             }
         }
 
@@ -572,7 +599,7 @@ ovsdb_jsonrpc_session_set_all_options(
         error = pstream_set_dscp(remote->listener, options->dscp);
         if (error) {
             VLOG_ERR("%s: set_dscp failed %s",
-                     pstream_get_name(remote->listener), strerror(error));
+                     pstream_get_name(remote->listener), ovs_strerror(error));
         } else {
             remote->dscp = options->dscp;
         }
index f2395fc..e6a1642 100644 (file)
@@ -26,6 +26,8 @@ struct simap;
 struct ovsdb_jsonrpc_server *ovsdb_jsonrpc_server_create(void);
 bool ovsdb_jsonrpc_server_add_db(struct ovsdb_jsonrpc_server *,
                                  struct ovsdb *);
+bool ovsdb_jsonrpc_server_remove_db(struct ovsdb_jsonrpc_server *,
+                                     struct ovsdb *);
 void ovsdb_jsonrpc_server_destroy(struct ovsdb_jsonrpc_server *);
 
 /* Options for a remote. */
index 1f21950..6d49dd6 100755 (executable)
@@ -18,6 +18,7 @@ def annotateSchema(schemaFile, annotationFile):
     schemaJson = ovs.json.from_file(schemaFile)
     execfile(annotationFile, globals(), {"s": schemaJson})
     ovs.json.to_stream(schemaJson, sys.stdout)
+    sys.stdout.write('\n')
 
 def constify(cType, const):
     if (const and cType.endswith('*') and not cType.endswith('**')):
index 82dd9c6..1201e6f 100644 (file)
@@ -45,12 +45,10 @@ Adds \fIremote\fR as a connection method used by \fBovsdb\-server\fR.
 .so ovsdb/remote-passive.man
 .so ovsdb/remote-active.man
 .
-.IP "\fBdb:\fR[\fIdb\fB,\fR]\fItable\fB,\fIcolumn\fR"
+.IP "\fBdb:\fIdb\fB,\fItable\fB,\fIcolumn\fR"
 Reads additional connection methods from \fIcolumn\fR in all of the
-rows in \fItable\fR within \fIdb\fR.  (If \fBovsdb\-server\fR is
-providing access to only one database, then \fIdb\fR is optional.)  As
-the contents of \fIcolumn\fR changes, \fBovsdb\-server\fR also adds
-and drops connection methods accordingly.
+rows in \fItable\fR within \fIdb\fR.  As the contents of \fIcolumn\fR changes,
+\fBovsdb\-server\fR also adds and drops connection methods accordingly.
 .IP
 If \fIcolumn\fR's type is string or set of strings, then the
 connection methods are taken directly from the column.  The connection
@@ -98,9 +96,9 @@ configured remotes.
 The options described below for configuring the SSL public key
 infrastructure accept a special syntax for obtaining their
 configuration from the database.  If any of these options is given
-\fBdb:\fR[\fIdb\fB,\fR]\fItable\fB,\fIcolumn\fR as its argument, then the
+\fBdb:\fIdb\fB,\fItable\fB,\fIcolumn\fR as its argument, then the
 actual file name is read from the specified \fIcolumn\fR in \fItable\fR
-within the \fBovsdb\-server\fR database.  The \fIcolumn\fR must have type
+within the \fIdb\fR database.  The \fIcolumn\fR must have type
 string or set of strings.  The first nonempty string in the table is taken
 as the file name.  (This means that ordinarily there should be at most
 one row in \fItable\fR.)
@@ -140,9 +138,9 @@ with an error if \fIremote\fR is not configured as a remote.  This
 command only works with remotes that were named on \fB\-\-remote\fR or
 \fBovsdb\-server/add\-remote\fR, that is, it will not remove remotes
 added indirectly because they were read from the database by
-configuring a \fBdb:\fR[\fIdb\fB,\fR]\fItable\fB,\fIcolumn\fR remote.
+configuring a \fBdb:\fIdb\fB,\fItable\fB,\fIcolumn\fR remote.
 (You can remove a database source with \fBovsdb\-server/remove\-remote
-\fBdb:\fR[\fIdb\fB,\fR]\fItable\fB,\fIcolumn\fR, but not individual
+\fBdb:\fIdb\fB,\fItable\fB,\fIcolumn\fR, but not individual
 remotes found indirectly through the database.)
 .
 .IP "\fBovsdb\-server/list\-remotes"
@@ -150,7 +148,30 @@ Outputs a list of the currently configured remotes named on
 \fB\-\-remote\fR or \fBovsdb\-server/add\-remote\fR, that is, it does
 not list remotes added indirectly because they were read from the
 database by configuring a
-\fBdb:\fR[\fIdb\fB,\fR]\fItable\fB,\fIcolumn\fR remote.
+\fBdb:\fIdb\fB,\fItable\fB,\fIcolumn\fR remote.
+.
+.IP "\fBovsdb\-server/add\-db \fIdatabase\fR"
+Adds the \fIdatabase\fR to the running \fBovsdb\-server\fR.  The database
+file must already have been created and initialized using, for example,
+\fBovsdb\-tool create\fR.
+.
+.IP "\fBovsdb\-server/remove\-db \fIdatabase\fR"
+Removes \fIdatabase\fR from the running \fBovsdb\-server\fR.  \fIdatabase\fR
+must be a database name as listed by \fBovsdb-server/list\-dbs\fR.
+.IP
+If a remote has been configured that points to the specified
+\fIdatabase\fR (e.g. \fB\-\-remote=db:\fIdatabase\fB,\fR... on the
+command line), then it will be disabled until another database with
+the same name is added again (with \fBovsdb\-server/add\-db\fR).
+.IP
+Any public key infrastructure options specified through this database
+(e.g. \fB\-\-private\-key=db:\fIdatabase,\fR... on the command line)
+will be disabled until another database with the same name is added
+again (with \fBovsdb\-server/add\-db\fR).
+.
+.IP "\fBovsdb\-server/list\-dbs"
+Outputs a list of the currently configured databases added either through
+the command line or through the \fBovsdb\-server/add\-db\fR command.
 .
 .so lib/vlog-unixctl.man
 .so lib/memory-unixctl.man
index 1ba7c3c..912f599 100644 (file)
@@ -32,7 +32,6 @@
 #include "json.h"
 #include "jsonrpc.h"
 #include "jsonrpc-server.h"
-#include "leak-checker.h"
 #include "list.h"
 #include "memory.h"
 #include "ovsdb.h"
@@ -43,6 +42,7 @@
 #include "process.h"
 #include "row.h"
 #include "simap.h"
+#include "shash.h"
 #include "stream-ssl.h"
 #include "stream.h"
 #include "stress.h"
@@ -77,27 +77,42 @@ static unixctl_cb_func ovsdb_server_exit;
 static unixctl_cb_func ovsdb_server_compact;
 static unixctl_cb_func ovsdb_server_reconnect;
 
-struct add_remote_aux {
+struct server_config {
     struct sset *remotes;
-    struct db *dbs;
-    size_t n_dbs;
+    struct shash *all_dbs;
+    FILE *config_tmpfile;
+    struct ovsdb_jsonrpc_server *jsonrpc;
 };
 static unixctl_cb_func ovsdb_server_add_remote;
 static unixctl_cb_func ovsdb_server_remove_remote;
 static unixctl_cb_func ovsdb_server_list_remotes;
 
+static unixctl_cb_func ovsdb_server_add_database;
+static unixctl_cb_func ovsdb_server_remove_database;
+static unixctl_cb_func ovsdb_server_list_databases;
+
+static char *open_db(struct server_config *config, const char *filename);
+
 static void parse_options(int *argc, char **argvp[],
                           struct sset *remotes, char **unixctl_pathp,
                           char **run_command);
 static void usage(void) NO_RETURN;
 
-static void reconfigure_from_db(struct ovsdb_jsonrpc_server *jsonrpc,
-                                const struct db dbs[], size_t n_dbs,
-                                struct sset *remotes);
+static char *reconfigure_remotes(struct ovsdb_jsonrpc_server *,
+                                 const struct shash *all_dbs,
+                                 struct sset *remotes);
+static char *reconfigure_ssl(const struct shash *all_dbs);
+static void report_error_if_changed(char *error, char **last_errorp);
 
 static void update_remote_status(const struct ovsdb_jsonrpc_server *jsonrpc,
                                  const struct sset *remotes,
-                                 struct db dbs[], size_t n_dbs);
+                                 struct shash *all_dbs);
+
+static void save_config__(FILE *config_file, const struct sset *remotes,
+                          const struct sset *db_filenames);
+static void save_config(struct server_config *);
+static void load_config(FILE *config_file, struct sset *remotes,
+                        struct sset *db_filenames);
 
 int
 main(int argc, char *argv[])
@@ -106,15 +121,18 @@ main(int argc, char *argv[])
     char *run_command = NULL;
     struct unixctl_server *unixctl;
     struct ovsdb_jsonrpc_server *jsonrpc;
-    struct sset remotes;
+    struct sset remotes, db_filenames;
+    const char *db_filename;
     struct process *run_process;
     bool exiting;
     int retval;
     long long int status_timer = LLONG_MIN;
-    struct add_remote_aux add_remote_aux;
-
-    struct db *dbs;
-    int n_dbs;
+    FILE *config_tmpfile;
+    struct server_config server_config;
+    struct shash all_dbs;
+    struct shash_node *node;
+    char *remotes_error, *ssl_error;
+    char *error;
     int i;
 
     proctitle_init(argc, argv);
@@ -125,36 +143,55 @@ main(int argc, char *argv[])
 
     parse_options(&argc, &argv, &remotes, &unixctl_path, &run_command);
 
-    daemonize_start();
+    /* Create and initialize 'config_tmpfile' as a temporary file to hold
+     * ovsdb-server's most basic configuration, and then save our initial
+     * configuration to it.  When --monitor is used, this preserves the effects
+     * of ovs-appctl commands such as ovsdb-server/add-remote (which saves the
+     * new configuration) across crashes. */
+    config_tmpfile = tmpfile();
+    if (!config_tmpfile) {
+        ovs_fatal(errno, "failed to create temporary file");
+    }
 
-    n_dbs = MAX(1, argc);
-    dbs = xcalloc(n_dbs + 1, sizeof *dbs);
+    sset_init(&db_filenames);
     if (argc > 0) {
         for (i = 0; i < argc; i++) {
-            dbs[i].filename = argv[i];
-        }
+            sset_add(&db_filenames, argv[i]);
+         }
     } else {
-        dbs[0].filename = xasprintf("%s/conf.db", ovs_dbdir());
+        char *default_db = xasprintf("%s/conf.db", ovs_dbdir());
+        sset_add(&db_filenames, default_db);
+        free(default_db);
     }
 
-    for (i = 0; i < n_dbs; i++) {
-        struct ovsdb_error *error;
+    server_config.remotes = &remotes;
+    server_config.config_tmpfile = config_tmpfile;
 
-        error = ovsdb_file_open(dbs[i].filename, false,
-                                &dbs[i].db, &dbs[i].file);
+    save_config__(config_tmpfile, &remotes, &db_filenames);
+
+    daemonize_start();
+
+    /* Load the saved config. */
+    load_config(config_tmpfile, &remotes, &db_filenames);
+    jsonrpc = ovsdb_jsonrpc_server_create();
+
+    shash_init(&all_dbs);
+    server_config.all_dbs = &all_dbs;
+    server_config.jsonrpc = jsonrpc;
+    SSET_FOR_EACH (db_filename, &db_filenames) {
+        error = open_db(&server_config, db_filename);
         if (error) {
-            ovs_fatal(0, "%s", ovsdb_error_to_string(error));
+            ovs_fatal(0, "%s", error);
         }
     }
 
-    jsonrpc = ovsdb_jsonrpc_server_create();
-    for (i = 0; i < n_dbs; i++) {
-        if (!ovsdb_jsonrpc_server_add_db(jsonrpc, dbs[i].db)) {
-            ovs_fatal(0, "%s: duplicate database name",
-                      dbs[i].db->schema->name);
-        }
+    error = reconfigure_remotes(jsonrpc, &all_dbs, &remotes);
+    if (!error) {
+        error = reconfigure_ssl(&all_dbs);
+    }
+    if (error) {
+        ovs_fatal(0, "%s", error);
     }
-    reconfigure_from_db(jsonrpc, dbs, n_dbs, &remotes);
 
     retval = unixctl_server_create(unixctl_path, &unixctl);
     if (retval) {
@@ -169,7 +206,7 @@ main(int argc, char *argv[])
         run_argv[2] = run_command;
         run_argv[3] = NULL;
 
-        retval = process_start(run_argv, NULL, 0, NULL, 0, &run_process);
+        retval = process_start(run_argv, &run_process);
         if (retval) {
             ovs_fatal(retval, "%s: process failed to start", run_command);
         }
@@ -188,63 +225,76 @@ main(int argc, char *argv[])
 
     unixctl_command_register("exit", "", 0, 0, ovsdb_server_exit, &exiting);
     unixctl_command_register("ovsdb-server/compact", "", 0, 1,
-                             ovsdb_server_compact, dbs);
+                             ovsdb_server_compact, &all_dbs);
     unixctl_command_register("ovsdb-server/reconnect", "", 0, 0,
                              ovsdb_server_reconnect, jsonrpc);
 
-    add_remote_aux.remotes = &remotes;
-    add_remote_aux.dbs = dbs;
-    add_remote_aux.n_dbs = n_dbs;
     unixctl_command_register("ovsdb-server/add-remote", "REMOTE", 1, 1,
-                             ovsdb_server_add_remote, &add_remote_aux);
+                             ovsdb_server_add_remote, &server_config);
     unixctl_command_register("ovsdb-server/remove-remote", "REMOTE", 1, 1,
-                             ovsdb_server_remove_remote, &remotes);
+                             ovsdb_server_remove_remote, &server_config);
     unixctl_command_register("ovsdb-server/list-remotes", "", 0, 0,
                              ovsdb_server_list_remotes, &remotes);
 
+    unixctl_command_register("ovsdb-server/add-db", "DB", 1, 1,
+                             ovsdb_server_add_database, &server_config);
+    unixctl_command_register("ovsdb-server/remove-db", "DB", 1, 1,
+                             ovsdb_server_remove_database, &server_config);
+    unixctl_command_register("ovsdb-server/list-dbs", "", 0, 0,
+                             ovsdb_server_list_databases, &all_dbs);
+
     exiting = false;
+    ssl_error = NULL;
+    remotes_error = NULL;
     while (!exiting) {
-        int i;
-
         memory_run();
         if (memory_should_report()) {
             struct simap usage;
 
             simap_init(&usage);
             ovsdb_jsonrpc_server_get_memory_usage(jsonrpc, &usage);
-            for (i = 0; i < n_dbs; i++) {
-                ovsdb_get_memory_usage(dbs[i].db, &usage);
+            SHASH_FOR_EACH(node, &all_dbs) {
+                struct db *db = node->data;
+                ovsdb_get_memory_usage(db->db, &usage);
             }
             memory_report(&usage);
             simap_destroy(&usage);
         }
 
-        /* Run unixctl_server_run() before reconfigure_from_db() because
+        /* Run unixctl_server_run() before reconfigure_remotes() because
          * ovsdb-server/add-remote and ovsdb-server/remove-remote can change
-         * the set of remotes that reconfigure_from_db() uses. */
+         * the set of remotes that reconfigure_remotes() uses. */
         unixctl_server_run(unixctl);
 
-        reconfigure_from_db(jsonrpc, dbs, n_dbs, &remotes);
+        report_error_if_changed(
+            reconfigure_remotes(jsonrpc, &all_dbs, &remotes),
+            &remotes_error);
+        report_error_if_changed(reconfigure_ssl(&all_dbs), &ssl_error);
         ovsdb_jsonrpc_server_run(jsonrpc);
 
-        for (i = 0; i < n_dbs; i++) {
-            ovsdb_trigger_run(dbs[i].db, time_msec());
+        SHASH_FOR_EACH(node, &all_dbs) {
+            struct db *db = node->data;
+            ovsdb_trigger_run(db->db, time_msec());
         }
-        if (run_process && process_exited(run_process)) {
-            exiting = true;
+        if (run_process) {
+            process_run();
+            if (process_exited(run_process)) {
+                exiting = true;
+            }
         }
 
         /* update Manager status(es) every 5 seconds */
         if (time_msec() >= status_timer) {
             status_timer = time_msec() + 5000;
-            update_remote_status(jsonrpc, &remotes, dbs, n_dbs);
+            update_remote_status(jsonrpc, &remotes, &all_dbs);
         }
 
         memory_wait();
         ovsdb_jsonrpc_server_wait(jsonrpc);
         unixctl_server_wait(unixctl);
-        for (i = 0; i < n_dbs; i++) {
-            ovsdb_trigger_wait(dbs[i].db, time_msec());
+        SHASH_FOR_EACH(node, &all_dbs) {
+            struct db *db = node->data;
+            ovsdb_trigger_wait(db->db, time_msec());
         }
         if (run_process) {
             process_wait(run_process);
@@ -256,8 +306,9 @@ main(int argc, char *argv[])
         poll_block();
     }
     ovsdb_jsonrpc_server_destroy(jsonrpc);
-    for (i = 0; i < n_dbs; i++) {
-        ovsdb_destroy(dbs[i].db);
+    SHASH_FOR_EACH(node, &all_dbs) {
+        struct db *db = node->data;
+        ovsdb_destroy(db->db);
     }
     sset_destroy(&remotes);
     unixctl_server_destroy(unixctl);
@@ -273,14 +324,42 @@ main(int argc, char *argv[])
     return 0;
 }
 
+static char *
+open_db(struct server_config *config, const char *filename)
+{
+    struct ovsdb_error *db_error;
+    struct db *db;
+    char *error;
+
+    db = xzalloc(sizeof *db);
+    db->filename = xstrdup(filename);
+
+    db_error = ovsdb_file_open(db->filename, false, &db->db, &db->file);
+    if (db_error) {
+        error = ovsdb_error_to_string(db_error);
+    } else if (!ovsdb_jsonrpc_server_add_db(config->jsonrpc, db->db)) {
+        error = xasprintf("%s: duplicate database name", db->db->schema->name);
+    } else {
+        shash_add_assert(config->all_dbs, db->db->schema->name, db);
+        return NULL;
+    }
+
+    ovsdb_error_destroy(db_error);
+    ovsdb_destroy(db->db);
+    free(db->filename);
+    free(db);
+    return error;
+}
+
 static const struct db *
-find_db(const struct db dbs[], size_t n_dbs, const char *db_name)
+find_db(const struct shash *all_dbs, const char *db_name)
 {
-    size_t i;
+    struct shash_node *node;
 
-    for (i = 0; i < n_dbs; i++) {
-        if (!strcmp(dbs[i].db->schema->name, db_name)) {
-            return &dbs[i];
+    SHASH_FOR_EACH(node, all_dbs) {
+        struct db *db = node->data;
+        if (!strcmp(db->db->schema->name, db_name)) {
+            return db;
         }
     }
 
@@ -288,13 +367,13 @@ find_db(const struct db dbs[], size_t n_dbs, const char *db_name)
 }
 
 static char * WARN_UNUSED_RESULT
-parse_db_column__(const struct db dbs[], size_t n_dbs,
+parse_db_column__(const struct shash *all_dbs,
                   const char *name_, char *name,
                   const struct db **dbp,
                   const struct ovsdb_table **tablep,
                   const struct ovsdb_column **columnp)
 {
-    const char *table_name, *column_name;
+    const char *db_name, *table_name, *column_name;
     const struct ovsdb_column *column;
     const struct ovsdb_table *table;
     const char *tokens[3];
@@ -309,28 +388,17 @@ parse_db_column__(const struct db dbs[], size_t n_dbs,
     tokens[0] = strtok_r(NULL, ",", &save_ptr);
     tokens[1] = strtok_r(NULL, ",", &save_ptr);
     tokens[2] = strtok_r(NULL, ",", &save_ptr);
-    if (!tokens[0] || !tokens[1]) {
+    if (!tokens[0] || !tokens[1] || !tokens[2]) {
         return xasprintf("\"%s\": invalid syntax", name_);
     }
-    if (tokens[2]) {
-        const char *db_name = tokens[0];
-        table_name = tokens[1];
-        column_name = tokens[2];
 
-        db = find_db(dbs, n_dbs, tokens[0]);
-        if (!db) {
-            return xasprintf("\"%s\": no database named %s", name_, db_name);
-        }
-    } else {
-        if (n_dbs > 1) {
-            return xasprintf("\"%s\": database name must be specified "
-                             "(because multiple databases are configured)",
-                             name_);
-        }
+    db_name = tokens[0];
+    table_name = tokens[1];
+    column_name = tokens[2];
 
-        table_name = tokens[0];
-        column_name = tokens[1];
-        db = &dbs[0];
+    db = find_db(all_dbs, tokens[0]);
+    if (!db) {
+        return xasprintf("\"%s\": no database named %s", name_, db_name);
     }
 
     table = ovsdb_get_table(db->db, table_name);
@@ -353,14 +421,14 @@ parse_db_column__(const struct db dbs[], size_t n_dbs,
 /* Returns NULL if successful, otherwise a malloc()'d string describing the
  * error. */
 static char * WARN_UNUSED_RESULT
-parse_db_column(const struct db dbs[], size_t n_dbs,
+parse_db_column(const struct shash *all_dbs,
                 const char *name_,
                 const struct db **dbp,
                 const struct ovsdb_table **tablep,
                 const struct ovsdb_column **columnp)
 {
     char *name = xstrdup(name_);
-    char *retval = parse_db_column__(dbs, n_dbs, name_, name,
+    char *retval = parse_db_column__(all_dbs, name_, name,
                                      dbp, tablep, columnp);
     free(name);
     return retval;
@@ -369,7 +437,7 @@ parse_db_column(const struct db dbs[], size_t n_dbs,
 /* Returns NULL if successful, otherwise a malloc()'d string describing the
  * error. */
 static char * WARN_UNUSED_RESULT
-parse_db_string_column(const struct db dbs[], size_t n_dbs,
+parse_db_string_column(const struct shash *all_dbs,
                        const char *name,
                        const struct db **dbp,
                        const struct ovsdb_table **tablep,
@@ -377,7 +445,7 @@ parse_db_string_column(const struct db dbs[], size_t n_dbs,
 {
     char *retval;
 
-    retval = parse_db_column(dbs, n_dbs, name, dbp, tablep, columnp);
+    retval = parse_db_column(all_dbs, name, dbp, tablep, columnp);
     if (retval) {
         return retval;
     }
@@ -392,8 +460,9 @@ parse_db_string_column(const struct db dbs[], size_t n_dbs,
     return NULL;
 }
 
-static OVS_UNUSED const char *
-query_db_string(const struct db dbs[], size_t n_dbs, const char *name)
+static const char *
+query_db_string(const struct shash *all_dbs, const char *name,
+                struct ds *errors)
 {
     if (!name || strncmp(name, "db:", 3)) {
         return name;
@@ -404,10 +473,11 @@ query_db_string(const struct db dbs[], size_t n_dbs, const char *name)
         const struct db *db;
         char *retval;
 
-        retval = parse_db_string_column(dbs, n_dbs, name,
+        retval = parse_db_string_column(all_dbs, name,
                                         &db, &table, &column);
         if (retval) {
-            ovs_fatal(0, "%s", retval);
+            ds_put_format(errors, "%s\n", retval);
+            return NULL;
         }
 
         HMAP_FOR_EACH (row, hmap_node, &table->rows) {
@@ -631,8 +701,8 @@ add_manager_options(struct shash *remotes, const struct ovsdb_row *row)
 }
 
 static void
-query_db_remotes(const char *name, const struct db dbs[], size_t n_dbs,
-                 struct shash *remotes)
+query_db_remotes(const char *name, const struct shash *all_dbs,
+                 struct shash *remotes, struct ds *errors)
 {
     const struct ovsdb_column *column;
     const struct ovsdb_table *table;
@@ -640,9 +710,11 @@ query_db_remotes(const char *name, const struct db dbs[], size_t n_dbs,
     const struct db *db;
     char *retval;
 
-    retval = parse_db_column(dbs, n_dbs, name, &db, &table, &column);
+    retval = parse_db_column(all_dbs, name, &db, &table, &column);
     if (retval) {
-        ovs_fatal(0, "%s", retval);
+        ds_put_format(errors, "%s\n", retval);
+        free(retval);
+        return;
     }
 
     if (column->type.key.type == OVSDB_TYPE_STRING
@@ -741,7 +813,7 @@ update_remote_row(const struct ovsdb_row *row, struct ovsdb_txn *txn,
 }
 
 static void
-update_remote_rows(const struct db dbs[], size_t n_dbs,
+update_remote_rows(const struct shash *all_dbs,
                    const char *remote_name,
                    const struct ovsdb_jsonrpc_server *jsonrpc)
 {
@@ -755,9 +827,10 @@ update_remote_rows(const struct db dbs[], size_t n_dbs,
         return;
     }
 
-    retval = parse_db_column(dbs, n_dbs, remote_name, &db, &table, &column);
+    retval = parse_db_column(all_dbs, remote_name, &db, &table, &column);
     if (retval) {
-        ovs_fatal(0, "%s", retval);
+        free(retval);
+        return;
     }
 
     if (column->type.key.type != OVSDB_TYPE_UUID
@@ -787,23 +860,27 @@ update_remote_rows(const struct db dbs[], size_t n_dbs,
 static void
 update_remote_status(const struct ovsdb_jsonrpc_server *jsonrpc,
                      const struct sset *remotes,
-                     struct db dbs[], size_t n_dbs)
+                     struct shash *all_dbs)
 {
     static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
     const char *remote;
-    size_t i;
+    struct db *db;
+    struct shash_node *node;
 
-    for (i = 0; i < n_dbs; i++) {
-        dbs[i].txn = ovsdb_txn_create(dbs[i].db);
+    SHASH_FOR_EACH(node, all_dbs) {
+        db = node->data;
+        db->txn = ovsdb_txn_create(db->db);
     }
 
     /* Iterate over --remote arguments given on command line. */
     SSET_FOR_EACH (remote, remotes) {
-        update_remote_rows(dbs, n_dbs, remote, jsonrpc);
+        update_remote_rows(all_dbs, remote, jsonrpc);
     }
 
-    for (i = 0; i < n_dbs; i++) {
-        struct ovsdb_error *error = ovsdb_txn_commit(dbs[i].txn, false);
+    SHASH_FOR_EACH(node, all_dbs) {
+        struct ovsdb_error *error;
+        db = node->data;
+        error = ovsdb_txn_commit(db->txn, false);
         if (error) {
             VLOG_ERR_RL(&rl, "Failed to update remote status: %s",
                         ovsdb_error_to_string(error));
@@ -812,11 +889,12 @@ update_remote_status(const struct ovsdb_jsonrpc_server *jsonrpc,
     }
 }
 
-/* Reconfigures ovsdb-server based on information in the database. */
-static void
-reconfigure_from_db(struct ovsdb_jsonrpc_server *jsonrpc,
-                    const struct db dbs[], size_t n_dbs, struct sset *remotes)
+/* Reconfigures ovsdb-server's remotes based on information in the database. */
+static char *
+reconfigure_remotes(struct ovsdb_jsonrpc_server *jsonrpc,
+                    const struct shash *all_dbs, struct sset *remotes)
 {
+    struct ds errors = DS_EMPTY_INITIALIZER;
     struct shash resolved_remotes;
     const char *name;
 
@@ -824,7 +902,7 @@ reconfigure_from_db(struct ovsdb_jsonrpc_server *jsonrpc,
     shash_init(&resolved_remotes);
     SSET_FOR_EACH (name, remotes) {
         if (!strncmp(name, "db:", 3)) {
-            query_db_remotes(name, dbs, n_dbs, &resolved_remotes);
+            query_db_remotes(name, all_dbs, &resolved_remotes, &errors);
         } else {
             add_remote(&resolved_remotes, name);
         }
@@ -832,11 +910,42 @@ reconfigure_from_db(struct ovsdb_jsonrpc_server *jsonrpc,
     ovsdb_jsonrpc_server_set_remotes(jsonrpc, &resolved_remotes);
     shash_destroy_free_data(&resolved_remotes);
 
-    /* Configure SSL. */
-    stream_ssl_set_key_and_cert(query_db_string(dbs, n_dbs, private_key_file),
-                                query_db_string(dbs, n_dbs, certificate_file));
-    stream_ssl_set_ca_cert_file(query_db_string(dbs, n_dbs, ca_cert_file),
-                                bootstrap_ca_cert);
+    return errors.string;
+}
+
+static char *
+reconfigure_ssl(const struct shash *all_dbs)
+{
+    struct ds errors = DS_EMPTY_INITIALIZER;
+    const char *resolved_private_key;
+    const char *resolved_certificate;
+    const char *resolved_ca_cert;
+
+    resolved_private_key = query_db_string(all_dbs, private_key_file, &errors);
+    resolved_certificate = query_db_string(all_dbs, certificate_file, &errors);
+    resolved_ca_cert = query_db_string(all_dbs, ca_cert_file, &errors);
+
+    stream_ssl_set_key_and_cert(resolved_private_key, resolved_certificate);
+    stream_ssl_set_ca_cert_file(resolved_ca_cert, bootstrap_ca_cert);
+
+    return errors.string;
+}
+
+static void
+report_error_if_changed(char *error, char **last_errorp)
+{
+    if (error) {
+        if (!*last_errorp || strcmp(error, *last_errorp)) {
+            VLOG_WARN("%s", error);
+            free(*last_errorp);
+            *last_errorp = error;
+            return;
+        }
+        free(error);
+    } else {
+        free(*last_errorp);
+        *last_errorp = NULL;
+    }
 }
 
 static void
@@ -853,14 +962,18 @@ static void
 ovsdb_server_compact(struct unixctl_conn *conn, int argc,
                      const char *argv[], void *dbs_)
 {
-    struct db *dbs = dbs_;
+    struct shash *all_dbs = dbs_;
     struct ds reply;
     struct db *db;
+    struct shash_node *node;
     int n = 0;
 
     ds_init(&reply);
-    for (db = dbs; db->filename != NULL; db++) {
-        const char *name = db->db->schema->name;
+    SHASH_FOR_EACH(node, all_dbs) {
+        const char *name;
+
+        db = node->data;
+        name = db->db->schema->name;
 
         if (argc < 2 || !strcmp(argv[1], name)) {
             struct ovsdb_error *error;
@@ -904,9 +1017,9 @@ ovsdb_server_reconnect(struct unixctl_conn *conn, int argc OVS_UNUSED,
  * ovsdb-server services. */
 static void
 ovsdb_server_add_remote(struct unixctl_conn *conn, int argc OVS_UNUSED,
-                        const char *argv[], void *aux_)
+                        const char *argv[], void *config_)
 {
-    struct add_remote_aux *aux = aux_;
+    struct server_config *config = config_;
     const char *remote = argv[1];
 
     const struct ovsdb_column *column;
@@ -916,10 +1029,12 @@ ovsdb_server_add_remote(struct unixctl_conn *conn, int argc OVS_UNUSED,
 
     retval = (strncmp("db:", remote, 3)
               ? NULL
-              : parse_db_column(aux->dbs, aux->n_dbs, remote,
+              : parse_db_column(config->all_dbs, remote,
                                 &db, &table, &column));
     if (!retval) {
-        sset_add(aux->remotes, remote);
+        if (sset_add(config->remotes, remote)) {
+            save_config(config);
+        }
         unixctl_command_reply(conn, NULL);
     } else {
         unixctl_command_reply_error(conn, retval);
@@ -931,14 +1046,15 @@ ovsdb_server_add_remote(struct unixctl_conn *conn, int argc OVS_UNUSED,
  * that ovsdb-server services. */
 static void
 ovsdb_server_remove_remote(struct unixctl_conn *conn, int argc OVS_UNUSED,
-                           const char *argv[], void *remotes_)
+                           const char *argv[], void *config_)
 {
-    struct sset *remotes = remotes_;
+    struct server_config *config = config_;
     struct sset_node *node;
 
-    node = sset_find(remotes, argv[1]);
+    node = sset_find(config->remotes, argv[1]);
     if (node) {
-        sset_delete(remotes, node);
+        sset_delete(config->remotes, node);
+        save_config(config);
         unixctl_command_reply(conn, NULL);
     } else {
         unixctl_command_reply_error(conn, "no such remote");
@@ -966,6 +1082,76 @@ ovsdb_server_list_remotes(struct unixctl_conn *conn, int argc OVS_UNUSED,
     ds_destroy(&s);
 }
 
+
+/* "ovsdb-server/add-db DB": adds the DB to ovsdb-server. */
+static void
+ovsdb_server_add_database(struct unixctl_conn *conn, int argc OVS_UNUSED,
+                          const char *argv[], void *config_)
+{
+    struct server_config *config = config_;
+    const char *filename = argv[1];
+    char *error;
+
+    error = open_db(config, filename);
+    if (!error) {
+        save_config(config);
+        unixctl_command_reply(conn, NULL);
+    } else {
+        unixctl_command_reply_error(conn, error);
+        free(error);
+    }
+}
+
+static void
+ovsdb_server_remove_database(struct unixctl_conn *conn, int argc OVS_UNUSED,
+                             const char *argv[], void *config_)
+{
+    struct server_config *config = config_;
+    struct shash_node *node;
+    struct db *db;
+    bool ok;
+
+    node = shash_find(config->all_dbs, argv[1]);
+    if (!node)  {
+        unixctl_command_reply_error(conn, "Failed to find the database.");
+        return;
+    }
+    db = node->data;
+
+    ok = ovsdb_jsonrpc_server_remove_db(config->jsonrpc, db->db);
+    ovs_assert(ok);
+
+    ovsdb_destroy(db->db);
+    shash_delete(config->all_dbs, node);
+    free(db->filename);
+    free(db);
+
+    save_config(config);
+    unixctl_command_reply(conn, NULL);
+}
+
+static void
+ovsdb_server_list_databases(struct unixctl_conn *conn, int argc OVS_UNUSED,
+                            const char *argv[] OVS_UNUSED, void *all_dbs_)
+{
+    struct shash *all_dbs = all_dbs_;
+    const struct shash_node **nodes;
+    struct ds s;
+    size_t i;
+
+    ds_init(&s);
+
+    nodes = shash_sort(all_dbs);
+    for (i = 0; i < shash_count(all_dbs); i++) {
+        struct db *db = nodes[i]->data;
+        ds_put_format(&s, "%s\n", db->db->schema->name);
+    }
+    free(nodes);
+
+    unixctl_command_reply(conn, ds_cstr(&s));
+    ds_destroy(&s);
+}
+
 static void
 parse_options(int *argcp, char **argvp[],
               struct sset *remotes, char **unixctl_pathp, char **run_command)
@@ -977,7 +1163,6 @@ parse_options(int *argcp, char **argvp[],
         OPT_BOOTSTRAP_CA_CERT,
         OPT_ENABLE_DUMMY,
         VLOG_OPTION_ENUMS,
-        LEAK_CHECKER_OPTION_ENUMS,
         DAEMON_OPTION_ENUMS
     };
     static const struct option long_options[] = {
@@ -988,7 +1173,6 @@ parse_options(int *argcp, char **argvp[],
         {"version",     no_argument, NULL, 'V'},
         DAEMON_LONG_OPTIONS,
         VLOG_LONG_OPTIONS,
-        LEAK_CHECKER_LONG_OPTIONS,
         {"bootstrap-ca-cert", required_argument, NULL, OPT_BOOTSTRAP_CA_CERT},
         {"private-key", required_argument, NULL, 'p'},
         {"certificate", required_argument, NULL, 'c'},
@@ -1031,7 +1215,6 @@ parse_options(int *argcp, char **argvp[],
 
         VLOG_OPTION_HANDLERS
         DAEMON_OPTION_HANDLERS
-        LEAK_CHECKER_OPTION_HANDLERS
 
         case 'p':
             private_key_file = optarg;
@@ -1086,6 +1269,101 @@ usage(void)
            "  --unixctl=SOCKET        override default control socket name\n"
            "  -h, --help              display this help message\n"
            "  -V, --version           display version information\n");
-    leak_checker_usage();
     exit(EXIT_SUCCESS);
 }
+\f
+static struct json *
+sset_to_json(const struct sset *sset)
+{
+    struct json *array;
+    const char *s;
+
+    array = json_array_create_empty();
+    SSET_FOR_EACH (s, sset) {
+        json_array_add(array, json_string_create(s));
+    }
+    return array;
+}
+
+/* Truncates and replaces the contents of 'config_file' by a representation of
+ * 'remotes' and 'db_filenames'. */
+static void
+save_config__(FILE *config_file, const struct sset *remotes,
+              const struct sset *db_filenames)
+{
+    struct json *obj;
+    char *s;
+
+    if (ftruncate(fileno(config_file), 0) == -1) {
+        VLOG_FATAL("failed to truncate temporary file (%s)",
+                   ovs_strerror(errno));
+    }
+
+    obj = json_object_create();
+    json_object_put(obj, "remotes", sset_to_json(remotes));
+    json_object_put(obj, "db_filenames", sset_to_json(db_filenames));
+    s = json_to_string(obj, 0);
+    json_destroy(obj);
+
+    if (fseek(config_file, 0, SEEK_SET) != 0
+        || fputs(s, config_file) == EOF
+        || fflush(config_file) == EOF) {
+        VLOG_FATAL("failed to write temporary file (%s)", ovs_strerror(errno));
+    }
+    free(s);
+}
+
+/* Truncates and replaces the contents of 'config_file' by a representation of
+ * 'config'. */
+static void
+save_config(struct server_config *config)
+{
+    struct sset db_filenames;
+    struct shash_node *node;
+
+    sset_init(&db_filenames);
+    SHASH_FOR_EACH (node, config->all_dbs) {
+        struct db *db = node->data;
+        sset_add(&db_filenames, db->filename);
+    }
+
+    save_config__(config->config_tmpfile, config->remotes, &db_filenames);
+
+    sset_destroy(&db_filenames);
+}
+
+static void
+sset_from_json(struct sset *sset, const struct json *array)
+{
+    size_t i;
+
+    sset_clear(sset);
+
+    ovs_assert(array->type == JSON_ARRAY);
+    for (i = 0; i < array->u.array.n; i++) {
+        const struct json *elem = array->u.array.elems[i];
+        sset_add(sset, json_string(elem));
+    }
+}
+
+/* Clears and replaces 'remotes' and 'dbnames' by a configuration read from
+ * 'config_file', which must have been previously written by save_config(). */
+static void
+load_config(FILE *config_file, struct sset *remotes, struct sset *db_filenames)
+{
+    struct json *json;
+
+    if (fseek(config_file, 0, SEEK_SET) != 0) {
+        VLOG_FATAL("seek failed in temporary file (%s)", ovs_strerror(errno));
+    }
+    json = json_from_stream(config_file);
+    if (json->type == JSON_STRING) {
+        VLOG_FATAL("reading json failed (%s)", json_string(json));
+    }
+    ovs_assert(json->type == JSON_OBJECT);
+
+    sset_from_json(remotes, shash_find_data(json_object(json), "remotes"));
+    sset_from_json(db_filenames,
+                   shash_find_data(json_object(json), "db_filenames"));
+    json_destroy(json);
+}
index bf4ef3c..82f55cb 100644 (file)
@@ -132,6 +132,19 @@ ovsdb_server_add_db(struct ovsdb_server *server, struct ovsdb *db)
     return shash_add_once(&server->dbs, db->schema->name, db);
 }
 
+/* Removes 'db' from the set of databases served out by 'server'.  Returns
+ * true if successful, false if there is no db associated with
+ * db->schema->name. */
+bool
+ovsdb_server_remove_db(struct ovsdb_server *server, struct ovsdb *db)
+{
+    void *data = shash_find_and_delete(&server->dbs, db->schema->name);
+    if (data) {
+        return true;
+    }
+    return false;
+}
+
 /* Destroys 'server'. */
 void
 ovsdb_server_destroy(struct ovsdb_server *server)
index 561f01e..047cbb7 100644 (file)
@@ -83,6 +83,7 @@ struct ovsdb_server {
 
 void ovsdb_server_init(struct ovsdb_server *);
 bool ovsdb_server_add_db(struct ovsdb_server *, struct ovsdb *);
+bool ovsdb_server_remove_db(struct ovsdb_server *, struct ovsdb *);
 void ovsdb_server_destroy(struct ovsdb_server *);
 
 struct ovsdb_lock_waiter *ovsdb_server_lock(struct ovsdb_server *,
index 0d4f522..e7545ad 100644 (file)
@@ -230,7 +230,7 @@ error:
  * The "isRoot" member is included in the JSON only if its value would differ
  * from 'default_is_root'.  Ordinarily 'default_is_root' should be false,
  * because ordinarily a table would be not be part of the root set if its
- * "isRoot" member is omitted.  However, garbage collection was not orginally
+ * "isRoot" member is omitted.  However, garbage collection was not originally
  * included in OVSDB, so in older schemas that do not include any "isRoot"
  * members, every table is implicitly part of the root set.  To serialize such
  * a schema in a way that can be read by older OVSDB tools, specify
index 55e7a73..e21a1cc 100644 (file)
@@ -468,7 +468,7 @@ class Datum(object):
         new datum's value is taken from 'value', which must take the form
         described as a valid return value from Datum.to_python() for 'type'.
 
-        Each scalar value within 'value' is initally passed through
+        Each scalar value within 'value' is initially passed through
         'row_to_uuid', which should convert objects that represent rows (if
         any) into uuid.UUID objects and return other data unchanged.
 
index 1b5a771..bc86232 100644 (file)
@@ -217,7 +217,7 @@ class TableSchema(object):
         differ from 'default_is_root'.  Ordinarily 'default_is_root' should be
         false, because ordinarily a table would be not be part of the root set
         if its "isRoot" member is omitted.  However, garbage collection was not
-        orginally included in OVSDB, so in older schemas that do not include
+        originally included in OVSDB, so in older schemas that do not include
         any "isRoot" members, every table is implicitly part of the root set.
         To serialize such a schema in a way that can be read by older OVSDB
         tools, specify 'default_is_root' as True.
index 6c225f7..dc40403 100644 (file)
@@ -1,6 +1,6 @@
 # Spec file for Open vSwitch.
 
-# Copyright (C) 2009, 2010 Nicira Networks, Inc.
+# Copyright (C) 2009, 2010, 2013 Nicira Networks, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -169,7 +169,6 @@ systemctl start openvswitch.service
 %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
-%doc /usr/share/man/man8/ovs-parse-leaks.8.gz
 %doc /usr/share/man/man8/ovs-vsctl.8.gz
 %doc /usr/share/man/man8/ovs-vswitchd.8.gz
 %doc /usr/share/man/man8/ovs-test.8.gz
@@ -179,7 +178,6 @@ systemctl start openvswitch.service
 %exclude /etc/openvswitch
 %exclude /usr/bin/ovs-benchmark
 %exclude /usr/bin/ovs-parse-backtrace
-%exclude /usr/bin/ovs-parse-leaks
 %exclude /usr/bin/ovs-pcap
 %exclude /usr/bin/ovs-tcpundump
 %exclude /usr/bin/ovs-vlan-test
index 9f40881..53512bc 100644 (file)
@@ -1,6 +1,6 @@
 # Spec file for Open vSwitch on Red Hat Enterprise Linux.
 
-# Copyright (C) 2009, 2010, 2011, 2012 Nicira, Inc.
+# Copyright (C) 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -116,7 +116,6 @@ exit 0
 /usr/bin/ovs-dpctl
 /usr/bin/ovs-ofctl
 /usr/bin/ovs-parse-backtrace
-/usr/bin/ovs-parse-leaks
 /usr/bin/ovs-pcap
 /usr/bin/ovs-pki
 /usr/bin/ovs-tcpundump
@@ -140,7 +139,6 @@ exit 0
 /usr/share/man/man8/ovs-dpctl.8.gz
 /usr/share/man/man8/ovs-ofctl.8.gz
 /usr/share/man/man8/ovs-parse-backtrace.8.gz
-/usr/share/man/man8/ovs-parse-leaks.8.gz
 /usr/share/man/man8/ovs-pki.8.gz
 /usr/share/man/man8/ovs-vlan-test.8.gz
 /usr/share/man/man8/ovs-vsctl.8.gz
index f6f113e..c6f589e 100644 (file)
@@ -17,6 +17,7 @@
 /test-flows
 /test-hash
 /test-heap
+/test-hindex
 /test-hmap
 /test-json
 /test-jsonrpc
index 4442eb5..755d88e 100644 (file)
@@ -37,6 +37,7 @@ TESTSUITE_AT = \
        tests/reconnect.at \
        tests/ovs-vswitchd.at \
        tests/ofproto-dpif.at \
+       tests/vlan-splinters.at \
        tests/ofproto-macros.at \
        tests/ofproto.at \
        tests/ovsdb.at \
@@ -97,6 +98,7 @@ valgrind_wrappers = \
        tests/valgrind/ovsdb-server \
        tests/valgrind/ovsdb-tool \
        tests/valgrind/test-aes128 \
+       tests/valgrind/test-atomic \
        tests/valgrind/test-bundle \
        tests/valgrind/test-byte-order \
        tests/valgrind/test-classifier \
@@ -105,6 +107,7 @@ valgrind_wrappers = \
        tests/valgrind/test-flows \
        tests/valgrind/test-hash \
        tests/valgrind/test-heap \
+       tests/valgrind/test-hindex \
        tests/valgrind/test-hmap \
        tests/valgrind/test-json \
        tests/valgrind/test-jsonrpc \
@@ -145,6 +148,12 @@ check-valgrind: all tests/atconfig tests/atlocal $(TESTSUITE) \
        @echo 'Valgrind output can be found in tests/testsuite.dir/*/valgrind.*'
        @echo '----------------------------------------------------------------------'
 \f
+# OFTest support.
+
+check-oftest: all
+       srcdir='$(srcdir)' $(SHELL) $(srcdir)/tests/run-oftest
+EXTRA_DIST += tests/run-oftest
+\f
 clean-local:
        test ! -f '$(TESTSUITE)' || $(SHELL) '$(TESTSUITE)' -C tests --clean
 
@@ -168,6 +177,10 @@ noinst_PROGRAMS += tests/test-aes128
 tests_test_aes128_SOURCES = tests/test-aes128.c
 tests_test_aes128_LDADD = lib/libopenvswitch.a $(SSL_LIBS)
 
+noinst_PROGRAMS += tests/test-atomic
+tests_test_atomic_SOURCES = tests/test-atomic.c
+tests_test_atomic_LDADD = lib/libopenvswitch.a $(SSL_LIBS)
+
 noinst_PROGRAMS += tests/test-bundle
 tests_test_bundle_SOURCES = tests/test-bundle.c
 tests_test_bundle_LDADD = lib/libopenvswitch.a $(SSL_LIBS)
@@ -197,6 +210,10 @@ noinst_PROGRAMS += tests/test-heap
 tests_test_heap_SOURCES = tests/test-heap.c
 tests_test_heap_LDADD = lib/libopenvswitch.a $(SSL_LIBS)
 
+noinst_PROGRAMS += tests/test-hindex
+tests_test_hindex_SOURCES = tests/test-hindex.c
+tests_test_hindex_LDADD = lib/libopenvswitch.a $(SSL_LIBS)
+
 noinst_PROGRAMS += tests/test-hmap
 tests_test_hmap_SOURCES = tests/test-hmap.c
 tests_test_hmap_LDADD = lib/libopenvswitch.a $(SSL_LIBS)
index 39ab3cc..9bc7810 100644 (file)
@@ -142,7 +142,7 @@ CHECK([kill -0 `cat daemon`])
 CHECK([ps -o ppid= -p `cat daemon` > monitor])
 CHECK([kill -0 `cat monitor`])
 CHECK([ps -o ppid= -p `cat monitor` > init])
-CHECK([test `cat init` = 1])
+CHECK([test `cat init` != $$])
 # Kill the daemon process, making it look like a segfault,
 # and wait for a new daemon process to get spawned.
 CHECK([cp daemon olddaemon])
@@ -159,7 +159,7 @@ CHECK([ps -o ppid= -p `cat daemon` > newmonitor])
 CHECK([diff monitor newmonitor])
 CHECK([kill -0 `cat newmonitor`])
 CHECK([ps -o ppid= -p `cat newmonitor` > init])
-CHECK([test `cat init` = 1])
+CHECK([test `cat init` != $$])
 # Kill the daemon process with SIGTERM, and wait for the daemon
 # and the monitor processes to go away and the pidfile to get deleted.
 CHECK([kill `cat daemon`], [0], [], [ignore])
index a80cd3e..7c30e10 100644 (file)
@@ -104,7 +104,7 @@ CHECK([kill -0 `cat daemon`])
 CHECK([ps -o ppid= -p `cat daemon` > monitor])
 CHECK([kill -0 `cat monitor`])
 CHECK([ps -o ppid= -p `cat monitor` > init])
-CHECK([test `cat init` = 1])
+CHECK([test `cat init` != $$])
 # Kill the daemon process, making it look like a segfault,
 # and wait for a new daemon process to get spawned.
 CHECK([cp daemon olddaemon])
@@ -121,7 +121,7 @@ CHECK([ps -o ppid= -p `cat daemon` > newmonitor])
 CHECK([diff monitor newmonitor])
 CHECK([kill -0 `cat newmonitor`])
 CHECK([ps -o ppid= -p `cat newmonitor` > init])
-CHECK([test `cat init` = 1])
+CHECK([test `cat init` != $$])
 # Kill the daemon process with SIGTERM, and wait for the daemon
 # and the monitor processes to go away and the pidfile to get deleted.
 CHECK([kill `cat daemon`], [0], [], [ignore])
index 5eb3e5d..ec1c347 100644 (file)
@@ -15,6 +15,28 @@ OFPT_FLOW_MOD (xid=0x3): ADD actions=learn(table=1,idle_timeout=10,hard_timeout=
 ]])
 AT_CLEANUP
 
+AT_SETUP([learning action - parsing and formatting - illegal in_port_oxm])
+AT_CHECK([[ovs-ofctl parse-flow 'actions=learn(table=1, in_port_oxm=123456)']],
+  [1], [], [stderr])
+AT_CHECK([sed -e 's/.*|ofp_util|WARN|//' < stderr], [0],
+  [[port 123456 is outside the supported range 0 through ffff or 0xffffff00 through 0xffffffff
+ovs-ofctl: 123456: port value out of range for in_port_oxm
+]], [[]])
+AT_CLEANUP
+
+AT_SETUP([learning action - parsing and formatting - OXM])
+AT_DATA([flows.txt], [[
+actions=learn(output:OXM_OF_IN_PORT[])
+actions=learn(table=1, in_port=1, load:OXM_OF_IN_PORT[]->NXM_NX_REG1[], load:0xfffffffe->OXM_OF_IN_PORT[])
+]])
+AT_CHECK([ovs-ofctl parse-flows flows.txt], [0],
+[[usable protocols: any
+chosen protocol: OpenFlow10-table_id
+OFPT_FLOW_MOD (xid=0x1): ADD actions=learn(table=1,output:OXM_OF_IN_PORT[])
+OFPT_FLOW_MOD (xid=0x2): ADD actions=learn(table=1,in_port=1,load:OXM_OF_IN_PORT[]->NXM_NX_REG1[],load:0xfffffffe->OXM_OF_IN_PORT[])
+]])
+AT_CLEANUP
+
 AT_SETUP([learning action - examples])
 AT_DATA([flows.txt], [[
 # These are the examples from nicira-ext.h.
@@ -75,7 +97,7 @@ AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
 
 # Trace an ARP packet arriving on port 3, to create a MAC learning entry.
 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])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow" -generate], [0], [stdout])
 actual=`tail -1 stdout | sed 's/Datapath actions: //'`
 
 expected="1,2,100"
@@ -92,7 +114,7 @@ NXST_FLOW reply:
 
 # Trace a packet arrival destined for the learned MAC.
 # (This will also learn a MAC.)
-AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(1),eth(src=50:54:00:00:00:06,dst=50:54:00:00:00:05),eth_type(0x0806),arp(sip=192.168.0.2,tip=192.168.0.1,op=2,sha=50:54:00:00:00:06,tha=50:54:00:00:00:05)' -generate], [0], [stdout])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:06,dst=50:54:00:00:00:05),eth_type(0x0806),arp(sip=192.168.0.2,tip=192.168.0.1,op=2,sha=50:54:00:00:00:06,tha=50:54:00:00:00:05)' -generate], [0], [stdout])
 AT_CHECK([tail -1 stdout], [0], [Datapath actions: 3
 ])
 
@@ -106,7 +128,7 @@ NXST_FLOW reply:
 
 # Trace a packet arrival that updates the first learned MAC entry.
 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])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow" -generate], [0], [stdout])
 actual=`tail -1 stdout | sed 's/Datapath actions: //'`
 
 expected="1,3,100"
@@ -147,7 +169,7 @@ AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
 
 # Trace an ICMP packet arriving on port 3, to create a MAC learning entry.
 flow="in_port(3),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)"
-AT_CHECK([ovs-appctl ofproto/trace br0 "$flow" -generate], [0], [stdout])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow" -generate], [0], [stdout])
 actual=`tail -1 stdout | sed 's/Datapath actions: //'`
 
 expected="1,2,100"
@@ -166,7 +188,7 @@ NXST_FLOW reply:
 # disappear as long as we refresh it every second.
 for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25; do
     ovs-appctl time/warp 1000
-    AT_CHECK([ovs-appctl ofproto/trace br0 "$flow" -generate], [0], [stdout])
+    AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow" -generate], [0], [stdout])
 
     # Check that the entry is there.
     AT_CHECK([ovs-ofctl dump-flows br0 table=1], [0], [stdout])
@@ -209,7 +231,7 @@ AT_CHECK([[ovs-ofctl add-flow br0 'table=0 tcp actions=learn(table=1, hard_timeo
 
 # Trace a TCPv4 packet arriving on port 3.
 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])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow" -generate], [0], [stdout])
 actual=`tail -1 stdout | sed 's/Datapath actions: //'`
 
 expected="1,2,100"
@@ -237,7 +259,7 @@ AT_CHECK([[ovs-ofctl add-flow br0 'table=0 tcp6 actions=learn(table=1, hard_time
 
 # Trace a TCPv6 packet arriving on port 3.
 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])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow" -generate], [0], [stdout])
 actual=`tail -1 stdout | sed 's/Datapath actions: //'`
 
 expected="1,2,100"
@@ -296,7 +318,7 @@ AT_SETUP([learning action - fin_timeout feature])
 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-appctl ofproto/trace ovs-dummy '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],
 [NXST_FLOW reply:
  table=1, dl_dst=50:54:00:00:00:05 actions=fin_timeout(idle_timeout=5,hard_timeout=10),output:1
index 650fef3..f84a55b 100644 (file)
@@ -20,6 +20,15 @@ AT_CHECK([test-hmap], [0], [.........
 ])
 AT_CLEANUP
 
+AT_SETUP([test hash index])
+AT_CHECK([test-hindex], [0], [..................
+])
+AT_CLEANUP
+
+AT_SETUP([test atomic operations])
+AT_CHECK([test-atomic])
+AT_CLEANUP
+
 AT_SETUP([test linked lists])
 AT_CHECK([test-list], [0], [..
 ])
index a6bcdf5..5776b95 100644 (file)
@@ -80,6 +80,72 @@ AT_CHECK_UNQUOTED([test-odp parse-keys < odp.txt], [0], [`cat odp.txt`
 ])
 AT_CLEANUP
 
+AT_SETUP([OVS datapath wildcarded key parsing and formatting - valid forms])
+dnl We could add a test for invalid forms, but that's less important.
+AT_DATA([odp-base.txt], [dnl
+in_port(1/0xff),eth(src=00:01:02:03:04:05/ff:ff:ff:ff:ff:f0,dst=10:11:12:13:14:15/ff:ff:ff:ff:ff:f0)
+in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x1234/0xfff0)
+in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41/255.255.255.0,dst=172.16.0.20/255.255.255.0,proto=5/0xf0,tos=0x80/0xf0,ttl=128/0xf0,frag=no/0xf0)
+in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=6,tos=0,ttl=128,frag=no),tcp(src=80/0xff00,dst=8080/0xff)
+in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=17,tos=0,ttl=128,frag=no),udp(src=81/0xff00,dst=6632/0xff)
+in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=17,tos=0,ttl=128,frag=no),udp(src=81/0,dst=6632/0)
+in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=1,tos=0,ttl=128,frag=no),icmp(type=1/0xf0,code=2/0xff)
+in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1/::255,dst=::2/::255,label=0/0xf0,proto=10/0xf0,tclass=0x70/0xf0,hlimit=128/0xf0,frag=no/0xf0)
+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=6,tclass=0,hlimit=128,frag=no),tcp(src=80/0xff00,dst=8080/0xff)
+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=17,tclass=0,hlimit=128,frag=no),udp(src=6630/0xff00,dst=22/0xff)
+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=1/0xf0,code=2/0xff)
+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=135,code=0),nd(target=::3/::250)
+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=135,code=0),nd(target=::3/::250,sll=00:05:06:07:08:09/ff:ff:ff:ff:ff:00)
+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/::250,tll=00:0a:0b:0c:0d:0e/ff:ff:ff:ff:ff:00)
+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/::250,sll=00:05:06:07:08:09/ff:ff:ff:ff:ff:00,tll=00:0a:0b:0c:0d:0e/ff:ff:ff:ff:ff:00)
+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/255.255.255.250,tip=5.6.7.8/255.255.255.250,op=1/0xf0,sha=00:0f:10:11:12:13/ff:ff:ff:ff:ff:00,tha=00:14:15:16:17:18/ff:ff:ff:ff:ff:00)
+skb_mark(0x1234/0xfff0),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 tunnel header.'
+ sed 's/^/tunnel(tun_id=0x7f10354\/0xff,src=10.10.10.10\/255.255.255.0,dst=20.20.20.20\/255.255.255.0,tos=0\/0xff,ttl=64\/0xff,flags(csum,key)),/' odp-base.txt
+
+ echo
+ echo '# Valid forms with VLAN header.'
+ sed 's/\(eth([[^)]]*)\),*/\1,eth_type(0x8100),vlan(vid=99,pcp=7),encap(/
+s/$/)/' odp-base.txt
+
+ echo
+ echo '# Valid forms with MPLS header.'
+ sed 's/\(eth([[^)]]*),?\)/\1,eth_type(0x8847),mpls(label=100\/0xff,tc=7\/7,ttl=64\/0xff,bos=1\/1)/' odp-base.txt
+
+ echo
+ echo '# Valid forms with QoS priority.'
+ sed 's/^/skb_priority(0x1234\/0xff),/' odp-base.txt
+
+ echo
+ echo '# Valid forms with tunnel and VLAN headers.'
+ sed 's/^/tunnel(tun_id=0xfedcba9876543210,src=10.0.0.1,dst=10.0.0.2,tos=0x8,ttl=128,flags(key)),/
+s/\(eth([[^)]]*)\),*/\1,eth_type(0x8100),vlan(vid=99/0xff0,pcp=7/0xe),encap(/
+s/$/)/' odp-base.txt
+
+ echo
+ echo '# Valid forms with QOS priority, tunnel, and VLAN headers.'
+ sed 's/^/skb_priority(0x1234),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
+
+ echo
+ echo '# Valid forms with IP first fragment.'
+sed -n 's/,frag=no),/,frag=first),/p' odp-base.txt
+
+ echo
+ echo '# Valid forms with IP later fragment.'
+sed -n 's/,frag=no),.*/,frag=later)/p' odp-base.txt) > odp.txt
+AT_CAPTURE_FILE([odp.txt])
+AT_CHECK_UNQUOTED([test-odp parse-wc-keys < odp.txt], [0], [`cat odp.txt`
+])
+AT_CLEANUP
+
 AT_SETUP([OVS datapath actions parsing and formatting - valid forms])
 AT_DATA([actions.txt], [dnl
 1,2,3
@@ -87,7 +153,6 @@ userspace(pid=555666777)
 userspace(pid=6633,sFlow(vid=9,pcp=7,output=10))
 userspace(pid=9765,slow_path())
 userspace(pid=9765,slow_path(cfm))
-userspace(pid=9765,slow_path(cfm,match))
 userspace(pid=1234567,userdata(0102030405060708090a0b0c0d0e0f))
 userspace(pid=6633,flow_sample(probability=123,collector_set_id=1234,obs_domain_id=2345,obs_point_id=3456))
 userspace(pid=6633,ipfix)
index 2ecbdb5..ebad040 100644 (file)
@@ -339,7 +339,7 @@ dnl Check that an empty Apply-Actions instruction gets dropped.
 0004 0008 00000000
 
 dnl Duplicate instruction type:
-# bad OF1.1 instructions: OFPBAC_UNSUPPORTED_ORDER
+# bad OF1.1 instructions: ONFBIC_DUP_INSTRUCTION
 0004 0008 00000000 0004 0008 00000000
 
 dnl Instructions not multiple of 8 in length.
@@ -356,6 +356,10 @@ dnl Goto-Table 1 instruction non-zero padding
 #  7: 01 -> 00
 0001 0008 01 000001
 
+dnl Goto-Table 1 instruction go back to the previous table.
+# bad OF1.1 instructions: OFPBRC_BAD_TABLE_ID
+2,0001 0008 01 000000
+
 dnl Goto-Table 1
 # actions=goto_table:1
 0001 0008 01 000000
@@ -377,7 +381,7 @@ dnl Write-Metadata too long.
 0002 0020 00000000 fedcba9876543210 ffffffffffffffff 0000000000000000
 
 dnl Write-Metadata duplicated.
-# bad OF1.1 instructions: OFPBAC_UNSUPPORTED_ORDER
+# bad OF1.1 instructions: ONFBIC_DUP_INSTRUCTION
 0002 0018 00000000 fedcba9876543210 ff00ff00ff00ff00 0002 0018 00000000 fedcba9876543210 ff00ff00ff00ff00
 
 dnl Write-Metadata in wrong position (OpenFlow 1.1+ disregards the order
index e99aca9..92f2a98 100644 (file)
@@ -87,16 +87,14 @@ dnl Thus, for OF1.1 we translate both of the latter error codes into 3,5.
 AT_SETUP([encoding OFPBIC_* experimenter errors])
 AT_KEYWORDS([ofp-print ofp-errors])
 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
+OpenFlow 1.1: vendor 0, type 3, code 5
+OpenFlow 1.2: vendor 0, type 3, code 5
+OpenFlow 1.3: vendor 0, type 3, code 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
+OpenFlow 1.1: vendor 0, type 3, code 5
+OpenFlow 1.2: vendor 0, type 3, code 6
+OpenFlow 1.3: vendor 0, type 3, code 6
 ])
 AT_CLEANUP
 
@@ -127,14 +125,47 @@ AT_CHECK([ovs-ofctl ofp-print '0201001455555555 00030005 0102000811111111'], [0]
 OFPT_ERROR (OF1.1) (xid=0x55555555): OFPBIC_BAD_EXPERIMENTER
 OFPT_ECHO_REQUEST (xid=0x11111111): 0 bytes of payload
 ])
-AT_KEYWORDS([ofp-print ofp-errors])
 AT_CHECK([ovs-ofctl ofp-print '0301001455555555 00030005 0102000811111111'], [0], [dnl
 OFPT_ERROR (OF1.2) (xid=0x55555555): OFPBIC_BAD_EXPERIMENTER
 OFPT_ECHO_REQUEST (xid=0x11111111): 0 bytes of payload
 ])
-AT_KEYWORDS([ofp-print ofp-errors])
 AT_CHECK([ovs-ofctl ofp-print '0301001455555555 00030006 0102000811111111'], [0], [dnl
 OFPT_ERROR (OF1.2) (xid=0x55555555): OFPBIC_BAD_EXP_TYPE
 OFPT_ECHO_REQUEST (xid=0x11111111): 0 bytes of payload
 ])
 AT_CLEANUP
+
+AT_SETUP([decoding experimenter errors])
+AT_KEYWORDS([ofp-print ofp-errors])
+AT_CHECK([ovs-ofctl ofp-print '0101001c55555555 b0c20000 0000232000010203 0102000811111111'], [0], [dnl
+OFPT_ERROR (xid=0x55555555): NXBRC_MUST_BE_ZERO
+OFPT_ECHO_REQUEST (xid=0x11111111): 0 bytes of payload
+])
+AT_CHECK([ovs-ofctl ofp-print '0201001c55555555 b0c20000 0000232000010203 0102000811111111'], [0], [dnl
+OFPT_ERROR (OF1.1) (xid=0x55555555): NXBRC_MUST_BE_ZERO
+OFPT_ECHO_REQUEST (xid=0x11111111): 0 bytes of payload
+])
+AT_CHECK([ovs-ofctl ofp-print '0301001855555555 ffff0004 00002320 0102000811111111'], [0], [dnl
+OFPT_ERROR (OF1.2) (xid=0x55555555): NXBRC_MUST_BE_ZERO
+OFPT_ECHO_REQUEST (xid=0x11111111): 0 bytes of payload
+])
+AT_CLEANUP
+
+AT_SETUP([encoding experimenter errors])
+AT_KEYWORDS([ofp-print ofp-errors])
+AT_CHECK(
+  [ovs-ofctl encode-error-reply NXBRC_MUST_BE_ZERO 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 03 01 00 00 08-12 34 56 78 @&t@
+])
+AT_CHECK(
+  [ovs-ofctl encode-error-reply NXBRC_MUST_BE_ZERO 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 03 02 00 00 08-12 34 56 78 @&t@
+])
+AT_CHECK(
+  [ovs-ofctl encode-error-reply NXBRC_MUST_BE_ZERO 0300000812345678], [0], [dnl
+00000000  03 01 00 18 12 34 56 78-ff ff 00 04 00 00 23 20 @&t@
+00000010  03 00 00 08 12 34 56 78-
+])
+AT_CLEANUP
index 35f599c..f8290a1 100644 (file)
@@ -451,6 +451,24 @@ tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:0
 ])
 AT_CLEANUP
 
+AT_SETUP([OFPT_PACKET_IN - OF1.0, with hex output of packet data)])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "\
+01 0a 00 4e 00 00 00 00 00 00 01 11 00 3c 00 03 \
+00 00 50 54 00 00 00 06 50 54 00 00 00 05 08 00 \
+45 00 00 28 bd 12 00 00 40 06 3c 6a c0 a8 00 01 \
+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 \
+" 3], [0], [dnl
+OFPT_PACKET_IN (xid=0x0): total_len=60 in_port=3 (via no_match) data_len=60 buffer=0x00000111
+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
+00000000  50 54 00 00 00 06 50 54-00 00 00 05 08 00 45 00
+00000010  00 28 bd 12 00 00 40 06-3c 6a c0 a8 00 01 c0 a8
+00000020  00 02 27 2f 00 00 78 50-cc 5b 57 af 42 1e 50 00
+00000030  02 00 26 e8 00 00 00 00-00 00 00 00
+])
+AT_CLEANUP
+
 AT_SETUP([OFPT_PACKET_IN - OF1.2])
 AT_KEYWORDS([ofp-print])
 AT_CHECK([ovs-ofctl ofp-print "\
@@ -465,6 +483,23 @@ rarp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=00:23:20:83:c1:5f,dl_dst=ff:ff:
 ])
 AT_CLEANUP
 
+AT_SETUP([OFPT_PACKET_IN - OF1.2, with hex output of packet data])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "\
+03 0a 00 4c 00 00 00 00 ff ff ff 00 00 2a 00 00 \
+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 \
+" 3], [0], [dnl
+OFPT_PACKET_IN (OF1.2) (xid=0x0): 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
+00000000  ff ff ff ff ff ff 00 23-20 83 c1 5f 80 35 00 01
+00000010  08 00 06 04 00 03 00 23-20 83 c1 5f 00 00 00 00
+00000020  00 23 20 83 c1 5f 00 00-00 00
+])
+AT_CLEANUP
+
 AT_SETUP([OFPT_PACKET_IN - OF1.3])
 AT_KEYWORDS([ofp-print])
 AT_CHECK([ovs-ofctl ofp-print "\
@@ -480,6 +515,24 @@ rarp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=00:23:20:83:c1:5f,dl_dst=ff:ff:
 ])
 AT_CLEANUP
 
+AT_SETUP([OFPT_PACKET_IN - OF1.3, with hex output of packet data])
+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 \
+" 3], [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
+00000000  ff ff ff ff ff ff 00 23-20 83 c1 5f 80 35 00 01
+00000010  08 00 06 04 00 03 00 23-20 83 c1 5f 00 00 00 00
+00000020  00 23 20 83 c1 5f 00 00-00 00
+])
+AT_CLEANUP
+
 AT_SETUP([OFPT_FLOW_REMOVED - OF1.0])
 AT_KEYWORDS([ofp-print])
 AT_CHECK([ovs-ofctl ofp-print "\
@@ -567,6 +620,40 @@ OFPT_PACKET_OUT (xid=0x0): in_port=1 actions=output:3 buffer=0x00000114
 ])
 AT_CLEANUP
 
+AT_SETUP([OFPT_PACKET_OUT - OF1.0, with packet])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "\
+01 0d 00 54 00 00 00 00 ff ff ff ff 00 01 00 08 \
+00 00 00 08 00 03 00 00 50 54 00 00 00 05 50 54 \
+00 00 00 06 08 00 45 00 00 28 00 00 40 00 40 06 \
+b9 7c c0 a8 00 02 c0 a8 00 01 00 00 2b 60 00 00 \
+00 00 6a 4f 2b 58 50 14 00 00 6d 75 00 00 00 00 \
+00 00 00 00 \
+"], [0], [dnl
+OFPT_PACKET_OUT (xid=0x0): in_port=1 actions=output:3 data_len=60
+tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=0,tp_dst=11104 tcp_csum:6d75
+])
+AT_CLEANUP
+
+AT_SETUP([OFPT_PACKET_OUT - OF1.0, with hex output of packet data])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "\
+01 0d 00 54 00 00 00 00 ff ff ff ff 00 01 00 08 \
+00 00 00 08 00 03 00 00 50 54 00 00 00 05 50 54 \
+00 00 00 06 08 00 45 00 00 28 00 00 40 00 40 06 \
+b9 7c c0 a8 00 02 c0 a8 00 01 00 00 2b 60 00 00 \
+00 00 6a 4f 2b 58 50 14 00 00 6d 75 00 00 00 00 \
+00 00 00 00 \
+" 3], [0], [dnl
+OFPT_PACKET_OUT (xid=0x0): in_port=1 actions=output:3 data_len=60
+tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=0,tp_dst=11104 tcp_csum:6d75
+00000000  50 54 00 00 00 05 50 54-00 00 00 06 08 00 45 00
+00000010  00 28 00 00 40 00 40 06-b9 7c c0 a8 00 02 c0 a8
+00000020  00 01 00 00 2b 60 00 00-00 00 6a 4f 2b 58 50 14
+00000030  00 00 6d 75 00 00 00 00-00 00 00 00
+])
+AT_CLEANUP
+
 AT_SETUP([OFPT_PACKET_OUT - OF1.1])
 AT_KEYWORDS([ofp-print])
 AT_CHECK([ovs-ofctl ofp-print "\
@@ -578,6 +665,22 @@ OFPT_PACKET_OUT (OF1.2) (xid=0x8858dfc5): in_port=LOCAL actions=FLOOD buffer=0xf
 ])
 AT_CLEANUP
 
+AT_SETUP([OFPT_PACKET_OUT - OF1.1, with packet])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "\
+03 0d 00 64 88 58 df c5 ff ff ff ff ff ff ff fe \
+00 10 00 00 00 00 00 00 00 00 00 10 ff ff ff fb \
+05 dc 00 00 00 00 00 00 50 54 00 00 00 05 50 54 \
+00 00 00 06 08 00 45 00 00 28 00 00 40 00 40 06 \
+b9 7c c0 a8 00 02 c0 a8 00 01 00 00 2b 60 00 00 \
+00 00 6a 4f 2b 58 50 14 00 00 6d 75 00 00 00 00 \
+00 00 00 00 \
+"], [0], [dnl
+OFPT_PACKET_OUT (OF1.2) (xid=0x8858dfc5): in_port=LOCAL actions=FLOOD data_len=60
+tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=0,tp_dst=11104 tcp_csum:6d75
+])
+AT_CLEANUP
+
 # The flow is formatted with cls_rule_format() for the low-verbosity case.
 AT_SETUP([OFPT_FLOW_MOD - OF1.0 - low verbosity])
 AT_KEYWORDS([ofp-print])
@@ -1292,6 +1395,45 @@ OFPST_PORT reply (OF1.2) (xid=0x2): 3 ports
 ])
 AT_CLEANUP
 
+AT_SETUP([OFPST_PORT reply - OF1.3])
+AT_KEYWORDS([ofp-print OFPT_STATS_REPLY])
+AT_CHECK([ovs-ofctl ofp-print "\
+04 13 01 60 00 00 00 02 00 04 00 00 00 00 00 00 \
+00 00 00 02 00 00 00 00 00 00 00 00 00 01 95 56 \
+00 00 00 00 00 00 00 88 00 00 00 00 02 5d 08 98 \
+00 00 00 00 00 00 2c f8 00 00 00 00 00 00 00 00 \
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \
+00 00 00 00 00 00 00 00 00 00 00 01 00 0f 42 40 \
+ff ff ff fe 00 00 00 00 \
+00 00 00 00 00 00 00 44 00 00 00 00 00 00 9d 2c \
+00 00 00 00 00 00 16 7c 00 00 00 00 01 1e 36 44 \
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \
+ff ff ff ff ff ff ff ff \
+00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 44 \
+00 00 00 00 00 00 9d 2c 00 00 00 00 00 00 16 7c \
+00 00 00 00 01 1e 36 44 00 00 00 00 00 00 00 00 \
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \
+00 00 00 00 00 00 00 00 00 00 00 00 07 54 d4 c0 \
+"], [0], [dnl
+OFPST_PORT reply (OF1.3) (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
+           duration=1.001s
+  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
+           duration=0.123s
+])
+AT_CLEANUP
+
 AT_SETUP([OFPST_QUEUE request - OF1.0])
 AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST])
 AT_CHECK([ovs-ofctl ofp-print "\
@@ -1439,6 +1581,95 @@ OFPST_PORT_DESC reply (xid=0x0):
 ])
 AT_CLEANUP
 
+AT_SETUP([OFPT_METER_MOD request - OF1.3])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "\
+04 1d 00 20 00 00 00 02 00 00 00 0d 00 00 00 05 \
+00 01 00 10 00 00 04 00 00 00 00 80 00 00 00 00 \
+"], [0], [dnl
+OFPT_METER_MOD (OF1.3) (xid=0x2): ADD meter=5 kbps burst stats bands=
+type=drop rate=1024 burst_size=128
+])
+AT_CLEANUP
+
+AT_SETUP([OFPST_METER request - OF1.3])
+AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST])
+AT_CHECK([ovs-ofctl ofp-print "041200180000000200090000000000000000000100000000"], [0], [dnl
+OFPST_METER request (OF1.3) (xid=0x2): meter=1
+])
+AT_CLEANUP
+
+AT_SETUP([OFPST_METER_CONFIG request - OF1.3])
+AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST])
+AT_CHECK([ovs-ofctl ofp-print "0412001800000002000a0000000000000000000100000000"], [0], [dnl
+OFPST_METER_CONFIG request (OF1.3) (xid=0x2): meter=1
+])
+AT_CLEANUP
+
+AT_SETUP([OFPST_METER_FEATURES request - OF1.3])
+AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST])
+AT_CHECK([ovs-ofctl ofp-print "0412001000000002000b000000000000"], [0], [dnl
+OFPST_METER_FEATURES request (OF1.3) (xid=0x2):
+])
+AT_CLEANUP
+
+AT_SETUP([OFPST_METER_FEATURES reply - OF1.3])
+AT_KEYWORDS([ofp-print OFPT_STATS_REPLY])
+AT_CHECK([ovs-ofctl ofp-print "\
+04 13 00 20 00 00 00 02 00 0b 00 00 00 00 00 00 \
+00 01 00 00 00 00 00 03 00 00 00 0F 10 02 00 00 \
+"], [0], [dnl
+OFPST_METER_FEATURES reply (OF1.3) (xid=0x2):
+max_meter:65536 max_bands:16 max_color:2
+band_types: drop dscp_remark
+capabilities: kbps pktps burst stats
+])
+AT_CLEANUP
+
+AT_SETUP([OFPST_METER_CONFIG reply - OF1.3])
+AT_KEYWORDS([ofp-print OFPT_STATS_REPLY])
+AT_CHECK([ovs-ofctl ofp-print "\
+04 13 00 50 00 00 00 02 00 0a 00 00 00 00 00 00 \
+00 28 00 05 00 00 00 01 \
+00 01 00 10 00 01 00 00 00 00 05 00 00 00 00 00 \
+00 02 00 10 00 10 00 00 00 00 f0 00 00 00 00 00 \
+00 18 00 09 00 00 00 02 \
+00 01 00 10 00 02 00 00 00 00 00 00 00 00 00 00 \
+"], [0], [dnl
+OFPST_METER_CONFIG reply (OF1.3) (xid=0x2):
+meter=1 kbps burst bands=
+type=drop rate=65536 burst_size=1280
+type=dscp_remark rate=1048576 burst_size=61440 prec_level=0
+
+meter=2 kbps stats bands=
+type=drop rate=131072
+])
+AT_CLEANUP
+
+AT_SETUP([OFPST_METER reply - OF1.3])
+AT_KEYWORDS([ofp-print OFPT_STATS_REPLY])
+AT_CHECK([ovs-ofctl ofp-print "\
+04 13 00 90 00 00 00 02 00 09 00 00 00 00 00 00 \
+00 00 00 01 00 48 00 00 00 00 00 00 00 00 00 05 \
+00 00 00 00 00 00 10 00 00 00 00 00 00 02 30 00 \
+00 00 01 8a 0a 6e 23 44 \
+00 00 00 00 00 00 00 7e 00 00 00 00 00 00 34 33 \
+00 00 00 00 00 00 00 e7 00 00 00 00 00 00 94 2e \
+00 00 00 02 00 38 00 00 00 00 00 00 00 00 00 02 \
+00 00 00 00 00 00 02 00 00 00 00 00 00 00 30 00 \
+00 00 01 87 0a 23 6e 44 \
+00 00 00 00 00 00 00 2a 00 00 00 00 00 00 04 33 \
+"], [0], [dnl
+OFPST_METER reply (OF1.3) (xid=0x2):
+meter:1 flow_count:5 packet_in_count:4096 byte_in_count:143360 duration:394.174990148s bands:
+0: packet_count:126 byte_count:13363
+1: packet_count:231 byte_count:37934
+
+meter:2 flow_count:2 packet_in_count:512 byte_in_count:12288 duration:391.170094148s bands:
+0: packet_count:42 byte_count:1075
+])
+AT_CLEANUP
+
 AT_SETUP([OFPT_BARRIER_REQUEST - OF1.0])
 AT_KEYWORDS([ofp-print])
 AT_CHECK([ovs-ofctl ofp-print '01 12 00 08 00 00 00 01'], [0], [dnl
@@ -1596,6 +1827,31 @@ tcp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_ds
 ])
 AT_CLEANUP
 
+AT_SETUP([NXT_PACKET_IN, with hex output of packet data])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "\
+01 04 00 ba 00 00 00 00 00 00 23 20 00 00 00 11 \
+ff ff ff ff 00 40 01 07 00 00 00 00 00 00 00 09 \
+00 4e 00 00 00 00 00 00 00 00 00 02 00 01 00 01 \
+20 08 00 00 00 00 00 00 00 06 00 01 00 04 00 00 \
+00 01 00 01 02 04 00 00 00 02 00 01 04 04 00 00 \
+00 03 00 01 06 04 00 00 00 04 00 01 08 04 00 00 \
+00 05 80 00 05 10 5a 5a 5a 5a 5a 5a 5a 5a ff ff \
+ff ff ff ff ff ff 00 00 00 00 82 82 82 82 82 82 \
+80 81 81 81 81 81 81 00 00 50 08 00 45 00 00 28 \
+00 00 00 00 00 06 32 05 53 53 53 53 54 54 54 54 \
+00 55 00 56 00 00 00 00 00 00 00 00 50 00 00 00 \
+31 6d 00 00 00 00 00 00 00 00 \
+" 3], [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)
+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
+00000000  82 82 82 82 82 82 80 81-81 81 81 81 81 00 00 50
+00000010  08 00 45 00 00 28 00 00-00 00 00 06 32 05 53 53
+00000020  53 53 54 54 54 54 00 55-00 56 00 00 00 00 00 00
+00000030  00 00 50 00 00 00 31 6d-00 00 00 00 00 00 00 00
+])
+AT_CLEANUP
+
 AT_SETUP([NXT_SET_ASYNC_CONFIG])
 AT_KEYWORDS([ofp-print])
 AT_CHECK([ovs-ofctl ofp-print "\
@@ -1923,7 +2179,7 @@ AT_SETUP([NXST_FLOW_MONITOR reply])
 AT_KEYWORDS([ofp-print OFPT_STATS_REPLY])
 AT_CHECK([ovs-ofctl ofp-print "\
 01 11 00 40 00 00 00 04 ff ff 00 00 00 00 23 20 00 00 00 02 00 00 00 00 \
-00 20 00 01 00 04 80 00 00 05 00 10 00 06 01 00 12 34 56 78 9a bc de f0 \
+00 20 00 01 00 05 80 00 00 05 00 10 00 06 01 00 12 34 56 78 9a bc de f0 \
 00 00 00 02 00 01 00 00 \
 00 08 00 03 00 01 86 a0 \
 "], [0], [dnl
index 1fdbac3..88492cf 100644 (file)
@@ -62,7 +62,7 @@ in_port=15,reg0=0x10,reg1=0x11,reg2=0x12,reg3=0x13,reg4=0x14,reg5=0x15,reg6=0x16
 
 ])
 AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
-AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(90),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=128,frag=no),icmp(type=8,code=0)'], [0], [stdout])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(90),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=128,frag=no),icmp(type=8,code=0)'], [0], [stdout])
 AT_CHECK([tail -1 stdout], [0],
   [Datapath actions: 20,21,22,33
 ])
@@ -81,7 +81,7 @@ in_port=5            actions=pop:NXM_NX_REG3[[]],output:NXM_NX_REG3[[]]
 
 ])
 AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
-AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(90),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=128,frag=no),icmp(type=8,code=0)'], [0], [stdout])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(90),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=128,frag=no),icmp(type=8,code=0)'], [0], [stdout])
 AT_CHECK([tail -1 stdout], [0],
   [Datapath actions: 33,22,21,20
 ])
@@ -102,7 +102,7 @@ in_port=7 actions=load:0x110000ff->NXM_NX_REG0[[]],output:NXM_NX_REG0[[]]
 in_port=8 actions=1,9,load:9->NXM_OF_IN_PORT[[]],1,9
 ])
 AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
-AT_CHECK([ovs-appctl ofproto/trace br0 '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=128,frag=no),icmp(type=8,code=0)'], [0], [stdout])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy '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=128,frag=no),icmp(type=8,code=0)'], [0], [stdout])
 AT_CHECK([tail -1 stdout], [0],
   [Datapath actions: 9,55,10,55,66,11,77,88,9,1
 ])
@@ -117,24 +117,24 @@ 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
 ])
 AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
-AT_CHECK([ovs-appctl ofproto/trace br0 '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=2,frag=no)' -generate], [0], [stdout])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy '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=2,frag=no)' -generate], [0], [stdout])
 AT_CHECK([tail -3 stdout], [0],
   [Datapath actions: set(ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=1,frag=no)),2,4
 This flow is handled by the userspace slow path because it:
        - Sends "packet-in" messages to the OpenFlow controller.
 ])
-AT_CHECK([ovs-appctl ofproto/trace br0 '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=3,frag=no)'], [0], [stdout])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy '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=3,frag=no)'], [0], [stdout])
 AT_CHECK([tail -1 stdout], [0],
   [Datapath actions: set(ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=2,frag=no)),2,set(ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=1,frag=no)),3,4
 ])
-AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=10,tclass=0x70,hlimit=128,frag=no)'], [0], [stdout])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=10,tclass=0x70,hlimit=128,frag=no)'], [0], [stdout])
 AT_CHECK([tail -1 stdout], [0],
   [Datapath actions: set(ipv6(src=::1,dst=::2,label=0,proto=10,tclass=0x70,hlimit=127,frag=no)),2,set(ipv6(src=::1,dst=::2,label=0,proto=10,tclass=0x70,hlimit=126,frag=no)),3,4
 ])
 
 AT_CAPTURE_FILE([ofctl_monitor.log])
 AT_CHECK([ovs-ofctl monitor br0 65534 invalid_ttl --detach --no-chdir --pidfile 2> ofctl_monitor.log])
-AT_CHECK([ovs-appctl ofproto/trace br0 '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=2,frag=no)' -generate], [0], [stdout])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy '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=2,frag=no)' -generate], [0], [stdout])
 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)
@@ -174,7 +174,7 @@ AT_CHECK([ovs-vsctl -- \
         --id=@newqos create QoS type=linux-htb queues=1=@q1,2=@q2 --\
         --id=@q1 create Queue dscp=1 --\
         --id=@q2 create Queue dscp=2], [0], [ignore])
-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([ovs-appctl ofproto/trace ovs-dummy '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
 100,dnl
@@ -204,7 +204,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(100),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 ovs-dummy '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
@@ -214,7 +214,7 @@ AT_CHECK([tail -1 stdout \
 7
 ])
 
-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([ovs-appctl ofproto/trace ovs-dummy '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
 100
@@ -224,7 +224,7 @@ AT_CHECK([tail -1 stdout \
 7
 ])
 
-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([ovs-appctl ofproto/trace ovs-dummy '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
 1
@@ -235,12 +235,12 @@ AT_CHECK([tail -1 stdout \
 7
 ])
 
-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([ovs-appctl ofproto/trace ovs-dummy '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: 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([ovs-appctl ofproto/trace ovs-dummy '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(skb_priority(0x1)),100,1,2,set(skb_priority(0x2)),3,set(skb_priority(0x1)),6,7
 ])
@@ -760,7 +760,7 @@ do
   echo "----------------------------------------------------------------------"
   echo "in_port=$in_port vlan=$vlan pcp=$pcp"
 
-  AT_CHECK([ovs-appctl ofproto/trace br0 "$flow"], [0], [stdout])
+  AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow"], [0], [stdout])
   actual=`tail -1 stdout | sed 's/Datapath actions: //'`
 
   AT_CHECK([ovs-dpctl normalize-actions "$flow" "$expected"], [0], [stdout])
@@ -804,9 +804,14 @@ do
   AT_CHECK([ovs-ofctl set-frags br0 $mode])
   for type in no first later; do
     eval flow=\$${type}_flow exp_output=\$$type
-    AT_CHECK([ovs-appctl ofproto/trace br0 "$flow"], [0], [stdout])
-    AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: $exp_output
-])
+    printf "\n%s\n" "----$mode $type-----"
+    AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow"], [0], [stdout])
+    : > expout
+    if test $mode = drop && test $type != no; then
+        echo 'Packets dropped because they are IP fragments and the fragment handling mode is "drop".' >> expout
+    fi
+    echo "Datapath actions: $exp_output" >> expout
+    AT_CHECK([grep 'IP fragments' stdout; tail -1 stdout], [0], [expout])
   done
 done
 OVS_VSWITCHD_STOP
@@ -821,15 +826,15 @@ in_port=2 actions=output:12,resubmit:1,output:12
 in_port=3 actions=output:13,resubmit:2,output:14
 ])
 AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
-AT_CHECK([ovs-appctl ofproto/trace br0 '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=128,frag=no),icmp(type=8,code=0)'], [0], [stdout])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy '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=128,frag=no),icmp(type=8,code=0)'], [0], [stdout])
 AT_CHECK([tail -1 stdout], [0],
   [Datapath actions: 10
 ])
-AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(2),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=128,frag=no),icmp(type=8,code=0)'], [0], [stdout])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),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=128,frag=no),icmp(type=8,code=0)'], [0], [stdout])
 AT_CHECK([tail -1 stdout], [0],
   [Datapath actions: 12,10
 ])
-AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(3),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=128,frag=no),icmp(type=8,code=0)'], [0], [stdout])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(3),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=128,frag=no),icmp(type=8,code=0)'], [0], [stdout])
 AT_CHECK([tail -1 stdout], [0],
   [Datapath actions: 13,12,10
 ])
@@ -852,13 +857,13 @@ in_port=2 actions=output:1
 AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
 
 flow="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=128,frag=no),icmp(type=8,code=0)"
-AT_CHECK([ovs-appctl ofproto/trace br0 "$flow"], [0], [stdout])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow"], [0], [stdout])
 AT_CHECK_UNQUOTED([tail -1 stdout], [0],
   [Datapath actions: 2,3
 ])
 
 flow="in_port(2),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=128,frag=no),icmp(type=8,code=0)"
-AT_CHECK([ovs-appctl ofproto/trace br0 "$flow"], [0], [stdout])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow"], [0], [stdout])
 AT_CHECK_UNQUOTED([tail -1 stdout], [0],
   [Datapath actions: 1,3
 ])
@@ -882,13 +887,13 @@ in_port=2 actions=output:1
 AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
 
 flow="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=128,frag=no),icmp(type=8,code=0)"
-AT_CHECK([ovs-appctl ofproto/trace br0 "$flow"], [0], [stdout])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow"], [0], [stdout])
 AT_CHECK_UNQUOTED([tail -1 stdout], [0],
   [Datapath actions: 2,3
 ])
 
 flow="in_port(2),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=128,frag=no),icmp(type=8,code=0)"
-AT_CHECK([ovs-appctl ofproto/trace br0 "$flow"], [0], [stdout])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow"], [0], [stdout])
 AT_CHECK_UNQUOTED([tail -1 stdout], [0],
   [Datapath actions: 1
 ])
@@ -931,13 +936,13 @@ in_port=2 actions=output:1
 AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
 
 flow="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=128,frag=no),icmp(type=8,code=0)"
-AT_CHECK([ovs-appctl ofproto/trace br0 "$flow"], [0], [stdout])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow"], [0], [stdout])
 AT_CHECK_UNQUOTED([tail -1 stdout], [0],
   [Datapath actions: 2,3
 ])
 
 flow="in_port(2),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=128,frag=no),icmp(type=8,code=0)"
-AT_CHECK([ovs-appctl ofproto/trace br0 "$flow"], [0], [stdout])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow"], [0], [stdout])
 AT_CHECK_UNQUOTED([tail -1 stdout], [0],
   [Datapath actions: 1
 ])
@@ -960,19 +965,19 @@ in_port=1, actions=output:2
 AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
 
 flow="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=128,frag=no),icmp(type=8,code=0)"
-AT_CHECK([ovs-appctl ofproto/trace br0 "$flow"], [0], [stdout])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow"], [0], [stdout])
 AT_CHECK_UNQUOTED([tail -1 stdout], [0],
   [Datapath actions: 2
 ])
 
 flow="in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x8100),vlan(vid=10,pcp=0),encap(eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0))"
-AT_CHECK([ovs-appctl ofproto/trace br0 "$flow"], [0], [stdout])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow"], [0], [stdout])
 AT_CHECK_UNQUOTED([tail -1 stdout], [0],
   [Datapath actions: 2
 ])
 
 flow="in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x8100),vlan(vid=11,pcp=0),encap(eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0))"
-AT_CHECK([ovs-appctl ofproto/trace br0 "$flow"], [0], [stdout])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow"], [0], [stdout])
 AT_CHECK_UNQUOTED([tail -1 stdout], [0],
   [Datapath actions: 2,3
 ])
@@ -996,13 +1001,13 @@ in_port=2 actions=output:1
 AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
 
 flow="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=128,frag=no),icmp(type=8,code=0)"
-AT_CHECK([ovs-appctl ofproto/trace br0 "$flow"], [0], [stdout])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow"], [0], [stdout])
 AT_CHECK_UNQUOTED([tail -1 stdout], [0],
   [Datapath actions: push_vlan(vid=17,pcp=0),2,pop_vlan,3
 ])
 
 flow="in_port(2),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=128,frag=no),icmp(type=8,code=0)"
-AT_CHECK([ovs-appctl ofproto/trace br0 "$flow"], [0], [stdout])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow"], [0], [stdout])
 AT_CHECK_UNQUOTED([tail -1 stdout], [0],
   [Datapath actions: 1,3
 ])
@@ -1024,7 +1029,7 @@ in_port=2 actions=mod_vlan_vid:17,output:1
 AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
 
 flow="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=128,frag=no),icmp(type=8,code=0)"
-AT_CHECK([ovs-appctl ofproto/trace br0 "$flow"], [0], [stdout])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow"], [0], [stdout])
 actual=`tail -1 stdout | sed 's/Datapath actions: //'`
 
 expected="2,push_vlan(vid=12,pcp=0),1,2,100"
@@ -1033,7 +1038,7 @@ mv stdout expout
 AT_CHECK([ovs-dpctl normalize-actions "$flow" "$actual"], [0], [expout])
 
 flow="in_port(2),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=128,frag=no),icmp(type=8,code=0)"
-AT_CHECK([ovs-appctl ofproto/trace br0 "$flow"], [0], [stdout])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$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),1,2,100"
@@ -1044,6 +1049,262 @@ AT_CHECK([ovs-dpctl normalize-actions "$flow" "$actual"], [0], [expout])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
+# Two testcases below are for the ofproto/trace command
+# The first one tests all correct syntax:
+# ofproto/trace [dp_name] odp_flow [-generate|packet]
+# ofproto/trace br_name br_flow [-generate|packet]
+AT_SETUP([ofproto-dpif - ofproto/trace command 1])
+OVS_VSWITCHD_START([set bridge br0 fail-mode=standalone])
+ADD_OF_PORTS([br0], 1, 2, 3)
+
+AT_DATA([flows.txt], [dnl
+in_port=1 actions=output:2
+in_port=2 actions=output:1
+])
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+
+odp_flow="in_port(1)"
+br_flow="in_port=1"
+# Test command: ofproto/trace odp_flow
+AT_CHECK([ovs-appctl ofproto/trace "$odp_flow"], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0], [dnl
+Datapath actions: 2
+])
+
+# Test command: ofproto/trace dp_name odp_flow
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$odp_flow"], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0], [dnl
+Datapath actions: 2
+])
+# Test commmand: ofproto/trace br_name br_flow
+AT_CHECK([ovs-appctl ofproto/trace br0 "$br_flow"], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0], [dnl
+Datapath actions: 2
+])
+
+# Delete the inserted flows
+AT_CHECK([ovs-ofctl del-flows br0 "in_port=1"], [0], [stdout])
+AT_CHECK([ovs-ofctl del-flows br0 "in_port=2"], [0], [stdout])
+
+# This section beflow tests the [-generate] option
+odp_flow="in_port(3),eth(src=50:54:00:00:00:05,dst=ff:ff:ff:ff:ff:ff)"
+br_flow="arp,metadata=0,in_port=3,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=ff:ff:ff:ff:ff:ff"
+
+# Test command: ofproto/trace odp_flow
+AT_CHECK([ovs-appctl ofproto/trace "$odp_flow"], [0], [stdout])
+# Check for no MAC learning entry
+AT_CHECK_UNQUOTED([ovs-appctl fdb/show br0 | sed 's/[[0-9]]\{1,\}$/?/'], [0], [dnl
+ port  VLAN  MAC                Age
+])
+
+# Test command: ofproto/trace br_name br_flow
+AT_CHECK([ovs-appctl ofproto/trace br0 "$br_flow"], [0], [stdout])
+# Check for no MAC learning entry
+AT_CHECK_UNQUOTED([ovs-appctl fdb/show br0 | sed 's/[[0-9]]\{1,\}$/?/'], [0], [dnl
+ port  VLAN  MAC                Age
+])
+
+# Test command: ofproto/trace odp_flow -generate
+AT_CHECK([ovs-appctl ofproto/trace "$odp_flow" -generate], [0], [stdout])
+# Check for the MAC learning entry
+AT_CHECK_UNQUOTED([ovs-appctl fdb/show br0 | sed 's/[[0-9]]\{1,\}$/?/'], [0], [dnl
+ port  VLAN  MAC                Age
+    3     0  50:54:00:00:00:05    ?
+])
+
+# Test command: ofproto/trace dp_name odp_flow -generate
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy \
+  "in_port(1),eth(src=50:54:00:00:00:06,dst=50:54:00:00:00:05)" \
+  -generate], [0], [stdout])
+# Check for both MAC learning entries
+AT_CHECK_UNQUOTED([ovs-appctl fdb/show br0 | sed 's/[[0-9]]\{1,\}$/?/'], [0], [dnl
+ port  VLAN  MAC                Age
+    3     0  50:54:00:00:00:05    ?
+    1     0  50:54:00:00:00:06    ?
+])
+
+# Test command: ofproto/trace br_name br_flow -generate
+AT_CHECK([ovs-appctl ofproto/trace br0 \
+  "in_port=2,dl_src=50:54:00:00:00:07,dl_dst=50:54:00:00:00:06" \
+  -generate], [0], [stdout])
+# Check for both MAC learning entries.
+AT_CHECK_UNQUOTED([ovs-appctl fdb/show br0 | sed 's/[[0-9]]\{1,\}$/?/'], [0], [dnl
+ port  VLAN  MAC                Age
+    3     0  50:54:00:00:00:05    ?
+    1     0  50:54:00:00:00:06    ?
+    2     0  50:54:00:00:00:07    ?
+])
+
+# This section beflow tests the [packet] option
+# The ovs-tcpundump of packets between port1 and port2
+pkt1to2="50540000000250540000000108064500001C000100004001F98CC0A80001C0A800020800F7FF00000000"
+pkt2to1="50540000000150540000000208064500001C000100004001F98CC0A80002C0A800010800F7FF00000000"
+
+# Construct the MAC learning table
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy \
+  "in_port(1),eth(src=50:54:00:00:00:01,dst=ff:ff:ff:ff:ff:ff)" \
+  -generate], [0], [stdout])
+
+# Construct the MAC learning table
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy \
+  "in_port(2),eth(src=50:54:00:00:00:02,dst=ff:ff:ff:ff:ff:ff)" \
+  -generate], [0], [stdout])
+
+# Test command: ofproto/trace odp_flow packet
+AT_CHECK([ovs-appctl ofproto/trace \
+  "in_port(1),skb_priority(1),skb_mark(2)" "$pkt1to2"], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0], [dnl
+Datapath actions: 2
+])
+AT_CHECK([head -n 3 stdout], [0], [dnl
+Bridge: br0
+Packet: arp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=50:54:00:00:00:01,dl_dst=50:54:00:00:00:02,arp_spa=0.0.0.0,arp_tpa=0.0.0.0,arp_sha=00:00:00:00:00:00,arp_tha=00:00:00:00:00:00
+Flow: skb_mark=0x2,skb_priority=0x1,arp,metadata=0,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:01,dl_dst=50:54:00:00:00:02,arp_spa=0.0.0.0,arp_tpa=0.0.0.0,arp_sha=00:00:00:00:00:00,arp_tha=00:00:00:00:00:00
+])
+
+# Test command: ofproto/trace dp_name odp_flow packet
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy \
+  "in_port(1),skb_priority(1),skb_mark(2)" "$pkt1to2"], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0], [dnl
+Datapath actions: 2
+])
+AT_CHECK([head -n 3 stdout], [0], [dnl
+Bridge: br0
+Packet: arp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=50:54:00:00:00:01,dl_dst=50:54:00:00:00:02,arp_spa=0.0.0.0,arp_tpa=0.0.0.0,arp_sha=00:00:00:00:00:00,arp_tha=00:00:00:00:00:00
+Flow: skb_mark=0x2,skb_priority=0x1,arp,metadata=0,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:01,dl_dst=50:54:00:00:00:02,arp_spa=0.0.0.0,arp_tpa=0.0.0.0,arp_sha=00:00:00:00:00:00,arp_tha=00:00:00:00:00:00
+])
+
+# Test command: ofproto/trace br_name br_flow packet
+AT_CHECK([ovs-appctl ofproto/trace br0 \
+  "in_port=2,skb_priority=2,skb_mark=1" "$pkt2to1"], [0], [stdout],[stderr])
+AT_CHECK([tail -1 stdout], [0], [dnl
+Datapath actions: set(skb_mark(0)),1
+])
+AT_CHECK([head -n 2 stdout], [0], [dnl
+Packet: arp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=50:54:00:00:00:02,dl_dst=50:54:00:00:00:01,arp_spa=0.0.0.0,arp_tpa=0.0.0.0,arp_sha=00:00:00:00:00:00,arp_tha=00:00:00:00:00:00
+Flow: skb_mark=0x1,skb_priority=0x2,arp,metadata=0,in_port=2,vlan_tci=0x0000,dl_src=50:54:00:00:00:02,dl_dst=50:54:00:00:00:01,arp_spa=0.0.0.0,arp_tpa=0.0.0.0,arp_sha=00:00:00:00:00:00,arp_tha=00:00:00:00:00:00
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+# The second test tests the corner cases
+AT_SETUP([ofproto-dpif - ofproto/trace command 2])
+OVS_VSWITCHD_START
+ADD_OF_PORTS([br0], 1, 2)
+
+# Define flows
+odp_flow="in_port(1),eth(src=50:54:00:00:00:01,dst=50:54:00:00:00:02)"
+br_flow="in_port=1,dl_src=50:54:00:00:00:01,dl_dst=50:54:00:00:00:02"
+# Define options
+generate="-generate"
+pkt="50540000000250540000000108064500001C000100004001F98CC0A80001C0A800020800F7FF00000000"
+
+# Test incorrect command: ofproto/trace wrong_name odp_flow [-generate|packet]
+m4_foreach(
+[option],
+[[],
+["$generate"],
+["$pkt"]],
+[AT_CHECK([ovs-appctl ofproto/trace wrong_name "$odp_flow" option],
+  [2], [], [stderr])
+AT_CHECK([tail -2 stderr], [0], [dnl
+Cannot find datapath of this name
+ovs-appctl: ovs-vswitchd: server returned an error
+])])
+
+# Test incorrect command: ofproto/trace empty_string odp_flow [-generate|packet]
+m4_foreach(
+[option],
+[[],
+["$generate"],
+["$pkt"]],
+[AT_CHECK([ovs-appctl ofproto/trace "" "$odp_flow" option],
+  [2], [], [stderr])
+AT_CHECK([tail -2 stderr], [0], [dnl
+Cannot find datapath of this name
+ovs-appctl: ovs-vswitchd: server returned an error
+])])
+
+# Test incorrect command: ofproto/trace nonexist_dp_name odp_flow [-generate|packet]
+m4_foreach(
+[option],
+[[],
+["$generate"],
+["$pkt"]],
+[AT_CHECK([ovs-appctl ofproto/trace ovs-system "$odp_flow" option],
+  [2], [], [stderr])
+AT_CHECK([tail -2 stderr], [0], [dnl
+Cannot find datapath of this name
+ovs-appctl: ovs-vswitchd: server returned an error
+])])
+
+# Test incorrect command: ofproto/trace br_name odp_flow [-generate|packet]
+m4_foreach(
+[option],
+[[],
+["$generate"],
+["$pkt"]],
+[AT_CHECK([ovs-appctl ofproto/trace br0 "$odp_flow" option],
+  [2], [], [stderr])
+AT_CHECK([tail -2 stderr], [0], [dnl
+Cannot find datapath of this name
+ovs-appctl: ovs-vswitchd: server returned an error
+])])
+
+# Test incorrect command: ofproto/trace dp_name br_flow [-generate|packet]
+m4_foreach(
+[option],
+[[],
+["$generate"],
+["$pkt"]],
+[AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$br_flow" option],
+  [2], [], [stderr])
+AT_CHECK([tail -2 stderr], [0], [dnl
+Unknown bridge name
+ovs-appctl: ovs-vswitchd: server returned an error
+])])
+
+# Test incorrect command: ofproto/trace br_flow [-generate|packet]
+m4_foreach(
+[option],
+[[],
+["$generate"],
+["$pkt"]],
+[AT_CHECK([ovs-appctl ofproto/trace "$br_flow" option],
+  [2], [], [stderr])
+AT_CHECK([tail -2 stderr], [0], [dnl
+Must specify bridge name
+ovs-appctl: ovs-vswitchd: server returned an error
+])])
+
+# Test incorrect command: ofproto/trace dp_name odp_flow garbage_option
+AT_CHECK([ovs-appctl ofproto/trace \
+  ovs-dummy "$odp_flow" garbage_option],
+  [2], [stdout],[stderr])
+AT_CHECK([tail -2 stderr], [0], [dnl
+Trailing garbage in packet data
+ovs-appctl: ovs-vswitchd: server returned an error
+])
+
+# Test incorrect command: ofproto/trace with 4 arguments
+AT_CHECK([ovs-appctl ofproto/trace \
+  arg1, arg2, arg3, arg4], [2], [stdout],[stderr])
+AT_CHECK([tail -2 stderr], [0], [dnl
+"ofproto/trace" command takes at most 3 arguments
+ovs-appctl: ovs-vswitchd: server returned an error
+])
+
+# Test incorrect command: ofproto/trace with 0 argument
+AT_CHECK([ovs-appctl ofproto/trace ], [2], [stdout],[stderr])
+AT_CHECK([tail -2 stderr], [0], [dnl
+"ofproto/trace" command requires at least 1 arguments
+ovs-appctl: ovs-vswitchd: server returned an error
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
 m4_define([OFPROTO_TRACE],
   [flow="$2"
    AT_CHECK([ovs-appctl ofproto/trace $1 "$flow" $3], [0], [stdout])
@@ -1063,7 +1324,7 @@ arp='eth_type(0x0806),arp(sip=192.168.0.1,tip=192.168.0.2,op=1,sha=50:54:00:00:0
 
 # Trace an ARP packet arriving on p3, to create a MAC learning entry.
 OFPROTO_TRACE(
-  [br0],
+  [ovs-dummy],
   [in_port(3),eth(src=50:54:00:00:00:05,dst=ff:ff:ff:ff:ff:ff),$arp],
   [-generate],
   [1,2,100])
@@ -1077,7 +1338,7 @@ AT_CHECK_UNQUOTED([ovs-appctl fdb/show br0 | sed 's/[[0-9]]\{1,\}$/?/'], [0], [d
 # Trace a packet arrival destined for the learned MAC.
 # (This will also learn a MAC.)
 OFPROTO_TRACE(
-  [br0],
+  [ovs-dummy],
   [in_port(1),eth(src=50:54:00:00:00:06,dst=50:54:00:00:00:05),$arp],
   [-generate],
   [3])
@@ -1091,7 +1352,7 @@ AT_CHECK_UNQUOTED([ovs-appctl fdb/show br0 | sed 's/[[0-9]]\{1,\}$/?/'], [0], [d
 
 # Trace a packet arrival that updates the first learned MAC entry.
 OFPROTO_TRACE(
-  [br0],
+  [ovs-dummy],
   [in_port(2),eth(src=50:54:00:00:00:05,dst=ff:ff:ff:ff:ff:ff),$arp],
   [-generate],
   [1,3,100])
@@ -1107,18 +1368,17 @@ AT_CHECK_UNQUOTED([ovs-appctl fdb/show br0 | sed 's/[[0-9]]\{1,\}$/?/'], [0], [d
 AT_CHECK(
   [ovs-vsctl \
      -- add-br br1 \
-     -- set bridge br1 datapath-type=dummy \
-     -- add-port br1 p4 -- set interface p4 type=dummy \
-     -- add-port br1 p5 -- set interface p5 type=dummy])
+     -- set bridge br1 datapath-type=dummy])
+ADD_OF_PORTS([br1], 4, 5)
 
 # Trace some packet arrivals in br1 to create MAC learning entries there too.
 OFPROTO_TRACE(
-  [br1],
+  [ovs-dummy],
   [in_port(4),eth(src=50:54:00:00:00:06,dst=ff:ff:ff:ff:ff:ff),$arp],
   [-generate],
   [5,101])
 OFPROTO_TRACE(
-  [br1],
+  [ovs-dummy],
   [in_port(5),eth(src=50:54:00:00:00:07,dst=ff:ff:ff:ff:ff:ff),$arp],
   [-generate],
   [4,101])
@@ -1147,10 +1407,8 @@ 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])
+  [set bridge br0 fail-mode=standalone other-config:mac-table-size=10])
+ADD_OF_PORTS([br0], 1, 2, 3)
 
 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)'
 
@@ -1159,7 +1417,7 @@ 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],
+      [ovs-dummy],
       [in_port(3),eth(src=50:54:00:00:00:0$i,dst=ff:ff:ff:ff:ff:ff),$arp],
       [-generate],
       [1,2,100])
@@ -1184,7 +1442,7 @@ AT_CHECK_UNQUOTED([ovs-appctl fdb/show br0 | sed 's/ *[[0-9]]\{1,\}$//' | sort],
 
 # Trace another ARP packet on another MAC.
 OFPROTO_TRACE(
-  [br0],
+  [ovs-dummy],
   [in_port(3),eth(src=50:54:00:00:00:10,dst=ff:ff:ff:ff:ff:ff),$arp],
   [-generate],
   [1,2,100])
@@ -1730,8 +1988,7 @@ AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip], [0],
 # flow stats updates are mainly what implements the fin_timeout
 # feature, we warp forward a couple of times to ensure that flow stats
 # run before re-checking the flow table.)
-AT_CHECK([ovs-appctl netdev-dummy/receive br0 0021853763af0026b98cb0f908004500003c2e2440004006465dac11370dac11370b828b0016751e267b00000000a00216d017360000020405b40402080a2d25085f0000000001030307], [0], [success
-])
+AT_CHECK([ovs-appctl netdev-dummy/receive br0 0021853763af0026b98cb0f908004500003c2e2440004006465dac11370dac11370b828b0016751e267b00000000a00216d017360000020405b40402080a2d25085f0000000001030307])
 AT_CHECK([ovs-appctl time/warp 1000 && ovs-appctl time/warp 1000], [0], [warped
 warped
 ])
@@ -1740,8 +1997,7 @@ AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip], [0],
  n_packets=1, n_bytes=74, idle_timeout=60, actions=fin_timeout(idle_timeout=5)
 ])
 # Check that a TCP FIN packet does change the timeout.
-AT_CHECK([ovs-appctl netdev-dummy/receive br0 0021853763af0026b98cb0f90800451000342e3e40004006463bac11370dac11370b828b0016751e319dfc96399b801100717ae800000101080a2d250a9408579588], [0], [success
-])
+AT_CHECK([ovs-appctl netdev-dummy/receive br0 0021853763af0026b98cb0f90800451000342e3e40004006463bac11370dac11370b828b0016751e319dfc96399b801100717ae800000101080a2d250a9408579588])
 AT_CHECK([ovs-appctl time/warp 1000 && ovs-appctl time/warp 1000], [0], [warped
 warped
 ])
@@ -1770,29 +2026,16 @@ 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
-       flows: cur: 0, avg: 0.000, max: 0, life span: 0(ms)
-               overall avg: add rate: 0.000/min, del rate: 0.000/min
-       br0 65534/100: (dummy)
-       p1 1/1: (dummy)
-       p2 2/2: (dummy)
-br1 (dummy@ovs-dummy):
-       lookups: hit:0 missed:0
-       flows: cur: 0, avg: 0.000, max: 0, life span: 0(ms)
-               overall avg: add rate: 0.000/min, del rate: 0.000/min
-       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
-       flows: cur: 0, avg: 0.000, max: 0, life span: 0(ms)
-               overall avg: add rate: 0.000/min, del rate: 0.000/min
-       br0 65534/100: (dummy)
-       p1 1/1: (dummy)
-       p2 2/2: (dummy)
+dummy@ovs-dummy: hit:0 missed:0
+       flows: cur: 0, avg: 0, max: 0, life span: 0ms
+       overall avg: add rate: 0.000/min, del rate: 0.000/min
+       br0: hit:0 missed:0
+               br0 65534/100: (dummy)
+               p1 1/1: (dummy)
+               p2 2/2: (dummy)
+       br1: hit:0 missed:0
+               br1 65534/101: (dummy)
+               p3 3/3: (dummy)
 ])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
@@ -1803,12 +2046,9 @@ OVS_VSWITCHD_START([add-br br1 -- \
 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 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)'])
+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)'])
+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)'])
 
 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:userspace(pid=0,slow_path(controller))
@@ -1828,12 +2068,9 @@ OVS_VSWITCHD_START([add-br br1 -- \
 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 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)'])
+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)'])
+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)'])
 
 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:userspace(pid=0,slow_path(controller))
@@ -1864,6 +2101,8 @@ OVS_VSWITCHD_START([add-br br1 \
 ADD_OF_PORTS([br0], [2])
 ADD_OF_PORTS([br1], [3])
 
+AT_CHECK([ovs-appctl time/stop]) dnl Make life span averages consistent.
+
 AT_CHECK([ovs-ofctl add-flow br0 actions=LOCAL,output:1,output:2])
 AT_CHECK([ovs-ofctl add-flow br1 actions=LOCAL,output:1,output:3])
 
@@ -1880,20 +2119,17 @@ warped
 ])
 
 AT_CHECK([ovs-appctl dpif/show], [0], [dnl
-br0 (dummy@ovs-dummy):
-       lookups: hit:9 missed:1
-       flows: cur: 1, avg: 1.000, max: 1, life span: 0(ms)
-               overall avg: add rate: 0.000/min, del rate: 0.000/min
-       br0 65534/100: (dummy)
-       p2 2/2: (dummy)
-       pbr0 1/none: (patch: peer=pbr1)
-br1 (dummy@ovs-dummy):
-       lookups: hit:4 missed:1
-       flows: cur: 1, avg: 1.000, max: 1, life span: 0(ms)
-               overall avg: add rate: 0.000/min, del rate: 0.000/min
-       br1 65534/101: (dummy)
-       p3 3/3: (dummy)
-       pbr1 1/none: (patch: peer=pbr0)
+dummy@ovs-dummy: hit:13 missed:2
+       flows: cur: 2, avg: 1, max: 2, life span: 1250ms
+       overall avg: add rate: 0.000/min, del rate: 0.000/min
+       br0: hit:9 missed:1
+               br0 65534/100: (dummy)
+               p2 2/2: (dummy)
+               pbr0 1/none: (patch: peer=pbr1)
+       br1: hit:4 missed:1
+               br1 65534/101: (dummy)
+               p3 3/3: (dummy)
+               pbr1 1/none: (patch: peer=pbr0)
 ])
 
 AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_USED], [0], [dnl
@@ -1922,6 +2158,7 @@ AT_SETUP([ofproto-dpif - ovs-appctl dpif/show rates])
 OVS_VSWITCHD_START([set Bridge br0 fail-mode=secure])
 ADD_OF_PORTS([br0], 1, 2)
 
+AT_CHECK([ovs-appctl time/stop]) dnl Make life span averages consistent.
 AT_CHECK([ovs-ofctl add-flow br0 actions=LOCAL,output:1,output:2])
 
 for i in $(seq 1 61); do
@@ -1934,15 +2171,427 @@ AT_CHECK([ovs-appctl time/warp 10000], [0], [warped
 ])
 
 AT_CHECK([ovs-appctl dpif/show | sed 's/ 10[[0-9]]\{3\}(ms)$/ 10000(ms)/'], [0], [dnl
-br0 (dummy@ovs-dummy):
-       lookups: hit:0 missed:61
-       flows: cur: 0, avg: 1.000, max: 1, life span: 10000(ms)
-               hourly avg: add rate: 0.641/min, del rate: 0.641/min
-               overall avg: add rate: 1.000/min, del rate: 1.000/min
-       br0 65534/100: (dummy)
-       p1 1/1: (dummy)
-       p2 2/2: (dummy)
+dummy@ovs-dummy: hit:0 missed:61
+       flows: cur: 0, avg: 0, max: 1, life span: 1666ms
+       hourly avg: add rate: 0.641/min, del rate: 0.641/min
+       overall avg: add rate: 1.000/min, del rate: 1.000/min
+       br0: hit:0 missed:61
+               br0 65534/100: (dummy)
+               p1 1/1: (dummy)
+               p2 2/2: (dummy)
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto-dpif - port duration])
+OVS_VSWITCHD_START([set Bridge br0 protocols=OpenFlow13])
+ADD_OF_PORTS([br0], 1, 2)
+
+ovs-appctl time/warp 10000
+
+AT_CHECK([ovs-ofctl -O openflow13 dump-ports br0], [0], [stdout])
+AT_CHECK([sed 's/=[[0-9]][[0-9]]\(\.[[0-9]][[0-9]]*\)\{0,1\}s/=?s/' stdout], [0],
+[dnl
+OFPST_PORT reply (OF1.3) (xid=0x2): 3 ports
+  port  1: 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
+           duration=?s
+  port  2: 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
+           duration=?s
+  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
+           duration=?s
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+dnl ----------------------------------------------------------------------
+AT_BANNER([ofproto-dpif -- megaflows])
+
+# Strips out uninteresting parts of megaflow output, as well as parts
+# that vary from one run to another (e.g., timing and bond actions).
+m4_define([STRIP_XOUT], [[sed '
+    s/used:[0-9]*\.[0-9]*/used:0.0/
+    s/Datapath actions:.*/Datapath actions: <del>/
+' | sort]])
+
+AT_SETUP([ofproto-dpif megaflow - port classification])
+OVS_VSWITCHD_START
+ADD_OF_PORTS([br0], [1], [2])
+AT_DATA([flows.txt], [dnl
+table=0 in_port=1 actions=output(2)
+])
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),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)'])
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
+AT_CHECK([ovs-appctl dpif/dump-megaflows br0 | STRIP_XOUT], [0], [dnl
+skb_priority=0,ip,in_port=1,nw_frag=no, n_subfacets:2, used:0.0s, Datapath actions: <del>
 ])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto-dpif megaflow - L2 classification])
+OVS_VSWITCHD_START
+ADD_OF_PORTS([br0], [1], [2])
+AT_DATA([flows.txt], [dnl
+table=0 in_port=1,dl_src=50:54:00:00:00:09 actions=output(2)
+])
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),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)'])
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
+AT_CHECK([ovs-appctl dpif/dump-megaflows br0 | STRIP_XOUT], [0], [dnl
+skb_priority=0,ip,in_port=1,dl_src=50:54:00:00:00:09,nw_frag=no, n_subfacets:1, used:0.0s, Datapath actions: <del>
+skb_priority=0,ip,in_port=1,dl_src=50:54:00:00:00:0b,nw_frag=no, n_subfacets:1, used:0.0s, Datapath actions: <del>
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto-dpif megaflow - L3 classification])
+OVS_VSWITCHD_START
+ADD_OF_PORTS([br0], [1], [2])
+AT_DATA([flows.txt], [dnl
+table=0 in_port=1,icmp,nw_src=10.0.0.4 actions=output(2)
+])
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),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)'])
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
+AT_CHECK([ovs-appctl dpif/dump-megaflows br0 | STRIP_XOUT], [0], [dnl
+skb_priority=0,icmp,in_port=1,nw_src=10.0.0.2,nw_frag=no, n_subfacets:1, used:0.0s, Datapath actions: <del>
+skb_priority=0,icmp,in_port=1,nw_src=10.0.0.4,nw_frag=no, n_subfacets:1, used:0.0s, Datapath actions: <del>
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto-dpif megaflow - L4 classification])
+OVS_VSWITCHD_START
+ADD_OF_PORTS([br0], [1], [2])
+AT_DATA([flows.txt], [dnl
+table=0 in_port=1,icmp,icmp_type=8 actions=output(2)
+])
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),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)'])
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
+AT_CHECK([ovs-appctl dpif/dump-megaflows br0 | STRIP_XOUT], [0], [dnl
+skb_priority=0,icmp,in_port=1,nw_frag=no,icmp_type=8, n_subfacets:2, used:0.0s, Datapath actions: <del>
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto-dpif megaflow - normal])
+OVS_VSWITCHD_START
+ADD_OF_PORTS([br0], [1], [2])
+AT_CHECK([ovs-ofctl add-flow br0 action=normal])
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),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)'])
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
+AT_CHECK([ovs-appctl dpif/dump-megaflows br0 | STRIP_XOUT], [0], [dnl
+skb_priority=0,ip,in_port=1,vlan_tci=0x0000/0x1fff,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_frag=no, n_subfacets:1, used:0.0s, Datapath actions: <del>
+skb_priority=0,ip,in_port=1,vlan_tci=0x0000/0x1fff,dl_src=50:54:00:00:00:0b,dl_dst=50:54:00:00:00:0c,nw_frag=no, n_subfacets:1, used:0.0s, Datapath actions: <del>
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
 
+AT_SETUP([ofproto-dpif megaflow - mpls])
+OVS_VSWITCHD_START
+ADD_OF_PORTS([br0], [1], [2])
+AT_DATA([flows.txt], [dnl
+table=0 dl_src=50:54:00:00:00:09 actions=push_mpls:0x8847,2
+table=0 dl_src=50:54:00:00:00:0b actions=pop_mpls:0x0800,2
+])
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x8847),mpls(label=11,tc=3,ttl=64,bos=1)'])
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0a),eth_type(0x8847),mpls(label=11,tc=3,ttl=64,bos=1)'])
+AT_CHECK([ovs-appctl dpif/dump-megaflows br0 | STRIP_XOUT], [0], [dnl
+skb_priority=0,mpls,in_port=1,dl_src=50:54:00:00:00:09,mpls_label=11,mpls_tc=3,mpls_ttl=64,mpls_bos=1,nw_frag=no, n_subfacets:1, used:0.0s, Datapath actions: <del>
+skb_priority=0,mpls,in_port=1,dl_src=50:54:00:00:00:0b,mpls_label=11,mpls_tc=3,mpls_ttl=64,mpls_bos=1,nw_frag=no, n_subfacets:1, used:0.0s, Datapath actions: <del>
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto-dpif megaflow - netflow])
+OVS_VSWITCHD_START
+ADD_OF_PORTS([br0], [1], [2])
+
+dnl NetFlow configuration disables wildcarding relevant fields
+ON_EXIT([kill `cat test-netflow.pid`])
+AT_CHECK([test-netflow --log-file --detach --no-chdir --pidfile 0:127.0.0.1 > netflow.log], [0], [], [ignore])
+AT_CAPTURE_FILE([netflow.log])
+NETFLOW_PORT=`parse_listening_port < test-netflow.log`
+ovs-vsctl \
+   set Bridge br0 netflow=@nf -- \
+   --id=@nf create NetFlow targets=\"127.0.0.1:$NETFLOW_PORT\" \
+     engine_id=1 engine_type=2 active_timeout=30 add-id-to-interface=false
+
+AT_CHECK([ovs-ofctl add-flow br0 action=normal])
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),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)'])
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
+AT_CHECK([ovs-appctl dpif/dump-megaflows br0 | STRIP_XOUT], [0], [dnl
+skb_priority=0,icmp,in_port=1,vlan_tci=0x0000/0x1fff,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_src=10.0.0.2,nw_dst=10.0.0.1,nw_tos=0,nw_frag=no,icmp_type=8,icmp_code=0, n_subfacets:1, used:0.0s, Datapath actions: <del>
+skb_priority=0,icmp,in_port=1,vlan_tci=0x0000/0x1fff,dl_src=50:54:00:00:00:0b,dl_dst=50:54:00:00:00:0c,nw_src=10.0.0.4,nw_dst=10.0.0.3,nw_tos=0,nw_frag=no,icmp_type=8,icmp_code=0, n_subfacets:1, used:0.0s, Datapath actions: <del>
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto-dpif megaflow - normal, active-backup bonding])
+OVS_VSWITCHD_START(
+  [add-port br0 p1 -- set Interface p1 type=dummy ofport_request=1 -- \
+   add-bond br0 bond0 p2 p3 bond_mode=active-backup -- \
+   set interface p2 type=dummy ofport_request=2 -- \
+   set interface p3 type=dummy ofport_request=3])
+AT_CHECK([ovs-appctl netdev-dummy/set-admin-state up], 0, [OK
+])
+
+AT_CHECK([ovs-ofctl add-flow br0 action=normal])
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),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)'])
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
+AT_CHECK([ovs-appctl dpif/dump-megaflows br0 | STRIP_XOUT], [0], [dnl
+skb_priority=0,ip,in_port=1,vlan_tci=0x0000/0x1fff,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_frag=no, n_subfacets:1, used:0.0s, Datapath actions: <del>
+skb_priority=0,ip,in_port=1,vlan_tci=0x0000/0x1fff,dl_src=50:54:00:00:00:0b,dl_dst=50:54:00:00:00:0c,nw_frag=no, n_subfacets:1, used:0.0s, Datapath actions: <del>
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto-dpif megaflow - normal, balance-slb bonding])
+OVS_VSWITCHD_START(
+  [add-port br0 p1 -- set Interface p1 type=dummy ofport_request=1 -- \
+   add-bond br0 bond0 p2 p3 bond_mode=balance-slb -- \
+   set interface p2 type=dummy ofport_request=2 -- \
+   set interface p3 type=dummy ofport_request=3])
+AT_CHECK([ovs-appctl netdev-dummy/set-admin-state up], 0, [OK
+])
+
+AT_CHECK([ovs-ofctl add-flow br0 action=normal])
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),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)'])
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
+AT_CHECK([ovs-appctl dpif/dump-megaflows br0 | STRIP_XOUT], [0], [dnl
+skb_priority=0,ip,in_port=1,vlan_tci=0x0000/0x1fff,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_frag=no, n_subfacets:1, used:0.0s, Datapath actions: <del>
+skb_priority=0,ip,in_port=1,vlan_tci=0x0000/0x1fff,dl_src=50:54:00:00:00:0b,dl_dst=50:54:00:00:00:0c,nw_frag=no, n_subfacets:1, used:0.0s, Datapath actions: <del>
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto-dpif megaflow - normal, balance-tcp bonding])
+# Create bond0 on br0 with interfaces p0 and p1
+#    and bond1 on br1 with interfaces p2 and p3
+# with p0 patched to p2 and p1 patched to p3.
+OVS_VSWITCHD_START(
+  [add-bond br0 bond0 p0 p1 bond_mode=balance-tcp lacp=active \
+                            other-config:lacp-time=fast \
+                            other-config:bond-rebalance-interval=0 -- \
+   set interface p0 type=patch options:peer=p2 ofport_request=1 -- \
+   set interface p1 type=patch options:peer=p3 ofport_request=2 -- \
+   add-br br1 -- \
+   set bridge br1 other-config:hwaddr=aa:66:aa:66:00:00 -- \
+   set bridge br1 datapath-type=dummy other-config:datapath-id=1234 \
+                  fail-mode=secure -- \
+   add-bond br1 bond1 p2 p3 bond_mode=balance-tcp lacp=active \
+                            other-config:lacp-time=fast \
+                            other-config:bond-rebalance-interval=0 -- \
+   set interface p2 type=patch options:peer=p0 ofport_request=3 -- \
+   set interface p3 type=patch options:peer=p1 ofport_request=4 --])
+
+AT_CHECK([ovs-appctl netdev-dummy/set-admin-state up], 0, [OK
+])
+ADD_OF_PORTS([br0], [7])
+AT_CHECK([ovs-ofctl add-flow br0 action=normal])
+AT_CHECK([ovs-ofctl add-flow br1 action=normal])
+ovs-appctl time/warp 5000
+AT_CHECK([ovs-appctl netdev-dummy/receive p7 'in_port(1),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)'])
+AT_CHECK([ovs-appctl netdev-dummy/receive p7 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
+
+AT_CHECK([ovs-appctl dpif/dump-megaflows br0 | STRIP_XOUT], [0], [dnl
+skb_priority=0,icmp,in_port=7,vlan_tci=0x0000/0x1fff,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_src=10.0.0.2,nw_dst=10.0.0.1,nw_frag=no,icmp_type=8,icmp_code=0, n_subfacets:1, used:0.0s, Datapath actions: <del>
+skb_priority=0,icmp,in_port=7,vlan_tci=0x0000/0x1fff,dl_src=50:54:00:00:00:0b,dl_dst=50:54:00:00:00:0c,nw_src=10.0.0.4,nw_dst=10.0.0.3,nw_frag=no,icmp_type=8,icmp_code=0, n_subfacets:1, used:0.0s, Datapath actions: <del>
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto-dpif megaflow - resubmit port action])
+OVS_VSWITCHD_START
+ADD_OF_PORTS([br0], [1], [2])
+AT_DATA([flows.txt], [dnl
+table=0 in_port=1,ip actions=resubmit(90)
+table=0 in_port=90,dl_src=50:54:00:00:00:09 actions=output(2)
+])
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),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)'])
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
+AT_CHECK([ovs-appctl dpif/dump-megaflows br0 | STRIP_XOUT], [0], [dnl
+skb_priority=0,ip,in_port=1,dl_src=50:54:00:00:00:09,nw_frag=no, n_subfacets:1, used:0.0s, Datapath actions: <del>
+skb_priority=0,ip,in_port=1,dl_src=50:54:00:00:00:0b,nw_frag=no, n_subfacets:1, used:0.0s, Datapath actions: <del>
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto-dpif megaflow - resubmit table action])
+OVS_VSWITCHD_START
+ADD_OF_PORTS([br0], [1], [2])
+AT_DATA([flows.txt], [dnl
+table=0 in_port=1,ip actions=resubmit(,1)
+table=1 dl_src=50:54:00:00:00:09 actions=output(2)
+])
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),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)'])
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=
+1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
+AT_CHECK([ovs-appctl dpif/dump-megaflows br0 | STRIP_XOUT], [0], [dnl
+skb_priority=0,ip,in_port=1,dl_src=50:54:00:00:00:09,nw_frag=no, n_subfacets:1, used:0.0s, Datapath actions: <del>
+skb_priority=0,ip,in_port=1,dl_src=50:54:00:00:00:0b,nw_frag=no, n_subfacets:1, used:0.0s, Datapath actions: <del>
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto-dpif megaflow - goto_table action])
+OVS_VSWITCHD_START
+ADD_OF_PORTS([br0], [1], [2])
+AT_DATA([flows.txt], [dnl
+table=0 in_port=1,ip actions=goto_table(1)
+table=1 dl_src=50:54:00:00:00:09 actions=output(2)
+])
+AT_CHECK([ovs-ofctl -O OpenFlow12 add-flows br0 flows.txt])
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),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)'])
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
+AT_CHECK([ovs-appctl dpif/dump-megaflows br0 | STRIP_XOUT], [0], [dnl
+skb_priority=0,ip,in_port=1,dl_src=50:54:00:00:00:09,nw_frag=no, n_subfacets:1, used:0.0s, Datapath actions: <del>
+skb_priority=0,ip,in_port=1,dl_src=50:54:00:00:00:0b,nw_frag=no, n_subfacets:1, used:0.0s, Datapath actions: <del>
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto-dpif megaflow - mirroring, select_all])
+OVS_VSWITCHD_START
+ADD_OF_PORTS([br0], [1], [2], [3])
+ovs-vsctl \
+        set Bridge br0 mirrors=@m --\
+        --id=@p3 get Port p3 --\
+        --id=@m create Mirror name=mymirror select_all=true output_port=@p3
+
+AT_DATA([flows.txt], [dnl
+in_port=1 actions=output:2
+])
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),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)'])
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
+AT_CHECK([ovs-appctl dpif/dump-megaflows br0 | STRIP_XOUT], [0], [dnl
+skb_priority=0,ip,in_port=1,nw_frag=no, n_subfacets:2, used:0.0s, Datapath actions: <del>
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto-dpif megaflow - mirroring, select_vlan])
+OVS_VSWITCHD_START
+ADD_OF_PORTS([br0], [1], [2], [3])
+ovs-vsctl \
+        set Bridge br0 mirrors=@m --\
+        --id=@p2 get Port p2 -- --id=@p3 get Port p3 --\
+        --id=@m create Mirror name=mymirror select_all=true select_vlan=11 output_port=@p3
+
+AT_DATA([flows.txt], [dnl
+in_port=1 actions=output:2
+])
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x8100),vlan(vid=11,pcp=7),encap(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))'])
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
+AT_CHECK([ovs-appctl dpif/dump-megaflows br0 | STRIP_XOUT], [0], [dnl
+skb_priority=0,ip,in_port=1,dl_vlan=11,nw_frag=no, n_subfacets:1, used:0.0s, Datapath actions: <del>
+skb_priority=0,ip,in_port=1,vlan_tci=0x0000/0x1fff,nw_frag=no, n_subfacets:1, used:0.0s, Datapath actions: <del>
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto-dpif megaflow - move action])
+OVS_VSWITCHD_START
+ADD_OF_PORTS([br0], [1], [2])
+AT_DATA([flows.txt], [dnl
+table=0 in_port=1 ip,actions=move:NXM_OF_IP_SRC[[]]->NXM_NX_REG0[[]],resubmit(90)
+table=0 in_port=90 ip,actions=move:NXM_NX_REG0[[]]->NXM_NX_REG1[[]],resubmit(91)
+table=0 in_port=91 reg0=0x0a000002,actions=output(2)
+])
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),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)'])
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
+AT_CHECK([ovs-appctl dpif/dump-megaflows br0 | STRIP_XOUT], [0], [dnl
+skb_priority=0,ip,in_port=1,nw_src=10.0.0.2,nw_frag=no, n_subfacets:1, used:0.0s, Datapath actions: <del>
+skb_priority=0,ip,in_port=1,nw_src=10.0.0.4,nw_frag=no, n_subfacets:1, used:0.0s, Datapath actions: <del>
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto-dpif megaflow - push action])
+OVS_VSWITCHD_START
+ADD_OF_PORTS([br0], [1], [2])
+AT_DATA([flows.txt], [dnl
+table=0 in_port=1 ip,actions=push:NXM_OF_IP_SRC[[]],output(2)
+])
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),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)'])
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
+AT_CHECK([ovs-appctl dpif/dump-megaflows br0 | STRIP_XOUT], [0], [dnl
+skb_priority=0,ip,in_port=1,nw_src=10.0.0.2,nw_frag=no, n_subfacets:1, used:0.0s, Datapath actions: <del>
+skb_priority=0,ip,in_port=1,nw_src=10.0.0.4,nw_frag=no, n_subfacets:1, used:0.0s, Datapath actions: <del>
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto-dpif megaflow - learning])
+OVS_VSWITCHD_START
+ADD_OF_PORTS([br0], [1], [2])
+AT_DATA([flows.txt], [dnl
+table=0 in_port=1 actions=load:2->NXM_NX_REG0[[0..15]],learn(table=1,priority=65535,NXM_OF_ETH_SRC[[]],NXM_OF_VLAN_TCI[[0..11]],output:NXM_NX_REG0[[0..15]]),output:2
+])
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),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)'])
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
+dnl The original flow is missing due to a revalidation.
+AT_CHECK([ovs-appctl dpif/dump-megaflows br0 | STRIP_XOUT], [0], [dnl
+skb_priority=0,ip,in_port=1,vlan_tci=0x0000/0x0fff,dl_src=50:54:00:00:00:09,nw_frag=no, n_subfacets:1, used:0.0s, Datapath actions: <del>
+skb_priority=0,ip,in_port=1,vlan_tci=0x0000/0x0fff,dl_src=50:54:00:00:00:0b,nw_frag=no, n_subfacets:1, used:0.0s, Datapath actions: <del>
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto-dpif megaflow - tunnels])
+OVS_VSWITCHD_START(
+  [add-port br0 p1 -- set Interface p1 type=dummy ofport_request=1 \
+     ofport_request=1 -- \
+   add-port br0 p2 -- set Interface p2 type=gre options:remote_ip=1.1.1.1 \
+     ofport_request=2 options:key=flow -- \
+   add-port br0 p3 -- set Interface p3 type=dummy ofport_request=3 \
+     ofport_request=3 -- \
+   add-port br0 p4 -- set Interface p4 type=gre options:remote_ip=1.1.1.2 \
+     options:tos=inherit options:ttl=inherit ofport_request=4 options:key=flow])
+AT_DATA([flows.txt], [dnl
+in_port=1,actions=output(2)
+in_port=3,actions=output(4)
+])
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+dnl ECN bits are always copied out, but don't use 0x3 (CE), since that
+dnl will cause the packet to be dropped.
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),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=0xfd,ttl=128,frag=no),icmp(type=8,code=0)'])
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0x1,ttl=64,frag=no),icmp(type=8,code=0)'])
+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=0xfd,ttl=128,frag=no),icmp(type=8,code=0)'])
+AT_CHECK([ovs-appctl netdev-dummy/receive p3 'in_port(3),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0x1,ttl=64,frag=no),icmp(type=8,code=0)'])
+AT_CHECK([ovs-appctl dpif/dump-megaflows br0 | STRIP_XOUT], [0], [dnl
+skb_priority=0,ip,in_port=1,nw_ecn=1,nw_frag=no, n_subfacets:2, used:0.0s, Datapath actions: <del>
+skb_priority=0,ip,in_port=3,nw_tos=0,nw_ecn=1,nw_ttl=64,nw_frag=no, n_subfacets:1, used:0.0s, Datapath actions: <del>
+skb_priority=0,ip,in_port=3,nw_tos=252,nw_ecn=1,nw_ttl=128,nw_frag=no, n_subfacets:1, used:0.0s, Datapath actions: <del>
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto-dpif megaflow - dec_ttl])
+OVS_VSWITCHD_START
+ADD_OF_PORTS([br0], [1], [2])
+AT_DATA([flows.txt], [dnl
+table=0 in_port=1,icmp,nw_src=10.0.0.4 actions=dec_ttl,output(2)
+])
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),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)'])
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
+AT_CHECK([ovs-appctl dpif/dump-megaflows br0 | STRIP_XOUT], [0], [dnl
+skb_priority=0,icmp,in_port=1,nw_src=10.0.0.2,nw_frag=no, n_subfacets:1, used:0.0s, Datapath actions: <del>
+skb_priority=0,icmp,in_port=1,nw_src=10.0.0.4,nw_dst=10.0.0.3,nw_tos=0,nw_ecn=0,nw_ttl=64, n_subfacets:1, used:0.0s, Datapath actions: <del>
+])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
index e8ac9d0..d034105 100644 (file)
@@ -38,7 +38,7 @@ 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])
+# OVS_VSWITCHD_START([vsctl-args], [vsctl-output], [=override])
 #
 # Creates a database and starts ovsdb-server, starts ovs-vswitchd
 # connected to that database, calls ovs-vsctl to create a bridge named
@@ -46,6 +46,11 @@ m4_define([TESTABLE_LOG], [-vPATTERN:ANY:'%c|%p|%m'])
 # commands to ovs-vsctl.  If 'vsctl-args' causes ovs-vsctl to provide
 # output (e.g. because it includes "create" commands) then 'vsctl-output'
 # specifies the expected output after filtering through uuidfilt.pl.
+#
+# If a test needs to use "system" devices (as dummies), then specify
+# =override (literally) as the third argument.  Otherwise, system devices
+# won't work at all (which makes sense because tests should not access a
+# system's real Ethernet devices).
 m4_define([OVS_VSWITCHD_START],
   [OVS_RUNDIR=`pwd`; export OVS_RUNDIR
    OVS_LOGDIR=`pwd`; export OVS_LOGDIR
@@ -68,7 +73,7 @@ m4_define([OVS_VSWITCHD_START],
    AT_CHECK([ovs-vsctl --no-wait init])
 
    dnl Start ovs-vswitchd.
-   AT_CHECK([ovs-vswitchd --detach --no-chdir --pidfile --enable-dummy --disable-system --log-file -vvconn -vofproto_dpif], [0], [], [stderr])
+   AT_CHECK([ovs-vswitchd --detach --no-chdir --pidfile --enable-dummy$3 --disable-system --log-file -vvconn -vofproto_dpif], [0], [], [stderr])
    AT_CAPTURE_FILE([ovs-vswitchd.log])
    AT_CHECK([[sed < stderr '
 /vlog|INFO|opened log file/d
index 166b03e..b3823a3 100644 (file)
@@ -1659,7 +1659,7 @@ if test -e /proc/sys/net/core/rmem_max; then
     # Linux
     rmem_max=`cat /proc/sys/net/core/rmem_max`
 elif rmem_max=`sysctl -n net.inet.tcp.recvbuf_max 2>/dev/null`; then
-    : # FreeBSD
+    : # FreeBSD, NetBSD
 else
     # Don't know how to get maximum socket receive buffer on this OS
     AT_SKIP_IF([:])
index 6198677..8133f75 100644 (file)
@@ -1,9 +1,111 @@
 AT_BANNER([ovs-ofctl])
 
+AT_SETUP([ovs-ofctl parse-flows choice of protocol])
+# This doesn't cover some potential vlan_tci test cases.
+for test_case in \
+    'tun_id=0                                    NXM,OXM' \
+    'tun_src=1.2.3.4                             NXM,OXM' \
+    'tun_dst=1.2.3.4                             NXM,OXM' \
+    'tun_flags=0                                 none' \
+    'tun_tos=0                                   none' \
+    'tun_ttl=0                                   none' \
+    'metadata=0                                  NXM,OXM' \
+    'in_port=1                                   any' \
+    'skb_priority=0                              none' \
+    'skb_mark=1                                  none' \
+    'reg0=0                                      NXM,OXM' \
+    'reg1=1                                      NXM,OXM' \
+    'reg2=2                                      NXM,OXM' \
+    'reg3=3                                      NXM,OXM' \
+    'reg4=4                                      NXM,OXM' \
+    'reg5=5                                      NXM,OXM' \
+    'reg6=6                                      NXM,OXM' \
+    'reg7=7                                      NXM,OXM' \
+    'dl_src=00:11:22:33:44:55                    any' \
+    'dl_src=00:11:22:33:44:55/00:ff:ff:ff:ff:ff  NXM,OXM' \
+    'dl_dst=00:11:22:33:44:55                    any' \
+    'dl_dst=00:11:22:33:44:55/00:ff:ff:ff:ff:ff  NXM,OXM' \
+    'dl_type=0x1234                              any' \
+    'dl_type=0x0800                              any' \
+    'dl_type=0x0806                              any' \
+    'dl_type=0x86dd                              any' \
+    'vlan_tci=0                                  any' \
+    'vlan_tci=0x1009                             any' \
+    'dl_vlan=9                                   any' \
+    'vlan_vid=11                                 any' \
+    'dl_vlan_pcp=6                               any' \
+    'vlan_pcp=5                                  any' \
+    'mpls,mpls_label=5                           NXM,OXM' \
+    'mpls,mpls_tc=1                              NXM,OXM' \
+    'mpls,mpls_bos=0                             NXM,OXM' \
+    'ip,ip_src=1.2.3.4                           any' \
+    'ip,ip_src=192.168.0.0/24                    any' \
+    'ip,ip_src=192.0.168.0/255.0.255.0           NXM,OXM' \
+    'ip,ip_dst=1.2.3.4                           any' \
+    'ip,ip_dst=192.168.0.0/24                    any' \
+    'ip,ip_dst=192.0.168.0/255.0.255.0           NXM,OXM' \
+    'ipv6,ipv6_src=::1                           NXM,OXM' \
+    'ipv6,ipv6_dst=::1                           NXM,OXM' \
+    'ipv6,ipv6_label=5                           NXM,OXM' \
+    'ip,nw_proto=1                               any' \
+    'ipv6,nw_proto=1                             NXM,OXM' \
+    'ip,nw_tos=0xf0                              any' \
+    'ipv6,nw_tos=0xf0                            NXM,OXM' \
+    'ip,nw_tos_shifted=0x3c                      any' \
+    'ipv6,nw_tos_shifted=0x3c                    NXM,OXM' \
+    'ip,nw_ecn=1                                 NXM,OXM' \
+    'ipv6,nw_ecn=1                               NXM,OXM' \
+    'ip,nw_ttl=5                                 NXM,OXM' \
+    'ipv6,nw_ttl=5                               NXM,OXM' \
+    'ip,ip_frag=no                               NXM,OXM' \
+    'ipv6,ip_frag=no                             NXM,OXM' \
+    'arp,arp_op=0                                any' \
+    'arp,arp_spa=1.2.3.4                         any' \
+    'arp,arp_tpa=1.2.3.4                         any' \
+    'arp,arp_sha=00:11:22:33:44:55               NXM,OXM' \
+    'arp,arp_tha=00:11:22:33:44:55               NXM,OXM' \
+    'tcp,tcp_src=80                              any' \
+    'tcp,tcp_src=0x1000/0x1000                   NXM,OXM' \
+    'tcp6,tcp_src=80                             NXM,OXM' \
+    'tcp6,tcp_src=0x1000/0x1000                  NXM,OXM' \
+    'tcp,tcp_dst=80                              any' \
+    'tcp,tcp_dst=0x1000/0x1000                   NXM,OXM' \
+    'tcp6,tcp_dst=80                             NXM,OXM' \
+    'tcp6,tcp_dst=0x1000/0x1000                  NXM,OXM' \
+    'udp,udp_src=80                              any' \
+    'udp,udp_src=0x1000/0x1000                   NXM,OXM' \
+    'udp6,udp_src=80                             NXM,OXM' \
+    'udp6,udp_src=0x1000/0x1000                  NXM,OXM' \
+    'udp,udp_dst=80                              any' \
+    'udp,udp_dst=0x1000/0x1000                   NXM,OXM' \
+    'udp6,udp_dst=80                             NXM,OXM' \
+    'udp6,udp_dst=0x1000/0x1000                  NXM,OXM' \
+    'icmp,icmp_type=1                            any' \
+    'icmp,icmp_type=1                            any' \
+    'icmp6,icmpv6_type=1                         NXM,OXM' \
+    'icmp6,icmpv6_code=2                         NXM,OXM'
+do
+    set $test_case
+    echo
+    echo "### test case: '$1' should have usable protocols '$2'"
+    if test "$2" = none; then
+      AT_CHECK([ovs-ofctl parse-flow "$1,actions=drop"], [1],
+               [usable protocols: none
+],
+               [ovs-ofctl: no usable protocol
+])
+    else
+      AT_CHECK_UNQUOTED([ovs-ofctl parse-flow "$1,actions=drop" | sed 1q], [0],
+                        [usable protocols: $2
+])
+    fi
+done
+AT_CLEANUP
+
 AT_SETUP([ovs-ofctl parse-flows (OpenFlow 1.0)])
 AT_DATA([flows.txt], [[
 # comment
-tcp,tp_src=123,actions=flood
+tcp,tp_src=123,out_port=5,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
@@ -20,7 +122,7 @@ AT_CHECK([ovs-ofctl parse-flows flows.txt
 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 tcp,tp_src=123 out_port:5 actions=FLOOD
 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
@@ -51,7 +153,7 @@ actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_
 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: NXM,OXM
+[[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
index 439bd2d..fa2c3ff 100644 (file)
@@ -73,7 +73,7 @@ m4_define([CHECK_BRIDGES],
 
    dnl Check that each bridge exists according to br-exists and that
    dnl a bridge that should not exist does not.
-   m4_foreach([brinfo], [$@], 
+   m4_foreach([brinfo], [$@],
               [AT_CHECK([RUN_OVS_VSCTL([br-exists m4_car(brinfo)])], [0], [],
                         [], [OVS_VSCTL_CLEANUP])])
    AT_CHECK([RUN_OVS_VSCTL([br-exists nonexistent])], [2], [], [],
@@ -111,7 +111,7 @@ m4_define([CHECK_PORTS],
 ],
             [OVS_VSCTL_CLEANUP])
    m4_foreach(
-     [port], m4_cdr($@), 
+     [port], m4_cdr($@),
      [AT_CHECK([RUN_OVS_VSCTL([[port-to-br] port])], [0], [$1
 ], [], [OVS_VSCTL_CLEANUP])])])
 
@@ -134,7 +134,7 @@ m4_define([CHECK_IFACES],
 ],
             [OVS_VSCTL_CLEANUP])
    m4_foreach(
-     [iface], m4_cdr($@), 
+     [iface], m4_cdr($@),
      [AT_CHECK([RUN_OVS_VSCTL([[iface-to-br] iface])], [0], [$1
 ],
                [], [OVS_VSCTL_CLEANUP])])])
@@ -262,7 +262,7 @@ AT_SETUP([add-br a, add-port a a1, add-port a a1])
 AT_KEYWORDS([ovs-vsctl])
 OVS_VSCTL_SETUP
 AT_CHECK([RUN_OVS_VSCTL(
-   [add-br a], 
+   [add-br a],
    [add-port a a1])], [0], [], [], [OVS_VSCTL_CLEANUP])
 AT_CHECK([RUN_OVS_VSCTL([add-port a a1])], [1], [],
   [ovs-vsctl: cannot create a port named a1 because a port named a1 already exists on bridge a
@@ -274,8 +274,8 @@ AT_SETUP([add-br a b, add-port a a1, add-port b b1, del-br a])
 AT_KEYWORDS([ovs-vsctl])
 OVS_VSCTL_SETUP
 AT_CHECK([RUN_OVS_VSCTL_TOGETHER(
-   [add-br a], 
-   [add-br b], 
+   [add-br a],
+   [add-br b],
    [add-port a a1],
    [add-port b b1],
    [--if-exists del-port b b2],
@@ -296,11 +296,11 @@ AT_SETUP([add-br a, add-bond a bond0 a1 a2 a3])
 AT_KEYWORDS([ovs-vsctl])
 OVS_VSCTL_SETUP
 AT_CHECK([RUN_OVS_VSCTL(
-   [add-br a], 
+   [add-br a],
    [add-bond a bond0 a1 a2 a3])], [0], [], [], [OVS_VSCTL_CLEANUP])
 AT_CHECK([RUN_OVS_VSCTL([--may-exist add-bond a bond0 a3 a1 a2])], [0], [], [],
   [OVS_VSCTL_CLEANUP])
-AT_CHECK([RUN_OVS_VSCTL([--may-exist add-bond a bond0 a2 a1])], [1], [], 
+AT_CHECK([RUN_OVS_VSCTL([--may-exist add-bond a bond0 a2 a1])], [1], [],
   [ovs-vsctl: "--may-exist add-bond a bond0 a2 a1" but bond0 actually has interface(s) a1, a2, a3
 ],
   [OVS_VSCTL_CLEANUP])
@@ -314,8 +314,8 @@ AT_SETUP([add-br a b, add-port a a1, add-port b b1, del-port a a1])
 AT_KEYWORDS([ovs-vsctl])
 OVS_VSCTL_SETUP
 AT_CHECK([RUN_OVS_VSCTL(
-  [add-br a], 
-  [add-br b], 
+  [add-br a],
+  [add-br b],
   [add-port a a1 tag=9],
   [get port a1 tag],
   [--may-exist add-port b b1],
@@ -323,7 +323,11 @@ AT_CHECK([RUN_OVS_VSCTL(
 ], [], [OVS_VSCTL_CLEANUP])
 AT_CHECK([RUN_OVS_VSCTL([--may-exist add-port b b1])], [0], [], [],
   [OVS_VSCTL_CLEANUP])
-AT_CHECK([RUN_OVS_VSCTL([--may-exist add-port a b1])], [1], [], 
+AT_CHECK([RUN_OVS_VSCTL([del-port a])], [1], [],
+  [ovs-vsctl: cannot delete port a because it is the local port for bridge a (deleting this port requires deleting the entire bridge)
+],
+  [OVS_VSCTL_CLEANUP])
+AT_CHECK([RUN_OVS_VSCTL([--may-exist add-port a b1])], [1], [],
   [ovs-vsctl: "--may-exist add-port a b1" but b1 is actually attached to bridge b
 ],
   [OVS_VSCTL_CLEANUP])
@@ -339,7 +343,7 @@ AT_SETUP([add-br a, add-bond a bond0 a1 a2 a3, del-port bond0])
 AT_KEYWORDS([ovs-vsctl])
 OVS_VSCTL_SETUP
 AT_CHECK([RUN_OVS_VSCTL_TOGETHER(
-  [add-br a], 
+  [add-br a],
   [add-bond a bond0 a1 a2 a3 tag=9],
   [get Port bond0 tag],
   [del-port bond0])], [0], [
@@ -356,7 +360,7 @@ AT_SETUP([external IDs])
 AT_KEYWORDS([ovs-vsctl])
 OVS_VSCTL_SETUP
 AT_CHECK([RUN_OVS_VSCTL_ONELINE(
-  [add-br a], 
+  [add-br a],
   [add-port a a1],
   [add-bond a bond0 a2 a3],
   [br-set-external-id a key0 value0],
@@ -621,7 +625,7 @@ cp stdout out1
 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], 
+AT_CHECK([${PERL} $srcdir/uuidfilt.pl out1 out2], [0],
   [[<0>
 
 _uuid               : <0>
@@ -685,13 +689,13 @@ AT_CHECK([RUN_OVS_VSCTL([get bridge br0 other_config:hwaddr -- --if-exists get b
   [0], ["00:11:22:33:44:55"
 
 ], [], [OVS_VSCTL_CLEANUP])
-AT_CHECK([RUN_OVS_VSCTL([remove br br0 other_config hwaddr 'datapath_id=""' -- get br br0 other_config])], 
+AT_CHECK([RUN_OVS_VSCTL([remove br br0 other_config hwaddr 'datapath_id=""' -- get br br0 other_config])],
   [0], [{datapath_id="0123456789ab"}
 ], [], [OVS_VSCTL_CLEANUP])
-AT_CHECK([RUN_OVS_VSCTL([remove br br0 other_config 'datapath_id="0123456789ab"' -- get br br0 other_config])], 
+AT_CHECK([RUN_OVS_VSCTL([remove br br0 other_config 'datapath_id="0123456789ab"' -- get br br0 other_config])],
   [0], [{}
 ], [], [OVS_VSCTL_CLEANUP])
-AT_CHECK([RUN_OVS_VSCTL([clear br br0 external-ids -- get br br0 external_ids])], 
+AT_CHECK([RUN_OVS_VSCTL([clear br br0 external-ids -- get br br0 external_ids])],
   [0], [{}
 ], [], [OVS_VSCTL_CLEANUP])
 AT_CHECK([RUN_OVS_VSCTL_TOGETHER([destroy b br0],
@@ -699,7 +703,7 @@ AT_CHECK([RUN_OVS_VSCTL_TOGETHER([destroy b br0],
                                  [destroy b br2],
                                  [clear o . bridges])],
   [0], [stdout], [], [OVS_VSCTL_CLEANUP])
-AT_CHECK([RUN_OVS_VSCTL([list b])], 
+AT_CHECK([RUN_OVS_VSCTL([list b])],
   [0], [], [], [OVS_VSCTL_CLEANUP])
 AT_CHECK([RUN_OVS_VSCTL([--if-exists get b x datapath_id])],
   [0], [], [], [OVS_VSCTL_CLEANUP])
@@ -732,9 +736,9 @@ AT_CHECK([ovs-vsctl -- --may-exist],
 
 AT_CHECK([RUN_OVS_VSCTL([add-br br0])],
   [0], [ignore], [], [OVS_VSCTL_CLEANUP])
-AT_CHECK([RUN_OVS_VSCTL([add-br br1])], 
+AT_CHECK([RUN_OVS_VSCTL([add-br br1])],
   [0], [ignore], [], [OVS_VSCTL_CLEANUP])
-AT_CHECK([RUN_OVS_VSCTL([set-controller br1 tcp:127.0.0.1])], 
+AT_CHECK([RUN_OVS_VSCTL([set-controller br1 tcp:127.0.0.1])],
   [0], [ignore], [], [OVS_VSCTL_CLEANUP])
 AT_CHECK([
     RUN_OVS_VSCTL_TOGETHER([--id=@n create netflow targets='"1.2.3.4:567"'],
@@ -743,7 +747,7 @@ AT_CHECK([
 cp stdout netflow-uuid
 AT_CHECK([RUN_OVS_VSCTL([list netflow `cat netflow-uuid`])],
   [0], [stdout], [], [OVS_VSCTL_CLEANUP])
-AT_CHECK([${PERL} $srcdir/uuidfilt.pl netflow-uuid stdout], [0], 
+AT_CHECK([${PERL} $srcdir/uuidfilt.pl netflow-uuid stdout], [0],
   [[<0>
 
 _uuid               : <0>
@@ -754,73 +758,73 @@ engine_type         : []
 external_ids        : {}
 targets             : ["1.2.3.4:567"]
 ]], [ignore], [test ! -e pid || kill `cat pid`])
-AT_CHECK([RUN_OVS_VSCTL([list interx x])], 
+AT_CHECK([RUN_OVS_VSCTL([list interx x])],
   [1], [], [ovs-vsctl: unknown table "interx"
 ], [OVS_VSCTL_CLEANUP])
-AT_CHECK([RUN_OVS_VSCTL([list b x])], 
+AT_CHECK([RUN_OVS_VSCTL([list b x])],
   [1], [], [ovs-vsctl: no row "x" in table Bridge
 ], [OVS_VSCTL_CLEANUP])
 AT_CHECK([RUN_OVS_VSCTL([get b x datapath_id])],
   [1], [], [ovs-vsctl: no row "x" in table Bridge
 ], [OVS_VSCTL_CLEANUP])
-AT_CHECK([RUN_OVS_VSCTL([get b br0 d])], 
+AT_CHECK([RUN_OVS_VSCTL([get b br0 d])],
   [1], [], [ovs-vsctl: Bridge contains more than one column whose name matches "d"
 ], [OVS_VSCTL_CLEANUP])
-AT_CHECK([RUN_OVS_VSCTL([get b br0 x])], 
+AT_CHECK([RUN_OVS_VSCTL([get b br0 x])],
   [1], [], [ovs-vsctl: Bridge does not contain a column whose name matches "x"
 ], [OVS_VSCTL_CLEANUP])
-AT_CHECK([RUN_OVS_VSCTL([get b br0 :y=z])], 
+AT_CHECK([RUN_OVS_VSCTL([get b br0 :y=z])],
   [1], [], [ovs-vsctl: :y=z: missing column name
 ], [OVS_VSCTL_CLEANUP])
-AT_CHECK([RUN_OVS_VSCTL([get b br0 datapath_id:y=z])], 
+AT_CHECK([RUN_OVS_VSCTL([get b br0 datapath_id:y=z])],
   [1], [], [ovs-vsctl: datapath_id:y=z: trailing garbage "=z" in argument
 ], [OVS_VSCTL_CLEANUP])
-AT_CHECK([RUN_OVS_VSCTL([set b br0 'datapath_id:y>=z'])], 
+AT_CHECK([RUN_OVS_VSCTL([set b br0 'datapath_id:y>=z'])],
   [1], [], [ovs-vsctl: datapath_id:y>=z: argument does not end in "=" followed by a value.
 ], [OVS_VSCTL_CLEANUP])
 AT_CHECK([RUN_OVS_VSCTL([set controller x connection_mode=standalone])],
   [1], [], [ovs-vsctl: no row "x" in table Controller
 ], [OVS_VSCTL_CLEANUP])
-AT_CHECK([RUN_OVS_VSCTL([wait-until b br0 datapath_id:y,z])], 
+AT_CHECK([RUN_OVS_VSCTL([wait-until b br0 datapath_id:y,z])],
   [1], [], [ovs-vsctl: datapath_id:y,z: argument does not end in "=", "!=", "<", ">", "<=", ">=", "{=}", "{!=}", "{<}", "{>}", "{<=}", or "{>=}" followed by a value.
 ], [OVS_VSCTL_CLEANUP])
-AT_CHECK([RUN_OVS_VSCTL([get b br0 datapath_id::])], 
+AT_CHECK([RUN_OVS_VSCTL([get b br0 datapath_id::])],
   [1], [], [ovs-vsctl: datapath_id::: trailing garbage ":" in argument
 ], [OVS_VSCTL_CLEANUP])
-AT_CHECK([RUN_OVS_VSCTL([get b br0 datapath_id:x])], 
+AT_CHECK([RUN_OVS_VSCTL([get b br0 datapath_id:x])],
   [1], [], [ovs-vsctl: cannot specify key to get for non-map column datapath_id
 ], [OVS_VSCTL_CLEANUP])
-AT_CHECK([RUN_OVS_VSCTL([get b br0 external_ids:x])], 
+AT_CHECK([RUN_OVS_VSCTL([get b br0 external_ids:x])],
   [1], [], [ovs-vsctl: no key "x" in Bridge record "br0" column external_ids
 ], [OVS_VSCTL_CLEANUP])
-AT_CHECK([RUN_OVS_VSCTL([set b br0 flood_vlans=-1])], 
+AT_CHECK([RUN_OVS_VSCTL([set b br0 flood_vlans=-1])],
   [1], [], [ovs-vsctl: constraint violation: -1 is not in the valid range 0 to 4095 (inclusive)
 ], [OVS_VSCTL_CLEANUP])
-AT_CHECK([RUN_OVS_VSCTL([set b br0 flood_vlans=4096])], 
+AT_CHECK([RUN_OVS_VSCTL([set b br0 flood_vlans=4096])],
   [1], [], [ovs-vsctl: constraint violation: 4096 is not in the valid range 0 to 4095 (inclusive)
 ], [OVS_VSCTL_CLEANUP])
-AT_CHECK([RUN_OVS_VSCTL([set c br1 'connection-mode=xyz'])], 
+AT_CHECK([RUN_OVS_VSCTL([set c br1 'connection-mode=xyz'])],
   [1], [], [[ovs-vsctl: constraint violation: xyz is not one of the allowed values ([in-band, out-of-band])
 ]], [OVS_VSCTL_CLEANUP])
-AT_CHECK([RUN_OVS_VSCTL([set c br1 connection-mode:x=y])], 
+AT_CHECK([RUN_OVS_VSCTL([set c br1 connection-mode:x=y])],
   [1], [], [ovs-vsctl: cannot specify key to set for non-map column connection_mode
 ], [OVS_VSCTL_CLEANUP])
-AT_CHECK([RUN_OVS_VSCTL([add b br1 datapath_id x y])], 
+AT_CHECK([RUN_OVS_VSCTL([add b br1 datapath_id x y])],
   [1], [], [ovs-vsctl: "add" operation would put 2 values in column datapath_id of table Bridge but the maximum number is 1
 ], [OVS_VSCTL_CLEANUP])
-AT_CHECK([RUN_OVS_VSCTL([remove netflow `cat netflow-uuid` targets '"1.2.3.4:567"'])], 
+AT_CHECK([RUN_OVS_VSCTL([remove netflow `cat netflow-uuid` targets '"1.2.3.4:567"'])],
   [1], [], [ovs-vsctl: "remove" operation would put 0 values in column targets of table NetFlow but the minimum number is 1
 ], [OVS_VSCTL_CLEANUP])
-AT_CHECK([RUN_OVS_VSCTL([remove netflow x targets '"1.2.3.4:567"'])], 
+AT_CHECK([RUN_OVS_VSCTL([remove netflow x targets '"1.2.3.4:567"'])],
   [1], [], [ovs-vsctl: no row "x" in table NetFlow
 ], [OVS_VSCTL_CLEANUP])
 AT_CHECK([RUN_OVS_VSCTL([clear netflow x targets])],
   [1], [], [ovs-vsctl: no row "x" in table NetFlow
 ], [OVS_VSCTL_CLEANUP])
-AT_CHECK([RUN_OVS_VSCTL([clear netflow `cat netflow-uuid` targets])], 
+AT_CHECK([RUN_OVS_VSCTL([clear netflow `cat netflow-uuid` targets])],
   [1], [], [ovs-vsctl: "clear" operation cannot be applied to column targets of table NetFlow, which is not allowed to be empty
 ], [OVS_VSCTL_CLEANUP])
-AT_CHECK([RUN_OVS_VSCTL([destroy b br2])], 
+AT_CHECK([RUN_OVS_VSCTL([destroy b br2])],
   [1], [], [ovs-vsctl: no row "br2" in table Bridge
 ], [OVS_VSCTL_CLEANUP])
 AT_CHECK([RUN_OVS_VSCTL([add in br1 name x])],
@@ -977,7 +981,7 @@ AT_SETUP([database commands -- wait-until immediately true])
 AT_KEYWORDS([ovs-vsctl])
 OVS_VSCTL_SETUP
 AT_CHECK([RUN_OVS_VSCTL(
-    [add-br br0], 
+    [add-br br0],
     [add-bond br0 bond0 eth0 eth1],
     [set port bond0 bond_updelay=500 other-config:abc=def])],
   [0], [], [], [OVS_VSCTL_CLEANUP])
@@ -1114,7 +1118,7 @@ AT_KEYWORDS([ovs-vsctl])
 OVS_VSCTL_SETUP
 AT_CHECK([RUN_OVS_VSCTL([--id=@br0 create Bridge name=br0 -- add Open_vSwitch . bridges @br0 -- list b])],
   [0], [stdout], [], [OVS_VSCTL_CLEANUP])
-AT_CHECK([${PERL} $srcdir/uuidfilt.pl stdout], [0], 
+AT_CHECK([${PERL} $srcdir/uuidfilt.pl stdout], [0],
   [[<0>
 _uuid               : <1>
 controller          : []
@@ -1185,3 +1189,65 @@ AT_CHECK([RUN_OVS_VSCTL(
    [-- list Queue])], [0], [], [], [OVS_VSCTL_CLEANUP])
 OVS_VSCTL_CLEANUP
 AT_CLEANUP
+
+dnl ----------------------------------------------------------------------
+AT_BANNER([ovs-vsctl add-port -- reserved port names])
+
+AT_SETUP([add-port -- reserved names 1])
+OVS_VSWITCHD_START
+
+# Test creating all reserved port names
+m4_foreach(
+[reserved_name],
+[[ovs-netdev],
+[ovs-dummy],
+[gre_system],
+[gre64_system],
+[lisp_system],
+[vxlan_system]],
+[
+# Try creating the port
+AT_CHECK([ovs-vsctl add-port br0 reserved_name], [0], [], [])
+# Detect the warning log message
+AT_CHECK([sed -n "s/^.*\(|bridge|WARN|.*\)$/\1/p" ovs-vswitchd.log], [0], [dnl
+|bridge|WARN|could not create interface reserved_name, name is reserved
+])
+# Delete the warning log message
+AT_CHECK([sed "/|bridge|WARN|/d" ovs-vswitchd.log > ovs-vswitchd.log], [0], [], [])
+# Delete the port
+AT_CHECK([ovs-vsctl del-port br0 reserved_name], [0], [], [])])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([add-port -- reserved names 2])
+# Creates all type of tunnel ports
+OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=gre \
+                    options:remote_ip=1.1.1.1 ofport_request=1\
+                    -- add-port br0 p2 -- set Interface p2 type=gre64 \
+                    options:local_ip=2.2.2.2 options:remote_ip=1.1.1.1 \
+                    ofport_request=2 \
+                    -- add-port br0 p3 -- set Interface p3 type=lisp \
+                    options:remote_ip=2.2.2.2 ofport_request=3 \
+                    -- add-port br0 p4 -- set Interface p4 type=vxlan \
+                    options:remote_ip=2.2.2.2 ofport_request=4])
+
+# Test creating all reserved tunnel port names
+m4_foreach(
+[reserved_name],
+[[gre_system],
+[gre64_system],
+[lisp_system],
+[vxlan_system]],
+[
+# Try creating the port
+AT_CHECK([ovs-vsctl add-port br0 reserved_name], [0], [], [])
+# Detect the warning log message
+AT_CHECK([sed -n "s/^.*\(|bridge|WARN|.*\)$/\1/p" ovs-vswitchd.log], [0], [dnl
+|bridge|WARN|could not create interface reserved_name, name is reserved
+])
+# Delete the warning log message
+AT_CHECK([sed "/|bridge|WARN|/d" ovs-vswitchd.log > ovs-vswitchd.log], [0], [], [])
+# Delete the port
+AT_CHECK([ovs-vsctl del-port br0 reserved_name], [0], [], [])])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
index 5f73c00..16a1d95 100644 (file)
@@ -164,6 +164,127 @@ AT_CHECK(
 OVSDB_SERVER_SHUTDOWN
 AT_CLEANUP
 
+AT_SETUP([ovsdb-server/add-db and remove-db])
+AT_KEYWORDS([ovsdb server positive])
+ON_EXIT([kill `cat ovsdb-server.pid`])
+OVS_RUNDIR=`pwd`; export OVS_RUNDIR
+OVS_LOGDIR=`pwd`; export OVS_LOGDIR
+ordinal_schema > schema1
+constraint_schema > schema2
+AT_CHECK([ovsdb-tool create db1 schema1], [0], [ignore], [ignore])
+AT_CHECK([ovsdb-tool create db2 schema2], [0], [ignore], [ignore])
+
+# Start ovsdb-server with just a single database - db1.
+AT_CHECK([ovsdb-server --detach --no-chdir --pidfile --remote=punix:socket db1], [0])
+AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/list-dbs],
+  [0], [ordinals
+])
+
+# Add the second database.
+AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/add-db db2], [0])
+AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/list-dbs],
+  [0], [constraints
+ordinals
+])
+
+# The databases are responsive.
+AT_CHECK([ovsdb-client list-tables unix:socket constraints], [0], [ignore], [ignore])
+AT_CHECK([ovsdb-client list-tables unix:socket ordinals], [0], [ignore], [ignore])
+
+# Add an already added database.
+AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/add-db db2], 2, [], [stderr])
+AT_CHECK([sed 's/(.*)/(...)/' stderr], [0],
+  [I/O error: db2: failed to lock lockfile (...)
+ovs-appctl: ovsdb-server: server returned an error
+])
+
+# Add a non-existing database.
+AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/add-db db3], 2, [], [stderr])
+AT_CHECK([sed 's/(.*)/(...)/' stderr], [0],
+  [I/O error: open: db3 failed (...)
+ovs-appctl: ovsdb-server: server returned an error
+])
+
+# Add a remote through a db path in db1.
+AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/add-remote db:ordinals,ordinals,name], [0])
+AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/list-remotes],
+  [0], [db:ordinals,ordinals,name
+punix:socket
+])
+
+# Removing db1 has no effect on its remote.
+AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/remove-db ordinals], [0])
+AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/list-dbs],
+  [0], [constraints
+])
+AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/list-remotes],
+  [0], [db:ordinals,ordinals,name
+punix:socket
+])
+AT_CHECK([ovsdb-client list-tables unix:socket ordinals], [1], [ignore], [ignore])
+
+# Remove db2.
+AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/remove-db constraints], [0])
+AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/list-dbs],
+  [0], [])
+AT_CHECK([ovsdb-client list-tables unix:socket constraints], [1], [ignore], [ignore])
+
+# Remove a non-existent database.
+AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/remove-db ordinals], [2],
+  [], [Failed to find the database.
+ovs-appctl: ovsdb-server: server returned an error
+])
+AT_CLEANUP
+
+AT_SETUP([ovsdb-server/add-db and remove-db with --monitor])
+AT_KEYWORDS([ovsdb server positive])
+# Start ovsdb-server, initially with one db.
+OVS_RUNDIR=`pwd`; export OVS_RUNDIR
+OVS_LOGDIR=`pwd`; export OVS_LOGDIR
+ordinal_schema > schema
+AT_CHECK([ovsdb-tool create db1 schema], [0], [ignore], [ignore])
+ON_EXIT([kill `cat *.pid`])
+AT_CHECK([ovsdb-server -v -vvlog:off --monitor --detach --no-chdir --pidfile --log-file db1])
+
+# Add the second database.
+constraint_schema > schema2
+AT_CHECK([ovsdb-tool create db2 schema2], [0], [ignore], [ignore])
+AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/add-db db2], [0])
+AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/list-dbs],
+  [0], [constraints
+ordinals
+])
+
+# Kill the daemon process, making it look like a segfault,
+# and wait for a new daemon process to get spawned.
+cp ovsdb-server.pid old.pid
+AT_CHECK([kill -SEGV `cat ovsdb-server.pid`])
+OVS_WAIT_WHILE([kill -0 `cat old.pid`])
+OVS_WAIT_UNTIL(
+  [test -s ovsdb-server.pid && test `cat ovsdb-server.pid` != `cat old.pid`])
+AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/list-dbs],
+  [0], [constraints
+ordinals
+])
+
+# Remove the recently added database.
+AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/remove-db constraints])
+AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/list-dbs],
+  [0], [ordinals
+])
+
+# Kill the daemon process, making it look like a segfault,
+# and wait for a new daemon process to get spawned.
+cp ovsdb-server.pid old.pid
+AT_CHECK([kill -SEGV `cat ovsdb-server.pid`])
+OVS_WAIT_WHILE([kill -0 `cat old.pid`])
+OVS_WAIT_UNTIL(
+  [test -s ovsdb-server.pid && test `cat ovsdb-server.pid` != `cat old.pid`])
+AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/list-dbs],
+  [0], [ordinals
+])
+AT_CLEANUP
+
 AT_SETUP([--remote=db: implementation])
 AT_KEYWORDS([ovsdb server positive])
 OVS_RUNDIR=`pwd`; export OVS_RUNDIR
@@ -207,7 +328,7 @@ AT_CHECK(
         "uuid-name": "x",
         "row": {"target": "punix:socket2"}}]']], [0], [ignore], [ignore])
 ON_EXIT([kill `cat ovsdb-server.pid`])
-AT_CHECK([ovsdb-server --enable-dummy --detach --no-chdir --pidfile --remote=db:Root,managers --remote=db:Root,manager_options --log-file db], [0], [ignore], [ignore])
+AT_CHECK([ovsdb-server --enable-dummy --detach --no-chdir --pidfile --remote=db:mydb,Root,managers --remote=db:mydb,Root,manager_options --log-file db], [0], [ignore], [ignore])
 for i in 1 2 3 4 5 6; do ovs-appctl -t ovsdb-server time/warp 1000; done
 AT_CHECK(
   [[ovsdb-client transact unix:socket1 \
@@ -271,6 +392,51 @@ AT_CHECK([test ! -e socket1])
 AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/list-remotes])
 AT_CLEANUP
 
+AT_SETUP([ovsdb-server/add-remote and remove-remote with --monitor])
+AT_KEYWORDS([ovsdb server positive])
+# Start ovsdb-server, initially with no remotes.
+OVS_RUNDIR=`pwd`; export OVS_RUNDIR
+OVS_LOGDIR=`pwd`; export OVS_LOGDIR
+ordinal_schema > schema
+AT_CHECK([ovsdb-tool create db schema], [0], [ignore], [ignore])
+ON_EXIT([kill `cat *.pid`])
+AT_CHECK([ovsdb-server -v -vvlog:off --monitor --detach --no-chdir --pidfile --log-file db])
+
+# Add a remote.
+AT_CHECK([test ! -e socket1])
+AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/add-remote punix:socket1])
+OVS_WAIT_UNTIL([test -S socket1])
+AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/list-remotes],
+  [0], [punix:socket1
+])
+
+# Kill the daemon process, making it look like a segfault,
+# and wait for a new daemon process to get spawned and for it to
+# start listening on 'socket1'.
+cp ovsdb-server.pid old.pid
+rm socket1
+AT_CHECK([kill -SEGV `cat ovsdb-server.pid`])
+OVS_WAIT_WHILE([kill -0 `cat old.pid`])
+OVS_WAIT_UNTIL(
+  [test -s ovsdb-server.pid && test `cat ovsdb-server.pid` != `cat old.pid`])
+OVS_WAIT_UNTIL([test -S socket1])
+
+# Remove the remote.
+AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/remove-remote punix:socket1])
+OVS_WAIT_UNTIL([test ! -e socket1])
+AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/list-remotes])
+
+# Kill the daemon process, making it look like a segfault,
+# and wait for a new daemon process to get spawned and make sure that it
+# does not listen on 'socket1'.
+cp ovsdb-server.pid old.pid
+AT_CHECK([kill -SEGV `cat ovsdb-server.pid`])
+OVS_WAIT_WHILE([kill -0 `cat old.pid`])
+OVS_WAIT_UNTIL(
+  [test -s ovsdb-server.pid && test `cat ovsdb-server.pid` != `cat old.pid`])
+AT_CHECK([test ! -e socket1])
+AT_CLEANUP
+
 AT_SETUP([SSL db: implementation])
 AT_KEYWORDS([ovsdb server positive ssl $5])
 AT_SKIP_IF([test "$HAVE_OPENSSL" = no])
@@ -299,9 +465,9 @@ AT_CHECK(
 OVS_LOGDIR=`pwd`; export OVS_LOGDIR
 AT_CHECK(
   [ovsdb-server --log-file --detach --no-chdir --pidfile="`pwd`"/pid \
-        --private-key=db:SSL,private_key \
-        --certificate=db:SSL,certificate \
-        --ca-cert=db:SSL,ca_cert \
+        --private-key=db:mydb,SSL,private_key \
+        --certificate=db:mydb,SSL,certificate \
+        --ca-cert=db:mydb,SSL,ca_cert \
         --remote=pssl:0:127.0.0.1 --unixctl="`pwd`"/unixctl db],
   [0], [ignore], [ignore])
 SSL_PORT=`parse_listening_port < ovsdb-server.log`
diff --git a/tests/run-oftest b/tests/run-oftest
new file mode 100755 (executable)
index 0000000..d12a22f
--- /dev/null
@@ -0,0 +1,94 @@
+#! /bin/sh
+
+set -e
+
+run () {
+    echo "$@"
+    "$@" || exit 1
+}
+
+# Put built tools early in $PATH.
+builddir=`pwd`
+if test ! -e vswitchd/ovs-vswitchd; then
+    echo >&2 'not in build directory, please change directory or run via \"make check-oftest'
+    exit 1
+fi
+PATH=$builddir/ovsdb:$builddir/vswitchd:$builddir/utilities:$PATH; export PATH
+
+# Find srcdir.
+case $srcdir in
+    '') srcdir=$builddir ;;
+    /*) ;;
+    *) srcdir=`pwd`/$srcdir ;;
+esac
+if test ! -e "$srcdir"/WHY-OVS; then
+    echo >&2 'source directory not found, please set $srcdir or run via \"make check-oftest'
+    exit 1
+fi
+
+# Make sure oftest is available.
+if test X"$OFT" = X; then
+    OFT=oft
+fi
+if ($OFT --version) >/dev/null 2>&1; then
+    :
+else
+    echo >&2 'OFTest "oft" binary not found or cannot be run, please add to $PATH or set $OFT'
+    exit 1
+fi
+
+# Create sandbox.
+rm -rf sandbox
+mkdir sandbox
+cd sandbox
+sandbox=`pwd`
+
+# Set up environment for OVS programs to sandbox themselves.
+OVS_RUNDIR=$sandbox; export OVS_RUNDIR
+OVS_LOGDIR=$sandbox; export OVS_LOGDIR
+OVS_DBDIR=$sandbox; export OVS_DBDIR
+OVS_SYSCONFDIR=$sandbox; export OVS_SYSCONFDIR
+
+trap 'kill `cat *.pid`' 0 1 2 3 13 14 15
+
+# Create database and start ovsdb-server.
+touch .conf.db.~lock~
+rm -f conf.db
+run ovsdb-tool create conf.db "$srcdir"/vswitchd/vswitch.ovsschema
+run ovsdb-server --detach --no-chdir --pidfile -vconsole:off --log-file \
+    --remote=punix:"$sandbox"/db.sock
+
+# Start ovs-vswitchd.
+run ovs-vswitchd --detach --no-chdir --pidfile -vconsole:off --log-file \
+    --enable-dummy --disable-system -vvconn -vnetdev_dummy
+
+# Add a bridge and some ports for OFTest to use,
+# and configure ovs-vswitchd to connect to OFTest.
+run ovs-vsctl --no-wait \
+    -- add-br br0 \
+    -- set bridge br0 datapath-type=dummy fail-mode=secure
+for port in p1 p2 p3 p4; do
+    run ovs-vsctl --no-wait \
+       -- add-port br0 $port \
+       -- set interface $port type=dummy \
+                               options:pstream=punix:$OVS_RUNDIR/$port
+done
+run ovs-vsctl \
+    -- set-controller br0 tcp:127.0.0.1 \
+    -- set controller br0 connection-mode=out-of-band max-backoff=1000
+
+# Run OFTest.
+run $OFT -P ovs-dummy $OFTFLAGS; status=$?
+
+cat <<EOF
+
+----------------------------------------------------------------------
+Logs may be found under $sandbox, e.g.:
+       $sandbox/oft.log
+       $sandbox/ovs-vswitchd.log
+       $sandbox/ovsdb-server.log
+----------------------------------------------------------------------
+EOF
+
+# Propagate OFTest exit status.
+exit $status
diff --git a/tests/test-atomic.c b/tests/test-atomic.c
new file mode 100644 (file)
index 0000000..27bf552
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 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.
+ * 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 "ovs-atomic.h"
+#include "util.h"
+
+#define TEST_ATOMIC_TYPE(ATOMIC_TYPE, BASE_TYPE)        \
+    {                                                   \
+        ATOMIC_TYPE x = ATOMIC_VAR_INIT(1);             \
+        BASE_TYPE value, orig;                          \
+                                                        \
+        atomic_read(&x, &value);                        \
+        ovs_assert(value == 1);                         \
+                                                        \
+        atomic_store(&x, 2);                            \
+        atomic_read(&x, &value);                        \
+        ovs_assert(value == 2);                         \
+                                                        \
+        atomic_init(&x, 3);                             \
+        atomic_read(&x, &value);                        \
+        ovs_assert(value == 3);                         \
+                                                        \
+        atomic_add(&x, 1, &orig);                       \
+        ovs_assert(orig == 3);                          \
+        atomic_read(&x, &value);                        \
+        ovs_assert(value == 4);                         \
+                                                        \
+        atomic_sub(&x, 2, &orig);                       \
+        ovs_assert(orig == 4);                          \
+        atomic_read(&x, &value);                        \
+        ovs_assert(value == 2);                         \
+                                                        \
+        atomic_or(&x, 6, &orig);                        \
+        ovs_assert(orig == 2);                          \
+        atomic_read(&x, &value);                        \
+        ovs_assert(value == 6);                         \
+                                                        \
+        atomic_and(&x, 10, &orig);                      \
+        ovs_assert(orig == 6);                          \
+        atomic_read(&x, &value);                        \
+        ovs_assert(value == 2);                         \
+                                                        \
+        atomic_xor(&x, 10, &orig);                      \
+        ovs_assert(orig == 2);                          \
+        atomic_read(&x, &value);                        \
+        ovs_assert(value == 8);                         \
+    }
+
+int
+main(void)
+{
+    TEST_ATOMIC_TYPE(atomic_char, char);
+    TEST_ATOMIC_TYPE(atomic_uchar, unsigned char);
+    TEST_ATOMIC_TYPE(atomic_schar, signed char);
+    TEST_ATOMIC_TYPE(atomic_short, short);
+    TEST_ATOMIC_TYPE(atomic_ushort, unsigned short);
+    TEST_ATOMIC_TYPE(atomic_int, int);
+    TEST_ATOMIC_TYPE(atomic_uint, unsigned int);
+    TEST_ATOMIC_TYPE(atomic_long, long int);
+    TEST_ATOMIC_TYPE(atomic_ulong, unsigned long int);
+    TEST_ATOMIC_TYPE(atomic_llong, long long int);
+    TEST_ATOMIC_TYPE(atomic_ullong, unsigned long long int);
+    TEST_ATOMIC_TYPE(atomic_size_t, size_t);
+    TEST_ATOMIC_TYPE(atomic_ptrdiff_t, ptrdiff_t);
+    TEST_ATOMIC_TYPE(atomic_intmax_t, intmax_t);
+    TEST_ATOMIC_TYPE(atomic_uintmax_t, uintmax_t);
+    TEST_ATOMIC_TYPE(atomic_intptr_t, intptr_t);
+    TEST_ATOMIC_TYPE(atomic_uintptr_t, uintptr_t);
+    TEST_ATOMIC_TYPE(atomic_uint8_t, uint8_t);
+    TEST_ATOMIC_TYPE(atomic_int8_t, int8_t);
+    TEST_ATOMIC_TYPE(atomic_uint16_t, uint16_t);
+    TEST_ATOMIC_TYPE(atomic_int16_t, int16_t);
+    TEST_ATOMIC_TYPE(atomic_uint32_t, uint32_t);
+    TEST_ATOMIC_TYPE(atomic_int32_t, int32_t);
+    TEST_ATOMIC_TYPE(atomic_uint64_t, uint64_t);
+    TEST_ATOMIC_TYPE(atomic_int64_t, int64_t);
+
+    return 0;
+}
index f5b24b4..0e7525c 100644 (file)
@@ -31,7 +31,7 @@
 #define MAX_SLAVES 8 /* Maximum supported by this test framework. */
 
 struct slave {
-    uint16_t slave_id;
+    ofp_port_t slave_id;
 
     bool enabled;
     size_t flow_count;
@@ -43,7 +43,7 @@ struct slave_group {
 };
 
 static struct slave *
-slave_lookup(struct slave_group *sg, uint16_t slave_id)
+slave_lookup(struct slave_group *sg, ofp_port_t slave_id)
 {
     size_t i;
 
@@ -57,7 +57,7 @@ slave_lookup(struct slave_group *sg, uint16_t slave_id)
 }
 
 static bool
-slave_enabled_cb(uint16_t slave_id, void *aux)
+slave_enabled_cb(ofp_port_t slave_id, void *aux)
 {
     struct slave *slave;
 
@@ -122,7 +122,7 @@ main(int argc, char *argv[])
     /* Generate 'slaves' array. */
     sg.n_slaves = 0;
     for (i = 0; i < bundle->n_slaves; i++) {
-        uint16_t slave_id = bundle->slaves[i];
+        ofp_port_t slave_id = bundle->slaves[i];
 
         if (slave_lookup(&sg, slave_id)) {
             ovs_fatal(0, "Redundant slaves are not supported. ");
@@ -138,7 +138,7 @@ main(int argc, char *argv[])
         random_bytes(&flows[i], sizeof flows[i]);
         memset(flows[i].zeros, 0, sizeof flows[i].zeros);
         flows[i].mpls_depth = 0;
-        flows[i].regs[0] = OFPP_NONE;
+        flows[i].regs[0] = ofp_to_u16(OFPP_NONE);
     }
 
     /* Cycles through each possible liveness permutation for the given
@@ -186,11 +186,13 @@ main(int argc, char *argv[])
         changed = 0;
         for (j = 0; j < N_FLOWS; j++) {
             struct flow *flow = &flows[j];
-            uint16_t old_slave_id, ofp_port;
+            ofp_port_t old_slave_id, ofp_port;
+            struct flow_wildcards wc;
 
-            old_slave_id = flow->regs[0];
-            ofp_port = bundle_execute(bundle, flow, slave_enabled_cb, &sg);
-            flow->regs[0] = ofp_port;
+            old_slave_id = u16_to_ofp(flow->regs[0]);
+            ofp_port = bundle_execute(bundle, flow, &wc, slave_enabled_cb,
+                                      &sg);
+            flow->regs[0] = ofp_to_u16(ofp_port);
 
             if (ofp_port != OFPP_NONE) {
                 slave_lookup(&sg, ofp_port)->flow_count++;
index 18dee86..601aaf8 100644 (file)
@@ -240,8 +240,9 @@ match(const struct cls_rule *wild_, const struct flow *fixed)
             eq = !((fixed->dl_type ^ wild.flow.dl_type)
                    & wild.wc.masks.dl_type);
         } else if (f_idx == CLS_F_IDX_IN_PORT) {
-            eq = !((fixed->in_port ^ wild.flow.in_port)
-                   & wild.wc.masks.in_port);
+            eq = !((fixed->in_port.ofp_port
+                    ^ wild.flow.in_port.ofp_port)
+                   & wild.wc.masks.in_port.ofp_port);
         } else {
             NOT_REACHED();
         }
@@ -298,7 +299,7 @@ static ovs_be64 tun_id_values[] = {
 static ovs_be64 metadata_values[] = {
     0,
     CONSTANT_HTONLL(UINT64_C(0xfedcba9876543210)) };
-static uint16_t in_port_values[] = { 1, OFPP_LOCAL };
+static ofp_port_t in_port_values[] = { OFP_PORT_C(1), OFPP_LOCAL };
 static ovs_be16 vlan_tci_values[] = { CONSTANT_HTONS(101), CONSTANT_HTONS(0) };
 static ovs_be16 dl_type_values[]
             = { CONSTANT_HTONS(ETH_TYPE_IP), CONSTANT_HTONS(ETH_TYPE_ARP) };
@@ -410,7 +411,8 @@ compare_classifiers(struct classifier *cls, struct tcls *tcls)
         flow.nw_dst = nw_dst_values[get_value(&x, N_NW_DST_VALUES)];
         flow.tunnel.tun_id = tun_id_values[get_value(&x, N_TUN_ID_VALUES)];
         flow.metadata = metadata_values[get_value(&x, N_METADATA_VALUES)];
-        flow.in_port = in_port_values[get_value(&x, N_IN_PORT_VALUES)];
+        flow.in_port.ofp_port = in_port_values[get_value(&x,
+                                                   N_IN_PORT_VALUES)];
         flow.vlan_tci = vlan_tci_values[get_value(&x, N_VLAN_TCI_VALUES)];
         flow.dl_type = dl_type_values[get_value(&x, N_DL_TYPE_VALUES)];
         flow.tp_src = tp_src_values[get_value(&x, N_TP_SRC_VALUES)];
@@ -422,7 +424,7 @@ compare_classifiers(struct classifier *cls, struct tcls *tcls)
         flow.nw_proto = nw_proto_values[get_value(&x, N_NW_PROTO_VALUES)];
         flow.nw_tos = nw_dscp_values[get_value(&x, N_NW_DSCP_VALUES)];
 
-        cr0 = classifier_lookup(cls, &flow);
+        cr0 = classifier_lookup(cls, &flow, NULL);
         cr1 = tcls_lookup(tcls, &flow);
         assert((cr0 == NULL) == (cr1 == NULL));
         if (cr0 != NULL) {
@@ -546,7 +548,7 @@ make_rule(int wc_fields, unsigned int priority, int value_pat)
         } else if (f_idx == CLS_F_IDX_DL_TYPE) {
             match.wc.masks.dl_type = htons(UINT16_MAX);
         } else if (f_idx == CLS_F_IDX_IN_PORT) {
-            match.wc.masks.in_port = UINT16_MAX;
+            match.wc.masks.in_port.ofp_port = u16_to_ofp(UINT16_MAX);
         } else {
             NOT_REACHED();
         }
@@ -1279,7 +1281,7 @@ test_minimask_combine(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
         minimask_init(&minimask2, &mask2);
 
         minimask_combine(&minicombined, &minimask, &minimask2, storage);
-        flow_wildcards_combine(&combined, &mask, &mask2);
+        flow_wildcards_and(&combined, &mask, &mask2);
         minimask_expand(&minicombined, &combined2);
         assert(flow_wildcards_equal(&combined, &combined2));
 
index c77372f..8308bf8 100644 (file)
@@ -58,7 +58,7 @@ main(int argc OVS_UNUSED, char *argv[])
         struct ofp10_match extracted_match;
         struct match match;
         struct flow flow;
-
+        union flow_in_port in_port_;
         n++;
 
         retval = pcap_read(pcap, &packet);
@@ -68,7 +68,8 @@ main(int argc OVS_UNUSED, char *argv[])
             ovs_fatal(retval, "error reading pcap file");
         }
 
-        flow_extract(packet, 0, 0, NULL, 1, &flow);
+        in_port_.ofp_port = u16_to_ofp(1);
+        flow_extract(packet, 0, 0, NULL, &in_port_, &flow);
         match_init_exact(&match, &flow);
         ofputil_match_to_ofp10_match(&match, &extracted_match);
 
diff --git a/tests/test-hindex.c b/tests/test-hindex.c
new file mode 100644 (file)
index 0000000..b5fe9f0
--- /dev/null
@@ -0,0 +1,319 @@
+/*
+ * Copyright (c) 2008, 2009, 2010, 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.
+ * 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.
+ */
+
+/* A non-exhaustive test for some of the functions and macros declared in
+ * hindex.h. */
+
+#include <config.h>
+#include "hindex.h"
+#include <string.h>
+#include "hash.h"
+#include "util.h"
+
+#undef NDEBUG
+#include <assert.h>
+
+/* Sample hindex element. */
+struct element {
+    int value;
+    struct hindex_node node;
+};
+
+typedef size_t hash_func(int value);
+
+static int
+compare_ints(const void *a_, const void *b_)
+{
+    const int *a = a_;
+    const int *b = b_;
+    return *a < *b ? -1 : *a > *b;
+}
+
+/* Verifies that 'hindex' contains exactly the 'n' values in 'values'. */
+static void
+check_hindex(struct hindex *hindex, const int values[], size_t n,
+           hash_func *hash)
+{
+    int *sort_values, *hindex_values;
+    struct element *e;
+    size_t i;
+
+    /* Check that all the values are there in iteration. */
+    sort_values = xmalloc(sizeof *sort_values * n);
+    hindex_values = xmalloc(sizeof *sort_values * n);
+
+    i = 0;
+    HINDEX_FOR_EACH (e, node, hindex) {
+        assert(i < n);
+        hindex_values[i++] = e->value;
+    }
+    assert(i == n);
+
+    memcpy(sort_values, values, sizeof *sort_values * n);
+    qsort(sort_values, n, sizeof *sort_values, compare_ints);
+    qsort(hindex_values, n, sizeof *hindex_values, compare_ints);
+
+    for (i = 0; i < n; i++) {
+        assert(sort_values[i] == hindex_values[i]);
+    }
+
+    free(hindex_values);
+    free(sort_values);
+
+    /* Check that all the values are there in lookup. */
+    for (i = 0; i < n; i++) {
+        size_t count = 0;
+
+        HINDEX_FOR_EACH_WITH_HASH (e, node, hash(values[i]), hindex) {
+            count += e->value == values[i];
+        }
+        assert(count == 1);
+    }
+
+    /* Check counters. */
+    assert(hindex_is_empty(hindex) == !n);
+    assert(hindex->n_unique <= n);
+}
+
+/* Puts the 'n' values in 'values' into 'elements', and then puts those
+ * elements into 'hindex'. */
+static void
+make_hindex(struct hindex *hindex, struct element elements[],
+          int values[], size_t n, hash_func *hash)
+{
+    size_t i;
+
+    hindex_init(hindex);
+    for (i = 0; i < n; i++) {
+        elements[i].value = i;
+        hindex_insert(hindex, &elements[i].node, hash(elements[i].value));
+        values[i] = i;
+    }
+}
+
+static void
+shuffle(int *p, size_t n)
+{
+    for (; n > 1; n--, p++) {
+        int *q = &p[rand() % n];
+        int tmp = *p;
+        *p = *q;
+        *q = tmp;
+    }
+}
+
+/* Prints the 'n' values in 'values', plus 'name' as a title. */
+static void OVS_UNUSED
+print_ints(const char *name, const int *values, size_t n)
+{
+    size_t i;
+
+    printf("%s:", name);
+    for (i = 0; i < n; i++) {
+        printf(" %d", values[i]);
+    }
+    printf("\n");
+}
+
+/* Prints the values in 'hindex', plus 'name' as a title. */
+static void OVS_UNUSED
+print_hindex(const char *name, struct hindex *hindex)
+{
+    struct element *e;
+
+    printf("%s:", name);
+    HINDEX_FOR_EACH (e, node, hindex) {
+        printf(" %d(%zu)", e->value, e->node.hash & hindex->mask);
+    }
+    printf("\n");
+}
+
+static size_t
+unique_hash(int value)
+{
+    return value;
+}
+
+static size_t
+good_hash(int value)
+{
+    return hash_int(value, 0x1234abcd);
+}
+
+static size_t
+constant_hash(int value OVS_UNUSED)
+{
+    return 123;
+}
+
+static size_t
+mod4_hash(int value)
+{
+    return value % 4;
+}
+
+static size_t
+mod3_hash(int value)
+{
+    return value % 3;
+}
+
+static size_t
+mod2_hash(int value)
+{
+    return value % 2;
+}
+
+/* Tests basic hindex insertion and deletion. */
+static void
+test_hindex_insert_delete(hash_func *hash)
+{
+    enum { N_ELEMS = 100 };
+
+    struct element elements[N_ELEMS];
+    int values[N_ELEMS];
+    struct hindex hindex;
+    size_t i;
+
+    hindex_init(&hindex);
+    for (i = 0; i < N_ELEMS; i++) {
+        elements[i].value = i;
+        hindex_insert(&hindex, &elements[i].node, hash(i));
+        values[i] = i;
+        check_hindex(&hindex, values, i + 1, hash);
+    }
+    shuffle(values, N_ELEMS);
+    for (i = 0; i < N_ELEMS; i++) {
+        hindex_remove(&hindex, &elements[values[i]].node);
+        check_hindex(&hindex, values + (i + 1), N_ELEMS - (i + 1), hash);
+    }
+    hindex_destroy(&hindex);
+}
+
+/* Tests basic hindex_reserve() and hindex_shrink(). */
+static void
+test_hindex_reserve_shrink(hash_func *hash)
+{
+    enum { N_ELEMS = 32 };
+
+    size_t i;
+
+    for (i = 0; i < N_ELEMS; i++) {
+        struct element elements[N_ELEMS];
+        int values[N_ELEMS];
+        struct hindex hindex;
+        size_t j;
+
+        hindex_init(&hindex);
+        hindex_reserve(&hindex, i);
+        for (j = 0; j < N_ELEMS; j++) {
+            elements[j].value = j;
+            hindex_insert(&hindex, &elements[j].node, hash(j));
+            values[j] = j;
+            check_hindex(&hindex, values, j + 1, hash);
+        }
+        shuffle(values, N_ELEMS);
+        for (j = 0; j < N_ELEMS; j++) {
+            hindex_remove(&hindex, &elements[values[j]].node);
+            hindex_shrink(&hindex);
+            check_hindex(&hindex, values + (j + 1), N_ELEMS - (j + 1), hash);
+        }
+        hindex_destroy(&hindex);
+    }
+}
+
+/* Tests that HINDEX_FOR_EACH_SAFE properly allows for deletion of the current
+ * element of a hindex.  */
+static void
+test_hindex_for_each_safe(hash_func *hash)
+{
+    enum { MAX_ELEMS = 10 };
+    size_t n;
+    unsigned long int pattern;
+
+    for (n = 0; n <= MAX_ELEMS; n++) {
+        for (pattern = 0; pattern < 1ul << n; pattern++) {
+            struct element elements[MAX_ELEMS];
+            int values[MAX_ELEMS];
+            struct hindex hindex;
+            struct element *e, *next;
+            size_t n_remaining;
+            int i;
+
+            make_hindex(&hindex, elements, values, n, hash);
+
+            i = 0;
+            n_remaining = n;
+            HINDEX_FOR_EACH_SAFE (e, next, node, &hindex) {
+                assert(i < n);
+                if (pattern & (1ul << e->value)) {
+                    size_t j;
+                    hindex_remove(&hindex, &e->node);
+                    for (j = 0; ; j++) {
+                        assert(j < n_remaining);
+                        if (values[j] == e->value) {
+                            values[j] = values[--n_remaining];
+                            break;
+                        }
+                    }
+                }
+                check_hindex(&hindex, values, n_remaining, hash);
+                i++;
+            }
+            assert(i == n);
+
+            for (i = 0; i < n; i++) {
+                if (pattern & (1ul << i)) {
+                    n_remaining++;
+                }
+            }
+            assert(n == n_remaining);
+
+            hindex_destroy(&hindex);
+        }
+    }
+}
+
+static void
+run_test(void (*function)(hash_func *))
+{
+    hash_func *hash_funcs[] = {
+        unique_hash,
+        good_hash,
+        constant_hash,
+        mod4_hash,
+        mod3_hash,
+        mod2_hash,
+    };
+    size_t i;
+
+    for (i = 0; i < ARRAY_SIZE(hash_funcs); i++) {
+        function(hash_funcs[i]);
+        printf(".");
+        fflush(stdout);
+    }
+}
+
+int
+main(void)
+{
+    run_test(test_hindex_insert_delete);
+    run_test(test_hindex_for_each_safe);
+    run_test(test_hindex_reserve_shrink);
+    printf("\n");
+    return 0;
+}
+
index 8442bc2..e8aacff 100644 (file)
@@ -57,6 +57,7 @@ main(int argc, char *argv[])
         memset(histogram, 0, sizeof histogram);
         for (i = 0; i < N_FLOWS; i++) {
             int old_link, new_link;
+            struct flow_wildcards wc;
             struct flow flow;
 
             random_bytes(&flow, sizeof flow);
@@ -64,11 +65,11 @@ main(int argc, char *argv[])
             flow.mpls_depth = 0;
 
             mp.max_link = n - 1;
-            multipath_execute(&mp, &flow);
+            multipath_execute(&mp, &flow, &wc);
             old_link = flow.regs[0];
 
             mp.max_link = n;
-            multipath_execute(&mp, &flow);
+            multipath_execute(&mp, &flow, &wc);
             new_link = flow.regs[0];
 
             assert(old_link >= 0 && old_link < n);
index 921f0fd..413837e 100644 (file)
@@ -184,7 +184,7 @@ main(int argc, char *argv[])
 
     sock = inet_open_passive(SOCK_DGRAM, target, 0, NULL, 0);
     if (sock < 0) {
-        ovs_fatal(0, "%s: failed to open (%s)", argv[1], strerror(-sock));
+        ovs_fatal(0, "%s: failed to open (%s)", argv[1], ovs_strerror(-sock));
     }
 
     daemon_save_fd(STDOUT_FILENO);
index 268a105..b1d2853 100644 (file)
@@ -26,7 +26,7 @@
 #include "vlog.h"
 
 static int
-parse_keys(void)
+parse_keys(bool wc_keys)
 {
     int exit_code = 0;
     struct ds in;
@@ -36,51 +36,60 @@ parse_keys(void)
     while (!ds_get_test_line(&in, stdin)) {
         enum odp_key_fitness fitness;
         struct ofpbuf odp_key;
+        struct ofpbuf odp_mask;
         struct flow flow;
         struct ds out;
         int error;
 
         /* Convert string to OVS DP key. */
         ofpbuf_init(&odp_key, 0);
-        error = odp_flow_key_from_string(ds_cstr(&in), NULL, &odp_key);
+        ofpbuf_init(&odp_mask, 0);
+        error = odp_flow_from_string(ds_cstr(&in), NULL,
+                                     &odp_key, &odp_mask);
         if (error) {
-            printf("odp_flow_key_from_string: error\n");
+            printf("odp_flow_from_string: error\n");
             goto next;
         }
 
-        /* Convert odp_key to flow. */
-        fitness = odp_flow_key_to_flow(odp_key.data, odp_key.size, &flow);
-        switch (fitness) {
-        case ODP_FIT_PERFECT:
-            break;
-
-        case ODP_FIT_TOO_LITTLE:
-            printf("ODP_FIT_TOO_LITTLE: ");
-            break;
-
-        case ODP_FIT_TOO_MUCH:
-            printf("ODP_FIT_TOO_MUCH: ");
-            break;
-
-        case ODP_FIT_ERROR:
-            printf("odp_flow_key_to_flow: error\n");
-            goto next;
-        }
-
-        /* Convert cls_rule back to odp_key. */
-        ofpbuf_uninit(&odp_key);
-        ofpbuf_init(&odp_key, 0);
-        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",
-                    odp_key.size, ODPUTIL_FLOW_KEY_BYTES);
-            exit_code = 1;
+        if (!wc_keys) {
+            /* Convert odp_key to flow. */
+            fitness = odp_flow_key_to_flow(odp_key.data, odp_key.size, &flow);
+            switch (fitness) {
+                case ODP_FIT_PERFECT:
+                    break;
+
+                case ODP_FIT_TOO_LITTLE:
+                    printf("ODP_FIT_TOO_LITTLE: ");
+                    break;
+
+                case ODP_FIT_TOO_MUCH:
+                    printf("ODP_FIT_TOO_MUCH: ");
+                    break;
+
+                case ODP_FIT_ERROR:
+                    printf("odp_flow_key_to_flow: error\n");
+                    goto next;
+            }
+            /* Convert cls_rule back to odp_key. */
+            ofpbuf_uninit(&odp_key);
+            ofpbuf_init(&odp_key, 0);
+            odp_flow_key_from_flow(&odp_key, &flow, flow.in_port.odp_port);
+
+            if (odp_key.size > ODPUTIL_FLOW_KEY_BYTES) {
+                printf ("too long: %zu > %d\n",
+                        odp_key.size, ODPUTIL_FLOW_KEY_BYTES);
+                exit_code = 1;
+            }
         }
 
         /* Convert odp_key to string. */
         ds_init(&out);
-        odp_flow_key_format(odp_key.data, odp_key.size, &out);
+        if (wc_keys) {
+            odp_flow_format(odp_key.data, odp_key.size,
+                            odp_mask.data, odp_mask.size, &out);
+        } else {
+            odp_flow_key_format(odp_key.data, odp_key.size, &out);
+        }
         puts(ds_cstr(&out));
         ds_destroy(&out);
 
@@ -130,10 +139,12 @@ int
 main(int argc, char *argv[])
 {
     if (argc == 2 &&!strcmp(argv[1], "parse-keys")) {
-        return parse_keys();
+        return parse_keys(false);
+    } else if (argc == 2 &&!strcmp(argv[1], "parse-wc-keys")) {
+        return parse_keys(true);
     } else if (argc == 2 && !strcmp(argv[1], "parse-actions")) {
         return parse_actions();
     } else {
-        ovs_fatal(0, "usage: %s parse-keys | parse-actions", argv[0]);
+        ovs_fatal(0, "usage: %s parse-keys | parse-wc-keys | parse-actions", argv[0]);
     }
 }
index edb996c..cba01b9 100644 (file)
@@ -510,7 +510,7 @@ main(int argc, char *argv[])
 
     sock = inet_open_passive(SOCK_DGRAM, target, 0, NULL, 0);
     if (sock < 0) {
-        ovs_fatal(0, "%s: failed to open (%s)", argv[1], strerror(-sock));
+        ovs_fatal(0, "%s: failed to open (%s)", argv[1], ovs_strerror(-sock));
     }
 
     daemon_save_fd(STDOUT_FILENO);
index 1e4e787..f54a0df 100644 (file)
@@ -59,9 +59,9 @@ static void
 check_errno(int a, int b, const char *as, const char *file, int line)
 {
     if (a != b) {
-        char *str_b = strdup(strerror(abs(b)));
+        char *str_b = strdup(ovs_strerror(abs(b)));
         ovs_fatal(0, "%s:%d: %s is %d (%s) but should be %d (%s)",
-                  file, line, as, a, strerror(abs(a)), b, str_b);
+                  file, line, as, a, ovs_strerror(abs(a)), b, str_b);
     }
 }
 
@@ -156,14 +156,14 @@ test_refuse_connection(int argc OVS_UNUSED, char *argv[])
     if (!strcmp(type, "tcp")) {
         if (error != ECONNRESET && error != EPIPE) {
             ovs_fatal(0, "unexpected vconn_connect() return value %d (%s)",
-                      error, strerror(error));
+                      error, ovs_strerror(error));
         }
     } else if (!strcmp(type, "unix")) {
         CHECK_ERRNO(error, EPIPE);
     } else if (!strcmp(type, "ssl")) {
         if (error != EPROTO && error != ECONNRESET) {
             ovs_fatal(0, "unexpected vconn_connect() return value %d (%s)",
-                      error, strerror(error));
+                      error, ovs_strerror(error));
         }
     } else {
         ovs_fatal(0, "invalid connection type %s", type);
@@ -194,7 +194,7 @@ test_accept_then_close(int argc OVS_UNUSED, char *argv[])
     if (!strcmp(type, "tcp") || !strcmp(type, "unix")) {
         if (error != ECONNRESET && error != EPIPE) {
             ovs_fatal(0, "unexpected vconn_connect() return value %d (%s)",
-                      error, strerror(error));
+                      error, ovs_strerror(error));
         }
     } else {
         CHECK_ERRNO(error, EPROTO);
@@ -249,7 +249,7 @@ test_read_hello(int argc OVS_UNUSED, char *argv[])
     error = vconn_connect_block(vconn);
     if (error != ECONNRESET && error != EPIPE) {
         ovs_fatal(0, "unexpected vconn_connect() return value %d (%s)",
-                  error, strerror(error));
+                  error, ovs_strerror(error));
     }
     vconn_close(vconn);
 }
index da52593..fbc701b 100644 (file)
@@ -98,6 +98,7 @@ m4_include([tests/reconnect.at])
 m4_include([tests/ovs-vswitchd.at])
 m4_include([tests/ofproto.at])
 m4_include([tests/ofproto-dpif.at])
+m4_include([tests/vlan-splinters.at])
 m4_include([tests/ovsdb.at])
 m4_include([tests/ovs-vsctl.at])
 m4_include([tests/ovs-monitor-ipsec.at])
index 2369bd2..697c217 100644 (file)
@@ -14,25 +14,21 @@ actions=IN_PORT
 
 AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
 
-AT_CHECK([ovs-appctl dpif/show], [0], [dnl
-br0 (dummy@ovs-dummy):
-       lookups: hit:0 missed:0
-       flows: cur: 0, avg: 0.000, max: 0, life span: 0(ms)
-               overall avg: add rate: 0.000/min, del rate: 0.000/min
-       br0 65534/100: (dummy)
-       p1 1/1: (gre: remote_ip=1.1.1.1)
-       p2 2/1: (gre: local_ip=2.2.2.2, remote_ip=1.1.1.1)
-       p3 3/1: (gre: remote_ip=2.2.2.2)
+AT_CHECK([ovs-appctl dpif/show | tail -n +5], [0], [dnl
+               br0 65534/100: (dummy)
+               p1 1/1: (gre: remote_ip=1.1.1.1)
+               p2 2/1: (gre: local_ip=2.2.2.2, remote_ip=1.1.1.1)
+               p3 3/1: (gre: remote_ip=2.2.2.2)
 ])
 
 dnl remote_ip
-AT_CHECK([ovs-appctl ofproto/trace br0 'tunnel(tun_id=0x0,src=1.1.1.1,dst=1.2.3.4,tos=0x0,ttl=64,flags()),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=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'tunnel(tun_id=0x0,src=1.1.1.1,dst=1.2.3.4,tos=0x0,ttl=64,flags()),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=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
 AT_CHECK([tail -1 stdout], [0],
   [Datapath actions: set(tunnel(tun_id=0x0,src=0.0.0.0,dst=1.1.1.1,tos=0x0,ttl=64,flags(df))),1
 ])
 
 dnl local_ip, remote_ip
-AT_CHECK([ovs-appctl ofproto/trace br0 'tunnel(tun_id=0x0,src=1.1.1.1,dst=2.2.2.2,tos=0x0,ttl=64,flags()),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=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'tunnel(tun_id=0x0,src=1.1.1.1,dst=2.2.2.2,tos=0x0,ttl=64,flags()),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=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
 AT_CHECK([tail -1 stdout], [0],
   [Datapath actions: set(tunnel(tun_id=0x0,src=2.2.2.2,dst=1.1.1.1,tos=0x0,ttl=64,flags(df))),1
 ])
@@ -41,28 +37,24 @@ dnl reconfigure, local_ip, remote_ip
 AT_CHECK([ovs-vsctl set Interface p2 type=gre options:local_ip=2.2.2.3 \
           options:df_default=false options:ttl=1 options:csum=true \
           -- set Interface p3 type=gre64])
-AT_CHECK([ovs-appctl dpif/show], [0], [dnl
-br0 (dummy@ovs-dummy):
-       lookups: hit:0 missed:0
-       flows: cur: 0, avg: 0.000, max: 0, life span: 0(ms)
-               overall avg: add rate: 0.000/min, del rate: 0.000/min
-       br0 65534/100: (dummy)
-       p1 1/1: (gre: remote_ip=1.1.1.1)
-       p2 2/1: (gre: csum=true, df_default=false, local_ip=2.2.2.3, remote_ip=1.1.1.1, ttl=1)
-       p3 3/64: (gre64: remote_ip=2.2.2.2)
-])
-AT_CHECK([ovs-appctl ofproto/trace br0 'tunnel(tun_id=0x0,src=1.1.1.1,dst=2.2.2.2,tos=0x0,ttl=64,flags()),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=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
+AT_CHECK([ovs-appctl dpif/show | tail -n +5], [0], [dnl
+               br0 65534/100: (dummy)
+               p1 1/1: (gre: remote_ip=1.1.1.1)
+               p2 2/1: (gre: csum=true, df_default=false, local_ip=2.2.2.3, remote_ip=1.1.1.1, ttl=1)
+               p3 3/64: (gre64: remote_ip=2.2.2.2)
+])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'tunnel(tun_id=0x0,src=1.1.1.1,dst=2.2.2.2,tos=0x0,ttl=64,flags()),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=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
 AT_CHECK([tail -1 stdout], [0],
   [Datapath actions: set(tunnel(tun_id=0x0,src=0.0.0.0,dst=1.1.1.1,tos=0x0,ttl=64,flags(df))),1
 ])
-AT_CHECK([ovs-appctl ofproto/trace br0 'tunnel(tun_id=0x0,src=1.1.1.1,dst=2.2.2.3,tos=0x0,ttl=64,flags()),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=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'tunnel(tun_id=0x0,src=1.1.1.1,dst=2.2.2.3,tos=0x0,ttl=64,flags()),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=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
 AT_CHECK([tail -1 stdout], [0],
   [Datapath actions: set(tunnel(tun_id=0x0,src=2.2.2.3,dst=1.1.1.1,tos=0x0,ttl=1,flags(csum))),1
 ])
 
 dnl nonexistent tunnel
-AT_CHECK([ovs-appctl ofproto/trace br0 'tunnel(tun_id=0x0,src=5.5.5.5,dst=6.6.6.6,tos=0x0,ttl=64,flags()),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=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [2], [ignore], [dnl
-Invalid flow
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'tunnel(tun_id=0x0,src=5.5.5.5,dst=6.6.6.6,tos=0x0,ttl=64,flags()),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=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [2], [ignore], [dnl
+Invalid datapath flow
 ovs-appctl: ovs-vswitchd: server returned an error
 ])
 
@@ -80,36 +72,32 @@ actions=2
 
 AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
 
-AT_CHECK([ovs-appctl dpif/show], [0], [dnl
-br0 (dummy@ovs-dummy):
-       lookups: hit:0 missed:0
-       flows: cur: 0, avg: 0.000, max: 0, life span: 0(ms)
-               overall avg: add rate: 0.000/min, del rate: 0.000/min
-       br0 65534/100: (dummy)
-       p1 1/1: (gre: remote_ip=1.1.1.1)
-       p2 2/2: (dummy)
+AT_CHECK([ovs-appctl dpif/show | tail -n +5], [0], [dnl
+               br0 65534/100: (dummy)
+               p1 1/1: (gre: remote_ip=1.1.1.1)
+               p2 2/2: (dummy)
 ])
 
 dnl Tunnel CE and encapsulated packet CE
-AT_CHECK([ovs-appctl ofproto/trace br0 'tunnel(tun_id=0x0,src=1.1.1.1,dst=2.2.2.2,tos=0x3,ttl=64,flags()),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=6,tos=3,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'tunnel(tun_id=0x0,src=1.1.1.1,dst=2.2.2.2,tos=0x3,ttl=64,flags()),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=6,tos=3,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
 AT_CHECK([tail -1 stdout], [0],
   [Datapath actions: 2
 ])
 
 dnl Tunnel CE and encapsulated packet ECT(1)
-AT_CHECK([ovs-appctl ofproto/trace br0 'tunnel(tun_id=0x0,src=1.1.1.1,dst=2.2.2.2,tos=0x3,ttl=64,flags()),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=6,tos=1,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'tunnel(tun_id=0x0,src=1.1.1.1,dst=2.2.2.2,tos=0x3,ttl=64,flags()),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=6,tos=1,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
 AT_CHECK([tail -1 stdout], [0],
   [Datapath actions: set(ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0x3,ttl=64,frag=no)),2
 ])
 
 dnl Tunnel CE and encapsulated packet ECT(2)
-AT_CHECK([ovs-appctl ofproto/trace br0 'tunnel(tun_id=0x0,src=1.1.1.1,dst=2.2.2.2,tos=0x3,ttl=64,flags()),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=6,tos=2,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'tunnel(tun_id=0x0,src=1.1.1.1,dst=2.2.2.2,tos=0x3,ttl=64,flags()),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=6,tos=2,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
 AT_CHECK([tail -1 stdout], [0],
   [Datapath actions: set(ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0x3,ttl=64,frag=no)),2
 ])
 
 dnl Tunnel CE and encapsulated packet Non-ECT
-AT_CHECK([ovs-appctl ofproto/trace br0 'tunnel(tun_id=0x0,src=1.1.1.1,dst=2.2.2.2,tos=0x3,ttl=64,flags()),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=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'tunnel(tun_id=0x0,src=1.1.1.1,dst=2.2.2.2,tos=0x3,ttl=64,flags()),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=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
 AT_CHECK([tail -1 stdout], [0],
   [Datapath actions: drop
 ])
@@ -128,24 +116,20 @@ actions=output:1
 
 AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
 
-AT_CHECK([ovs-appctl dpif/show], [0], [dnl
-br0 (dummy@ovs-dummy):
-       lookups: hit:0 missed:0
-       flows: cur: 0, avg: 0.000, max: 0, life span: 0(ms)
-               overall avg: add rate: 0.000/min, del rate: 0.000/min
-       br0 65534/100: (dummy)
-       p1 1/1: (gre: key=5, local_ip=2.2.2.2, remote_ip=1.1.1.1)
-       p2 2/2: (dummy)
+AT_CHECK([ovs-appctl dpif/show | tail -n +5], [0], [dnl
+               br0 65534/100: (dummy)
+               p1 1/1: (gre: key=5, local_ip=2.2.2.2, remote_ip=1.1.1.1)
+               p2 2/2: (dummy)
 ])
 
 dnl Basic
-AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(2),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=6,tos=4,ttl=128,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),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=6,tos=4,ttl=128,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
 AT_CHECK([tail -1 stdout], [0],
   [Datapath actions: set(tunnel(tun_id=0x5,src=2.2.2.2,dst=1.1.1.1,tos=0x0,ttl=64,flags(df,key))),1
 ])
 
 dnl ECN
-AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(2),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=6,tos=1,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),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=6,tos=1,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
 AT_CHECK([tail -1 stdout], [0],
   [Datapath actions: set(tunnel(tun_id=0x5,src=2.2.2.2,dst=1.1.1.1,tos=0x1,ttl=64,flags(df,key))),1
 ])
@@ -164,30 +148,26 @@ actions=output:1
 
 AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
 
-AT_CHECK([ovs-appctl dpif/show], [0], [dnl
-br0 (dummy@ovs-dummy):
-       lookups: hit:0 missed:0
-       flows: cur: 0, avg: 0.000, max: 0, life span: 0(ms)
-               overall avg: add rate: 0.000/min, del rate: 0.000/min
-       br0 65534/100: (dummy)
-       p1 1/1: (gre: remote_ip=1.1.1.1, tos=inherit, ttl=inherit)
-       p2 2/2: (dummy)
+AT_CHECK([ovs-appctl dpif/show | tail -n +5], [0], [dnl
+               br0 65534/100: (dummy)
+               p1 1/1: (gre: remote_ip=1.1.1.1, tos=inherit, ttl=inherit)
+               p2 2/2: (dummy)
 ])
 
 dnl Basic
-AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(2),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=6,tos=4,ttl=128,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),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=6,tos=4,ttl=128,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
 AT_CHECK([tail -1 stdout], [0],
   [Datapath actions: set(tunnel(tun_id=0x0,src=0.0.0.0,dst=1.1.1.1,tos=0x4,ttl=128,flags(df))),1
 ])
 
 dnl ECN
-AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(2),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=6,tos=5,ttl=128,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),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=6,tos=5,ttl=128,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
 AT_CHECK([tail -1 stdout], [0],
   [Datapath actions: set(tunnel(tun_id=0x0,src=0.0.0.0,dst=1.1.1.1,tos=0x5,ttl=128,flags(df))),1
 ])
 
 dnl non-IP
-AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(2),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),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)'], [0], [stdout])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),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)'], [0], [stdout])
 AT_CHECK([tail -1 stdout], [0],
   [Datapath actions: set(tunnel(tun_id=0x0,src=0.0.0.0,dst=1.1.1.1,tos=0x0,ttl=64,flags(df))),1
 ])
@@ -210,19 +190,15 @@ actions=set_tunnel:1,output:1,set_tunnel:2,output:2,set_tunnel:3,output:3,set_tu
 
 AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
 
-AT_CHECK([ovs-appctl dpif/show], [0], [dnl
-br0 (dummy@ovs-dummy):
-       lookups: hit:0 missed:0
-       flows: cur: 0, avg: 0.000, max: 0, life span: 0(ms)
-               overall avg: add rate: 0.000/min, del rate: 0.000/min
-       br0 65534/100: (dummy)
-       p1 1/1: (gre: key=flow, remote_ip=1.1.1.1)
-       p2 2/1: (gre: key=flow, remote_ip=2.2.2.2)
-       p3 3/1: (gre: key=flow, remote_ip=3.3.3.3)
-       p4 4/1: (gre: key=flow, remote_ip=4.4.4.4)
+AT_CHECK([ovs-appctl dpif/show | tail -n +5], [0], [dnl
+               br0 65534/100: (dummy)
+               p1 1/1: (gre: key=flow, remote_ip=1.1.1.1)
+               p2 2/1: (gre: key=flow, remote_ip=2.2.2.2)
+               p3 3/1: (gre: key=flow, remote_ip=3.3.3.3)
+               p4 4/1: (gre: key=flow, remote_ip=4.4.4.4)
 ])
 
-AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(100),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=128,frag=no),icmp(type=8,code=0)'], [0], [stdout])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(100),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=128,frag=no),icmp(type=8,code=0)'], [0], [stdout])
 AT_CHECK([tail -1 stdout], [0], [Datapath actions: dnl
 set(tunnel(tun_id=0x1,src=0.0.0.0,dst=1.1.1.1,tos=0x0,ttl=64,flags(df,key))),1,dnl
 set(tunnel(tun_id=0x2,src=0.0.0.0,dst=2.2.2.2,tos=0x0,ttl=64,flags(df,key))),1,dnl
@@ -246,40 +222,36 @@ actions=IN_PORT,output:1,output:2,output:3
 
 AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
 
-AT_CHECK([ovs-appctl dpif/show], [0], [dnl
-br0 (dummy@ovs-dummy):
-       lookups: hit:0 missed:0
-       flows: cur: 0, avg: 0.000, max: 0, life span: 0(ms)
-               overall avg: add rate: 0.000/min, del rate: 0.000/min
-       br0 65534/100: (dummy)
-       p1 1/1: (gre: key=1, remote_ip=1.1.1.1)
-       p2 2/1: (gre: in_key=2, out_key=3, remote_ip=1.1.1.1)
-       p3 3/1: (gre: out_key=5, remote_ip=1.1.1.1)
+AT_CHECK([ovs-appctl dpif/show | tail -n +5], [0], [dnl
+               br0 65534/100: (dummy)
+               p1 1/1: (gre: key=1, remote_ip=1.1.1.1)
+               p2 2/1: (gre: in_key=2, out_key=3, remote_ip=1.1.1.1)
+               p3 3/1: (gre: out_key=5, remote_ip=1.1.1.1)
 ])
 
-AT_CHECK([ovs-appctl ofproto/trace br0 'tunnel(tun_id=0x1,src=1.1.1.1,dst=2.2.2.2,tos=0x0,ttl=64,flags(key)),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=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'tunnel(tun_id=0x1,src=1.1.1.1,dst=2.2.2.2,tos=0x0,ttl=64,flags(key)),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=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
 AT_CHECK([tail -1 stdout], [0], [Datapath actions: dnl
 set(tunnel(tun_id=0x1,src=0.0.0.0,dst=1.1.1.1,tos=0x0,ttl=64,flags(df,key))),1,dnl
 set(tunnel(tun_id=0x3,src=0.0.0.0,dst=1.1.1.1,tos=0x0,ttl=64,flags(df,key))),1,dnl
 set(tunnel(tun_id=0x5,src=0.0.0.0,dst=1.1.1.1,tos=0x0,ttl=64,flags(df,key))),1
 ])
 
-AT_CHECK([ovs-appctl ofproto/trace br0 'tunnel(tun_id=0x2,src=1.1.1.1,dst=2.2.2.2,tos=0x0,ttl=64,flags(key)),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=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'tunnel(tun_id=0x2,src=1.1.1.1,dst=2.2.2.2,tos=0x0,ttl=64,flags(key)),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=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
 AT_CHECK([tail -1 stdout], [0], [Datapath actions: dnl
 set(tunnel(tun_id=0x3,src=0.0.0.0,dst=1.1.1.1,tos=0x0,ttl=64,flags(df,key))),1,dnl
 set(tunnel(tun_id=0x1,src=0.0.0.0,dst=1.1.1.1,tos=0x0,ttl=64,flags(df,key))),1,dnl
 set(tunnel(tun_id=0x5,src=0.0.0.0,dst=1.1.1.1,tos=0x0,ttl=64,flags(df,key))),1
 ])
 
-AT_CHECK([ovs-appctl ofproto/trace br0 'tunnel(tun_id=0x0,src=1.1.1.1,dst=2.2.2.2,tos=0x0,ttl=64,flags()),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=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'tunnel(tun_id=0x0,src=1.1.1.1,dst=2.2.2.2,tos=0x0,ttl=64,flags()),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=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
 AT_CHECK([tail -1 stdout], [0], [Datapath actions: dnl
 set(tunnel(tun_id=0x5,src=0.0.0.0,dst=1.1.1.1,tos=0x0,ttl=64,flags(df,key))),1,dnl
 set(tunnel(tun_id=0x1,src=0.0.0.0,dst=1.1.1.1,tos=0x0,ttl=64,flags(df,key))),1,dnl
 set(tunnel(tun_id=0x3,src=0.0.0.0,dst=1.1.1.1,tos=0x0,ttl=64,flags(df,key))),1
 ])
 
-AT_CHECK([ovs-appctl ofproto/trace br0 'tunnel(tun_id=0xf,src=1.1.1.1,dst=2.2.2.2,tos=0x0,ttl=64,flags(key)),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=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [2], [ignore], [dnl
-Invalid flow
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'tunnel(tun_id=0xf,src=1.1.1.1,dst=2.2.2.2,tos=0x0,ttl=64,flags(key)),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=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [2], [ignore], [dnl
+Invalid datapath flow
 ovs-appctl: ovs-vswitchd: server returned an error
 ])
 OVS_VSWITCHD_STOP(["/receive tunnel port not found/d"])
@@ -302,35 +274,31 @@ tun_id=4,actions=output:5
 
 AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
 
-AT_CHECK([ovs-appctl dpif/show], [0], [dnl
-br0 (dummy@ovs-dummy):
-       lookups: hit:0 missed:0
-       flows: cur: 0, avg: 0.000, max: 0, life span: 0(ms)
-               overall avg: add rate: 0.000/min, del rate: 0.000/min
-       br0 65534/100: (dummy)
-       p1 1/1: (gre: key=flow, remote_ip=1.1.1.1)
-       p2 2/1: (gre: key=3, remote_ip=3.3.3.3)
-       p3 3/3: (dummy)
-       p4 4/4: (dummy)
-       p5 5/5: (dummy)
+AT_CHECK([ovs-appctl dpif/show | tail -n +5], [0], [dnl
+               br0 65534/100: (dummy)
+               p1 1/1: (gre: key=flow, remote_ip=1.1.1.1)
+               p2 2/1: (gre: key=3, remote_ip=3.3.3.3)
+               p3 3/3: (dummy)
+               p4 4/4: (dummy)
+               p5 5/5: (dummy)
 ])
 
-AT_CHECK([ovs-appctl ofproto/trace br0 'tunnel(tun_id=0x2,src=1.1.1.1,dst=2.2.2.2,tos=0x0,ttl=64,flags(key)),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=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'tunnel(tun_id=0x2,src=1.1.1.1,dst=2.2.2.2,tos=0x0,ttl=64,flags(key)),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=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
 AT_CHECK([tail -1 stdout], [0], [dnl
 Datapath actions: 3
 ])
 
-AT_CHECK([ovs-appctl ofproto/trace br0 'tunnel(tun_id=0x3,src=1.1.1.1,dst=2.2.2.2,tos=0x0,ttl=64,flags(key)),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=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'tunnel(tun_id=0x3,src=1.1.1.1,dst=2.2.2.2,tos=0x0,ttl=64,flags(key)),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=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
 AT_CHECK([tail -1 stdout], [0], [dnl
 Datapath actions: 4,3,set(tunnel(tun_id=0x3,src=0.0.0.0,dst=3.3.3.3,tos=0x0,ttl=64,flags(df,key))),1,5
 ])
 
-AT_CHECK([ovs-appctl ofproto/trace br0 'tunnel(tun_id=0x3,src=3.3.3.3,dst=2.2.2.2,tos=0x0,ttl=64,flags(key)),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=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'tunnel(tun_id=0x3,src=3.3.3.3,dst=2.2.2.2,tos=0x0,ttl=64,flags(key)),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=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
 AT_CHECK([tail -1 stdout], [0], [dnl
 Datapath actions: 4,3,5
 ])
 
-AT_CHECK([ovs-appctl ofproto/trace br0 'tunnel(tun_id=0x0,src=1.1.1.1,dst=2.2.2.2,tos=0x0,ttl=64,flags(key)),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=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'tunnel(tun_id=0x0,src=1.1.1.1,dst=2.2.2.2,tos=0x0,ttl=64,flags(key)),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=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
 AT_CHECK([tail -1 stdout], [0], [dnl
        - Sends "packet-in" messages to the OpenFlow controller.
 ])
@@ -342,13 +310,9 @@ AT_SETUP([tunnel - VXLAN])
 OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=vxlan \
                     options:remote_ip=1.1.1.1 ofport_request=1])
 
-AT_CHECK([ovs-appctl dpif/show], [0], [dnl
-br0 (dummy@ovs-dummy):
-       lookups: hit:0 missed:0
-       flows: cur: 0, avg: 0.000, max: 0, life span: 0(ms)
-               overall avg: add rate: 0.000/min, del rate: 0.000/min
-       br0 65534/100: (dummy)
-       p1 1/1: (vxlan: remote_ip=1.1.1.1)
+AT_CHECK([ovs-appctl dpif/show | tail -n +5], [0], [dnl
+               br0 65534/100: (dummy)
+               p1 1/1: (vxlan: remote_ip=1.1.1.1)
 ])
 
 OVS_VSWITCHD_STOP
@@ -358,13 +322,9 @@ AT_SETUP([tunnel - LISP])
 OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=lisp \
                     options:remote_ip=1.1.1.1 ofport_request=1])
 
-AT_CHECK([ovs-appctl dpif/show], [0], [dnl
-br0 (dummy@ovs-dummy):
-       lookups: hit:0 missed:0
-       flows: cur: 0, avg: 0.000, max: 0, life span: 0(ms)
-               overall avg: add rate: 0.000/min, del rate: 0.000/min
-       br0 65534/100: (dummy)
-       p1 1/1: (lisp: remote_ip=1.1.1.1)
+AT_CHECK([ovs-appctl dpif/show | tail -n +5], [0], [dnl
+               br0 65534/100: (dummy)
+               p1 1/1: (lisp: remote_ip=1.1.1.1)
 ])
 
 OVS_VSWITCHD_STOP
@@ -374,39 +334,27 @@ AT_SETUP([tunnel - different VXLAN UDP port])
 OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=vxlan \
                     options:remote_ip=1.1.1.1 ofport_request=1 options:dst_port=4341])
 
-AT_CHECK([ovs-appctl dpif/show], [0], [dnl
-br0 (dummy@ovs-dummy):
-       lookups: hit:0 missed:0
-       flows: cur: 0, avg: 0.000, max: 0, life span: 0(ms)
-               overall avg: add rate: 0.000/min, del rate: 0.000/min
-       br0 65534/100: (dummy)
-       p1 1/1: (vxlan: dst_port=4341, remote_ip=1.1.1.1)
+AT_CHECK([ovs-appctl dpif/show | tail -n +5], [0], [dnl
+               br0 65534/100: (dummy)
+               p1 1/1: (vxlan: dst_port=4341, remote_ip=1.1.1.1)
 ])
 
 dnl change UDP port
 
 AT_CHECK([ovs-vsctl -- set Interface p1 options:dst_port=5000])
 
-AT_CHECK([ovs-appctl dpif/show], [0], [dnl
-br0 (dummy@ovs-dummy):
-       lookups: hit:0 missed:0
-       flows: cur: 0, avg: 0.000, max: 0, life span: 0(ms)
-               overall avg: add rate: 0.000/min, del rate: 0.000/min
-       br0 65534/100: (dummy)
-       p1 1/2: (vxlan: dst_port=5000, remote_ip=1.1.1.1)
+AT_CHECK([ovs-appctl dpif/show | tail -n +5], [0], [dnl
+               br0 65534/100: (dummy)
+               p1 1/2: (vxlan: dst_port=5000, remote_ip=1.1.1.1)
 ])
 
 dnl change UDP port to default
 
 AT_CHECK([ovs-vsctl -- set Interface p1 options:dst_port=4789])
 
-AT_CHECK([ovs-appctl dpif/show], [0], [dnl
-br0 (dummy@ovs-dummy):
-       lookups: hit:0 missed:0
-       flows: cur: 0, avg: 0.000, max: 0, life span: 0(ms)
-               overall avg: add rate: 0.000/min, del rate: 0.000/min
-       br0 65534/100: (dummy)
-       p1 1/1: (vxlan: remote_ip=1.1.1.1)
+AT_CHECK([ovs-appctl dpif/show | tail -n +5], [0], [dnl
+               br0 65534/100: (dummy)
+               p1 1/1: (vxlan: remote_ip=1.1.1.1)
 ])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
@@ -433,7 +381,7 @@ in_port=4 actions=set_field:2.2.2.2->tun_dst,output:4
 in_port=5 actions=set_field:5->tun_id
 ])
 AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
-AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(90),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=128,frag=no),icmp(type=8,code=0)'], [0], [stdout])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(90),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=128,frag=no),icmp(type=8,code=0)'], [0], [stdout])
 AT_CHECK([tail -1 stdout], [0],
   [Datapath actions: set(tunnel(tun_id=0x2a,src=0.0.0.0,dst=1.1.1.1,tos=0x0,ttl=64,flags(df,key))),1,set(tunnel(tun_id=0x2a,src=0.0.0.0,dst=3.3.3.3,tos=0x0,ttl=64,flags(df,key))),1,set(tunnel(tun_id=0x2a,src=1.1.1.1,dst=4.4.4.4,tos=0x0,ttl=64,flags(df,key))),1,set(tunnel(tun_id=0x3,src=0.0.0.0,dst=2.2.2.2,tos=0x0,ttl=64,flags(df,key))),1
 ])
diff --git a/tests/vlan-splinters.at b/tests/vlan-splinters.at
new file mode 100644 (file)
index 0000000..3cc6187
--- /dev/null
@@ -0,0 +1,46 @@
+AT_BANNER([VLAN splinters])
+
+AT_SETUP([VLAN splinters])
+OVS_VSWITCHD_START([], [], [=override])
+ADD_OF_PORTS([br0], 1, 2, 3, 4)
+AT_CHECK([ovs-vsctl \
+  -- set Bridge br0 fail-mode=standalone flood_vlans=0,9,11,15 \
+  -- set port br0 tag=0 \
+  -- set port p1 trunks=0,9,11,15 \
+  -- set interface p1 other-config:enable-vlan-splinters=true \
+  -- set port p2 tag=9 \
+  -- set port p3 tag=11 \
+  -- set port p4 tag=15])
+
+ovs-appctl dpif/show | sed -n '
+s/\./_/g
+s/^[[  ]]*\([[^        ]][[^   ]]*\) [[0-9]]*\/\([[0-9]]*\).*/\1=\2/p
+' > port-numbers
+cat port-numbers
+. ./port-numbers
+
+for args in '9 p2' '11 p3' '15 p4'; do
+    set $args
+    vlan=$1
+    eval access_port=\$$2
+    eval splinter_port=\$p1_$vlan
+
+    # Check that when a packet is received on $splinter_port, it is
+    # treated as if it had been received on p1 in the correct VLAN.
+    AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "in_port($splinter_port)"],
+             [0], [stdout])
+    AT_CHECK_UNQUOTED([sed -n '/^Flow/p; /^Datapath/p' stdout], [0], [dnl
+Flow: metadata=0,in_port=$p1,dl_vlan=$vlan,dl_vlan_pcp=0,dl_src=00:00:00:00:00:00,dl_dst=00:00:00:00:00:00,dl_type=0x05ff
+Datapath actions: $access_port
+])
+
+    # Check that when an OpenFlow action sends a packet to p1 on
+    # splintered VLAN $vlan, it is actually output to $splinter_port.
+    AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "in_port($access_port)"],
+             [0], [stdout])
+    AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: $splinter_port
+])
+done
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
index c537bad..0506a20 100644 (file)
@@ -253,7 +253,7 @@ A switch should also not forward IEEE 802.1D Spanning Tree Protocol
 packets with reserved multicast protocols:
 
     ovs-ofctl add-flow br0 \
-        "table=0, dl_dst=01:08:c2:00:00:00/ff:ff:ff:ff:ff:f0, actions=drop"
+        "table=0, dl_dst=01:80:c2:00:00:00/ff:ff:ff:ff:ff:f0, actions=drop"
 
 We could add flows to drop other protocols, but these demonstrate the
 pattern.
@@ -287,12 +287,12 @@ such a flow would be treated as it goes through the switch.
 
 Try this command:
 
-    ovs-appctl ofproto/trace br0 in_port=1,dl_dst=01:08:c2:00:00:05
+    ovs-appctl ofproto/trace br0 in_port=1,dl_dst=01:80:c2:00:00:05
 
 The output should look something like this:
 
-    Flow: metadata=0,in_port=1,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=01:08:c2:00:00:05,dl_type=0x0000
-    Rule: table=0 cookie=0 dl_dst=01:08:c2:00:00:00/ff:ff:ff:ff:ff:f0
+    Flow: metadata=0,in_port=1,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=01:80:c2:00:00:05,dl_type=0x0000
+    Rule: table=0 cookie=0 dl_dst=01:80:c2:00:00:00/ff:ff:ff:ff:ff:f0
     OpenFlow actions=drop
 
     Final flow: unchanged
@@ -315,11 +315,11 @@ interesting here.
 
 Try another command:
 
-    ovs-appctl ofproto/trace br0 in_port=1,dl_dst=01:08:c2:00:00:10
+    ovs-appctl ofproto/trace br0 in_port=1,dl_dst=01:80:c2:00:00:10
 
 The output should be:
 
-    Flow: metadata=0,in_port=1,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=01:08:c2:00:00:10,dl_type=0x0000
+    Flow: metadata=0,in_port=1,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=01:80:c2:00:00:10,dl_type=0x0000
     Rule: table=0 cookie=0 priority=0
     OpenFlow actions=resubmit(,1)
 
index 55687ee..63db6f0 100755 (executable)
@@ -4,6 +4,6 @@ ovs-ofctl add-flow br0 \
     "table=0, dl_src=01:00:00:00:00:00/01:00:00:00:00:00, actions=drop"
 
 ovs-ofctl add-flow br0 \
-    "table=0, dl_dst=01:08:c2:00:00:00/ff:ff:ff:ff:ff:f0, actions=drop"
+    "table=0, dl_dst=01:80:c2:00:00:00/ff:ff:ff:ff:ff:f0, actions=drop"
 
 ovs-ofctl add-flow br0 "table=0, priority=0, actions=resubmit(,1)"
index ad99dda..a0bd97f 100644 (file)
@@ -19,7 +19,6 @@
 /ovs-ofctl
 /ovs-ofctl.8
 /ovs-parse-backtrace
-/ovs-parse-leaks
 /ovs-pcap
 /ovs-pcap.1
 /ovs-pki
index ab8774a..797748c 100644 (file)
@@ -4,7 +4,7 @@ bin_PROGRAMS += \
        utilities/ovs-dpctl \
        utilities/ovs-ofctl \
        utilities/ovs-vsctl
-bin_SCRIPTS += utilities/ovs-pki utilities/ovs-parse-leaks
+bin_SCRIPTS += utilities/ovs-pki
 if HAVE_PYTHON
 bin_SCRIPTS += \
        utilities/ovs-l3ping \
@@ -26,7 +26,6 @@ EXTRA_DIST += \
        utilities/ovs-l3ping.in \
        utilities/ovs-lib.in \
        utilities/ovs-parse-backtrace.in \
-       utilities/ovs-parse-leaks.in \
        utilities/ovs-pcap.in \
        utilities/ovs-pki.in \
        utilities/ovs-save \
@@ -42,7 +41,6 @@ MAN_ROOTS += \
        utilities/ovs-l3ping.8.in \
        utilities/ovs-ofctl.8.in \
        utilities/ovs-parse-backtrace.8 \
-       utilities/ovs-parse-leaks.8 \
        utilities/ovs-pcap.1.in \
        utilities/ovs-pki.8.in \
        utilities/ovs-tcpundump.1.in \
@@ -63,7 +61,6 @@ DISTCLEANFILES += \
        utilities/ovs-lib \
        utilities/ovs-ofctl.8 \
        utilities/ovs-parse-backtrace \
-       utilities/ovs-parse-leaks \
        utilities/ovs-pcap \
        utilities/ovs-pcap.1 \
        utilities/ovs-pki \
@@ -85,7 +82,6 @@ man_MANS += \
        utilities/ovs-l3ping.8 \
        utilities/ovs-ofctl.8 \
        utilities/ovs-parse-backtrace.8 \
-       utilities/ovs-parse-leaks.8 \
        utilities/ovs-pcap.1 \
        utilities/ovs-pki.8 \
        utilities/ovs-tcpundump.1 \
index 70bc2e6..6d08af5 100644 (file)
@@ -136,7 +136,7 @@ main(int argc, char *argv[])
             }
         }
         if (retval) {
-            VLOG_ERR("%s: connect: %s", name, strerror(retval));
+            VLOG_ERR("%s: connect: %s", name, ovs_strerror(retval));
         }
     }
     if (n_switches == 0 && n_listeners == 0) {
index 1f10491..6465fdf 100755 (executable)
@@ -202,6 +202,19 @@ start_ovsdb () {
     fi
 }
 
+add_managers () {
+    # Now that ovs-vswitchd has started and completed its initial
+    # configuration, tell ovsdb-server to conenct to the remote managers.  We
+    # used to do this at ovsdb-server startup time, but waiting for
+    # ovs-vswitchd to finish configuring means that remote managers see less
+    # churn in the database at startup or restart.  (For example, managers
+    # won't briefly see empty datapath-id or ofport columns for records that
+    # exist at startup.)
+    action "Enabling remote OVSDB managers" \
+       ovs-appctl -t ovsdb-server ovsdb-server/add-remote \
+           db:Open_vSwitch,Open_vSwitch,manager_options
+}
+
 start_forwarding () {
     check_force_cores
 
@@ -224,17 +237,6 @@ start_forwarding () {
            fi
            start_daemon "$OVS_VSWITCHD_PRIORITY" "$OVS_VSWITCHD_WRAPPER" "$@"
     fi
-
-    # Now that ovs-vswitchd has started and completed its initial
-    # configuration, tell ovsdb-server to conenct to the remote managers.  We
-    # used to do this at ovsdb-server startup time, but waiting for
-    # ovs-vswitchd to finish configuring means that remote managers see less
-    # churn in the database at startup or restart.  (For example, managers
-    # won't briefly see empty datapath-id or ofport columns for records that
-    # exist at startup.)
-    action "Enabling remote OVSDB managers" \
-       ovs-appctl -t ovsdb-server ovsdb-server/add-remote \
-           db:Open_vSwitch,Open_vSwitch,manager_options
 }
 
 ## ---- ##
@@ -304,6 +306,15 @@ restore_ofports () {
         action "Restoring ofport values" "${script_ofports}"
 }
 
+flow_restore_wait () {
+    ovs_vsctl set open_vswitch . other_config:flow-restore-wait="true"
+}
+
+flow_restore_complete () {
+    ovs_vsctl --if-exists remove open_vswitch . other_config \
+        flow-restore-wait="true"
+}
+
 restore_flows () {
     [ -x "${script_flows}" ] && \
         action "Restoring saved flows" "${script_flows}"
@@ -355,6 +366,7 @@ force_reload_kmod () {
     else
         log_warning_msg "Failed to save configuration, not replacing kernel module"
         start_forwarding
+        add_managers
         exit 1
     fi
     chmod +x "$script_interfaces"
@@ -370,9 +382,14 @@ force_reload_kmod () {
         action "Removing openvswitch module" rmmod openvswitch
     fi
 
+    # Start vswitchd by asking it to wait till flow restore is finished.
+    flow_restore_wait
     start_forwarding
 
+    # Restore saved flows and inform vswitchd that we are done.
     restore_flows
+    flow_restore_complete
+    add_managers
 
     restore_interfaces
 
@@ -414,10 +431,15 @@ restart () {
     restore_ofports
 
     stop_forwarding
+
+    # Start vswitchd by asking it to wait till flow restore is finished.
+    flow_restore_wait
     start_forwarding
 
-    # Restore the saved flows.
+    # Restore saved flows and inform vswitchd that we are done.
     restore_flows
+    flow_restore_complete
+    add_managers
 
     # Restore the interfaces if required. Return true even if restore fails.
     restore_interfaces || true
@@ -664,6 +686,7 @@ case $command in
     start)
         start_ovsdb
         start_forwarding
+        add_managers
         ;;
     stop)
         stop_forwarding
index 54505e8..2389942 100644 (file)
@@ -308,7 +308,7 @@ dpctl_add_if(int argc OVS_UNUSED, char *argv[])
         char *save_ptr = NULL;
         struct netdev *netdev = NULL;
         struct smap args;
-        uint32_t port_no = UINT32_MAX;
+        odp_port_t port_no = ODPP_NONE;
         char *option;
         int error;
 
@@ -335,7 +335,7 @@ dpctl_add_if(int argc OVS_UNUSED, char *argv[])
             if (!strcmp(key, "type")) {
                 type = value;
             } else if (!strcmp(key, "port_no")) {
-                port_no = atoi(value);
+                port_no = u32_to_odp(atoi(value));
             } else if (!smap_add_once(&args, key, value)) {
                 ovs_error(0, "duplicate \"%s\" option", key);
             }
@@ -388,7 +388,7 @@ dpctl_set_if(int argc, char *argv[])
         char *type = NULL;
         const char *name;
         struct smap args;
-        uint32_t port_no;
+        odp_port_t port_no;
         char *option;
         int error;
 
@@ -441,7 +441,7 @@ dpctl_set_if(int argc, char *argv[])
                     failure = true;
                 }
             } else if (!strcmp(key, "port_no")) {
-                if (port_no != atoi(value)) {
+                if (port_no != u32_to_odp(atoi(value))) {
                     ovs_error(0, "%s: can't change port number from "
                               "%"PRIu32" to %d",
                               name, port_no, atoi(value));
@@ -476,7 +476,7 @@ next:
 }
 
 static bool
-get_port_number(struct dpif *dpif, const char *name, uint32_t *port)
+get_port_number(struct dpif *dpif, const char *name, odp_port_t *port)
 {
     struct dpif_port dpif_port;
 
@@ -500,11 +500,11 @@ 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];
-        uint32_t port;
+        odp_port_t port;
         int error;
 
         if (!name[strspn(name, "0123456789")]) {
-            port = atoi(name);
+            port = u32_to_odp(atoi(name));
         } else if (!get_port_number(dpif, name, &port)) {
             failure = true;
             continue;
@@ -590,13 +590,13 @@ show_dpif(struct dpif *dpif)
                     free(nodes);
                 } else {
                     printf(", could not retrieve configuration (%s)",
-                           strerror(error));
+                           ovs_strerror(error));
                 }
                 smap_destroy(&config);
 
                 netdev_close(netdev);
             } else {
-                printf(": open failed (%s)", strerror(error));
+                printf(": open failed (%s)", ovs_strerror(error));
             }
             putchar(')');
         }
@@ -608,12 +608,12 @@ show_dpif(struct dpif *dpif)
 
             error = netdev_open(dpif_port.name, dpif_port.type, &netdev);
             if (error) {
-                printf(", open failed (%s)", strerror(error));
+                printf(", open failed (%s)", ovs_strerror(error));
                 continue;
             }
             error = netdev_get_stats(netdev, &s);
             if (error) {
-                printf(", could not retrieve stats (%s)", strerror(error));
+                printf(", could not retrieve stats (%s)", ovs_strerror(error));
                 continue;
             }
 
@@ -743,9 +743,11 @@ dpctl_dump_flows(int argc, char *argv[])
     const struct nlattr *actions;
     struct dpif_flow_dump dump;
     const struct nlattr *key;
+    const struct nlattr *mask;
     size_t actions_len;
     struct dpif *dpif;
     size_t key_len;
+    size_t mask_len;
     struct ds ds;
     char *name;
 
@@ -756,10 +758,12 @@ dpctl_dump_flows(int argc, char *argv[])
     ds_init(&ds);
     dpif_flow_dump_start(&dump, dpif);
     while (dpif_flow_dump_next(&dump, &key, &key_len,
+                               &mask, &mask_len,
                                &actions, &actions_len, &stats)) {
         ds_clear(&ds);
-        odp_flow_key_format(key, key_len, &ds);
+        odp_flow_format(key, key_len, mask, mask_len, &ds);
         ds_put_cstr(&ds, ", ");
+
         dpif_flow_stats_format(stats, &ds);
         ds_put_cstr(&ds, ", actions:");
         format_odp_actions(&ds, actions, actions_len);
@@ -778,26 +782,32 @@ dpctl_put_flow(int argc, char *argv[], enum dpif_flow_put_flags flags)
     struct dpif_flow_stats stats;
     struct ofpbuf actions;
     struct ofpbuf key;
+    struct ofpbuf mask;
     struct dpif *dpif;
+    struct ds s;
     char *dp_name;
 
+    ds_init(&s);
     ofpbuf_init(&key, 0);
-    run(odp_flow_key_from_string(key_s, NULL, &key), "parsing flow key");
+    ofpbuf_init(&mask, 0);
+    run(odp_flow_from_string(key_s, NULL, &key, &mask), "parsing flow key");
 
     ofpbuf_init(&actions, 0);
     run(odp_actions_from_string(actions_s, NULL, &actions), "parsing actions");
 
-    dp_name = argc == 3 ? xstrdup(argv[1]) : get_one_dp();
+    dp_name = argc == 4 ? xstrdup(argv[1]) : get_one_dp();
     run(parsed_dpif_open(dp_name, false, &dpif), "opening datapath");
     free(dp_name);
 
     run(dpif_flow_put(dpif, flags,
                       key.data, key.size,
+                      mask.size == 0 ? NULL : mask.data, mask.size,
                       actions.data, actions.size,
                       print_statistics ? &stats : NULL),
         "updating flow table");
 
     ofpbuf_uninit(&key);
+    ofpbuf_uninit(&mask);
     ofpbuf_uninit(&actions);
 
     if (print_statistics) {
@@ -838,11 +848,13 @@ dpctl_del_flow(int argc, char *argv[])
     const char *key_s = argv[argc - 1];
     struct dpif_flow_stats stats;
     struct ofpbuf key;
+    struct ofpbuf mask; /* To be ignored. */
     struct dpif *dpif;
     char *dp_name;
 
     ofpbuf_init(&key, 0);
-    run(odp_flow_key_from_string(key_s, NULL, &key), "parsing flow key");
+    ofpbuf_init(&mask, 0);
+    run(odp_flow_from_string(key_s, NULL, &key, &mask), "parsing flow key");
 
     dp_name = argc == 2 ? xstrdup(argv[1]) : get_one_dp();
     run(parsed_dpif_open(dp_name, false, &dpif), "opening datapath");
@@ -853,6 +865,7 @@ dpctl_del_flow(int argc, char *argv[])
                       print_statistics ? &stats : NULL), "deleting flow");
 
     ofpbuf_uninit(&key);
+    ofpbuf_uninit(&mask);
 
     if (print_statistics) {
         struct ds s;
@@ -1032,11 +1045,11 @@ dpctl_normalize_actions(int argc, char *argv[])
 
     /* Parse flow key. */
     ofpbuf_init(&keybuf, 0);
-    run(odp_flow_key_from_string(argv[1], &port_names, &keybuf),
+    run(odp_flow_from_string(argv[1], &port_names, &keybuf, NULL),
         "odp_flow_key_from_string");
 
     ds_clear(&s);
-    odp_flow_key_format(keybuf.data, keybuf.size, &s);
+    odp_flow_format(keybuf.data, keybuf.size, NULL, 0, &s);
     printf("input flow: %s\n", ds_cstr(&s));
 
     run(odp_flow_key_to_flow(keybuf.data, keybuf.size, &flow),
index 061982b..e66c605 100644 (file)
@@ -944,9 +944,10 @@ followed by another \fBpush_mpls\fR will result in the first
 \fBpush_mpls\fR being discarded.
 .
 .IP \fBpop_mpls\fR:\fIethertype\fR
-Strips the outermost MPLS label stack entry.  If the MPLS label
-stripped was the only one, changes the ethertype of a packet to
-\fIethertype\fR, which should not ordinarily be an MPLS Ethertype.
+Strips the outermost MPLS label stack entry.
+Currently the implementation restricts \fIethertype\fR to a non-MPLS Ethertype
+and thus \fBpop_mpls\fR should only be applied to packets with
+an MPLS label stack depth of one.
 .
 .IP
 There are some limitations in the implementation.  \fBpop_mpls\fR
index 24b9434..adf86ad 100644 (file)
@@ -361,7 +361,7 @@ open_vconn_socket(const char *name, struct vconn **vconnp)
                        vconnp);
     if (error && error != ENOENT) {
         ovs_fatal(0, "%s: failed to open socket (%s)", name,
-                  strerror(error));
+                  ovs_strerror(error));
     }
     free(vconn_name);
 
@@ -412,7 +412,7 @@ open_vconn__(const char *name, enum open_target target,
     error = vconn_connect_block(*vconnp);
     if (error) {
         ovs_fatal(0, "%s: failed to connect to socket (%s)", name,
-                  strerror(error));
+                  ovs_strerror(error));
     }
 
     ofp_version = vconn_get_version(*vconnp);
@@ -632,7 +632,7 @@ ofctl_dump_tables(int argc OVS_UNUSED, char *argv[])
 
 static bool
 fetch_port_by_features(const char *vconn_name,
-                       const char *port_name, unsigned int port_no,
+                       const char *port_name, ofp_port_t port_no,
                        struct ofputil_phy_port *pp, bool *trunc)
 {
     struct ofputil_switch_features features;
@@ -670,7 +670,7 @@ fetch_port_by_features(const char *vconn_name,
     }
 
     while (!ofputil_pull_phy_port(oh->version, &b, pp)) {
-        if (port_no != UINT_MAX
+        if (port_no != OFPP_NONE
             ? port_no == pp->port_no
             : !strcmp(pp->name, port_name)) {
             found = true;
@@ -685,7 +685,7 @@ exit:
 
 static bool
 fetch_port_by_stats(const char *vconn_name,
-                    const char *port_name, unsigned int port_no,
+                    const char *port_name, ofp_port_t port_no,
                     struct ofputil_phy_port *pp)
 {
     struct ofpbuf *request;
@@ -729,8 +729,8 @@ fetch_port_by_stats(const char *vconn_name,
             }
 
             while (!ofputil_pull_phy_port(oh->version, &b, pp)) {
-                if (port_no != UINT_MAX ? port_no == pp->port_no
-                                        : !strcmp(pp->name, port_name)) {
+                if (port_no != OFPP_NONE ? port_no == pp->port_no
+                                         : !strcmp(pp->name, port_name)) {
                     found = true;
                     break;
                 }
@@ -746,6 +746,16 @@ fetch_port_by_stats(const char *vconn_name,
     return found;
 }
 
+static bool
+str_to_ofp(const char *s, ofp_port_t *ofp_port)
+{
+    bool ret;
+    uint32_t port_;
+
+    ret = str_to_uint(s, 10, &port_);
+    *ofp_port = u16_to_ofp(port_);
+    return ret;
+}
 
 /* Opens a connection to 'vconn_name', fetches the port structure for
  * 'port_name' (which may be a port name or number), and copies it into
@@ -754,13 +764,13 @@ static void
 fetch_ofputil_phy_port(const char *vconn_name, const char *port_name,
                        struct ofputil_phy_port *pp)
 {
-    unsigned int port_no;
+    ofp_port_t port_no;
     bool found;
     bool trunc;
 
     /* Try to interpret the argument as a port number. */
-    if (!str_to_uint(port_name, 10, &port_no)) {
-        port_no = UINT_MAX;
+    if (!str_to_ofp(port_name, &port_no)) {
+        port_no = OFPP_NONE;
     }
 
     /* Try to find the port based on the Features Reply.  If it looks
@@ -779,10 +789,10 @@ fetch_ofputil_phy_port(const char *vconn_name, const char *port_name,
 
 /* Returns the port number corresponding to 'port_name' (which may be a port
  * name or number) within the switch 'vconn_name'. */
-static uint16_t
+static ofp_port_t
 str_to_port_no(const char *vconn_name, const char *port_name)
 {
-    uint16_t port_no;
+    ofp_port_t port_no;
 
     if (ofputil_port_from_string(port_name, &port_no)) {
         return port_no;
@@ -1223,7 +1233,7 @@ ofctl_send(struct unixctl_conn *conn, int argc,
         error = vconn_send_block(vconn, msg);
         if (error) {
             ofpbuf_delete(msg);
-            ds_put_format(&reply, "%s\n", strerror(error));
+            ds_put_format(&reply, "%s\n", ovs_strerror(error));
             ok = false;
         } else {
             ds_put_cstr(&reply, "sent\n");
@@ -1260,7 +1270,7 @@ ofctl_barrier(struct unixctl_conn *conn, int argc OVS_UNUSED,
     error = vconn_send_block(aux->vconn, msg);
     if (error) {
         ofpbuf_delete(msg);
-        unixctl_command_reply_error(conn, strerror(error));
+        unixctl_command_reply_error(conn, ovs_strerror(error));
     } else {
         aux->conn = conn;
     }
@@ -1274,7 +1284,7 @@ ofctl_set_output_file(struct unixctl_conn *conn, int argc OVS_UNUSED,
 
     fd = open(argv[1], O_CREAT | O_TRUNC | O_WRONLY, 0666);
     if (fd < 0) {
-        unixctl_command_reply_error(conn, strerror(errno));
+        unixctl_command_reply_error(conn, ovs_strerror(errno));
         return;
     }
 
@@ -1489,7 +1499,7 @@ ofctl_dump_ports(int argc, char *argv[])
 {
     struct ofpbuf *request;
     struct vconn *vconn;
-    uint16_t port;
+    ofp_port_t port;
 
     open_vconn(argv[1], &vconn);
     port = argc > 2 ? str_to_port_no(argv[1], argv[2]) : OFPP_ANY;
@@ -2607,10 +2617,20 @@ ofctl_parse_ofp11_instructions(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
         enum ofperr error;
         size_t size;
         struct ds s;
+        const char *table_id;
+        char *instructions;
+
+        /* Parse table_id separated with the follow-up instructions by ",", if
+         * any. */
+        instructions = ds_cstr(&in);
+        table_id = NULL;
+        if (strstr(instructions, ",")) {
+            table_id = strsep(&instructions, ",");
+        }
 
         /* Parse hex bytes. */
         ofpbuf_init(&of11_in, 0);
-        if (ofpbuf_put_hex(&of11_in, ds_cstr(&in), NULL)[0] != '\0') {
+        if (ofpbuf_put_hex(&of11_in, instructions, NULL)[0] != '\0') {
             ovs_fatal(0, "Trailing garbage in hex data");
         }
 
@@ -2619,6 +2639,13 @@ ofctl_parse_ofp11_instructions(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
         size = of11_in.size;
         error = ofpacts_pull_openflow11_instructions(&of11_in, of11_in.size,
                                                      &ofpacts);
+        if (!error) {
+            /* Verify actions. */
+            struct flow flow;
+            memset(&flow, 0, sizeof flow);
+            error = ofpacts_check(ofpacts.data, ofpacts.size, &flow, OFPP_MAX,
+                                  table_id ? atoi(table_id) : 0);
+        }
         if (error) {
             printf("bad OF1.1 instructions: %s\n\n", ofperr_get_name(error));
             ofpbuf_uninit(&ofpacts);
@@ -2765,13 +2792,16 @@ ofctl_print_error(int argc OVS_UNUSED, char *argv[])
 
     for (version = 0; version <= UINT8_MAX; version++) {
         const char *name = ofperr_domain_get_name(version);
-        if (!name) {
-            continue;
+        if (name) {
+            int vendor = ofperr_get_vendor(error, version);
+            int type = ofperr_get_type(error, version);
+            int code = ofperr_get_code(error, version);
+
+            if (vendor != -1 || type != -1 || code != -1) {
+                printf("%s: vendor %#x, type %d, code %d\n",
+                       name, vendor, type, code);
+            }
         }
-        printf("%s: %d,%d\n",
-               ofperr_domain_get_name(version),
-               ofperr_get_type(error, version),
-               ofperr_get_code(error, version));
     }
 }
 
diff --git a/utilities/ovs-parse-leaks.8 b/utilities/ovs-parse-leaks.8
deleted file mode 100644 (file)
index e7bd1c5..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-.TH ovs\-parse\-leaks 8 "August 2010" "Open vSwitch" "Open vSwitch Manual"
-.
-.SH NAME
-ovs\-parse\-leaks \- parses OVS leak checker log files
-.
-.SH SYNOPSIS
-\fBovs\-parse\-leaks\fR [\fIbinary\fR] \fB< \fIlog\fR
-.
-.SH DESCRIPTION
-Many Open vSwitch daemons accept a \fB\-\-check\-leaks\fR option that
-writes information about memory allocation and deallocation to a log
-file.  \fBovs\-parse\-leaks\fR parses log files produced by this
-option and prints a summary of the results.  The most interesting part
-of the output is a list of memory blocks that were allocated but not
-freed, which Open vSwitch developers can use to find and fix memory
-leaks.
-.PP
-The log file must be supplied on standard input.  The binary that
-produced the output should be supplied as the sole non-option
-argument.  For best results, the binary should have debug symbols.
-.
-.SH OPTIONS
-.TP
-\fB\-\-help\fR
-Prints a usage message and exits.
-.SH BUGS
-The output can be hard to interpret, especially for a daemon that does
-not exit in normal operation.  Using \fBovs\-appctl\fR(8) to invoke
-the \fBexit\fR command that some Open vSwitch daemons support
-sometimes helps with this.
-.PP
-\fBovs\-parse\-leaks\fR usually incorrectly reports one or more ``bad
-frees of not-allocated address'' errors at the beginning of output.
-These reflect frees of data that were allocated before the leak
-checker was turned on during program initialization.
diff --git a/utilities/ovs-parse-leaks.in b/utilities/ovs-parse-leaks.in
deleted file mode 100755 (executable)
index 72417e5..0000000
+++ /dev/null
@@ -1,299 +0,0 @@
-#! @PERL@
-
-# Copyright (c) 2009, 2010 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.
-
-use strict;
-use warnings;
-
-if (grep($_ eq '--help', @ARGV)) {
-    print <<EOF;
-$0, for parsing leak checker logs
-usage: $0 [BINARY] < LOG
-where LOG is a file produced by an Open vSwitch program's --check-leaks option
-  and BINARY is the binary that wrote LOG.
-EOF
-    exit 0;
-}
-
-die "$0: zero or one arguments required; use --help for help\n" if @ARGV > 1;
-die "$0: $ARGV[0] does not exist" if @ARGV > 0 && ! -e $ARGV[0];
-
-our ($binary);
-our ($a2l) = search_path("addr2line");
-my ($no_syms) = "symbols will not be translated (use --help for help)";
-if (!@ARGV) {
-    print "no binary specified; $no_syms\n";
-} elsif (! -e $ARGV[0]) {
-    print "$ARGV[0] does not exist; $no_syms";
-} elsif (!defined($a2l)) {
-    print "addr2line not found in PATH; $no_syms";
-} else {
-    $binary = $ARGV[0];
-}
-
-our ($objdump) = search_path("objdump");
-print "objdump not found; dynamic library symbols will not be translated\n"
-  if !defined($objdump);
-
-our %blocks;
-our @segments;
-while (<STDIN>) {
-    my $ptr = "((?:0x)?[0-9a-fA-F]+|\\(nil\\))";
-    my $callers = ":((?: $ptr)+)";
-    if (/^malloc\((\d+)\) -> $ptr$callers$/) {
-        allocated($., $2, $1, $3);
-    } elsif (/^claim\($ptr\)$callers$/) {
-        claimed($., $1, $2);
-    } elsif (/realloc\($ptr, (\d+)\) -> $ptr$callers$/) {
-        my ($callers) = $4;
-        freed($., $1, $callers);
-        allocated($., $3, $2, $callers);
-    } elsif (/^free\($ptr\)$callers$/) {
-        freed($., $1, $2);
-    } elsif (/^segment: $ptr-$ptr $ptr [-r][-w][-x][sp] (.*)/) {
-        add_segment(hex($1), hex($2), hex($3), $4);
-    } else {
-        print "stdin:$.: syntax error\n";
-    }
-}
-if (%blocks) {
-    my $n_blocks = scalar(keys(%blocks));
-    my $n_bytes = 0;
-    $n_bytes += $_->{SIZE} foreach values(%blocks);
-    print "$n_bytes bytes in $n_blocks blocks not freed at end of run\n";
-    my %blocks_by_callers;
-    foreach my $block (values(%blocks)) {
-        my ($trimmed_callers) = trim_callers($block->{CALLERS});
-        push (@{$blocks_by_callers{$trimmed_callers}}, $block);
-    }
-    foreach my $callers (sort {@{$b} <=> @{$a}} (values(%blocks_by_callers))) {
-        $n_blocks = scalar(@{$callers});
-        $n_bytes = 0;
-        $n_bytes += $_->{SIZE} foreach @{$callers};
-        print "$n_bytes bytes in these $n_blocks blocks were not freed:\n";
-        my $i = 0;
-        my $max = 5;
-        foreach my $block (sort {$a->{LINE} <=> $b->{LINE}} (@{$callers})) {
-            printf "\t%d-byte block at 0x%08x allocated on stdin:%d\n",
-              $block->{SIZE}, $block->{BASE}, $block->{LINE};
-            last if $i++ > $max;
-        }
-        print "\t...and ", $n_blocks - $max, " others...\n"
-          if $n_blocks > $max;
-        print "The blocks listed above were allocated by:\n";
-        print_callers("\t", ${$callers}[0]->{CALLERS});
-    }
-}
-sub interp_pointer {
-    my ($s_ptr) = @_;
-    return $s_ptr eq '(nil)' ? 0 : hex($s_ptr);
-}
-
-sub allocated {
-    my ($line, $s_base, $size, $callers) = @_;
-    my ($base) = interp_pointer($s_base);
-    return if !$base;
-    my ($info) = {LINE => $line,
-                  BASE => $base,
-                  SIZE => $size,
-                  CALLERS => $callers};
-    if (exists($blocks{$base})) {
-        print "In-use address returned by allocator:\n";
-        print "\tInitial allocation:\n";
-        print_block("\t\t", $blocks{$base});
-        print "\tNew allocation:\n";
-        print_block("\t\t", $info);
-    }
-    $blocks{$base} = $info;
-}
-
-sub claimed {
-    my ($line, $s_base, $callers) = @_;
-    my ($base) = interp_pointer($s_base);
-    return if !$base;
-    if (exists($blocks{$base})) {
-       $blocks{$base}{LINE} = $line;
-       $blocks{$base}{CALLERS} = $callers;
-    } else {
-        printf "Claim asserted on not-in-use block 0x%08x by:\n", $base;
-       print_callers('', $callers);
-    }
-}
-
-sub freed {
-    my ($line, $s_base, $callers) = @_;
-    my ($base) = interp_pointer($s_base);
-    return if !$base;
-
-    if (!delete($blocks{$base})) {
-        printf "Bad free of not-allocated address 0x%08x on stdin:%d by:\n", $base, $line;
-        print_callers('', $callers);
-    }
-}
-
-sub print_block {
-    my ($prefix, $info) = @_;
-    printf '%s%d-byte block at 0x%08x allocated on stdin:%d by:' . "\n",
-      $prefix, $info->{SIZE}, $info->{BASE}, $info->{LINE};
-    print_callers($prefix, $info->{CALLERS});
-}
-
-sub print_callers {
-    my ($prefix, $callers) = @_;
-    foreach my $pc (split(' ', $callers)) {
-        print "$prefix\t", lookup_pc($pc), "\n";
-    }
-}
-
-our (%cache);
-sub lookup_pc {
-    my ($s_pc) = @_;
-    if (defined($binary)) {
-        my ($pc) = hex($s_pc);
-        my ($output) = "$s_pc: ";
-        if (!exists($cache{$pc})) {
-            open(A2L, "$a2l -fe $binary --demangle $s_pc|");
-            chomp(my $function = <A2L>);
-            chomp(my $line = <A2L>);
-            close(A2L);
-            if ($function eq '??') {
-                ($function, $line) = lookup_pc_by_segment($pc);
-            }
-            $line =~ s/^(\.\.\/)*//;
-            $line = "..." . substr($line, -25) if length($line) > 28;
-            $cache{$pc} = "$s_pc: $function ($line)";
-        }
-        return $cache{$pc};
-    } else {
-        return "$s_pc";
-    }
-}
-
-sub trim_callers {
-    my ($in) = @_;
-    my (@out);
-    foreach my $pc (split(' ', $in)) {
-        my $xlated = lookup_pc($pc);
-        if ($xlated =~ /\?\?/) {
-            push(@out, "...") if !@out || $out[$#out] ne '...';
-        } else {
-            push(@out, $pc);
-        }
-    }
-    return join(' ', @out);
-}
-
-sub search_path {
-    my ($target) = @_;
-    for my $dir (split (':', $ENV{PATH})) {
-       my ($file) = "$dir/$target";
-       return $file if -e $file;
-    }
-    return undef;
-}
-
-sub add_segment {
-    my ($vm_start, $vm_end, $vm_pgoff, $file) = @_;
-    for (my $i = 0; $i <= $#segments; $i++) {
-        my ($s) = $segments[$i];
-        next if $vm_end <= $s->{START} || $vm_start >= $s->{END};
-        if ($vm_start <= $s->{START} && $vm_end >= $s->{END}) {
-            splice(@segments, $i, 1);
-            --$i;
-        } else {
-            $s->{START} = $vm_end if $vm_end > $s->{START};
-            $s->{END} = $vm_start if $vm_start <= $s->{END};
-        }
-    }
-    push(@segments, {START => $vm_start,
-                     END => $vm_end,
-                     PGOFF => $vm_pgoff,
-                     FILE => $file});
-    @segments = sort { $a->{START} <=> $b->{START} } @segments;
-}
-
-sub binary_search {
-    my ($array, $value) = @_;
-    my $l = 0;
-    my $r = $#{$array};
-    while ($l <= $r) {
-        my $m = int(($l + $r) / 2);
-        my $e = $array->[$m];
-        if ($value < $e->{START}) {
-            $r = $m - 1;
-        } elsif ($value >= $e->{END}) {
-            $l = $m + 1;
-        } else {
-            return $e;
-        }
-    }
-    return undef;
-}
-
-sub read_sections {
-    my ($file) = @_;
-    my (@sections);
-    open(OBJDUMP, "$objdump -h $file|");
-    while (<OBJDUMP>) {
-        my $ptr = "([0-9a-fA-F]+)";
-        my ($name, $size, $vma, $lma, $file_off)
-          = /^\s*\d+\s+(\S+)\s+$ptr\s+$ptr\s+$ptr\s+$ptr/
-            or next;
-        push(@sections, {START => hex($file_off),
-                         END => hex($file_off) + hex($size),
-                         NAME => $name});
-    }
-    close(OBJDUMP);
-    return [sort { $a->{START} <=> $b->{START} } @sections ];
-}
-
-our %file_to_sections;
-sub segment_to_section {
-    my ($file, $file_offset) = @_;
-    if (!defined($file_to_sections{$file})) {
-        $file_to_sections{$file} = read_sections($file);
-    }
-    return binary_search($file_to_sections{$file}, $file_offset);
-}
-
-sub address_to_segment {
-    my ($pc) = @_;
-    return binary_search(\@segments, $pc);
-}
-
-sub lookup_pc_by_segment {
-    return ('??', 0) if !defined($objdump);
-
-    my ($pc) = @_;
-    my ($segment) = address_to_segment($pc);
-    return ('??', 0) if !defined($segment) || $segment->{FILE} eq '';
-
-    my ($file_offset) = $pc - $segment->{START} + $segment->{PGOFF};
-    my ($section) = segment_to_section($segment->{FILE}, $file_offset);
-    return ('??', 0) if !defined($section);
-
-    my ($section_offset) = $file_offset - $section->{START};
-    open(A2L, sprintf("%s -fe %s --demangle --section=$section->{NAME} 0x%x|",
-                      $a2l, $segment->{FILE}, $section_offset));
-    chomp(my $function = <A2L>);
-    chomp(my $line = <A2L>);
-    close(A2L);
-
-    return ($function, $line);
-}
-
-# Local Variables:
-# mode: perl
-# End:
index 19ab472..2d8c7c7 100644 (file)
@@ -2016,13 +2016,17 @@ cmd_del_port(struct vsctl_context *ctx)
 {
     bool must_exist = !shash_find(&ctx->options, "--if-exists");
     bool with_iface = shash_find(&ctx->options, "--with-iface") != NULL;
+    const char *target = ctx->argv[ctx->argc - 1];
     struct vsctl_port *port;
 
     vsctl_context_populate_cache(ctx);
-    if (!with_iface) {
-        port = find_port(ctx, ctx->argv[ctx->argc - 1], must_exist);
+    if (find_bridge(ctx, target, false)) {
+        vsctl_fatal("cannot delete port %s because it is the local port "
+                    "for bridge %s (deleting this port requires deleting "
+                    "the entire bridge)", target, target);
+    } else if (!with_iface) {
+        port = find_port(ctx, target, must_exist);
     } else {
-        const char *target = ctx->argv[ctx->argc - 1];
         struct vsctl_iface *iface;
 
         port = find_port(ctx, target, false);
index cf26e87..28bf082 100644 (file)
@@ -67,13 +67,13 @@ 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. */
+    ofp_port_t ofport;                  /* Requested OpenFlow port number. */
 };
 
 /* OpenFlow port slated for removal from ofproto. */
 struct ofpp_garbage {
     struct list list_node;      /* Node in bridge's ofpp_garbage. */
-    uint16_t ofp_port;          /* Port to be deleted. */
+    ofp_port_t ofp_port;        /* Port to be deleted. */
 };
 
 struct iface {
@@ -86,7 +86,8 @@ struct iface {
     /* These members are valid only after bridge_reconfigure() causes them to
      * be initialized. */
     struct hmap_node ofp_port_node; /* In struct bridge's "ifaces" hmap. */
-    int ofp_port;               /* OpenFlow port number, -1 if unknown. */
+    ofp_port_t ofp_port;        /* OpenFlow port number, */
+                                /* OFPP_NONE if unknown. */
     struct netdev *netdev;      /* Network device. */
     const char *type;           /* Usually same as cfg->type. */
     const struct ovsrec_interface *cfg;
@@ -195,8 +196,8 @@ static size_t bridge_get_controllers(const struct bridge *br,
 static void bridge_add_del_ports(struct bridge *,
                                  const unsigned long int *splinter_vlans);
 static void bridge_refresh_ofp_port(struct bridge *);
+static void bridge_configure_flow_miss_model(const char *opt);
 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_table(struct bridge *);
@@ -245,7 +246,8 @@ static bool mirror_configure(struct mirror *);
 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_create(struct bridge *, struct if_cfg *,
+                         ofp_port_t 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 *,
@@ -255,9 +257,9 @@ static struct iface *iface_lookup(const struct bridge *, const char *name);
 static struct iface *iface_find(const char *name);
 static struct if_cfg *if_cfg_lookup(const struct bridge *, const char *name);
 static struct iface *iface_from_ofp_port(const struct bridge *,
-                                         uint16_t ofp_port);
+                                         ofp_port_t ofp_port);
 static void iface_set_mac(struct iface *);
-static void iface_set_ofport(const struct ovsrec_interface *, int64_t ofport);
+static void iface_set_ofport(const struct ovsrec_interface *, ofp_port_t ofport);
 static void iface_clear_db_record(const struct ovsrec_interface *if_cfg);
 static void iface_configure_qos(struct iface *, const struct ovsrec_qos *);
 static void iface_configure_cfm(struct iface *);
@@ -265,7 +267,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 *);
+static ofp_port_t iface_pick_ofport(const struct ovsrec_interface *);
 
 /* Linux VLAN device support (e.g. "eth0.10" for VLAN 10.)
  *
@@ -494,6 +496,13 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg)
     ovs_assert(!reconfiguring);
     reconfiguring = true;
 
+    ofproto_set_flow_eviction_threshold(
+        smap_get_int(&ovs_cfg->other_config, "flow-eviction-threshold",
+                     OFPROTO_FLOW_EVICTION_THRESHOLD_DEFAULT));
+
+    bridge_configure_flow_miss_model(smap_get(&ovs_cfg->other_config,
+                                              "force-miss-model"));
+
     /* Destroy "struct bridge"s, "struct port"s, and "struct iface"s according
      * to 'ovs_cfg' while update the "if_cfg_queue", with only very minimal
      * configuration otherwise.
@@ -565,7 +574,7 @@ bridge_reconfigure_ofp(void)
         struct if_cfg *if_cfg, *next;
 
         HMAP_FOR_EACH_SAFE (if_cfg, next, hmap_node, &br->if_cfg_todo) {
-            iface_create(br, if_cfg, -1);
+            iface_create(br, if_cfg, OFPP_NONE);
             time_refresh();
             if (time_msec() >= deadline) {
                 return false;
@@ -612,7 +621,6 @@ 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_table(br);
         bridge_configure_remotes(br, managers, n_managers);
@@ -622,6 +630,13 @@ bridge_reconfigure_continue(const struct ovsrec_open_vswitch *ovs_cfg)
         bridge_configure_stp(br);
         bridge_configure_tables(br);
         bridge_configure_dp_desc(br);
+
+        if (smap_get(&br->cfg->other_config, "flow-eviction-threshold")) {
+            /* XXX: Remove this warning message eventually. */
+            VLOG_WARN_ONCE("As of June 2013, flow-eviction-threshold has been"
+                           " moved to the Open_vSwitch table.  Ignoring its"
+                           " setting in the bridge table.");
+        }
     }
     free(managers);
 
@@ -680,7 +695,7 @@ bridge_update_ofprotos(void)
                 error = ofproto_port_del(br2->ofproto, ofproto_port.ofp_port);
                 if (error) {
                     VLOG_ERR("failed to delete port %s: %s", ofproto_port.name,
-                             strerror(error));
+                             ovs_strerror(error));
                 }
                 ofproto_port_destroy(&ofproto_port);
             }
@@ -689,7 +704,7 @@ bridge_update_ofprotos(void)
         error = ofproto_create(br->name, br->type, &br->ofproto);
         if (error) {
             VLOG_ERR("failed to create bridge %s: %s", br->name,
-                     strerror(error));
+                     ovs_strerror(error));
             bridge_destroy(br);
         }
     }
@@ -793,6 +808,22 @@ port_configure(struct port *port)
     free(s.lacp_slaves);
 }
 
+static void
+bridge_configure_flow_miss_model(const char *opt)
+{
+    enum ofproto_flow_miss_model model = OFPROTO_HANDLE_MISS_AUTO;
+
+    if (opt) {
+        if (strcmp(opt, "with-facets")) {
+            model = OFPROTO_HANDLE_MISS_WITH_FACETS;
+        } else if (strcmp(opt, "without-facets")) {
+            model = OFPROTO_HANDLE_MISS_WITHOUT_FACETS;
+        }
+    }
+
+    ofproto_set_flow_miss_model(model);
+}
+
 /* Pick local port hardware address and datapath ID for 'br'. */
 static void
 bridge_configure_datapath_id(struct bridge *br)
@@ -811,7 +842,7 @@ bridge_configure_datapath_id(struct bridge *br)
             static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
             VLOG_ERR_RL(&rl, "bridge %s: failed to set bridge "
                         "Ethernet address: %s",
-                        br->name, strerror(error));
+                        br->name, ovs_strerror(error));
         }
     }
     memcpy(br->ea, ea, ETH_ADDR_LEN);
@@ -1267,13 +1298,14 @@ add_del_bridges(const struct ovsrec_open_vswitch *cfg)
 }
 
 static void
-iface_set_ofp_port(struct iface *iface, int ofp_port)
+iface_set_ofp_port(struct iface *iface, ofp_port_t ofp_port)
 {
     struct bridge *br = iface->port->bridge;
 
-    ovs_assert(iface->ofp_port < 0 && ofp_port >= 0);
+    ovs_assert(iface->ofp_port == OFPP_NONE && ofp_port != OFPP_NONE);
     iface->ofp_port = ofp_port;
-    hmap_insert(&br->ifaces, &iface->ofp_port_node, hash_int(ofp_port, 0));
+    hmap_insert(&br->ifaces, &iface->ofp_port_node,
+                hash_ofp_port(ofp_port));
     iface_set_ofport(iface->cfg, ofp_port);
 }
 
@@ -1288,7 +1320,7 @@ iface_set_netdev_config(const struct ovsrec_interface *iface_cfg,
     error = netdev_set_config(netdev, &iface_cfg->options);
     if (error) {
         VLOG_WARN("could not configure network device %s (%s)",
-                  iface_cfg->name, strerror(error));
+                  iface_cfg->name, ovs_strerror(error));
     }
     return error;
 }
@@ -1308,12 +1340,12 @@ bridge_refresh_one_ofp_port(struct bridge *br,
 {
     const char *name = ofproto_port->name;
     const char *type = ofproto_port->type;
-    uint16_t ofp_port = ofproto_port->ofp_port;
+    ofp_port_t ofp_port = ofproto_port->ofp_port;
 
     struct iface *iface = iface_lookup(br, name);
     if (iface) {
         /* Check that the name-to-number mapping is one-to-one. */
-        if (iface->ofp_port >= 0) {
+        if (iface->ofp_port != OFPP_NONE) {
             VLOG_WARN("bridge %s: interface %s reported twice",
                       br->name, name);
             return false;
@@ -1367,7 +1399,7 @@ bridge_refresh_ofp_port(struct bridge *br)
         struct iface *iface;
 
         LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
-            iface->ofp_port = -1;
+            iface->ofp_port = OFPP_NONE;
         }
     }
 
@@ -1389,7 +1421,7 @@ bridge_refresh_ofp_port(struct bridge *br)
         struct iface *iface, *iface_next;
 
         LIST_FOR_EACH_SAFE (iface, iface_next, port_elem, &port->ifaces) {
-            if (iface->ofp_port < 0) {
+            if (iface->ofp_port == OFPP_NONE) {
                 bridge_queue_if_cfg(br, iface->cfg, port->cfg);
                 iface_destroy(iface);
             }
@@ -1402,7 +1434,7 @@ bridge_refresh_ofp_port(struct bridge *br)
 }
 
 /* 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
+ * is OFPP_NONE, adds the network device to br->ofproto and stores the OpenFlow
  * port number in '*ofp_portp'; otherwise leaves br->ofproto and '*ofp_portp'
  * untouched.
  *
@@ -1411,18 +1443,25 @@ bridge_refresh_ofp_port(struct bridge *br)
 static int
 iface_do_create(const struct bridge *br,
                 const struct if_cfg *if_cfg,
-                int *ofp_portp, struct netdev **netdevp)
+                ofp_port_t *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;
+    struct netdev *netdev = NULL;
     int error;
 
+    if (netdev_is_reserved_name(iface_cfg->name)) {
+        VLOG_WARN("could not create interface %s, name is reserved",
+                  iface_cfg->name);
+        error = EINVAL;
+        goto error;
+    }
+
     error = netdev_open(iface_cfg->name,
                         iface_get_type(iface_cfg, br->cfg), &netdev);
     if (error) {
         VLOG_WARN("could not open network device %s (%s)",
-                  iface_cfg->name, strerror(error));
+                  iface_cfg->name, ovs_strerror(error));
         goto error;
     }
 
@@ -1431,8 +1470,8 @@ iface_do_create(const struct bridge *br,
         goto error;
     }
 
-    if (*ofp_portp < 0) {
-        uint16_t ofp_port = if_cfg->ofport;
+    if (*ofp_portp == OFPP_NONE) {
+        ofp_port_t ofp_port = if_cfg->ofport;
 
         error = ofproto_port_add(br->ofproto, netdev, &ofp_port);
         if (error) {
@@ -1462,13 +1501,13 @@ error:
 }
 
 /* Creates a new iface on 'br' based on 'if_cfg'.  The new iface has OpenFlow
- * port number 'ofp_port'.  If ofp_port is negative, an OpenFlow port is
+ * port number 'ofp_port'.  If ofp_port is OFPP_NONE, an OpenFlow port is
  * automatically allocated for the iface.  Takes ownership of and
  * deallocates 'if_cfg'.
  *
  * Return true if an iface is successfully created, false otherwise. */
 static bool
-iface_create(struct bridge *br, struct if_cfg *if_cfg, int ofp_port)
+iface_create(struct bridge *br, struct if_cfg *if_cfg, ofp_port_t ofp_port)
 {
     const struct ovsrec_interface *iface_cfg = if_cfg->cfg;
     const struct ovsrec_port *port_cfg = if_cfg->parent;
@@ -1489,7 +1528,7 @@ iface_create(struct bridge *br, struct if_cfg *if_cfg, int ofp_port)
     error = iface_do_create(br, if_cfg, &ofp_port, &netdev);
     bridge_run_fast();
     if (error) {
-        iface_set_ofport(iface_cfg, -1);
+        iface_set_ofport(iface_cfg, OFPP_NONE);
         iface_clear_db_record(iface_cfg);
         ok = false;
         goto done;
@@ -1508,7 +1547,7 @@ iface_create(struct bridge *br, struct if_cfg *if_cfg, int ofp_port)
                 hash_string(iface_cfg->name, 0));
     iface->port = port;
     iface->name = xstrdup(iface_cfg->name);
-    iface->ofp_port = -1;
+    iface->ofp_port = OFPP_NONE;
     iface->netdev = netdev;
     iface->type = iface_get_type(iface_cfg, br->cfg);
     iface->cfg = iface_cfg;
@@ -1530,13 +1569,13 @@ iface_create(struct bridge *br, struct if_cfg *if_cfg, int ofp_port)
 
             error = netdev_open(port->name, "internal", &netdev);
             if (!error) {
-                uint16_t fake_ofp_port = if_cfg->ofport;
+                ofp_port_t fake_ofp_port = if_cfg->ofport;
 
                 ofproto_port_add(br->ofproto, netdev, &fake_ofp_port);
                 netdev_close(netdev);
             } else {
                 VLOG_WARN("could not open network device %s (%s)",
-                          port->name, strerror(error));
+                          port->name, ovs_strerror(error));
             }
         } else {
             /* Already exists, nothing to do. */
@@ -1551,23 +1590,6 @@ done:
     return ok;
 }
 
-/* Set Flow eviction threshold */
-static void
-bridge_configure_flow_eviction_threshold(struct bridge *br)
-{
-    const char *threshold_str;
-    unsigned threshold;
-
-    threshold_str = smap_get(&br->cfg->other_config,
-                             "flow-eviction-threshold");
-    if (threshold_str) {
-        threshold = strtoul(threshold_str, NULL, 10);
-    } else {
-        threshold = OFPROTO_FLOW_EVICTION_THRESHOLD_DEFAULT;
-    }
-    ofproto_set_flow_eviction_threshold(br->ofproto, threshold);
-}
-
 /* Set forward BPDU option. */
 static void
 bridge_configure_forward_bpdu(struct bridge *br)
@@ -2314,6 +2336,13 @@ bridge_run(void)
      * returns immediately. */
     bridge_init_ofproto(cfg);
 
+    /* Once the value of flow-restore-wait is false, we no longer should
+     * check its value from the database. */
+    if (cfg && ofproto_get_flow_restore_wait()) {
+        ofproto_set_flow_restore_wait(smap_get_bool(&cfg->other_config,
+                                        "flow-restore-wait", false));
+    }
+
     /* Let each datapath type do the work that it needs to do. */
     sset_init(&types);
     ofproto_enumerate_types(&types);
@@ -2537,7 +2566,7 @@ qos_unixctl_show_cb(unsigned int queue_id,
         }
     } else {
         ds_put_format(ds, "\tFailed to get statistics for queue %u: %s",
-                      queue_id, strerror(error));
+                      queue_id, ovs_strerror(error));
     }
 }
 
@@ -2573,7 +2602,8 @@ qos_unixctl_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
         error = netdev_dump_queues(iface->netdev, qos_unixctl_show_cb, &data);
 
         if (error) {
-            ds_put_format(&ds, "failed to dump queues: %s", strerror(error));
+            ds_put_format(&ds, "failed to dump queues: %s",
+                          ovs_strerror(error));
         }
         unixctl_command_reply(conn, ds_cstr(&ds));
     } else {
@@ -3284,7 +3314,7 @@ iface_configure_lacp(struct iface *iface, struct lacp_slave_settings *s)
     key = smap_get_int(&iface->cfg->other_config, "lacp-aggregation-key", 0);
 
     if (portid <= 0 || portid > UINT16_MAX) {
-        portid = iface->ofp_port;
+        portid = ofp_to_u16(iface->ofp_port);
     }
 
     if (priority <= 0 || priority > UINT16_MAX) {
@@ -3408,11 +3438,11 @@ iface_destroy(struct iface *iface)
         struct port *port = iface->port;
         struct bridge *br = port->bridge;
 
-        if (br->ofproto && iface->ofp_port >= 0) {
+        if (br->ofproto && iface->ofp_port != OFPP_NONE) {
             ofproto_port_unregister(br->ofproto, iface->ofp_port);
         }
 
-        if (iface->ofp_port >= 0) {
+        if (iface->ofp_port != OFPP_NONE) {
             hmap_remove(&br->ifaces, &iface->ofp_port_node);
         }
 
@@ -3472,12 +3502,12 @@ if_cfg_lookup(const struct bridge *br, const char *name)
 }
 
 static struct iface *
-iface_from_ofp_port(const struct bridge *br, uint16_t ofp_port)
+iface_from_ofp_port(const struct bridge *br, ofp_port_t ofp_port)
 {
     struct iface *iface;
 
-    HMAP_FOR_EACH_IN_BUCKET (iface, ofp_port_node,
-                             hash_int(ofp_port, 0), &br->ifaces) {
+    HMAP_FOR_EACH_IN_BUCKET (iface, ofp_port_node, hash_ofp_port(ofp_port),
+                             &br->ifaces) {
         if (iface->ofp_port == ofp_port) {
             return iface;
         }
@@ -3505,7 +3535,7 @@ iface_set_mac(struct iface *iface)
             int error = netdev_set_etheraddr(iface->netdev, ea);
             if (error) {
                 VLOG_ERR("interface %s: setting MAC failed (%s)",
-                         iface->name, strerror(error));
+                         iface->name, ovs_strerror(error));
             }
         }
     }
@@ -3513,10 +3543,12 @@ iface_set_mac(struct iface *iface)
 
 /* Sets the ofport column of 'if_cfg' to 'ofport'. */
 static void
-iface_set_ofport(const struct ovsrec_interface *if_cfg, int64_t ofport)
+iface_set_ofport(const struct ovsrec_interface *if_cfg, ofp_port_t ofport)
 {
+    int64_t port_;
+    port_ = (ofport == OFPP_NONE) ? -1 : ofp_to_u16(ofport);
     if (if_cfg && !ovsdb_idl_row_is_synthetic(&if_cfg->header_)) {
-        ovsrec_interface_set_ofport(if_cfg, &ofport, 1);
+        ovsrec_interface_set_ofport(if_cfg, &port_, 1);
     }
 }
 
@@ -3622,7 +3654,7 @@ iface_configure_qos(struct iface *iface, const struct ovsrec_qos *qos)
         }
     }
 
-    if (iface->ofp_port >= 0) {
+    if (iface->ofp_port != OFPP_NONE) {
         const struct ofproto_port_queue *port_queues = queues_buf.data;
         size_t n_queues = queues_buf.size / sizeof *port_queues;
 
@@ -3684,6 +3716,7 @@ iface_configure_cfm(struct iface *iface)
 
     s.extended = smap_get_bool(&iface->cfg->other_config, "cfm_extended",
                                false);
+    s.demand = smap_get_bool(&iface->cfg->other_config, "cfm_demand", false);
 
     opstate_str = smap_get(&iface->cfg->other_config, "cfm_opstate");
     s.opup = !opstate_str || !strcasecmp("up", opstate_str);
@@ -3699,11 +3732,13 @@ iface_is_synthetic(const struct iface *iface)
     return ovsdb_idl_row_is_synthetic(&iface->cfg->header_);
 }
 
-static int64_t
+static ofp_port_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;
+    ofp_port_t ofport = cfg->n_ofport ? u16_to_ofp(*cfg->ofport)
+                                      : OFPP_NONE;
+    return cfg->n_ofport_request ? u16_to_ofp(*cfg->ofport_request)
+                                 : ofport;
 }
 
 \f
@@ -4081,7 +4116,7 @@ static void
 configure_splinter_port(struct port *port)
 {
     struct ofproto *ofproto = port->bridge->ofproto;
-    uint16_t realdev_ofp_port;
+    ofp_port_t realdev_ofp_port;
     const char *realdev_name;
     struct iface *vlandev, *realdev;
 
index 43c5d24..86500c9 100644 (file)
@@ -98,7 +98,6 @@ configuration.
 .so lib/ssl-bootstrap.man
 .so lib/vlog.man
 .so lib/common.man
-.so lib/leak-checker.man
 .
 .SH "RUNTIME MANAGEMENT COMMANDS"
 \fBovs\-appctl\fR(8) can send commands to a running
@@ -211,6 +210,26 @@ enabled.
 .so lib/coverage-unixctl.man
 .so lib/stress-unixctl.man
 .
+.SH "OPENFLOW IMPLEMENTATION"
+.
+.PP
+This section documents aspects of OpenFlow for which the OpenFlow
+specification requires documentation.
+.
+.SS "Packet buffering."
+The OpenFlow specification, version 1.2, says:
+.
+.IP
+Switches that implement buffering are expected to expose, through
+documentation, both the amount of available buffering, and the length
+of time before buffers may be reused.
+.
+.PP
+Open vSwitch maintains a separate set of 256 packet buffers for each
+OpenFlow connection.  Any given packet buffer is preserved until it is
+referenced by an \fBOFPT_FLOW_MOD\fR or \fBOFPT_PACKET_OUT\fR request
+or for 5 seconds, whichever comes first.
+.
 .SH "LIMITS"
 .
 .PP
@@ -227,7 +246,9 @@ requires 17 file descriptors per datapath.)
 per bridge due to fixed hash table sizing.
 .
 .IP \(bu
-2,048 MAC learning entries per bridge.
+2,048 MAC learning entries per bridge, by default.  (This is
+configurable via \fBother\-config:mac\-table\-size\fR in the
+\fBBridge\fR table.  See \fBovs\-vswitchd.conf.db\fR(5) for details.)
 .
 .IP \(bu
 Kernel flows are limited only by memory available to the kernel.
index 78ebd31..bb087bd 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.
@@ -32,7 +32,6 @@
 #include "dirs.h"
 #include "dpif.h"
 #include "dummy.h"
-#include "leak-checker.h"
 #include "memory.h"
 #include "netdev.h"
 #include "openflow/openflow.h"
@@ -88,7 +87,7 @@ main(int argc, char *argv[])
     if (want_mlockall) {
 #ifdef HAVE_MLOCKALL
         if (mlockall(MCL_CURRENT | MCL_FUTURE)) {
-            VLOG_ERR("mlockall failed: %s", strerror(errno));
+            VLOG_ERR("mlockall failed: %s", ovs_strerror(errno));
         }
 #else
         VLOG_ERR("mlockall not supported on this system");
@@ -140,7 +139,6 @@ main(int argc, char *argv[])
     }
     bridge_exit();
     unixctl_server_destroy(unixctl);
-    signal_unregister(sighup);
 
     return 0;
 }
@@ -153,7 +151,6 @@ parse_options(int argc, char *argv[], char **unixctl_pathp)
         OPT_MLOCKALL,
         OPT_UNIXCTL,
         VLOG_OPTION_ENUMS,
-        LEAK_CHECKER_OPTION_ENUMS,
         OPT_BOOTSTRAP_CA_CERT,
         OPT_ENABLE_DUMMY,
         OPT_DISABLE_SYSTEM,
@@ -166,7 +163,6 @@ parse_options(int argc, char *argv[], char **unixctl_pathp)
         {"unixctl",     required_argument, NULL, OPT_UNIXCTL},
         DAEMON_LONG_OPTIONS,
         VLOG_LONG_OPTIONS,
-        LEAK_CHECKER_LONG_OPTIONS,
         STREAM_SSL_LONG_OPTIONS,
         {"peer-ca-cert", required_argument, NULL, OPT_PEER_CA_CERT},
         {"bootstrap-ca-cert", required_argument, NULL, OPT_BOOTSTRAP_CA_CERT},
@@ -202,7 +198,6 @@ parse_options(int argc, char *argv[], char **unixctl_pathp)
 
         VLOG_OPTION_HANDLERS
         DAEMON_OPTION_HANDLERS
-        LEAK_CHECKER_OPTION_HANDLERS
         STREAM_SSL_OPTION_HANDLERS
 
         case OPT_PEER_CA_CERT:
@@ -261,7 +256,6 @@ usage(void)
            "  --unixctl=SOCKET        override default control socket name\n"
            "  -h, --help              display this help message\n"
            "  -V, --version           display version information\n");
-    leak_checker_usage();
     exit(EXIT_SUCCESS);
 }
 
index 842bc20..f0f53c0 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010, 2012 Nicira, Inc.
+/* Copyright (c) 2010, 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,7 +131,8 @@ get_memory_stats(struct smap *stats)
 
         stream = fopen(file_name, "r");
         if (!stream) {
-            VLOG_WARN_ONCE("%s: open failed (%s)", file_name, strerror(errno));
+            VLOG_WARN_ONCE("%s: open failed (%s)",
+                           file_name, ovs_strerror(errno));
             return;
         }
 
@@ -183,7 +184,8 @@ get_boot_time(void)
 
         stream = fopen(stat_file, "r");
         if (!stream) {
-            VLOG_ERR_ONCE("%s: open failed (%s)", stat_file, strerror(errno));
+            VLOG_ERR_ONCE("%s: open failed (%s)",
+                          stat_file, ovs_strerror(errno));
             return boot_time;
         }
 
@@ -242,7 +244,8 @@ get_raw_process_info(pid_t pid, struct raw_process_info *raw)
     sprintf(file_name, "/proc/%lu/stat", (unsigned long int) pid);
     stream = fopen(file_name, "r");
     if (!stream) {
-        VLOG_ERR_ONCE("%s: open failed (%s)", file_name, strerror(errno));
+        VLOG_ERR_ONCE("%s: open failed (%s)",
+                      file_name, ovs_strerror(errno));
         return false;
     }
 
@@ -327,13 +330,13 @@ count_crashes(pid_t pid)
     sprintf(file_name, "/proc/%lu/cmdline", (unsigned long int) pid);
     stream = fopen(file_name, "r");
     if (!stream) {
-        VLOG_WARN_ONCE("%s: open failed (%s)", file_name, strerror(errno));
+        VLOG_WARN_ONCE("%s: open failed (%s)", file_name, ovs_strerror(errno));
         goto exit;
     }
 
     if (!fgets(line, sizeof line, stream)) {
         VLOG_WARN_ONCE("%s: read failed (%s)", file_name,
-                       feof(stream) ? "end of file" : strerror(errno));
+                       feof(stream) ? "end of file" : ovs_strerror(errno));
         goto exit_close;
     }
 
@@ -398,7 +401,8 @@ get_process_stats(struct smap *stats)
 
     dir = opendir(ovs_rundir());
     if (!dir) {
-        VLOG_ERR_ONCE("%s: open failed (%s)", ovs_rundir(), strerror(errno));
+        VLOG_ERR_ONCE("%s: open failed (%s)",
+                      ovs_rundir(), ovs_strerror(errno));
         return;
     }
 
@@ -457,7 +461,7 @@ get_filesys_stats(struct smap *stats OVS_UNUSED)
 
     stream = setmntent(file_name, "r");
     if (!stream) {
-        VLOG_ERR_ONCE("%s: open failed (%s)", file_name, strerror(errno));
+        VLOG_ERR_ONCE("%s: open failed (%s)", file_name, ovs_strerror(errno));
         return;
     }
 
index 4396779..12780d6 100644 (file)
         The Citrix XenServer universally unique identifier for the physical
         host as displayed by <code>xe host-list</code>.
       </column>
+
+      <column name="other_config" key="flow-restore-wait"
+              type='{"type": "boolean"}'>
+        <p>
+          When <code>ovs-vswitchd</code> starts up, it has an empty flow table
+          and therefore it handles all arriving packets in its default fashion
+          according to its configuration, by dropping them or sending them to
+          an OpenFlow controller or switching them as a standalone switch.
+          This behavior is ordinarily desirable.  However, if
+          <code>ovs-vswitchd</code> is restarting as part of a ``hot-upgrade,''
+          then this leads to a relatively long period during which packets are
+          mishandled.
+        </p>
+        <p>
+          This option allows for improvement.  When <code>ovs-vswitchd</code>
+          starts with this value set as <code>true</code>, it will neither
+          flush or expire previously set datapath flows nor will it send and
+          receive any packets to or from the datapath.  When this value is
+          later set to <code>false</code>, <code>ovs-vswitchd</code> will
+          start receiving packets from the datapath and re-setup the flows.
+        </p>
+        <p>
+          Thus, with this option, the procedure for a hot-upgrade of
+          <code>ovs-vswitchd</code> becomes roughly the following:
+        </p>
+        <ol>
+          <li>
+            Stop <code>ovs-vswitchd</code>.
+          </li>
+          <li>
+            Set <ref column="other_config" key="flow-restore-wait"/>
+            to <code>true</code>.
+          </li>
+          <li>
+            Start <code>ovs-vswitchd</code>.
+          </li>
+          <li>
+            Use <code>ovs-ofctl</code> (or some other program, such as an
+            OpenFlow controller) to restore the OpenFlow flow table
+            to the desired state.
+          </li>
+          <li>
+            Set <ref column="other_config" key="flow-restore-wait"/>
+            to <code>false</code> (or remove it entirely from the database).
+          </li>
+        </ol>
+        <p>
+          The <code>ovs-ctl</code>'s ``restart'' and ``force-reload-kmod''
+          functions use the above config option during hot upgrades.
+        </p>
+      </column>
+
+      <column name="other_config" key="flow-eviction-threshold"
+              type='{"type": "integer", "minInteger": 0}'>
+        <p>
+          A number of flows as a nonnegative integer.  This sets number of
+          flows at which eviction from the datapath flow table will be
+          triggered.  If there are a large number of flows then increasing this
+          value to around the number of flows present can result in reduced CPU
+          usage and packet loss.
+        </p>
+        <p>
+          The default is 2500.  Values below 100 will be rounded up to 100.
+        </p>
+      </column>
+
+      <column name="other_config" key="force-miss-model">
+        <p>
+          Specifies userspace behaviour for handling flow misses. This takes
+          precedence over flow-eviction-threshold.
+        </p>
+        <p>
+          <dl>
+            <dt><code>auto</code></dt>
+            <dd>Handle automatically based on the flow-eviction-threshold and
+            the flow setup governer (default, recommended).</dd>
+            <dt><code>with-facets</code></dt>
+            <dd>Always create facets. Expensive kernel flow creation and
+            statistics tracking is always performed, even on flows with only
+            a small number of packets.</dd>
+            <dt><code>without-facets</code></dt>
+            <dd>Always handle without facets. Forces flow misses to be handled
+            in userspace. May cause an increase in CPU usage and packet loss
+            on high throughput.</dd>
+          </dl>
+        </p>
+      </column>
     </group>
 
     <group title="Status">
         datapath ID.
       </column>
 
-      <column name="other_config" key="flow-eviction-threshold"
-              type='{"type": "integer", "minInteger": 0}'>
-        <p>
-          A number of flows as a nonnegative integer.  This sets number of
-          flows at which eviction from the kernel flow table will be triggered.
-          If there are a large number of flows then increasing this value to
-          around the number of flows present can result in reduced CPU usage
-          and packet loss.
-        </p>
-        <p>
-          The default is 1000.  Values below 100 will be rounded up to 100.
-        </p>
-      </column>
-
       <column name="other_config" key="forward-bpdu"
               type='{"type": "boolean"}'>
         Option to allow forwarding of BPDU frames when NORMAL action is
         compatibility with 802.1ag compliant implementations.  Defaults to
         <code>false</code>.
       </column>
+
+      <column name="other_config" key="cfm_demand" type='{"type": "boolean"}'>
+        <p>
+          When <code>true</code>, and
+          <ref column="other_config" key="cfm_extended"/> is true, the CFM
+          module operates in demand mode.  When in demand mode, traffic
+          received on the <ref table="Interface"/> is used to indicate
+          liveness.  CCMs are still transmitted and received, but if the
+          <ref table="Interface"/> is receiving traffic, their absence does not
+          cause a connectivity fault.
+        </p>
+
+        <p>
+            Demand mode has a couple of caveats:
+          <ul>
+            <li>
+              To ensure that ovs-vswitchd has enough time to pull statistics
+              from the datapath, the minimum
+              <ref column="other_config" key="cfm_interval"/> is 500ms.
+            </li>
+
+            <li>
+              To avoid ambiguity, demand mode disables itself when there are
+              multiple remote maintenance points.
+            </li>
+
+            <li>
+              If the <ref table="Interface"/> is heavily congested, CCMs
+              containing the <ref column="other_config" key="cfm_opstate"/>
+              status may be dropped causing changes in the operational state to
+              be delayed.  Similarly, if CCMs containing the RDI bit are not
+              received, unidirectional link failures may not be detected.
+            </li>
+          </ul>
+        </p>
+      </column>
+
       <column name="other_config" key="cfm_opstate"
               type='{"type": "string", "enum": ["set", ["down", "up"]]}'>
         When <code>down</code>, the CFM module marks all CCMs it generates as
index 7427e4c..1f26e0d 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009, 2010 Nicira, Inc.
+/* Copyright (c) 2009, 2010, 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.
@@ -38,7 +38,7 @@ read_host_uuid(void)
         if (errno == ENOENT) {
             VLOG_DBG("not running on a XenServer");
         } else {
-            VLOG_INFO("%s: open: %s", filename, strerror(errno));
+            VLOG_INFO("%s: open: %s", filename, ovs_strerror(errno));
         }
         return NULL;
     }
index 4e8f576..dff18d0 100644 (file)
@@ -1,6 +1,6 @@
 # Spec file for Open vSwitch.
 
-# Copyright (C) 2009, 2010, 2011, 2012 Nicira, Inc.
+# Copyright (C) 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -428,7 +428,6 @@ exit 0
 /usr/bin/ovs-dpctl
 /usr/bin/ovs-ofctl
 /usr/bin/ovs-parse-backtrace
-/usr/bin/ovs-parse-leaks
 /usr/bin/ovs-pcap
 /usr/bin/ovs-tcpundump
 /usr/bin/ovs-vlan-test
@@ -446,7 +445,6 @@ exit 0
 /usr/share/man/man8/ovs-dpctl.8.gz
 /usr/share/man/man8/ovs-ofctl.8.gz
 /usr/share/man/man8/ovs-parse-backtrace.8.gz
-/usr/share/man/man8/ovs-parse-leaks.8.gz
 /usr/share/man/man1/ovs-pcap.1.gz
 /usr/share/man/man1/ovs-tcpundump.1.gz
 /usr/share/man/man8/ovs-vlan-bug-workaround.8.gz
index 22af3c2..f37e038 100644 (file)
@@ -923,7 +923,7 @@ class Datapath(object):
 
            Should assume any configuration files changed attached in
            the preconfigure stage are applied and bring up the
-           necesary devices to provide the datapath for the
+           necessary devices to provide the datapath for the
            PIF.
 
            Should not bring up the IPdev.
index 72bd5e4..3b5c861 100755 (executable)
@@ -242,7 +242,7 @@ def ipdev_configure_static_routes(interface, oc, f):
           172.18.0.0/16 via 192.168.0.4 dev xenbr1
     """
     if oc.has_key('static-routes'):
-        # The key is present - extract comma seperates entries
+        # The key is present - extract comma separates entries
         lines = oc['static-routes'].split(',')
     else:
         # The key is not present, i.e. there are no static routes
index cb35e7a..1c8ad51 100755 (executable)
@@ -1,5 +1,5 @@
 #!/usr/bin/python
-# 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.
@@ -41,6 +41,7 @@ vlog = ovs.vlog.Vlog("ovs-xapi-sync")
 session = None
 flush_cache = False
 exiting = False
+xapi_down = False
 
 
 def unixctl_exit(conn, unused_argv, unused_aux):
@@ -88,6 +89,19 @@ def get_network_by_bridge(br_name):
 
     return None
 
+# There are possibilities when multiple xs-network-uuids are set for a bridge.
+# In cases like that, we should choose the bridge-id associated with the bridge
+# name.
+def get_single_bridge_id(bridge_ids, br_name, default=None):
+    global xapi_down
+
+    rec = get_network_by_bridge(br_name)
+    if rec and rec['uuid'] in bridge_ids:
+        return rec['uuid']
+
+    vlog.warn("Failed to get a single bridge id from Xapi.")
+    xapi_down = True
+    return default
 
 # By default, the "bridge-id" external id in the Bridge table is the
 # same as "xs-network-uuids".  This may be overridden by defining a
@@ -209,7 +223,7 @@ def update_in_band_mgmt(row):
 
 
 def main():
-    global flush_cache
+    global flush_cache, xapi_down
 
     parser = argparse.ArgumentParser()
     parser.add_argument("database", metavar="DATABASE",
@@ -256,13 +270,18 @@ def main():
             break;
 
         idl.run()
-        if not flush_cache and seqno == idl.change_seqno:
+        if not xapi_down and not flush_cache and seqno == idl.change_seqno:
             poller = ovs.poller.Poller()
             unixctl_server.wait(poller)
             idl.wait(poller)
             poller.block()
             continue
 
+        if xapi_down:
+            vlog.warn("Xapi is probably down. Retry again after a second.")
+            time.sleep(1)
+            xapi_down = False
+
         if flush_cache:
             vlog.info("Flushing cache as the result of unixctl.")
             bridges = {}
@@ -275,22 +294,27 @@ def main():
 
         new_bridges = {}
         for row in idl.tables["Bridge"].rows.itervalues():
-            if row.name in bridges:
-                nbd = bridges[row.name]
-            else:
-                # New bridge.
+            bridge_id = bridges.get(row.name)
+            if bridge_id is None:
+                # Configure the new bridge.
                 update_fail_mode(row)
                 update_in_band_mgmt(row)
-                nbd = get_bridge_id(row.name)
 
-            bridge_id = nbd
-            if bridge_id is None:
-                bridge_id = row.external_ids.get("xs-network-uuids")
+                # Get the correct bridge_id, if we can.
+                bridge_id = get_bridge_id(row.name)
+                if bridge_id is None:
+                    xs_network_uuids = row.external_ids.get("xs-network-uuids")
+                    if xs_network_uuids:
+                        bridge_ids = xs_network_uuids.split(";")
+                        if len(bridge_ids) == 1:
+                            bridge_id = bridge_ids[0]
+                        else:
+                            bridge_id = get_single_bridge_id(bridge_ids,
+                                                             row.name)
+            set_external_id(row, "bridge-id", bridge_id)
 
             if bridge_id is not None:
-                set_external_id(row, "bridge-id", bridge_id.split(";")[0])
-
-            new_bridges[row.name] = nbd
+                new_bridges[row.name] = bridge_id
         bridges = new_bridges
 
         iface_by_name = {}